vet

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 99f949f8afcffdc1b4bd06eebf223e1dc275e4a4
parent ad8c4788e6e93c42873f49a7254ec457dfb26506
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date:   Thu,  5 Feb 2026 19:52:10 +0000

Agent usage skills (#13)

* Vibecoding UX considerations :(

* Bug fix for session loading. OpenCode works!

* Bug fix for session loading. OpenCode works!

* Stronger wording

* Updated wording

* Refactoring

* Black

* Added install stuff

* Reworded skill
Diffstat:
Askills/vet/SKILL.md | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Askills/vet/scripts/export_codex_session.py | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Askills/vet/scripts/export_opencode_session.py | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 197 insertions(+), 0 deletions(-)

diff --git a/skills/vet/SKILL.md b/skills/vet/SKILL.md @@ -0,0 +1,72 @@ +--- +name: vet +description: Run vet immediately after ANY logical unit of code changes. Do not batch your changes, do not wait to be asked to run vet, make sure you are proactive. +--- + +# Vet + +**Run vet immediately after ANY logical unit of code changes. Do not batch changes, do not wait to be asked to run vet, make sure you are proactive.** + +Vet reviews git diffs and conversation history to find issues in code changes and conversation history. It is most effective when run frequently with conversation history, which helps it catch misunderstandings between what was requested and what was implemented. + +## Installation + +Install vet using pip or uv: + +```bash +# Using pip +pip install git+https://github.com/imbue-ai/vet.git + +# Using uv (recommended) +uv pip install git+https://github.com/imbue-ai/vet.git + +Verify installation: +```bash +vet --help +``` + +## Running Vet + +### Standard Usage + +Always include conversation history for best results. Set the session environment variable and use `--history-loader`: + +**OpenCode:** +```bash +export VET_SESSION_ID="<current-session-id>" +vet "goal" --history-loader "python /path/to/skills/vet/scripts/export_opencode_session.py" +``` + +**Codex:** +```bash +export CODEX_SESSION_FILE="<path-to-current-session.jsonl>" +vet "goal" --history-loader "python /path/to/skills/vet/scripts/export_codex_session.py" +``` + +### Finding Your Session + +**OpenCode:** The session ID appears in task metadata or can be found as the most recent file in `~/.local/share/opencode/storage/session/`. + +**Codex:** Session files are stored in `~/.codex/sessions/YYYY/MM/DD/`. Find the most recently modified `.jsonl` file. + +## Common Options + +- `--base-commit REF`: Git ref for diff base (default: HEAD) +- `--model MODEL`: LLM model to use (default: claude-4-5-haiku) +- `--confidence-threshold N`: Minimum confidence 0.0-1.0 (default: 0.8) +- `--output-format FORMAT`: Output as `text` or `json` +- `--quiet`: Suppress progress output +- `--help`: Show comprehensive list of options + +## Configuration + +Create `vet.toml` in your repo for project-specific presets: + +```toml +[ci] +confidence_threshold = 0.9 +base_commit = "main" +quiet = true +``` + +Then run with `vet --config ci "goal"`. diff --git a/skills/vet/scripts/export_codex_session.py b/skills/vet/scripts/export_codex_session.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +import json +import os +import sys +from pathlib import Path + +SESSION_FILE = os.environ.get("CODEX_SESSION_FILE") +if not SESSION_FILE: + sessions_dir = Path.home() / ".codex/sessions" + if sessions_dir.exists(): + files = list(sessions_dir.rglob("*.jsonl")) + if files: + SESSION_FILE = str(max(files, key=lambda f: f.stat().st_mtime)) + +if not SESSION_FILE or not Path(SESSION_FILE).exists(): + sys.exit(0) + +for line in Path(SESSION_FILE).read_text().splitlines(): + if not line.strip(): + continue + try: + entry = json.loads(line) + except json.JSONDecodeError as e: + print( + f"WARNING: Skipping malformed JSON line in {SESSION_FILE}: {e}", + file=sys.stderr, + ) + continue + + if entry.get("type") != "response_item": + continue + + payload = entry.get("payload", {}) + if payload.get("type") != "message": + continue + + role = payload.get("role") + content = payload.get("content", []) + + if role == "user": + text = " ".join(c.get("text", "") for c in content if c.get("type") == "input_text") + if text: + print(json.dumps({"object_type": "ChatInputUserMessage", "text": text})) + elif role == "assistant": + blocks = [] + for c in content: + if c.get("type") == "output_text" and c.get("text"): + blocks.append({"object_type": "TextBlock", "type": "text", "text": c["text"]}) + if blocks: + print( + json.dumps( + { + "object_type": "ResponseBlockAgentMessage", + "role": "assistant", + "assistant_message_id": "codex_msg", + "content": blocks, + } + ) + ) diff --git a/skills/vet/scripts/export_opencode_session.py b/skills/vet/scripts/export_opencode_session.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +import json +import os +import sys +from pathlib import Path + +SESSION_ID = os.environ.get("VET_SESSION_ID") +if not SESSION_ID: + sys.exit(0) + +STORAGE = Path.home() / ".local/share/opencode/storage" +MSG_DIR = STORAGE / "message" / SESSION_ID +PART_DIR = STORAGE / "part" + +if not MSG_DIR.exists(): + sys.exit(0) + +messages = [] +for msg_file in sorted(MSG_DIR.glob("*.json")): + try: + msg = json.loads(msg_file.read_text()) + except json.JSONDecodeError as e: + print(f"WARNING: Skipping malformed message file {msg_file}: {e}", file=sys.stderr) + continue + messages.append((msg.get("time", {}).get("created", 0), msg)) + +for _, msg in sorted(messages, key=lambda x: x[0]): + msg_id = msg["id"] + role = msg.get("role", "user") + part_dir = PART_DIR / msg_id + + if not part_dir.exists(): + continue + + parts = [] + for part_file in part_dir.glob("*.json"): + try: + part = json.loads(part_file.read_text()) + except json.JSONDecodeError as e: + print( + f"WARNING: Skipping malformed part file {part_file}: {e}", + file=sys.stderr, + ) + continue + parts.append(part) + + if role == "user": + text = " ".join(p.get("text", "") for p in parts if p.get("type") == "text") + if text: + print(json.dumps({"object_type": "ChatInputUserMessage", "text": text})) + else: + content = [] + for p in parts: + if p.get("type") == "text" and p.get("text"): + content.append({"object_type": "TextBlock", "type": "text", "text": p["text"]}) + if content: + print( + json.dumps( + { + "object_type": "ResponseBlockAgentMessage", + "role": "assistant", + "assistant_message_id": msg_id, + "content": content, + } + ) + )