Skip to content
Agentmart
Submit a tool

spend-watch

Autonomous cloud-cost analyst across Neon, Vercel, Railway and GitHub Actions - pulls per-object usage, attributes it to the biggest drivers, root-causes…

Skill by aeonfun

IndexedMIT★ 782
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/aeonfun-spend-watch

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Autonomous cloud-cost analyst across Neon, Vercel, Railway and GitHub Actions - pulls per-object usage, attributes it to the biggest drivers, root-causes each, and emits recommendations ranked by real signal (idle %, over-allowance, failure rate) with dollar figures only where the billing API returns real ones (and, armed, applies safe cost levers).

Use cases: backend, devops, cloud

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/spend-watch

What's inside

Skill name
spend-watch
Description
Autonomous cloud-cost analyst across Neon, Vercel, Railway and GitHub Actions - pulls per-object usage, attributes it to the biggest drivers, root-causes each, and emits recommendations ranked by real signal (idle %, over-allowance, failure rate) with dollar figures only where the billing API returns real ones (and, armed, applies safe cost levers).
License
MIT
Metadata
var: · mode: write · title: Spend Watch · category: dev
Instructions
325 lines in SKILL.md · view on GitHub

Before installing

  • Instructions call the network
  • Instructions read files in your home folder (~/.ssh, ~/.aws…)

Files (1)

  • SKILL.md

SKILL.md (first lines)

Today is ${today}.

> **${var}** — scope selector + optional arm flag.
> - **empty** / `all` → sweep every platform whose secret is present, emit one combined digest.
> - `neon` | `vercel` | `railway` | `actions` → run one adapter only.
> - prepend `arm:` (e.g. `arm:neon`, `arm:actions`) → authorize the adapter's **safe write levers** for this run. Without `arm:` the skill is read-only: it recommends, never mutates.
> - `dry-run` appended anywhere → build the digest but do not `./notify` (for testing).
>
> Runs unattended — treat `${var}` as final, no confirmation step, except a delete/mutation always re-reads the target's current state before acting (see each adapter's arm rules).

This skill is a cost **analyst**, not a bill alarm. Each run answers, per platform:

1. **Attribution** — what is consuming the most? Rank drivers by dollars (or the resource unit that maps to dollars), down to the specific object: service, branch, route, RPC method, workflow. Top-N with each line's % share.
2. **Root cause** — *why* is that line expensive?
3. **Recommendation** — a ranked action list, each carrying: the concrete lever, an effort/risk tag, whether it's armable now, and a **saving in real dollars ONLY when the platform's billing API returns real dollars** (Railway `currentUsage`, Actions overage). Everywhere else there is **no dollar figure** — the line carries its real signal instead (idle-awake %, % over the included allowance, cache-miss rate, stale-preview count, failure rate). Never invent a `$X/mo`.

**Dollars-only-when-real is the core rule.** A fabricated "$5/mo" is worse than the true signal "idle-awake 71%". Rank each recommendation by: real-$ saving first (when known), then signal magnitude × how actionable it is (armable > 1-click > code-change > investigate). The recommendation is the deliverable; the signal justifies it; the dollar is a bonus only when the API hands it over.

Across platforms (`all`): a roll-up — the **real** spend where billing exposes it (Railway $, Actions $), the biggest signal-ranked driver anywhere, and the top actions fleet-wide. No synthetic grand total.

The monitoring (deltas, real budgets, signal thresholds) is the *trend context and the trigger*; the deliverable is the ranked recommendations.

---

## Shared setup (every run)

1. Read `memory/MEMORY.md` for context and `memory/spend-config.md` for real-$ budgets, signal thresholds, and ignore-lists (see the config schema at the bottom). If `spend-config.md` is missing, run with the built-in defaults and note `NO_CONFIG` in the log — recommendations still work; they rank by signal regardless.
2. Read the last 7 days of `memory/logs/` — used to detect *newly* expensive drivers vs ongoing, and to avoid repeat-nagging a recommendation already sent.
3. Parse `${var}`:

```bash
RAW="$(printf '%s' "${var}" | tr '[:upper:]' '[:lower:]' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
ARM=0;   case "$RAW" in arm:*) ARM=1; RAW="${RAW#arm:}";; esac
DRYRUN=0; case "$RAW" in *dry-run*) DRYRUN=1; RAW="$(printf '%s' "$RAW" | sed 's/dry-run//g' | tr -s ' ')";; esac
RAW="$(printf '%s' "$RAW" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
case "$RAW" in
  ""|all) SCOPE=all ;;
  neon|vercel|railway|actions) SCOPE="$RAW" ;;
  *) SCOPE=all ;;   # unrecognized -> full sweep, note it in the log
esac
```

4. Run the matching adapter(s). Each adapter **self-skips** if its secret is absent — log `<platform>: SKIP no-secret` and continue. In `all` mode, run every adapter whose secret is present, then run the **Synthesis** section.

### Common adapter contract

Every adapter produces the same intermediate shape (the model builds it in memory, one entry per cost driver):

```
{ platform, object, metric, amount, share_pct, signal, real_usd, saving_usd,
  trend, root_cause, recommendation, effort, armable }
```

- `effort` ∈ `armable` | `1-click` | `code-change` | `investigate`.
- `real_usd` / `saving_usd` — populated **only** from a billing API that returns actual dollars (Railway `customer.currentUsage`, Actions summed `netAmount`/overage). Otherwise **`null`** — do not compute a dollar figure from a config rate. There is no cost-rate multiplication anywhere in this skill.
- `signal` — the real, dollar-free magnitude that justifies the line and drives its rank when `saving_usd` is null: e.g. `idle-awake 71%`, `18% over included minutes`, `stale-previews 300`, `failure-rate 51%`, `RSS 4.6× working set`. Always present.
- `metric`/`amount` — the raw usage unit (compute-hours, minutes, GB-hrs, requests) behind the signal.
- `trend` ∈ `new` | `up` | `flat` | `down`, computed against the prior snapshot.

