@abnersajr/claude-timeline 1.0.0

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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +227 -0
  3. package/dist/capture.js +140 -0
  4. package/dist/classifier.d.ts +37 -0
  5. package/dist/classifier.d.ts.map +1 -0
  6. package/dist/classifier.test.d.ts +2 -0
  7. package/dist/classifier.test.d.ts.map +1 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +1328 -0
  10. package/dist/context-tracker.d.ts +44 -0
  11. package/dist/context-tracker.d.ts.map +1 -0
  12. package/dist/context-tracker.test.d.ts +2 -0
  13. package/dist/context-tracker.test.d.ts.map +1 -0
  14. package/dist/conversation-groups.d.ts +11 -0
  15. package/dist/conversation-groups.d.ts.map +1 -0
  16. package/dist/conversation-groups.test.d.ts +2 -0
  17. package/dist/conversation-groups.test.d.ts.map +1 -0
  18. package/dist/cost-stream-capture.d.ts +47 -0
  19. package/dist/cost-stream-capture.d.ts.map +1 -0
  20. package/dist/cost-stream-db.d.ts +87 -0
  21. package/dist/cost-stream-db.d.ts.map +1 -0
  22. package/dist/cost-stream-merger.d.ts +38 -0
  23. package/dist/cost-stream-merger.d.ts.map +1 -0
  24. package/dist/db-reader-BrPRGqww.mjs +1028 -0
  25. package/dist/db-reader-BrPRGqww.mjs.map +1 -0
  26. package/dist/db-reader-CPXmkt55.mjs +2 -0
  27. package/dist/db-reader.d.ts +58 -0
  28. package/dist/db-reader.d.ts.map +1 -0
  29. package/dist/db.js +100 -0
  30. package/dist/dedup.d.ts +16 -0
  31. package/dist/dedup.d.ts.map +1 -0
  32. package/dist/dedup.test.d.ts +2 -0
  33. package/dist/dedup.test.d.ts.map +1 -0
  34. package/dist/index.d.ts +20 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/jsonl-parser.d.ts +14 -0
  37. package/dist/jsonl-parser.d.ts.map +1 -0
  38. package/dist/jsonl-parser.test.d.ts +2 -0
  39. package/dist/jsonl-parser.test.d.ts.map +1 -0
  40. package/dist/merger.d.ts +31 -0
  41. package/dist/merger.d.ts.map +1 -0
  42. package/dist/model-parser.d.ts +25 -0
  43. package/dist/model-parser.d.ts.map +1 -0
  44. package/dist/model-parser.test.d.ts +2 -0
  45. package/dist/model-parser.test.d.ts.map +1 -0
  46. package/dist/noise-filter.d.ts +6 -0
  47. package/dist/noise-filter.d.ts.map +1 -0
  48. package/dist/pricing-B-rwfwDB.mjs +2 -0
  49. package/dist/pricing-DTmya3JY.mjs +273 -0
  50. package/dist/pricing-DTmya3JY.mjs.map +1 -0
  51. package/dist/pricing.d.ts +26 -0
  52. package/dist/pricing.d.ts.map +1 -0
  53. package/dist/server.cjs +31237 -0
  54. package/dist/session-state.d.ts +19 -0
  55. package/dist/session-state.d.ts.map +1 -0
  56. package/dist/session-state.test.d.ts +2 -0
  57. package/dist/session-state.test.d.ts.map +1 -0
  58. package/dist/subagent-locator.d.ts +30 -0
  59. package/dist/subagent-locator.d.ts.map +1 -0
  60. package/dist/subagent-locator.test.d.ts +2 -0
  61. package/dist/subagent-locator.test.d.ts.map +1 -0
  62. package/dist/subagent-resolver.d.ts +35 -0
  63. package/dist/subagent-resolver.d.ts.map +1 -0
  64. package/dist/subagent-resolver.test.d.ts +2 -0
  65. package/dist/subagent-resolver.test.d.ts.map +1 -0
  66. package/dist/tool-extraction.d.ts +34 -0
  67. package/dist/tool-extraction.d.ts.map +1 -0
  68. package/dist/tool-extraction.test.d.ts +2 -0
  69. package/dist/tool-extraction.test.d.ts.map +1 -0
  70. package/dist/tool-matcher.d.ts +35 -0
  71. package/dist/tool-matcher.d.ts.map +1 -0
  72. package/dist/types.d.ts +272 -0
  73. package/dist/types.d.ts.map +1 -0
  74. package/dist/utils.d.ts +24 -0
  75. package/dist/utils.d.ts.map +1 -0
  76. package/dist/web/assets/index-Dr0FGYfS.js +158 -0
  77. package/dist/web/assets/index-nXTIEelb.css +1 -0
  78. package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  79. package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  80. package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  81. package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  82. package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  83. package/dist/web/favicon-light.svg +14 -0
  84. package/dist/web/favicon.svg +14 -0
  85. package/dist/web/index.html +14 -0
  86. package/dist/web/logo.svg +20 -0
  87. package/package.json +73 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 claude-timeline contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,227 @@
