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
package/README.md ADDED
@@ -0,0 +1,178 @@
1
+ # zozul-cli
2
+
3
+ Observability for [Claude Code](https://code.claude.com/) — track token usage, costs, turns, and full conversation history. No external services, no Docker, no cloud.
4
+
5
+ ## What it does
6
+
7
+ zozul is a single local process that captures everything Claude Code does. Data flows in from three complementary sources and lands in a SQLite database at `~/.zozul/zozul.db`. A built-in web dashboard and JSON API sit on top of that.
8
+
9
+ | Source | What it provides |
10
+ |---|---|
11
+ | **OTEL receiver** | Token counts, cost (USD), active time, API events, user prompts — streamed from Claude Code every ~60s |
12
+ | **Hooks** | Real-time session lifecycle, tool calls, user prompts — fired synchronously as events happen |
13
+ | **JSONL watcher** | Full turn content, assistant responses, per-turn token detail — read directly from Claude Code's transcript files |
14
+
15
+ Each source has different strengths. OTEL is the authoritative source for **cost and duration**. JSONL is the only source for **full conversation text**. Hooks provide **real-time signals** and trigger transcript ingestion on session end. The `sessions` table is kept in sync from all three.
16
+
17
+ ## Quick start
18
+
19
+ ```bash
20
+ # Install and build
21
+ npm install && npm link
22
+
23
+ # Configure Claude Code and install as a background service (recommended)
24
+ zozul install --service
25
+
26
+ # Open the dashboard
27
+ open http://localhost:7890/dashboard
28
+
29
+ # Use Claude Code normally — data appears automatically
30
+ claude
31
+ ```
32
+
33
+ Or if you'd rather manage the process yourself:
34
+
35
+ ```bash
36
+ zozul install # Configure Claude Code hooks + OTEL
37
+ zozul serve # Start the server
38
+ open http://localhost:7890/dashboard
39
+ ```
40
+
41
+ ## Dashboard
42
+
43
+ `http://localhost:7890/dashboard`
44
+
45
+ - **Stats bar** — sessions, user prompts, interruptions, tokens, and cost at a glance
46
+ - **Token usage chart** — daily input/output/cache token trends with time range controls
47
+ - **Cost chart** — daily spend
48
+ - **Tool usage** — which tools Claude uses most
49
+ - **Model breakdown** — cost and tokens per model
50
+ - **Sessions table** — paginated (Load More), filterable; click any session for the full conversation with per-turn token counts, expandable tool call inputs/outputs
51
+ - **Auto-refresh** — dashboard polls every 10s automatically; click the Auto button to refresh immediately
52
+
53
+ ## Commands
54
+
55
+ | Command | Description |
56
+ |---|---|
57
+ | `zozul serve` | Start the server (dashboard, hooks, OTEL receiver, API) on port 7890 |
58
+ | `zozul install` | Configure Claude Code hooks and OTEL in `~/.claude/settings.json` |
59
+ | `zozul install --service` | Configure Claude Code **and** install zozul as a login service (auto-starts) |
60
+ | `zozul uninstall` | Remove zozul config from Claude Code settings |
61
+ | `zozul uninstall --service` | Also stop and remove the background service |
62
+ | `zozul restart` | Restart the background service (picks up new builds) |
63
+ | `zozul service-status` | Show whether the background service is installed and running |
64
+ | `zozul ingest` | Parse all Claude Code session JSONL files into the database |
65
+ | `zozul ingest --force` | Re-ingest sessions that already exist (picks up new turns) |
66
+ | `zozul sessions` | List recorded sessions with token/cost summaries |
67
+ | `zozul session <id>` | Show full details and conversation for a session |
68
+ | `zozul stats` | Show aggregate statistics across all sessions |
69
+ | `zozul db-clean` | Remove rows with invalid timestamps from the database |
70
+ | `zozul db-clean --session <id>` | Remove all data for a specific session |
71
+ | `zozul show-config` | Preview the Claude Code config that would be installed |
72
+
73
+ ## Architecture
74
+
75
+ ```
76
+ Claude Code
77
+ |
78
+ +-------------+-------------+
79
+ | | |
80
+ OTEL export Hook POSTs ~/.claude/projects/
81
+ (every ~60s) (real-time) <project>/<uuid>.jsonl
82
+ | | |
83
+ v v v
84
+ /v1/metrics /hook/* fs.watch (live)
85
+ /v1/logs | zozul ingest (manual)
86
+ | | |
87
+ | updateSessionFromOtel |
88
+ | | persistSession
89
+ +------+------+------+------+
90
+ |
91
+ SQLite (WAL)
92
+ ~/.zozul/zozul.db
93
+ |
94
+ +------+------+
95
+ | |
96
+ /dashboard /api/*
97
+ (browser) (JSON)
98
+ ```
99
+
100
+ Everything runs in a single process on port 7890.
101
+
102
+ ### Data sources and ownership
103
+
104
+ Each field in the `sessions` table has a designated owner:
105
+
106
+ | Field | Owner | Notes |
107
+ |---|---|---|
108
+ | `id`, `started_at`, `project_path`, `model` | JSONL | Set from transcript filename and content |
109
+ | `total_turns` | JSONL | Count of turns parsed from transcript |
110
+ | `total_cost_usd` | OTEL | JSONL transcripts do not include cost data |
111
+ | `total_duration_ms` | OTEL | Accumulated from `claude_code.active_time.total` |
112
+ | `total_*_tokens` (session level) | OTEL (preferred) | JSONL provides seeds; OTEL accumulates via `MAX()` |
113
+ | `ended_at` | Both | OTEL keeps it current as batches arrive; JSONL sets it at ingest |
114
+
115
+ The `sessions` upsert uses `MAX()` for all metric fields so OTEL-accumulated values are never clobbered by a JSONL re-ingest that may have lower (or zero) values.
116
+
117
+ ### JSONL watcher
118
+
119
+ When `zozul serve` starts it:
120
+
121
+ 1. Performs a catch-up pass — ingests all JSONL files found under `~/.claude/projects/`
122
+ 2. Watches that directory for changes via `fs.watch` (recursive, FSEvents on macOS)
123
+ 3. Debounces per-file at 500ms and calls `ingestSessionFile` on each change
124
+
125
+ This means starting zozul after Claude Code is already running is fine — all existing turns are recovered immediately and new turns appear within ~500ms of being written.
126
+
127
+ ### OTEL metrics
128
+
129
+ Claude Code exports OTLP JSON to `http://localhost:7890` on a 60s interval (metrics) and 5s interval (logs). Each batch contains **delta values** for the export window — not cumulative totals. zozul accumulates these into the `sessions` table via `updateSessionFromOtel` on every batch received.
130
+
131
+ Raw metric rows are also stored in `otel_metrics` and `otel_events` for dashboard charts and event replay.
132
+
133
+ ## Background service
134
+
135
+ `zozul install --service` installs zozul as a persistent background service:
136
+
137
+ - **macOS**: writes `~/Library/LaunchAgents/com.zozul.serve.plist` and loads it via `launchctl`. Starts on login, restarts on crash.
138
+ - **Linux**: writes `~/.config/systemd/user/zozul.service` and enables it with `systemctl --user`.
139
+
140
+ The service bakes in the exact node binary path (nvm-safe) and the script path at install time, so it doesn't depend on shell PATH.
141
+
142
+ Logs write to `~/.zozul/zozul.log`.
143
+
144
+ ## Configuration
145
+
146
+ Settings via `.env` in the working directory (see `.env.example`) or environment variables:
147
+
148
+ ```bash
149
+ ZOZUL_PORT=7890 # Server port (default: 7890)
150
+ ZOZUL_DB_PATH=~/.zozul/zozul.db # Database path
151
+ ZOZUL_VERBOSE=1 # Log every event to stderr
152
+ OTEL_ENDPOINT=http://localhost:7890 # Where Claude Code sends OTEL
153
+ OTEL_PROTOCOL=http/json # Must be http/json
154
+ OTEL_LOG_USER_PROMPTS=1 # Include prompt text in OTEL events
155
+ OTEL_LOG_TOOL_DETAILS=1 # Include tool names in OTEL events
156
+ ```
157
+
158
+ CLI flags override `.env` values.
159
+
160
+ ## Data captured
161
+
162
+ | Data point | Source | Granularity |
163
+ |---|---|---|
164
+ | Token usage (input/output/cache/creation) | OTEL + JSONL | Per-session and per-turn |
165
+ | Cost (USD) | OTEL | Per-session, per-model |
166
+ | Active time | OTEL | Per-session |
167
+ | Turns / API calls | JSONL | Full content and metadata |
168
+ | User prompts | Hooks (`UserPromptSubmit`) + JSONL | Count (aggregate) + full text (per-turn) |
169
+ | Interruptions | Hooks (`Stop`) | Count (aggregate) |
170
+ | Model responses | JSONL only | Full text |
171
+ | Tool calls and results | Hooks + JSONL | Name, input, output |
172
+ | Session lifecycle | Hooks | Start, end, stop events |
173
+
174
+ ## Requirements
175
+
176
+ - Node.js 18+
177
+ - Claude Code installed (`claude --version`)
178
+ - Claude Pro, Max, Teams, Enterprise, or API key
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function buildCli(): Command;
3
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8BpC,wBAAgB,QAAQ,IAAI,OAAO,CA6SlC"}
@@ -0,0 +1,307 @@
1
+ import { Command } from "commander";
2
+ import { getDb } from "../storage/db.js";
3
+ import { SessionRepo } from "../storage/repo.js";
4
+ import { createHookServer } from "../hooks/server.js";
5
+ import { installHooksToSettings, uninstallHooksFromSettings, generateHooksConfig } from "../hooks/config.js";
6
+ import { installOtelToSettings, uninstallOtelFromSettings, generateOtelShellExports } from "../otel/config.js";
7
+ import { ingestAllSessions } from "../parser/ingest.js";
8
+ import { watchSessionFiles } from "../parser/watcher.js";
9
+ import { installService, uninstallService, serviceStatus, restartService } from "../service/index.js";
10
+ import { getActiveContext, setActiveContext, clearActiveContext } from "../context/index.js";
11
+ import { installGitHook, uninstallGitHook } from "../hooks/git.js";
12
+ import { runSync } from "../sync/index.js";
13
+ import { ZozulApiClient } from "../sync/client.js";
14
+ function envPort() {
15
+ return process.env.ZOZUL_PORT ?? "7890";
16
+ }
17
+ function envOtelEndpoint() {
18
+ return process.env.OTEL_ENDPOINT ?? "http://localhost:7890";
19
+ }
20
+ function envOtelProtocol() {
21
+ return process.env.OTEL_PROTOCOL ?? "http/json";
22
+ }
23
+ function envVerbose() {
24
+ return process.env.ZOZUL_VERBOSE === "true" || process.env.ZOZUL_VERBOSE === "1";
25
+ }
26
+ function envDbPath() {
27
+ return process.env.ZOZUL_DB_PATH || undefined;
28
+ }
29
+ export function buildCli() {
30
+ const program = new Command();
31
+ program
32
+ .name("zozul")
33
+ .description("Observability for Claude Code — track tokens, costs, turns, and conversations")
34
+ .version("0.1.0");
35
+ program
36
+ .command("serve")
37
+ .description("Start the hooks HTTP server to receive real-time events from Claude Code")
38
+ .option("-p, --port <port>", "Port to listen on", envPort())
39
+ .option("-v, --verbose", "Print events to stderr as they arrive")
40
+ .action(async (opts) => {
41
+ const port = parseInt(opts.port, 10);
42
+ const verbose = opts.verbose || envVerbose();
43
+ const db = getDb(envDbPath());
44
+ const repo = new SessionRepo(db);
45
+ const server = createHookServer({ port, repo, verbose });
46
+ server.on("error", (err) => {
47
+ if (err.code === "EADDRINUSE") {
48
+ console.error(`Port ${port} is already in use. Is zozul already running?`);
49
+ console.error(" Check with: lsof -ti :" + port);
50
+ db.close();
51
+ process.exit(0); // clean exit so launchd/systemd won't respawn
52
+ }
53
+ throw err;
54
+ });
55
+ server.listen(port, async () => {
56
+ console.log(`zozul listening on http://localhost:${port}`);
57
+ console.log(` Dashboard: http://localhost:${port}/dashboard`);
58
+ console.log(` Hooks: http://localhost:${port}/hook/<event>`);
59
+ console.log(` OTLP receiver: http://localhost:${port}/v1/metrics & /v1/logs`);
60
+ console.log(` API: http://localhost:${port}/api/*`);
61
+ console.log("\nPress Ctrl+C to stop.\n");
62
+ const stopWatcher = await watchSessionFiles({ repo, verbose, catchUp: true });
63
+ process.on("SIGINT", () => {
64
+ stopWatcher();
65
+ server.close();
66
+ db.close();
67
+ process.exit(0);
68
+ });
69
+ });
70
+ });
71
+ program
72
+ .command("install")
73
+ .description("Install hooks, OTEL config, and optionally the background service")
74
+ .option("-p, --port <port>", "Hook server port", envPort())
75
+ .option("--otel-endpoint <endpoint>", "OTLP endpoint", envOtelEndpoint())
76
+ .option("--otel-protocol <protocol>", "OTLP protocol", envOtelProtocol())
77
+ .option("--no-otel", "Skip OTEL configuration")
78
+ .option("--no-hooks", "Skip hooks configuration")
79
+ .option("--service", "Also install zozul as a background service (starts on login)")
80
+ .option("--status", "Show background service status")
81
+ .option("--restart", "Restart the background service")
82
+ .option("--dry-run", "Print the config that would be installed without writing")
83
+ .action((opts) => {
84
+ if (opts.status) {
85
+ console.log(`Service status: ${serviceStatus()}`);
86
+ return;
87
+ }
88
+ if (opts.restart) {
89
+ try {
90
+ restartService();
91
+ console.log("Service restarted.");
92
+ console.log(` Status: ${serviceStatus()}`);
93
+ }
94
+ catch (err) {
95
+ console.error(`Restart failed: ${err instanceof Error ? err.message : err}`);
96
+ process.exit(1);
97
+ }
98
+ return;
99
+ }
100
+ const port = parseInt(opts.port, 10);
101
+ if (opts.dryRun) {
102
+ console.log("── Hooks Config (for ~/.claude/settings.json) ──\n");
103
+ console.log(JSON.stringify(generateHooksConfig({ port }), null, 2));
104
+ console.log("\n── OTEL Environment Variables ──\n");
105
+ console.log(generateOtelShellExports({ endpoint: opts.otelEndpoint }));
106
+ return;
107
+ }
108
+ if (opts.hooks !== false) {
109
+ const result = installHooksToSettings({ port });
110
+ console.log(`Hooks installed to ${result.path}`);
111
+ if (result.merged)
112
+ console.log(" (merged with existing hooks)");
113
+ }
114
+ if (opts.otel !== false) {
115
+ const result = installOtelToSettings({
116
+ endpoint: opts.otelEndpoint,
117
+ protocol: opts.otelProtocol,
118
+ });
119
+ console.log(`OTEL config installed to ${result.path}`);
120
+ }
121
+ if (opts.service) {
122
+ try {
123
+ const result = installService({ port, dbPath: envDbPath() });
124
+ console.log(`Service installed: ${result.servicePath}`);
125
+ console.log(" zozul is now running in the background and will start automatically on login.");
126
+ }
127
+ catch (err) {
128
+ console.error(`Service install failed: ${err instanceof Error ? err.message : err}`);
129
+ console.error(" Run 'zozul serve' manually as a fallback.");
130
+ }
131
+ }
132
+ else {
133
+ console.log("\nDone. Start the server with: zozul serve");
134
+ console.log("Or install as a background service with: zozul install --service");
135
+ }
136
+ const gitResult = installGitHook();
137
+ if (gitResult) {
138
+ if (gitResult.created) {
139
+ console.log(`Git post-commit hook installed: ${gitResult.path}`);
140
+ console.log(" (auto-clears task context on commit)");
141
+ }
142
+ else {
143
+ console.log("Git post-commit hook already installed.");
144
+ }
145
+ }
146
+ console.log("Launch Claude Code normally with: claude");
147
+ });
148
+ program
149
+ .command("uninstall")
150
+ .description("Remove zozul hooks, OTEL config, and background service")
151
+ .option("-p, --port <port>", "Hook server port (to match installed hooks)", envPort())
152
+ .action((opts) => {
153
+ const port = parseInt(opts.port, 10);
154
+ const hooksRemoved = uninstallHooksFromSettings({ port });
155
+ const otelRemoved = uninstallOtelFromSettings();
156
+ const gitRemoved = uninstallGitHook();
157
+ if (hooksRemoved)
158
+ console.log("Hooks removed from Claude Code settings.");
159
+ if (otelRemoved)
160
+ console.log("OTEL config removed from Claude Code settings.");
161
+ if (gitRemoved)
162
+ console.log("Git post-commit hook removed.");
163
+ const serviceResult = uninstallService();
164
+ if (serviceResult.removed) {
165
+ console.log("Background service stopped and removed.");
166
+ }
167
+ if (!hooksRemoved && !otelRemoved && !gitRemoved && !serviceResult.removed) {
168
+ console.log("Nothing to remove.");
169
+ }
170
+ });
171
+ program
172
+ .command("context [tags...]")
173
+ .description("Set, view, or clear the active task tags for tagging turns")
174
+ .option("--clear", "Clear the active task context")
175
+ .option("--list", "List all tasks that have been used")
176
+ .action((tags, opts) => {
177
+ if (opts.clear) {
178
+ clearActiveContext();
179
+ console.log("Task context cleared.");
180
+ return;
181
+ }
182
+ if (opts.list) {
183
+ const db = getDb(envDbPath());
184
+ const repo = new SessionRepo(db);
185
+ const tasks = repo.listTasks();
186
+ if (tasks.length === 0) {
187
+ console.log("No tasks found.");
188
+ }
189
+ else {
190
+ for (const t of tasks) {
191
+ console.log(` ${t.task} (${t.turn_count} turns, last tagged: ${t.last_tagged})`);
192
+ }
193
+ }
194
+ db.close();
195
+ return;
196
+ }
197
+ if (tags.length > 0) {
198
+ const ctx = setActiveContext(tags);
199
+ console.log(`Active tags: ${ctx.active.join(", ")}`);
200
+ console.log(` Set at: ${ctx.set_at}`);
201
+ return;
202
+ }
203
+ // No arguments: show current context
204
+ const ctx = getActiveContext();
205
+ if (ctx?.active && ctx.active.length > 0) {
206
+ console.log(`Active tags: ${ctx.active.join(", ")}`);
207
+ console.log(` Set at: ${ctx.set_at}`);
208
+ }
209
+ else {
210
+ console.log("No active task context.");
211
+ console.log('Set one with: zozul context "UI" "Feature"');
212
+ }
213
+ });
214
+ program
215
+ .command("sync")
216
+ .description("Sync local data to the remote zozul backend")
217
+ .option("--dry-run", "Show what would be synced without sending data")
218
+ .option("-v, --verbose", "Print detailed progress")
219
+ .action(async (opts) => {
220
+ const apiUrl = process.env.ZOZUL_API_URL;
221
+ const apiKey = process.env.ZOZUL_API_KEY;
222
+ if (!apiUrl || !apiKey) {
223
+ console.error("Missing required environment variables:");
224
+ if (!apiUrl)
225
+ console.error(" ZOZUL_API_URL — base URL of the zozul backend");
226
+ if (!apiKey)
227
+ console.error(" ZOZUL_API_KEY — API key for authentication");
228
+ console.error("\nSet them in .env or export them in your shell.");
229
+ process.exit(1);
230
+ }
231
+ const db = getDb(envDbPath());
232
+ const repo = new SessionRepo(db);
233
+ const client = new ZozulApiClient({ apiUrl, apiKey });
234
+ if (opts.dryRun) {
235
+ console.log(`Dry run — checking what would sync to ${apiUrl}...\n`);
236
+ }
237
+ else {
238
+ console.log(`Syncing to ${apiUrl}...\n`);
239
+ }
240
+ const result = await runSync(repo, client, {
241
+ verbose: opts.verbose || envVerbose(),
242
+ dryRun: opts.dryRun,
243
+ });
244
+ console.log("── Sync Summary ──");
245
+ const label = opts.dryRun ? "pending" : "synced";
246
+ for (const [table, counts] of Object.entries(result)) {
247
+ const status = counts.failed > 0 ? "PARTIAL" : "OK";
248
+ console.log(` ${table.padEnd(15)} ${counts.synced} ${label}, ${counts.failed} failed [${status}]`);
249
+ }
250
+ const totalFailed = Object.values(result).reduce((s, c) => s + c.failed, 0);
251
+ if (totalFailed > 0) {
252
+ console.error(`\n${totalFailed} items failed to sync. Re-run 'zozul sync' to retry.`);
253
+ db.close();
254
+ process.exit(1);
255
+ }
256
+ db.close();
257
+ });
258
+ // ── Hidden maintenance commands ──
259
+ program
260
+ .command("ingest", { hidden: true })
261
+ .description("Re-ingest all Claude Code session files (backfill)")
262
+ .option("-f, --force", "Re-ingest sessions that already exist in the database")
263
+ .option("--no-tag", "Skip tagging turns with the active task context")
264
+ .action(async (opts) => {
265
+ const db = getDb(envDbPath());
266
+ const repo = new SessionRepo(db);
267
+ console.log("Scanning for Claude Code session files...");
268
+ const result = await ingestAllSessions(repo, { force: opts.force, noTag: opts.tag === false });
269
+ console.log(`Ingested: ${result.ingested} Skipped: ${result.skipped}`);
270
+ db.close();
271
+ });
272
+ program
273
+ .command("db-clean", { hidden: true })
274
+ .description("Remove invalid/test rows from the database")
275
+ .option("--session <id>", "Remove all data for a specific session ID")
276
+ .action((opts) => {
277
+ const db = getDb(envDbPath());
278
+ if (opts.session) {
279
+ const id = opts.session;
280
+ db.transaction(() => {
281
+ db.prepare(`DELETE FROM task_tags WHERE turn_id IN (SELECT id FROM turns WHERE session_id = ?)`).run(id);
282
+ for (const table of ["otel_metrics", "otel_events", "hook_events", "tool_uses", "turns"]) {
283
+ db.prepare(`DELETE FROM ${table} WHERE session_id = ?`).run(id);
284
+ }
285
+ db.prepare(`DELETE FROM sessions WHERE id = ?`).run(id);
286
+ })();
287
+ console.log(`Removed all data for session: ${id}`);
288
+ }
289
+ else {
290
+ const minDate = "2025-01-01";
291
+ const result = db.prepare(`
292
+ SELECT COUNT(*) as n FROM otel_metrics WHERE timestamp < ?
293
+ `).get(minDate);
294
+ if (result.n === 0) {
295
+ console.log("Nothing to clean.");
296
+ }
297
+ else {
298
+ db.prepare(`DELETE FROM otel_metrics WHERE timestamp < ?`).run(minDate);
299
+ db.prepare(`DELETE FROM otel_events WHERE timestamp < ?`).run(minDate);
300
+ console.log(`Removed ${result.n} row(s) with timestamps before ${minDate}.`);
301
+ }
302
+ }
303
+ db.close();
304
+ });
305
+ return program;
306
+ }
307
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { SessionRow, TurnRow } from "../storage/db.js";
2
+ export declare function formatSessionList(sessions: SessionRow[]): string;
3
+ export declare function formatSessionDetail(session: SessionRow, turns: TurnRow[]): string;
4
+ export declare function formatStats(stats: Record<string, unknown>): string;
5
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE5D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,MAAM,CA+BhE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,CAwDjF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAWlE"}
@@ -0,0 +1,115 @@
1
+ export function formatSessionList(sessions) {
2
+ if (sessions.length === 0)
3
+ return "No sessions found.";
4
+ const lines = [];
5
+ lines.push(pad("SESSION ID", 40) +
6
+ pad("STARTED", 22) +
7
+ pad("TURNS", 7) +
8
+ pad("TOKENS (in/out)", 20) +
9
+ pad("COST", 10) +
10
+ "MODEL");
11
+ lines.push("─".repeat(120));
12
+ for (const s of sessions) {
13
+ const started = formatTimestamp(s.started_at);
14
+ const tokens = `${fmtNum(s.total_input_tokens)}/${fmtNum(s.total_output_tokens)}`;
15
+ const cost = `$${s.total_cost_usd.toFixed(4)}`;
16
+ const id = s.id.length > 36 ? s.id.slice(0, 36) + "…" : s.id;
17
+ lines.push(pad(id, 40) +
18
+ pad(started, 22) +
19
+ pad(String(s.total_turns), 7) +
20
+ pad(tokens, 20) +
21
+ pad(cost, 10) +
22
+ (s.model ?? "—"));
23
+ }
24
+ return lines.join("\n");
25
+ }
26
+ export function formatSessionDetail(session, turns) {
27
+ const lines = [];
28
+ lines.push(`Session: ${session.id}`);
29
+ lines.push(`Project: ${session.project_path ?? "—"}`);
30
+ lines.push(`Started: ${formatTimestamp(session.started_at)}`);
31
+ lines.push(`Ended: ${session.ended_at ? formatTimestamp(session.ended_at) : "—"}`);
32
+ lines.push(`Model: ${session.model ?? "—"}`);
33
+ lines.push("");
34
+ lines.push("── Token Usage ──");
35
+ lines.push(` Input tokens: ${fmtNum(session.total_input_tokens)}`);
36
+ lines.push(` Output tokens: ${fmtNum(session.total_output_tokens)}`);
37
+ lines.push(` Cache read tokens: ${fmtNum(session.total_cache_read_tokens)}`);
38
+ lines.push(` Cache creation tokens: ${fmtNum(session.total_cache_creation_tokens)}`);
39
+ lines.push(` Total cost: $${session.total_cost_usd.toFixed(4)}`);
40
+ lines.push(` Total turns: ${session.total_turns}`);
41
+ lines.push(` Total duration: ${formatDuration(session.total_duration_ms)}`);
42
+ if (turns.length > 0) {
43
+ lines.push("");
44
+ lines.push("── Conversation ──");
45
+ for (const turn of turns) {
46
+ const role = turn.role === "assistant" ? "🤖 Assistant" : "👤 User";
47
+ lines.push("");
48
+ lines.push(`[${turn.turn_index}] ${role} (${formatTimestamp(turn.timestamp)})`);
49
+ if (turn.input_tokens > 0 || turn.output_tokens > 0) {
50
+ lines.push(` tokens: ${fmtNum(turn.input_tokens)} in / ${fmtNum(turn.output_tokens)} out` +
51
+ (turn.cost_usd > 0 ? ` | cost: $${turn.cost_usd.toFixed(4)}` : "") +
52
+ (turn.model ? ` | model: ${turn.model}` : ""));
53
+ }
54
+ if (turn.content_text) {
55
+ const preview = turn.content_text.length > 500
56
+ ? turn.content_text.slice(0, 500) + "…"
57
+ : turn.content_text;
58
+ lines.push(` ${preview.replace(/\n/g, "\n ")}`);
59
+ }
60
+ if (turn.tool_calls) {
61
+ try {
62
+ const calls = JSON.parse(turn.tool_calls);
63
+ for (const call of calls) {
64
+ lines.push(` 🔧 ${call.toolName}`);
65
+ }
66
+ }
67
+ catch {
68
+ // skip
69
+ }
70
+ }
71
+ }
72
+ }
73
+ return lines.join("\n");
74
+ }
75
+ export function formatStats(stats) {
76
+ const lines = [];
77
+ lines.push("── Aggregate Statistics ──");
78
+ lines.push(` Total sessions: ${stats.total_sessions ?? 0}`);
79
+ lines.push(` Total input tokens: ${fmtNum(Number(stats.total_input_tokens ?? 0))}`);
80
+ lines.push(` Total output tokens: ${fmtNum(Number(stats.total_output_tokens ?? 0))}`);
81
+ lines.push(` Total cache read: ${fmtNum(Number(stats.total_cache_read_tokens ?? 0))}`);
82
+ lines.push(` Total cost: $${Number(stats.total_cost_usd ?? 0).toFixed(4)}`);
83
+ lines.push(` Total turns: ${stats.total_turns ?? 0}`);
84
+ lines.push(` Total active time: ${formatDuration(Number(stats.total_duration_ms ?? 0))}`);
85
+ return lines.join("\n");
86
+ }
87
+ function pad(str, len) {
88
+ return str.padEnd(len);
89
+ }
90
+ function fmtNum(n) {
91
+ return n.toLocaleString("en-US");
92
+ }
93
+ function formatTimestamp(ts) {
94
+ try {
95
+ return new Date(ts).toLocaleString();
96
+ }
97
+ catch {
98
+ return ts;
99
+ }
100
+ }
101
+ function formatDuration(ms) {
102
+ if (ms < 1000)
103
+ return `${ms}ms`;
104
+ const seconds = Math.floor(ms / 1000);
105
+ if (seconds < 60)
106
+ return `${seconds}s`;
107
+ const minutes = Math.floor(seconds / 60);
108
+ const remainingSeconds = seconds % 60;
109
+ if (minutes < 60)
110
+ return `${minutes}m ${remainingSeconds}s`;
111
+ const hours = Math.floor(minutes / 60);
112
+ const remainingMinutes = minutes % 60;
113
+ return `${hours}h ${remainingMinutes}m`;
114
+ }
115
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"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"}