AI agents

First-pass bookkeeping agent for small online shops

Agent that matches payment payouts to orders, categorises bank lines, proposes explained ledger entries and drafts a monthly VAT summary.

typescriptllmvitestpnpmcsv

Built 1 time, downloaded 0 times.

Install from your terminal

Run this in your project folder. It signs you in, shows what changes and asks before it overwrites a file.

npx claude-lab add first-pass-bookkeeping-agent-for-small-online-shops-b9dc3e
11 filesDownload zip
CLAUDE.md
# Bookkeeping agent

## Purpose

First-pass bookkeeping for small online shops. The agent ingests one
payment provider (Stripe as the first adapter, behind a `PaymentProvider`
interface) and CSV bank exports. It matches payouts to orders, categorises
expenses against a chart of accounts, and produces a monthly summary
(revenue, refunds, fees, top expense categories, VAT owed).

Product spec: @SPEC.md
Build phases and /goal prompts: @PLAN.md
Review rules (read by /code-review): @REVIEW.md

## Hard rules

1. Never write to accounting records.
   The only output is `ProposedEntry` objects with status `proposed`.
   `approved` is set only by a human action in the review queue.
   No code path may call an accounting API write.
   Grep for one in every review.
2. Every decision is explained.
   Every `ProposedEntry` and every match carries a `Decision` with:
   - `reason`: plain English an accountant can read
   - `evidence`: source row ids and file + line
   - `method`: `rule:<id>` | `llm:<model>` | `human`
   - `confidence`: 0-1
   - `inputsHash`
   A missing reason is a bug.
3. Money is integer minor units.
   Use `amountMinor: bigint` (or a number of pence/cents) plus an
   ISO 4217 currency. Never use floats.
   VAT is computed with explicit rounding per line, using the mode set
   in the shop config.
4. Uncertain or large items go to review.
   Anything below `review.minConfidence` (default 0.85), and any amount
   over `review.maxAutoAmountMinor`, goes to the review queue with a
   `flagReason`.

## Pipeline order

ingest -> normalise -> deterministic match -> rule-based categorise
-> LLM categorise only the leftovers -> propose -> summarise

- Rules run first. The LLM never matches money.
- The LLM only sees lines no rule could categorise.

## Cost rules

- Haiku 5.5 for bulk categorisation, in batches of up to 50 lines.
- Sonnet 5.5 only for flagged or ambiguous lines.
- Use prompt caching on the chart of accounts and the system prompt.
- Log every API call to `CostLedger` with shopId and tokens.
- Target: per-shop monthly cost under `COST_BUDGET_USD`
  (placeholder, set in SPEC.md).
- Never call Opus or Fable from product code.

## Commands

- `pnpm install`
- `pnpm test`: vitest, no network
- `pnpm test:golden`: fixture end-to-end runs
- `pnpm typecheck`: tsc --noEmit
- `pnpm lint`
- `pnpm eval:cost`: replays the fixture month with recorded LLM
  responses and prints cost per shop
- `pnpm dev:run --shop fixtures/shops/demo-uk --month 2026-09`
- `pnpm eval:live`: live Claude calls, needs `ANTHROPIC_API_KEY`.
  Only when the user asks.

## Tests and the LLM

- Tests never hit the real Claude API.
- Use the recorded-response client in `src/llm/replayClient.ts`.
- Live calls run only in `pnpm eval:live`.

## Data rules

- Real shop data lives only in `data/raw/`.
  It is gitignored and blocked from Claude by settings.
- Fixtures in `fixtures/` are synthetic, or anonymised with
  `pnpm fixtures:anonymise`.
- Never paste real IBANs, names or emails anywhere:
  code, tests, prompts, commits or chat.

## Layout

- `src/ingest/`: provider and bank adapters
- `src/domain/`: types, money, VAT
- `src/match/`: deterministic matching rules
- `src/categorise/`: rules, merchant cache, LLM leftovers
- `src/propose/`: double-entry `ProposedEntry` builder
- `src/summary/`: monthly summary JSON + Markdown
- `src/llm/`: Claude client wrapper, replay client, `CostLedger`
- `fixtures/`: synthetic and anonymised shop data
- `evals/`: recordings and cost snapshots

## Gotchas

- Stripe payouts net out fees and refunds.
  Reconcile gross - fees - refunds = payout.
- Bank CSVs differ in date format, sign convention and encoding.
  Handle a BOM and `;` delimiters.
- Payouts can split across several bank lines, or several payouts
  can combine into one bank line.
- Timezone: bucket by the shop's timezone, not UTC.

## Workflow

1. Explore the relevant code first.
2. Plan the change.
3. Code it.
4. Run the verify skill before saying done.

- Use the test-runner subagent for full test runs.
  It returns failures only.
- Use the audit-reviewer subagent and /code-review before merging.
Use for my project