Skip to main content

beans

Professional-grade double-entry accounting for personal finance, from the command line.

beans lets you keep your household books the way a company keeps theirs: every transaction is a balanced set of debits and credits, and your finances roll up into the same three statements analysts use to evaluate public companies — an income statement, a balance sheet, and a statement of cash flows. On top of the ledger it layers budgeting, forecasting, and ratio analysis tailored to personal finance.

  • True double-entry — transactions must balance to the cent; the trial balance always ties.
  • Corporate-style statements — income statement (with common-size % and prior-period comparison), balance sheet (with computed retained earnings), and a direct-method statement of cash flows classified into operating, investing, and financing activities.
  • Trends over time — report trend puts income, expenses and every account side by side across N months or quarters, ranked by what moved most, so drift is visible before it is obvious. It stops at the last complete period, because a part-elapsed one reads as a collapse.
  • Budgeting — per-account budgets at weekly/monthly/quarterly/yearly cadence, with budget-vs-actual variance reports over any period.
  • Forecasting — project income, expenses, cash, and net worth forward from historical averages, a linear trend, or your budgets — and, with --report, a projected balance sheet, income statement or statement of cash flows in the same format beans report prints, balancing to the cent and tying to one another.
  • Analysis — savings rate, liquidity runway, debt-to-assets, debt-to-income, and expense composition.
  • Reconciliation — clear postings against bank statements and reconcile to the cent, the way errors actually get caught. Hand reconcile the statement CSV itself and it matches line by line, sorting what doesn't tie into named discrepancy classes — read-only, so it never touches the register.
  • Investments — FIFO lots, price history, realized gains on sale, and mark-to-market adjustments so the balance sheet carries market value.
  • Multi-currency — foreign-denominated accounts with parallel foreign balances, exchange-rate history, and FX revaluation, while the books and statements stay in your base (functional) currency.
  • Export & backup — the whole ledger as JSON or flat CSV, and consistent point-in-time SQLite snapshots.
  • Archival — beans archive rolls the books forward into a new, much smaller file when the register gets long, replacing each archived month with one summary entry. Every period report — balance sheet, income statement, cash flows, trend, net worth, the forecast's income and expense projections, every ratio — returns exactly the same figures; a 25-year ledger loses 93% of its size and none of its answers. The original is never touched.
  • Goals — savings targets and debt payoff dates with required-monthly math, plus period close to lock historical books.
  • Ease of use — a beans status dashboard, spend / earn / transfer shortcuts with instant budget feedback, fuzzy account matching (groceries → Expenses:Food:Groceries), full-text search, undo, deduplicating CSV import that categorizes itself — beans categorize suggests a counter-account for every row of a bank export from your own ledger's history, with a confidence score and the evidence behind it, and writes an editable file for you to review before anything is imported — shell completions, and --json output on every report for scripting.
  • AI assistant (optional) — an opt-in beans ai command group: ask questions in plain English (beans ai ask) and get a CFO-style narrative review of your finances (beans ai review). Off by default, read-only, with a --dry-run transparency switch and support for local models so nothing has to leave your machine. See AI assistant.
  • Use it from Claude (optional MCP) — an opt-in beans mcp server exposes the ledger as read-only tools and a review prompt to Claude Desktop and Claude Code, so Claude can read your finances directly. Local-first, read-only by default. See Use beans from Claude.
  • Agent skills for Claude Code (optional) — three packaged workflows: beans-import gets a bank CSV into the ledger, categorized and reconciled (it writes, after you approve a dry run); beans-report reads trends across periods and writes a ranked briefing; beans-economic builds an economic balance sheet from an interview and stress-tests it. The last two are strictly read-only. See Agent skills for Claude Code.
  • No dependencies — the core is pure Python standard library and fully offline; data lives in a single SQLite file you own. The optional extras add the only opt-in surfaces: [ai] reaches a model provider (the sole networked feature), and [mcp] runs a local server for Claude — and both are built on just the standard library, so neither adds a third-party dependency.

Installation

From PyPI:

pip install beans-ledger

The package is published on PyPI as beans-ledger (the name beans was already taken), but it still installs the beans command and is imported as beans. Or install from a checkout:

pip install .

Requires Python 3.10+. This installs the beans command.

Quick start

# Create a ledger (default: ~/.beans/ledger.db; override with -f or $BEANS_LEDGER)
beans init

