zozul-cli 0.1.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 (139) hide show
  1. package/.env.example +44 -0
  2. package/.github/workflows/publish.yml +26 -0
  3. package/DEVELOPMENT.md +288 -0
  4. package/LICENSE +201 -0
  5. package/README.md +178 -0
  6. package/dist/cli/commands.d.ts +3 -0
  7. package/dist/cli/commands.d.ts.map +1 -0
  8. package/dist/cli/commands.js +307 -0
  9. package/dist/cli/commands.js.map +1 -0
  10. package/dist/cli/format.d.ts +5 -0
  11. package/dist/cli/format.d.ts.map +1 -0
  12. package/dist/cli/format.js +115 -0
  13. package/dist/cli/format.js.map +1 -0
  14. package/dist/context/index.d.ts +8 -0
  15. package/dist/context/index.d.ts.map +1 -0
  16. package/dist/context/index.js +37 -0
  17. package/dist/context/index.js.map +1 -0
  18. package/dist/dashboard/html.d.ts +17 -0
  19. package/dist/dashboard/html.d.ts.map +1 -0
  20. package/dist/dashboard/html.js +79 -0
  21. package/dist/dashboard/html.js.map +1 -0
  22. package/dist/dashboard/index.html +1245 -0
  23. package/dist/hooks/config.d.ts +19 -0
  24. package/dist/hooks/config.d.ts.map +1 -0
  25. package/dist/hooks/config.js +106 -0
  26. package/dist/hooks/config.js.map +1 -0
  27. package/dist/hooks/git.d.ts +6 -0
  28. package/dist/hooks/git.d.ts.map +1 -0
  29. package/dist/hooks/git.js +73 -0
  30. package/dist/hooks/git.js.map +1 -0
  31. package/dist/hooks/index.d.ts +4 -0
  32. package/dist/hooks/index.d.ts.map +1 -0
  33. package/dist/hooks/index.js +3 -0
  34. package/dist/hooks/index.js.map +1 -0
  35. package/dist/hooks/server.d.ts +16 -0
  36. package/dist/hooks/server.d.ts.map +1 -0
  37. package/dist/hooks/server.js +349 -0
  38. package/dist/hooks/server.js.map +1 -0
  39. package/dist/index.d.ts +3 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +6 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/otel/config.d.ts +36 -0
  44. package/dist/otel/config.d.ts.map +1 -0
  45. package/dist/otel/config.js +109 -0
  46. package/dist/otel/config.js.map +1 -0
  47. package/dist/otel/index.d.ts +4 -0
  48. package/dist/otel/index.d.ts.map +1 -0
  49. package/dist/otel/index.js +3 -0
  50. package/dist/otel/index.js.map +1 -0
  51. package/dist/otel/receiver.d.ts +10 -0
  52. package/dist/otel/receiver.d.ts.map +1 -0
  53. package/dist/otel/receiver.js +155 -0
  54. package/dist/otel/receiver.js.map +1 -0
  55. package/dist/parser/index.d.ts +4 -0
  56. package/dist/parser/index.d.ts.map +1 -0
  57. package/dist/parser/index.js +3 -0
  58. package/dist/parser/index.js.map +1 -0
  59. package/dist/parser/ingest.d.ts +20 -0
  60. package/dist/parser/ingest.d.ts.map +1 -0
  61. package/dist/parser/ingest.js +98 -0
  62. package/dist/parser/ingest.js.map +1 -0
  63. package/dist/parser/jsonl.d.ts +14 -0
  64. package/dist/parser/jsonl.d.ts.map +1 -0
  65. package/dist/parser/jsonl.js +202 -0
  66. package/dist/parser/jsonl.js.map +1 -0
  67. package/dist/parser/types.d.ts +81 -0
  68. package/dist/parser/types.d.ts.map +1 -0
  69. package/dist/parser/types.js +9 -0
  70. package/dist/parser/types.js.map +1 -0
  71. package/dist/parser/watcher.d.ts +16 -0
  72. package/dist/parser/watcher.d.ts.map +1 -0
  73. package/dist/parser/watcher.js +103 -0
  74. package/dist/parser/watcher.js.map +1 -0
  75. package/dist/pricing/index.d.ts +2 -0
  76. package/dist/pricing/index.d.ts.map +1 -0
  77. package/dist/pricing/index.js +37 -0
  78. package/dist/pricing/index.js.map +1 -0
  79. package/dist/service/index.d.ts +31 -0
  80. package/dist/service/index.d.ts.map +1 -0
  81. package/dist/service/index.js +252 -0
  82. package/dist/service/index.js.map +1 -0
  83. package/dist/storage/db.d.ts +75 -0
  84. package/dist/storage/db.d.ts.map +1 -0
  85. package/dist/storage/db.js +117 -0
  86. package/dist/storage/db.js.map +1 -0
  87. package/dist/storage/index.d.ts +4 -0
  88. package/dist/storage/index.d.ts.map +1 -0
  89. package/dist/storage/index.js +3 -0
  90. package/dist/storage/index.js.map +1 -0
  91. package/dist/storage/repo.d.ts +162 -0
  92. package/dist/storage/repo.d.ts.map +1 -0
  93. package/dist/storage/repo.js +472 -0
  94. package/dist/storage/repo.js.map +1 -0
  95. package/dist/sync/client.d.ts +24 -0
  96. package/dist/sync/client.d.ts.map +1 -0
  97. package/dist/sync/client.js +41 -0
  98. package/dist/sync/client.js.map +1 -0
  99. package/dist/sync/index.d.ts +18 -0
  100. package/dist/sync/index.d.ts.map +1 -0
  101. package/dist/sync/index.js +135 -0
  102. package/dist/sync/index.js.map +1 -0
  103. package/dist/sync/sync.test.d.ts +2 -0
  104. package/dist/sync/sync.test.d.ts.map +1 -0
  105. package/dist/sync/sync.test.js +412 -0
  106. package/dist/sync/sync.test.js.map +1 -0
  107. package/dist/sync/transform.d.ts +80 -0
  108. package/dist/sync/transform.d.ts.map +1 -0
  109. package/dist/sync/transform.js +90 -0
  110. package/dist/sync/transform.js.map +1 -0
  111. package/package.json +50 -0
  112. package/src/cli/commands.ts +332 -0
  113. package/src/cli/format.ts +133 -0
  114. package/src/context/index.ts +42 -0
  115. package/src/dashboard/html.ts +97 -0
  116. package/src/dashboard/index.html +1245 -0
  117. package/src/hooks/config.ts +119 -0
  118. package/src/hooks/git.ts +77 -0
  119. package/src/hooks/index.ts +7 -0
  120. package/src/hooks/server.ts +397 -0
  121. package/src/index.ts +6 -0
  122. package/src/otel/config.ts +141 -0
  123. package/src/otel/index.ts +8 -0
  124. package/src/otel/receiver.ts +183 -0
  125. package/src/parser/index.ts +3 -0
  126. package/src/parser/ingest.ts +119 -0
  127. package/src/parser/jsonl.ts +241 -0
  128. package/src/parser/types.ts +89 -0
  129. package/src/parser/watcher.ts +116 -0
  130. package/src/pricing/index.ts +51 -0
  131. package/src/service/index.ts +272 -0
  132. package/src/storage/db.ts +198 -0
  133. package/src/storage/index.ts +3 -0
  134. package/src/storage/repo.ts +601 -0
  135. package/src/sync/client.ts +63 -0
  136. package/src/sync/index.ts +207 -0
  137. package/src/sync/sync.test.ts +447 -0
  138. package/src/sync/transform.ts +184 -0
  139. package/tsconfig.json +19 -0
