ITADN

Claude usage: opt-in live window feed from Claude Code's statusLine JSON — needs a ruling on AGENTS.md:47

#2733Openluisgonzaleznf 创建于 14 天前
P2clawsweeper:no-new-fix-prclawsweeper:needs-product-decisionimpact:auth-providerissue-rating: 🦞 diamond lobsterclawsweeper:source-repro
L
luisgonzaleznfcommented
## Summary Claude is the only provider whose live sources are both budget-gated, which makes its card structurally stale rather than occasionally wrong. Claude Code ≥ 2.1.80 exposes a server-authoritative `rate_limits` object that costs nothing to read, but the only channel it comes through is the `statusLine` command — and `AGENTS.md:47` says never to rely on the status line for usage parsing. I think there's a real distinction between what that rule forbids and what this would do, but it's your rule and your call. **I'd rather get a ruling before writing code than send you a surprise PR that contradicts a documented line.** Happy to implement whichever way you want it, or drop it. ## The problem is staleness, not blanking #2247 (`ff52c6d`) fixed the blanking half of #2241 — transient parse failures now preserve the last-good snapshot and its timestamp. That part works. What's left is freshness, and it's structural rather than a parser bug. Both live sources carry the same 5-minute cooldown: - `ClaudeOAuthUsageRateLimitGate.swift:6` — `defaultCooldown: TimeInterval = 60 * 5`, keyed per access token, armed whenever `/api/oauth/usage` 429s - `ClaudeCLIRateLimitGate.swift:5` — same cooldown for the PTY path Both gates are correct given what they're gating. `api.anthropic.com/api/oauth/usage` has a tight budget and 429s under polling; the PTY path pays a subprocess plus session-artifact cleanup per probe. The issue history is what that looks like from the outside: #1679 (rate limited), #2117 (slow refresh), #2251 (session-list pollution, since mitigated by the `ClaudeProbe` artifact cleanup), and the 8.6% probe success rate measured in #2241. CodexBar has no Claude source that is cheap enough to read often. Every other fix is scheduling against a fixed budget. ## The one free channel Claude Code ≥ 2.1.80 passes a JSON object on stdin to whatever `statusLine` command is configured. It contains: ```json { "rate_limits": { "five_hour": { "used_percentage": 42.7, "resets_at": 1786099200 }, "seven_day": { "used_percentage": 63.1, "resets_at": 1786608000 } } } ``` This is piggybacked on Messages API responses the session already made, so reading it spends nothing against the usage endpoint's budget. It appears only for Claude.ai-subscriber sessions, and only after the session's first API response. **I checked whether it lands anywhere on disk first, because a file read would need none of this machinery.** It doesn't: - no window fields in `~/.claude/projects/*/*.jsonl` session transcripts - nothing in `~/.claude.json` beyond a GrowthBook promo string that happens to contain `seven_day` - `stats-cache.json` doesn't carry it either Corroborating evidence that the hook surface doesn't expose it: [orca](https://github.com/stablyai/orca) (MIT) installs the full Claude hook suite — SessionStart, UserPromptSubmit, Stop, PreToolUse — and still had to build a *separate* statusline route to get `rate_limits`. If any hook payload carried it, they wouldn't have. So the statusline slot appears to be the only channel, which is inconvenient given the rule. ## The AGENTS.md:47 question > `AGENTS.md:47` — "Claude CLI status line is custom + user-configurable; never rely on it for usage parsing." The argument that this proposal doesn't violate it: 1. **It reads input, not output.** The rule's stated rationale is that the status line is *custom and user-configurable* — which is true of what a status line **renders**. This would read the JSON Claude Code **supplies to** the command. That payload is Anthropic's schema, identical regardless of what the user's status line prints. The rendered text is never inspected. 2. **The contrast is already in your codebase.** `ClaudeStatusProbe` scrapes rendered terminal output today — `docs/claude.md:195-197`: "Strips ANSI, locates 'Current session' + 'Current week' headers." *That* is the fragile pattern the rule describes. This is the opposite direction. 3. **Nothing would *rely* on it.** Purely additive. OAuth and CLI keep their existing priority; this fills the gaps between polls. If it never fires, behavior is exactly what it is today. The argument against, which I don't think is weak: the feature still **occupies a user-configurable settings slot**, and slot ownership is plausibly half of why the rule exists. That concern is real and survives points 1–3. I don't think I should be the one to resolve that. If you read it broadly, say so and I'll close this. ## Proposed shape (if you're open to it) Opt-in, default off, and removable: - **Transport: file drop, no listener.** A shim writes the payload atomically to `~/Library/Application Support/CodexBar/` with `0600`; the app watches via `DispatchSource`. No port, no bearer token, no network surface. I originally sketched a loopback HTTP listener mirroring orca's design, but that would have meant lifting `CLILocalHTTPServer` out of `CodexBarCLI` — which is an `executableTarget` (`Package.swift:81`) and therefore unimportable from the app — touching `CLIServeCommand.swift` and its test suite for no benefit. A listening socket in a menu bar app also isn't worth the scrutiny when a file achieves the same trust model. - **Never clobber the slot.** `statusLine` is a single slot, not a list. Detect three states — managed / user-owned / empty-after-prior-install — and treat "empty after we installed once" as an opt-out rather than an invitation to reinstall. Orca's `hook-settings.ts:138-186` handles exactly this and is worth mirroring. - **Additive priority.** Slots into the planner ahead of `.cli`, behind `.oauth`. - **Clean uninstall** that removes both the shim and the settings entry. ### Design constraints I'd want your view on These are the parts where I think the real work is, and where I'd most likely guess wrong: 1. **The live snapshot is partial.** The statusline payload carries only `five_hour` and `seven_day`. It has no `seven_day_sonnet` / `seven_day_opus`, no `limits[].weekly_scoped`, no `seven_day_routines` / `cowork`, no `extra_usage`, and no identity or plan fields — all of which OAuth maps today (`docs/claude.md:79-90`). A live-fed card would move its two main bars while those rows sit frozen at their last OAuth values. The merge semantics across sources are the actual design problem here, not the parser. 2. **Multi-account attribution.** The payload carries no account identity — at best `CLAUDE_CONFIG_DIR`. `claude-swap` switches accounts *within the same config dir*, so a session started before a swap would post the previous account's numbers with nothing to distinguish them. Token-based accounts have no config dir at all. Orca drops posts whose `configDir` doesn't match the last auth snapshot (`service.ts:1455-1471`); something equivalent seems necessary here, and it interacts with the provider-siloing rule in `AGENTS.md:46`. 3. **Two `.auto` decision sites.** `docs/claude.md:12-15` and `27-29` note the lower-level direct fetcher still has its own `.auto` order pending the refactor. A fifth source probably needs to land in both, or explicitly in only one — your call which. 4. **Precedence when the user stops coding.** At what age does a live snapshot lose to a fresh poll, and should a recent live value suppress a poll at all? ## Prior art [stablyai/orca](https://github.com/stablyai/orca) (MIT) ships this technique — `src/shared/claude-statusline-rate-limits.ts`, `src/main/claude/statusline-script.ts`, `src/main/claude/hook-settings.ts`. Their code comment on why they built it is the same diagnosis as above: *"the endpoint 429s under Orca's polling."* They kept their OAuth fetcher and layered this on top rather than replacing it. Note the difference in position, because it cuts both ways: orca owns the sessions it feeds from, so it only sees its own panes. CodexBar would see every Claude Code session on the machine — broader coverage, but it also means CodexBar is writing a settings file for sessions it doesn't own. That asymmetry is worth weighing. ## What I'm asking 1. Does `AGENTS.md:47` forbid this, reading it as you intended it? 2. If not — is the file-drop shape right, or would you rather it went in differently? If both are green I'll send a draft PR with tests. If it's a no, no hard feelings — closing this is a perfectly good outcome and I'd rather know now.
5 条评论