claude-rpc 0.5.0 → 0.6.1
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 +156 -195
- 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 +58 -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,143 +8,119 @@
|
|
|
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 —
|
|
11
|
+
Your live model, project, current tool, tokens, and lifetime stats — in your Discord profile. Driven by the hooks Claude Code already fires. Zero polling between sessions.
|
|
12
12
|
|
|
13
13
|
[](LICENSE)
|
|
14
14
|
[](https://nodejs.org)
|
|
15
15
|
[](https://claude.com/claude-code)
|
|
16
16
|
[](https://discord.com/developers/docs/topics/rpc)
|
|
17
|
+
[](https://github.com/rar-file/claude-rpc/releases/latest)
|
|
17
18
|
|
|
18
19
|
</div>
|
|
19
20
|
|
|
20
21
|
---
|
|
21
22
|
|
|
22
23
|
<div align="center">
|
|
23
|
-
<img src="docs/demo.gif" width="560" alt="Discord Rich Presence card
|
|
24
|
+
<img src="docs/demo.gif" width="560" alt="Discord Rich Presence card showing Claude Code working in claude-rpc on Opus 4.7" />
|
|
24
25
|
</div>
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
A small Node daemon that takes the lifecycle events Claude Code already fires and pipes them into the Discord rich-presence card on your profile. Your friends see what you're building; your future self gets lifetime stats. Built solo, on weekends.
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<sub>Sample badges (your numbers, your README):</sub>
|
|
31
|
-
|
|
32
|
-
<img src="https://img.shields.io/badge/claude%20%C2%B7%207d-12.4h-4c1" alt="hours" /> <img src="https://img.shields.io/badge/streak-23%20days-fe7d37" alt="streak" /> <img src="https://img.shields.io/badge/claude%20cost%20%C2%B7%2030d-$48.20-3a7" alt="cost" /> <img src="https://img.shields.io/badge/lines%20%C2%B7%20all--time-24.1k-08c" alt="lines" /> <img src="https://img.shields.io/badge/prompts%20%C2%B7%2030d-1.2k-5865F2" alt="prompts" />
|
|
33
|
-
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
> **What's new in v0.2.0** — Cost estimation, code churn, languages, MCP/built-in split, bash + web + subagent leaderboards, redesigned web dashboard with SSE push, six-tab Electron settings GUI, new `insights` and `badge` subcommands. [Full release notes →](https://github.com/rar-file/claude-rpc/releases/tag/v0.2.0)
|
|
37
|
-
|
|
38
|
-
## Features
|
|
39
|
-
|
|
40
|
-
**In Discord**
|
|
41
|
-
|
|
42
|
-
| | |
|
|
43
|
-
| :--- | :--- |
|
|
44
|
-
| 🔴 **Live status** | Model, project, current tool/file, and token counts update as you work |
|
|
45
|
-
| 🎞️ **Status art** | Large image swaps between *working*, *thinking*, *idle*, *stale*, *notification* |
|
|
46
|
-
| 🔁 **Rotation frames** | Cycle through today's stats, streak, top file, lifetime totals, anything you template |
|
|
47
|
-
| 🐙 **Auto GitHub button** | When your cwd is a git repo with a github origin, a *View on GitHub* button appears |
|
|
48
|
-
|
|
49
|
-
**Beyond Discord**
|
|
50
|
-
|
|
51
|
-
| | |
|
|
52
|
-
| :--- | :--- |
|
|
53
|
-
| 📊 **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
|
-
| 💰 **Cost estimate** | Per-model spend (Opus/Sonnet/Haiku) using public list prices — editable in `src/pricing.js` |
|
|
55
|
-
| 🧠 **Insights** | `claude-rpc insights` generates 3–5 contextual lines: weekly trend, peak weekday, hotspot file, cost pace, streak progress |
|
|
56
|
-
| 🖥️ **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, achievements, theme toggle |
|
|
58
|
-
| 🪪 **README badges** | `claude-rpc badge --metric hours --range 7d --out h.svg` (or live at `/api/badge.svg?metric=…`) |
|
|
59
|
-
| ⚙️ **Config GUI** | Electron app with six tabs: Presence (drag-reorder, variable autocomplete, presets), Discord, Assets, Timing, Daemon (start/stop/restart, tail log), Stats |
|
|
60
|
-
|
|
61
|
-
## Screens
|
|
62
|
-
|
|
63
|
-
<table>
|
|
64
|
-
<tr>
|
|
65
|
-
<td align="center" width="50%"><b>Web dashboard</b><br/><sub><code>claude-rpc serve</code></sub><br/><br/><img src="docs/dashboard.png" alt="Web dashboard with range selector, activity chart, heatmap, cost panel, languages stack, and leaderboards" /></td>
|
|
66
|
-
<td align="center" width="50%"><b>Settings GUI</b><br/><sub><code>npm run dashboard</code></sub><br/><br/><img src="docs/electron.png" alt="Electron config editor with Presence / Discord / Assets / Timing / Daemon / Stats tabs" /></td>
|
|
67
|
-
</tr>
|
|
68
|
-
</table>
|
|
69
|
-
|
|
70
|
-
## Install
|
|
29
|
+
## install
|
|
71
30
|
|
|
72
|
-
**Windows (no Node required)** — grab the
|
|
31
|
+
**Windows (no Node required)** — [grab the portable exe from the latest release](https://github.com/rar-file/claude-rpc/releases/latest):
|
|
73
32
|
|
|
74
33
|
```sh
|
|
75
|
-
# From https://github.com/rar-file/claude-rpc/releases/latest
|
|
76
|
-
# Download claude-rpc.exe, drop it anywhere on PATH.
|
|
77
34
|
claude-rpc setup
|
|
78
35
|
claude-rpc start
|
|
79
36
|
```
|
|
80
37
|
|
|
81
|
-
|
|
38
|
+
That's the whole pitch. Open Claude Code in any project — the daemon picks it up within a second. Something looks wrong? `claude-rpc doctor`.
|
|
39
|
+
|
|
40
|
+
The Discord *desktop* app must be running. The browser client doesn't expose the local IPC bridge that Rich Presence uses.
|
|
41
|
+
|
|
42
|
+
<details>
|
|
43
|
+
<summary><b>other platforms / from source</b></summary>
|
|
82
44
|
|
|
83
45
|
```sh
|
|
84
46
|
git clone https://github.com/rar-file/claude-rpc.git
|
|
85
47
|
cd claude-rpc
|
|
86
48
|
npm install
|
|
87
|
-
|
|
88
|
-
|
|
49
|
+
node ./src/cli.js setup
|
|
50
|
+
node ./src/cli.js start
|
|
89
51
|
```
|
|
90
52
|
|
|
91
|
-
|
|
53
|
+
Or `npm install -g claude-rpc` for the global bin. Both modes survive `npm update` without losing your `clientId` — user config lives under the per-OS config dir, not inside `node_modules`.
|
|
54
|
+
</details>
|
|
55
|
+
|
|
56
|
+
<details>
|
|
57
|
+
<summary><b>use your own Discord app</b></summary>
|
|
92
58
|
|
|
93
|
-
|
|
59
|
+
A working public Discord application is bundled into the default config — you don't need to register your own to get started. If you want a different app name on the card, create one in the [Discord Developer Portal](https://discord.com/developers/applications), copy the Application ID, and drop it into your config:
|
|
94
60
|
|
|
95
61
|
```sh
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
62
|
+
# Linux
|
|
63
|
+
echo '{ "clientId": "YOUR_ID" }' > ~/.config/claude-rpc/config.json
|
|
64
|
+
# macOS
|
|
65
|
+
echo '{ "clientId": "YOUR_ID" }' > ~/Library/Application\ Support/claude-rpc/config.json
|
|
66
|
+
# Windows (PowerShell)
|
|
67
|
+
'{ "clientId": "YOUR_ID" }' | Set-Content $env:APPDATA\claude-rpc\config.json
|
|
100
68
|
```
|
|
101
69
|
|
|
102
|
-
|
|
70
|
+
`claude-rpc upgrade-config` if you're carrying forward a v0.3-era file.
|
|
71
|
+
</details>
|
|
72
|
+
|
|
73
|
+
## what claude-rpc does
|
|
103
74
|
|
|
104
|
-
|
|
75
|
+
### on discord
|
|
105
76
|
|
|
106
|
-
|
|
77
|
+
A card that updates as you work. The large image swaps between five states (working / thinking / idle / stale / notification — those gifs at the top of this README). The two lines of text rotate through frames you template — current file, today's hours, lifetime totals, top hotspot, code churn, cost — and the daemon skips frames whose required template variables are empty. The `SessionEnd` hook clears the card instantly when you close Claude Code; no "is it still running?" timeout.
|
|
107
78
|
|
|
108
|
-
|
|
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.
|
|
79
|
+
A *View on GitHub →* button appears automatically when your cwd is a git repo with a github origin. The daemon checks `.git/config` directly — no shell-out, no surprise GH API call.
|
|
112
80
|
|
|
113
|
-
|
|
81
|
+
### on your machine
|
|
114
82
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
| `today` | Today's stats + 24h histogram |
|
|
124
|
-
| `week` | This week's stats + daily breakdown |
|
|
125
|
-
| `serve` | Open the local web dashboard (port 47474) |
|
|
126
|
-
| `preview` | Show how each rotation frame renders right now |
|
|
127
|
-
| `scan` | Incrementally rescan `~/.claude/projects` for aggregates |
|
|
128
|
-
| `rescan` | Force re-parse every transcript |
|
|
129
|
-
| `insights` | Print 3–5 auto-generated insight lines |
|
|
130
|
-
| `badge` | Render a Shields-style SVG (`--metric hours\|streak\|cost\|lines`, `--range 7d\|30d\|all`, `--out file.svg`) |
|
|
131
|
-
| `tail` | Tail the daemon log |
|
|
132
|
-
| `daemon` | Run the daemon in the foreground (for debugging) |
|
|
83
|
+
Three local surfaces, all reading the same `~/.claude-rpc/aggregate.json`:
|
|
84
|
+
|
|
85
|
+
<table>
|
|
86
|
+
<tr>
|
|
87
|
+
<td align="center" width="50%"><b>web dashboard</b><br/><sub><code>claude-rpc serve</code> · port 47474</sub><br/><br/><img src="docs/dashboard.png" alt="Web dashboard with range selector, activity chart, heatmap, cost panel, languages stack, and leaderboards" /></td>
|
|
88
|
+
<td align="center" width="50%"><b>settings gui</b><br/><sub><code>npm run dashboard</code> · Electron</sub><br/><br/><img src="docs/electron.png" alt="Electron config editor with Presence / Discord / Assets / Timing / Daemon / Stats tabs" /></td>
|
|
89
|
+
</tr>
|
|
90
|
+
</table>
|
|
133
91
|
|
|
134
|
-
|
|
92
|
+
```text
|
|
93
|
+
claude-rpc status (TUI — heatmap, hour histogram, leaderboards)
|
|
94
|
+
claude-rpc today (today's stats, focused)
|
|
95
|
+
claude-rpc week (weekday breakdown)
|
|
96
|
+
claude-rpc preview (every rotation frame rendered with real data)
|
|
97
|
+
claude-rpc insights (3–5 auto-generated lines: trend, peak, hotspot)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The web dashboard pushes updates via SSE; the TUI refreshes on a 3-second tick.
|
|
101
|
+
|
|
102
|
+
### beyond your machine
|
|
103
|
+
|
|
104
|
+
Shields-style badges and a poster-style summary card you can paste into a README or a Discord message:
|
|
135
105
|
|
|
136
106
|
```sh
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
npm run build # → dist/claude-rpc-dashboard.exe (Windows portable)
|
|
107
|
+
claude-rpc badge --metric hours --range 7d --out claude-hours.svg
|
|
108
|
+
claude-rpc badge --metric streak --out claude-streak.svg
|
|
109
|
+
claude-rpc card --range year --out year-on-claude.svg
|
|
141
110
|
```
|
|
142
111
|
|
|
143
|
-
|
|
112
|
+
<div align="center">
|
|
113
|
+
<img src="site/examples/year-on-claude.svg" width="560" alt="Year-on-claude card — hours, prompts, tokens, lines, cost, daily activity strip" />
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
Live equivalents when the daemon is up:
|
|
144
117
|
|
|
145
|
-
|
|
118
|
+
- `http://127.0.0.1:47474/api/badge.svg?metric=hours&range=7d`
|
|
119
|
+
- `http://127.0.0.1:47474/api/card.svg?range=year`
|
|
146
120
|
|
|
147
|
-
|
|
121
|
+
Cost numbers come from `src/pricing.js`, seeded with **approximate** public list prices. Your actual Claude Code subscription bill is unrelated.
|
|
122
|
+
|
|
123
|
+
## three pieces, glued by json files
|
|
148
124
|
|
|
149
125
|
```
|
|
150
126
|
Claude Code Discord desktop
|
|
@@ -162,139 +138,124 @@ Three cooperating pieces, glued by JSON files on disk.
|
|
|
162
138
|
└────────────┘
|
|
163
139
|
```
|
|
164
140
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
141
|
+
No database, no message bus, no background polling when Claude Code isn't running. State on disk you can `cat` and `jq`. The single runtime dependency is `@xhayper/discord-rpc`.
|
|
142
|
+
|
|
143
|
+
1. **hook** ([`src/hook.js`](src/hook.js)) — Claude Code spawns it on every lifecycle event. Parses the JSON from stdin and mutates the shared state file. Runs in ~20ms.
|
|
144
|
+
2. **daemon** ([`src/daemon.js`](src/daemon.js)) — long-running. Connects to Discord's local IPC, watches the state file, pushes presence frames every few seconds. Exponential backoff with jitter on reconnect; `daemon.log` rotates at 5 MB.
|
|
145
|
+
3. **scanner** ([`src/scanner.js`](src/scanner.js)) — walks `~/.claude/projects/**/*.jsonl` for all-time aggregates (active time, prompts, tools, tokens, streaks, hotspots, lines, languages, cost, bash, web, subagents). Incremental — re-parses only changed files.
|
|
168
146
|
|
|
169
|
-
Persistent state
|
|
147
|
+
Persistent state, all human-readable JSON:
|
|
170
148
|
|
|
171
149
|
| Path | What |
|
|
172
150
|
| ---- | ---- |
|
|
173
151
|
| `$TMPDIR/claude-rpc/state.json` | Current session, volatile |
|
|
174
152
|
| `~/.claude-rpc/aggregate.json` | All-time aggregates |
|
|
175
153
|
| `~/.claude-rpc/scan-cache.json` | Per-transcript scan cache |
|
|
154
|
+
| `~/.claude-rpc/private-list.json` | Runtime privacy toggles |
|
|
176
155
|
| `~/.claude/settings.json` | Hook registrations (managed by `setup`) |
|
|
177
156
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
| Key | Default | Notes |
|
|
184
|
-
| ------------------------- | ------- | ------------------------------------------------------------------- |
|
|
185
|
-
| `clientId` | — | **Required.** Discord application ID |
|
|
186
|
-
| `updateIntervalMs` | `4000` | How often the daemon pushes to Discord |
|
|
187
|
-
| `rotationIntervalMs` | `12000` | How fast rotation frames cycle |
|
|
188
|
-
| `rescanIntervalSec` | `300` | How often transcripts are re-aggregated |
|
|
189
|
-
| `idleThresholdSec` | `60` | No activity for this long → status `idle` |
|
|
190
|
-
| `staleSessionMin` | `5` | No activity for this long (minutes) → status `stale`; presence is cleared |
|
|
191
|
-
| `notificationWindowSec` | `8` | How long the `notification` status sticks |
|
|
192
|
-
| `showElapsed` | `true` | Include the elapsed timer |
|
|
193
|
-
| `activityType` | `0` | `0` Playing, `2` Listening, `3` Watching, `5` Competing |
|
|
194
|
-
| `statusAssets` | `{}` | Image per status (working / thinking / idle / stale / notification) |
|
|
195
|
-
| `presence.largeImageKey` | — | Fallback large image when no `statusAssets` match |
|
|
196
|
-
| `presence.largeImageText` | — | Tooltip on hover |
|
|
197
|
-
| `presence.smallImageKey` | — | Small badge in the corner of the large image |
|
|
198
|
-
| `presence.smallImageText` | — | Tooltip on hover |
|
|
199
|
-
| `presence.rotation` | `[]` | Array of frames, each `{ details, state, requires? }` |
|
|
200
|
-
| `presence.buttons` | `[]` | Up to 2 `{ label, url }` buttons |
|
|
201
|
-
| `statusIcons` | `{}` | Small image key per status (empty string hides it) |
|
|
202
|
-
|
|
203
|
-
### Rotation frames
|
|
157
|
+
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* — every key has a baked default. `{ "clientId": "..." }` is a complete config file. Defaults live in [`src/default-config.js`](src/default-config.js); the loader deep-merges over them.
|
|
158
|
+
|
|
159
|
+
## privacy
|
|
160
|
+
|
|
161
|
+
Per-project, runtime, or auto-detected — whichever fits how you work.
|
|
204
162
|
|
|
205
163
|
```jsonc
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
{ "details": "{currentToolPretty} · {currentFilePretty}",
|
|
211
|
-
"state": "{tokensFmt} tokens",
|
|
212
|
-
"requires": ["currentFile"] },
|
|
213
|
-
{ "details": "Today · {todayHours}",
|
|
214
|
-
"state": "{todayPromptsLabel}",
|
|
215
|
-
"requires": ["todayActiveMs"] }
|
|
216
|
-
]
|
|
217
|
-
}
|
|
218
|
-
}
|
|
164
|
+
// drop at your project root: <project>/.claude-rpc.json
|
|
165
|
+
{ "private": true } // shortcut for visibility: "hidden"
|
|
166
|
+
{ "visibility": "name-only" } // project name only, no file/tool detail
|
|
167
|
+
{ "projectName": "redacted" } // show this name on Discord instead
|
|
219
168
|
```
|
|
220
169
|
|
|
221
|
-
|
|
170
|
+
Or from the command line, in any project:
|
|
222
171
|
|
|
223
|
-
|
|
224
|
-
-
|
|
225
|
-
-
|
|
172
|
+
```sh
|
|
173
|
+
claude-rpc private # add cwd to ~/.claude-rpc/private-list.json
|
|
174
|
+
claude-rpc public # remove cwd
|
|
175
|
+
claude-rpc privacy # show the resolved visibility for the current dir
|
|
176
|
+
```
|
|
226
177
|
|
|
227
|
-
|
|
178
|
+
Or globally, in `config.json`:
|
|
228
179
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
Both `details` and `state` (and button labels and URLs) support `{name}` substitution.
|
|
233
|
-
|
|
234
|
-
| Variable | Sample |
|
|
235
|
-
| ----------------------- | ------------------ |
|
|
236
|
-
| `{statusVerbose}` | `Working` |
|
|
237
|
-
| `{project}` | `claude-rpc` |
|
|
238
|
-
| `{modelPretty}` | `Opus 4.7` |
|
|
239
|
-
| `{currentToolPretty}` | `Edit` |
|
|
240
|
-
| `{currentFilePretty}` | `src/app/page.tsx` |
|
|
241
|
-
| `{tokensFmt}` | `2.3k` |
|
|
242
|
-
| `{messagesLabel}` | `8 prompts` |
|
|
243
|
-
| `{projectSessionLabel}` | `Session #1` |
|
|
244
|
-
| `{projectHours}` | `22m` |
|
|
245
|
-
| `{todayHours}` | `56m` |
|
|
246
|
-
| `{weekHours}` | `3.1h` |
|
|
247
|
-
| `{streakLabel}` | `7-day streak` |
|
|
248
|
-
| `{daysSinceFirstLabel}` | `Day 31` |
|
|
249
|
-
| `{allHours}` | `52h` |
|
|
250
|
-
| `{allTokensFmt}` | `2.82B` |
|
|
251
|
-
| `{peakHour}` | `22:00` |
|
|
252
|
-
| `{topEditedFile}` | `index.html` |
|
|
253
|
-
| `{linesAddedFmt}` | `24k` |
|
|
254
|
-
| `{todayLinesAddedFmt}` | `320` |
|
|
255
|
-
| `{linesNetFmt}` | `+18k` |
|
|
256
|
-
| `{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
|
-
| `{todayCostFmt}` | `$1.23` |
|
|
263
|
-
| `{allCostFmt}` | `$89.42` |
|
|
264
|
-
| `{weekdayLabel}` | `Thursday` |
|
|
265
|
-
| `{startTimeLabel}` | `started 09:14` |
|
|
266
|
-
|
|
267
|
-
Run `node ./src/cli.js preview` to see every frame rendered with your real data, including which ones would be hidden by their `requires`.
|
|
180
|
+
```json
|
|
181
|
+
{ "privacy": { "patterns": ["client-*", "secret-stuff"], "mode": "hidden" } }
|
|
182
|
+
```
|
|
268
183
|
|
|
269
|
-
|
|
184
|
+
If [`gh`](https://cli.github.com/) is installed and authenticated, GitHub-private repos auto-hide (`privacy.githubPrivateMode`, default `hidden` — opt out with `privacy.autoDetectGithubPrivate: false`). 5-minute cache, 1.5s timeout, silent skip when `gh` isn't there.
|
|
270
185
|
|
|
271
|
-
|
|
186
|
+
Aggregates and local dashboards are never affected. Privacy is a one-way valve between local state and Discord.
|
|
272
187
|
|
|
273
|
-
|
|
188
|
+
## customizing the card
|
|
274
189
|
|
|
275
190
|
```sh
|
|
276
|
-
claude-rpc
|
|
277
|
-
claude-rpc
|
|
278
|
-
claude-rpc badge --metric cost --range 30d --out claude-cost.svg
|
|
279
|
-
claude-rpc badge --metric lines --range all --out claude-lines.svg
|
|
191
|
+
claude-rpc preview # render every rotation frame with your real data
|
|
192
|
+
claude-rpc vars # dump the full template-variable list as JSON
|
|
280
193
|
```
|
|
281
194
|
|
|
282
|
-
|
|
195
|
+
Frames have a `requires` field; the daemon skips a frame when any of its required vars resolve empty / zero. Write seven frames knowing only the relevant ones render.
|
|
283
196
|
|
|
284
|
-
```
|
|
285
|
-
|
|
197
|
+
```jsonc
|
|
198
|
+
"idle": {
|
|
199
|
+
"details": "Idle in {project}",
|
|
200
|
+
"state": "{modelPretty} · {todayHours} today",
|
|
201
|
+
"rotation": [
|
|
202
|
+
{ "details": "This week · {weekHours}", "state": "{weekPromptsLabel} · {weekTokensFmt} tokens",
|
|
203
|
+
"requires": ["weekActiveMs"] },
|
|
204
|
+
{ "details": "Code churn · {linesAddedFmt} added",
|
|
205
|
+
"state": "{linesNetFmt} net · {topLanguage}",
|
|
206
|
+
"requires": ["topLanguage"] }
|
|
207
|
+
]
|
|
208
|
+
}
|
|
286
209
|
```
|
|
287
210
|
|
|
288
|
-
|
|
211
|
+
The full default config is in [`src/default-config.js`](src/default-config.js) — that's the canonical list of every key. ~140 template variables are available; `claude-rpc vars` is the source of truth.
|
|
289
212
|
|
|
290
|
-
##
|
|
213
|
+
## commands
|
|
291
214
|
|
|
292
|
-
|
|
215
|
+
| Command | What it does |
|
|
216
|
+
| ---------------- | ------------ |
|
|
217
|
+
| `setup` | Install Claude Code hooks (test-fires one synthetic SessionStart to prove the pipe works) |
|
|
218
|
+
| `uninstall` | Remove Claude Code hooks |
|
|
219
|
+
| `upgrade-config` | Re-run idempotent migrations on `config.json` |
|
|
220
|
+
| `start` / `stop` / `restart` | Lifecycle for the detached daemon |
|
|
221
|
+
| `status` | Interactive TUI — heatmap, hour histogram, leaderboards (`--dump` for plain output) |
|
|
222
|
+
| `today` / `week` | Focused views (today's stats, weekday breakdown) |
|
|
223
|
+
| `serve` | Open the local web dashboard (port 47474) |
|
|
224
|
+
| `preview` | Render every rotation frame against real state |
|
|
225
|
+
| `scan` / `rescan`| Incremental / forced re-parse of `~/.claude/projects` |
|
|
226
|
+
| `backfill <dir>` | Import transcripts from any folder (backup, other machine) |
|
|
227
|
+
| `insights` | Print 3–5 auto-generated lines about your week |
|
|
228
|
+
| `badge` | Shields-style SVG (`--metric` `--range` `--out`) |
|
|
229
|
+
| `card` | Poster-style SVG (`--range year\|month\|week\|all`) |
|
|
230
|
+
| `private` / `public` / `privacy` | Per-cwd visibility toggles + status |
|
|
231
|
+
| `doctor` | Diagnostic checklist with one-line fix hints |
|
|
232
|
+
| `tail` / `logs` | Tail the daemon log |
|
|
233
|
+
| `daemon` | Run the daemon in the foreground (debugging) |
|
|
234
|
+
| `vars` | Dump the full template-var list as JSON |
|
|
293
235
|
|
|
294
|
-
|
|
236
|
+
Exit codes: `0` ok · `1` user error · `2` system error · `3` wrong state. `--version` and `--help` work as expected.
|
|
237
|
+
|
|
238
|
+
## troubleshooting
|
|
239
|
+
|
|
240
|
+
**First step is always `claude-rpc doctor`.** It checks Node version, hook registration, daemon liveness, Discord IPC connection, aggregate freshness, and privacy resolution — with a one-line fix hint per failure.
|
|
241
|
+
|
|
242
|
+
- **Discord doesn't show anything.** Discord *desktop* must be running. The browser client doesn't expose the local IPC bridge. `claude-rpc tail` shows what the daemon is actually doing.
|
|
243
|
+
- **Hooks don't fire.** `claude-rpc setup` re-registers them and now test-fires a synthetic `SessionStart` end-to-end, so a broken hook command surfaces immediately. Restart Claude Code afterwards so it re-reads its hook config.
|
|
244
|
+
- **Config error.** Bad JSON in `config.json` no longer crashes anything — the daemon logs one line and falls back to baked defaults. `claude-rpc tail` shows the parse error verbatim.
|
|
245
|
+
- **Old binary path baked into hooks.** Common after manual exe replacement. `claude-rpc setup` rewrites hook entries to point at the canonical install location.
|
|
246
|
+
|
|
247
|
+
## development
|
|
248
|
+
|
|
249
|
+
```sh
|
|
250
|
+
npm test # 134 tests, ~1.7s
|
|
251
|
+
npm run start # run daemon in foreground
|
|
252
|
+
npm run serve # web dashboard against your real data
|
|
253
|
+
npm run dashboard # Electron settings GUI (dev mode)
|
|
254
|
+
npm run build:exe # SEA single-file binary for the current OS
|
|
255
|
+
```
|
|
295
256
|
|
|
296
|
-
|
|
257
|
+
Tests are `node --test` with zero deps. The CI pipeline ([release.yml](.github/workflows/release.yml)) gates the matrix build and the npm publish behind the test job. Every public export of `src/*.js` is exercised at least once.
|
|
297
258
|
|
|
298
|
-
##
|
|
259
|
+
## license
|
|
299
260
|
|
|
300
261
|
[MIT](LICENSE) © Archer Simmons
|
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)">
|