Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rustledgerpi

rustledgerpi is an illustrative Pi coding agent for plain-text bookkeeping. It lets a CPA or bookkeeper ask an LLM to help maintain a Beancount ledger, while the code narrows what the agent can change and gives it a deterministic ledger engine: rustledger compiled to WebAssembly (@rustledger/wasm).

The repository is intentionally small. Its purpose is educational: show how a financial professional can reason about an AI-assisted workflow in control terms, then trace those controls directly to code.

See the accompanying YouTube video here

What This Demonstrates

  • Segregation of capability: the agent gets only read, write, edit, and rustledger; shell execution is removed from the normal tool list.
  • Restricted change surface: write and edit are blocked unless the target is a top-level *.beancount file in the current working directory.
  • Independent validation: the custom rustledger tool parses, validates, formats, queries, and reports BALANCES using a deterministic WASM accounting engine instead of relying on the model's arithmetic.
  • Readable evidence: tool outputs include validation errors, query columns, row counts, and truncation flags so a reviewer can see what was checked.

Control Model

Think of the LLM as a junior preparer. It can propose and make bookkeeping entries, but the extension places controls around what it can touch, what tools it can use, and how ledger correctness is checked.

Control Objectives

  1. Prevent unrelated file changes. The agent should not be able to edit code, notes, configuration, or files outside the demo ledger folder.
  2. Prevent shell-based escape routes. The agent should not run arbitrary commands such as rm, curl, package installers, or scripts.
  3. Keep the ledger scope understandable. Multi-file operations should only use top-level .beancount files that are visible in the working directory.
  4. Validate with accounting software, not vibes. Balancing, parsing, and BQL queries should run through rustledger.
  5. Make failures explicit. Refused paths, missing entry points, parse errors, and validation errors are returned as structured messages.

Where The Controls Live

  • package.json starts Pi with an explicit tool list: pi --tools read,write,edit,rustledger -e ./extensions/bookkeeping-agent.ts.
  • extensions/bookkeeping-agent.ts calls setActiveTools on session_start so the same allowlist is reinforced after the extension is loaded.
  • extensions/bookkeeping-agent.ts handles tool_call events and blocks:
    • bash tool calls;
    • write calls whose path is not a top-level .beancount file;
    • edit calls whose path is not a top-level .beancount file.
  • The helper assertTopLevelBeancount() performs the path control. It checks the filename extension, rejects subdirectories, rejects paths outside the current working directory, and uses realpath to catch existing files or symlinks that resolve somewhere else.
  • The helper loadTopLevelBeancountFiles() reads only top-level .beancount files for ledger-wide rustledger actions.

These controls are implemented in code. They are not merely prompt instructions. The prompt still matters for workflow guidance, such as reminding the model to validate after edits, but the write/edit boundary is enforced by the extension.

Important Limits

This is a demo control pattern, not a full sandbox.

  • read is intentionally left available for developer convenience and is not restricted by this extension to .beancount files.
  • Pi extensions run with your operating-system user privileges.
  • The controls apply to Pi tool calls handled by this extension. They do not protect you from other processes, manual edits, editor extensions, or commands you run outside the agent.
  • For production or client data, add stronger isolation such as a separate OS user, a container, a temporary workspace, network controls, and review/approval gates.

Repository Tour

  • extensions/bookkeeping-agent.ts contains the Pi extension, tool controls, and custom rustledger tool.
  • main.beancount is the sample ledger.
  • scripts/smoke-rustledger.mjs verifies that the WASM package can load and validate a minimal Beancount directive without involving an LLM.
  • AGENTS.md describes the bookkeeping-agent operating rules used by coding agents in this workspace.
  • package.json defines the agent, TypeScript check, and smoke-test scripts.

Prerequisites

  • Node.js 20+ recommended because this project uses ESM and WASM.
  • A Pi-supported LLM provider, either through an API key or pi login. See Pi providers.

Install

cd /path/to/rustledgerpi
npm install

Run The Bookkeeping Agent

From the repository root:

npm run agent

This runs:

pi --tools read,write,edit,rustledger -e ./extensions/bookkeeping-agent.ts

Pi resolves the extension as TypeScript. The extension loads WASM from the installed @rustledger/wasm package by reading rustledger_wasm_bg.wasm from disk and passing the bytes to initWasm({ module_or_path: bytes }).

Optional Project Pi Config

To load the same extension whenever you run pi in this repo, add .pi/settings.json with paths relative to .pi/:

{
  "extensions": ["../extensions/bookkeeping-agent.ts"]
}

You should still start Pi with --tools read,write,edit,rustledger, or an equivalent allowlist, if you want to exclude bash and other built-in tools at startup.

