@arcreflex/agent-transcripts 0.1.9 → 0.1.11

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.
package/CLAUDE.md CHANGED
@@ -4,7 +4,9 @@
4
4
 
5
5
  ## Architectural Notes
6
6
 
7
- - **Source paths are stable**: Session source paths (e.g., `~/.claude/projects/.../sessions/`) are standardized by the tools that create them. Don't over-engineer for path changes—use source paths as cache keys directly.
7
+ - **Source paths are stable**: Session source paths (e.g., `~/.claude/projects/.../sessions/`) are standardized by the tools that create them. Archive entries store the absolute source path for traceability.
8
+ - **Archive is the central store**: All derived data (titles, etc.) lives on archive entries. Rendered HTML is in-memory only (LRU in serve). No persistent cache layer.
9
+ - **Serve is snapshot-based**: `serve` loads the archive once at startup. It does not live-reload when `watch` archives new sessions. This is a known simplification — revisit if live-updating becomes important.
8
10
 
9
11
  ## Verification
10
12
 
package/README.md CHANGED
@@ -17,30 +17,35 @@ src/
17
17
  render.ts # Intermediate format → markdown
18
18
  render-html.ts # HTML transcript rendering
19
19
  render-index.ts # Index page rendering
20
- convert.ts # Full pipeline with provenance tracking
21
- sync.ts # Batch sync sessions → markdown
22
- serve.ts # HTTP server for dynamic transcript serving
23
- cache.ts # Content-hash-based caching (~/.cache/agent-transcripts/)
24
- title.ts # LLM title generation
20
+ convert.ts # Direct pipeline (parse render to stdout or directory)
21
+ archive.ts # Persistent archive store (~/.local/share/agent-transcripts/archive/)
22
+ watch.ts # Continuous archive updates via fs.watch + polling
23
+ serve.ts # HTTP server serving from archive with in-memory LRU
24
+ title.ts # LLM title generation (writes to archive entries)
25
25
  types.ts # Core types (Transcript, Message, Adapter)
26
26
  adapters/ # Source format adapters (currently: claude-code)
27
27
  utils/
28
28
  naming.ts # Deterministic output file naming
29
- provenance.ts # Source tracking via transcripts.json + YAML front matter
30
29
  summary.ts # Tool call summary extraction
31
30
  openrouter.ts # OpenRouter API client for title generation
32
31
  html.ts # HTML escaping utility
33
- tree.ts # Tree navigation utilities
32
+ tree.ts # Tree navigation and walkTranscriptTree generator
33
+ text.ts # Shared text utilities (truncate)
34
+ theme.ts # Shared CSS theme constants
34
35
  test/
35
36
  fixtures/ # Snapshot test inputs/outputs
36
37
  snapshots.test.ts
38
+ archive.test.ts
39
+ tree.test.ts
40
+ naming.test.ts
41
+ summary.test.ts
37
42
  ```
38
43
 
39
44
  ## Commands
40
45
 
41
46
  ```bash
42
47
  bun run check # typecheck + prettier
43
- bun run test # snapshot tests
48
+ bun run test # snapshot tests + archive tests
44
49
  bun run format # auto-format
