|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 | """Parse a Claude Code NDJSON session log into per-turn files. |
3 | 3 |
|
4 | | -Filters to: user prompts, assistant text replies, and XcodeBuildMCP tool |
| 4 | +Filters to: user prompts, assistant text replies, and configured tool |
5 | 5 | calls/results. Strips screenshot image blobs to a short placeholder. |
6 | 6 |
|
7 | 7 | Usage: |
8 | 8 | parse_claude_conversation.py <session.jsonl> [output_dir] \\ |
9 | | - [--tool-prefix=mcp__xcodebuildmcp] |
| 9 | + [--tool-prefix=mcp__xcodebuildmcp] [--tool-name=Bash] |
10 | 10 | """ |
11 | 11 |
|
12 | 12 | from __future__ import annotations |
@@ -101,10 +101,14 @@ def extract_user_text(entry: dict) -> str: |
101 | 101 | return "\n\n".join(parts) |
102 | 102 |
|
103 | 103 |
|
104 | | -def parse(path: Path, out_dir: Path, tool_prefix: str) -> bool: |
| 104 | +def matches_tool_name(name: str, tool_prefixes: list[str], tool_names: set[str]) -> bool: |
| 105 | + return name in tool_names or any(name.startswith(prefix) for prefix in tool_prefixes) |
| 106 | + |
| 107 | + |
| 108 | +def parse(path: Path, out_dir: Path, tool_prefixes: list[str], tool_names: set[str]) -> bool: |
105 | 109 | out_dir.mkdir(parents=True, exist_ok=True) |
106 | 110 |
|
107 | | - # Track tool_use_ids that target our prefix so we keep matching results. |
| 111 | + # Track tool_use_ids that target configured tools so we keep matching results. |
108 | 112 | tracked_ids: set[str] = set() |
109 | 113 | tool_name_by_id: dict[str, str] = {} |
110 | 114 | counter = 0 |
@@ -185,7 +189,7 @@ def next_path(kind: str, label: str | None = None) -> Path: |
185 | 189 | ) |
186 | 190 | elif btype == "tool_use": |
187 | 191 | name = block.get("name", "") |
188 | | - if not name.startswith(tool_prefix): |
| 192 | + if not matches_tool_name(name, tool_prefixes, tool_names): |
189 | 193 | continue |
190 | 194 | tool_id = block.get("id", "") |
191 | 195 | tracked_ids.add(tool_id) |
@@ -220,17 +224,25 @@ def main() -> int: |
220 | 224 | ) |
221 | 225 | ap.add_argument( |
222 | 226 | "--tool-prefix", |
223 | | - default="mcp__xcodebuildmcp", |
| 227 | + action="append", |
| 228 | + default=None, |
224 | 229 | help="Only include tool calls whose name starts with this prefix", |
225 | 230 | ) |
| 231 | + ap.add_argument( |
| 232 | + "--tool-name", |
| 233 | + action="append", |
| 234 | + default=[], |
| 235 | + help="Also include tool calls whose name exactly matches this value", |
| 236 | + ) |
226 | 237 | args = ap.parse_args() |
227 | 238 |
|
228 | 239 | if not args.jsonl.is_file(): |
229 | 240 | print(f"error: not a file: {args.jsonl}", file=sys.stderr) |
230 | 241 | return 1 |
231 | 242 |
|
232 | 243 | out = args.output or args.jsonl.with_name(f"{args.jsonl.stem}_conversation") |
233 | | - return 0 if parse(args.jsonl, out, args.tool_prefix) else 1 |
| 244 | + tool_prefixes = args.tool_prefix or ["mcp__xcodebuildmcp"] |
| 245 | + return 0 if parse(args.jsonl, out, tool_prefixes, set(args.tool_name)) else 1 |
234 | 246 |
|
235 | 247 |
|
236 | 248 | if __name__ == "__main__": |
|
0 commit comments