claude-code-runrate 0.4.0 → 0.5.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/CHANGELOG.md +114 -0
- package/README.md +26 -7
- package/bin/ccr.js +12 -2
- package/package.json +6 -2
- package/src/doctor.js +21 -1
- package/src/history-privacy.js +435 -0
- package/src/launch-win.js +168 -8
- package/src/pane-config.js +40 -6
- package/src/render/economy.js +30 -5
- package/src/render/shared.js +21 -1
- package/src/render/statusline.js +10 -3
- package/src/sidecar-keys.js +16 -3
- package/src/sidecar.js +30 -8
- package/src/state-dir.js +18 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Every release also has a [GitHub release](https://github.com/bingh0/ccr/releases)
|
|
4
|
+
and a release commit, and those carry the full notes — what was measured, what
|
|
5
|
+
was rejected, and why. This file is the index: enough to decide whether a
|
|
6
|
+
version is worth upgrading to, and where to read the rest.
|
|
7
|
+
|
|
8
|
+
Dates are npm publish dates, because that is when a version reached anyone.
|
|
9
|
+
|
|
10
|
+
## 0.5.0 — 2026-08-22
|
|
11
|
+
|
|
12
|
+
The Windows release. The things that could not be reasoned about were measured
|
|
13
|
+
on a real Windows 11 machine, because none of them are reachable from CI: every
|
|
14
|
+
Windows test injects the launcher, so the suite proves the argv `ccr` builds and
|
|
15
|
+
nothing about what Windows Terminal does when handed it.
|
|
16
|
+
|
|
17
|
+
- **The panes open where `ccr` was run.** `wt.exe` does not inherit the working
|
|
18
|
+
directory the way `tmux new-session` does, so every pane opened in
|
|
19
|
+
`%USERPROFILE%` while the launch record claimed otherwise — and the sidecar
|
|
20
|
+
prefers that record, so the git pane described the project in full confidence
|
|
21
|
+
for a terminal that was not in it. One source now feeds both.
|
|
22
|
+
- **Measured limits, and they are real.** Past 256 characters `wt` opens *no
|
|
23
|
+
tab at all*; a UNC path opens a tab and silently lands in `%SystemRoot%`; a
|
|
24
|
+
semicolon costs the whole tab; a backtick does not, so refusing it was wrong
|
|
25
|
+
and no longer happens. Each unusable path degrades — the panes open in the
|
|
26
|
+
default directory and stderr names which of the four reasons applied.
|
|
27
|
+
- **The sidecar pane has a cycle key** on Windows Terminal. F3 or Space. F3 has
|
|
28
|
+
three encodings and on Windows which one arrives depends on the *Node*
|
|
29
|
+
version, so `ccr` answers all three rather than pinning a Node floor for a
|
|
30
|
+
keystroke.
|
|
31
|
+
- **A pane config that says what is wrong.** A UTF-8 BOM is stripped; UTF-16 —
|
|
32
|
+
what PowerShell 5.1 writes for `>` and `Out-File` — is named as an encoding
|
|
33
|
+
problem instead of reported as bad JSON; `~\` expands as well as `~/`; and a
|
|
34
|
+
broken config says so on the panel and in `ccr doctor`, which reports pane
|
|
35
|
+
paths as *resolved*.
|
|
36
|
+
- **`ccr sidecar` resolves the instance from the working directory**, which the
|
|
37
|
+
feature files and the README had both described for a while.
|
|
38
|
+
- **The economy screen earns a decimal** where the next tenth is a decision: at
|
|
39
|
+
or past 95% used, one truncated digit, so `floor(shown)` still matches
|
|
40
|
+
`/usage`. A snapshot that stopped refreshing dims rather than blanks.
|
|
41
|
+
|
|
42
|
+
## 0.4.0 — 2026-08-07
|
|
43
|
+
|
|
44
|
+
[The instance layer and the git pane.](https://github.com/bingh0/ccr/releases/tag/v0.4.0)
|
|
45
|
+
|
|
46
|
+
- **Instances.** Every launch is a named, slotted instance under one container
|
|
47
|
+
home; the name is derived from the project or given with `-i`, and travels
|
|
48
|
+
into the window title, status line and sidebar.
|
|
49
|
+
- **A git pane** on F3: which repository the tab is in, the working tree, and
|
|
50
|
+
recent history in lanes. Read-only — it never writes to the repository.
|
|
51
|
+
- The three launchers (tmux, Windows Terminal, VS Code) take naming, titles and
|
|
52
|
+
retention from one shared path rather than the tmux path owning them.
|
|
53
|
+
|
|
54
|
+
## 0.3.0 — 2026-08-04
|
|
55
|
+
|
|
56
|
+
[External tool panes in the sidebar.](https://github.com/bingh0/ccr/releases/tag/v0.3.0)
|
|
57
|
+
|
|
58
|
+
- **Panes from other tools.** The sidebar hosts full-height read-only panes
|
|
59
|
+
rendered from small JSON blobs another tool writes beside its own artifacts.
|
|
60
|
+
You list the path in `~/.config/ccr/config.json`; F3 cycles.
|
|
61
|
+
- A rate-limit correctness fix, and `strict` type checking across the codebase.
|
|
62
|
+
|
|
63
|
+
## 0.2.4 — 2026-07-12
|
|
64
|
+
|
|
65
|
+
[VS Code sidecars stop piling up; profile state-dir fix.](https://github.com/bingh0/ccr/releases/tag/v0.2.4)
|
|
66
|
+
|
|
67
|
+
- One live sidecar per session: relaunching inside VS Code no longer
|
|
68
|
+
accumulates identical panes.
|
|
69
|
+
- Profile sessions write their snapshots to the right state dir.
|
|
70
|
+
|
|
71
|
+
## 0.2.3 — 2026-07-03
|
|
72
|
+
|
|
73
|
+
[Sidecars agree on 5h/weekly across profiles.](https://github.com/bingh0/ccr/releases/tag/v0.2.3)
|
|
74
|
+
|
|
75
|
+
- The 5h and weekly walls are one account-wide resource, but each profile only
|
|
76
|
+
captures its own; two sidecars on the same account could disagree. They
|
|
77
|
+
reconcile now.
|
|
78
|
+
|
|
79
|
+
## 0.2.2 — 2026-07-02
|
|
80
|
+
|
|
81
|
+
[Aligned meter bars + a staleness marker.](https://github.com/bingh0/ccr/releases/tag/v0.2.2)
|
|
82
|
+
|
|
83
|
+
- A barely-used window projects an enormous time-to-exhaust, which overflowed
|
|
84
|
+
the fixed time column and shoved that row's bar out of line. Long horizons
|
|
85
|
+
are capped, so the bars stay aligned and the figure reads more honestly.
|
|
86
|
+
- A stale snapshot is annotated (`· updated Nm ago`) rather than silently
|
|
87
|
+
frozen. Never a wipe — Claude Code legitimately stops ticking during a long
|
|
88
|
+
operation.
|
|
89
|
+
|
|
90
|
+
## 0.2.1 — 2026-06-26
|
|
91
|
+
|
|
92
|
+
[Windows Terminal teardown and sidecar width fixes.](https://github.com/bingh0/ccr/releases/tag/v0.2.1)
|
|
93
|
+
|
|
94
|
+
- ConPTY makes a split pane's reported width unreliable, so the panel
|
|
95
|
+
soft-wrapped. The launcher injects the computed width and the sidecar clamps
|
|
96
|
+
to the smaller of the two.
|
|
97
|
+
- On exit the sidecar pane collapses first and the border sweeps left to right,
|
|
98
|
+
fixing an ordering bug where Claude's pane could close first.
|
|
99
|
+
|
|
100
|
+
## 0.2.0 — 2026-06-26
|
|
101
|
+
|
|
102
|
+
[Native Windows and VS Code live sidecars.](https://github.com/bingh0/ccr/releases/tag/v0.2.0)
|
|
103
|
+
|
|
104
|
+
- **Native Windows** — the live sidecar hosted in Windows Terminal split panes,
|
|
105
|
+
no tmux, bash or WSL, with a graceful fallback when `wt.exe` is absent.
|
|
106
|
+
- **VS Code integrated terminal on any OS** — split-pane sidecar via a
|
|
107
|
+
clipboard one-liner.
|
|
108
|
+
- `statusLine` injected per launch through a temp settings file, so `~/.claude`
|
|
109
|
+
is never mutated.
|
|
110
|
+
|
|
111
|
+
## 0.1.0 — 2026-06-24
|
|
112
|
+
|
|
113
|
+
First publish: the CLI and status line — `economy`, `resume`, `statusline` —
|
|
114
|
+
with the live sidebar on tmux. Native Windows arrived in 0.2.0.
|
package/README.md
CHANGED
|
@@ -173,11 +173,11 @@ small JSON blob beside its own artifacts; you list that file's path in ccr's
|
|
|
173
173
|
config; the sidebar cycles between the economy panel, the built-in git pane, and
|
|
174
174
|
each configured pane.
|
|
175
175
|
|
|
176
|
-
**Cycling views.** Under tmux the launcher binds **F3
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
pane and press **Space**
|
|
180
|
-
lives in a separate parent process that runs the panel as a child, the same
|
|
176
|
+
**Cycling views.** Under tmux the launcher binds **F3** at the host. **Windows
|
|
177
|
+
Terminal** binds no key of its own, and neither do VS Code and its forks (Cursor,
|
|
178
|
+
Positron, Antigravity) — so on those the sidecar pane carries its own key: click
|
|
179
|
+
the pane and press **Space** or **F3**. The renderer still reads no input — the
|
|
180
|
+
key lives in a separate parent process that runs the panel as a child, the same
|
|
181
181
|
separation tmux enforces. Anywhere else, `ccr cycle-view -i <name>` (or
|
|
182
182
|
`--state-dir <dir>`); a bare `ccr cycle-view` resolves the instance from your
|
|
183
183
|
working directory, like `ccr economy` does.
|
|
@@ -191,6 +191,19 @@ read from a repository:
|
|
|
191
191
|
{ "panes": [ { "path": "/home/you/project/.your-tool/sidecar.json" } ] }
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
+
A leading `~` expands, with either separator (`~/tools/blob.json`,
|
|
195
|
+
`~\tools\blob.json`), and a relative path resolves against the config file's own
|
|
196
|
+
directory. **On Windows, save the file as UTF-8** — `>` and `Out-File` in Windows
|
|
197
|
+
PowerShell 5.1 write UTF-16, which is not JSON as far as any parser is concerned:
|
|
198
|
+
|
|
199
|
+
```powershell
|
|
200
|
+
Set-Content -Encoding utf8 $env:USERPROFILE\.config\ccr\config.json $json
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`ccr doctor` reports what it read out of the config — the paths as **resolved**,
|
|
204
|
+
not as written — and names the problem when there is one, so a pane that never
|
|
205
|
+
appears is a question you can answer rather than a silence.
|
|
206
|
+
|
|
194
207
|
A pane is a full-height view carrying the producing tool's own rows — here
|
|
195
208
|
`gherkin-trace`, whose blob ships as the golden example:
|
|
196
209
|
|
|
@@ -253,10 +266,16 @@ method has followed since, and is honest about which point on that line this
|
|
|
253
266
|
repository represents.
|
|
254
267
|
|
|
255
268
|
```bash
|
|
256
|
-
npm test
|
|
257
|
-
npm run typecheck
|
|
269
|
+
npm test # node --test — harness self-tests + feature scenarios
|
|
270
|
+
npm run typecheck # tsc --noEmit over @ts-check'd JS (needs: npm i first)
|
|
271
|
+
npm run install-hooks # copy .githooks/ into this clone (run once)
|
|
258
272
|
```
|
|
259
273
|
|
|
274
|
+
`install-hooks` installs a fail-closed `pre-push` guard for the public remote:
|
|
275
|
+
only `main`, fast-forward only, and a scan of the commits the push would make
|
|
276
|
+
public. It is worth running in any clone you push from — a push is the moment
|
|
277
|
+
history stops being private, and it is the last moment the check can help.
|
|
278
|
+
|
|
260
279
|
- **No runtime dependencies**, ever — it's what lets `npx claude-code-runrate` install
|
|
261
280
|
instantly on every OS, including native Windows.
|
|
262
281
|
- **Dev-only** tooling (`typescript`, `@types/node`) exists solely for
|
package/bin/ccr.js
CHANGED
|
@@ -323,8 +323,18 @@ function cmdResume(arg) {
|
|
|
323
323
|
* @returns {number | undefined}
|
|
324
324
|
*/
|
|
325
325
|
function cmdSidecar(stateDir, showHint, exitOnEnd, useKeys, view, target = null) {
|
|
326
|
-
|
|
327
|
-
|
|
326
|
+
// Resolve whether or not -i was typed, which is what `ccr cycle-view` has
|
|
327
|
+
// always done. Consulting the chain ONLY for -i meant a bare `ccr sidecar`
|
|
328
|
+
// skipped every other link in it — including the one that matters most here,
|
|
329
|
+
// "the live instance whose launch directory contains the cwd" — and landed on
|
|
330
|
+
// the CONTAINER (~/.ccr) instead. The container holds no session, so the pane
|
|
331
|
+
// sat empty next to a running instance, and the fix looked like naming the
|
|
332
|
+
// instance exactly right when the whole point of the chain is not having to.
|
|
333
|
+
//
|
|
334
|
+
// --hint is left out: it prints the VS Code split instructions, which are
|
|
335
|
+
// worth printing before any instance exists, so requiring a live one to
|
|
336
|
+
// explain how to start one would be backwards.
|
|
337
|
+
if (!stateDir && !showHint) {
|
|
328
338
|
const res = require('../src/instance-resolve').resolveInstance({ target, command: 'sidecar' });
|
|
329
339
|
if (!res.ok) { process.stderr.write(res.error + '\n'); return 1; }
|
|
330
340
|
stateDir = res.stateDir;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-runrate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Claude Code run-rate — subscription burn-rate & economy for your Claude Code sessions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bing Ho <reps-attic-riot@duck.com>",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"scripts/launch.sh",
|
|
26
26
|
"sidecar",
|
|
27
27
|
"README.md",
|
|
28
|
+
"CHANGELOG.md",
|
|
28
29
|
"LICENSE"
|
|
29
30
|
],
|
|
30
31
|
"scripts": {
|
|
@@ -32,7 +33,10 @@
|
|
|
32
33
|
"typecheck": "tsc --noEmit -p jsconfig.json",
|
|
33
34
|
"lint": "oxlint",
|
|
34
35
|
"release-gate": "node scripts/release-gate.js",
|
|
35
|
-
"
|
|
36
|
+
"install-hooks": "node scripts/install-hooks.js",
|
|
37
|
+
"probe:wt": "node scripts/probe-wt.js",
|
|
38
|
+
"scan-package": "node scripts/scan-package.js",
|
|
39
|
+
"prepublishOnly": "npm run release-gate && npm run scan-package && npm run lint && npm run typecheck && npm test"
|
|
36
40
|
},
|
|
37
41
|
"keywords": [
|
|
38
42
|
"claude-code",
|
package/src/doctor.js
CHANGED
|
@@ -9,6 +9,7 @@ const path = require('node:path');
|
|
|
9
9
|
const os = require('node:os');
|
|
10
10
|
const { spawnSync } = require('node:child_process');
|
|
11
11
|
const { stripControl } = require('./sanitize');
|
|
12
|
+
const { loadPaneConfig } = require('./pane-config');
|
|
12
13
|
|
|
13
14
|
const ok = (/** @type {string} */ s) => `\x1b[32m✓\x1b[0m ${s}`;
|
|
14
15
|
const bad = (/** @type {string} */ s) => `\x1b[31m✗\x1b[0m ${s}`;
|
|
@@ -36,7 +37,8 @@ function isExec(/** @type {string} */ f) {
|
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* @param {{ platform?: string, has?: (cmd: string) => (string|null),
|
|
39
|
-
* homedir?: string, repo?: string, write?: (s: string) => void
|
|
40
|
+
* homedir?: string, repo?: string, write?: (s: string) => void,
|
|
41
|
+
* env?: Record<string, string|undefined> }} [opts]
|
|
40
42
|
* side effects are injectable for testing; defaults hit the real environment
|
|
41
43
|
* @returns {number} exit code (0 = healthy)
|
|
42
44
|
*/
|
|
@@ -91,6 +93,24 @@ function run(opts = {}) {
|
|
|
91
93
|
out.push(dim('· ccs not installed (optional — only for `ccr <profile>`)'));
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
// Pane wiring. This command exists to diagnose "nothing happens", and a pane
|
|
97
|
+
// config the user wrote and got wrong is exactly that — the sidecar has room
|
|
98
|
+
// for a one-line marker and no more. Here there is room for the path, the
|
|
99
|
+
// reason, and what ccr actually read out of the file, which is the question
|
|
100
|
+
// someone whose pane never appeared is really asking.
|
|
101
|
+
const cfg = loadPaneConfig({ env: opts.env, home: homedir });
|
|
102
|
+
if (cfg.error) {
|
|
103
|
+
out.push(bad(`pane config: ${cfg.error} — ${stripControl(cfg.configPath)}`));
|
|
104
|
+
problems++;
|
|
105
|
+
} else if (cfg.panes.length) {
|
|
106
|
+
out.push(ok(`pane config: ${cfg.panes.length} pane(s) (${stripControl(cfg.configPath)})`));
|
|
107
|
+
// The path as ccr resolved it, not as written: a tilde that did not expand
|
|
108
|
+
// is invisible in the source string and obvious in the resolved one.
|
|
109
|
+
for (const pane of cfg.panes) out.push(dim(` · ${stripControl(pane.path)}`));
|
|
110
|
+
} else {
|
|
111
|
+
out.push(dim(`· no panes configured (optional — ${stripControl(cfg.configPath)})`));
|
|
112
|
+
}
|
|
113
|
+
|
|
94
114
|
// newest captured snapshot across the container. Instances live TWO levels
|
|
95
115
|
// down under the 0.4.0 layout (~/.ccr/instances/<n>/last-status.json) — the
|
|
96
116
|
// one-level scan alone would report "no status captured" while instances run
|