# Record opening balances against equity, like a company's opening entry
beans tx add --date 2026-01-01 --desc "Opening balances" \
    --post Assets:Checking 5000 \
    --post Assets:Savings 10000 \
    --post "Liabilities:Credit Card" -1200 \
    --post "Equity:Opening Balances"        # omitted amount auto-balances

# Day-to-day entries — fuzzy account names, defaults to your checking account
beans earn 6000 Salary --date 2026-01-15 --desc "January paycheck"
beans spend 1800 Rent --date 2026-02-01
beans spend 450.25 Groceries --payee "Market"
beans transfer 1000 Checking Savings

# The three statements
beans report income --period ytd
beans report balance
beans report cashflow --period ytd

# Or just run `beans` for the dashboard: cash, net worth, month vs budget,
# due recurring rules, and goal progress on one screen.
beans

Walkthroughs

New here? The docs/vignettes/ directory has guided, task-oriented walkthroughs that take a workflow from start to finish with real captured output:

  1. Getting started — set up a ledger and read your first statements.
  2. Budgeting a month — set targets and track spending against them.
  3. Import & reconcile — import a bank CSV and tie out to your statement.
  4. Recurring, goals & investing — automate bills, set goals, and track investments.
  5. Loans & liquidity — classify current vs non-current, finance a loan, and read a classified balance sheet with liquidity ratios.
  6. The economic balance sheet — value human capital and future consumption to see lifetime net worth alongside the accounting balance sheet.
  7. The AI assistant — install the optional extra, configure a provider (hosted or local), and ask questions and run reviews over your ledger with full transparency.
  8. Using beans from Claude (MCP) — connect the optional MCP server to Claude Desktop and Claude Code so Claude can read your ledger directly, read-only and local-first.

The rest of this README is the command reference. For the full instruction manual — every command, every flag, with parameter tables and best practices for each — see docs/MANUAL.md.

Concepts

Accounts

Accounts are hierarchical (Expenses:Food:Groceries) and typed: asset, liability, equity, income, or expense. beans init creates a starter chart of ~23 accounts; reshape it freely:

beans account add Expenses:Pets --type expense
beans account add Assets:HSA --type asset --cash
beans account modify Liabilities:Loans --rename Liabilities:Mortgage
beans account list                # with balances; --type expense to filter
beans account close Assets:HSA    # requires a zero balance

Two flags drive the statement of cash flows:

  • --cash marks an asset as cash or a cash equivalent (checking, savings, wallet). The cash flow statement explains the change in these accounts.
  • --cashflow operating|investing|financing overrides the activity an account's flows are classified under. Defaults follow corporate convention: income/expense → operating, non-cash assets → investing, liabilities/equity → financing.

Assets and liabilities also carry a liquidity classification — current (realizable or due within a year) or noncurrent (beyond a year) — that drives the classified balance sheet and the working-capital ratios. Everything defaults to current; mark the long-term ones:

beans account add Assets:Prepaid:Insurance --type asset          # current
beans account modify Retirement --noncurrent                     # long-term
beans account add "Liabilities:Mortgage" --type liability --noncurrent
beans account modify "Credit Card" --current

For an amortizing debt (mortgage, auto, student loan), don't classify by hand — attach a loan and let the amortization schedule split it (see Loans below).

Transactions

Every transaction is a list of postings that sum to zero (debits positive, credits negative). The general form handles any complexity — a paycheck with a 401(k) deduction, for example:

beans tx add --desc "Paycheck" --date 2026-02-15 \
    --post Assets:Checking 4000 \
    --post Assets:Investments:Retirement 1000 \
    --post Income:Salary              # balances to -5000

For the common cases there are shortcuts, each a balanced two-leg entry:

beans spend 54.20 Dining --from "Credit Card" -m "Pizza night"
beans earn 120 Interest --to Savings
beans transfer 500 Checking Savings

spend/earn default the cash side to your checking account; change it with beans config set default_account Savings. Inspect and correct history with:

beans tx list --period this-month
beans tx show 42
beans tx void 42        # voids keep the audit trail; nothing is deleted
beans undo              # void the most recent transaction (typo insurance)
beans search "whole foods"             # full-text over descriptions/payees
beans tx add --like 42 --date today    # clone a prior transaction
beans register Checking --period ytd   # running-balance view of one account
beans balances          # everything, grouped by type
beans report trial      # the accountant's sanity check

After beans spend against a budgeted category, you get instant feedback ("Groceries: 92% of June budget used"), and any command reminds you (on stderr) when recurring rules are due.