How It Works

flowchart LR
  user[CPA or developer]
  pi[Pi agent]
  tools[read write edit rustledger]
  ext[bookkeeping-agent.ts]
  wasm["@rustledger/wasm"]
  ledger[Top-level .beancount files]

  user --> pi
  pi --> tools
  tools --> ext
  ext --> wasm
  ext --> ledger
Loading
  1. The user starts Pi through npm run agent.
  2. Pi exposes only the requested tool names to the model.
  3. The extension reinforces the active tool allowlist after session startup.
  4. Every attempted bash, write, or edit tool call passes through the extension's tool_call handler.
  5. write and edit paths must pass assertTopLevelBeancount() before Pi can perform the operation.
  6. Ledger validation and reporting go through the rustledger tool.

The rustledger Tool

The model can call rustledger with these parameters:

Field Meaning
action One of validate, parse, format, query, or balances.
path Optional top-level .beancount file, such as main.beancount. Required for format. For validate and parse, omit it to use the whole top-level ledger set.
entry_point Optional root file for multi-file mode. Defaults to main.beancount if present, otherwise the first sorted .beancount file.
bql Required when action is query.

Large BQL result sets are truncated in the text output with a rowsTruncated flag so the agent does not flood its context window.

Example Session

Create main.beancount in the same directory where you run npm run agent:

option "title" "Demo ledger"
option "operating_currency" "USD"

2024-01-01 open Assets:Bank USD
2024-01-01 open Expenses:Food USD

2024-01-15 * "Coffee"
  Expenses:Food   5.00 USD
  Assets:Bank    -5.00 USD

Start the agent and ask:

  • "Validate my ledger with rustledger and report errors."
  • "Show balances."
  • "Add a transaction for groceries and re-validate."
  • "Try to edit README.md." The extension should refuse because README.md is not a top-level .beancount file.

Developer Verification

Run these checks without involving an LLM:

npm run check
npm run smoke:wasm

npm run check type-checks the TypeScript extension. npm run smoke:wasm loads the rustledger WASM package and validates a minimal Beancount directive.

License

This project is licensed as GPL-3.0-only to match @rustledger/wasm.

License terms are part of software governance. CPAs are used to asking, "What are the obligations, restrictions, and downstream risks?" Open-source licenses answer those questions for code.

Common licenses differ in how much freedom they give downstream users and what they require if someone redistributes modified software:

License Plain-English Summary Typical Obligations Business Implication
MIT Very permissive. You can use, modify, distribute, and include the code in proprietary products. Keep the copyright notice and license text. Low friction for commercial use. Often preferred for libraries and examples.
Apache-2.0 Permissive like MIT, with more explicit patent protections. Keep notices, include the license, preserve NOTICE files if present, and follow patent terms. Common in enterprise settings because patent language is clearer.
BSD-2-Clause / BSD-3-Clause Permissive licenses similar to MIT. BSD-3 adds a non-endorsement clause. Keep copyright and license notices; do not imply endorsement under BSD-3. Low friction, widely accepted.
MPL-2.0 File-level copyleft. Modified MPL-covered files must remain open, but larger combined works can use other licenses. Publish source for modified MPL files when distributing them. Middle ground between permissive and GPL-style copyleft.
LGPL-3.0 Weaker copyleft, commonly used for libraries. Applications can link to the library under certain conditions. Allow users to replace or relink the LGPL component; publish modifications to the LGPL component. More commercial flexibility than GPL, but still requires compliance controls.
GPL-3.0 Strong copyleft. If you distribute a derivative work, the whole covered work generally must be available under GPL-3.0. Provide source code, preserve license notices, state changes, and grant recipients the same freedoms. Good for ensuring improvements remain open; requires care before embedding in proprietary distributed software.
AGPL-3.0 GPL-3.0 plus network-use obligations. Running modified software as a service can trigger source-sharing duties. GPL-3.0 obligations plus source availability to network users. Important for hosted SaaS use cases; often reviewed closely by legal/compliance teams.

For this repository, the key point is simple: because the demo depends on @rustledger/wasm, and that package is GPL-licensed, this project uses GPL-3.0-only as well.

Practical notes:

  • Using this repository internally for learning or experimentation is usually simpler than redistributing it.
  • If you modify and distribute this project, expect to provide the corresponding source code under GPL-3.0-only.
  • If you plan to embed this code in a commercial product, especially one you distribute to clients, review the dependency licenses with counsel.
  • Ledger data, client records, and your own .beancount files are not automatically licensed as GPL just because this tool can read or edit them. The license applies to the software, not to your accounting data.

About

A minimal Pi bookkeeping agent using rustledger via WASM

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages