|
| 1 | +--- |
| 2 | +name: costguard |
| 3 | +description: Find and quantify CI/cron and cloud-spend waste. Audit repos, run read-only provider billing checks, preview or apply CI auto-fixes, and render a monthly cost digest. |
| 4 | +license: MIT |
| 5 | +--- |
| 6 | + |
| 7 | +Drive **Costguard** — a read-only cost auditor for CI minutes, cron schedules, |
| 8 | +and cloud provider billing (GitHub Actions, Vercel, Supabase, Railway, Netlify, Neon, Cloudflare, and more). |
| 9 | +It finds waste, estimates the monthly dollar cost, and can surgically auto-fix |
| 10 | +CI workflow files. It never writes to provider accounts, never pushes git, and |
| 11 | +never prints tokens. |
| 12 | + |
| 13 | +Map the user's request to one Costguard CLI call and run it from the repo root. |
| 14 | + |
| 15 | +## Command launcher |
| 16 | + |
| 17 | +Costguard reads a `workspaces.json` registry from the **current working |
| 18 | +directory**, so run it from a project that has one (or run `registry init` |
| 19 | +first). Pick the launcher that matches your context; below, `costguard |
| 20 | +<subcommand>` means whichever you use. |
| 21 | + |
| 22 | +### If installed via plugin |
| 23 | + |
| 24 | +When this skill is loaded from the Costguard plugin, run the bundled build — it |
| 25 | +ships a prebuilt `dist/cli/index.js`, so no build step is needed. Locate the |
| 26 | +plugin root (the dir containing `dist/cli/index.js`): |
| 27 | + |
| 28 | +- **Claude Code** — it is `${CLAUDE_PLUGIN_ROOT}`: |
| 29 | + |
| 30 | + ```bash |
| 31 | + node "${CLAUDE_PLUGIN_ROOT}/dist/cli/index.js" <subcommand> ... |
| 32 | + ``` |
| 33 | + |
| 34 | +- **Codex** — walk up from this `SKILL.md` to the dir holding |
| 35 | + `.codex-plugin/plugin.json`: |
| 36 | + |
| 37 | + ```bash |
| 38 | + node "<plugin-root>/dist/cli/index.js" <subcommand> ... |
| 39 | + ``` |
| 40 | + |
| 41 | +### If using npx (no plugin) |
| 42 | + |
| 43 | +No checkout and no build — run the published CLI directly: |
| 44 | + |
| 45 | +```bash |
| 46 | +npx -y -p @costguard/costguard-mcp costguard <subcommand> ... |
| 47 | +``` |
| 48 | + |
| 49 | +Or, if `costguard` is on `PATH` (`npm i -g @costguard/costguard-mcp`), use it |
| 50 | +directly: `costguard <subcommand> ...`. Heads-up: `npx -y @costguard/costguard-mcp` |
| 51 | +(no `-p`, no subcommand) starts the MCP **server**, whereas `npx -y -p |
| 52 | +@costguard/costguard-mcp costguard <subcommand>` runs the **CLI**. |
| 53 | + |
| 54 | +## 1. Audit for waste (the main action) |
| 55 | + |
| 56 | +```bash |
| 57 | +costguard audit <workspace...> # named workspaces |
| 58 | +costguard audit --all # every registered workspace |
| 59 | +costguard audit <ws> --providers all # + read-only cloud billing checks |
| 60 | +costguard audit <ws> --ci-only # static CI checks only |
| 61 | +costguard audit <ws> --crons-only # cron checks only |
| 62 | +costguard audit <ws> --site # + read-only live-site checks (site URL from registry) |
| 63 | +costguard audit <ws> --substitutions # + cross-tool cheaper-alternative suggestions |
| 64 | +costguard audit <ws> --json # JSON instead of Markdown |
| 65 | +``` |
| 66 | + |
| 67 | +Prints a report: each finding has a severity, an estimated monthly USD cost, a |
| 68 | +detail, and a fix suggestion. Report stdout verbatim. |
| 69 | + |
| 70 | +## 2. Scan / registry / report |
| 71 | + |
| 72 | +```bash |
| 73 | +costguard scan # discover CI + cron files under the registry root |
| 74 | +costguard registry list # show registered workspaces |
| 75 | +costguard registry init # create a workspaces.json in the cwd |
| 76 | +costguard report # re-render the last saved audit run |
| 77 | +``` |
| 78 | + |
| 79 | +## 3. Auto-fix CI files (dry-run first) |
| 80 | + |
| 81 | +```bash |
| 82 | +costguard fix <ws> # dry-run: print a unified-diff preview, write nothing |
| 83 | +costguard fix <ws> --apply # write the surgical edits to disk (idempotent) |
| 84 | +costguard fix <ws> --pr # also emit local PR artifacts (no push) |
| 85 | +``` |
| 86 | + |
| 87 | +Default is dry-run. Only deterministic ADD-rule fixers run (timeout, |
| 88 | +concurrency, paths-ignore). Costguard never pushes; `--open-pr` is gated and |
| 89 | +refuses without an explicit token. |
| 90 | + |
| 91 | +## 4. Monthly cost digest |
| 92 | + |
| 93 | +```bash |
| 94 | +costguard digest # render the digest from the last run (dry-run) |
| 95 | +costguard digest --post # delivery adapter (inert unless configured) |
| 96 | +``` |
| 97 | + |
| 98 | +## 5. Auto-discover providers |
| 99 | + |
| 100 | +Detect which providers a repo uses — from config files, `package.json` deps, and |
| 101 | +env-var **names** (never values, never secrets). Covers all 13 wired providers |
| 102 | +plus inngest. |
| 103 | + |
| 104 | +```bash |
| 105 | +costguard discover [dir] # list detected providers + evidence (default dir: .) |
| 106 | +costguard discover . --json # JSON: { dir, providers, detections } |
| 107 | +costguard discover . --write # union-merge detected providers into ./workspaces.json (non-destructive) |
| 108 | +``` |
| 109 | + |
| 110 | +## 6. Live-site cost checks |
| 111 | + |
| 112 | +Read-only, GET-only checks on a live URL (no browser, no form submit, no auth |
| 113 | +replay). Flags transfer weight, oversized images, missing compression, weak cache |
| 114 | +headers, and render-blocking scripts. The `$/mo` headline is the single |
| 115 | +`site/transfer-weight` line — sourced when the host bills transfer (Vercel/Netlify), |
| 116 | +or an explicit `$0` performance note (Cloudflare Pages static / unknown host). |
| 117 | +Per-asset findings (`oversized-image`, `missing-compression`) put their dollar share |
| 118 | +in `detail` and carry `estMonthlyUsd: 0` (no double-count); a `$0` performance-only |
| 119 | +page never raises a `high` finding, so it never fails CI on cost alone. |
| 120 | + |
| 121 | +```bash |
| 122 | +costguard site <url> # Markdown report |
| 123 | +costguard site <url> --json # JSON findings |
| 124 | +``` |
| 125 | + |
| 126 | +`audit --site` runs the same checks for any workspace whose `workspaces.json` |
| 127 | +entry has a `site` URL. `audit --substitutions` adds cross-tool |
| 128 | +`<provider>/cheaper-alternative` suggestions (e.g. a static Vercel/Netlify Pro |
| 129 | +site → Cloudflare Pages), each with a sourced saving, migration effort, and |
| 130 | +lock-in caveat. |
| 131 | + |
| 132 | +## Provider billing checks |
| 133 | + |
| 134 | +`--providers <ids|all>` adds read-only billing checks for the providers listed |
| 135 | +on each workspace in `workspaces.json`. Tokens are read from the environment / |
| 136 | +`.env` only. A provider whose token env var is absent is **skipped**, not |
| 137 | +failed. Supported: `github`, `supabase`, `railway`, `netlify`, `neon`, `vercel`, |
| 138 | +`sentry`, `upstash`, `atlas`, `cloudflare`, `fly`, `render`, `datadog` (+ inngest |
| 139 | +detection). |
| 140 | + |
| 141 | +## 7. MCP tools (for AI coding agents) |
| 142 | + |
| 143 | +Costguard also ships a bundled **MCP server** that exposes the same engine over a |
| 144 | +host-agnostic tool surface (Claude Code, Codex, any MCP host). It wraps the same |
| 145 | +read-only engine functions — no new behavior, same posture. In Claude Code it is |
| 146 | +declared by `.claude-plugin/.mcp.json` and launched from the bundled build: |
| 147 | + |
| 148 | +```json |
| 149 | +{ "mcpServers": { "costguard": { "command": "node", |
| 150 | + "args": ["${CLAUDE_PLUGIN_ROOT}/dist/mcp/server.js"] } } } |
| 151 | +``` |
| 152 | + |
| 153 | +In the plugin, the server runs from the committed `dist/mcp/server.js` — no |
| 154 | +install step. |
| 155 | + |
| 156 | +### Codex MCP config |
| 157 | + |
| 158 | +For **Codex**, add one of these to `~/.codex/config.toml`. Use npx for a |
| 159 | +no-checkout install (pulls the published package), or the bundled build if you |
| 160 | +run Codex from the plugin: |
| 161 | + |
| 162 | +```toml |
| 163 | +# npx — no checkout |
| 164 | +[mcp_servers.costguard] |
| 165 | +command = "npx" |
| 166 | +args = ["-y", "@costguard/costguard-mcp"] |
| 167 | +``` |
| 168 | + |
| 169 | +```toml |
| 170 | +# bundled plugin build |
| 171 | +[mcp_servers.costguard] |
| 172 | +command = "node" |
| 173 | +args = ["<plugin-root>/dist/mcp/server.js"] |
| 174 | +``` |
| 175 | + |
| 176 | +Tools: |
| 177 | + |
| 178 | +| Tool | Posture | |
| 179 | +|---|---| |
| 180 | +| `audit_workspace` | read-only; returns a Findings envelope (`includeSite` adds site checks) | |
| 181 | +| `discover_providers` | read-only; env-var NAMES only, never values | |
| 182 | +| `audit_site` | read-only, GET-only | |
| 183 | +| `plan_fix` | dry-run; returns unified diffs only, writes nothing | |
| 184 | +| `apply_fix` | writes local CI files; REFUSES unless `confirmApply:true`; never pushes git | |
| 185 | +| `plan_live_checks` | plans a live billing read (see below); emits a snippet only with consent | |
| 186 | +| `ingest_live_reading` | parses a returned billing figure into a Finding | |
| 187 | + |
| 188 | +## 8. Live billing checks (`--live`) — opt-in, consent-gated |
| 189 | + |
| 190 | +`--live` **extends** the read-only posture above: it adds **browser-driven reads |
| 191 | +over your already-logged-in session**, performed by the **playwriter** MCP server |
| 192 | +under the agent's orchestration. This is a genuine posture change and is treated |
| 193 | +as one — **off by default, opt-in, and consent-gated.** costguard's own tools |
| 194 | +still never drive a browser and never see credentials: `plan_live_checks` only |
| 195 | +emits a **read-only** snippet (navigation + reading rendered billing figures — no |
| 196 | +clicks, typing, form submits, credential replay, cookies, localStorage, |
| 197 | +sessionStorage, or screenshots), and `ingest_live_reading` only parses the |
| 198 | +returned figure. The browser action is performed by playwriter, authorized by you. |
| 199 | + |
| 200 | +**API-first / browser-fallback:** `plan_live_checks` is API-first when a provider |
| 201 | +module exists and its API token resolves from the environment (a deterministic |
| 202 | +env-NAME check, no network probe) — in that case prefer `audit_workspace`. Only |
| 203 | +when there is no usable API token does it fall back to a browser playbook. |
| 204 | + |
| 205 | +**Three consent gates (all required):** (1) the host's MCP tool-call consent; |
| 206 | +(2) costguard's own per-run confirmation — `plan_live_checks` returns a |
| 207 | +`consentNotice` the agent MUST surface, and emits the actionable snippet only when |
| 208 | +called with `confirmLive:true`; (3) playwriter's own consent before it executes. |
| 209 | + |
| 210 | +**Graceful degrade:** if playwriter is not connected, the agent cannot run the |
| 211 | +snippet; `ingest_live_reading` returns a `kind:"diagnostic"` Finding (excluded |
| 212 | +from cost totals) and the audit never blocks. |
| 213 | + |
| 214 | +## Notes |
| 215 | + |
| 216 | +- All provider calls are read-only (GET / read-only GraphQL). No POST/PUT/PATCH/ |
| 217 | + DELETE to provider accounts. |
| 218 | +- Estimated dollar costs are best-effort and depend on plan/tier; treat them as |
| 219 | + directional, not invoices. |
| 220 | +- Requires `node` on `PATH`. The bare `costguard` command is optional when the |
| 221 | + skill runs from the plugin — use the plugin-root `node` launcher above. |
0 commit comments