Recurring transactions

Bills, paychecks, subscriptions — define them once and post them on demand:

beans recur add rent --freq monthly --start 2026-07-01 \
    --post Expenses:Housing:Rent 1800 --post Assets:Checking
beans recur add paycheck --freq biweekly --start 2026-07-03 \
    --desc "Salary deposit" --post Assets:Checking 2500 --post Income:Salary

beans recur list            # shows which rules are due
beans recur run --dry-run   # preview everything due through today
beans recur run             # post it (idempotent — run as often as you like)
beans recur run --to 2026-12-31   # post ahead, e.g. for planning

Frequencies: daily, weekly, biweekly, monthly, quarterly, yearly. Monthly-style rules anchor to the start date's day-of-month and clamp to short months (a rule started Jan 31 posts Feb 28, then Mar 31). Rules can have an --end date, be paused/resumed, and removed — already-posted transactions always stay in the ledger, tagged recurring.

Periods

Reports accept --period with: ytd, all, this-month, last-month, this-quarter, last-quarter, this-year, last-year, 2026, 2026-06, 2026-Q2 — or explicit --from/--to dates.

Financial statements

beans report income --period 2026-Q1 --compare   # with prior-quarter deltas
beans report is --period 2026-09 --level 2       # rolled up: Expenses:Food, ...
beans report balance --date 2026-03-31
beans report cashflow --period 2026
beans report trend --periods 12                  # a series, not a snapshot

The income statement shows each line as a % of total income (a common-size view). --level N rolls it up to the first N segments of each account name: 1 is just Total Income, Total Expenses and Net Income, 2 is one level below (Expenses:Food), 3 one further (Expenses:Food:Groceries); the default lists every account.

The balance sheet is classified — assets and liabilities are split into current and non-current sections (use --flat for a by-type-only listing). It computes retained earnings on the fly — cumulative net income that was never formally closed — so Assets = Liabilities + Equity always holds. The cash flow statement uses the direct method: every transaction that moves cash is classified by the counter-account's activity, and the net change reconciles to beginning and ending cash. Transactions that move no cash (e.g. groceries charged to a credit card) correctly appear in the income statement but not the cash flow statement until the card is paid.

report trend is the odd one out, and deliberately so: every other statement covers one period, so a question about drift — "are groceries creeping up?", "how has my savings rate moved?" — had no command. It reports income, expenses, net and per-account flows across N months or quarters, ranked by largest change, and its window ends at the last complete period. That default matters: four days into a month, that month has rent posted and no paycheck, so including it in a series reads as an income collapse that reverses on the 15th. --include-partial opts back in, marks the period, and still keeps it out of the averages.

Add --json to any report for machine-readable output:

beans report balance --json | jq .net_worth

Budgeting

beans budget set Groceries 600                  # monthly by default
beans budget set Insurance 1200 --period yearly # normalized automatically
beans budget set Salary 6000                    # income targets work too
beans budget report                             # this month, budget vs actual
beans budget report --period 2026-Q1            # scaled to any period
beans budget list
beans budget remove Insurance

Budgets are normalized to the report period — a $600/month grocery budget shows as $1,800 for a quarter and is pro-rated for partial periods.

Reconciliation

Check the ledger against reality — your bank statement:

beans reconcile Checking --balance 4512.33   # where do we stand?
beans clear Checking 12 14 15                # mark matched entries cleared
beans clear Checking --through 2026-05-31    # or sweep a whole statement
beans reconcile Checking --balance 4512.33   # difference -> $0.00

The register shows a * next to cleared entries, and a nonzero difference with no uncleared postings points straight at a missing or duplicated transaction.

Reconciling line by line

The balance check tells you that something is off. To find out which line, give reconcile the statement export itself:

beans reconcile Checking --statement may.csv --balance 4512.33

It pairs each statement row against the register and sorts the rest into named classes:

Matched                         28
  on an exact date              26
  within the date window         2
-----------------------------------
Amount mismatch                  1
In bank, not in ledger           1
In ledger, not in bank           1
Cleared, absent from statement   0

Amounts must match exactly — an amount difference is a finding, not something to fuzz away — while dates get a ±5 day window and descriptions are compared loosely, so WHOLE FOODS MARKET #412 matches Whole Foods. That window is what keeps recurring entries quiet: book rent to the 1st of the month and a statement line on the 4th (or the 29th of the month before) still reads as a match, not a discrepancy. Tune it with --window, or set --window 0 to demand exact dates.

