From p-assist
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.
How this skill is triggered — by the user, by Claude, or both
Slash command
/p-assist:analyze-cc-statementsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
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.
YYYY-MM (e.g., 2026-03). This is required — if missing or malformed, inform the user of the expected format and stop.trxns/ and analysis/ subdirectories../trxns/{YYYY-MM}.csv./analysis/{YYYY-MM}.mdcard_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.
Follow these steps in order:
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.
Use the Glob tool to check if ./trxns/{YYYY-MM}.csv exists. If the file does not exist, inform the user and stop.
Tell the user: "Parsing CSV file to extract transaction data..."
Execute the helper script to accurately parse the CSV and get structured transaction data:
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 = debitcredit_count — count of all rows where type = credittotal_debit — sum of all debit amounts (before any adjustments)total_credit — sum of all credit amountstransactions — array of all debit transactions with fields: row, card, date, description, amount, installmentcredits — array of all credit transactions with fields: row, card, date, description, amountcards — sorted list of unique card_last4 valuesdate_range — min/max transaction dates in ISO formatskipped_rows — any rows that couldn't be parsed (malformed)Handle script errors:
debit_count is 0, inform the user that no spending transactions were found and stop — do not write an output fileIMPORTANT: 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:
Card payments — credits with descriptions like "PAYMENT", "PEMBAYARAN TAGIHAN", "Pembayaran Kartu Kredit" — these are payments TO the card, NOT spending adjustments. Exclude from analysis.
Refunds/waivers/reversals — credits that reverse a specific debit:
Build matched pairs list:
matched_waiver_pairs array with objects: {debit_row, debit_amount, debit_description, credit_row, credit_amount, credit_description}waived_debits_total = sum of all debit_amount in matched pairswaived_credits_total = sum of all credit_amount in matched pairswaived_debits_total equals waived_credits_total (or document any discrepancy). These should match because each waiver credit reverses its corresponding debit.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_totalgross_spend equals the sum of all non-waived debits. If using categorized transactions sum, it should match.debit_count - (number of matched waiver debits)gross_spend - waived_credits_total — that would double-count the waiver.Unmatched credits — if a credit doesn't match any debit and isn't a card payment, treat as miscellaneous credit:
unmatched_credits array for reportingunmatched_credits_total = sum of all unmatched credit amountsFilter transactions for categorization: Create a filtered list of transactions excluding those that were matched with refunds/waivers. These are the transactions to categorize.
Tell the user first: "Starting parallel categorization of {N} transactions across {M} subagents..."
Determine chunk size and number of subagents:
Chunking strategy:
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" → Food & Drinks (Social)
- "GRAB*TRANSPORT", "GRAB*CAR" → Transport
4. APPLE.COM/BILL, GOOGLE* → Subscriptions (if software/service), Shopping (if hardware)
For EACH transaction, determine:
1. Primary category
2. Sub-category (only for Food & Drinks: "Essential" or "Social", otherwise null)
3. Confidence (only for non-obvious cases): "low" if uncertain, omit otherwise
OUTPUT FORMAT (strict JSON):
{
"categorized_transactions": [
{
"row": <original row number>,
"card": "<card last 4>",
"date": "<transaction date>",
"description": "<merchant description>",
"amount": <amount>,
"category": "<Primary Category>",
"sub_category": "<Essential|Social|null>",
"confidence": "<low>" // only include if uncertain
}
],
"chunk_num": <chunk number>,
"total_chunks": <total chunks>
}
Rules:
- Process ALL transactions. Do not skip any.
- Return ONLY valid JSON.
- Do NOT include reasoning for individual transactions — categorization should be self-evident from the guidelines.
- Only include "confidence": "low" when genuinely uncertain (e.g., garbled description, ambiguous merchant).
Wait for all subagents to complete before proceeding.
Aggregate results:
categorized_transactions arrays from each subagentCompute total spend per category (and sub-category for Food & Drinks). Flag any category where the total exceeds 40% of the overall spend as potentially disproportionate. This flagging is at the aggregate level — do not flag individual transactions.
Calculate final net spend (CRITICAL - follow this exact formula to avoid double-counting):
# From parser
total_debit_raw = total_debit # Includes ALL debits
# From waiver matching (step 4)
waived_debits_total = sum of debit amounts from matched waiver pairs
# From unmatched credits (step 4)
unmatched_credits_total = sum of unmatched miscellaneous credit amounts
# Final calculation
gross_spend = total_debit_raw - waived_debits_total
net_spend = gross_spend - unmatched_credits_total
GUARDRAIL CHECKS (perform these before finalizing):
gross_spend should equal the sum of all categorized transaction amounts. If using the filtered transactions from step 4, verify: sum(categorized_transactions.amount) == gross_spendgross_spend + waived_debits_total == total_debit_rawwaived_credits_total from gross_spend. The waiver credit already cancelled out the debit — subtracting it again would be double-counting.waived_credits_total to unmatched_credits_total. Waiver credits are matched pairs, not miscellaneous credits.Report breakdown in the report header:
Tell the user: "Checking for previous month's analysis for comparison..."
Compute the previous month's YYYY-MM:
Use the Glob tool to check if ./analysis/{previous-YYYY-MM}.md exists. If it exists, read it with the Read tool. Extract the category totals from the "Spend by Category" section (it's a pipe-delimited markdown table) for month-over-month comparison.
If the previous month's file does not exist, or its format cannot be parsed, skip the MoM comparison and note it in the output.
Before writing, VALIDATE CALCULATIONS using the helper script:
python3 <script_path>/scripts/validate_calculations.py \
<total_debit_raw> <waived_debits_total> <unmatched_credits_total> <gross_spend> <net_spend>
Where:
<total_debit_raw> = total_debit from parser<waived_debits_total> = sum of matched waiver debit amounts<unmatched_credits_total> = sum of unmatched miscellaneous credits<gross_spend> = calculated gross spend<net_spend> = calculated net spendIf validation fails, do NOT write the report. Instead, debug the calculation discrepancy first.
Use the Write tool to create ./analysis/{YYYY-MM}.md. Follow the template structure defined in assets/templates/template.md.
Fill in all sections:
After writing the file, present a concise summary to the user:
When the previous month's analysis file exists and is parseable:
Write 3-5 opinionated, specific savings recommendations based on:
Be direct and specific. "You spent Rp 890,000 at Tokopedia this month — that's 35% of your Shopping category" is useful. "Consider reducing discretionary spending" is not.
references/categorization-guidelines.mdassets/templates/template.mdAlways inform the user before starting long-running operations (especially subagent launches).
npx claudepluginhub irfansofyana/ai-marketplace --plugin p-assistGenerates monthly financial digests with income/expenses, savings rate, category breakdowns, top expenses, anomalies, trends, and MoM changes using spending_summary, transaction_search, and anomaly_detect.
Generates detailed expense reports with category breakdowns, top vendors, trends, insights, and period comparisons from Norman Finance transactions.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.