@@ -0,0 +1,202 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import readline from "node:readline";
5
+ import { computeTurnCost } from "../pricing/index.js";
6
+ const CLAUDE_DIR = path.join(os.homedir(), ".claude");
7
+ const PROJECTS_DIR = path.join(CLAUDE_DIR, "projects");
8
+ // Claude Code stores session files as:
9
+ // ~/.claude/projects/<encoded-path>/<session-uuid>.jsonl
10
+ // Session UUIDs match the standard UUID v4 format.
11
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
12
+ /**
13
+ * Discover all session JSONL files across all projects.
14
+ * Files are stored directly in each project directory (not in a sessions/ subdir).
15
+ */
16
+ export function discoverSessionFiles() {
17
+ const results = [];
18
+ if (!fs.existsSync(PROJECTS_DIR))
19
+ return results;
20
+ for (const projectDir of fs.readdirSync(PROJECTS_DIR)) {
21
+ const projectDirPath = path.join(PROJECTS_DIR, projectDir);
22
+ const stat = fs.statSync(projectDirPath);
23
+ if (!stat.isDirectory())
24
+ continue;
25
+ const decodedProject = decodeProjectPath(projectDir);
26
+ for (const file of fs.readdirSync(projectDirPath)) {
27
+ if (!UUID_RE.test(file))
28
+ continue;
29
+ results.push({
30
+ filePath: path.join(projectDirPath, file),
31
+ projectPath: decodedProject,
32
+ });
33
+ }
34
+ }
35
+ return results.sort((a, b) => {
36
+ const aStat = fs.statSync(a.filePath);
37
+ const bStat = fs.statSync(b.filePath);
38
+ return bStat.mtimeMs - aStat.mtimeMs;
39
+ });
40
+ }
41
+ /**
42
+ * Parse a single session JSONL file into a structured ParsedSession.
43
+ */
44
+ export async function parseSessionFile(filePath, projectPath) {
45
+ const entries = await readJsonlFile(filePath);
46
+ const sessionId = path.basename(filePath, ".jsonl");
47
+ const turns = [];
48
+ let turnIndex = 0;
49
+ let model = null;
50
+ let startedAt = null;
51
+ let endedAt = null;
52
+ let totalInputTokens = 0;
53
+ let totalOutputTokens = 0;
54
+ let totalCacheReadTokens = 0;
55
+ let totalCacheCreationTokens = 0;
56
+ let totalCostUsd = 0;
57
+ let totalDurationMs = 0;
58
+ // Track whether each entry is a real user prompt or an automatic tool-result
59
+ const entryIsRealUser = [];
60
+ for (const entry of entries) {
61
+ if (!entry.message)
62
+ continue;
63
+ const msg = entry.message;
64
+ const timestamp = entry.timestamp ?? new Date().toISOString();
65
+ if (!startedAt)
66
+ startedAt = timestamp;
67
+ endedAt = timestamp;
68
+ if (msg.model)
69
+ model = msg.model;
70
+ const usage = msg.usage ?? {};
71
+ const inputTokens = usage.input_tokens ?? 0;
72
+ const outputTokens = usage.output_tokens ?? 0;
73
+ const cacheRead = usage.cache_read_input_tokens ?? 0;
74
+ const cacheCreation = usage.cache_creation_input_tokens ?? 0;
75
+ const costUsd = entry.costUSD ?? computeTurnCost(msg.model ?? model, inputTokens, outputTokens, cacheRead, cacheCreation);
76
+ const durationMs = entry.durationMs ?? 0;
77
+ totalInputTokens += inputTokens;
78
+ totalOutputTokens += outputTokens;
79
+ totalCacheReadTokens += cacheRead;
80
+ totalCacheCreationTokens += cacheCreation;
81
+ totalCostUsd += costUsd;
82
+ const { text, toolCalls } = extractContent(msg.content);
83
+ const realUser = msg.role === "user" && !entry.sourceToolAssistantUUID;
84
+ entryIsRealUser.push(realUser);
85
+ turns.push({
86
+ turnIndex,
87
+ role: msg.role,
88
+ timestamp,
89
+ inputTokens,
90
+ outputTokens,
91
+ cacheReadTokens: cacheRead,
92
+ cacheCreationTokens: cacheCreation,
93
+ costUsd,
94
+ durationMs,
95
+ model: msg.model ?? null,
96
+ contentText: text,
97
+ toolCalls,
98
+ isRealUser: realUser,
99
+ });
100
+ turnIndex++;
101
+ }
102
+ // Compute processing duration: for each real user prompt, find the last
103
+ // assistant turn before the next real user prompt. Duration = that gap.
104
+ // Only assigned to the real user turn (the one that triggered processing).
105
+ const realUserTurnIndices = [];
106
+ for (let i = 0; i < turns.length; i++) {
107
+ if (entryIsRealUser[i])
108
+ realUserTurnIndices.push(i);
109
+ }
110
+ for (let ri = 0; ri < realUserTurnIndices.length; ri++) {
111
+ const userIdx = realUserTurnIndices[ri];
112
+ const nextUserIdx = ri + 1 < realUserTurnIndices.length
113
+ ? realUserTurnIndices[ri + 1]
114
+ : turns.length;
115
+ let lastAssistantTs = null;
116
+ for (let j = userIdx + 1; j < nextUserIdx; j++) {
117
+ if (turns[j].role === "assistant") {
118
+ lastAssistantTs = turns[j].timestamp;
119
+ }
120
+ }
121
+ if (lastAssistantTs && turns[userIdx].durationMs === 0) {
122
+ const userTime = new Date(turns[userIdx].timestamp).getTime();
123
+ const assistantTime = new Date(lastAssistantTs).getTime();
124
+ const gap = assistantTime - userTime;
125
+ if (gap > 0)
126
+ turns[userIdx].durationMs = gap;
127
+ }
128
+ }
129
+ totalDurationMs = turns.reduce((sum, t) => sum + t.durationMs, 0);
130
+ return {
131
+ sessionId,
132
+ projectPath: projectPath ?? null,
133
+ startedAt: startedAt ?? new Date().toISOString(),
134
+ endedAt,
135
+ model,
136
+ turns,
137
+ totalInputTokens,
138
+ totalOutputTokens,
139
+ totalCacheReadTokens,
140
+ totalCacheCreationTokens,
141
+ totalCostUsd,
142
+ totalDurationMs,
143
+ };
144
+ }
145
+ /**
146
+ * Extract text content and tool calls from a message's content field.
147
+ */
148
+ function extractContent(content) {
149
+ if (!content)
150
+ return { text: "", toolCalls: [] };
151
+ if (typeof content === "string")
152
+ return { text: content, toolCalls: [] };
153
+ const textParts = [];
154
+ const toolCalls = [];
155
+ for (const block of content) {
156
+ if (block.type === "text" && block.text) {
157
+ textParts.push(block.text);
158
+ }
159
+ else if (block.type === "tool_use" && block.name) {
160
+ toolCalls.push({
161
+ toolName: block.name,
162
+ toolInput: block.input ?? {},
163
+ });
164
+ }
165
+ else if (block.type === "tool_result") {
166
+ const resultText = typeof block.content === "string"
167
+ ? block.content
168
+ : Array.isArray(block.content)
169
+ ? block.content
170
+ .filter((b) => b.type === "text" && b.text)
171
+ .map((b) => b.text)
172
+ .join("\n")
173
+ : "";
174
+ const matchingCall = toolCalls.find((tc) => !tc.toolResult);
175
+ if (matchingCall) {
176
+ matchingCall.toolResult = resultText;
177
+ }
178
+ }
179
+ }
180
+ return { text: textParts.join("\n"), toolCalls };
181
+ }
182
+ async function readJsonlFile(filePath) {
183
+ const entries = [];
184
+ const stream = fs.createReadStream(filePath, { encoding: "utf-8" });
185
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
186
+ for await (const line of rl) {
187
+ const trimmed = line.trim();
188
+ if (!trimmed)
189
+ continue;
190
+ try {
191
+ entries.push(JSON.parse(trimmed));
192
+ }
193
+ catch {
194
+ // Skip malformed lines
195
+ }
196
+ }
197
+ return entries;
198
+ }
199
+ function decodeProjectPath(encoded) {
200
+ return encoded.replace(/-/g, "/");
201
+ }
202
+ //# sourceMappingURL=jsonl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl.js","sourceRoot":"","sources":["../../src/parser/jsonl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Types representing the JSONL session file format that Claude Code writes to:
3
+ * ~/.claude/projects/<encoded-path>/sessions/<session-uuid>.jsonl
4
+ *
5
+ * Each line is one JSON object. The first line is typically a summary.
6
+ * Subsequent lines are user/assistant messages with full content.
7
+ */
8
+ export interface ContentBlock {
9
+ type: string;
10
+ text?: string;
11
+ id?: string;
12
+ name?: string;
13
+ input?: Record<string, unknown>;
14
+ tool_use_id?: string;
15
+ content?: string | ContentBlock[];
16
+ }
17
+ export interface UsageInfo {
18
+ input_tokens?: number;
19
+ output_tokens?: number;
20
+ cache_creation_input_tokens?: number;
21
+ cache_read_input_tokens?: number;
22
+ }
23
+ export interface MessagePayload {
24
+ role: string;
25
+ content: string | ContentBlock[];
26
+ model?: string;
27
+ usage?: UsageInfo;
28
+ }
29
+ export interface SessionEntry {
30
+ type: string;
31
+ uuid?: string;
32
+ parentUuid?: string;
33
+ timestamp?: string;
34
+ sessionId?: string;
35
+ cwd?: string;
36
+ version?: string;
37
+ message?: MessagePayload;
38
+ requestId?: string;
39
+ costUSD?: number;
40
+ durationMs?: number;
41
+ isSidechain?: boolean;
42
+ sourceToolAssistantUUID?: string;
43
+ summary?: string;
44
+ leafUuid?: string;
45
+ numLeaves?: number;
46
+ }
47
+ export interface ParsedSession {
48
+ sessionId: string;
49
+ projectPath: string | null;
50
+ startedAt: string;
51
+ endedAt: string | null;
52
+ model: string | null;
53
+ turns: ParsedTurn[];
54
+ totalInputTokens: number;
55
+ totalOutputTokens: number;
56
+ totalCacheReadTokens: number;
57
+ totalCacheCreationTokens: number;
58
+ totalCostUsd: number;
59
+ totalDurationMs: number;
60
+ }
61
+ export interface ParsedTurn {
62
+ turnIndex: number;
63
+ role: string;
64
+ timestamp: string;
65
+ inputTokens: number;
66
+ outputTokens: number;
67
+ cacheReadTokens: number;
68
+ cacheCreationTokens: number;
69
+ costUsd: number;
70
+ durationMs: number;
71
+ model: string | null;
72
+ contentText: string;
73
+ toolCalls: ToolCallInfo[];
74
+ isRealUser: boolean;
75
+ }
76
+ export interface ToolCallInfo {
77
+ toolName: string;
78
+ toolInput: Record<string, unknown>;
79
+ toolResult?: string;
80
+ }
81
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/parser/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Types representing the JSONL session file format that Claude Code writes to:
3
+ * ~/.claude/projects/<encoded-path>/sessions/<session-uuid>.jsonl
4
+ *
5
+ * Each line is one JSON object. The first line is typically a summary.
6
+ * Subsequent lines are user/assistant messages with full content.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/parser/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,16 @@
1
+ import type { SessionRepo } from "../storage/repo.js";
2
+ export interface WatcherOptions {
3
+ repo: SessionRepo;
4
+ verbose?: boolean;
5
+ /** Re-ingest all existing JSONL files on startup to catch up on missed sessions. Default true. */
6
+ catchUp?: boolean;
7
+ }
8
+ /**
9
+ * Watch ~/.claude/projects for JSONL session file changes and ingest them
10
+ * into the database as they are written. Returns a stop function.
11
+ *
12
+ * On startup, performs an initial catch-up pass so that sessions written
13
+ * while zozul was not running are immediately available.
14
+ */
15
+ export declare function watchSessionFiles(opts: WatcherOptions): Promise<() => void>;
16
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/parser/watcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kGAAkG;IAClG,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CA+EjF"}
@@ -0,0 +1,103 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import { ingestSessionFile } from "./ingest.js";
5
+ import { discoverSessionFiles } from "./jsonl.js";
6
+ const PROJECTS_DIR = path.join(os.homedir(), ".claude", "projects");
7
+ const DEBOUNCE_MS = 500;
8
+ /**
9
+ * Watch ~/.claude/projects for JSONL session file changes and ingest them
10
+ * into the database as they are written. Returns a stop function.
11
+ *
12
+ * On startup, performs an initial catch-up pass so that sessions written
13
+ * while zozul was not running are immediately available.
14
+ */
15
+ export async function watchSessionFiles(opts) {
16
+ const { repo, verbose } = opts;
17
+ const catchUp = opts.catchUp ?? true;
18
+ // ── Initial catch-up pass ──
19
+ if (catchUp) {
20
+ const files = discoverSessionFiles();
21
+ let caught = 0;
22
+ for (const { filePath, projectPath } of files) {
23
+ try {
24
+ await ingestSessionFile(repo, filePath, projectPath);
25
+ caught++;
26
+ }
27
+ catch {
28
+ // Ignore parse errors on individual files
29
+ }
30
+ }
31
+ if (verbose && caught > 0) {
32
+ process.stderr.write(`[watcher] catch-up: ingested ${caught} session file(s)\n`);
33
+ }
34
+ }
35
+ if (!fs.existsSync(PROJECTS_DIR)) {
36
+ if (verbose) {
37
+ process.stderr.write(`[watcher] ${PROJECTS_DIR} not found, watching skipped\n`);
38
+ }
39
+ return () => { };
40
+ }
41
+ // ── Per-file debounce ──
42
+ const timers = new Map();
43
+ function scheduleIngest(filePath) {
44
+ const existing = timers.get(filePath);
45
+ if (existing)
46
+ clearTimeout(existing);
47
+ timers.set(filePath, setTimeout(async () => {
48
+ timers.delete(filePath);
49
+ try {
50
+ const projectPath = decodeProjectPath(filePath);
51
+ await ingestSessionFile(repo, filePath, projectPath ?? undefined);
52
+ if (verbose) {
53
+ process.stderr.write(`[watcher] ingested: ${filePath}\n`);
54
+ }
55
+ }
56
+ catch (err) {
57
+ if (verbose) {
58
+ process.stderr.write(`[watcher] ingest failed (${filePath}): ${err}\n`);
59
+ }
60
+ }
61
+ }, DEBOUNCE_MS));
62
+ }
63
+ // ── fs.watch with recursive ──
64
+ // recursive: true uses FSEvents on macOS and ReadDirectoryChangesW on Windows.
65
+ const watcher = fs.watch(PROJECTS_DIR, { recursive: true }, (_event, filename) => {
66
+ if (!filename)
67
+ return;
68
+ if (!filename.endsWith(".jsonl"))
69
+ return;
70
+ // filename is relative to PROJECTS_DIR on macOS/Windows
71
+ const filePath = path.join(PROJECTS_DIR, filename);
72
+ // Only act if the file actually exists (ignore delete events)
73
+ if (!fs.existsSync(filePath))
74
+ return;
75
+ scheduleIngest(filePath);
76
+ });
77
+ watcher.on("error", (err) => {
78
+ if (verbose)
79
+ process.stderr.write(`[watcher] error: ${err}\n`);
80
+ });
81
+ if (verbose) {
82
+ process.stderr.write(`[watcher] watching ${PROJECTS_DIR}\n`);
83
+ }
84
+ return () => {
85
+ watcher.close();
86
+ for (const t of timers.values())
87
+ clearTimeout(t);
88
+ timers.clear();
89
+ };
90
+ }
91
+ /**
92
+ * Extract the decoded project path from an absolute JSONL file path.
93
+ * ~/.claude/projects/<encoded>/<uuid>.jsonl
94
+ * where <encoded> has "/" replaced with "-".
95
+ */
96
+ function decodeProjectPath(filePath) {
97
+ // Match project dir directly containing the UUID file
98
+ const match = filePath.match(/projects\/([^/]+)\/[0-9a-f-]{36}\.jsonl$/i);
99
+ if (!match)
100
+ return null;
101
+ return match[1].replace(/-/g, "/");
102
+ }
103
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/parser/watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function computeTurnCost(model: string | null, inputTokens: number, outputTokens: number, cacheReadTokens: number, cacheCreationTokens: number): number;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pricing/index.ts"],"names":[],"mappings":"AAiCA,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,EACvB,mBAAmB,EAAE,MAAM,GAC1B,MAAM,CAWR"}
@@ -0,0 +1,37 @@
1
+ // Prices per million tokens
2
+ const PRICING = {
3
+ "claude-opus-4-6": { input: 5.00, output: 25.00, cacheRead: 0.50, cacheCreation: 6.25 },
4
+ "claude-sonnet-4-6": { input: 3.00, output: 15.00, cacheRead: 0.30, cacheCreation: 3.75 },
5
+ "claude-sonnet-4-5-20250514": { input: 3.00, output: 15.00, cacheRead: 0.30, cacheCreation: 3.75 },
6
+ "claude-haiku-4-5-20251001": { input: 1.00, output: 5.00, cacheRead: 0.10, cacheCreation: 1.25 },
7
+ "claude-3-5-haiku-20241022": { input: 0.80, output: 4.00, cacheRead: 0.08, cacheCreation: 1.00 },
8
+ };
9
+ function findPricing(model) {
10
+ if (PRICING[model])
11
+ return PRICING[model];
12
+ // Fuzzy match: try prefix matching for versioned model names
13
+ for (const [key, pricing] of Object.entries(PRICING)) {
14
+ if (model.startsWith(key) || key.startsWith(model))
15
+ return pricing;
16
+ }
17
+ // Match by family name
18
+ if (model.includes("opus"))
19
+ return PRICING["claude-opus-4-6"];
20
+ if (model.includes("sonnet"))
21
+ return PRICING["claude-sonnet-4-6"];
22
+ if (model.includes("haiku"))
23
+ return PRICING["claude-haiku-4-5-20251001"];
24
+ return null;
25
+ }
26
+ export function computeTurnCost(model, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens) {
27
+ if (!model)
28
+ return 0;
29
+ const pricing = findPricing(model);
30
+ if (!pricing)
31
+ return 0;
32
+ return ((inputTokens * pricing.input) / 1_000_000 +
33
+ (outputTokens * pricing.output) / 1_000_000 +
34
+ (cacheReadTokens * pricing.cacheRead) / 1_000_000 +
35
+ (cacheCreationTokens * pricing.cacheCreation) / 1_000_000);
36
+ }
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pricing/index.ts"],"names":[],"mappings":"AAWA,4BAA4B;AAC5B,MAAM,OAAO,GAAiC;IAC5C,iBAAiB,EAAY,EAAE,KAAK,EAAE,IAAI,EAAG,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAClG,mBAAmB,EAAU,EAAE,KAAK,EAAE,IAAI,EAAG,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAClG,4BAA4B,EAAC,EAAE,KAAK,EAAE,IAAI,EAAG,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAClG,2BAA2B,EAAE,EAAE,KAAK,EAAE,IAAI,EAAG,MAAM,EAAE,IAAI,EAAG,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAClG,2BAA2B,EAAE,EAAE,KAAK,EAAE,IAAI,EAAG,MAAM,EAAE,IAAI,EAAG,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;CACnG,CAAC;AAEF,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,6DAA6D;IAC7D,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACrD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;IACrE,CAAC;IACD,uBAAuB;IACvB,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9D,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAClE,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACzE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,KAAoB,EACpB,WAAmB,EACnB,YAAoB,EACpB,eAAuB,EACvB,mBAA2B;IAE3B,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IACrB,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC;IAEvB,OAAO,CACL,CAAC,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS;QACzC,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS;QAC3C,CAAC,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS;QACjD,CAAC,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,SAAS,CAC1D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ export interface ServiceInstallOptions {
2
+ port: number;
3
+ dbPath?: string;
4
+ }
5
+ export interface ServiceResult {
6
+ platform: "macos" | "linux" | "unsupported";
7
+ servicePath: string;
8
+ alreadyRunning: boolean;
9
+ }
10
+ /**
11
+ * Install and immediately start zozul as a background service.
12
+ * Uses launchd on macOS, systemd --user on Linux.
13
+ */
14
+ export declare function installService(opts: ServiceInstallOptions): ServiceResult;
15
+ /**
16
+ * Stop and remove the zozul background service.
17
+ */
18
+ export declare function uninstallService(): {
19
+ removed: boolean;
20
+ platform: "macos" | "linux" | "unsupported";
21
+ };
22
+ /**
23
+ * Restart the running service in-place (kills and relaunches the current process).
24
+ * Throws if the service is not installed.
25
+ */
26
+ export declare function restartService(): void;
27
+ /**
28
+ * Returns a human-readable status string for the running service.
29
+ */
30
+ export declare function serviceStatus(): string;
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/service/index.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,GAAG,OAAO,GAAG,aAAa,CAAC;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,aAAa,CAoBzE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,GAAG,aAAa,CAAA;CAAE,CAoCpG;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAiBrC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CA+BtC"}