claude-rpc 0.5.0 → 0.6.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.
- package/README.md +96 -136
- package/config.example.json +2 -65
- package/package.json +1 -1
- package/src/card.js +2 -1
- package/src/cli.js +105 -24
- package/src/config.js +89 -0
- package/src/daemon.js +113 -19
- package/src/doctor.js +10 -30
- package/src/git.js +2 -2
- package/src/hook.js +1 -1
- package/src/install.js +51 -5
- package/src/pricing.js +29 -6
- package/src/privacy.js +1 -1
- package/src/scanner.js +2 -2
- package/src/server/api.js +6 -3
- package/src/server/page.js +52 -14
- package/src/server/sse.js +2 -2
- package/src/tui.js +6 -7
- package/src/ui.js +89 -0
- package/src/version.js +26 -0
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# claude-rpc
|
|
9
9
|
|
|
10
10
|
**Discord Rich Presence for [Claude Code](https://claude.com/claude-code).**
|
|
11
|
-
Your model, project, current tool, tokens, and lifetime stats — live in your Discord profile.
|
|
11
|
+
Your model, project, current tool, tokens, and lifetime stats — live in your Discord profile. Driven by Claude Code's hooks. Zero polling, zero overhead between sessions.
|
|
12
12
|
|
|
13
13
|
[](LICENSE)
|
|
14
14
|
[](https://nodejs.org)
|
|
@@ -23,17 +23,47 @@ Your model, project, current tool, tokens, and lifetime stats — live in your D
|
|
|
23
23
|
<img src="docs/demo.gif" width="560" alt="Discord Rich Presence card: Claude Code, working in claude-rpc on Opus 4.7" />
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
## Install
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
**Windows** (no Node required) — [grab the latest portable exe](https://github.com/rar-file/claude-rpc/releases/latest):
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
```sh
|
|
31
|
+
claude-rpc setup
|
|
32
|
+
claude-rpc start
|
|
33
|
+
```
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
That's it. Open Claude Code in any project — the daemon picks it up within a second.
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
The Discord *desktop* app must be running (the browser client doesn't expose the local IPC). Something looks wrong? `claude-rpc doctor`.
|
|
38
|
+
|
|
39
|
+
<details>
|
|
40
|
+
<summary><b>Other platforms / from source</b></summary>
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
git clone https://github.com/rar-file/claude-rpc.git
|
|
44
|
+
cd claude-rpc
|
|
45
|
+
npm install
|
|
46
|
+
node ./src/cli.js setup
|
|
47
|
+
node ./src/cli.js start
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or `npm install -g claude-rpc` for the global bin.
|
|
51
|
+
</details>
|
|
52
|
+
|
|
53
|
+
<details>
|
|
54
|
+
<summary><b>Use your own Discord app</b></summary>
|
|
55
|
+
|
|
56
|
+
A working Discord application is bundled into the default config — you don't need to register your own to get started. To use a different app name on the card, create one in the [Discord Developer Portal](https://discord.com/developers/applications), copy the Application ID, and put it into `clientId` in your config:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
# Linux / macOS
|
|
60
|
+
echo '{ "clientId": "YOUR_ID" }' > ~/.config/claude-rpc/config.json
|
|
61
|
+
# Windows
|
|
62
|
+
echo { "clientId": "YOUR_ID" } > %APPDATA%\claude-rpc\config.json
|
|
63
|
+
```
|
|
35
64
|
|
|
36
|
-
|
|
65
|
+
Run `claude-rpc upgrade-config` afterwards if you carry forward a v0.3-era config.
|
|
66
|
+
</details>
|
|
37
67
|
|
|
38
68
|
## Features
|
|
39
69
|
|
|
@@ -45,18 +75,19 @@ Driven entirely by Claude Code's hook system. Zero polling, zero overhead betwee
|
|
|
45
75
|
| 🎞️ **Status art** | Large image swaps between *working*, *thinking*, *idle*, *stale*, *notification* |
|
|
46
76
|
| 🔁 **Rotation frames** | Cycle through today's stats, streak, top file, lifetime totals, anything you template |
|
|
47
77
|
| 🐙 **Auto GitHub button** | When your cwd is a git repo with a github origin, a *View on GitHub* button appears |
|
|
78
|
+
| 🔒 **Privacy mode** | Per-project `.claude-rpc.json`, runtime `claude-rpc private` toggle, glob-pattern matchers, and auto-detection of GitHub private repos via `gh` |
|
|
48
79
|
|
|
49
80
|
**Beyond Discord**
|
|
50
81
|
|
|
51
82
|
| | |
|
|
52
83
|
| :--- | :--- |
|
|
53
|
-
| 📊 **All-time aggregates** | Hours, prompts, tokens, streaks, hotspots,
|
|
84
|
+
| 📊 **All-time aggregates** | Hours, prompts, tokens, streaks, hotspots, lines changed, languages, cost, bash usage, web domains, subagent runs — incremental scanner over `~/.claude/projects/*.jsonl` |
|
|
54
85
|
| 💰 **Cost estimate** | Per-model spend (Opus/Sonnet/Haiku) using public list prices — editable in `src/pricing.js` |
|
|
55
86
|
| 🧠 **Insights** | `claude-rpc insights` generates 3–5 contextual lines: weekly trend, peak weekday, hotspot file, cost pace, streak progress |
|
|
56
87
|
| 🖥️ **CLI dashboard** | `claude-rpc status` — heatmap, hour histogram, top tools / files / projects / languages / bash commands / cost |
|
|
57
|
-
| 🌐 **Web dashboard** | `claude-rpc serve` — range selector (7d / 30d / 90d / 1y / All), live SSE updates, project drilldown, day-detail modal,
|
|
58
|
-
| 🪪 **
|
|
59
|
-
| ⚙️ **Config GUI** | Electron app
|
|
88
|
+
| 🌐 **Web dashboard** | `claude-rpc serve` — range selector (7d / 30d / 90d / 1y / All), live SSE updates, project drilldown, day-detail modal, theme toggle |
|
|
89
|
+
| 🪪 **Badges & cards** | `claude-rpc badge --metric hours --range 7d` (Shields-style SVG) and `claude-rpc card --range year` (poster-style summary) |
|
|
90
|
+
| ⚙️ **Config GUI** | Electron app — six tabs: Presence, Discord, Assets, Timing, Daemon, Stats |
|
|
60
91
|
|
|
61
92
|
## Screens
|
|
62
93
|
|
|
@@ -67,69 +98,32 @@ Driven entirely by Claude Code's hook system. Zero polling, zero overhead betwee
|
|
|
67
98
|
</tr>
|
|
68
99
|
</table>
|
|
69
100
|
|
|
70
|
-
## Install
|
|
71
|
-
|
|
72
|
-
**Windows (no Node required)** — grab the latest portable exe:
|
|
73
|
-
|
|
74
|
-
```sh
|
|
75
|
-
# From https://github.com/rar-file/claude-rpc/releases/latest
|
|
76
|
-
# Download claude-rpc.exe, drop it anywhere on PATH.
|
|
77
|
-
claude-rpc setup
|
|
78
|
-
claude-rpc start
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
**Any OS (from source)** — Node 18+:
|
|
82
|
-
|
|
83
|
-
```sh
|
|
84
|
-
git clone https://github.com/rar-file/claude-rpc.git
|
|
85
|
-
cd claude-rpc
|
|
86
|
-
npm install
|
|
87
|
-
cp config.example.json config.json
|
|
88
|
-
npm link # optional, makes `claude-rpc` global
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Requires the Discord **desktop** client (RPC IPC is unavailable in the browser client) and Claude Code with hook support.
|
|
92
|
-
|
|
93
|
-
## Quick start
|
|
94
|
-
|
|
95
|
-
```sh
|
|
96
|
-
node ./src/cli.js setup # register hooks into ~/.claude/settings.json
|
|
97
|
-
node ./src/cli.js start # launch the daemon (detached)
|
|
98
|
-
node ./src/cli.js status # CLI dashboard
|
|
99
|
-
node ./src/cli.js serve # web dashboard at http://127.0.0.1:47474
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Open Claude Code in any project. Hooks fire on `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `Notification`, `Stop`, and `SessionEnd`, and the daemon pushes updated presence to Discord within a second.
|
|
103
|
-
|
|
104
|
-
If you `npm link` (or install the packaged exe), every command above becomes `claude-rpc <command>`.
|
|
105
|
-
|
|
106
|
-
## Discord app setup
|
|
107
|
-
|
|
108
|
-
1. Open the [Discord Developer Portal](https://discord.com/developers/applications) and create a new application named something like `Claude Code`.
|
|
109
|
-
2. Copy the **Application ID** into `config.json` under `clientId`.
|
|
110
|
-
3. *(Optional)* Under **Rich Presence → Art Assets**, upload images named `claude`, `working`, `thinking`, `idle`, `notification` to match the keys in `statusAssets`.
|
|
111
|
-
4. Or skip uploading and use direct URLs in `statusAssets` (e.g. `"https://example.com/working.gif"`). Modern Discord clients fetch them through their media proxy.
|
|
112
|
-
|
|
113
101
|
## Commands
|
|
114
102
|
|
|
115
|
-
| Command
|
|
116
|
-
|
|
|
117
|
-
| `setup`
|
|
118
|
-
| `uninstall`
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
103
|
+
| Command | Description |
|
|
104
|
+
| ---------------- | -------------------------------------------------------- |
|
|
105
|
+
| `setup` | Install Claude Code hooks (`~/.claude/settings.json`) |
|
|
106
|
+
| `uninstall` | Remove Claude Code hooks |
|
|
107
|
+
| `upgrade-config` | Re-run idempotent migrations on `config.json` |
|
|
108
|
+
| `start` | Start the daemon (detached) |
|
|
109
|
+
| `stop` | Stop the daemon |
|
|
110
|
+
| `restart` | Stop then start |
|
|
111
|
+
| `status` | Current session + all-time stats (interactive TUI or `--dump`) |
|
|
112
|
+
| `today` | Today's stats + 24h histogram |
|
|
113
|
+
| `week` | This week's stats + daily breakdown |
|
|
114
|
+
| `serve` | Open the local web dashboard (port 47474) |
|
|
115
|
+
| `preview` | Show how each rotation frame renders right now |
|
|
116
|
+
| `scan` / `rescan`| Incremental / forced re-parse of `~/.claude/projects` |
|
|
117
|
+
| `backfill <dir>` | Import transcripts from any folder (backup, other machine) |
|
|
118
|
+
| `insights` | Print 3–5 auto-generated insight lines |
|
|
119
|
+
| `badge` | Render a Shields-style SVG (`--metric` `--range` `--out`)|
|
|
120
|
+
| `card` | Poster-style SVG summary (`--range year\|month\|week\|all`) |
|
|
121
|
+
| `private` / `public` / `privacy` | Per-cwd visibility toggles + status |
|
|
122
|
+
| `doctor` | Diagnostic checklist — common-failure triage |
|
|
123
|
+
| `tail` / `logs` | Tail the daemon log |
|
|
124
|
+
| `daemon` | Run the daemon in the foreground (debug) |
|
|
125
|
+
|
|
126
|
+
Exit codes: `0` ok · `1` user error · `2` system error · `3` wrong state. `--version` and `--help` work as expected.
|
|
133
127
|
|
|
134
128
|
## Config GUI
|
|
135
129
|
|
|
@@ -137,7 +131,8 @@ If you `npm link` (or install the packaged exe), every command above becomes `cl
|
|
|
137
131
|
cd dashboard
|
|
138
132
|
npm install
|
|
139
133
|
npm start # dev mode
|
|
140
|
-
npm run
|
|
134
|
+
npm run dist:mac # → .dmg
|
|
135
|
+
npm run dist:win # → portable .exe
|
|
141
136
|
```
|
|
142
137
|
|
|
143
138
|
The Electron app reads and writes `config.json` directly. The daemon hot-reloads.
|
|
@@ -162,11 +157,11 @@ Three cooperating pieces, glued by JSON files on disk.
|
|
|
162
157
|
└────────────┘
|
|
163
158
|
```
|
|
164
159
|
|
|
165
|
-
1. **Hook** (`src/hook.js`) — Claude Code spawns it on every lifecycle event. Parses the event
|
|
166
|
-
2. **Daemon** (`src/daemon.js`) — Long-running. Connects to Discord's local IPC, watches the state file plus periodic transcript scans, pushes presence frames every few seconds.
|
|
167
|
-
3. **Scanner** (`src/scanner.js`) — Walks `~/.claude/projects/**/*.jsonl` transcripts for all-time aggregates
|
|
160
|
+
1. **Hook** (`src/hook.js`) — Claude Code spawns it on every lifecycle event. Parses the JSON event from stdin and mutates the shared state file.
|
|
161
|
+
2. **Daemon** (`src/daemon.js`) — Long-running. Connects to Discord's local IPC, watches the state file plus periodic transcript scans, pushes presence frames every few seconds. Exponential backoff with jitter on reconnect.
|
|
162
|
+
3. **Scanner** (`src/scanner.js`) — Walks `~/.claude/projects/**/*.jsonl` transcripts for all-time aggregates. Cached at `~/.claude-rpc/aggregate.json` for incremental updates.
|
|
168
163
|
|
|
169
|
-
Persistent state
|
|
164
|
+
Persistent state:
|
|
170
165
|
|
|
171
166
|
| Path | What |
|
|
172
167
|
| ---- | ---- |
|
|
@@ -175,54 +170,34 @@ Persistent state lives in a few well-known places:
|
|
|
175
170
|
| `~/.claude-rpc/scan-cache.json` | Per-transcript scan cache |
|
|
176
171
|
| `~/.claude/settings.json` | Hook registrations (managed by `setup`) |
|
|
177
172
|
|
|
173
|
+
User config lives at `%APPDATA%\claude-rpc\config.json` (Windows), `~/Library/Application Support/claude-rpc/config.json` (macOS), or `$XDG_CONFIG_HOME/claude-rpc/config.json` (Linux). It only needs to hold *overrides* — defaults are baked into the binary.
|
|
174
|
+
|
|
178
175
|
<details>
|
|
179
176
|
<summary><b>Configuration reference</b></summary>
|
|
180
177
|
|
|
181
|
-
|
|
178
|
+
Every key is optional. The shipped defaults work out of the box. Override what you want:
|
|
182
179
|
|
|
183
180
|
| Key | Default | Notes |
|
|
184
181
|
| ------------------------- | ------- | ------------------------------------------------------------------- |
|
|
185
|
-
| `clientId` |
|
|
182
|
+
| `clientId` | bundled | Discord application ID (a working public app ships by default) |
|
|
186
183
|
| `updateIntervalMs` | `4000` | How often the daemon pushes to Discord |
|
|
187
184
|
| `rotationIntervalMs` | `12000` | How fast rotation frames cycle |
|
|
188
185
|
| `rescanIntervalSec` | `300` | How often transcripts are re-aggregated |
|
|
189
186
|
| `idleThresholdSec` | `60` | No activity for this long → status `idle` |
|
|
190
|
-
| `staleSessionMin` | `5` | No activity
|
|
187
|
+
| `staleSessionMin` | `5` | No activity (minutes) → status `stale`; presence cleared |
|
|
191
188
|
| `notificationWindowSec` | `8` | How long the `notification` status sticks |
|
|
192
189
|
| `showElapsed` | `true` | Include the elapsed timer |
|
|
193
190
|
| `activityType` | `0` | `0` Playing, `2` Listening, `3` Watching, `5` Competing |
|
|
194
|
-
| `statusAssets` |
|
|
195
|
-
| `presence.
|
|
196
|
-
| `presence.
|
|
197
|
-
| `presence.
|
|
198
|
-
| `presence.
|
|
199
|
-
| `presence.
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
202
|
-
|
|
203
|
-
### Rotation frames
|
|
204
|
-
|
|
205
|
-
```jsonc
|
|
206
|
-
{
|
|
207
|
-
"presence": {
|
|
208
|
-
"rotation": [
|
|
209
|
-
{ "details": "{statusVerbose} in {project}", "state": "{modelPretty}" },
|
|
210
|
-
{ "details": "{currentToolPretty} · {currentFilePretty}",
|
|
211
|
-
"state": "{tokensFmt} tokens",
|
|
212
|
-
"requires": ["currentFile"] },
|
|
213
|
-
{ "details": "Today · {todayHours}",
|
|
214
|
-
"state": "{todayPromptsLabel}",
|
|
215
|
-
"requires": ["todayActiveMs"] }
|
|
216
|
-
]
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Each frame has:
|
|
191
|
+
| `statusAssets` | gifs | Image per status (working / thinking / idle / stale / notification) |
|
|
192
|
+
| `presence.byStatus` | full | Per-status template block (preferred over `rotation`) |
|
|
193
|
+
| `presence.rotation` | — | Legacy: flat array of `{ details, state, requires? }` |
|
|
194
|
+
| `presence.buttons` | one | Up to 2 `{ label, url }` buttons |
|
|
195
|
+
| `presence.largeImageKey` | gif | Fallback large image when no `statusAssets` match |
|
|
196
|
+
| `presence.largeImageText` | tpl | Tooltip on hover |
|
|
197
|
+
| `privacy.patterns` | `[]` | Glob list of cwd basenames to treat as private |
|
|
198
|
+
| `privacy.mode` | hidden | What `patterns` does — `hidden` / `name-only` / `public` |
|
|
222
199
|
|
|
223
|
-
|
|
224
|
-
- `state` — lighter second line (Discord max 128 chars)
|
|
225
|
-
- `requires` *(optional)* — a variable name or array of names. The frame is skipped if any required variable is empty / `0`. Lets you have context-dependent frames (e.g. only show the *current tool* frame when there's actually a tool running).
|
|
200
|
+
Image precedence: `statusAssets[status]` → `modelAssets[opus|sonnet|haiku]` → `presence.largeImageKey`.
|
|
226
201
|
|
|
227
202
|
</details>
|
|
228
203
|
|
|
@@ -240,60 +215,45 @@ Both `details` and `state` (and button labels and URLs) support `{name}` substit
|
|
|
240
215
|
| `{currentFilePretty}` | `src/app/page.tsx` |
|
|
241
216
|
| `{tokensFmt}` | `2.3k` |
|
|
242
217
|
| `{messagesLabel}` | `8 prompts` |
|
|
243
|
-
| `{projectSessionLabel}` | `Session #1` |
|
|
244
|
-
| `{projectHours}` | `22m` |
|
|
245
218
|
| `{todayHours}` | `56m` |
|
|
246
219
|
| `{weekHours}` | `3.1h` |
|
|
247
220
|
| `{streakLabel}` | `7-day streak` |
|
|
248
|
-
| `{daysSinceFirstLabel}` | `Day 31` |
|
|
249
221
|
| `{allHours}` | `52h` |
|
|
250
222
|
| `{allTokensFmt}` | `2.82B` |
|
|
251
223
|
| `{peakHour}` | `22:00` |
|
|
252
224
|
| `{topEditedFile}` | `index.html` |
|
|
253
225
|
| `{linesAddedFmt}` | `24k` |
|
|
254
|
-
| `{todayLinesAddedFmt}` | `320` |
|
|
255
|
-
| `{linesNetFmt}` | `+18k` |
|
|
256
226
|
| `{topLanguage}` | `TypeScript` |
|
|
257
|
-
| `{languagesLabel}` | `TypeScript · Python · Rust` |
|
|
258
|
-
| `{topBashCmdLabel}` | `git × 820` |
|
|
259
|
-
| `{topDomainLabel}` | `docs.anthropic.com × 28` |
|
|
260
|
-
| `{subagentLabel}` | `Explore × 18` |
|
|
261
|
-
| `{mcpToolPercentLabel}` | `12% MCP` |
|
|
262
227
|
| `{todayCostFmt}` | `$1.23` |
|
|
263
228
|
| `{allCostFmt}` | `$89.42` |
|
|
264
|
-
| `{
|
|
265
|
-
| `{startTimeLabel}` | `started 09:14` |
|
|
229
|
+
| `{gitBranch}` | `main` |
|
|
266
230
|
|
|
267
|
-
Run `
|
|
231
|
+
Run `claude-rpc preview` to see every frame rendered with your real data, including which ones would be hidden by their `requires`. Run `claude-rpc vars` for the full machine-readable list.
|
|
268
232
|
|
|
269
233
|
</details>
|
|
270
234
|
|
|
271
235
|
## Badges
|
|
272
236
|
|
|
273
|
-
Generate a Shields-style SVG you can drop into a README:
|
|
274
|
-
|
|
275
237
|
```sh
|
|
276
|
-
claude-rpc badge --metric hours --range 7d
|
|
238
|
+
claude-rpc badge --metric hours --range 7d --out claude-hours.svg
|
|
277
239
|
claude-rpc badge --metric streak --out claude-streak.svg
|
|
278
240
|
claude-rpc badge --metric cost --range 30d --out claude-cost.svg
|
|
279
241
|
claude-rpc badge --metric lines --range all --out claude-lines.svg
|
|
280
242
|
```
|
|
281
243
|
|
|
282
|
-
|
|
244
|
+
Live via the dashboard too: `http://127.0.0.1:47474/api/badge.svg?metric=hours&range=7d`.
|
|
283
245
|
|
|
284
|
-
|
|
285
|
-
http://127.0.0.1:47474/api/badge.svg?metric=hours&range=7d
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
Cost numbers come from `src/pricing.js`, seeded with **approximate** public list prices for Anthropic models. Edit that file to override — your actual Claude Code subscription bill is unrelated.
|
|
246
|
+
Cost numbers come from `src/pricing.js`, seeded with **approximate** public list prices. Your actual Claude Code subscription bill is unrelated.
|
|
289
247
|
|
|
290
248
|
## Troubleshooting
|
|
291
249
|
|
|
292
|
-
**
|
|
250
|
+
**First step is always `claude-rpc doctor`.** It checks Node version, hook registration, daemon liveness, Discord connection, aggregate freshness, and privacy resolution — with a one-line fix hint per failure.
|
|
251
|
+
|
|
252
|
+
**Discord doesn't pick up presence.** The Discord *desktop* app must be running. The browser client doesn't expose the local IPC. Run `claude-rpc tail` to watch the daemon log live.
|
|
293
253
|
|
|
294
|
-
**Hooks don't fire.** Run `claude-rpc setup` and check the `hooks` section of `~/.claude/settings.json`. Restart Claude Code
|
|
254
|
+
**Hooks don't fire.** Run `claude-rpc setup` and check the `hooks` section of `~/.claude/settings.json`. Restart Claude Code so it re-reads hook config. `setup` now test-fires a SessionStart through the same launcher Claude Code will use, so a broken hook command should be caught at install time.
|
|
295
255
|
|
|
296
|
-
**
|
|
256
|
+
**Config error.** A bad `config.json` won't brick the daemon any more — it logs one line and falls back to baked-in defaults. Check the daemon log via `claude-rpc tail` to see the parse error.
|
|
297
257
|
|
|
298
258
|
## License
|
|
299
259
|
|
package/config.example.json
CHANGED
|
@@ -1,67 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"updateIntervalMs": 4000,
|
|
5
|
-
"rotationIntervalMs": 12000,
|
|
6
|
-
"rescanIntervalSec": 300,
|
|
7
|
-
"idleThresholdSec": 60,
|
|
8
|
-
"staleSessionMin": 5,
|
|
9
|
-
"hideWhenStale": true,
|
|
10
|
-
"notificationWindowSec": 8,
|
|
11
|
-
"showElapsed": true,
|
|
12
|
-
"activityType": 0,
|
|
13
|
-
"statusAssets": {
|
|
14
|
-
"working": "https://cdn.qualit.ly/clawd-working-building.gif",
|
|
15
|
-
"thinking": "https://cdn.qualit.ly/clawd-working-typing.gif",
|
|
16
|
-
"idle": "https://cdn.qualit.ly/clawd-sleeping.gif",
|
|
17
|
-
"stale": "https://cdn.qualit.ly/clawd-sleeping.gif",
|
|
18
|
-
"notification": "https://cdn.qualit.ly/clawd-notification.gif"
|
|
19
|
-
},
|
|
20
|
-
"presence": {
|
|
21
|
-
"largeImageKey": "https://cdn.qualit.ly/clawd-sleeping.gif",
|
|
22
|
-
"largeImageText": "{modelPretty} · {allHours} on Claude · {streakLabel}",
|
|
23
|
-
"smallImageKey": "{statusIcon}",
|
|
24
|
-
"smallImageText": "{statusVerbose}",
|
|
25
|
-
"byStatus": {
|
|
26
|
-
"working": {
|
|
27
|
-
"details": "Working in {project}",
|
|
28
|
-
"state": "{currentToolPretty} · {currentFilePretty} · {tokensFmt} tokens",
|
|
29
|
-
"largeImageText": "Working on a {fileLang} file"
|
|
30
|
-
},
|
|
31
|
-
"thinking": {
|
|
32
|
-
"details": "Thinking in {project}",
|
|
33
|
-
"state": "{modelPretty} · {messagesLabel} · {tokensFmt} tokens",
|
|
34
|
-
"largeImageText": "Reasoning with {modelPretty}"
|
|
35
|
-
},
|
|
36
|
-
"notification": {
|
|
37
|
-
"details": "Waiting on you · {project}",
|
|
38
|
-
"state": "{modelPretty} · {messagesLabel}",
|
|
39
|
-
"largeImageText": "Permission needed"
|
|
40
|
-
},
|
|
41
|
-
"idle": {
|
|
42
|
-
"details": "Idle in {project}",
|
|
43
|
-
"state": "{modelPretty} · {todayHours} today",
|
|
44
|
-
"largeImageText": "Idle · {modelPretty}",
|
|
45
|
-
"rotation": [
|
|
46
|
-
{ "details": "This week · {weekHours}", "state": "{weekPromptsLabel} · {weekTokensFmt} tokens", "requires": ["weekActiveMs"] },
|
|
47
|
-
{ "details": "{streakLabel}", "state": "{daysSinceFirstLabel} · {allSessionsLabel}", "requires": ["streakIsMilestone"] },
|
|
48
|
-
{ "details": "Hotspot · {topEditedFile}", "state": "{topEditedCountLabel} all-time", "requires": ["topEditedCount"] },
|
|
49
|
-
{ "details": "{allHours} on Claude all-time", "state": "{allSessionsLabel} · {allMessagesFmt} prompts", "requires": ["allSessions"] },
|
|
50
|
-
{ "details": "Lifetime · {allTokensFmt} tokens", "state": "{allToolsFmt} tool calls · {allFilesFmt} files", "requires": ["allTools"] },
|
|
51
|
-
{ "details": "Code churn · {linesAddedFmt} added","state": "{linesNetFmt} net · {topLanguage}", "requires": ["topLanguage"] },
|
|
52
|
-
{ "details": "Cost · {todayCostFmt} today", "state": "{allCostFmt} all-time", "requires": ["allCost"] }
|
|
53
|
-
]
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"buttons": [
|
|
57
|
-
{ "label": "Claude Code", "url": "https://claude.com/claude-code" }
|
|
58
|
-
]
|
|
59
|
-
},
|
|
60
|
-
"statusIcons": {
|
|
61
|
-
"working": "working",
|
|
62
|
-
"thinking": "thinking",
|
|
63
|
-
"idle": "idle",
|
|
64
|
-
"notification": "",
|
|
65
|
-
"stale": ""
|
|
66
|
-
}
|
|
2
|
+
"_comment": "Two lines is enough. Defaults for everything else are baked into the binary (see src/default-config.js). Override any key here and it deep-merges over the shipped defaults — no need to copy the full shape.",
|
|
3
|
+
"clientId": "1506443909406920948"
|
|
67
4
|
}
|
package/package.json
CHANGED
package/src/card.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { dayKey } from './scanner.js';
|
|
15
15
|
import { fmtCost } from './pricing.js';
|
|
16
16
|
import { rangeToDays, rangeLabel, pickWindow } from './badge.js';
|
|
17
|
+
import { VERSION } from './version.js';
|
|
17
18
|
|
|
18
19
|
const W = 880;
|
|
19
20
|
const H = 540;
|
|
@@ -273,7 +274,7 @@ export function renderCard(aggregate, { range = 'year', generatedAt = new Date()
|
|
|
273
274
|
font-size="13"
|
|
274
275
|
fill="${PALETTE.inkMute}">${subtitle}</text>
|
|
275
276
|
</g>
|
|
276
|
-
${tapeSticker(W - 220, 40,
|
|
277
|
+
${tapeSticker(W - 220, 40, `claude-rpc · v${VERSION}`, { rotate: 3 })}
|
|
277
278
|
|
|
278
279
|
<!-- ── hero hours card ── -->
|
|
279
280
|
<g transform="translate(60 130)">
|