Skip to content

Repository files navigation

Claude Pace (claude-pace)

An execution-policy layer for Claude Code. It reads locally available usage signals (five-hour window, weekly window, context pressure), combines them with task size, and tells Claude how to work: how much to explore, how many subagents to use, how much to implement, how hard to validate, and when to checkpoint or stop.

Usage monitors provide observability. Claude Pace consumes available local signals and turns them into an execution policy.

Why this is not another usage monitor

Excellent dashboards, terminal HUDs, and statuslines for Claude Code usage already exist. This project deliberately does none of that: no dashboard, no HUD, no usage history database, no forecasting UI, no analytics. It is the layer after observability — the one that changes Claude's plan so coherent units of work finish before the allowance runs out.

What it does

  • Classifies the task (small, medium, large, open-ended).
  • Reads local usage/context state and selects the most restrictive signal.
  • Maps that to a governor mode (healthy / moderate / low / critical).
  • Emits concrete policy: exploration scope, max subagent count, validation scope, whether broad refactors are allowed, whether to checkpoint, whether to write a continuation plan.
  • Re-checks after major phases of long tasks.
  • Falls back to task size + context pressure when account usage data is unavailable — it stays useful with no state file at all.

What it does not do

  • It does not increase, bypass, or modify Anthropic rate or usage limits.
  • It does not continuously control Claude during an active response — it sets a policy that Claude then follows.
  • It does not guarantee automatic skill invocation (explicit invocation is documented below).
  • It does not automatically change the main Claude Code model.
  • It does not claim to know exact usage when the data is unavailable, and it never fabricates percentages or reset times.
  • It does not replace an existing usage monitor.

Architecture

Claude Code or local usage monitor
               |
               | local JSON state
               v
        read_pace.py
               |
               | normalized policy
               v
      Claude Pace skill
               |
               v
 Exploration, subagent, scope,
 validation, and checkpoint policy

Three separated concerns: signal (usage/context JSON) → normalized budget state (read_pace.py) → execution policy (the skill applies it to planning).

Claude Code exposes rate-limit and context data (rate_limits.five_hour, rate_limits.seven_day, context_window) only to status-line commands via stdin JSON — skills cannot read it directly, so a small local state file is the bridge. Any writer works: your existing status line, an existing monitor, or nothing at all.

Supported input schema

The reader accepts, at ~/.claude/claude-pace/usage-state.json (or $CLAUDE_PACE_STATE, or --state-file):

A. Normalized schema (for monitor integrations; five_hour/seven_day accepted as aliases for short_window/weekly):

{
  "available": true,
  "updated_at": "2026-07-11T10:00:00Z",
  "short_window": { "used_percentage": 25, "remaining_percentage": 75, "resets_at": 1780000000 },
  "weekly":       { "used_percentage": 40, "remaining_percentage": 60, "resets_at": 1780500000 },
  "model_weekly": { "used_percentage": 45, "remaining_percentage": 55, "resets_at": 1780500000 },
  "context":      { "used_percentage": 35, "remaining_percentage": 65 }
}

model_weekly is optional: the per-model weekly limit shown in the Claude usage screen (e.g. "Fable only" / "Opus only"), which is often the most restrictive signal.

B. Official Claude Code status-line JSON — the exact object Claude Code pipes to your status-line command. Only rate_limits and context_window are read; every other field (session ids, paths, cost, workspace) is ignored and never emitted. The documented schema carries five_hour and seven_day; if the JSON contains additional valid windows under rate_limits (such as a per-model weekly limit), the most restrictive one is captured best-effort as model_weekly.

Invalid values (non-numeric, outside 0–100) are ignored, not clamped. State older than 30 minutes (configurable via --max-age) is treated as stale and its usage signals are ignored. Note: Claude Code populates rate_limits only for Claude.ai subscription accounts (Pro/Max); with API-key auth the field is absent and the governor falls back to context/task-size signals.

Governor modes

Universal safe thresholds — project defaults, not Anthropic-defined values — applied to the most restrictive remaining percentage, on every plan:

Mode Remaining Exploration Subagents Validation Broad refactor Checkpoint
healthy > 60% normal ≤ 4 comprehensive yes
moderate 30–60% focused ≤ 2 targeted + critical integration strong justification
low 10–30% targeted ≤ 1 targeted no after next unit
critical < 10% current task only 0 essential only no + continuation plan, then stop

