A debt-focused, AI-agent-first personal finance CLI. Translate the user's intent into the right command(s) and report the result.
First run in any session
Always begin a session by capturing the current command surface so you don't guess based on stale knowledge:
finance schema --json
The reply lists every command, positional args, flags (with types), and whether each command mutates state. Use it to:
- Validate command names before invoking them.
- Discover new flags or commands added in updates.
- Confirm
schema_version; if it has bumped past 1.0, re-read this skill's references for shape changes.
If finance schema fails with ENOENT or "command not found", the binary
isn't installed. Drive the install flow — see
references/install.md for the full walkthrough
(detect platform, pick install method, verify, run finance init).
If finance schema succeeds but the data file is missing (ENOENT from a
later command), the binary is installed but finance init hasn't run yet —
walk the user through it (also covered in install.md).
Core operating rules
- Always pass
--json. Parse the envelope rather than scraping prose. - Mutation-safety order: pass
--dry-runfirst when the request is destructive (delete,loan pay) or when the amount/category looks ambiguous; show the preview to the user; commit only after they confirm. - Retry safety: for
add/edit/delete/loan add/loan pay, pass--idempotency-key <KEY>whenever the same logical operation might be reissued (timeouts, multi-step flows, the user repeating a request). Keys dedupe within 24h. Use a reproducible key likeadd-2026-04-29-lunch-80rather than UUIDs the user can't see. - Currency-agnostic: the binary uses whatever currency lives in
~/.finance/data.json(settings.currencySymbol). Don't assume USD. - Today is today: dates are ISO YYYY-MM-DD. When the user says "yesterday" or "last Friday", convert to ISO before passing.
Common intents to commands
| User says… | Run |
|---|---|
| "log $80 lunch" | finance add 80 food "lunch" --json |
| "what's my balance" | finance balance --json (or --raw for plain int) |
| "month overview" / "where am I" | finance status --json |
| "can I afford 5000 by may 31?" | finance afford 5000 --by 2026-05-31 --json |
| "should I push freelance?" | finance runway --json |
| "show my loans" | finance loans --json (envelope embeds amortization) |
| "what if I throw 5000 extra at debt?" | finance simulate --extra 5000 --strategy avalanche --json |
| "did I overspend in april?" | finance diff 2026-04-01 --json |
| "preview rent gets logged" / new month | finance recurring sync --dry-run --json, then commit |
| "fix the 80 to 90" | finance edit <id> --amount 90 --json --idempotency-key edit-<id>-amount-90 |
| "delete that lunch" | finance delete <id> --dry-run --json, confirm, then without dry-run |
| "pay this month's instalment" | finance loan pay <id> --dry-run --json, confirm, then without |
| "log 30k freelance" | finance add 30000 freelance "client X" --income --json |
| "export to ledger" | finance export --format hledger --output journal.txt |
| "what commands exist" | finance schema --json |
For multi-step flows see references/recipes.md. For envelope shape and error codes see references/envelope.md.
Reading results — what actually matters
afford:verdictis the headline (yes/tight/no).tightmeans the final balance covers the ask but the running balance dips negative — surfacemin_running_balanceandmin_running_date.runway:recommendationis the headline (push/coast/behind).push_to_break_evenis the exact extra freelance amount needed to flip the projection positive.simulate: reportmonths_savedandinterest_savedin absolute numbers, not percentages. Compare both strategies if the user is weighing.loans(with amortization): each debt hasamortization.nextPayment({interest, principal}) andamortization.totalInterestRemaining. For "should I prepay X?", surface the highest-rate debt first.- Errors: never invent a fix. The
codefield tells you why:ENOENT(no data file — suggestfinance init),ENOTFOUND(id doesn't exist — re-list and ask),EVALIDATION(bad input — explain what was wrong),EAMBIGUOUS(category prefix matched multiple — show options),EUNKNOWN(system error — surface verbatim).
Things to NOT do
- Don't edit
~/.finance/data.jsondirectly. Use the CLI. - Don't read source files in a finance-cli repo to figure out flags;
call
finance schema --jsoninstead. - Don't pass
--yestofinance deleteuntil the user has confirmed the dry-run preview. - Don't assume HKD or USD. Read
settings.currencySymbolfrom any status or loans response if you need to format numbers in prose.