{"id":51336304,"url":"https://github.com/thehammer/bishop","last_synced_at":"2026-07-02T03:04:15.383Z","repository":{"id":361688937,"uuid":"1232076001","full_name":"thehammer/bishop","owner":"thehammer","description":"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","archived":false,"fork":false,"pushed_at":"2026-05-31T20:25:09.000Z","size":59,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T21:23:19.103Z","etag":null,"topics":["claude","claude-code","macos","shell"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thehammer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-07T15:06:45.000Z","updated_at":"2026-05-31T20:25:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/thehammer/bishop","commit_stats":null,"previous_names":["thehammer/bishop"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/thehammer/bishop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thehammer%2Fbishop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thehammer%2Fbishop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thehammer%2Fbishop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thehammer%2Fbishop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thehammer","download_url":"https://codeload.github.com/thehammer/bishop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thehammer%2Fbishop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35030924,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-02T02:00:06.368Z","response_time":173,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude","claude-code","macos","shell"],"created_at":"2026-07-02T03:04:10.162Z","updated_at":"2026-07-02T03:04:15.367Z","avatar_url":"https://github.com/thehammer.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bishop\n\nBishop is a small macOS shell tool that turns Claude Code budget data into a\nsingle, machine-readable JSON snapshot — the *budget posture* — that any\nconsumer (Mother, Claudia, scripts) can read in one syscall.\n\nNamed after the synthetic from *Aliens*, the same franchise as Mother. Bishop\nis not an agent; it is a non-interactive CLI and an optional launchd heartbeat.\nIt never crashes, never blocks, and degrades silently to `posture: Cruise` when\ninput is missing.\n\n## Install\n\n```bash\nbash scripts/install.sh        # symlinks bin/bishop + bin/bishop-fetch-usage to ~/.local/bin/\nbishop install-agent           # loads the launchd job (runs every 60s)\n```\n\nVerify dependencies:\n\n```bash\nbash scripts/doctor.sh\n```\n\nRequired: `jq`, `flock` (`brew install jq flock`). Optional for tests: `bats`\n(`brew install bats-core`).\n\n## Data sources\n\nBishop tries two sources in order, falling back gracefully:\n\n### Primary — Anthropic OAuth `/api/oauth/usage`\n\n`bishop-fetch-usage` pulls proactive per-model utilization from the same\nendpoint that powers Claude Code's `/usage` UI:\n\n```\nhttps://api.anthropic.com/api/oauth/usage\nAuthorization: Bearer \u003ctoken\u003e\nanthropic-beta: oauth-2025-04-20\n```\n\nThe OAuth token is read from the macOS keychain (service: `Claude Code-credentials`).\n**This is macOS only.** On Linux or where the keychain entry is absent, Bishop\nfalls back to the Mother aggregate source automatically.\n\nBishop caches the OAuth response for `BISHOP_USAGE_CACHE_TTL_SECONDS` (default\n55 seconds — one tick under the 60-second launchd cadence). On HTTP 429\n(rate-limited) the previous cached response is preserved and reused; the cache\nfile is never corrupted by a failed fetch.\n\n### Fallback — Mother aggregate (`~/.mother/rate-limits.json`)\n\nMother streams Claude Code rate-limit events into this file. It provides\naggregate 5h and 7d utilization but no per-model breakdowns. When Bishop\nsuccessfully reads the OAuth endpoint, this file is not consulted for the\nmain posture computation (but it remains on disk as a backstop).\n\n## What it produces\n\n`bishop --refresh` writes `~/.claude/budget-posture.json`:\n\n```json\n{\n  \"ts\": \"2024-11-14T22:13:20Z\",\n  \"source\": \"oauth_usage\",\n  \"posture\": \"Cruise\",\n  \"five_hour\": {\n    \"used_pct\": 12,\n    \"elapsed_pct\": 50.0,\n    \"pace\": 0.24,\n    \"resets_at\": 1700009000,\n    \"level\": \"Put the hammer down\"\n  },\n  \"seven_day\": {\n    \"used_pct\": 71,\n    \"elapsed_pct\": 50.0,\n    \"pace\": 1.42,\n    \"resets_at\": 1700302400,\n    \"level\": \"Pump the brakes\"\n  },\n  \"models\": {\n    \"sonnet\": { \"status\": \"exhausted\", \"used_pct\": 100, \"resets_at\": 1700302400 },\n    \"opus\":   null,\n    \"haiku\":  null\n  },\n  \"exhausted_models\": [\"sonnet\"],\n  \"extra_usage\": {\n    \"is_enabled\": true,\n    \"used_credits\": 15650,\n    \"currency\": \"USD\",\n    \"monthly_limit\": null,\n    \"utilization\": null\n  },\n  \"source_mtime\": \"2024-11-14T22:12:50Z\",\n  \"source_age_seconds\": 30,\n  \"stale_input\": false\n}\n```\n\nWhen falling back to the Mother aggregate, `source` is `\"mother_aggregate\"`,\n`models` is `{\"sonnet\": null, \"opus\": null, \"haiku\": null}`, and\n`exhausted_models` is `[]`.\n\n\u003e **Note:** timestamps are second-precision ISO 8601 (`Z`-suffixed), not\n\u003e microsecond. This is a `jq` `todateiso8601` constraint.\n\n### Per-model fields\n\n| Field | Meaning |\n|-------|---------|\n| `models.sonnet` | Plan-specific 7-day Sonnet bucket. `null` if plan has no separate Sonnet cap. |\n| `models.opus`   | Plan-specific 7-day Opus bucket. `null` if plan has no separate Opus cap. |\n| `models.haiku`  | Plan-specific 7-day Haiku bucket. `null` if plan has no separate Haiku cap. |\n| `exhausted_models` | Array of model names where `status == \"exhausted\"`. |\n| `extra_usage` | Overage / pay-per-use block. `null` when source is Mother aggregate. |\n\nA model entry is `null` when either: the OAuth response has `seven_day_\u003cname\u003e: null`\n(plan doesn't have that bucket), or the bucket's `resets_at` is already in the past\n(window already rolled over).\n\n## Posture levels\n\nFive tiers, severity left → right. Top-level `posture` is the *most cautious*\nlevel across both windows.\n\n| Level                  | 5h pace      | 7d pace      | Meaning                                                       |\n|------------------------|--------------|--------------|---------------------------------------------------------------|\n| `Pump the brakes`      | \u003e 1.4        | \u003e 1.3        | Way over pace. Slow down — use cheaper models, defer heavy work. |\n| `Ease up`              | 1.1 – 1.4    | 1.0 – 1.3    | Drifting hot. Course-correct now before it gets worse.        |\n| `Cruise`               | 0.85 – 1.1   | 0.85 – 1.0   | On track. No change to model selection.                       |\n| `Push`                 | 0.6 – 0.85   | 0.6 – 0.85   | Below pace. Some headroom available.                          |\n| `Put the hammer down`  | \u003c 0.6        | \u003c 0.6        | Well under pace. Budget is ample; expensive models are fine.  |\n\n`stale_input: true` (source older than `BISHOP_SOURCE_STALE_SECONDS`) forces\n`posture` to `\"Cruise\"` as a safe default.\n\n## CLI reference\n\n```\nbishop --refresh          Compute posture and write budget-posture.json\nbishop status             Human-readable summary (includes per-model line)\nbishop status --json      Raw budget-posture.json to stdout\nbishop get \u003cfield\u003e        Single field (dot-path: bishop get five_hour.level)\n                          Examples: bishop get models.sonnet.status\n                                    bishop get exhausted_models\nbishop install-agent      Register launchd heartbeat\nbishop uninstall-agent    Remove launchd heartbeat\nbishop --help             Full usage\n```\n\n## Environment variables\n\n| Variable                        | Default                              | Purpose                                        |\n|---------------------------------|--------------------------------------|------------------------------------------------|\n| `BISHOP_SOURCE_PATH`            | `~/.mother/rate-limits.json`         | Mother aggregate fallback input                |\n| `BISHOP_OUTPUT_PATH`            | `~/.claude/budget-posture.json`      | Posture output file                            |\n| `BISHOP_SOURCE_STALE_SECONDS`   | `600`                                | Age threshold for stale input                  |\n| `BISHOP_DISABLED`               | (unset)                              | Set to any value to skip --refresh entirely    |\n| `BISHOP_NOW_OVERRIDE`           | (unset)                              | Override current epoch (testing)               |\n| `BISHOP_MTIME_OVERRIDE`         | (unset)                              | Override source mtime (testing, legacy path)   |\n| `BISHOP_USAGE_FETCH_CMD`        | `\u003cbishop-dir\u003e/bishop-fetch-usage`    | Path to fetch helper                           |\n| `BISHOP_USAGE_CACHE_PATH`       | `~/.claude/oauth-usage.json`         | Cached OAuth response                          |\n| `BISHOP_USAGE_CACHE_TTL_SECONDS`| `55`                                 | Max cache age before re-fetching               |\n\n## Consumer examples\n\n**Shell:**\n```bash\nbishop --refresh\nposture=\"$(bishop get posture)\"\necho \"Current posture: $posture\"\necho \"Exhausted models: $(bishop get exhausted_models)\"\n```\n\n**Mother integration (separate repo):**  \nMother reads `~/.claude/budget-posture.json` at job spawn time to bias\nmodel/effort selection. See `docs/PLAN.md` §\"Mother integration\" for the\nfull contract. The kill switch is `MOTHER_POSTURE_ENABLED=0`.\n\n## Run tests\n\n```bash\nbats tests/bishop.bats\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthehammer%2Fbishop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthehammer%2Fbishop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthehammer%2Fbishop/lists"}