feat(statusline): delegate to the statusline the exporter shadows

Claude Code ranks a repo's .claude/settings.local.json above
~/.claude/settings.json, so the statusLine Codeman injects for the Plan
Usage chip shadows whatever statusline the user configured globally. The
inline exporter then printed Codeman's own footer in its place, and
running `claude` by hand in a managed repo rendered the bare word
`codeman` — the response the server returns for an unknown session id.

The exporter becomes a generated shim, following the deepseek-status-shim
pattern: a versioned .mjs in the data dir, refreshed on a marker change,
written through temp-and-rename so a live render cannot read a
half-written file. It forwards the same payload to /api/status-telemetry
and, concurrently, resolves the statusline it shadows and prints that.
Codeman's footer still appears when there is nothing to shadow, so the
exporter keeps its value on a machine with no statusline of its own.

The delegate resolves at render time, walking the settings files Claude
Code consults from the render directory upward and then the home ones,
skipping Codeman's own entry in either form. Late resolution means
editing a global statusline needs no reinjection.

Ownership now keys on the version-free `codeman-statusline-shim` token,
and applyStatusLineConfig still reads the old /api/status-telemetry
command as ours, so managed repos upgrade in place instead of being
mistaken for hand-authored. A hand-authored statusLine is left alone
exactly as before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 641795821fdbb171f9f47c4909c6e8945465d05b)
This commit is contained in:
Michael Grundberg
2026-09-14 13:32:35 +02:00
committed by Codeman maintainer
parent c4b74415ee
commit c375268879
8 changed files with 845 additions and 17 deletions
@@ -0,0 +1,26 @@
---
"aicodeman": patch
---
feat(statusline): stop the plan-usage exporter from stealing the user's statusline
Claude Code ranks a repo's `.claude/settings.local.json` above `~/.claude/settings.json`, so
the statusLine Codeman injects for the Plan Usage chip SHADOWS whatever statusline the user
configured globally. The inline exporter then printed Codeman's own footer in its place, and
running `claude` by hand in a managed repo rendered the bare word `codeman` — the response
the server returns for a session id it does not know.
The exporter is now a generated shim, `src/statusline-shim.ts`, following the
`deepseek-status-shim` pattern: versioned `.mjs` written into the data dir, refreshed on a
marker change, temp-and-rename so a live render cannot read a half-written file. It forwards
the same payload to `/api/status-telemetry` and, concurrently, resolves the statusline it is
shadowing and prints that instead. Codeman's footer still appears when there is nothing to
shadow, so the exporter keeps its value on a machine with no statusline of its own.
The delegate is resolved at render time by walking the settings files Claude Code consults,
nearest first, skipping Codeman's own entry in either the shim or the pre-shim form. Late
resolution means editing a global statusline takes effect with no reinjection. Ownership now
keys on the version-free `codeman-statusline-shim` token, and `applyStatusLineConfig` still
reads the old `/api/status-telemetry` command as ours so managed repos upgrade in place
rather than being mistaken for hand-authored. A hand-authored statusLine is left alone
exactly as before.