Plan detection and the universal safe policy

All usage percentages are already plan-relative — Claude Code reports used_percentage against your plan's quota, so the governor works identically on Pro, Max 5x, and Max 20x. In the 0.x series the resolved plan is therefore reporting-only: one set of universal safe thresholds applies to every plan, and a detected plan never relaxes policy. Plan-specific threshold presets are reserved for a future explicitly configurable feature.

Automatic detection (default on): no documented Claude Code interface exposes your plan, but the local config (~/.claude.json, or $CLAUDE_CONFIG_DIR/.claude.json when that is set) carries an undocumented rate-limit tier string (e.g. default_claude_max_5x). The reader maps that single field to pro / max-5x / max-20x and reports it with "plan_source": "auto-detected". It reads only the tier string — never emails, account/organization identifiers, tokens, or any other field — and never stores or emits the raw config or tier value. Being undocumented, detection may stop working after a Claude Code update; every failure mode (missing file, malformed JSON, unrecognized tier) degrades silently to "plan": "unknown" and the universal policy.

Disable detection with either:

python3 scripts/read_pace.py --json --task-size medium --no-detect-plan
export CLAUDE_PACE_DETECT_PLAN=0   # environment opt-out; CLI flags win over it

Plan-selection precedence (first valid value wins; explicit configuration always beats detection):

  1. --plan pro|max-5x|max-20x
  2. $CLAUDE_PACE_PLAN
  3. a "plan" field in the state file
  4. automatic local detection (unless disabled)
  5. universal safe policy with "plan": "unknown", "plan_source": "unavailable"

Output always discloses the source (plan_source: cli, environment, state-file, auto-detected, or unavailable) and the policy basis (policy_source: "universal-safe"). Unknown, malformed, or failed plan information can never make the policy less conservative than the universal thresholds below.

Task-size adjustment

Remaining allowance is not the only input. A transparent matrix adjusts by task size:

  • small — validation is capped at targeted even in healthy/moderate mode; a small fix proceeds fine under moderate or low mode.
  • medium — no adjustment.
  • large — the mode is demoted one step (healthy→moderate, moderate→low): big migrations become constrained earlier.
  • open-ended — demoted one step like large, and a checkpoint is recommended even in healthy mode.

A critical production fix can justify overriding deferral behavior, but should still skip unnecessary exploration — say so explicitly in the session.

Requirements

  • Claude Code ≥ 2.x
  • Python 3.10+ (standard library only — no dependencies)
  • macOS or Linux

Installation

As a plugin (recommended — one command, enables automatic setup):

claude plugin marketplace add ablhub/claude-pace
claude plugin install claude-pace@claude-pace

Then answer the enable_usage_feed prompt (or pass --config enable_usage_feed=true to the install command); the usage feed wires itself on your next session, as described under "Automatic setup". Invoke with /claude-pace:claude-pace.

As a user-level skill (no plugin system involved):

git clone https://github.com/ablhub/claude-pace.git
cd claude-pace
./install.sh                       # skill + bridge; asks before touching settings
./install.sh --enable-statusline   # additionally wires the bridge as your status line

The installer copies the skill to ~/.claude/skills/claude-pace (backing up any previous different version) and creates the state directory. By default it touches nothing else — no settings files, no other plugins, no root, no network. When run in a terminal it asks once whether to enable the usage feed (default: no; suppress the question with CLAUDE_PACE_NO_PROMPT=1). Run it again anytime; it is idempotent.

--enable-statusline is the explicit opt-in for live usage data: it wires the bridge into your statusLine in ~/.claude/settings.json, backing the file up first and preserving every other setting and the file's permissions. If a different status line is already configured it is wrapped, never replaced: your display stays exactly as it was, and the same payload is teed into the bridge. The original command is saved to ~/.claude/claude-pace/original-statusline.cmd; undo by restoring the backup. Usage data starts flowing in your next interactive Claude Code session.

Automatic setup (plugin mode)

Installed as a plugin (marketplace or --plugin-dir), Claude Pace can wire the usage feed automatically with consent given once at install time: the plugin's enable_usage_feed option (prompted when you enable the plugin) authorizes a SessionStart hook to perform exactly the --enable-statusline procedure above — set the bridge if you have no status line, wrap yours if you do, back up settings.json either way, and tell you what it did in a one-time message.