This is read-only — no transaction is posted, nothing is cleared. The one file it can write is the hand-off to import:

beans reconcile Checking --statement may.csv --unmatched-out new.csv
$EDITOR new.csv                       # fill in any blank category
beans import new.csv --account Checking

new.csv holds just the in-bank-not-in-ledger rows, in the exact shape import reads, with the category filled in from your saved rules wherever one matched — the blanks are the edit it's asking for. Since you're meant to edit it, reconcile won't overwrite an existing one without --force. For credit cards, whose exports usually report a purchase as a positive number, add --invert.

Once a statement is reconciled, lock it:

beans period close 2026-05-31   # transactions on/before can't change
beans period status
beans period reopen

Forecasting

beans forecast                          # 6 months from 6-month averages
beans forecast --months 12 --method trend --lookback 12
beans forecast --use-budget             # budgets drive accounts that have them
beans forecast --use-recurring          # scheduled txns at exact amounts/dates

beans forecast -n 1  --report bs        # the balance sheet one month out
beans forecast -n 12 --report is        # the next twelve months' income
beans forecast -n 6  --report all       # all three statements
beans forecast -n 12 --report is --level 2   # ...rolled up by category

Source priority per account: recurring schedule > budget > history.

--report projects balanced transactions, not totals, and hands them to the same code that renders your historical statements. It reads how each category was funded straight off your books (that groceries go on the card, that salary lands in checking), projects the transfers an income statement cannot see (a savings sweep, a brokerage contribution, a card payoff), and amortizes any loan on its schedule. The statements balance and articulate: net income over the window is the rise in retained earnings, and net change in cash is the change in the cash line. Investments are held at their last mark and foreign balances at the last known rate — a forecast projects your behaviour, not the market's, and says so on its face.

Goals

beans goal add house --account Savings --target 20000 --by 2028-01-01
beans goal add debt-free --account "Credit Card" --by 2027-06-01  # payoff
beans goal list    # progress bars + required monthly contribution

Investments

Hold securities as FIFO lots with a price history; everything stays balanced double-entry:

beans invest buy VTI 10 --price 280 --account Brokerage   # cash -> lots
beans price set VTI 295
beans invest list                       # qty, cost basis, market, unrealized
beans invest mark                       # post mark-to-market vs Income:Unrealized Gains
beans invest sell VTI 5 --price 300 --account Brokerage   # FIFO, books realized gain

mark adjusts each investment account's book value to market (assumes the account is driven by invest commands), so the balance sheet reads like a brokerage statement while Assets = Liabilities + Equity still holds.

Loans

Attach amortization terms to a liability account and beans derives the payment schedule, the split between principal and interest, and — for the balance sheet — the current portion of long-term debt (principal scheduled to come due within the next twelve months):

beans account add "Liabilities:Auto Loan" --type liability
beans loan add --account "Auto Loan" --principal 30000 --rate 6.25 --term 60 \
    --start 2026-01-01           # payment derived: 583.48/month
beans loan show "Auto Loan"      # the full amortization schedule
beans loan list                  # balance, current portion, non-current, rate
beans loan pay "Auto Loan"       # post one payment: principal + interest + cash out

Give --payment instead of --term to solve for the number of payments. On a classified balance sheet the loan's ledger balance is split into current and non-current buckets using the schedule; the balance itself always comes from the ledger, so the two buckets sum to the real balance and the sheet still balances. beans loan pay computes interest on the actual outstanding balance and posts it to Expenses:Interest, so extra or missed payments stay accurate. (A variable rate or extra principal makes only the split point approximate, never the totals.)

Multi-currency

beans keeps its books in one base currency — the "functional currency", as a company would — so every transaction balances and every statement stays consistent. Asset and liability accounts can be denominated in a foreign currency; their postings carry both the base amount and the foreign amount:

beans account add "Assets:EUR Savings" --type asset --currency EUR
beans currency set EUR 1.0832            # base units per 1 EUR
beans transfer 1100 Checking "EUR Savings" --foreign 1000   # exact EUR
beans transfer 550 Checking "EUR Savings"    # EUR derived from the rate

The foreign amount comes from the latest rate on or before the transaction date unless given explicitly (--foreign on spend/earn/transfer, or a third value on tx add --post ACCOUNT AMOUNT FOREIGN). Then:

beans currency list      # foreign balances, rates, unrealized FX
beans currency rates     # rate history
beans currency revalue   # post FX gains/losses vs Income:FX Gains