45
50
  ```
46
51
 
@@ -50,9 +55,23 @@ bun run format # auto-format
50
55
  # Subcommands (convert is default if omitted)
51
56
  agent-transcripts convert <file> # Parse and render to stdout
52
57
  agent-transcripts convert <file> -o <dir> # Parse and render to directory
53
- agent-transcripts sync <dir> -o <out> # Batch sync sessions
54
- agent-transcripts serve <dir> # Serve transcripts via HTTP
55
- agent-transcripts serve <dir> -p 8080 # Serve on custom port
58
+
59
+ # Archive management
60
+ agent-transcripts archive <source> # Archive sessions from source dir
61
+ agent-transcripts archive <source> --archive-dir ~/my-archive
62
+
63
+ # Serving
64
+ agent-transcripts serve # Serve from default archive
65
+ agent-transcripts serve --archive-dir <dir> # Serve from custom archive
66
+ agent-transcripts serve -p 8080 # Custom port
67
+
68
+ # Watching
69
+ agent-transcripts watch <source> # Keep archive updated continuously
70
+ agent-transcripts watch <source> --poll-interval 60000
71
+
72
+ # Title generation
73
+ agent-transcripts title # Generate titles for archive entries
74
+ agent-transcripts title -f # Force regenerate all titles
56
75
 
57
76
  # Use "-" for stdin
58
77
  cat session.jsonl | agent-transcripts -
@@ -60,67 +79,61 @@ cat session.jsonl | agent-transcripts -
60
79
 
61
80
  ## Architecture
62
81
 
63
- Two-stage pipeline: Parse (source → intermediate) → Render (intermediate → markdown).
82
+ ```
83
+ Source (Claude Code sessions)
84
+ ↓ [archive / watch]
85
+ Archive (~/.local/share/agent-transcripts/archive/{sessionId}.json)
86
+ ↓ [serve]
87
+ HTML (rendered on demand, in-memory LRU)
88
+ ```
89
+
90
+ `convert` is a standalone direct pipeline (no archive dependency).
91
+
92
+ `serve` loads the archive once at startup — it won't pick up new sessions archived by a concurrent `watch` without a restart. Live-reloading could be added later (periodic re-listing or file-watch trigger) if needed.
64
93
 
65
94
  - Adapters handle source formats (see `src/adapters/index.ts` for registry)
66
95
  - Auto-detection: paths containing `.claude/` → claude-code adapter
67
96
  - Branching conversations preserved via `parentMessageRef` on messages
68
- - Provenance tracking via `transcripts.json` index + YAML front matter
69
97
  - Deterministic naming: `{datetime}-{sessionId}.md`
70
- - Sync uses sessions-index.json for discovery (claude-code), skipping subagent files
71
- - Sync uses content hash to skip unchanged sources (see Cache section)
72
98
 
73
- ### Cache
99
+ ### Archive
74
100
 
75
- Derived content (rendered outputs, LLM-generated titles) is cached at `~/.cache/agent-transcripts/`:
101
+ The archive is the central data store at `~/.local/share/agent-transcripts/archive/`:
76
102
 
77
103
  ```
78
- ~/.cache/agent-transcripts/
79
- {source-path-hash}.json → CacheEntry
104
+ ~/.local/share/agent-transcripts/archive/
105
+ {sessionId}.json → ArchiveEntry
80
106
  ```
81
107
 
82
108
  ```typescript
