commit a21f6bccf3222c4482649e08df251f6ecd637d1f
parent 95ea24e4c91a9073da80a4739828cf06819a752d
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date: Thu, 5 Feb 2026 21:41:12 +0000
Updated readme (#17)
* Updated readme
* Refactoring + support proprietary garbage (claude code)
Diffstat:
5 files changed, 96 insertions(+), 26 deletions(-)
diff --git a/README.md b/README.md
@@ -31,24 +31,31 @@ Vet ships as an [agent skill](https://agentskills.io) that coding agents like [O
### Install the skill globally
```bash
-mkdir -p ~/.agents/skills/vet/scripts && \
+for dir in ~/.agents ~/.claude ~/.codex; do
+ mkdir -p "$dir/skills/vet/scripts"
curl -fsSL https://raw.githubusercontent.com/imbue-ai/vet/main/skills/vet/SKILL.md \
- -o ~/.agents/skills/vet/SKILL.md && \
+ -o "$dir/skills/vet/SKILL.md"
curl -fsSL https://raw.githubusercontent.com/imbue-ai/vet/main/skills/vet/scripts/export_opencode_session.py \
- -o ~/.agents/skills/vet/scripts/export_opencode_session.py && \
+ -o "$dir/skills/vet/scripts/export_opencode_session.py"
curl -fsSL https://raw.githubusercontent.com/imbue-ai/vet/main/skills/vet/scripts/export_codex_session.py \
- -o ~/.agents/skills/vet/scripts/export_codex_session.py
+ -o "$dir/skills/vet/scripts/export_codex_session.py"
+ curl -fsSL https://raw.githubusercontent.com/imbue-ai/vet/main/skills/vet/scripts/export_claude_code_session.py \
+ -o "$dir/skills/vet/scripts/export_claude_code_session.py"
+done
```
-This places the skill in `~/.agents/skills/vet/`, which is discovered by both OpenCode and Codex.
+This places the skill in `~/.agents/skills/vet/`, `~/.claude/skills/vet/`, and `~/.codex/skills/vet/`, so it is discovered by OpenCode, Claude Code, and Codex.
### Install per-project
To have agents use vet automatically in a specific repo, copy the skill into the project:
```bash
-cp -r /path/to/vet/skills/vet .agents/skills/vet
-git add .agents/skills/vet && git commit -m "Add vet agent skill"
+for dir in .agents .claude .codex; do
+ cp -r /path/to/vet/skills/vet "$dir/skills/vet"
+done
+git add .agents/skills/vet .claude/skills/vet .codex/skills/vet && \
+ git commit -m "Add vet agent skill"
```
## How it works
diff --git a/skills/vet/SKILL.md b/skills/vet/SKILL.md
@@ -29,18 +29,21 @@ vet --help
### Standard Usage
-Always include conversation history for best results. Set the session environment variable and use `--history-loader`:
+Always include conversation history for best results. Pass the session identifier to the export script via `--history-loader`:
**OpenCode:**
```bash
-export VET_SESSION_ID="<current-session-id>"
-vet "goal" --history-loader "python ~/.agents/skills/vet/scripts/export_opencode_session.py"
+vet "goal" --history-loader "python ~/.agents/skills/vet/scripts/export_opencode_session.py --session-id <session-uuid>"
```
**Codex:**
```bash
-export CODEX_SESSION_FILE="<path-to-current-session.jsonl>"
-vet "goal" --history-loader "python ~/.agents/skills/vet/scripts/export_codex_session.py"
+vet "goal" --history-loader "python ~/.codex/skills/vet/scripts/export_codex_session.py --session-file <path-to-session.jsonl>"
+```
+
+**Claude Code:**
+```bash
+vet "goal" --history-loader "python ~/.claude/skills/vet/scripts/export_claude_code_session.py --session-file <path-to-session.jsonl>"
```
### Finding Your Session
@@ -49,6 +52,8 @@ vet "goal" --history-loader "python ~/.agents/skills/vet/scripts/export_codex_se
**Codex:** Session files are stored in `~/.codex/sessions/YYYY/MM/DD/`. Find the most recently modified `.jsonl` file.
+**Claude Code:** Session files are stored in `~/.claude/projects/<encoded-path>/`. The encoded path replaces `/` with `-` (e.g. `/home/user/myproject` becomes `-home-user-myproject`). Find the most recently modified `.jsonl` file in the directory matching your project.
+
## Common Options
- `--base-commit REF`: Git ref for diff base (default: HEAD)
diff --git a/skills/vet/scripts/export_claude_code_session.py b/skills/vet/scripts/export_claude_code_session.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+import argparse
+import json
+import sys
+from pathlib import Path
+
+parser = argparse.ArgumentParser(description="Export Claude Code session history for vet")
+parser.add_argument("--session-file", required=True, help="Path to Claude Code session .jsonl file")
+args = parser.parse_args()
+
+SESSION_FILE = Path(args.session_file)
+if not SESSION_FILE.exists():
+ sys.exit(0)
+
+for line in 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
+
+ entry_type = entry.get("type")
+ if entry_type not in ("user", "assistant"):
+ continue
+
+ if entry.get("isSidechain"):
+ continue
+
+ message = entry.get("message", {})
+ content = message.get("content")
+
+ if entry_type == "user":
+ if isinstance(content, str) and content.strip():
+ print(json.dumps({"object_type": "ChatInputUserMessage", "text": content}))
+ elif isinstance(content, list):
+ text = " ".join(c.get("text", "") for c in content if isinstance(c, dict) and c.get("type") == "text")
+ if text.strip():
+ print(json.dumps({"object_type": "ChatInputUserMessage", "text": text}))
+ elif entry_type == "assistant":
+ if not isinstance(content, list):
+ continue
+ blocks = []
+ for c in content:
+ if isinstance(c, dict) and c.get("type") == "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": message.get("id", entry.get("uuid", "claude_code_msg")),
+ "content": blocks,
+ }
+ )
+ )
diff --git a/skills/vet/scripts/export_codex_session.py b/skills/vet/scripts/export_codex_session.py
@@ -1,18 +1,15 @@
#!/usr/bin/env python3
+import argparse
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))
+parser = argparse.ArgumentParser(description="Export Codex session history for vet")
+parser.add_argument("--session-file", required=True, help="Path to Codex session .jsonl file")
+args = parser.parse_args()
-if not SESSION_FILE or not Path(SESSION_FILE).exists():
+SESSION_FILE = args.session_file
+if not Path(SESSION_FILE).exists():
sys.exit(0)
for line in Path(SESSION_FILE).read_text().splitlines():
diff --git a/skills/vet/scripts/export_opencode_session.py b/skills/vet/scripts/export_opencode_session.py
@@ -1,15 +1,15 @@
#!/usr/bin/env python3
+import argparse
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)
+parser = argparse.ArgumentParser(description="Export OpenCode session history for vet")
+parser.add_argument("--session-id", required=True, help="OpenCode session UUID")
+args = parser.parse_args()
STORAGE = Path.home() / ".local/share/opencode/storage"
-MSG_DIR = STORAGE / "message" / SESSION_ID
+MSG_DIR = STORAGE / "message" / args.session_id
PART_DIR = STORAGE / "part"
if not MSG_DIR.exists():