From cashflow
Triggered by "tag travel", "tag trips", "travel expenses", "organize trips", "trip tagging", "which trip was this"
npx claudepluginhub davepoon/buildwithclaude --plugin cashflowThis skill uses the workspace's default tool permissions.
Tag travel expenses with trip names. Two phases: silently cluster transactions into candidate trips, then present the full grouping to the user for review.
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.
Tag travel expenses with trip names. Two phases: silently cluster transactions into candidate trips, then present the full grouping to the user for review.
Only the cashflow MCP tools are required. Every other data source (email, calendar, browser) is opportunistic — attempt each, use what's available, skip gracefully if unavailable.
Use the todo tools (TaskCreate / TaskUpdate) to track progress. This state survives context compaction and is visible to the user.
During the clustering phase, create one task per candidate trip:
Always store full UUIDs (not truncated prefixes) and confirmation numbers in task descriptions. This is critical — context compaction and session restarts destroy in-context state. With UUIDs in tasks, you can tag directly without re-querying. Confirmation numbers let you re-find the email instantly if you need to revisit a booking.
Also create a task for unresolved transactions that couldn't be matched to any trip.
After the user reviews and approves groupings, work through the tasks — tag each trip's transactions and mark the task completed.
Optionally maintain /tmp/tag-trips.md with a table of confirmed (tagged) trips for quick reference:
| Tag | Destination | Start | End |
|-----|-------------|-------|-----|
| Hawaii Dec 2024 | Maui | 2024-12-20 | 2024-12-28 |
Transaction dates are often booking dates, not travel dates. A hotel booked in December may be for a trip in March. Never assume the transaction date is when travel happened. Use these signals to determine actual travel dates:
Airline names are not destination signals — Alaska Airlines flies everywhere, not just Alaska.
TRIP DE = December, TRIP JA = January, TRIP FE = February. This tells you which month the rental was for, even if booked earlier.NZL, GBR, EGY, GRC, JOR, DEU etc. — strong destination signals.PAYPAL INST XFER prefixed with a date (e.g. 260131) — that's the transfer date, followed by the merchant name.query { "detail": true, "group": "Travel", "period": "last_90d", "sort": "-amount", "limit": 100 }
If $ARGUMENTS contains a time period, use that instead of last_90d. Paginate with cursor until all transactions are fetched.
Scan all transactions and group them into candidate trips. Work biggest first — large flights and hotels are the anchors that define trips.
For each transaction:
If a refund (positive amount) — pair it with the original charge.
If already tagged — add the tag to the scratchpad if not already there.
If untagged — identify the trip:
When you can't tell which trip a transaction belongs to from the description alone, try these in order. Stop as soon as one works.
Email searches (if available) — search in the main agent, read in a subagent:
If a promising email result comes back, use a subagent (Task tool) to read the full message and extract just: travel dates, route/destination, passengers, amount, and confirmation/booking number. Confirmation emails are extremely verbose — never read them in the main context.
Web searches (if available) — for hotels, tours, unfamiliar parties: 4. Web search the cleaned/display name (e.g. "Tock Inc") 5. Web search the raw description — country codes, location info, or abbreviations often reveal the destination 6. Web search any phone numbers or domain names embedded in the raw description
Can't resolve — mark it as unresolved for now. Don't ask the user yet.
Each trip in the scratchpad has three fields: tag, destination, and date range. Match by destination + travel date overlap (not transaction date):
Show the complete proposed grouping — all candidate trips with their transactions. For each trip show: tag name, destination, travel date range, transaction count, total amount, and a few representative charges.
Also list any unresolved transactions with their details.
The user corrects, confirms, renames, or regroups in one pass. This is where all human input happens — batch it into a single review rather than asking about each transaction individually.
Apply tags based on the user-approved grouping:
annotate { "action": "tag", "transaction_ids": ["<id1>", "<id2>", ...], "tag_name": "Maui Dec 2024" }
Tag in bulk per trip — not one transaction at a time.
query { "by": ["tag"], "group": "Travel", "period": "<same period as Step 1>", "type": "expense" }
Present a summary table of trips by tag with totals. Note any large untagged transactions that couldn't be resolved.
Ask: "Want me to look for dining, rideshare, or other expenses during your trip dates?" If yes, query those categories within each trip's date range and present candidates for confirmation. Foreign transactions (country codes like NZL, GBR in raw descriptions) are high-confidence candidates.
-amount sort means anchors (flights, hotels) come first and define trips for smaller charges later.Stick to the facts. Present findings and suggestions without judgement — just clear, plain-language observations and actionable options.