revalue is the FX twin of invest mark: it trues each foreign account's base value up to the current rate, so the balance sheet reflects today's rates while remaining balanced.

Export & backup

beans export json -o ledger.json   # everything: accounts, transactions,
                                   # budgets, rules, goals, lots, rates
beans export csv                   # one row per posting, for spreadsheets
beans -f new.db restore ledger.json  # rebuild a ledger from a JSON export
beans backup                       # timestamped copy next to the ledger
beans backup ~/backups/            # ...or wherever you keep them

Both exports are complete: voided transactions are included (the CSV carries a void column, 1 for voided rows, alongside cleared), so your archived data matches the ledger rather than silently dropping voids.

The JSON export round-trips: beans -f new.db restore ledger.json rebuilds a fresh ledger from it — accounts, transactions (with void/cleared flags and foreign amounts), budgets, rules, goals, lots, prices, and FX rates — by replaying them through the normal write path, so every transaction is re-validated to balance. It restores into an empty ledger only (it won't overwrite an initialized one), which makes it handy for moving a ledger between machines or restoring from a text backup.

Backups use SQLite's online backup API, so they're consistent even if taken mid-write. Restore the binary snapshot by just pointing at it (beans -f backup.db); use restore for the portable JSON form.

Archiving into a new ledger

Keep books long enough and the register gets long — twenty-five years of daily household activity is around 28,000 transactions. beans reads that in under a tenth of a second, so this is never urgent, but at some point you simply want a fresh, smaller file to work in.

beans archive --through 2025-12-31 --dry-run          # look first
beans archive --through 2025-12-31 -o ~/.beans/ledger-2026.db

Rather than throwing the history away, archive compacts it: every transaction on or before the cutover becomes one summary transaction per month, carrying that month's per-account totals. Because every aggregate beans computes is a sum, and a sum of monthly sums is the monthly sum, the balance sheet, income statement, statement of cash flows, report trend, networth, forecast and every ratio in analyze come back identical — retained earnings included. On a 25-year ledger that is 28,564 transactions down to 300, and 4.6 MB down to 0.34 MB. Accounts, budgets, recurring rules, import rules, goals, loans, lots, prices and FX rates all carry over untouched, and the archive verifies every balance and every monthly flow against the source before it writes the file.

What you give up is line-item detail for archived months — payees, descriptions, individual amounts — so search, register, tx show and reconcile no longer reach back past the cutover, and mid-month windows become approximate rather than exact. --dry-run also names the merchants whose categorization history is about to disappear, so you can pin them down with beans rule add first.

The source ledger is never modified: it keeps every line item and is the archive of record, so keep it. There is a --drop-detail mode that keeps balances only, in the shape most people first imagine — it is not the default because it takes the savings rate, liquidity runway, trend and forecast with it.

Projects monthly income, expenses, net savings, cash position, and net worth, with a breakdown of which accounts drive the projection and from what basis (history vs budget).

Analysis

beans analyze --period ytd
beans networth --months 12     # month-end net worth trend with deltas

Reports the ratios you would compute for a company, adapted to a household: savings rate (margin), working capital and the current & quick ratios (from the current vs non-current split), liquidity runway in months of expenses, debt-to-assets, debt-to-annual-income, and your top expense categories as a % of income.

Economic balance sheet

The accounting balance sheet shows what you own and owe today. The economic balance sheet adds the present value of the future: your human capital (the discounted value of income you expect to earn) as an asset, and your future consumption (the discounted value of your lifetime spending) as a liability, plus optional pensions, expected inheritances, and planned bequests.

beans economic bs --rate 3 --work-years 25 --live-years 40   # quick estimate
beans economic npv                                           # just the headline
beans economic create-template -o economic.md                # a config to edit
beans economic bs --file economic.md                         # a detailed plan

Human capital and future consumption are estimated from your recent income/expense run-rate, projected over the horizons and discounted — or specified precisely in a markdown config document, where each input can be a flat amount or a dated cashflow stream (e.g. a salary that stops at retirement, a pension that starts later, a one-off inheritance). The forward-looking inputs are assumptions and are never posted to your ledger, so the result always reconciles with the accounting balance sheet:

economic net worth = accounting net worth
                   + human capital + pensions/benefits
                   - future consumption - bequests/obligations

CSV import

Import bank exports with a date, description, signed amount (positive = money in), and optional category column:

beans import bank.csv --account Checking --category Expenses:Other --dry-run
beans import bank.csv --account Checking --category Expenses:Other

Column names are remappable (--date-col, --amount-col, --desc-col, --category-col) to fit whatever your bank produces.

Re-importing overlapping exports is safe: deduplication is count-aware, so re-importing the same file is a no-op, but two genuinely distinct rows that share a date and amount (say, two identical coffees on one day) both import rather than collapsing into one (disable dedupe entirely with --no-dedupe). Rows without a category are routed by saved rules before falling back to --category. When several rules match, the longest — most specific — pattern wins, whatever order you added them in:

beans rule add "WHOLE FOODS" Groceries
beans rule add "SHELL" Transportation
beans rule list

Letting the ledger categorize for you

Rules only cover merchants you remembered to write a rule for. categorize covers the rest by reading the answer out of your own register — how you categorized this same merchant the last twenty times — and tells you how sure it is:

beans categorize bank.csv --account Checking             # preview only
beans categorize bank.csv --account Checking -o prep.csv
Already set in the file  0
From an import rule      1
Inferred from history    1
Needs a decision         1

Date        Description                   Amount  Account          Conf  Basis
---------------------------------------------------------------------------------------
2026-10-20  HARBOR POINT VETERINARY       -88.00  —                0.00  no match
2026-10-09  BLUE RIDGE DENTAL ASSOC 41   -210.00  Expenses:Health  0.60  3 prior
2026-10-02  PAYROLL DEPOSIT ACME CORP   3,200.00  Income:Salary    1.00  rule "PAYROLL"

Rows come out least certain first, so the ones needing you are at the top. The dentist files itself from three prior transactions with no rule ever written — and the store number in ...ASSOC 41 doesn't throw it off, because merchant matching ignores digits.

Read the basis, not just the score: 3 prior is thin evidence that firms up on its own, while 20 prior: 14 Shopping / 6 Cloud is plenty of evidence that disagrees — a merchant that genuinely goes two ways. Same score, different action. Confidence ranks your attention; it is not a probability, and nothing is imported on its strength.

-o writes a CSV in exactly the shape import reads (the extra columns are ignored on import), so the loop is preview → edit the blanks → import. Rules still matter for what history can't know: a brand-new merchant, a deliberate change of mind, and a fresh ledger with no history at all.

Shell completions

beans completions bash > ~/.local/share/bash-completion/completions/beans
beans completions zsh  > ~/.zfunc/_beans    # with fpath+=(~/.zfunc)

Completes commands, subcommands, and account names (via beans account list --names).

Using beans with AI

Four ways to point a model at your books, and they are complements rather than alternatives. Pick by what you are trying to do:

You want to… Use Needs
ask from a terminal, a script, or a local model, with nothing else installed beans ai [ai] extra + a provider key (or a local endpoint)
ask from Claude Desktop, Claude Code, or any other MCP host beans mcp [mcp] extra + a host
get a bank statement into the ledger, categorized and reconciled beans-import skill Claude Code
understand what has been happening across months — trends, drift, inferences beans-report skill Claude Code
build an economic balance sheet and find out what it depends on beans-economic skill Claude Code

The division is worth internalizing: the server reads (a period's numbers, on demand), the skills do a job (a statement in; a briefing out), and beans ai is the one that needs no other software at all. Everything here is opt-in, and neither optional extra adds a third-party dependency.

AI assistant (optional)

beans ai is an opt-in, off-by-default command group — the only part of the tool that reaches the network. Install the extra (it adds no third-party dependency; the client uses the standard library) and set a provider key:

pip install "beans-ledger[ai]"
export ANTHROPIC_API_KEY=sk-...        # or BEANS_AI_KEY, or an OpenAI key

Ask questions in plain English — an agent runs read-only beans commands and reads their JSON to answer, so figures never drift from what beans report would show:

beans ai ask "how much did I spend on eating out last quarter vs the one before?"
beans ai ask --explain "am I over budget anywhere this month?"   # show the commands it ran

Get a CFO-style narrative over your statements and ratios:

beans ai review                        # this-period briefing
beans ai review --brief --period ytd   # 3-bullet TL;DR
beans ai review --focus trend          # add a 12-period series to the bundle

Privacy & data flow. Only the JSON of the read-only commands the assistant chooses to run is sent to the provider — never the ledger file itself, and nothing is written back without a per-command confirmation showing the exact command. --dry-run prints exactly what would be sent and sends nothing:

beans ai review --dry-run              # print the bundle, contact no one

Local models. Point at any OpenAI-compatible endpoint (Ollama, LM Studio, vLLM) to keep everything on-box:

beans ai config set ai.provider openai
beans ai config set ai.base_url http://localhost:11434/v1
beans ai ask "what's my runway if I lost my job today?"

See docs/MANUAL.md for the full ai reference and the AI assistant vignette for a walkthrough.

Use beans from Claude (MCP)

beans mcp is an opt-in MCP server — the way to let Claude Desktop and Claude Code read your ledger directly. The host owns the model; beans just answers tool calls, locally and read-only by default. There is no API key in beans and no embedded LLM (that's the separate [ai] feature). The [mcp] extra adds no third-party dependency — the protocol is hand-rolled on the standard library.

pip install "beans-ledger[mcp]"
beans mcp doctor        # verifies your setup and prints a ready-to-paste config

doctor finds your beans-mcp path and ledger, starts the server to confirm a clean protocol stream, and prints a filled-in claude_desktop_config.json snippet. Register it with Claude Code inside WSL:

claude mcp add beans --scope user -- beans-mcp --file ~/.beans/ledger.db

Then ask Claude about your finances — it calls read-only tools (beans_income_statement, beans_trend, beans_analyze, …) and a review prompt, so its numbers match your statements exactly. Writes are off unless you pass --allow-writes, and even then the host approves each call.

The important detail for most setups is the WSL/Windows boundary (beans in WSL, Claude Desktop on Windows). The focused guide — docs/mcp-setup-wsl.md — covers the claude_desktop_config.json block, the native-Windows fallback, and a troubleshooting table; the MCP vignette is a start-to-finish walkthrough.

Agent skills for Claude Code

Three agent skills ship with this repository and install independently. All drive the ordinary beans CLI, so their figures are the ones your statements show.

git clone https://github.com/rtrimble13/beans.git
cd beans && ./scripts/install_skill.sh     # all, symlinked into ~/.claude/skills
./scripts/install_skill.sh beans-report    # or just one

beans-import — statements into the ledger

It teaches Claude the statement-import workflow — inspect the export, categorize, triage what's uncertain, dry-run, import, reconcile — and the guardrails that go with writing to a financial record. From any directory:

import my November checking statement from ~/statements/november.csv

Claude derives the column mapping and sign convention from the file itself (MM/DD/YYYY dates and split debit/credit columns are rewritten into a working copy — your original is never touched), runs beans categorize, and hands you a table of only the rows that need a decision, with transfers and refunds flagged. It shows a --dry-run and waits for your go-ahead before writing anything, then reconciles the result against the statement.

It will not use --learn, will not skip the dry run, will not invent an account name, and will not reopen a closed period. Confidence scores rank your attention; there is no auto-accept threshold, in beans or in the skill.

Setup, troubleshooting and WSL notes: docs/claude-skill-setup.md. Start-to-finish walkthrough: the Claude skill vignette.

beans-report is the read-only counterpart. beans reports one period at a time — every statement except networth is a snapshot, and --compare reaches exactly one period back — so nothing in the tool can answer "is my grocery spend drifting up, and what does that do to my runway?" This skill assembles the series beans doesn't keep, and reads it with rules.

run my monthly financial review — what's been happening?
are my expenses creeping up, or was that just a bad month?
how has my savings rate moved this year?

It gathers many periods in a single pass, then classifies each account as drift, step, one-off, new, stopped or stable using median-based statistics, so one vet bill never becomes a trend. Two thresholds gate every finding — whether it clears the series' own noise, and whether it is large enough to matter against your income — which is what keeps a briefing to four lines instead of thirty.

The rules that matter most are the ones about not fooling you:

  • A period in progress is never trended. On the 4th of the month, that month shows four days of spending and often no salary; run it into a series and it reads as an income collapse that never happened.
  • A lapsed payment is surfaced, not celebrated. A recurring charge that stopped is money you may still owe, or a service you stopped getting — never reported as a welcome drop in spending.
  • Data quality is checked first. If a quarter of your spending sits in Expenses:Other, category trends measure filing habits; the skill says so and offers totals instead.
  • "Nothing much changed" is a valid answer, and the correct one for a well-run ledger most months.

It never writes to your ledger. Recommendations — a recalibrated budget, a paused rule to resume — are printed as commands for you to run.

Setup and troubleshooting for every skill: docs/claude-skill-setup.md. Walkthroughs: the import vignette and the trend-briefing vignette.

beans-economic — the future, and what it rests on

beans economic bs will print an economic balance sheet. The catch is that exactly one of its lines — Financial Capital — comes from your books. The other five come from beliefs about your own life, and they move the answer far more than anything in the ledger does. On one worked example, holding the books completely constant:

Change one assumption Economic net worth
defaults $512,714.80
income growth 3% $1,069,479.92
inflation 3% −$114,889.96

So this skill does the two things a single command cannot. It interviews you for the six forward-looking lines — when you stop working, what happens to spending then, pension, inheritance, bequests, other obligations — and writes a beans economic config document from your answers, validated by running beans against it before the file lands. Then it stress-tests the result:

build my economic balance sheet
what would retiring five years earlier cost me?
how sensitive is my plan to inflation?

It reports which assumption moves the answer most, the value at which economic net worth crosses zero ("negative above 2.60% long-run inflation"), which inputs your config has pinned so sweeping them proves nothing, and — for a decision question — the scenario delta decomposed by line, which is the cost of that decision in today's dollars.

Three things it will not do: state a figure without the assumptions that produced it, let a line you never mentioned become a silent zero, or write to your ledger. It also refuses an ambiguous rate — beans reads a bare 0.03 as 0.03%, not 3%, and that alone nearly doubles future consumption.

Walkthrough: the economic balance sheet vignette.

Customization

  • beans -f path/to/ledger.db … or export BEANS_LEDGER=… — keep multiple ledgers, store them in a synced folder, anything.
  • beans init --currency EUR — any ISO code; symbol and decimal places adapt.
  • beans config set default_account Savings — default cash side for spend/earn.
  • beans account modify … --cashflow … — reshape cash flow classification.
  • --json everywhere — pipe into jq, spreadsheets, or your own tooling.

Development

pip install -e .[dev]
pytest

The codebase is small and orthogonal: ledger.py (SQLite double-entry core), reports.py (statements), budget.py, forecast.py, proforma.py (projected transactions and the read-only overlay the projected statements are built on), analysis.py, loans.py (amortization), economic.py (economic balance sheet / NPV), importer.py, cli.py. All amounts are stored as integers in minor units; postings are debit-positive/credit-negative and must sum to zero.

Versioning & releases

beans uses semantic versioning with vX.X.X tags. The version lives in a single place — beans/__init__.py — and pyproject.toml reads it dynamically, so there's nothing to keep in sync by hand.

Check the installed version any time:

beans --version

Cutting a release is a two-step, tag-driven flow (see the manual for full detail):

# 1. Bump the version, commit, and create an annotated vX.X.X tag.
scripts/bump_version.py v1.2.3

# 2. Push the commit and the tag. Pushing the tag is what triggers publishing.
git push origin HEAD
git push origin v1.2.3

Pushing a v* tag runs the release workflow, which:

  • builds the sdist and wheel and verifies the tag matches the package version,
  • creates a GitHub Release with an auto-generated "What's Changed" section (categorised via .github/release.yml), and
  • publishes the distributions directly to PyPI (as beans-ledger) using Trusted Publishing (OIDC — no API tokens stored in the repo).

scripts/bump_version.py --show prints the current version, and --push will push the commit and tag for you in one step.

Bugs & feature requests

Found a bug or have an idea for a new feature? Email roger@turningbull.com.

Metadata

Release files for beans-ledger 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for beans-ledger 1.2.0
File Size Uploaded
beans_ledger-1.2.0.tar.gz 265.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for beans-ledger 1.2.0
File Interpreter ABI Platform
beans_ledger-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 441.2 kB

Release files / beans_ledger-1.2.0.tar.gz

Download URL beans_ledger-1.2.0.tar.gz
Size 265.4 kB
Tags Source
SHA-256 checksum
How to use checksums
047f6ee980a203421bd476477fe7c59bf1b5dd02cad38647fa2545260da71552
BLAKE2b-256 checksum
How to use checksums
63e6f96709d0b898df59e7237052410d382c47a299e2723cfafd4b04f743c3a7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / beans_ledger-1.2.0-py3-none-any.whl

Download URL beans_ledger-1.2.0-py3-none-any.whl
Size 175.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b3044a3c2bcc9ad7e0cb735d598a45699a577b33117c50309c27bdc99248de9c
BLAKE2b-256 checksum
How to use checksums
737f72fd6546c7bf75b3297ff36158dedd87765ad9be70a8c284f8fa0d2d7a50
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page