{
  "id": "agent-spend-cap",
  "type": "loadout",
  "title": "One spend cap across Claude Code, Codex and Antigravity",
  "summary": "Run headless Claude Code, Codex and Antigravity CLI through one guard: a shared daily cap, per-run budgets, a ledger, and the provider limits that stop spend.",
  "description": "Each coding agent has its own meter. Claude Code reports dollars and can stop itself with --max-budget-usd. Codex and Antigravity CLI report only tokens and have no budget flag, so a script that mixes them can burn through every budget overnight. This loadout puts one standard-library guard in front of every headless run. It refuses the run once the shared daily or monthly cap or the run count is reached, and gives Claude Code a budget no bigger than what is left. It prices Codex and Antigravity tokens from prices you set, and writes one ledger line per run. It also lists the provider-side limits that hold when something bypasses the guard, including keeping Antigravity's useG1Credits off. The verifier drives the guard with stubs that print each agent's documented JSON and checks the arithmetic and the refusal.\n",
  "highlights": [
    "One daily, monthly and run-count cap across three agents",
    "Claude Code: --max-budget-usd shrinks to what is left; total_cost_usd recorded",
    "Codex and Antigravity: documented token usage priced from your own price table",
    "Exit 3 means stop the loop; agents never raise caps or buy credits",
    "Provider-side limits listed, including Antigravity's useG1Credits"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "Agent Loadout",
    "url": "https://loadout.jnw-it.com"
  },
  "credits": [
    {
      "title": "Claude Code docs: manage costs",
      "url": "https://code.claude.com/docs/en/costs"
    },
    {
      "title": "Claude Code docs: run Claude Code programmatically (total_cost_usd, --max-budget-usd)",
      "url": "https://code.claude.com/docs/en/headless"
    },
    {
      "title": "Antigravity CLI docs: headless mode (usage envelope)",
      "url": "https://antigravity.google/docs/cli/headless"
    },
    {
      "title": "Antigravity CLI docs: settings (useG1Credits)",
      "url": "https://antigravity.google/docs/cli/reference"
    },
    {
      "title": "Codex SDK event types (turn.completed usage)",
      "url": "https://github.com/openai/codex/blob/main/sdk/typescript/src/events.ts"
    }
  ],
  "categories": [
    "ai-agents",
    "workflow"
  ],
  "stacks": [
    "python",
    "bash"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "Rules for running coding agents headless under one shared spend cap.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": "AGENTS.md",
        "codex": "AGENTS.md",
        "cursor": "AGENTS.md",
        "gemini-cli": "AGENTS.md",
        "antigravity-cli": "AGENTS.md",
        "copilot": "AGENTS.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/AGENTS.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/latest/AGENTS.md",
      "bytes": 1253,
      "lang": "markdown",
      "content": "# AGENTS.md: shared spend cap for coding agents\n\nThis repository runs coding agents headless (scripts, loops, CI): Claude Code, Codex and Antigravity CLI. Every\nheadless run goes through one spend guard with a shared daily and monthly cap. The user sets the caps; agents never\nchange them.\n\n## Rules\n- Run headless agents only through the `spend-cap` skill's guard:\n  `python3 spend_guard.py run --agent <claude|codex|agy> --task <name> -- <agent command>`.\n- Use the machine-readable output each agent documents (`--output-format json` for Claude Code and Antigravity,\n  `--json` for Codex) so the guard can read cost or tokens.\n- Exit 3 from the guard means a cap is reached. Stop, report the totals from `spend_guard.py report`, and wait for\n  the user. Do not retry, and do not call the agent directly instead.\n- Never raise caps, buy credits, change plans, turn on overage or usage credits, or set Antigravity's\n  `useG1Credits` to `true`.\n- Keep each run small: one task, scoped files, a clear stop condition. Prefer one agent per task to fan-outs.\n- Report spend as an estimate, and say that interactive sessions are not in the ledger.\n\n## Done means\n- The ledger has a line for every headless run, and `spend_guard.py report` is under the caps.\n"
    },
    {
      "path": "CLAUDE.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Claude Code.",
      "skill": null,
      "agent": "claude-code",
      "install_to": {
        "claude-code": "CLAUDE.md",
        "codex": null,
        "cursor": null,
        "gemini-cli": null,
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/latest/CLAUDE.md",
      "bytes": 401,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n\n- Skills from this loadout live in `.claude/skills/`: `spend-cap`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"One spend cap across Claude Code, Codex and Antigravity\" v1.0.0 — https://loadout.jnw-it.com/loadouts/agent-spend-cap/ -->\n"
    },
    {
      "path": "GEMINI.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Gemini CLI.",
      "skill": null,
      "agent": "gemini-cli",
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": null,
        "gemini-cli": "GEMINI.md",
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/GEMINI.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/latest/GEMINI.md",
      "bytes": 402,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n\n- Skills from this loadout live in `.agents/skills/`: `spend-cap`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"One spend cap across Claude Code, Codex and Antigravity\" v1.0.0 — https://loadout.jnw-it.com/loadouts/agent-spend-cap/ -->\n"
    },
    {
      "path": "skills/spend-cap/SKILL.md",
      "role": "skill",
      "description": "Set caps, run agents through the guard, read the ledger.",
      "skill": "spend-cap",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/spend-cap/SKILL.md",
        "codex": ".agents/skills/spend-cap/SKILL.md",
        "cursor": ".agents/skills/spend-cap/SKILL.md",
        "gemini-cli": ".agents/skills/spend-cap/SKILL.md",
        "antigravity-cli": ".agents/skills/spend-cap/SKILL.md",
        "copilot": ".agents/skills/spend-cap/SKILL.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/SKILL.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/latest/skills/spend-cap/SKILL.md",
      "bytes": 3179,
      "lang": "markdown",
      "content": "---\nname: spend-cap\ndescription: Put one daily and monthly spend cap across headless Claude Code, Codex and Antigravity CLI runs, with a per-run budget and a ledger, and set the provider-side limits that are the real hard stop. Use when scripting agent runs, running agents in loops or CI, or when someone asks what the agents cost.\n---\n\n# One spend cap across agents\n\nEach agent has its own meter. Claude Code reports dollars (`total_cost_usd` in `--output-format json`) and can stop\nitself (`--max-budget-usd`). Codex (`codex exec --json`) and Antigravity (`agy -p --output-format json`) report\ntokens only, and neither has a budget flag. A loop that mixes them can spend all three budgets before anyone looks.\n`scripts/spend_guard.py` puts one ledger and one cap in front of all of them.\n\n## Set up\n\n```bash\nmkdir -p .agent-spend\ncp <this skill>/references/caps.example.json .agent-spend/caps.json\necho \".agent-spend/\" >> .gitignore\n```\n\nEdit `caps.json` with the user: `daily_usd`, `monthly_usd`, `per_run_usd` and `max_runs_per_day`. Add `prices` for\nCodex and Antigravity in USD per million tokens, taken from the provider's current price page for the model in use.\nNever guess a price. With zero prices those agents count towards `max_runs_per_day` only.\n\n## Run every headless agent through it\n\n```bash\npython3 spend_guard.py run --agent claude --task fix-tests -- claude -p \"Fix the failing tests\" --output-format json\npython3 spend_guard.py run --agent codex  --task triage    -- codex exec --json \"Triage open issues\"\npython3 spend_guard.py run --agent agy    --task docs      -- agy -p \"Update the README\" --output-format json\npython3 spend_guard.py report\n```\n\n- Before the run it refuses with exit 3 if today's or this month's cap is reached, if what is left is under\n  `min_run_usd`, or if `max_runs_per_day` is used up. Treat exit 3 as \"stop the loop\", not \"retry\".\n- For Claude Code it appends `--max-budget-usd` set to the smaller of `per_run_usd` and what is left today, so the\n  agent stops itself.\n- Codex and Antigravity cannot be stopped mid-run. Their cost is estimated from tokens afterwards, and a run can\n  overshoot by up to one run. Keep `per_run_usd` small and the prompts scoped.\n- Exit 4 means no cost or usage was found in the output (wrong flags, or a crash). Look before running again.\n- The ledger is `.agent-spend/ledger.jsonl`: one line per run with agent, task, exit code, cost and token usage.\n  Days and months use the machine's time zone.\n\n## Provider-side limits are the hard stop\n\nThe guard only sees runs that go through it. Set the provider's own limit as well, and read\n`references/provider-limits.md` for where each one lives. Antigravity's `useG1Credits` setting must stay `false`\nunless the user decides to pay for credits after the plan quota runs out.\n\n## Rules\n\n- Never raise a cap, add credits, change a plan or turn on overage billing yourself. Report the refusal and the\n  ledger totals, and let the user decide.\n- Never route around the guard by calling an agent directly in a loop.\n- Interactive sessions are outside the ledger. Say so when you report spend.\n- Costs here are estimates. The provider's bill is the truth.\n"
    },
    {
      "path": "skills/spend-cap/scripts/spend_guard.py",
      "role": "reference",
      "description": "Shared cap, per-run budget and ledger for three agents.",
      "skill": "spend-cap",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/spend-cap/scripts/spend_guard.py",
        "codex": ".agents/skills/spend-cap/scripts/spend_guard.py",
        "cursor": ".agents/skills/spend-cap/scripts/spend_guard.py",
        "gemini-cli": ".agents/skills/spend-cap/scripts/spend_guard.py",
        "antigravity-cli": ".agents/skills/spend-cap/scripts/spend_guard.py",
        "copilot": ".agents/skills/spend-cap/scripts/spend_guard.py"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/scripts/spend_guard.py",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/latest/skills/spend-cap/scripts/spend_guard.py",
      "bytes": 8498,
      "lang": "markdown",
      "content": "#!/usr/bin/env python3\n\"\"\"Shared spend cap for headless agent runs (Claude Code, Codex, Antigravity CLI).\n\n    spend_guard.py run --agent claude --task fix-tests -- claude -p \"...\" --output-format json\n    spend_guard.py run --agent codex  --task triage    -- codex exec --json \"...\"\n    spend_guard.py run --agent agy    --task docs      -- agy -p \"...\" --output-format json\n    spend_guard.py report\n\nBefore a run it refuses (exit 3) when today's or this month's spend has reached the cap, or\nwhen what is left is less than one run's budget. For Claude Code it adds --max-budget-usd so\nthe agent itself stops at the smaller of the per-run cap and what is left. Codex and\nAntigravity report tokens, not money, so their cost comes from the prices you set in\ncaps.json, and they cannot be stopped mid-run: the provider-side limit is the hard stop.\n\nConfig: .agent-spend/caps.json (or SPEND_DIR). Ledger: .agent-spend/ledger.jsonl.\nStandard library only. Costs are estimates; the provider's bill is the truth.\n\"\"\"\n\nimport argparse\nimport datetime as dt\nimport fcntl\nimport json\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nSPEND_DIR = Path(os.environ.get(\"SPEND_DIR\", \".agent-spend\"))\nCAPS = SPEND_DIR / \"caps.json\"\nLEDGER = SPEND_DIR / \"ledger.jsonl\"\nREFUSED = 3\n\n\ndef load_caps():\n    if not CAPS.exists():\n        sys.exit(f\"spend_guard: {CAPS} not found; copy the spend-cap skill's caps.example.json there\")\n    caps = json.loads(CAPS.read_text())\n    for key in (\"daily_usd\", \"monthly_usd\", \"per_run_usd\"):\n        if not isinstance(caps.get(key), (int, float)) or caps[key] <= 0:\n            sys.exit(f\"spend_guard: caps.json needs a positive {key}\")\n    return caps\n\n\ndef now():\n    return dt.datetime.now().astimezone()\n\n\ndef totals(at):\n    \"\"\"Spend today, spend this month and runs today, in the local time zone.\"\"\"\n    day = month = 0.0\n    runs = 0\n    if LEDGER.exists():\n        for line in LEDGER.read_text().splitlines():\n            if not line.strip():\n                continue\n            rec = json.loads(line)\n            ts = dt.datetime.fromisoformat(rec[\"ts\"]).astimezone(at.tzinfo)\n            cost = rec.get(\"cost_usd\") or 0.0\n            if (ts.year, ts.month) == (at.year, at.month):\n                month += cost\n                if ts.date() == at.date():\n                    day += cost\n                    runs += 1\n    return round(day, 4), round(month, 4), runs\n\n\ndef price(caps, agent, usage):\n    p = (caps.get(\"prices\") or {}).get(agent)\n    if not p:\n        return None\n    mtok = 1_000_000\n    cached = usage.get(\"cached_input_tokens\", usage.get(\"cache_read_tokens\", 0))\n    fresh_input = max(usage.get(\"input_tokens\", 0) - cached, 0)\n    output = usage.get(\"output_tokens\", 0) + usage.get(\"reasoning_output_tokens\", usage.get(\"thinking_tokens\", 0))\n    cost = (\n        fresh_input * p[\"input_per_mtok\"]\n        + cached * p.get(\"cached_input_per_mtok\", p[\"input_per_mtok\"])\n        + output * p[\"output_per_mtok\"]\n    ) / mtok\n    return round(cost, 6)\n\n\ndef parse(agent, stdout):\n    \"\"\"Return (cost_usd or None, usage dict) from the agent's documented JSON output.\"\"\"\n    usage, cost = {}, None\n    lines = [ln for ln in stdout.splitlines() if ln.strip().startswith(\"{\")]\n    if agent == \"claude\":\n        for ln in reversed(lines):\n            obj = json.loads(ln)\n            if \"total_cost_usd\" in obj:\n                return float(obj[\"total_cost_usd\"]), obj.get(\"usage\", {})\n    elif agent == \"codex\":\n        for ln in lines:\n            obj = json.loads(ln)\n            if obj.get(\"type\") == \"turn.completed\":\n                for k, v in obj.get(\"usage\", {}).items():\n                    usage[k] = usage.get(k, 0) + v\n    elif agent == \"agy\":\n        for ln in reversed(lines):\n            obj = json.loads(ln)\n            result = obj.get(\"result\", obj) if obj.get(\"event\") in (None, \"result\") else None\n            if result and \"usage\" in result:\n                usage = result[\"usage\"]\n                break\n    return cost, usage\n\n\ndef required_output_flags(agent, cmd):\n    if agent == \"claude\" and \"--output-format\" not in cmd:\n        return \"add --output-format json (or stream-json) so the cost can be read\"\n    if agent == \"codex\" and \"--json\" not in cmd:\n        return \"add --json so token usage can be read\"\n    if agent == \"agy\" and \"--output-format\" not in cmd:\n        return \"add --output-format json (or stream-json) so token usage can be read\"\n    return None\n\n\ndef run(args):\n    caps = load_caps()\n    cmd = args.cmd[1:] if args.cmd and args.cmd[0] == \"--\" else args.cmd\n    if not cmd:\n        sys.exit(\"spend_guard: give the agent command after --\")\n    problem = required_output_flags(args.agent, cmd)\n    if problem:\n        sys.exit(f\"spend_guard: {problem}\")\n    if args.agent in (\"codex\", \"agy\") and not (caps.get(\"prices\") or {}).get(args.agent):\n        sys.exit(f\"spend_guard: no prices for {args.agent} in caps.json, so its spend cannot be capped\")\n\n    SPEND_DIR.mkdir(parents=True, exist_ok=True)\n    lock = open(SPEND_DIR / \".lock\", \"w\")\n    fcntl.flock(lock, fcntl.LOCK_EX)\n    at = now()\n    day, month, runs = totals(at)\n    left = min(caps[\"daily_usd\"] - day, caps[\"monthly_usd\"] - month)\n    budget = round(min(caps[\"per_run_usd\"], left), 2)\n    max_runs = caps.get(\"max_runs_per_day\")\n    if max_runs is not None and runs >= max_runs:\n        print(f\"spend_guard: REFUSED {args.agent}/{args.task}: {runs} runs today, cap {max_runs}\", file=sys.stderr)\n        return REFUSED\n    if left <= 0 or budget < caps.get(\"min_run_usd\", 0.05):\n        print(\n            f\"spend_guard: REFUSED {args.agent}/{args.task}: today ${day:.2f} of ${caps['daily_usd']:.2f}, \"\n            f\"month ${month:.2f} of ${caps['monthly_usd']:.2f}\",\n            file=sys.stderr,\n        )\n        return REFUSED\n    if args.agent == \"claude\" and \"--max-budget-usd\" not in cmd:\n        cmd = cmd + [\"--max-budget-usd\", f\"{budget:.2f}\"]\n    print(f\"spend_guard: {args.agent}/{args.task} budget ${budget:.2f} (left today ${left:.2f})\", file=sys.stderr)\n    fcntl.flock(lock, fcntl.LOCK_UN)\n\n    proc = subprocess.run(cmd, stdout=subprocess.PIPE, text=True)\n    sys.stdout.write(proc.stdout)\n    cost, usage = parse(args.agent, proc.stdout)\n    if cost is None:\n        cost = price(caps, args.agent, usage)\n    rec = {\n        \"ts\": now().isoformat(timespec=\"seconds\"),\n        \"agent\": args.agent,\n        \"task\": args.task,\n        \"exit\": proc.returncode,\n        \"cost_usd\": cost,\n        \"estimated_from_tokens\": args.agent != \"claude\",\n        \"usage\": usage,\n    }\n    if cost is None:\n        rec[\"note\"] = \"no cost or usage found in the output; recorded as unknown\"\n    fcntl.flock(lock, fcntl.LOCK_EX)\n    with LEDGER.open(\"a\") as f:\n        f.write(json.dumps(rec) + \"\\n\")\n    fcntl.flock(lock, fcntl.LOCK_UN)\n    shown = \"unknown\" if cost is None else f\"${cost:.4f}\"\n    print(f\"spend_guard: recorded {args.agent}/{args.task} {shown}\", file=sys.stderr)\n    if cost is None:\n        return 4\n    if cost > budget:\n        print(f\"spend_guard: WARNING run cost {shown} exceeded its ${budget:.2f} budget\", file=sys.stderr)\n    return proc.returncode\n\n\ndef report(_args):\n    caps = load_caps()\n    at = now()\n    day, month, runs = totals(at)\n    by_agent = {}\n    if LEDGER.exists():\n        for line in LEDGER.read_text().splitlines():\n            if line.strip():\n                rec = json.loads(line)\n                ts = dt.datetime.fromisoformat(rec[\"ts\"]).astimezone(at.tzinfo)\n                if (ts.year, ts.month) == (at.year, at.month):\n                    a = by_agent.setdefault(rec[\"agent\"], [0, 0.0])\n                    a[0] += 1\n                    a[1] += rec.get(\"cost_usd\") or 0.0\n    print(f\"today  ${day:.2f} of ${caps['daily_usd']:.2f} ({runs} run(s))\")\n    print(f\"month  ${month:.2f} of ${caps['monthly_usd']:.2f}\")\n    for agent, (runs, cost) in sorted(by_agent.items()):\n        print(f\"{agent:<7}{runs} run(s) ${cost:.2f} this month\")\n    return 0\n\n\ndef main():\n    ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])\n    sub = ap.add_subparsers(dest=\"command\", required=True)\n    r = sub.add_parser(\"run\")\n    r.add_argument(\"--agent\", required=True, choices=[\"claude\", \"codex\", \"agy\"])\n    r.add_argument(\"--task\", required=True)\n    r.add_argument(\"cmd\", nargs=argparse.REMAINDER)\n    r.set_defaults(func=run)\n    sub.add_parser(\"report\").set_defaults(func=report)\n    args = ap.parse_args()\n    sys.exit(args.func(args))\n\n\nif __name__ == \"__main__\":\n    main()\n"
    },
    {
      "path": "skills/spend-cap/references/caps.example.json",
      "role": "reference",
      "description": "Caps and price table to copy into .agent-spend/.",
      "skill": "spend-cap",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/spend-cap/references/caps.example.json",
        "codex": ".agents/skills/spend-cap/references/caps.example.json",
        "cursor": ".agents/skills/spend-cap/references/caps.example.json",
        "gemini-cli": ".agents/skills/spend-cap/references/caps.example.json",
        "antigravity-cli": ".agents/skills/spend-cap/references/caps.example.json",
        "copilot": ".agents/skills/spend-cap/references/caps.example.json"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/caps.example.json",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/latest/skills/spend-cap/references/caps.example.json",
      "bytes": 511,
      "lang": "json",
      "content": "{\n  \"daily_usd\": 5.0,\n  \"monthly_usd\": 60.0,\n  \"per_run_usd\": 1.0,\n  \"min_run_usd\": 0.05,\n  \"max_runs_per_day\": 40,\n  \"prices\": {\n    \"codex\": { \"input_per_mtok\": 0.0, \"cached_input_per_mtok\": 0.0, \"output_per_mtok\": 0.0 },\n    \"agy\": { \"input_per_mtok\": 0.0, \"cached_input_per_mtok\": 0.0, \"output_per_mtok\": 0.0 }\n  },\n  \"_note\": \"Fill prices from your provider's current price page for the model you run (USD per million tokens). Zero prices mean those agents count towards max_runs_per_day but not money.\"\n}\n"
    },
    {
      "path": "skills/spend-cap/references/provider-limits.md",
      "role": "reference",
      "description": "Where each provider's own limit lives.",
      "skill": "spend-cap",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/spend-cap/references/provider-limits.md",
        "codex": ".agents/skills/spend-cap/references/provider-limits.md",
        "cursor": ".agents/skills/spend-cap/references/provider-limits.md",
        "gemini-cli": ".agents/skills/spend-cap/references/provider-limits.md",
        "antigravity-cli": ".agents/skills/spend-cap/references/provider-limits.md",
        "copilot": ".agents/skills/spend-cap/references/provider-limits.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/provider-limits.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/latest/skills/spend-cap/references/provider-limits.md",
      "bytes": 1981,
      "lang": "markdown",
      "content": "# Provider-side spend limits\n\nThe guard is a soft cap in front of headless runs. These are the limits that hold even when something bypasses it.\nCheck each against the provider's current docs before relying on it.\n\n## Claude Code\n\n- Per run: `claude -p ... --max-budget-usd <amount>` (print mode only). With `--output-format json` the result\n  includes `total_cost_usd`, a client-side estimate.\n- Claude Console (API key): set workspace spend limits on the \"Claude Code\" workspace. Usage and cost reporting is in\n  the Console.\n- Pro and Max with usage credits: set a monthly spend limit under Settings > Usage on claude.ai. `/usage` shows spend\n  against it.\n- Team and Enterprise: admins set spend limits for the organisation, groups or individual members.\n- Amazon Bedrock, Google Cloud or Microsoft Foundry: use that cloud's budget controls.\n\nSource: https://code.claude.com/docs/en/costs and https://code.claude.com/docs/en/headless\n\n## Codex\n\n- `codex exec --json` emits `turn.completed` events with `usage` (`input_tokens`, `cached_input_tokens`,\n  `output_tokens`, `reasoning_output_tokens`). There is no money figure and no budget flag.\n- Cap spend through your OpenAI plan or API project settings, and check which of them your account actually enforces.\n\nSource: https://developers.openai.com/codex/noninteractive and the Codex SDK event types\n(https://github.com/openai/codex/blob/main/sdk/typescript/src/events.ts)\n\n## Antigravity CLI\n\n- `agy -p ... --output-format json` returns `usage` (`input_tokens`, `output_tokens`, `thinking_tokens`,\n  `cache_read_tokens`, `total_tokens`). There is no money figure and no budget flag. `--print-timeout` bounds time,\n  not tokens.\n- `useG1Credits` (default `false`) lets the CLI spend personal AI credits once plan quotas are exhausted. Keep it\n  `false`. `/credits` shows the remaining credits and `/usage` the model quota.\n\nSource: https://antigravity.google/docs/cli/headless and https://antigravity.google/docs/cli/reference\n"
    }
  ],
  "requires": {
    "tools": [
      "python3"
    ],
    "env": [],
    "mcp": []
  },
  "tested": {
    "status": "untested",
    "last_run": null,
    "fixture": null,
    "per_agent": {
      "claude-code": "untested",
      "codex": "untested",
      "cursor": "untested",
      "gemini-cli": "untested",
      "antigravity-cli": "untested",
      "copilot": "untested"
    }
  },
  "security": {
    "scanned": true,
    "scanner": "intake-precheck/1",
    "verdict": "clean",
    "network": false,
    "scripts": [
      "spend_guard.py"
    ],
    "notes": "The guard runs only the agent command you pass it and writes a ledger under .agent-spend/. It makes no network calls of its own and never changes agent settings or billing."
  },
  "featured": false,
  "deprecated": false,
  "last_checked": "2026-10-17",
  "published": "2026-10-17",
  "updated": "2026-10-17",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "antigravity-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 1,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://loadout.jnw-it.com/loadouts/agent-spend-cap/",
  "api_url": "https://loadout.jnw-it.com/api/loadouts/agent-spend-cap.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-17T21:00:35Z",
    "commit": "08c3710727a4d6fc320a98ecf32bdc0a780eaa6c",
    "content_current": true,
    "agent_tests": "not-run",
    "counts": {
      "pass": 9,
      "fail": 0,
      "skip": 0,
      "not_applicable": 1
    },
    "meaning": "Level 1: automated checks on the files (parse, install paths, MCP config, safety scan, references, size, licence, starter build). No AI agent was run.",
    "checks": [
      {
        "id": "listing",
        "name": "Listing",
        "status": "pass",
        "summary": "Schema valid; 7 files declared, none missing or extra",
        "details": [
          "tested.status: untested (agent tests are separate from Level 1)"
        ]
      },
      {
        "id": "parse",
        "name": "Files parse",
        "status": "pass",
        "summary": "All 7 files parse; front matter valid",
        "details": []
      },
      {
        "id": "adapters",
        "name": "Adapters",
        "status": "pass",
        "summary": "CLAUDE.md and GEMINI.md import AGENTS.md and are up to date",
        "details": [
          "CLAUDE.md: matches the generator",
          "GEMINI.md: matches the generator"
        ]
      },
      {
        "id": "install-paths",
        "name": "Install paths",
        "status": "pass",
        "summary": "Correct paths for all 6 agents; layouts re-discovered",
        "details": [
          "Claude Code: 6 files, 1 skill in .claude/skills/",
          "Codex CLI: 5 files, 1 skill in .agents/skills/",
          "Cursor: 5 files, 1 skill in .agents/skills/",
          "Gemini CLI: 6 files, 1 skill in .agents/skills/",
          "Antigravity CLI: 5 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 5 files, 1 skill in .agents/skills/"
        ]
      },
      {
        "id": "mcp",
        "name": "MCP config",
        "status": "n/a",
        "summary": "No MCP servers in this loadout",
        "details": []
      },
      {
        "id": "safety",
        "name": "Safety scan",
        "status": "pass",
        "summary": "Clean: no credentials, risky shell or hidden text",
        "details": [
          "Scanner intake-precheck/1: verdict clean; network no (listing says no)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "2 references between files resolve for every agent",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "7 files, 15.8 KB total; largest skills/spend-cap/scripts/spend_guard.py 8.3 KB",
        "details": [
          "Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule"
        ]
      },
      {
        "id": "licence",
        "name": "Licence",
        "status": "pass",
        "summary": "MIT, by Agent Loadout; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "The spend-cap skill's guard runs stubbed Claude Code, Codex and Antigravity CLI commands that print each agent's documented JSON. It reads Claude's dollar cost, prices Codex and Antigravity tokens from caps.json, shrinks Claude's --max-budget-usd to what is left, refuses the run past the daily cap, ",
        "details": [
          "Applied 11 loadout files at every agent's paths",
          "✓ set up caps and stub agents: bash -c cp .agents/skills/spend-cap/scripts/spend_guard.py . && mkdir -p .agent-spend && cp caps.json .agent-spend/caps.json && python3 -c 'import json; json.load(open(\".agents/skills/spend-cap/references/caps.example.json\"))' && echo ready",
          "✓ Claude run gets --max-budget-usd and records total_cost_usd: bash -c PATH=\"$PWD/bin:$PATH\" python3 spend_guard.py run --agent claude --task fix-tests -- claude -p fix --output-format json 2>&1",
          "✓ Codex turn.completed tokens priced from caps.json: bash -c PATH=\"$PWD/bin:$PATH\" python3 spend_guard.py run --agent codex --task triage -- codex exec --json triage 2>&1 >/dev/null",
          "✓ Antigravity usage envelope priced from caps.json: bash -c PATH=\"$PWD/bin:$PATH\" python3 spend_guard.py run --agent agy --task docs -- agy -p docs --output-format json 2>&1 >/dev/null",
          "✓ Claude budget shrinks to what is left: bash -c STUB_COST=0.06 PATH=\"$PWD/bin:$PATH\" python3 spend_guard.py run --agent claude --task last -- claude -p last --output-format json 2>&1 >/dev/null",
          "✓ refused once the daily cap is reached: bash -c PATH=\"$PWD/bin:$PATH\" python3 spend_guard.py run --agent claude --task more -- claude -p more --output-format json; echo \"exit=$?\"",
          "✓ report and ledger: bash -c python3 spend_guard.py report && wc -l < .agent-spend/ledger.jsonl",
          "✓ a run without machine-readable output is rejected before it starts: bash -c PATH=\"$PWD/bin:$PATH\" python3 spend_guard.py run --agent codex --task plain -- codex exec plain; echo \"exit=$?\"",
          "Covers: One ledger and one daily cap across Claude Code, Codex and Antigravity CLI headless runs",
          "Covers: Claude Code's documented total_cost_usd and --max-budget-usd; Codex turn.completed usage; Antigravity's usage envelope",
          "Covers: Per-run budget shrinks to what is left, and the run past the cap never starts",
          "Covers: Runs without machine-readable output are rejected"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/spend-cap/SKILL.md",
          "target": ".claude/skills/spend-cap/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/spend-cap/scripts/spend_guard.py",
          "target": ".claude/skills/spend-cap/scripts/spend_guard.py",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/scripts/spend_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/caps.example.json",
          "target": ".claude/skills/spend-cap/references/caps.example.json",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/caps.example.json",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/provider-limits.md",
          "target": ".claude/skills/spend-cap/references/provider-limits.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/provider-limits.md",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/agent-spend-cap/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/spend-cap/SKILL.md",
          "target": ".agents/skills/spend-cap/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/spend-cap/scripts/spend_guard.py",
          "target": ".agents/skills/spend-cap/scripts/spend_guard.py",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/scripts/spend_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/caps.example.json",
          "target": ".agents/skills/spend-cap/references/caps.example.json",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/caps.example.json",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/provider-limits.md",
          "target": ".agents/skills/spend-cap/references/provider-limits.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/provider-limits.md",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/agent-spend-cap/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/spend-cap/SKILL.md",
          "target": ".agents/skills/spend-cap/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/spend-cap/scripts/spend_guard.py",
          "target": ".agents/skills/spend-cap/scripts/spend_guard.py",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/scripts/spend_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/caps.example.json",
          "target": ".agents/skills/spend-cap/references/caps.example.json",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/caps.example.json",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/provider-limits.md",
          "target": ".agents/skills/spend-cap/references/provider-limits.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/provider-limits.md",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/agent-spend-cap/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/spend-cap/SKILL.md",
          "target": ".agents/skills/spend-cap/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/spend-cap/scripts/spend_guard.py",
          "target": ".agents/skills/spend-cap/scripts/spend_guard.py",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/scripts/spend_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/caps.example.json",
          "target": ".agents/skills/spend-cap/references/caps.example.json",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/caps.example.json",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/provider-limits.md",
          "target": ".agents/skills/spend-cap/references/provider-limits.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/provider-limits.md",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/agent-spend-cap/gemini-cli.zip"
    },
    "antigravity-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/spend-cap/SKILL.md",
          "target": ".agents/skills/spend-cap/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/spend-cap/scripts/spend_guard.py",
          "target": ".agents/skills/spend-cap/scripts/spend_guard.py",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/scripts/spend_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/caps.example.json",
          "target": ".agents/skills/spend-cap/references/caps.example.json",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/caps.example.json",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/provider-limits.md",
          "target": ".agents/skills/spend-cap/references/provider-limits.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/provider-limits.md",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/agent-spend-cap/antigravity-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/spend-cap/SKILL.md",
          "target": ".agents/skills/spend-cap/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/spend-cap/scripts/spend_guard.py",
          "target": ".agents/skills/spend-cap/scripts/spend_guard.py",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/scripts/spend_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/caps.example.json",
          "target": ".agents/skills/spend-cap/references/caps.example.json",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/caps.example.json",
          "role": "reference"
        },
        {
          "path": "skills/spend-cap/references/provider-limits.md",
          "target": ".agents/skills/spend-cap/references/provider-limits.md",
          "raw_url": "https://loadout.jnw-it.com/raw/agent-spend-cap/1.0.0/skills/spend-cap/references/provider-limits.md",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/agent-spend-cap/copilot.zip"
    }
  },
  "source_zip_url": "https://loadout.jnw-it.com/download/agent-spend-cap/source.zip"
}