83
- interface CacheEntry {
84
- contentHash: string; // hash of source content (invalidation key)
85
- segments: Array<{
86
- title?: string; // LLM-generated title
87
- html?: string; // rendered HTML
88
- md?: string; // rendered markdown
89
- }>;
109
+ interface ArchiveEntry {
110
+ sessionId: string;
111
+ sourcePath: string; // absolute source path
112
+ sourceHash: string; // content hash (invalidation key)
113
+ adapterName: string;
114
+ adapterVersion: string; // e.g. "claude-code:1"
115
+ schemaVersion: number;
116
+ archivedAt: string; // ISO timestamp
117
+ title?: string; // harness-provided or LLM-generated
118
+ transcripts: Transcript[];
90
119
  }
91
120
  ```
92
121
 
93
- Cache is keyed by source path (hashed), invalidated by content hash. When source content changes, all cached data is invalidated and regenerated.
94
-
95
- ### transcripts.json
96
-
97
- The index file is a table of contents for the output directory:
98
-
99
- ```typescript
100
- interface TranscriptsIndex {
101
- version: 1;
102
- entries: {
103
- [outputFilename: string]: {
104
- source: string; // absolute path to source
105
- sessionId: string; // full session ID from filename
106
- segmentIndex?: number; // for multi-transcript sources (1-indexed)
107
- syncedAt: string; // ISO timestamp
108
- firstUserMessage: string; // first user message content
109
- title?: string; // copied from cache for convenience
110
- messageCount: number;
111
- startTime: string; // ISO timestamp
112
- endTime: string; // ISO timestamp
113
- cwd?: string; // working directory
114
- };
115
- };
116
- }
117
- ```
122
+ Freshness is determined by `sourceHash + adapterVersion + schemaVersion`. When any changes, the entry is re-archived.
118
123
 
119
124
  ## Key Types
120
125
 
121
126
  - `Transcript`: source info, warnings, messages array
122
127
  - `Message`: union of UserMessage | AssistantMessage | SystemMessage | ToolCallGroup | ErrorMessage
123
- - `Adapter`: name, discover function, parse function
128
+ - `Adapter`: name, version, discover function, parse function
129
+
130
+ ### Titles
131
+
132
+ Transcripts get titles from (in priority order):
133
+
134
+ 1. Harness-provided summary (e.g., Claude Code's sessions-index.json `summary` field)
135
+ 2. Existing title from previous archive entry
136
+ 3. LLM-generated title via OpenRouter (requires `OPENROUTER_API_KEY`)
124
137
 
125
138
  ## Adding an Adapter
126
139
 
@@ -128,8 +141,14 @@ interface TranscriptsIndex {
128
141
  2. Register in `src/adapters/index.ts` (adapters map + detection rules)
129
142
  3. Add test fixtures in `test/fixtures/<name>/`
130
143
 
144
+ ## Development Scripts
145
+
146
+ - `scripts/infer-cc-types.prose`: open-prose program to infer types from real CC session data
147
+
131
148
  ## Tests
132
149
 
133
150
  Snapshot-based: `*.input.jsonl` → parse → render → compare against `*.output.md`
134
151
 
152
+ Archive tests: real fixture files + temp dirs to verify archiving, freshness, listing.
153
+
135
154
  To update snapshots: manually edit the expected `.output.md` files.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcreflex/agent-transcripts",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Transform AI coding agent session files into readable transcripts",
5
5
  "type": "module",
6
6
  "repository": {
@@ -0,0 +1,87 @@
1
+ # Infer TypeScript types from Claude Code session data
2
+ #
3
+ # Usage: prose run scripts/infer-cc-types.prose
4
+ #
5
+ # Examines real CC session directories and updates the type definitions
6
+ # in src/adapters/claude-code.ts to match the current data format.
7
+
8
+ # Agent that synthesizes TypeScript types from JSON examples
9
+ agent type-inferrer:
10
+ model: sonnet
11
+ prompt: """
12
+ You analyze JSON data samples and synthesize TypeScript type definitions.
13
+
14
+ Your output should be:
15
+ - Clean, minimal TypeScript interfaces
16
+ - JSDoc comments explaining non-obvious fields
17
+ - Optional fields marked with ?
18
+ - Union types where the data shows multiple shapes
19
+ - `unknown` (never `any`) for truly dynamic data
20
+
21
+ When you see multiple examples, infer which fields are always present
22
+ (required) vs sometimes present (optional).
23
+ """
24
+
25
+ # Discover CC session data locations (once)
26
+ let discovery = session "Find CC session data"
27
+ model: sonnet
28
+ prompt: """
29
+ Find Claude Code session data on this system.
30
+
31
+ 1. Search for sessions-index.json files under ~/.claude
32
+ 2. For each, note the directory path and count of .jsonl files
33
+
34
+ Return a structured list of what you found.
35
+ If nothing found, say so clearly.
36
+ """
37
+
38
+ # Iterate: infer types → implement → check → repeat if needed
39
+ let feedback = "(no feedback)"
40
+
41
+ loop until **feedback indicates success** (max: 5):
42
+ # Parallel type inference
43
+ parallel:
44
+ index_types = session: type-inferrer
45
+ prompt: """
46
+ Analyze the sessions-index.json files from the discovered locations.
47
+
48
+ Generate TypeScript interfaces for:
49
+ - SessionsIndex (the root object)
50
+ - SessionIndexEntry (each entry in the entries array)
51
+ """
52
+ context: { discovery, feedback }
53
+
54
+ record_types = session: type-inferrer
55
+ prompt: """
56
+ Analyze Claude Code session .jsonl files (sample 20-30 lines from a few files).
57
+
58
+ Generate TypeScript types describing JSONL records.
59
+ - ClaudeRecord (the JSONL line structure)
60
+ - ContentBlock (the message.content array elements)
61
+ """
62
+ context: { discovery, feedback }
63
+
64
+ # Implement and verify
65
+ feedback = session "Update adapter types"
66
+ model: opus
67
+ prompt: """
68
+ Update the type definitions in src/adapters/claude-code.ts to match these inferred types.
69
+
70
+ ## Inferred from sessions-index.json
71
+ {index_types}
72
+
73
+ ## Inferred from JSONL records
74
+ {record_types}
75
+
76
+ ## Task
77
+
78
+ 1. Read the current types in src/adapters/claude-code.ts
79
+ 2. Edit to match the inferred types (add new fields, fix types, etc.)
80
+ 3. Run: bun run typecheck
81
+
82
+ If typecheck succeeds and you're happy with the types, output a message indicating success.
83
+ If typecheck fails, assess whether the failures reflect a real need to change the code or a flaw in the type design.
84
+ If yes, and you're happy with the types, make the changes and output a message indicating success.
85
+ Otherwise: output feedback requesting revisions to the type design.
86
+ """
87
+ context: { index_types, record_types }