From cashflow
Triggered by "tidy up", "clean up transactions", "categorize uncategorized", "organize my transactions"
npx claudepluginhub davepoon/buildwithclaude --plugin cashflowThis skill uses the workspace's default tool permissions.
Incrementally clean up uncategorized and miscategorized transactions. Designed to run many times — each run makes a small dent (~10 items per phase).
Enforces C++ Core Guidelines for writing, reviewing, and refactoring modern C++ code (C++17+), promoting RAII, immutability, type safety, and idiomatic practices.
Provides patterns for shared UI in Compose Multiplatform across Android, iOS, Desktop, and Web: state management with ViewModels/StateFlow, navigation, theming, and performance.
Implements Playwright E2E testing patterns: Page Object Model, test organization, configuration, reporters, artifacts, and CI/CD integration for stable suites.
Incrementally clean up uncategorized and miscategorized transactions. Designed to run many times — each run makes a small dent (~10 items per phase).
Only the cashflow MCP tools are required. Every other data source (email, browser) is opportunistic — attempt each, use what's available, skip gracefully if unavailable.
When you can't identify a merchant from the description alone, try these in order. Stop as soon as one works.
General knowledge — start here, it's free:
Web searches (if available) — cheap, no subagent needed: 2. Web search the cleaned/display name (e.g. "Tock Inc") 3. Web search the raw description — processor codes, location info, or abbreviations often reveal more 4. Web search any phone numbers or domain names embedded in the raw description — highly specific identifiers
Email searches (if available) — search in the main agent, read in a subagent: 5. Email search the exact dollar amount including cents (e.g. "$47.23", not "$47") with a date range near the transaction date. Receipts almost always include the exact amount. 6. Email search the cleaned merchant name with a date range near the transaction date
If a promising email result comes back, use a subagent (Agent tool) to read the full message and extract just: merchant name, what was purchased, amount. Order confirmation emails are extremely verbose — never read them in the main context.
Last resort: 7. Ask the user — show the transaction details and ask what it is
This skill is designed to run repeatedly. On subsequent runs:
admin { "entity": "rule", "action": "list" } and compare party names). Focus on parties without rule coverage.Use the period from $ARGUMENTS (e.g. "this month", "last 30 days"), or last_90d as default.
Start with the highest-value uncategorized transactions — they have the most impact on accuracy.
Fetch top 10 uncategorized expenses by amount:
query { "detail": true, "is_uncategorized": true, "type": "expense", "period": "<period>", "limit": 10, "sort": "-amount" }
Research each transaction using the merchant research steps above. For each, determine:
Present findings as a batch. Show a table with: description, amount, date, proposed party name, proposed category, and confidence level. Ask: "Any corrections before I apply these?"
Apply fixes:
admin { "entity": "rule", "action": "preview", "conditions": { "description_pattern": "<pattern>" }, "actions": { "category_name": "<category>", "party_name": "<party>" } }
Show match count, then create if confirmed. After creating rules, apply them retroactively:
annotate { "action": "apply_rules", "rule_ids": ["<new-rule-id-1>", "<new-rule-id-2>"] }
start/end to scope to the tidy period)
annotate { "action": "categorize", "filter": { "search": "<pattern>", "start": "<period-start>", "end": "<period-end>" }, "category_name": "<category>" }
annotate { "action": "set_party", "filter": { "search": "<pattern>", "start": "<period-start>", "end": "<period-end>" }, "party_name": "<party>" }
Review the most populated expense categories for miscategorized or messy transactions.
Fetch top 10 expense categories by amount:
query { "by": ["category"], "type": "expense", "period": "<period>", "top": 10 }
For each category, fetch top parties:
query { "by": ["party"], "type": "expense", "category": "<category_name>", "period": "<period>", "top": 10 }
Scan for issues in each category:
Present findings as a batch. Show what you found: which parties look miscategorized, which names need cleanup, which are unidentifiable. Ask: "Any corrections before I apply these?"
Apply fixes:
set_party to fix the canonical nameUse the built-in cluster detection to consolidate duplicate merchant names.
Fetch top 10 clusters:
query { "clusters": true, "top": 10 }
Clusters group variant spellings/truncations of the same merchant. Focus on clusters where variants are clearly the same merchant — skip clusters where variants are different businesses.
For each actionable cluster:
Present findings as a batch. For each cluster show: proposed canonical name, variant names, count, suggested category. Ask: "Any corrections before I apply these?"
Preview and create rules for confirmed clusters:
admin { "entity": "rule", "action": "preview", "conditions": { "description_pattern": "<pattern>" }, "actions": { "category_name": "<category>", "party_name": "<canonical_name>" } }
Show match count, then create if confirmed. After creating rules, apply them retroactively:
annotate { "action": "apply_rules", "rule_ids": ["<new-rule-id-1>", "<new-rule-id-2>"] }
Summarize what was done: transactions categorized, rules created, party names cleaned up, clusters consolidated.
Report remaining work:
query { "detail": true, "is_uncategorized": true, "period": "<period>", "limit": 1 }
Report the remaining uncategorized count from the response metadata. If there's more to do, suggest running /tidy again.
Stick to the facts. Present findings and suggestions without judgement — no commentary on spending habits. Just clear, plain-language observations and actionable options.