You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Feasibility investigation: how hard would it be to make ccpool work across other agentic CLIs (OpenAI Codex CLI, pi, Google Antigravityagy), not just Claude Code?
Short version: the governance loop is more portable than expected. Codex, pi, and Antigravity all have mid-turn hook systems with context injection, so the warn mechanic ports to all three. ccusage tracks API-equivalent $ for all of them, so the dollar layer ports too. What does not port is essentially one Claude-specific lever (the live subagent-downshift env var), plus a per-host usage-% adapter over data that is undocumented / header-only / RPC-only depending on the host (and absent entirely for pi).
This is a scoping issue to capture findings, not a commitment to build. Sources cited inline.
The four ccpool pillars vs each host (documented)
Pillar
Claude Code (today)
Codex CLI
pi
Antigravity agy
1. Read account usage %
statusline payload on stdin
✅ verifiedrate_limits.primary in session JSONL (used_percent/window_minutes/resets_at; shape below); /status is the only interactive surface
✗ no normalized %; only raw provider headers via after_provider_response
⚠️ backend RPC FetchQuotaStatus, no local file
2. Mid-turn warn (hook → context)
UserPromptSubmit/PostToolUse → additionalContext
✅ near-identical: same event names + hookSpecificOutput.additionalContext
✅ before_agent_start / context / tool_call inject into context
✗ built-in item list only (tui.status_line); command-statusline is an open FR
⚠️ctx.ui.setStatus imperative push, not a payload callback
✅✅ /statusline <command>, and it natively carries StatusLineQuotaBucket + StatusLineSubagent
4. Auto-downshift subagent model/effort
CLAUDE_CODE_SUBAGENT_MODEL env, at launch
⚠️ subagents exist w/ per-file model/model_reasoning_effort, but no live env lever
✗ no subagents in core (explicit non-goal); per-instance --model/--thinking only
⚠️ subagent fan-out exists but children "inherit the current preset config"; no per-subagent model field
Verified: Codex usage % shape (on disk, 2026-07-16)
Pillar 1 for Codex is confirmed against live session files (~/.codex/sessions/<date>/rollout-*.jsonl) — upgraded from inferred to observed. Each turn appends a rate_limits record:
The % anchor is real and local.primary.{used_percent, resets_at} + window_minutes: 10080 (= 7d) is the direct analogue of Claude's seven_day.{used_percentage, resets_at}, so the pace/pool engine can run on Codex (unlike pi). Field names differ (used_percent not used_percentage; the window is carried as window_minutes, not a named key), so the ingestion adapter maps rather than aliases.
primary is nullable (null until the first limit-fetch of a session) — read the newest populated line; it's last-write-wins per turn.
secondary exists but was null in every observed session — presumably the shorter window (Claude's five_hour analogue); its populated shape is still unconfirmed (open item below).
credits is nested insiderate_limits (not a sibling): {has_credits, unlimited, balance} is a local fallback-credit signal — contrast Antigravity, whose credits are server-side only. So Codex's hard-stop is partly readable offline (has_credits:false + used_percent at 100).
rate_limit_reached_type (null until tripped) is the local "which limit did I hit" signal for the error-on-hit path.
Observed live: primary.used_percent climbed 96 → 98 → 100 across turns (written per-turn), then error-on-hit at 100% with has_credits:false — no clean CLI surface short of parsing this file or interactive /status (ref FR #15281).
The $ layer ports (ccusage is multi-source)
ccusage computes API-equivalent USD (pricing from LiteLLM's dataset) for Claude Code, Codex, OpenCode, Amp, Droid, Codebuff, Hermes Agent, pi-agent, Goose, OpenClaw, Kilo, Kimi, Qwen, GitHub Copilot CLI, and Gemini CLI, via per-source subcommands (ccusage codex daily --json, ccusage pi session --json, ccusage gemini daily, …). ccpool's "delegate every dollar to ccusage" invariant holds cross-CLI — same tool, different source.
Two documented caveats keep it from being free:
calib shells ccusage blocks --json, and blocks is Claude-specific. The 5-hour billing-block view exists only for the Claude source; Codex and pi expose only daily / monthly / session (both --json). So internal/calib needs per-source command + result-shape handling, not just a source flag. (Codex source is flagged experimental: "Codex log support is experimental while the Codex CLI log format continues to evolve.")
ccusage gives the $ numerator; the $/1% calibration still needs a host account-% denominator. ccpool's output isn't raw spend — it's $ per 1% of the account-global pool, joining ccusage cost against rate_limits %-deltas. That % still comes from the host (see pillar 1), and pi exposes no account-global % at all (only provider retry-after / anthropic-ratelimit-* headers). So for pi you'd get a dollar figure but nothing to calibrate it into a pool/pace signal — the core verdict engine can't run without a % anchor.
The one thing that is genuinely Claude-only, everywhere
Dynamic subagent downshift (the "coast when ahead of pace" lever). Ports worst across the board: Codex only sets model/effort statically in agent TOML (no runtime env lever); Antigravity subagents inherit the parent preset; pi has no subagents. Claude Code's launch-time CLAUDE_CODE_SUBAGENT_MODEL is genuinely special.
What ports well
The read + warn + statusline loop. Antigravity is the best non-Claude target — its statusline already ingests a quota bucket and subagent state natively, which is exactly ccpool's shape. Codex is close on warn but blocked on statusline (built-in items only).
Architecture caveat that affects effort
Codex & agy are config-file + external-command shaped (like Claude Code) → a Go wrapper binary fits; you write per-host adapters for the config format and usage-signal source.
pi is different in kind: its hooks/statusline are a TypeScript extension API (pi.on(...), ctx.ui.setStatus), not shell-command hooks. A pi port is not a ccpool adapter — it's a rewrite as a pi extension in TS (different language, different distribution).
Effort read
Target
Read %
warn
Statusline
Downshift
$ (ccusage)
Verdict
Codex
✅ verified JSONL (primary.used_percent)
✅ hooks
✗ blocked (FR only)
⚠️ static-only
✅ ccusage codex (daily/session, no blocks; experimental)
~1 wk warn-only governor w/ $; no statusline, no dynamic downshift
Antigravity
⚠️ backend RPC
✅ hooks
✅ best fit (quota bucket)
⚠️ inherit-only
⚠️ not in ccusage source list; credits are server-side
most complete non-Claude port; $ story unclear (server-side credits, not yet a ccusage source)
pi
✗ none (headers only)
✅ (TS extension)
⚠️setStatus push
✗ no subagents
✅ ccusage pi (daily/monthly/session)
$ available but no % anchor to calibrate against → pace/pool engine can't run; also a TS-extension rewrite, not an adapter
Prerequisite in ccpool
There is no provider-abstraction seam today — every coupling reads Claude field names inline. The Claude-specific surface is concentrated in five spots, all isolated:
Ingestion field names — internal/pool/pool.go (rate_limits.{seven_day,five_hour}.{used_percentage,resets_at}), internal/history/history.go, internal/statusline/statusline.go. (Codex analogue, verified: rate_limits.primary.{used_percent,resets_at} + window_minutes; see the verified shape above — the adapter maps names + derives the window tier from window_minutes.)
init wiring shape — settings.json schema in internal/initcmd/init.go.
Everything else — profile, burn, runway, report, store, pace/verdict math — is already provider-agnostic. calib is nearly so (it delegates to ccusage, which is multi-source) but hardcodes the Claude-only blocks --json command + blocks[] schema, so it needs a per-source command/shape adapter (ccusage <source> daily|session --json). A port means introducing a Source/host adapter interface at points 1-5 (+ the calib command).
Open questions / possible next steps
Decide the actual goal: show other-CLI users their pool (feasible) vs govern their spend the way ccpool governs Claude (not achievable on any host — the two Claude-only levers).
Tier-0 refactor: extract a host/Source seam feeding normalized {used%, resets_at, window} tuples (net-neutral, worth doing regardless). Adapters should also declare capabilities (read-%, warn, statusline, downshift) so the governance layer degrades per host ("governance unavailable on this host") rather than branching on host identity or asserting false parity. (Capability-declaration framing via @ofekron / Better Agent.)
PoC a Codex or Antigravity read + warn adapter behind that seam, shipped as an explicit --host mode with "governance features unavailable on this host" degradation (no false parity claim). Codex is the strongest first target: hooks (parity), usage-% (JSONL, shape now verified), and $ (ccusage) all present; only statusline + dynamic downshift missing.
Confirm Codex's secondary (short-window) populated shape — only primary (weekly, window_minutes:10080) was seen populated; secondary was null in every session observed 2026-07-16, so the five_hour analogue is unproven.
Teach calib a per-source ccusage command (ccusage <source> daily|session --json) since blocks is Claude-only.
Check whether ccusage has (or plans) an Antigravity source — it isn't in the current 15-source list, and Antigravity bills server-side "AI Credits", so the $ path there is the open question, not the %.
Confirm the Antigravity claims against live docs (the antigravity.google/docs/* pages are an Angular SPA; page bodies were not machine-readable — findings were corroborated from the installed agy v1.1.1 binary instead).
🤖 Claude-driven investigation (parallel research agents against official docs + locally installed binaries). Findings above are scoping, not verified-in-code parity claims.
Summary
Feasibility investigation: how hard would it be to make ccpool work across other agentic CLIs (OpenAI Codex CLI, pi, Google Antigravity
agy), not just Claude Code?Short version: the governance loop is more portable than expected. Codex, pi, and Antigravity all have mid-turn hook systems with context injection, so the
warnmechanic ports to all three. ccusage tracks API-equivalent$for all of them, so the dollar layer ports too. What does not port is essentially one Claude-specific lever (the live subagent-downshift env var), plus a per-host usage-%adapter over data that is undocumented / header-only / RPC-only depending on the host (and absent entirely for pi).This is a scoping issue to capture findings, not a commitment to build. Sources cited inline.
The four ccpool pillars vs each host (documented)
agyrate_limits.primaryin session JSONL (used_percent/window_minutes/resets_at; shape below);/statusis the only interactive surfaceafter_provider_responseFetchQuotaStatus, no local fileUserPromptSubmit/PostToolUse→additionalContexthookSpecificOutput.additionalContextbefore_agent_start/context/tool_callinject into contextplugins/<name>/hooks.jsonPreToolUse/PostToolUse+injectStepsstatusLinecommand, JSON on stdintui.status_line); command-statusline is an open FRctx.ui.setStatusimperative push, not a payload callback/statusline <command>, and it natively carriesStatusLineQuotaBucket+StatusLineSubagentCLAUDE_CODE_SUBAGENT_MODELenv, at launchmodel/model_reasoning_effort, but no live env lever--model/--thinkingonlyVerified: Codex usage
%shape (on disk, 2026-07-16)Pillar 1 for Codex is confirmed against live session files (
~/.codex/sessions/<date>/rollout-*.jsonl) — upgraded from inferred to observed. Each turn appends arate_limitsrecord:Adapter implications:
%anchor is real and local.primary.{used_percent, resets_at}+window_minutes: 10080(= 7d) is the direct analogue of Claude'sseven_day.{used_percentage, resets_at}, so the pace/pool engine can run on Codex (unlike pi). Field names differ (used_percentnotused_percentage; the window is carried aswindow_minutes, not a named key), so the ingestion adapter maps rather than aliases.primaryis nullable (null until the first limit-fetch of a session) — read the newest populated line; it's last-write-wins per turn.secondaryexists but was null in every observed session — presumably the shorter window (Claude'sfive_houranalogue); its populated shape is still unconfirmed (open item below).creditsis nested insiderate_limits(not a sibling):{has_credits, unlimited, balance}is a local fallback-credit signal — contrast Antigravity, whose credits are server-side only. So Codex's hard-stop is partly readable offline (has_credits:false+used_percentat 100).rate_limit_reached_type(null until tripped) is the local "which limit did I hit" signal for the error-on-hit path.Observed live:
primary.used_percentclimbed 96 → 98 → 100 across turns (written per-turn), then error-on-hit at 100% withhas_credits:false— no clean CLI surface short of parsing this file or interactive/status(ref FR #15281).The
$layer ports (ccusage is multi-source)ccusage computes API-equivalent USD (pricing from LiteLLM's dataset) for Claude Code, Codex, OpenCode, Amp, Droid, Codebuff, Hermes Agent, pi-agent, Goose, OpenClaw, Kilo, Kimi, Qwen, GitHub Copilot CLI, and Gemini CLI, via per-source subcommands (
ccusage codex daily --json,ccusage pi session --json,ccusage gemini daily, …). ccpool's "delegate every dollar to ccusage" invariant holds cross-CLI — same tool, different source.Two documented caveats keep it from being free:
calibshellsccusage blocks --json, andblocksis Claude-specific. The 5-hour billing-block view exists only for the Claude source; Codex and pi expose onlydaily/monthly/session(both--json). Sointernal/calibneeds per-source command + result-shape handling, not just a source flag. (Codex source is flagged experimental: "Codex log support is experimental while the Codex CLI log format continues to evolve.")$numerator; the$/1%calibration still needs a host account-%denominator. ccpool's output isn't raw spend — it's$per 1% of the account-global pool, joining ccusage cost againstrate_limits%-deltas. That%still comes from the host (see pillar 1), and pi exposes no account-global % at all (only providerretry-after/anthropic-ratelimit-*headers). So for pi you'd get a dollar figure but nothing to calibrate it into a pool/pace signal — the core verdict engine can't run without a%anchor.The one thing that is genuinely Claude-only, everywhere
Dynamic subagent downshift (the "coast when ahead of pace" lever). Ports worst across the board: Codex only sets model/effort statically in agent TOML (no runtime env lever); Antigravity subagents inherit the parent preset; pi has no subagents. Claude Code's launch-time
CLAUDE_CODE_SUBAGENT_MODELis genuinely special.What ports well
The read + warn + statusline loop. Antigravity is the best non-Claude target — its statusline already ingests a quota bucket and subagent state natively, which is exactly ccpool's shape. Codex is close on
warnbut blocked on statusline (built-in items only).Architecture caveat that affects effort
pi.on(...),ctx.ui.setStatus), not shell-command hooks. A pi port is not a ccpool adapter — it's a rewrite as a pi extension in TS (different language, different distribution).Effort read
%$(ccusage)primary.used_percent)ccusage codex(daily/session, noblocks; experimental)$; no statusline, no dynamic downshift$story unclear (server-side credits, not yet a ccusage source)setStatuspushccusage pi(daily/monthly/session)$available but no%anchor to calibrate against → pace/pool engine can't run; also a TS-extension rewrite, not an adapterPrerequisite in ccpool
There is no provider-abstraction seam today — every coupling reads Claude field names inline. The Claude-specific surface is concentrated in five spots, all isolated:
internal/pool/pool.go(rate_limits.{seven_day,five_hour}.{used_percentage,resets_at}),internal/history/history.go,internal/statusline/statusline.go. (Codex analogue, verified:rate_limits.primary.{used_percent,resets_at}+window_minutes; see the verified shape above — the adapter maps names + derives the window tier fromwindow_minutes.)internal/warn/warn.go(UserPromptSubmit/PostToolUse,hookSpecificOutput.additionalContext).internal/run/run.go(CLAUDE_CODE_SUBAGENT_MODEL/CLAUDE_CODE_EFFORT_LEVEL).internal/paths/paths.go,internal/initcmd/init.go.initwiring shape —settings.jsonschema ininternal/initcmd/init.go.Everything else —
profile,burn,runway,report,store, pace/verdict math — is already provider-agnostic.calibis nearly so (it delegates to ccusage, which is multi-source) but hardcodes the Claude-onlyblocks --jsoncommand +blocks[]schema, so it needs a per-source command/shape adapter (ccusage <source> daily|session --json). A port means introducing aSource/host adapter interface at points 1-5 (+ the calib command).Open questions / possible next steps
Sourceseam feeding normalized{used%, resets_at, window}tuples (net-neutral, worth doing regardless). Adapters should also declare capabilities (read-%, warn, statusline, downshift) so the governance layer degrades per host ("governance unavailable on this host") rather than branching on host identity or asserting false parity. (Capability-declaration framing via @ofekron / Better Agent.)warnadapter behind that seam, shipped as an explicit--hostmode with "governance features unavailable on this host" degradation (no false parity claim). Codex is the strongest first target: hooks (parity), usage-% (JSONL, shape now verified), and$(ccusage) all present; only statusline + dynamic downshift missing.secondary(short-window) populated shape — onlyprimary(weekly,window_minutes:10080) was seen populated;secondarywas null in every session observed 2026-07-16, so thefive_houranalogue is unproven.caliba per-source ccusage command (ccusage <source> daily|session --json) sinceblocksis Claude-only.$path there is the open question, not the%.antigravity.google/docs/*pages are an Angular SPA; page bodies were not machine-readable — findings were corroborated from the installedagyv1.1.1 binary instead).Sources
Codex: subagents, config-reference, config-advanced, hooks, slash-commands; FRs #15281 (expose usage), #20043 (command statusline), #16933 (additionalContext reaches model), #23190 (unstable reset window).
pi: earendil-works/pi, coding-agent README, extensions.md.
Antigravity: llms.txt, CLI product, docs/hooks, docs/subagents, docs/cli/statusline, docs/cli/credits; plus the installed
agyv1.1.1 binary and local config.ccusage (multi-source
$): all reports / source list, Codex source (beta), pi source (beta), Gemini source, repo.🤖 Claude-driven investigation (parallel research agents against official docs + locally installed binaries). Findings above are scoping, not verified-in-code parity claims.