1
+ <p align="center">
2
+ <img src="packages/web/public/logo.svg" alt="claude-timeline" width="400" />
3
+ </p>
4
+
5
+ <p align="center">
6
+ <strong>Extract and visualize Claude Code session timelines</strong><br/>
7
+ Conversations · Tool calls · Pricing · Tokens · Subagents
8
+ </p>
9
+
10
+ <p align="center">
11
+ <a href="https://github.com/abnersajr/claude-timeline/actions"><img src="https://github.com/abnersajr/claude-timeline/workflows/CI/badge.svg" alt="CI" /></a>
12
+ <a href="https://www.npmjs.com/package/@abnersajr/claude-timeline"><img src="https://img.shields.io/npm/v/@abnersajr/claude-timeline" alt="npm version" /></a>
13
+ <a href="https://github.com/abnersajr/claude-timeline/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@abnersajr/claude-timeline" alt="license" /></a>
14
+ <a href="https://ko-fi.com/abnersajr"><img src="https://img.shields.io/badge/Ko--fi-Support%20the%20project-ff5e5b?logo=ko-fi&logoColor=white" alt="Ko-fi" /></a>
15
+ </p>
16
+
17
+ > **👋 About me:** I'm Abner — a dev from Brazil 🇧🇷 now living in Montreal 🍁. This project started because I was frustrated with existing tools and decided to vibe-code my own. It grew into something real, and now I'm sharing it with the community.
18
+ >
19
+ > **☕ Donations** will support continued development of this project, and a portion will be donated to an NGO that takes care of animals 🐾. If this tool saved you time, consider fueling the next feature!
20
+
21
+ ---
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install -g @abnersajr/claude-timeline
27
+ ```
28
+
29
+ Or run directly with npx (no install needed):
30
+
31
+ ```bash
32
+ npx @abnersajr/claude-timeline
33
+ ```
34
+
35
+ **Requirements:** Node.js >= 22
36
+
37
+ ## Usage
38
+
39
+ ### Web UI (recommended)
40
+
41
+ Start the server and open the interactive timeline in your browser:
42
+
43
+ ```bash
44
+ claude-timeline serve
45
+ ```
46
+
47
+ Opens `http://localhost:5199` automatically. Use `--port` to change the port:
48
+
49
+ ```bash
50
+ claude-timeline serve --port 3000
51
+ ```
52
+
53
+ ### CLI
54
+
55
+ List recent sessions:
56
+
57
+ ```bash
58
+ claude-timeline list
59
+ ```
60
+
61
+ Extract a specific session to JSON:
62
+
63
+ ```bash
64
+ claude-timeline extract --session-id <id>
65
+ ```
66
+
67
+ ### Cost Capture Setup
68
+
69
+ Get real-time per-turn cost tracking by installing the statusline wrapper:
70
+
71
+ ```bash
72
+ claude-timeline setup
73
+ ```
74
+
75
+ This hooks into Claude Code's statusline and captures token usage, pricing, and model data to a local SQLite database. The web UI reads this data for live cost streaming.
76
+
77
+ To uninstall, remove the `statusLine` key from `~/.claude/settings.json` and run:
78
+
79
+ ```bash
80
+ rm -rf ~/.claude-timeline/
81
+ ```
82
+
83
+ ## What is this?
84
+
85
+ Claude Code stores rich session data in local SQLite and JSONL files, but there's no built-in way to see the full picture. **claude-timeline** extracts, merges, and visualizes everything — conversations, tool calls, token usage, costs, and subagent activity.
86
+
87
+ ### Features
88
+
89
+ - 📊 **Full session timelines** — conversations, tool calls, file edits, all in order
90
+ - 💰 **Cost tracking** — per-turn and per-model pricing with token breakdowns
91
+ - 🤖 **Subagent detection** — automatically identifies and resolves subagent sessions
92
+ - 🧠 **Context analysis** — tracks context window usage, phases, and injections
93
+ - 🎨 **Web UI** — interactive timeline visualization with dark/light themes
94
+ - ⚡ **CLI tool** — extract session data from the command line
95
+
96
+ ## CLI Reference
97
+
98
+ ```
99
+ claude-timeline <command> [options]
100
+
101
+ Commands:
102
+ serve [--port <port>] Start web UI + API server (default: 5199)
103
+ extract --session-id <id> Extract a specific session to JSON
104
+ list List all available sessions
105
+ setup Install cost-capture statusline wrapper
106
+ update-pricing Fetch latest model pricing from Anthropic
107
+ --help Show help
108
+
109
+ Options:
110
+ --port <port> Server port (serve mode, default: 5199)
111
+ --db-path <path> SQLite DB path (default: ~/.claude/usage.db)
112
+ --projects-dir <path> Projects directory (default: ~/.claude/projects)
113
+ --output <path> Write JSON to file instead of stdout
114
+ ```
115
+
116
+ ## Understanding Claude Code Sessions
117
+
118
+ To get the most out of claude-timeline, it helps to understand how Claude Code works under the hood.
119
+
120
+ ### Core Concepts
121
+
122
+ | Concept | What it is |
123
+ |---------|------------|
124
+ | **Session** | A single Claude Code interaction lifecycle. Started when you launch `claude` or run a slash command. All work shares a persistent conversation history. |
125
+ | **Turn** | A single API call to the Claude model. Each turn includes cached context, new input, and model output. |
126
+ | **Token** | A unit of text (~4 English characters). All billing is token-based. "Hello world" is roughly 3 tokens. |
127
+ | **Prompt Caching** | Claude caches repeated context (system prompt, conversation history) to avoid re-processing. Cached context is billed at 10% of standard input rates. |
128
+
129
+ ### How Tokens Are Billed
130
+
131
+ Every Claude Code interaction uses four billable token types:
132
+
133
+ | Token Type | What it means | Cost (Sonnet 4) |
134
+ |------------|---------------|-----------------|
135
+ | **Input** | New text sent to the model that is NOT cached. Usually tiny (1-2 tokens). | $3.00 / MTok |
136
+ | **Output** | Model-generated text, tool requests, thinking. The most expensive type. | $15.00 / MTok |
137
+ | **Cache Creation** | One-time cost to write context to the cache. | $3.75 / MTok |
138
+ | **Cache Read** | Per-turn cost to retrieve cached context. Compounds over turns. | $0.30 / MTok |
139
+
140
+ > **Key insight**: `input_tokens` is NOT the total input. It's the non-cached delta (typically 1-2 tokens). The bulk of your context is in `cache_read_input_tokens`.
141
+
142
+ ### How Cache Read Compounds
143
+
144
+ Each turn reads the entire cached context (system prompt + all previous conversation). As the conversation grows, so does cache read:
145
+
146
+ | Session Length | Avg Cache Read/Turn | Total Cache Read | Cache Read Cost |
147
+ |----------------|---------------------|------------------|-----------------|
148
+ | 1 turn | 12k | 12k | ~$0.004 |
149
+ | 10 turns | 34k | 340k | ~$0.10 |
150
+ | 28 turns | 33k | 929k | ~$0.28 |
151
+ | 100 turns | 35k | 3.5M | ~$1.05 |
152
+
153
+ The 929k number sounds large, but at $0.30/MTok it costs less than a quarter. **Output tokens are 5x more expensive** than cache read.
154
+
155
+ ### How Tool Calls Affect Cost
156
+
157
+ Tool results (success or failure) are added to conversation history. Large tool outputs compound:
158
+
159
+ - Reading a 100k-token file in Turn 5 adds 100k tokens to history
160
+ - Turns 6-28 each read that extra 100k in cache
161
+ - Cost: 23 turns × 100k × $0.30/MTok = ~$0.69 extra
162
+
163
+ This is why targeted searches (`grep "ERROR" app.log`) are cheaper than reading entire files (`cat app.log`).
164
+
165
+ ## Tips for Efficient Sessions
166
+
167
+ | Strategy | Impact |
168
+ |----------|--------|
169
+ | **Start in the project directory** | `cd ~/projects/my-app && claude` instead of `cd ~ && claude "fix X in my-app"`. Broader scope = more file discovery = more cache bloat. |
170
+ | **Use exact file paths** | "Edit `src/components/Button.tsx`" instead of "edit the button component". Reduces discovery turns. |
171
+ | **Batch related tasks** | "Fix X, Y, Z" in one session vs three separate sessions. Fewer turns = less cumulative cache read. |
172
+ | **Keep sessions under 15 turns** | After 10-15 turns, restart to reset cache accumulation. A 10-turn session costs ~$0.18 vs ~$0.63 for 28 turns. |
173
+ | **Watch tool output sizes** | Use `head`, `tail`, `grep` to limit output. A 100k-token file read adds ~$0.69 in cache bloat over 28 turns. |
174
+ | **Use `.claudeignore`** | Exclude `node_modules/`, `dist/`, `*.log` from discovery to prevent unrelated files from entering context. |
175
+
176
+ ## Data Sources
177
+
178
+ | Source | Location | Contents |
179
+ |--------|----------|----------|
180
+ | SQLite | `~/.claude/usage.db` | Sessions, turns, token counts |
181
+ | JSONL | `~/.claude/projects/<project>/<session>.jsonl` | Full message content, tool calls, file paths |
182
+
183
+ ## Troubleshooting
184
+
185
+ ### Find your recent sessions
186
+
187
+ ```bash
188
+ sqlite3 ~/.claude/usage.db "SELECT session_id, project_name, total_cache_read, turn_count, last_timestamp FROM sessions WHERE last_timestamp > datetime('now', '-1 day') ORDER BY total_cache_read DESC LIMIT 5;"
189
+ ```
190
+
191
+ ### Get full token breakdown for a session
192
+
193
+ ```bash
194
+ sqlite3 ~/.claude/usage.db "SELECT timestamp, cache_read_tokens, cache_creation_tokens, input_tokens, output_tokens, tool_name FROM turns WHERE session_id='YOUR_SESSION_ID' ORDER BY timestamp;"
195
+ ```
196
+
197
+ ### Calculate session cost (Sonnet 4 rates)
198
+
199
+ ```bash
200
+ sqlite3 ~/.claude/usage.db "SELECT
201
+ session_id,
202
+ ROUND(total_cache_read * 0.30 / 1000000, 4) AS cache_read_USD,
203
+ ROUND(total_output_tokens * 15.00 / 1000000, 4) AS output_USD,
204
+ ROUND(total_cache_creation * 3.75 / 1000000, 4) AS creation_USD,
205
+ ROUND(total_input_tokens * 3.00 / 1000000, 4) AS input_USD,
206
+ ROUND((total_cache_read * 0.30 + total_output_tokens * 15.00 + total_cache_creation * 3.75 + total_input_tokens * 3.00) / 1000000, 4) AS total_USD
207
+ FROM sessions WHERE session_id='YOUR_SESSION_ID';"
208
+ ```
209
+
210
+ ### Check which files were read in a session
211
+
212
+ ```bash
213
+ cat ~/.claude/projects/YOUR_PROJECT_DIR/YOUR_SESSION_ID.jsonl | jq -r 'select(.type == "assistant" and .message.content) | .message.content[]? | select(.type == "tool_use" and .name == "Read") | .input.filePath' 2>/dev/null
214
+ ```
215
+
216
+ ## Development
217
+
218
+ ```bash
219
+ git clone https://github.com/abnersajr/claude-timeline.git
220
+ cd claude-timeline
221
+ pnpm install
222
+ pnpm dev
223
+ ```
224
+
225
+ ## License
226
+
227
+ [MIT](LICENSE)
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { execSync } from "node:child_process";
6
+ import { getDb, upsertSnapshot, upsertSessionSummary } from "./db.js";
7
+
8
+ const CONFIG_PATH = join(process.env.HOME, ".claude-timeline", "config.json");
9
+
10
+ function loadConfig() {
11
+ try {
12
+ return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
13
+ } catch {
14
+ return {};
15
+ }
16
+ }
17
+
18
+ function parseStdin() {
19
+ return new Promise((resolve) => {
20
+ if (process.stdin.isTTY) return resolve(null);
21
+
22
+ let raw = "";
23
+ let settled = false;
24
+ let firstByteTimer;
25
+ let idleTimer;
26
+
27
+ const cleanup = () => {
28
+ clearTimeout(firstByteTimer);
29
+ clearTimeout(idleTimer);
30
+ process.stdin.off("data", onData);
31
+ process.stdin.off("end", onEnd);
32
+ process.stdin.pause();
33
+ };
34
+
35
+ const finish = (value) => {
36
+ if (settled) return;
37
+ settled = true;
38
+ cleanup();
39
+ resolve(value);
40
+ };
41
+
42
+ const tryParse = () => {
43
+ const trimmed = raw.trim();
44
+ if (!trimmed) return null;
45
+ try { return JSON.parse(trimmed); } catch { return undefined; }
46
+ };
47
+
48
+ const onData = (chunk) => {
49
+ clearTimeout(firstByteTimer);
50
+ raw += String(chunk);
51
+ const parsed = tryParse();
52
+ if (parsed !== undefined) return finish(parsed);
53
+ clearTimeout(idleTimer);
54
+ idleTimer = setTimeout(() => finish(tryParse() ?? null), 30);
55
+ };
56
+
57
+ const onEnd = () => finish(tryParse() ?? null);
58
+
59
+ process.stdin.setEncoding("utf8");
60
+ process.stdin.on("data", onData);
61
+ process.stdin.on("end", onEnd);
62
+ firstByteTimer = setTimeout(() => finish(null), 250);
63
+ });
64
+ }
65
+
66
+ function extractCostData(json) {
67
+ const cost = json.cost ?? {};
68
+ const ctx = json.context_window ?? {};
69
+ const current = ctx.current_usage ?? {};
70
+
71
+ return {
72
+ session_id: json.session_id ?? null,
73
+ total_cost_usd: typeof cost.total_cost_usd === "number" ? cost.total_cost_usd : null,
74
+ duration_ms: cost.total_duration_ms ?? null,
75
+ api_duration_ms: cost.total_api_duration_ms ?? null,
76
+ input_tokens: ctx.total_input_tokens ?? current.input_tokens ?? 0,
77
+ output_tokens: ctx.total_output_tokens ?? current.output_tokens ?? 0,
78
+ cache_read_tokens: current.cache_read_input_tokens ?? 0,
79
+ cache_creation_tokens: current.cache_creation_input_tokens ?? 0,
80
+ model: json.model?.display_name ?? json.model?.id ?? null,
81
+ lines_added: cost.total_lines_added ?? 0,
82
+ lines_removed: cost.total_lines_removed ?? 0,
83
+ };
84
+ }
85
+
86
+ function killOtherInstances() {
87
+ try {
88
+ const myPid = process.pid;
89
+ const pids = execSync("pgrep -f 'node.*capture\\.js'", { encoding: "utf-8" })
90
+ .trim().split("\n").filter(Boolean).map(Number).filter((p) => p !== myPid);
91
+ for (const pid of pids) {
92
+ try { process.kill(pid, "SIGTERM"); } catch {}
93
+ }
94
+ } catch {}
95
+ }
96
+
97
+ async function main() {
98
+ killOtherInstances();
99
+ const data = await parseStdin();
100
+ if (!data?.session_id) {
101
+ process.exit(0);
102
+ }
103
+
104
+ const costData = extractCostData(data);
105
+
106
+ try {
107
+ const db = getDb();
108
+ upsertSnapshot(db, { ...costData, raw_json: JSON.stringify(data) });
109
+ if (costData.total_cost_usd !== null) {
110
+ upsertSessionSummary(db, {
111
+ session_id: costData.session_id,
112
+ total_cost_usd: costData.total_cost_usd,
113
+ model: costData.model,
114
+ });
115
+ }
116
+ db.close();
117
+ } catch (e) {
118
+ process.stderr.write(`[cost-capture] DB error: ${e.message}\n`);
119
+ }
120
+
121
+ const config = loadConfig();
122
+ if (config.originalStatusLine?.command) {
123
+ try {
124
+ const { spawnSync } = await import("node:child_process");
125
+ const result = spawnSync("sh", ["-c", config.originalStatusLine.command], {
126
+ input: JSON.stringify(data),
127
+ encoding: "utf-8",
128
+ timeout: 2000,
129
+ env: { ...process.env, COLUMNS: process.env.COLUMNS ?? "80" },
130
+ });
131
+ if (result.stdout) process.stdout.write(result.stdout);
132
+ } catch {
133
+ // Original statusline failed — not our problem
134
+ }
135
+ }
136
+
137
+ process.exit(0);
138
+ }
139
+
140
+ main();
@@ -0,0 +1,37 @@
1
+ import type { ClassifiedMessage, MessageCategory, RawJsonlRecord } from "./types";
2
+ /**
3
+ * Hard noise: system/summary/file-history-snapshot/queue-operation/attachment/last-prompt/permission-mode types,
4
+ * sidechain, synthetic assistant, hard noise tags, interruptions.
5
+ */
6
+ export declare function isHardNoise(record: RawJsonlRecord): boolean;
7
+ /** Compact messages are marked by isCompactSummary flag */
8
+ export declare function isCompactMessage(record: RawJsonlRecord): boolean;
9
+ /**
10
+ * System messages: user-type messages that contain command output
11
+ * (local-command-stdout/stderr). These arrive as type="user" in JSONL
12
+ * but represent command output, not user input.
13
+ */
14
+ export declare function isSystemMessage(record: RawJsonlRecord): boolean;
15
+ /**
16
+ * User messages: type=user, isMeta=false, has text/image content
17
+ * (not just tool_result blocks). Meta messages (tool results) are
18
+ * classified as assistant because they represent assistant context.
19
+ * Tool-result-only records (isMeta=null, content is array of tool_result)
20
+ * are also classified as assistant — they're CLI tool outputs, not user input.
21
+ */
22
+ export declare function isUserMessage(record: RawJsonlRecord): boolean;
23
+ /**
24
+ * Classify a single JSONL record into a category using the priority cascade:
25
+ * 1. hardNoise — noise types, sidechain, synthetic, hard noise tags, interruptions
26
+ * 2. compact — isCompactSummary === true
27
+ * 3. system — user messages with command output (local-command-stdout/stderr)
28
+ * 4. user — type=user, not meta, has text/image content
29
+ * 5. assistant — everything else (catch-all)
30
+ */
31
+ export declare function classifyMessage(record: RawJsonlRecord): MessageCategory;
32
+ /**
33
+ * Classify an array of messages, returning ClassifiedMessage objects
34
+ * that pair each record with its category.
35
+ */
36
+ export declare function classifyMessages(records: RawJsonlRecord[]): ClassifiedMessage[];
37
+ //# sourceMappingURL=classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.d.ts","sourceRoot":"","sources":["../src/classifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AA4CjF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAwB3D;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAK/D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAe7D;AAID;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CAMvE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,iBAAiB,EAAE,CAK/E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=classifier.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.test.d.ts","sourceRoot":"","sources":["../src/classifier.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}