Controls, in order of precedence:

  • decline the enable_usage_feed option → the hook does nothing, ever;
  • CLAUDE_PACE_AUTOWIRE=0 → kill switch, the hook exits immediately;
  • undo after the fact → restore the backup named in the enable message; the hook wires at most once (marker file ~/.claude/claude-pace/.autowired), so your revert sticks. Delete the marker to allow wiring again.

The hook is idempotent and silent when there is nothing to do. It never runs network calls and never touches anything except statusLine in settings.json and files under ~/.claude/claude-pace/. (Why the plugin option defaults to yes while the installer's shell prompt defaults to no: the plugin flow surfaces an explicit choice UI at enable time, whereas a shell prompt can be hit by an accidental Enter — so it stays conservative.)

In-session self-healing (the skill wires itself)

The skill closes the loop end to end: if the reader finds no usage feed, the skill asks you once, in the conversation whether to enable it. Say yes and it runs the enable engine right there (Claude Code's own permission prompt doubles as the consent gate), your status line starts rendering within seconds — Claude Code applies statusLine changes live, no restart — and the reader re-polls (--wait-fresh) and reports real percentages in the same session. The bridge entry uses refreshInterval: 30, so the feed stays at most ~30s old while a session is open. Say no and it proceeds on the fallback policy without asking again.

So the full journey for any user is: install (plugin or install.sh) → invoke the skill → answer one yes/no question → live usage-governed pacing from that moment on.

How usage data can — and cannot — be obtained

For anyone wondering whether there is a more direct route: Claude Code exposes rate-limit percentages only to status-line commands via stdin JSON. We verified empirically that the status line does not fire in headless (claude -p) sessions, hook stdin payloads (SessionStart, UserPromptSubmit, Stop) carry no rate-limit fields, no local Claude Code cache or transcript persists them, there is no documented CLI/API to read usage non-interactively, and claude config set cannot reliably write object settings. Two "reliable" shortcuts exist and are deliberately rejected: reading the OAuth token from local credential storage to call Anthropic's usage endpoints (credential access — violates this project's privacy model and reasonable API-use expectations), and estimating limits from transcript token counts (unreliable guesswork, and dashboard territory that belongs to usage monitors). The wired status line is therefore the only sanctioned feed, which is why Claude Pace focuses on making that wiring automatic, consented, and reversible.

To try it as a plugin without installing:

claude --plugin-dir /path/to/claude-pace

Validate the plugin with the official command:

claude plugin validate /path/to/claude-pace

Explicit invocation

Automatic skill invocation is possible but not guaranteed, so invoke it explicitly before big work:

  • Installed as user skill: /claude-pace
  • Loaded via --plugin-dir: /claude-pace:claude-pace

Or run the reader directly:

python3 scripts/read_pace.py                       # human-readable
python3 scripts/read_pace.py --json                # machine-readable
python3 scripts/read_pace.py --task-size large
python3 scripts/read_pace.py --state-file examples/usage-state.json
python3 scripts/read_pace.py --context-remaining 20 --task-size open-ended

CLAUDE.md integration

Copy the rule block from examples/CLAUDE.md into your project's CLAUDE.md: invoke the governor before broad analysis, large implementations, refactors, multiple subagents, or extensive tests; skip it for trivial edits; re-check after each major phase. This is advisory — CLAUDE.md does not provide deterministic enforcement.

Integration with existing local monitors

Any monitor can feed the governor by writing the normalized schema above to ~/.claude/claude-pace/usage-state.json (or any path exported as CLAUDE_PACE_STATE). No specific third-party project is bundled, required, or officially endorsed.

A ready-made bridge ships with the plugin: scripts/statusline-bridge.sh (the installer also copies it to ~/.claude/claude-pace/statusline-bridge.sh). It normalizes the status-line JSON before anything touches disk: only usage windows (percentages and reset timestamps) are written to the state file, atomically and with mode 0600 — session ids, prompts, workspace/repository paths, cost, and account data are parsed and discarded. It then prints a minimal one-liner (Fable | ctx 81% | 5h 87% left | 7d 78% left). Enable it with ./install.sh --enable-statusline, or add it yourself in ~/.claude/settings.json — settings are edited only when you opt in (the flag, the installer's prompt, or the plugin option), and an existing status line is wrapped, never replaced:

"statusLine": { "type": "command", "command": "~/.claude/claude-pace/statusline-bridge.sh" }

If you already have a status line you like, keep it./install.sh --enable-statusline (and the plugin autowire) wraps it with exactly this forwarding pattern, preserving your display. To do it by hand instead:

#!/usr/bin/env bash
INPUT="$(cat)"
printf '%s' "$INPUT" | "$HOME/.claude/claude-pace/statusline-bridge.sh" >/dev/null
printf '%s' "$INPUT" | /path/to/your-existing-statusline

Usage examples

$ # the sample file has a fixed timestamp, so relax staleness for the demo
$ python3 scripts/read_pace.py --state-file examples/usage-state.json \
    --task-size large --max-age 315360000
Claude Pace — execution policy
  usage data     : available
  short window left: 75%
  weekly left    : 60%
  model weekly left: 55%
  most restrictive: model_weekly (55%)
  task size      : large
  plan           : max-5x (auto-detected, best-effort; reporting only)
  policy         : universal safe thresholds
  mode           : low
  exploration    : targeted
  subagents      : max 1
  ...

In a session:

> /claude-pace
> now migrate the auth module to the new API

Claude will classify the task, run the reader, print a one-line policy note, and work within it.

Testing

python3 -m unittest discover -s tests -v
bash -n install.sh

CI runs the same on Linux with Python 3.10 and 3.13 (see .github/workflows/test.yml).

Updating

cd claude-pace && git pull && ./install.sh

Privacy

Everything is local:

  • no network requests, ever;
  • no telemetry;
  • no usage data uploaded anywhere;
  • no prompts stored;
  • no repository contents stored;
  • no account identifiers stored or emitted — the reader consumes only percentage/reset fields and drops everything else;
  • the status-line bridge persists only normalized usage fields (window percentages and reset timestamps), never the raw status-line payload, session ids, or paths;
  • automatic plan detection reads exactly one best-effort field from the local Claude config — the rate-limit tier string — and emits only the mapped name (pro / max-5x / max-20x / unknown); the raw config and raw tier string are never stored, printed, or returned;
  • detection can be disabled entirely (--no-detect-plan or CLAUDE_PACE_DETECT_PLAN=0), in which case the local Claude config is not read at all;
  • automatic status-line wiring happens only with consent (the --enable-statusline flag, the installer's interactive question, or the plugin's enable_usage_feed install-time option), always backs up settings.json, always announces what it changed, and has a kill switch (CLAUDE_PACE_AUTOWIRE=0).

Limitations

  • Rate-limit data reaches Claude Code's status line only for Claude.ai subscription accounts; API-key sessions fall back to context/task-size signals.
  • Per-model weekly limits (e.g. "Fable only" in the usage screen) are not part of the documented status-line schema; the reader captures them best-effort when present, and any monitor can supply them explicitly via model_weekly.
  • Plan detection relies on an undocumented local config field and may stop working after Claude Code updates; explicit --plan / $CLAUDE_PACE_PLAN configuration is the stable path, and any detection failure falls back to the universal safe policy.
  • The governor cannot see usage without a local state file being written by something (your status line or a monitor); with nothing, it still governs on task size and stated context pressure.
  • Skill invocation by Claude is probabilistic; use explicit invocation or CLAUDE.md rules for consistency.
  • Policy is applied at planning boundaries, not continuously mid-response.

Troubleshooting

  • "usage data: unavailable" — no usage feed is wired. Easiest fix: invoke /claude-pace and answer yes when it offers to enable the feed (works in the same session). Alternatives: ./install.sh --enable-statusline, or check ls -la ~/.claude/claude-pace/ and your bridge/monitor.
  • Signals ignored — values outside 0–100 or non-numeric are rejected by design; fix the writer.
  • Skill not found — re-run ./install.sh, or start with claude --plugin-dir /path/to/claude-pace and use /claude-pace:claude-pace.
  • Stale state — the writer stopped updating; the governor deliberately ignores old numbers rather than trusting them.

License

MIT — see LICENSE. Copyright (c) 2026 Abylay Sagyndyk.

Disclaimer

This is an unofficial community project. It is not affiliated with, endorsed by, or supported by Anthropic. Mode thresholds are project choices, not Anthropic definitions.

About

Execution-policy layer for Claude Code: adapts exploration, subagents, scope, and validation to available usage signals, context pressure, and task size. Complements usage monitors.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages