CommunityRedacción y edicióngithub.com

RowanJu/subs

Analyze SaaS subscription fees — a portable skill for any coding agent (Plaid + Gmail)

Compatible conClaude Code~Codex CLI~Cursor
npx skills add RowanJu/subs

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentación

subs — SaaS subscription spend analyzer

Analyze the user's recurring subscription spending from bank/card CSV exports and email receipts. Produce a spend summary, flag waste, track renewals, and recommend savings.

The ledger is the spine

subscriptions.yaml in the workspace is the single source of truth. Every run reconciles fresh data into it — it never re-derives everything from scratch. This is what makes renewal tracking and run-to-run consistency real, and it makes "waste" computable (a sub charged for months then suddenly absent = lapsed or worth cancelling). Never overwrite the ledger wholesale; reconcile into it.

Ledger entry schema (one entry per subscription):

- vendor: GitHub               # canonical name
  amount: 4.00                 # most recent charge
  currency: USD
  cycle: monthly               # weekly | monthly | quarterly | semiannual | annual
  category: Dev tools
  first_seen: 2025-01-12       # ISO date of earliest known charge
  last_seen: 2026-06-03        # ISO date of most recent charge
  next_renewal: 2026-07-03     # last_seen + one cycle
  status: active               # active | price_hike | lapsed? | cancelled
  notes: "was 4.00 since signup"

Prerequisite: a Gmail connector

This skill reads subscription receipts from Gmail via a Gmail connector/MCP available to your coding agent (e.g. Claude Code's Gmail connector). Before step 1, confirm your agent has Gmail search/read tools (a search_threads / get_thread pair, or equivalent). If no Gmail integration is available, stop and tell the user to enable one, then re-run the skill. Details and queries in email-receipts.md.

Process

Work through these steps in order. Do not report until step 5.

0. Locate the workspace

Use the current working directory as the workspace. Ensure data/statements/, data/receipts/, and reports/ exist (create them if missing). Load subscriptions.yaml if it exists; otherwise treat the ledger as empty. Done when: the workspace dirs exist and the ledger is loaded (possibly empty). If data/statements/ is empty AND no subscriptions.yaml exists yet AND the user isn't using Plaid (next section), tell the user to drop their bank/card CSV exports into data/statements/ and stop — the CSVs are the spine of recurrence detection.

1. Gather inputs

Bank data via the Plaid CLI (preferred — no manual CSV export): ensure the CLI is ready and pull transactions into data/statements/plaid-transactions.csv per plaid.md. Otherwise rely on CSVs the user dropped into data/statements/.

Then list every CSV in data/statements/. Pull subscription receipts from Gmail per email-receipts.md, and also read any supplementary .eml/.txt files in data/receipts/. Done when: every CSV in data/statements/ is enumerated, and the Gmail receipt search has returned (or the run stopped on a missing connector).

2. Detect recurring charges

Run the deterministic detector over every statement CSV (including the Plaid-derived one) — do not eyeball rows. <skill-dir> is this skill's own directory (in Claude Code: ~/.claude/skills/subs/):

python3 <skill-dir>/scripts/detect_recurring.py data/statements/*.csv

For low-confidence candidates and merchant-normalization edge cases, consult recurrence-detection.md. Combine these candidates with the Gmail receipts gathered in step 1 — receipts catch annual subs a short statement misses and identify vague descriptors. Done when: every recurring candidate has a merchant, cadence, amount, and last-seen date.

3. Reconcile into the ledger

Reconcile candidates into subscriptions.yaml:

  • New merchant → add an entry; categorize via categories.md.
  • Existing → refresh last_seen and next_renewal; if the amount rose, set status: price_hike and record the old amount in notes.
  • In the ledger but absent from recent data (no charge within ~1.5 cycles of today) → set status: lapsed?.

Done when: every ledger entry's status is one of active | price_hike | lapsed? | cancelled, and every candidate maps to exactly one ledger entry.

4. Analyze

From the reconciled ledger compute all four:

  • (a) Spend summary — monthly total and annualized total, grouped by category, by vendor, and by billing cycle. Normalize every entry to a monthly figure ($/mo): annual ÷ 12, semiannual ÷ 6, quarterly ÷ 3, monthly × 1, biweekly × 2.17, weekly × 4.33, semimonthly × 2. The reports reuse this $/mo (single source of truth).
  • (b) Waste — duplicates (same service twice), overlapping tools (two products in the same category doing the same job), price hikes (status: price_hike), and lapsed/unused (status: lapsed?).
  • (c) Upcoming renewals — entries whose next_renewal falls within the next 60 days, soonest first.
  • (d) Savings recommendations — annual-vs-monthly switches, downgrades, consolidations, and cancellations. Each must cite a ledger entry and a dollar impact.

Done when: all four are produced and every recommendation cites an entry + a dollar figure.

5. Report

Save the ledger, then write both reports from it:

  • markdown reports/<YYYY-MM-DD>-subscriptions.md per report-template.md;
  • a self-contained HTML dashboard reports/<YYYY-MM-DD>-subscriptions.html per html-report.md, and open it (open <path>).

Then present a concise summary in chat (headline totals + top 3 actions). Done when: both reports contain the spend summary, waste flags, upcoming renewals, and ranked recommendations, and subscriptions.yaml is saved.

Skills relacionados