zozul-cli 0.2.0 → 0.2.2
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/README.md +82 -133
- package/dist/dashboard/index.html +2 -1
- package/package.json +1 -1
- package/src/dashboard/index.html +2 -1
package/README.md
CHANGED
|
@@ -1,182 +1,131 @@
|
|
|
1
|
-
# zozul
|
|
1
|
+
# zozul
|
|
2
2
|
|
|
3
|
-
Observability for [Claude Code](https://code.claude.com/)
|
|
3
|
+
Observability for [Claude Code](https://code.claude.com/). Track costs, sessions, and conversations across projects.
|
|
4
4
|
|
|
5
|
-
|
|
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.
|
|
5
|
+
Works locally out of the box. Optionally syncs to a remote backend for persistent storage and team visibility.
|
|
16
6
|
|
|
17
7
|
## Quick start
|
|
18
8
|
|
|
19
9
|
```bash
|
|
20
|
-
# Install globally from npm
|
|
21
10
|
npm install -g zozul-cli
|
|
22
11
|
|
|
23
|
-
# Configure Claude Code and
|
|
12
|
+
# Configure Claude Code and run as a background service
|
|
24
13
|
zozul install --service
|
|
25
14
|
|
|
26
15
|
# Open the dashboard
|
|
27
16
|
open http://localhost:7890/dashboard
|
|
28
|
-
|
|
29
|
-
# Use Claude Code normally — data appears automatically
|
|
30
|
-
claude
|
|
31
17
|
```
|
|
32
18
|
|
|
33
|
-
|
|
19
|
+
That's it. Use Claude Code normally — data appears automatically.
|
|
34
20
|
|
|
35
|
-
|
|
36
|
-
zozul install # Configure Claude Code hooks + OTEL
|
|
37
|
-
zozul serve # Start the server
|
|
38
|
-
open http://localhost:7890/dashboard
|
|
39
|
-
```
|
|
21
|
+
## What you get
|
|
40
22
|
|
|
41
|
-
|
|
23
|
+
A single local process that captures everything Claude Code does via three sources:
|
|
42
24
|
|
|
43
|
-
|
|
25
|
+
- **OTEL** — cost, tokens, active time. Streamed every ~60s. The authoritative source for spend.
|
|
26
|
+
- **Hooks** — session lifecycle, tool calls, user prompts. Real-time.
|
|
27
|
+
- **JSONL** — full conversation text, assistant responses. Read from Claude Code's transcript files.
|
|
44
28
|
|
|
45
|
-
|
|
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
|
|
29
|
+
All data lands in SQLite at `~/.zozul/zozul.db`. A web dashboard and JSON API sit on top.
|
|
52
30
|
|
|
53
|
-
##
|
|
31
|
+
## Dashboard
|
|
54
32
|
|
|
55
|
-
|
|
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 install --status` | Show whether the background service is installed and running |
|
|
61
|
-
| `zozul install --restart` | Restart the background service (picks up new builds) |
|
|
62
|
-
| `zozul install --dry-run` | Preview the config that would be installed |
|
|
63
|
-
| `zozul uninstall` | Remove zozul hooks, OTEL config, git hook, and background service |
|
|
64
|
-
| `zozul context <tags...>` | Set active task tags for tagging turns (e.g. `zozul context "UI" "Feature"`) |
|
|
65
|
-
| `zozul context --list` | List all tasks that have been used |
|
|
66
|
-
| `zozul context --clear` | Clear the active task context |
|
|
67
|
-
| `zozul sync` | Sync local data to the remote zozul backend |
|
|
68
|
-
| `zozul sync --dry-run` | Show what would be synced without sending data |
|
|
69
|
-
|
|
70
|
-
## Architecture
|
|
33
|
+
`http://localhost:7890/dashboard`
|
|
71
34
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
(every ~60s) (real-time) <project>/<uuid>.jsonl
|
|
79
|
-
| | |
|
|
80
|
-
v v v
|
|
81
|
-
/v1/metrics /hook/* fs.watch (live)
|
|
82
|
-
/v1/logs | zozul ingest (manual)
|
|
83
|
-
| | |
|
|
84
|
-
| updateSessionFromOtel |
|
|
85
|
-
| | persistSession
|
|
86
|
-
+------+------+------+------+
|
|
87
|
-
|
|
|
88
|
-
SQLite (WAL)
|
|
89
|
-
~/.zozul/zozul.db
|
|
90
|
-
|
|
|
91
|
-
+------+------+
|
|
92
|
-
| |
|
|
93
|
-
/dashboard /api/*
|
|
94
|
-
(browser) (JSON)
|
|
95
|
-
```
|
|
35
|
+
| View | What it answers |
|
|
36
|
+
|---|---|
|
|
37
|
+
| **Summary** | How much have I spent? Cost chart, project breakdown, totals. |
|
|
38
|
+
| **Tasks** | What did I work on? Groups turns by tag combination, shows cost and time per task. |
|
|
39
|
+
| **Tags** | How much per category? Per-tag stats with drill-down into individual prompts. |
|
|
40
|
+
| **Sessions** | Raw session list. Sortable, filterable, click to see full conversation. |
|
|
96
41
|
|
|
97
|
-
|
|
42
|
+
All views support time filtering (7d / 30d / All). Auto-refreshes every 10s.
|
|
98
43
|
|
|
99
|
-
|
|
44
|
+
When a remote backend is configured, the dashboard auto-detects it via health check and uses it as the data source. Falls back to local if unavailable.
|
|
100
45
|
|
|
101
|
-
|
|
46
|
+
## Task tagging
|
|
102
47
|
|
|
103
|
-
|
|
104
|
-
|---|---|---|
|
|
105
|
-
| `id`, `started_at`, `project_path`, `model` | JSONL | Set from transcript filename and content |
|
|
106
|
-
| `total_turns` | JSONL | Count of turns parsed from transcript |
|
|
107
|
-
| `total_cost_usd` | OTEL | JSONL transcripts do not include cost data |
|
|
108
|
-
| `total_duration_ms` | OTEL | Accumulated from `claude_code.active_time.total` |
|
|
109
|
-
| `total_*_tokens` (session level) | OTEL (preferred) | JSONL provides seeds; OTEL accumulates via `MAX()` |
|
|
110
|
-
| `ended_at` | Both | OTEL keeps it current as batches arrive; JSONL sets it at ingest |
|
|
48
|
+
Tag your work so costs are attributed to what you're building:
|
|
111
49
|
|
|
112
|
-
|
|
50
|
+
```bash
|
|
51
|
+
zozul context "auth" "backend" # Set active tags
|
|
52
|
+
# ... use Claude Code ...
|
|
53
|
+
git commit # Tags auto-clear on commit
|
|
54
|
+
```
|
|
113
55
|
|
|
114
|
-
|
|
56
|
+
Tags appear in the Tasks and Tags views. Turns are grouped by their tag combination.
|
|
115
57
|
|
|
116
|
-
|
|
58
|
+
## Remote sync
|
|
117
59
|
|
|
118
|
-
|
|
119
|
-
2. Watches that directory for changes via `fs.watch` (recursive, FSEvents on macOS)
|
|
120
|
-
3. Debounces per-file at 500ms and calls `ingestSessionFile` on each change
|
|
60
|
+
Optionally push data to a remote backend:
|
|
121
61
|
|
|
122
|
-
|
|
62
|
+
```bash
|
|
63
|
+
# Set in .env or environment
|
|
64
|
+
ZOZUL_API_URL=https://your-backend.example.com
|
|
65
|
+
ZOZUL_API_KEY=your-key
|
|
123
66
|
|
|
124
|
-
|
|
67
|
+
zozul sync
|
|
68
|
+
```
|
|
125
69
|
|
|
126
|
-
|
|
70
|
+
Sync is incremental (watermark-based) and also runs automatically on session end when the service is running. The dashboard switches to the remote API when available.
|
|
127
71
|
|
|
128
|
-
|
|
72
|
+
## Commands
|
|
129
73
|
|
|
130
|
-
|
|
74
|
+
| Command | Description |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `zozul serve` | Start the server on port 7890 |
|
|
77
|
+
| `zozul install` | Configure Claude Code hooks and OTEL |
|
|
78
|
+
| `zozul install --service` | Also install as a background service (auto-starts on login) |
|
|
79
|
+
| `zozul install --status` | Check if the service is running |
|
|
80
|
+
| `zozul install --restart` | Restart the service after code changes |
|
|
81
|
+
| `zozul uninstall` | Remove all hooks, config, and service |
|
|
82
|
+
| `zozul context <tags...>` | Set active task tags |
|
|
83
|
+
| `zozul context --clear` | Clear tags |
|
|
84
|
+
| `zozul sync` | Push local data to remote backend |
|
|
85
|
+
|
|
86
|
+
## How it works
|
|
131
87
|
|
|
132
|
-
|
|
88
|
+
```
|
|
89
|
+
Claude Code
|
|
90
|
+
|
|
|
91
|
+
+--- OTEL export (every ~60s) ---> /v1/metrics, /v1/logs
|
|
92
|
+
+--- Hook POSTs (real-time) -----> /hook/*
|
|
93
|
+
+--- JSONL transcripts ----------> fs.watch
|
|
94
|
+
|
|
|
95
|
+
v
|
|
96
|
+
SQLite (~/.zozul/zozul.db)
|
|
97
|
+
|
|
|
98
|
+
+--- /dashboard (browser)
|
|
99
|
+
+--- /api/* (JSON)
|
|
100
|
+
+--- zozul sync --> remote backend (optional)
|
|
101
|
+
```
|
|
133
102
|
|
|
134
|
-
|
|
135
|
-
- **Linux**: writes `~/.config/systemd/user/zozul.service` and enables it with `systemctl --user`.
|
|
103
|
+
Single process on port 7890. macOS uses launchd, Linux uses systemd.
|
|
136
104
|
|
|
137
|
-
|
|
105
|
+
### Data ownership
|
|
138
106
|
|
|
139
|
-
|
|
107
|
+
| What | Source | Notes |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| Cost | OTEL | JSONL doesn't include cost |
|
|
110
|
+
| Duration | OTEL | Accumulated from active time metrics |
|
|
111
|
+
| Tokens | OTEL (preferred) | JSONL provides initial values, OTEL accumulates |
|
|
112
|
+
| Conversation text | JSONL | Full turns, tool calls, assistant responses |
|
|
113
|
+
| Session events | Hooks | Start, end, stop, tool use |
|
|
140
114
|
|
|
141
115
|
## Configuration
|
|
142
116
|
|
|
143
|
-
|
|
117
|
+
Via `.env` or environment variables:
|
|
144
118
|
|
|
145
119
|
```bash
|
|
146
|
-
ZOZUL_PORT=7890
|
|
147
|
-
ZOZUL_DB_PATH=~/.zozul/zozul.db
|
|
148
|
-
ZOZUL_VERBOSE=1
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
OTEL_LOG_USER_PROMPTS=1 # Include prompt text in OTEL events
|
|
152
|
-
OTEL_LOG_TOOL_DETAILS=1 # Include tool names in OTEL events
|
|
120
|
+
ZOZUL_PORT=7890 # Default: 7890
|
|
121
|
+
ZOZUL_DB_PATH=~/.zozul/zozul.db # Default: ~/.zozul/zozul.db
|
|
122
|
+
ZOZUL_VERBOSE=1 # Log all events
|
|
123
|
+
ZOZUL_API_URL=https://... # Remote backend URL (optional)
|
|
124
|
+
ZOZUL_API_KEY=... # Remote backend API key (optional)
|
|
153
125
|
```
|
|
154
126
|
|
|
155
|
-
CLI flags override `.env` values.
|
|
156
|
-
|
|
157
|
-
## Data captured
|
|
158
|
-
|
|
159
|
-
| Data point | Source | Granularity |
|
|
160
|
-
|---|---|---|
|
|
161
|
-
| Token usage (input/output/cache/creation) | OTEL + JSONL | Per-session and per-turn |
|
|
162
|
-
| Cost (USD) | OTEL | Per-session, per-model |
|
|
163
|
-
| Active time | OTEL | Per-session |
|
|
164
|
-
| Turns / API calls | JSONL | Full content and metadata |
|
|
165
|
-
| User prompts | Hooks (`UserPromptSubmit`) + JSONL | Count (aggregate) + full text (per-turn) |
|
|
166
|
-
| Interruptions | Hooks (`Stop`) | Count (aggregate) |
|
|
167
|
-
| Model responses | JSONL only | Full text |
|
|
168
|
-
| Tool calls and results | Hooks + JSONL | Name, input, output |
|
|
169
|
-
| Session lifecycle | Hooks | Start, end, stop events |
|
|
170
|
-
|
|
171
127
|
## Requirements
|
|
172
128
|
|
|
173
129
|
- Node.js 18+
|
|
174
|
-
-
|
|
175
|
-
- Claude Code installed (`claude --version`)
|
|
130
|
+
- Claude Code (`claude --version`)
|
|
176
131
|
- Claude Pro, Max, Teams, Enterprise, or API key
|
|
177
|
-
|
|
178
|
-
## Updating
|
|
179
|
-
|
|
180
|
-
```bash
|
|
181
|
-
npm install -g zozul-cli
|
|
182
|
-
```
|
|
@@ -590,6 +590,7 @@
|
|
|
590
590
|
let dataSource = 'local';
|
|
591
591
|
let autoRefreshTimer = null;
|
|
592
592
|
let currentView = 'summary';
|
|
593
|
+
let currentTimeWindow = '7d';
|
|
593
594
|
let previousView = 'tasks';
|
|
594
595
|
let allTaskGroups = [];
|
|
595
596
|
let allTagStats = [];
|
|
@@ -844,7 +845,7 @@ async function loadSummary() {
|
|
|
844
845
|
const [stats, tasks, costSeries, sessionsResp] = await Promise.all([
|
|
845
846
|
fetchJson('/api/stats'),
|
|
846
847
|
fetchJson('/api/tasks'),
|
|
847
|
-
fetchJson('/api/metrics/cost?range=
|
|
848
|
+
fetchJson('/api/metrics/cost?range=' + (currentTimeWindow === 'all' ? '90d' : currentTimeWindow) + '&step=1d'),
|
|
848
849
|
fetchJson('/api/sessions?limit=500&offset=0'),
|
|
849
850
|
]);
|
|
850
851
|
renderSummaryStats({
|
package/package.json
CHANGED
package/src/dashboard/index.html
CHANGED
|
@@ -590,6 +590,7 @@
|
|
|
590
590
|
let dataSource = 'local';
|
|
591
591
|
let autoRefreshTimer = null;
|
|
592
592
|
let currentView = 'summary';
|
|
593
|
+
let currentTimeWindow = '7d';
|
|
593
594
|
let previousView = 'tasks';
|
|
594
595
|
let allTaskGroups = [];
|
|
595
596
|
let allTagStats = [];
|
|
@@ -844,7 +845,7 @@ async function loadSummary() {
|
|
|
844
845
|
const [stats, tasks, costSeries, sessionsResp] = await Promise.all([
|
|
845
846
|
fetchJson('/api/stats'),
|
|
846
847
|
fetchJson('/api/tasks'),
|
|
847
|
-
fetchJson('/api/metrics/cost?range=
|
|
848
|
+
fetchJson('/api/metrics/cost?range=' + (currentTimeWindow === 'all' ? '90d' : currentTimeWindow) + '&step=1d'),
|
|
848
849
|
fetchJson('/api/sessions?limit=500&offset=0'),
|
|
849
850
|
]);
|
|
850
851
|
renderSummaryStats({
|