finance-cli
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-run first 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 like
add-2026-04-29-lunch-80 rather 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: verdict is the headline (yes / tight / no).
tight means the final balance covers the ask but the running balance
dips negative — surface min_running_balance and min_running_date.
runway: recommendation is the headline (push / coast /
behind). push_to_break_even is the exact extra freelance amount
needed to flip the projection positive.
simulate: report months_saved and interest_saved in absolute
numbers, not percentages. Compare both strategies if the user is weighing.
loans (with amortization): each debt has amortization.nextPayment
({interest, principal}) and amortization.totalInterestRemaining. For
"should I prepay X?", surface the highest-rate debt first.
- Errors: never invent a fix. The
code field tells you why:
ENOENT (no data file — suggest finance 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.json directly. Use the CLI.
- Don't read source files in a finance-cli repo to figure out flags;
call
finance schema --json instead.
- Don't pass
--yes to finance delete until the user has confirmed
the dry-run preview.
- Don't assume HKD or USD. Read
settings.currencySymbol from any
status or loans response if you need to format numbers in prose.
1---2name: finance-cli3description: Drive the finance-cli (github.com/kelvin6365/finance-cli) — a local, offline, single-binary terminal finance tracker built for AI-agent use. Trigger when the user wants to log a transaction, check their balance, review obligations, ask can-I-afford-X-by-Y, should-I-push-freelance-this-month, what-is-my-debt-situation, simulate paying extra on debt, what-changed-since-DATE, or any related personal-finance bookkeeping / cashflow / debt-payoff task. Every command supports --json with a stable envelope (ok, schema_version, data or error, code). Prefer this skill over reading source files when the user has the binary installed.4---56# finance-cli78A debt-focused, AI-agent-first personal finance CLI. Translate the user's9intent into the right command(s) and report the result.1011## First run in any session1213Always begin a session by capturing the current command surface so you don't14guess based on stale knowledge:1516```bash17finance schema --json18```1920The reply lists every command, positional args, flags (with types), and21whether each command mutates state. Use it to:22- Validate command names before invoking them.23- Discover new flags or commands added in updates.24- Confirm `schema_version`; if it has bumped past 1.0, re-read this skill's25 references for shape changes.2627If `finance schema` fails with `ENOENT` or "command not found", the binary28isn't installed. **Drive the install flow** — see29[references/install.md](references/install.md) for the full walkthrough30(detect platform, pick install method, verify, run `finance init`).3132If `finance schema` succeeds but the data file is missing (`ENOENT` from a33later command), the binary is installed but `finance init` hasn't run yet —34walk the user through it (also covered in install.md).3536## Core operating rules37381. **Always pass `--json`.** Parse the envelope rather than scraping prose.392. **Mutation-safety order:** pass `--dry-run` first when the request is40 destructive (`delete`, `loan pay`) or when the amount/category looks41 ambiguous; show the preview to the user; commit only after they confirm.423. **Retry safety:** for `add` / `edit` / `delete` / `loan add` / `loan pay`,43 pass `--idempotency-key <KEY>` whenever the same logical operation might44 be reissued (timeouts, multi-step flows, the user repeating a request).45 Keys dedupe within 24h. Use a reproducible key like46 `add-2026-04-29-lunch-80` rather than UUIDs the user can't see.474. **Currency-agnostic:** the binary uses whatever currency lives in48 `~/.finance/data.json` (`settings.currencySymbol`). Don't assume USD.495. **Today is today:** dates are ISO YYYY-MM-DD. When the user says50 "yesterday" or "last Friday", convert to ISO before passing.5152## Common intents to commands5354| User says… | Run |55|---|---|56| "log $80 lunch" | `finance add 80 food "lunch" --json` |57| "what's my balance" | `finance balance --json` (or `--raw` for plain int) |58| "month overview" / "where am I" | `finance status --json` |59| "can I afford 5000 by may 31?" | `finance afford 5000 --by 2026-05-31 --json` |60| "should I push freelance?" | `finance runway --json` |61| "show my loans" | `finance loans --json` (envelope embeds amortization) |62| "what if I throw 5000 extra at debt?" | `finance simulate --extra 5000 --strategy avalanche --json` |63| "did I overspend in april?" | `finance diff 2026-04-01 --json` |64| "preview rent gets logged" / new month | `finance recurring sync --dry-run --json`, then commit |65| "fix the 80 to 90" | `finance edit <id> --amount 90 --json --idempotency-key edit-<id>-amount-90` |66| "delete that lunch" | `finance delete <id> --dry-run --json`, confirm, then without dry-run |67| "pay this month's instalment" | `finance loan pay <id> --dry-run --json`, confirm, then without |68| "log 30k freelance" | `finance add 30000 freelance "client X" --income --json` |69| "export to ledger" | `finance export --format hledger --output journal.txt` |70| "what commands exist" | `finance schema --json` |7172For multi-step flows see [references/recipes.md](references/recipes.md).73For envelope shape and error codes see [references/envelope.md](references/envelope.md).7475## Reading results — what actually matters7677- **`afford`**: `verdict` is the headline (`yes` / `tight` / `no`).78 `tight` means the final balance covers the ask but the running balance79 dips negative — surface `min_running_balance` and `min_running_date`.80- **`runway`**: `recommendation` is the headline (`push` / `coast` /81 `behind`). `push_to_break_even` is the exact extra freelance amount82 needed to flip the projection positive.83- **`simulate`**: report `months_saved` and `interest_saved` in absolute84 numbers, not percentages. Compare both strategies if the user is weighing.85- **`loans` (with amortization)**: each debt has `amortization.nextPayment`86 (`{interest, principal}`) and `amortization.totalInterestRemaining`. For87 "should I prepay X?", surface the highest-rate debt first.88- **Errors**: never invent a fix. The `code` field tells you why:89 `ENOENT` (no data file — suggest `finance init`),90 `ENOTFOUND` (id doesn't exist — re-list and ask),91 `EVALIDATION` (bad input — explain what was wrong),92 `EAMBIGUOUS` (category prefix matched multiple — show options),93 `EUNKNOWN` (system error — surface verbatim).9495## Things to NOT do9697- Don't edit `~/.finance/data.json` directly. Use the CLI.98- Don't read source files in a finance-cli repo to figure out flags;99 call `finance schema --json` instead.100- Don't pass `--yes` to `finance delete` until the user has confirmed101 the dry-run preview.102- Don't assume HKD or USD. Read `settings.currencySymbol` from any103 status or loans response if you need to format numbers in prose.