Claude
Skills
Sign in
Back

analyze-cc-statements

Included with Lifetime
$97 forever

Analyze credit card statement CSV files, categorize transactions, and produce a markdown spending report with month-over-month comparison. Activate when the user says "analyze my credit card statement", "analyze cc statement", "spending analysis for YYYY-MM", "categorize my transactions", "credit card report", "analyze-cc-statements", or provides a month argument like "2026-03" in the context of credit card or transaction analysis.

Data & Analyticsscriptsassets

What this skill does


This skill reads a month's credit card transactions from a CSV file, categorizes each transaction, and writes a structured markdown analysis report with month-over-month comparison and savings recommendations.

## Input requirements

- **Argument:** `YYYY-MM` (e.g., `2026-03`). This is required — if missing or malformed, inform the user of the expected format and stop.
- **Working directory:** The user must invoke this skill from a directory containing `trxns/` and `analysis/` subdirectories.
- **CSV path:** `./trxns/{YYYY-MM}.csv`
- **Output path:** `./analysis/{YYYY-MM}.md`

### CSV schema

```
card_last4, transaction_date, posting_date, description, amount, type, installment_info
```

- `amount` — numeric, may contain commas or currency symbols (strip before parsing)
- `type` — `debit` (purchase) or `credit` (payment to card)
- `installment_info` — mostly empty; if present, contains installment status (e.g., "5/12", "Final Settlement")

**Filter rule:** Only analyze rows where `type = debit`. Ignore `credit` rows — those are payments back to the card.

## Workflow

Follow these steps in order:

### 1. Validate the argument

Extract the YYYY-MM from the user's input. Verify it matches the `YYYY-MM` pattern (4-digit year, dash, 2-digit month, month between 01-12). If invalid, tell the user the expected format and stop.

### 2. Verify CSV file exists

Use the Glob tool to check if `./trxns/{YYYY-MM}.csv` exists. If the file does not exist, inform the user and stop.

### 3. Parse CSV using the helper script

**Tell the user:** "Parsing CSV file to extract transaction data..."

Execute the helper script to accurately parse the CSV and get structured transaction data:

```bash
python3 <script_path>/scripts/parse_transactions.py ./trxns/{YYYY-MM}.csv
```

Where `<script_path>` is the path to this skill directory (derive it from the path to this SKILL.md file — the scripts directory is at `scripts/parse_transactions.py` relative to it).

The script returns JSON with:
- `debit_count` — count of all rows where `type = debit`
- `credit_count` — count of all rows where `type = credit`
- `total_debit` — sum of all debit amounts (before any adjustments)
- `total_credit` — sum of all credit amounts
- `transactions` — array of all debit transactions with fields: row, card, date, description, amount, installment
- `credits` — array of all credit transactions with fields: row, card, date, description, amount
- `cards` — sorted list of unique card_last4 values
- `date_range` — min/max transaction dates in ISO format
- `skipped_rows` — any rows that couldn't be parsed (malformed)

**Handle script errors:**
- If the script returns an error (file not found, empty file, wrong column count), inform the user and stop
- If `debit_count` is 0, inform the user that no spending transactions were found and stop — do not write an output file

### 4. Analyze for refunds, waivers, and reversals

**IMPORTANT**: Do not use `total_debit` from the script directly as the final spend. First, identify credits that are refunds/waivers/reversals (not card payments).

Using AI judgment, analyze both `transactions` (debits) and `credits` arrays to identify:

1. **Card payments** — credits with descriptions like "PAYMENT", "PEMBAYARAN TAGIHAN", "Pembayaran Kartu Kredit" — these are payments TO the card, NOT spending adjustments. Exclude from analysis.

2. **Refunds/waivers/reversals** — credits that reverse a specific debit:
   - Look for description keywords: "REFUND", "WAIVER", "FEE REVERSAL", "PEMBEBASAN", "REVERSAL", "CREDIT" (when not a payment)
   - Match to debits by: similar description keywords, same card, similar amount, credit date after debit date
   - Examples:
     - Debit "IURAN TAHUNAN 500000" + Credit "PEMBEBASAN IURAN TAHUNAN 500000" = waiver pair
     - Debit "APPLE.COM/BILL" + Credit "Credit IRL CORK APPLE.COM/BILL" = refund pair

