@arcreflex/agent-transcripts 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Extract one-line summaries from tool call inputs.
3
+ */
4
+
5
+ function truncate(str: string, maxLen: number): string {
6
+ if (str.length <= maxLen) return str;
7
+ return str.slice(0, maxLen - 3) + "...";
8
+ }
9
+
10
+ type ToolInput = Record<string, unknown>;
11
+
12
+ const extractors: Record<string, (input: ToolInput) => string> = {
13
+ Read: (i) => String(i.file_path || ""),
14
+ Write: (i) => String(i.file_path || ""),
15
+ Edit: (i) => String(i.file_path || ""),
16
+ Bash: (i) => {
17
+ if (i.description) return String(i.description);
18
+ if (i.command) return truncate(String(i.command), 60);
19
+ return "";
20
+ },
21
+ Grep: (i) => {
22
+ const pattern = String(i.pattern || "");
23
+ const path = i.path ? ` in ${i.path}` : "";
24
+ return truncate(pattern + path, 80);
25
+ },
26
+ Glob: (i) => String(i.pattern || ""),
27
+ WebFetch: (i) => String(i.url || ""),
28
+ WebSearch: (i) => String(i.query || ""),
29
+ Task: (i) => truncate(String(i.description || i.prompt || ""), 60),
30
+ TodoWrite: () => "update todos",
31
+ AskUserQuestion: () => "ask user",
32
+ NotebookEdit: (i) => String(i.notebook_path || ""),
33
+ };
34
+
35
+ export function extractToolSummary(toolName: string, input: ToolInput): string {
36
+ const extractor = extractors[toolName];
37
+ if (extractor) {
38
+ const summary = extractor(input);
39
+ if (summary) return summary;
40
+ }
41
+ // Fallback: just use tool name
42
+ return "";
43
+ }
@@ -0,0 +1,4 @@
1
+ {"type": "user", "uuid": "msg-1", "message": {"role": "user", "content": "Hello, can you help me with a coding question?"}, "timestamp": "2024-01-15T10:00:00Z"}
2
+ {"type": "assistant", "uuid": "msg-2", "parentUuid": "msg-1", "message": {"role": "assistant", "content": [{"type": "text", "text": "Of course! I'd be happy to help. What's your question?"}]}, "timestamp": "2024-01-15T10:00:05Z"}
3
+ {"type": "user", "uuid": "msg-3", "parentUuid": "msg-2", "message": {"role": "user", "content": "How do I reverse a string in Python?"}, "timestamp": "2024-01-15T10:00:30Z"}
4
+ {"type": "assistant", "uuid": "msg-4", "parentUuid": "msg-3", "message": {"role": "assistant", "content": [{"type": "text", "text": "You can reverse a string in Python using slicing:\n\n```python\nreversed_string = original[::-1]\n```\n\nThe `[::-1]` slice notation means start from the end and step backwards."}]}, "timestamp": "2024-01-15T10:00:35Z"}
@@ -0,0 +1,28 @@
1
+ # Transcript
2
+
3
+ **Source**: `test/fixtures/claude/basic-conversation.input.jsonl`
4
+ **Adapter**: claude-code
5
+
6
+ ---
7
+
8
+ ## User
9
+
10
+ Hello, can you help me with a coding question?
11
+
12
+ ## Assistant
13
+
14
+ Of course! I'd be happy to help. What's your question?
15
+
16
+ ## User
17
+
18
+ How do I reverse a string in Python?
19
+
20
+ ## Assistant
21
+
22
+ You can reverse a string in Python using slicing:
23
+
24
+ ```python
25
+ reversed_string = original[::-1]
26
+ ```
27
+
28
+ The `[::-1]` slice notation means start from the end and step backwards.
@@ -0,0 +1,2 @@
1
+ {"type": "user", "uuid": "msg-1", "message": {"role": "user", "content": "Check the files"}, "timestamp": "2024-01-15T10:00:00Z"}
2
+ {"type": "assistant", "uuid": "msg-2", "parentUuid": "msg-1", "message": {"role": "assistant", "content": [{"type": "tool_use", "id": "tool-1", "name": "Read", "input": {"file_path": "/src/main.ts"}}, {"type": "tool_use", "id": "tool-2", "name": "Glob", "input": {"pattern": "**/*.test.ts"}}, {"type": "tool_use", "id": "tool-3", "name": "Bash", "input": {"command": "ls -la", "description": "List files"}}]}, "timestamp": "2024-01-15T10:00:02Z"}
@@ -0,0 +1,15 @@
1
+ # Transcript
2
+
3
+ **Source**: `test/fixtures/claude/multiple-tools.input.jsonl`
4
+ **Adapter**: claude-code
5
+
6
+ ---
7
+
8
+ ## User
9
+
10
+ Check the files
11
+
12
+ **Tools**:
13
+ - Read `/src/main.ts`
14
+ - Glob `**/*.test.ts`
15
+ - Bash `List files`
@@ -0,0 +1,2 @@
1
+ {"type": "user", "uuid": "msg-1", "message": {"role": "user", "content": "What's 17 * 23?"}, "timestamp": "2024-01-15T10:00:00Z"}
2
+ {"type": "assistant", "uuid": "msg-2", "parentUuid": "msg-1", "message": {"role": "assistant", "content": [{"type": "thinking", "thinking": "Let me calculate 17 * 23.\n17 * 23 = 17 * 20 + 17 * 3\n = 340 + 51\n = 391"}, {"type": "text", "text": "17 * 23 = 391"}]}, "timestamp": "2024-01-15T10:00:02Z"}
@@ -0,0 +1,24 @@
1
+ # Transcript
2
+
3
+ **Source**: `test/fixtures/claude/with-thinking.input.jsonl`
4
+ **Adapter**: claude-code
5
+
6
+ ---
7
+
8
+ ## User
9
+
10
+ What's 17 * 23?
11
+
12
+ ## Assistant
13
+
14
+
15
+ <details>
16
+ <summary>Thinking...</summary>
17
+
18
+ Let me calculate 17 * 23.
19
+ 17 * 23 = 17 * 20 + 17 * 3
20
+ = 340 + 51
21
+ = 391
22
+ </details>
23
+
24
+ 17 * 23 = 391
@@ -0,0 +1,2 @@
1
+ {"type": "user", "uuid": "msg-1", "message": {"role": "user", "content": "Read the config file"}, "timestamp": "2024-01-15T10:00:00Z"}
2
+ {"type": "assistant", "uuid": "msg-2", "parentUuid": "msg-1", "message": {"role": "assistant", "content": [{"type": "text", "text": "I'll read the config file for you."}, {"type": "tool_use", "id": "tool-1", "name": "Read", "input": {"file_path": "/project/config.json"}}]}, "timestamp": "2024-01-15T10:00:02Z"}
@@ -0,0 +1,16 @@
1
+ # Transcript
2
+
3
+ **Source**: `test/fixtures/claude/with-tools.input.jsonl`
4
+ **Adapter**: claude-code
5
+
6
+ ---
7
+
8
+ ## User
9
+
10
+ Read the config file
11
+
12
+ ## Assistant
13
+
14
+ I'll read the config file for you.
15
+
16
+ **Tool**: Read `/project/config.json`
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { join, dirname } from "path";
3
+ import { Glob } from "bun";
4
+
5
+ const fixturesDir = join(dirname(import.meta.path), "fixtures/claude");
6
+ const binPath = join(dirname(import.meta.path), "../bin/agent-transcripts");
7
+
8
+ // Find all input fixtures
9
+ const inputFiles: string[] = [];
10
+ const glob = new Glob("*.input.jsonl");
11
+ for await (const file of glob.scan(fixturesDir)) {
12
+ inputFiles.push(file);
13
+ }
14
+ inputFiles.sort();
15
+
16
+ describe("snapshot tests", () => {
17
+ for (const inputFile of inputFiles) {
18
+ const name = inputFile.replace(".input.jsonl", "");
19
+
20
+ it(name, async () => {
21
+ const inputPath = join(fixturesDir, inputFile);
22
+ const expectedPath = inputPath.replace(".input.jsonl", ".output.md");
23
+ // Use relative path for consistent Source: field in output
24
+ const relativeInputPath = `test/fixtures/claude/${inputFile}`;
25
+
26
+ const expectedOutput = await Bun.file(expectedPath).text();
27
+
28
+ // Run the CLI: parse to temp JSON, then render to temp MD
29
+ const tempJson = `/tmp/test-${name}-${Date.now()}.json`;
30
+ const tempMd = `/tmp/test-${name}-${Date.now()}.md`;
31
+
32
+ // Parse
33
+ const parseResult = Bun.spawnSync([
34
+ binPath,
35
+ "parse",
36
+ relativeInputPath,
37
+ "--adapter",
38
+ "claude-code",
39
+ "-o",
40
+ tempJson,
41
+ ]);
42
+ expect(parseResult.exitCode).toBe(0);
43
+
44
+ // Render
45
+ const renderResult = Bun.spawnSync([
46
+ binPath,
47
+ "render",
48
+ tempJson,
49
+ "-o",
50
+ tempMd,
51
+ ]);
52
+ expect(renderResult.exitCode).toBe(0);
53
+
54
+ // Compare output
55
+ const actualOutput = await Bun.file(tempMd).text();
56
+ expect(actualOutput.trimEnd()).toBe(expectedOutput.trimEnd());
57
+
58
+ // Cleanup
59
+ await Bun.file(tempJson)
60
+ .delete()
61
+ .catch(() => {});
62
+ await Bun.file(tempMd)
63
+ .delete()
64
+ .catch(() => {});
65
+ });
66
+ }
67
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "allowImportingTsExtensions": true,
7
+ "types": ["bun"],
8
+ "strict": true,
9
+ "skipLibCheck": true,
10
+ "noEmit": true,
11
+ "esModuleInterop": true,
12
+ "resolveJsonModule": true
13
+ },
14
+ "include": ["src/**/*"]
15
+ }