Skip to main content
CryptoFlex// chris johnson
Shipping
Series

Ledgerly

3 posts in this series

1
A dark teal cover graphic titled Ledgerly, Part 1, Local-First Personal Finance, with the kicker one SQLite file, zero cloud accounts, no API key. Four feature panels describe the app: Import, any bank's CSV or Excel export with AI-mapped columns confirmed before anything lands; Dashboard, spending donut, trends, budgets, and goals reading the same numbers from one place; Chat, ask questions of your own spending, grounded in the database on your machine and never uploaded; and Reports, PDF, Excel, and CSV export of a monthly breakdown you can print, file, or hand to someone. Below the panels, a tagline reads "My bank data lives in one file on my machine. The AI runs through a CLI I already had installed."

I wanted a personal finance app that molds to how I actually track money: any bank's CSV or Excel export, real categorization, budgets and goals, and an AI I can ask questions of my own spending. Ledgerly is what I built: local-first, one SQLite file on my machine, no cloud, and AI that runs through the Claude CLI I already have installed. This is part one of the series, covering the use case, the build, and two bugs a layman QA gate caught before I started running v1 against my real money.

Chris Johnson··17 min read
2
A dark teal cover graphic for Ledgerly, Part 2, A Chat That Can Query Its Own Database, with the kicker ten read-only tools, twelve turns, the same SQLite file. The dominant element is a terminal-style chat transcript panel titled ledgerly, chat, turn 3 of 12 with an SSE live indicator. In it the user asks "list my recurring expenses" and the assistant turn shows two green tool-call chips in order, get_recurring_merchants then query_transactions, labelled tools/call over stdio, followed by a serif answer whose second sentence is highlighted in amber, "One thing worth flagging, your recurring mortgage payment is marked excluded in your ledger." An amber callout beneath it reads 1,054 rows, Nobody Asked, wrongly excluded since the first import, restored, 3,056 excluded rows audited with a dry run first. A narrower right rail titled Every Turn, On The Wire, 5 hops, 1 file, stacks five numbered hops joined by dashed arrows, Browser, Next.js /api/chat spawning the CLI as an argv array with no shell, claude CLI v2.1.228 headless with --max-turns 12 picking tools, a hand-rolled MCP stdio server of about 250 lines with no SDK exposing 10 tools, and SQLite opened with query_only on, read only. Below the rail a flags block reads Confined By Flags, $0.5715 to $0.0438 per turn, with --tools empty, --setting-sources empty, and --strict-mcp-config. A bottom line reads "Ten tools. Twelve turns. It found 1,054 transactions nobody asked about."

Part one's chat answered from two months of category totals stuffed into a single-turn prompt, and it could not run one query against the database sitting right there. Part two replaces that with a hand-rolled MCP server, ten read-only tools, and multi-turn tool calling confined by CLI flags a de-risking spike worked out against the real claude CLI. Three review passes ran before the commit, and one confirmed finding was a tool-name mismatch that every unit test passed and only a captured NDJSON fixture caught. The same work also turned up a data problem that had nothing to do with any of it: 1,054 real transactions wrongly marked excluded since the original import in part one.

Chris Johnson··20 min read
3
A dark teal cover graphic titled Ledgerly, Part 3, One Design System, Eight Agents, with the kicker looked like stock shadcn, now wears the same design system as this site. On the left, a glowing teal card labelled one design system record shows the path _ds/cryptoflex-design-system/ above a mono file tree of tokens/fonts.css, tokens/colors.css, tokens/typography.css, tokens/spacing.css, tokens/elevation.css, tokens/motion.css, tokens/base.css, styles.css, and _ds_bundle.js, a chip reading linked by path, 2nd project, and an italic note that it was built for cryptoflexllc.com in April, linked rather than copied by Ledgerly's 783-line prototype in August, with the main session keeping the theme, shared components, sidebar, and dashboard. A teal bus line leaves the record, runs along the top under the labels one session, eight concurrent agents and one route directory plus its spec per agent, and drops two trunks with eight arrowed stubs into eight lane cards in two columns. Lane 01 db-engineer, schema and analytics, migrations 0004 and 0005, four new aggregates, an amber pragma bug tag, caught mid-build with two regression tests. Lane 02 transactions, transactions.spec.ts, six tests and zero lines touched. Lane 03 budgets, spec changed with the product, not the paint. Lane 04 chat, zero lines touched. Lane 05 cash flow and lane 06 recurrings, both tagged new route, covered by nav.spec.ts growing from six entries to eight. Lane 07 reports, spec changed with the product. Lane 08 import plus onboarding, zero lines touched. Along the bottom, a rail reading 46 Playwright tests, all green, with 18 outlined green squares for tests in the five spec files never touched and 28 filled squares for the five that changed with the product, and the line "One design system, eight agents at once. Forty-six tests stayed green."

Ledgerly's UI still worked but looked like stock shadcn, so the whole thing went through Claude Design and came back wearing CryptoFlex's Cyber Editorial system, the same one from April's site rebuild. Eight parallel agents rebuilt every screen against real backend constraints while 46 Playwright tests stayed green, one of them written to prove a SQLite migration bug on purpose.

Chris Johnson··27 min read

Navigation

Blog Posts

↑↓ navigate openesc close