https://github.com/thehammer/bishop
Claude Code budget-posture producer — turns rolling rate-limit data into a single normalized posture (conservative/normal/elevated/flush) any consumer can read in one syscall
https://github.com/thehammer/bishop
claude claude-code macos shell
Last synced: about 4 hours ago
JSON representation
Claude Code budget-posture producer — turns rolling rate-limit data into a single normalized posture (conservative/normal/elevated/flush) any consumer can read in one syscall
- Host: GitHub
- URL: https://github.com/thehammer/bishop
- Owner: thehammer
- License: mit
- Created: 2026-05-07T15:06:45.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-31T20:25:09.000Z (about 1 month ago)
- Last Synced: 2026-05-31T21:23:19.103Z (about 1 month ago)
- Topics: claude, claude-code, macos, shell
- Language: Shell
- Size: 57.6 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Bishop
Bishop is a small macOS shell tool that turns Claude Code budget data into a
single, machine-readable JSON snapshot — the *budget posture* — that any
consumer (Mother, Claudia, scripts) can read in one syscall.
Named after the synthetic from *Aliens*, the same franchise as Mother. Bishop
is not an agent; it is a non-interactive CLI and an optional launchd heartbeat.
It never crashes, never blocks, and degrades silently to `posture: Cruise` when
input is missing.
## Install
```bash
bash scripts/install.sh # symlinks bin/bishop + bin/bishop-fetch-usage to ~/.local/bin/
bishop install-agent # loads the launchd job (runs every 60s)
```
Verify dependencies:
```bash
bash scripts/doctor.sh
```
Required: `jq`, `flock` (`brew install jq flock`). Optional for tests: `bats`
(`brew install bats-core`).
## Data sources
Bishop tries two sources in order, falling back gracefully:
### Primary — Anthropic OAuth `/api/oauth/usage`
`bishop-fetch-usage` pulls proactive per-model utilization from the same
endpoint that powers Claude Code's `/usage` UI:
```
https://api.anthropic.com/api/oauth/usage
Authorization: Bearer
anthropic-beta: oauth-2025-04-20
```
The OAuth token is read from the macOS keychain (service: `Claude Code-credentials`).
**This is macOS only.** On Linux or where the keychain entry is absent, Bishop
falls back to the Mother aggregate source automatically.
Bishop caches the OAuth response for `BISHOP_USAGE_CACHE_TTL_SECONDS` (default
55 seconds — one tick under the 60-second launchd cadence). On HTTP 429
(rate-limited) the previous cached response is preserved and reused; the cache
file is never corrupted by a failed fetch.
### Fallback — Mother aggregate (`~/.mother/rate-limits.json`)
Mother streams Claude Code rate-limit events into this file. It provides
aggregate 5h and 7d utilization but no per-model breakdowns. When Bishop
successfully reads the OAuth endpoint, this file is not consulted for the
main posture computation (but it remains on disk as a backstop).
## What it produces
`bishop --refresh` writes `~/.claude/budget-posture.json`:
```json
{
"ts": "2024-11-14T22:13:20Z",
"source": "oauth_usage",
"posture": "Cruise",
"five_hour": {
"used_pct": 12,
"elapsed_pct": 50.0,
"pace": 0.24,
"resets_at": 1700009000,
"level": "Put the hammer down"
},
"seven_day": {
"used_pct": 71,
"elapsed_pct": 50.0,
"pace": 1.42,
"resets_at": 1700302400,
"level": "Pump the brakes"
},
"models": {
"sonnet": { "status": "exhausted", "used_pct": 100, "resets_at": 1700302400 },
"opus": null,
"haiku": null
},
"exhausted_models": ["sonnet"],
"extra_usage": {
"is_enabled": true,
"used_credits": 15650,
"currency": "USD",
"monthly_limit": null,
"utilization": null
},
"source_mtime": "2024-11-14T22:12:50Z",
"source_age_seconds": 30,
"stale_input": false
}
```
When falling back to the Mother aggregate, `source` is `"mother_aggregate"`,
`models` is `{"sonnet": null, "opus": null, "haiku": null}`, and
`exhausted_models` is `[]`.
> **Note:** timestamps are second-precision ISO 8601 (`Z`-suffixed), not
> microsecond. This is a `jq` `todateiso8601` constraint.
### Per-model fields
| Field | Meaning |
|-------|---------|
| `models.sonnet` | Plan-specific 7-day Sonnet bucket. `null` if plan has no separate Sonnet cap. |
| `models.opus` | Plan-specific 7-day Opus bucket. `null` if plan has no separate Opus cap. |
| `models.haiku` | Plan-specific 7-day Haiku bucket. `null` if plan has no separate Haiku cap. |
| `exhausted_models` | Array of model names where `status == "exhausted"`. |
| `extra_usage` | Overage / pay-per-use block. `null` when source is Mother aggregate. |
A model entry is `null` when either: the OAuth response has `seven_day_: null`
(plan doesn't have that bucket), or the bucket's `resets_at` is already in the past
(window already rolled over).
## Posture levels
Five tiers, severity left → right. Top-level `posture` is the *most cautious*
level across both windows.
| Level | 5h pace | 7d pace | Meaning |
|------------------------|--------------|--------------|---------------------------------------------------------------|
| `Pump the brakes` | > 1.4 | > 1.3 | Way over pace. Slow down — use cheaper models, defer heavy work. |
| `Ease up` | 1.1 – 1.4 | 1.0 – 1.3 | Drifting hot. Course-correct now before it gets worse. |
| `Cruise` | 0.85 – 1.1 | 0.85 – 1.0 | On track. No change to model selection. |
| `Push` | 0.6 – 0.85 | 0.6 – 0.85 | Below pace. Some headroom available. |
| `Put the hammer down` | < 0.6 | < 0.6 | Well under pace. Budget is ample; expensive models are fine. |
`stale_input: true` (source older than `BISHOP_SOURCE_STALE_SECONDS`) forces
`posture` to `"Cruise"` as a safe default.
## CLI reference
```
bishop --refresh Compute posture and write budget-posture.json
bishop status Human-readable summary (includes per-model line)
bishop status --json Raw budget-posture.json to stdout
bishop get Single field (dot-path: bishop get five_hour.level)
Examples: bishop get models.sonnet.status
bishop get exhausted_models
bishop install-agent Register launchd heartbeat
bishop uninstall-agent Remove launchd heartbeat
bishop --help Full usage
```
## Environment variables
| Variable | Default | Purpose |
|---------------------------------|--------------------------------------|------------------------------------------------|
| `BISHOP_SOURCE_PATH` | `~/.mother/rate-limits.json` | Mother aggregate fallback input |
| `BISHOP_OUTPUT_PATH` | `~/.claude/budget-posture.json` | Posture output file |
| `BISHOP_SOURCE_STALE_SECONDS` | `600` | Age threshold for stale input |
| `BISHOP_DISABLED` | (unset) | Set to any value to skip --refresh entirely |
| `BISHOP_NOW_OVERRIDE` | (unset) | Override current epoch (testing) |
| `BISHOP_MTIME_OVERRIDE` | (unset) | Override source mtime (testing, legacy path) |
| `BISHOP_USAGE_FETCH_CMD` | `/bishop-fetch-usage` | Path to fetch helper |
| `BISHOP_USAGE_CACHE_PATH` | `~/.claude/oauth-usage.json` | Cached OAuth response |
| `BISHOP_USAGE_CACHE_TTL_SECONDS`| `55` | Max cache age before re-fetching |
## Consumer examples
**Shell:**
```bash
bishop --refresh
posture="$(bishop get posture)"
echo "Current posture: $posture"
echo "Exhausted models: $(bishop get exhausted_models)"
```
**Mother integration (separate repo):**
Mother reads `~/.claude/budget-posture.json` at job spawn time to bias
model/effort selection. See `docs/PLAN.md` §"Mother integration" for the
full contract. The kill switch is `MOTHER_POSTURE_ENABLED=0`.
## Run tests
```bash
bats tests/bishop.bats
```