3. **Build matched pairs list**:
   - Create `matched_waiver_pairs` array with objects: `{debit_row, debit_amount, debit_description, credit_row, credit_amount, credit_description}`
   - Calculate `waived_debits_total` = sum of all `debit_amount` in matched pairs
   - Calculate `waived_credits_total` = sum of all `credit_amount` in matched pairs
   - **GUARDRAIL CHECK**: Verify that `waived_debits_total` equals `waived_credits_total` (or document any discrepancy). These should match because each waiver credit reverses its corresponding debit.

4. **Net spending calculation** (CRITICAL - avoid double-counting):
   - `total_debit_raw` = `total_debit` from parser (includes ALL debits, including waived ones)
   - `gross_spend` = `total_debit_raw` - `waived_debits_total`
   - **GUARDRAIL CHECK**: Verify `gross_spend` equals the sum of all non-waived debits. If using categorized transactions sum, it should match.
   - Transaction count for report = `debit_count` - (number of matched waiver debits)
   - **IMPORTANT**: The waiver credit is NOT subtracted again — it simply cancels out the waived debit. Do NOT do: `gross_spend - waived_credits_total` — that would double-count the waiver.

5. **Unmatched credits** — if a credit doesn't match any debit and isn't a card payment, treat as miscellaneous credit:
   - Add to `unmatched_credits` array for reporting
   - Calculate `unmatched_credits_total` = sum of all unmatched credit amounts
   - These WILL reduce the final spend (see step 6)
   - **GUARDRAIL CHECK**: Ensure no credit is counted as both a waiver match AND an unmatched credit. Each credit should be classified exactly once: (1) card payment, (2) waiver/refund match, or (3) unmatched miscellaneous.

6. **Filter transactions for categorization**: Create a filtered list of transactions excluding those that were matched with refunds/waivers. These are the transactions to categorize.

### 5. Categorize transactions using parallel subagents

**Tell the user first:** "Starting parallel categorization of {N} transactions across {M} subagents..."

**Determine chunk size and number of subagents:**
- If ≤50 transactions: use 1 subagent (no parallelization needed)
- If 51-150 transactions: split into 2 chunks, launch 2 subagents in parallel
- If 151-300 transactions: split into 3-4 chunks, launch 3-4 subagents in parallel
- If >300 transactions: split into 5-6 chunks, launch 5-6 subagents in parallel
- Maximum chunk size: 60 transactions per subagent

**Chunking strategy:**
- Divide transactions array into roughly equal chunks
- Each chunk should have contiguous transactions (no randomization needed)
- Track chunk index for each subagent (0, 1, 2, ...)

**Launch all subagents in parallel** using the Agent tool. For each chunk, launch a subagent with this prompt:

```
Categorize these credit card transactions according to the guidelines.

TRANSACTIONS TO CATEGORIZE (Chunk {chunk_num}/{total_chunks}):
{JSON array of transactions for this chunk}

CATEGORIZATION GUIDELINES:
Read and follow: references/categorization-guidelines.md

Categories available:
- Food & Drinks — sub-categorize as Essential (groceries, warung, restaurants, supermarkets) or Social (cafes, bars, food delivery)
- Transport — ride-hailing, fuel, parking, tolls, public transit
- Shopping — e-commerce, retail, fashion, electronics
- Subscriptions — streaming, software, gaming, recurring memberships, CLOUD SERVICES (e.g. Biznet NEO)
- Health — pharmacies, clinics, hospitals, fitness
- Bills & Utilities — electricity, water, internet, insurance, phone
- Other — anything that doesn't fit above

CRITICAL RULES (common errors to avoid):
1. CLOUD SERVICES → Subscriptions, NOT Transport:
   - "ALIBABA CLOUD", "ANYCLOUD", "AWS", "GOOGLE CLOUD", "AZURE" → Subscriptions
   - "PROTON" (ProtonVPN/Mail), "NANONOBLE" → Subscriptions
   - Any transaction with "CLOUD", "HOSTING", "SERVER" → Subscriptions
2. INSTALLMENTS → categorize by MERCHANT, NOT as "Other":
   - "CICILAN BCA SMARTPHONE", "CICILAN MATAHARI" → Shopping
   - Look at the merchant name to determine category
3. GRAB* entries:
   - "GRAB*FOOD" 

Related in Data & Analytics