### State ledger

Each adapter reads and rewrites `memory/state/spend-<platform>.json`:

```json
{
  "updated_at": "<ISO8601>",
  "drivers": [ { "object": "...", "metric": "...", "amount": 0, "signal": "idle-awake 71%", "real_usd": null } ],
  "recommendations_sent": [ { "id": "neon:tighten-timeout:ep-x", "sent": "<ISO8601>", "signal": "idle-awake 71%" } ]
}
```

`recommendations_sent` is how the skill avoids repeat-nagging: if a rec's `id` was sent within the config's `nag_cooldown_days` (default 14) and the driver hasn't grown, downgrade it to a one-line "(still open)" mention rather than re-ranking it at the top.

### Notify

The `./notify` body is the **ranked recommendation list**, not a raw usage dump. Severity gate:
- `critical` — a **real-$** breach: Railway `currentUsage` over `budgets_usd_month.railway` or its `usageLimit`, or Actions over the global included allowance (real overage $). Real dollars only — a signal alone never escalates to critical.
- `warn` — an actionable recommendation exists, or a driver trending `up`/`new` past the config's `alert_share_pct` (signal-based, no dollars needed).
- `info` — nothing worth acting on. **Send nothing** (silence is the signal, like `price-alert`). Just log.

`dry-run` suppresses the send entirely.

---

## Adapter: actions  (GitHub Actions — build-first, highest feasibility)

Aeon runs *on* GitHub Actions, so this adapter optimizes the agent's own runner bill. Auth is `gh` (uses `GH_GLOBAL` ambiently as `GH_TOKEN`) — **not** `secretcurl` (`GH_GLOBAL` ends in `_GLOBAL`, which secretcurl does not substitute).

### 1. Pull usage — account-global first, per-repo only for attribution

**Billing is ONE account-wide pool.** Included minutes are a single global allowance and overage is billed on the account total — NOT per repo. So the budget check is global; per-repo is only for attributing *where* the global minutes go. Never gate savings on a per-repo dollar figure.

```bash
ME="$(gh api user --jq .login)"
# Global account usage — the budget number. Probe both; branch on HTTP status.
gh api "/users/$ME/settings/billing/usage" 2>/dev/null     # enhanced: per-repo-per-day rows {quantity(min),grossAmount,discountAmount,netAmount,repositoryName}. SUM across ALL rows = global minutes + global net $ this cycle.
gh api "/users/$ME/settings/billing/actions" 2>/dev/null    # classic (410 on migrated accounts): total_minutes_used + included_minutes (the GLOBAL allowance) + minutes_used_breakdown{UBUNTU,MACOS,WINDOWS}
gh api "/users/$ME/settings/billing/shared-storage" 2>/dev/null  # global artifacts+packages storage GB
```

Note on the enhanced endpoint: it returns per-repo rows, but that's a reporting breakdown of one global pool — **sum them for the account figure**, group by `repositoryName` only to attribute. `netAmount` is real billed $ after the included-minutes discount; sum of `netAmount` = your true global Actions bill. Public repos are free/unlimited and don't draw the pool; only private-repo minutes consume the global allowance.

```bash
# Attribution: which repos/workflows drive the global minutes. Scope = every repo with usage
# in the billing rows this cycle (NOT a fixed private-list). Optional actions.repos in config
# just narrows/orders which repos to fetch per-workflow detail for.
for repo in <repos appearing in the billing rows>; do
  gh api "/repos/$repo/actions/cache/usage" 2>/dev/null
  gh api "/repos/$repo/actions/artifacts?per_page=100" --jq '[.artifacts[]|{name,size:.size_in_bytes,created:.created_at,expires:.expires_at,id}]' 2>/dev/null
  gh api "/repos/$repo/actions/runs?per_page=100" --jq '[.workflow_runs[]|{name,workflow_id,run_started_at,updated_at,conclusion}]' 2>/dev/null
done
```

### 2. Budget check (global) + attribute (per-repo/workflow)

**Budget check — global only.** Compare account `total_minutes_used` vs `included_minutes` (classic) or summed `netAmount` vs `budgets_usd_month.actions` (enhanced). Inside the included allowance → net $0 → nothing to save (say so; it's `info`). Over the allowance → the overage $ is the *only* real Actions saving, and it's global. This is the single number that decides severity.

**Attribution — per-repo → per-workflow.** Only once there's a global overage (or to pre-empt one) rank which repos/workflows drive the pool by **minutes** (the signal), so a trim targets the biggest draw. Dollars come only from real `netAmount`/overage; if billing $ is absent, rank by minutes and % of the included allowance — never multiply by a rate.

From aeonfun/aeon, MIT.

More from aeonfun/aeon

Compare and alternatives

    Featured in

    Signals

    Stars
    782
    Forks
    292
    Open issues
    0
    Last commit (this folder)
    Aug 7, 2026 (2 months ago)
    Last release
    Jul 9, 2026 (3 months ago)

    From GitHub (fetched Oct 24, 2026).

    Details

    License
    MIT
    First seen
    Oct 24, 2026
    Last checked
    Oct 24, 2026

    More from aeonfun

    Cite this page

    spend-watch skill. Agent Mart. https://agentmart.dev/skills/aeonfun-spend-watch (last updated 2026-10-24)