Personal finance tracker for the terminal. Reads CSV/XLSX exports from your bank, categorises transactions with a rule engine, and presents income, expenses and investments across configurable time granularities.
| Bank | Format | Notes |
|---|---|---|
| Revolut | CSV | es-ES and en-GB exports |
| BBVA | XLSX | Standard account statement export |
Other banks are not supported yet. Adding a new one requires implementing the
Parserinterface ininternal/parser/.
- Rule-based categorisation: pattern matching with a TOML config file
- Review queue: triage uncategorised expenses without leaving the terminal
- Category pivot table: drill down from any cell into filtered transactions
- Granularities: daily / weekly / monthly / yearly / all-time with a single keypress
- AI chat: ask natural-language questions about your finances via a local Ollama model
With Go installed (no clone needed):
go install github.com/SpollaL/cashew/cmd/cashew@latest
go install github.com/SpollaL/cashew/cmd/server@latest # optional web UIBuild from source:
git clone https://github.com/SpollaL/cashew
cd cashew
make buildRequires Go 1.24+.
make test # run all tests
make lint # run golangci-lint (requires golangci-lint v2 installed)
make build # compile both binaries with version injected from git tag
make clean # remove compiled binariesTo install golangci-lint: https://golangci-lint.run/welcome/install/
Terminal UI
./cashew data/*.csv data/*.xlsx
./cashew -model gemma3 data/*.csv # choose a specific Ollama model for chat
./cashew -debug data/*.csv # show LLM roundtrip details in the chat viewWeb UI (handy on mobile)
./cashew-server -addr :8080 data/*.csv data/*.xlsx
# then open http://localhost:8080 in any browserPlace your bank exports in a data/ directory (gitignored). The app detects each bank automatically and deduplicates transactions when date ranges overlap across files.
On first run, cashew opens the review queue — a list of every transaction it couldn't automatically categorise. Work through it to teach the app your spending patterns:
- Select a transaction and press
enterto assign a category (marks it as an expense). - Press
iif it is income,Tif it is an internal transfer,Iif it is an investment. - Each action saves a rule to
~/.cashew/rules.tomlso future transactions with the same description are categorised automatically. - Press
nto skip a transaction without saving a rule.
Once the queue is empty, switch to the summary (s) or categories (c) views to explore your finances.
cashew tracks four transaction types. Only expenses and income flow through to the summary — the others are intentionally excluded so your numbers stay accurate.
| Type | Meaning | Counted in summary? |
|---|---|---|
| Expense | Money spent | Yes |
| Income | Money received (salary, freelance, etc.) | Yes |
| Transfer | Internal move between your own accounts — e.g. topping up Revolut from your current account. No money enters or leaves your finances. | No |
| Investment | Money moved into a portfolio or savings product | No |
Mark a transaction as transfer whenever money moves between accounts you own. If you don't, the same euros will appear as both an outgoing expense and an incoming deposit, double-counting them in the summary.
| Key | Action |
|---|---|
s |
Summary view |
c |
Categories pivot |
t |
Transactions view |
r |
Review queue |
a |
AI chat |
q |
Quit |
Summary / Categories
| Key | Granularity |
|---|---|
a |
All time |
y |
Yearly |
M |
Monthly |
w |
Weekly |
d |
Daily |
Transactions
| Key | Action |
|---|---|
↑/↓ |
Navigate |
e |
Edit type and category |
f |
Filter panel |
esc |
Clear filter / go back |
Review queue
| Key | Action |
|---|---|
enter |
Pick category (marks as expense) |
i |
Mark as income |
T |
Mark as transfer |
I |
Mark as investment |
n |
Dismiss without saving a rule |
Chat
| Key | Action |
|---|---|
↑ ↓ pgup pgdn |
Scroll conversation history |
enter |
Send message |
esc |
Leave chat, return to previous view |
Press / from any view to open the chat panel. Type a question and press enter.
Prerequisites
- Install Ollama and start it (
ollama serve). - Pull a model —
hf.co/Qwen/Qwen3-4B-GGUF:Q4_K_Mis the default:ollama pull hf.co/Qwen/Qwen3-4B-GGUF:Q4_K_M
- Pass a different model with the
-modelflag if needed:./cashew -model gemma3 data/*.csv
The app connects to Ollama at http://localhost:11434 by default. Set OLLAMA_HOST to override.
Example questions
- "How much did I spend on groceries last month?"
- "What were my biggest expenses in January?"
- "Categorize all my uncategorized transactions."
- "Show me a summary of the last 3 months."
The model has access to four tools it can call autonomously:
| Tool | What it does |
|---|---|
get_uncategorized_transactions |
Fetch uncategorized transactions in batches of 20; pass offset to page through them |
get_transactions |
Fetch transactions, optionally filtered by category, month, or type |
get_monthly_summary |
Income / expenses / investments per month |
get_categories |
List all known spending categories |
save_category_rule |
Persist a new pattern → category rule to rules.toml |
Note:
save_category_rulewrites directly to~/.cashew/rules.tomlbut the in-memory view is not refreshed until you restart the app or navigate through the review queue. The rules will be applied correctly on the next launch.
Model compatibility: multi-step tool calling works best with models that support function calling well —
Qwen3,gemma3,llama3.1, andqwen2.5are good choices. Smaller models may summarise instead of invoking tools; try a larger variant if that happens.
Rules live at ~/.cashew/rules.toml. On first run cashew creates the file with a default set of category buckets and no rules — ready for you to populate via the review workflow.
To open the file in your $EDITOR:
cashew rulesOverride the path with an environment variable:
CASHEW_RULES=/path/to/rules.toml cashew data/*.csvRules match transaction descriptions by substring (case-insensitive) and can set a type, a category, or both.
[categories]
buckets = ["Groceries", "Dining", "Transport", "Housing"]
[[rules]]
pattern = "Mercadona"
category = "Groceries"
[[rules]]
pattern = "Al Pocket"
type = "transfer"
[[rules]]
pattern = "Nómina"
type = "income"Rules saved from the TUI are prepended so they take precedence over handwritten substring rules. See rules.example.toml for a full example.
cmd/cashew/ terminal UI entry point
cmd/server/ web UI entry point
internal/
domain/ Transaction, TransactionType, Period, Granularity, Rule
parser/ bank-specific parsers (Revolut, BBVA) + deduplication
rules/ rule engine + TOML persistence
ledger/ immutable, chainable filters + aggregation
llm/ Ollama client, tool schemas, and tool-calling loop
tui/ Bubble Tea app + views (summary, categories, transactions, review, chat)
server/ HTTP handlers + HTML templates (summary, categories, transactions, review)
