- Python 54.1%
- Zig 35.4%
- TypeScript 3.9%
- JavaScript 2.5%
- HTML 2.1%
- Other 2%
| .agents/plugins | ||
| .codex-plugin | ||
| apps | ||
| assets | ||
| evolution/proposals | ||
| nvim/beanz | ||
| obsidian-plugin | ||
| python | ||
| scripts | ||
| skills/beanz-ledger | ||
| src | ||
| tests | ||
| third_party | ||
| tools | ||
| vscode-extension | ||
| wasm | ||
| .gitignore | ||
| .mcp.json | ||
| beanz-format.json | ||
| build.zig | ||
| build.zig.zon | ||
| CONSTITUTION.md | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
beanz
beanz is a zero-copy Beancount reimplementation in Zig.
The project is shaped around the Zig compiler's parser architecture:
- immutable AST over externally-owned source
- zero-copy token spans
- explicit parse/lower/check phases
- mergeable per-file units
- editor-first workspace state for LSP, MCP, Neovim, VS Code, Obsidian, and native host APIs
The first deployment target is replacing Beancount inside local repos without depending on upstream Python runtime.
Status
What is working now:
- parser, lowering, formatter, checker, LSP, MCP, native Neovim host
- WebAssembly host for Obsidian/mobile integration
- single
beanzbinary for CLI, LSP, MCP, and Neovim - native shared library at
zig-out/lib/libbeanz_native.* - initial Python compatibility package in
python/backed by the native library, not the CLI
What is not complete yet:
- full upstream Beancount Python API/data-model parity
- full upstream checker/loader/plugin semantics
- direct CPython binary extension module
- full upstream
bean-doctor/treeifyparity
Toolchain
This repo is pinned to Zig master through Mitchell Hashimoto's Zig overlay.
nix develop
Core verification:
zig build test
zig build native
zig build wasm
zig build install
python3 tools/report_parser_fixture_progress.py
Current parser fixture target remains:
272 / 272 static extracted parser fixtures passing
CLI
The main binary is beanz.
beanz parse ledger.beancount
beanz check ledger.beancount
beanz lint ledger.beancount
beanz query ledger.beancount 'select distinct account, count(*) as count group by account order by account desc'
beanz format --check ledger.beancount
beanz format -i ledger.beancount
beanz doctor parse --json ledger.beancount
beanz doctor snapshot --json ledger.beancount
beanz lsp
beanz mcp --file ledger.beancount
beanz nvim
Notes:
checkandlintare aliases.queryruns a native bean-query-style executor over the loaded workspace and is also exposed through LSP (beanz/query) and MCP (query_ledger).doctor list-optionsanddoctor print-optionsaliasdoctor options.formataccepts Beancount width flags for compatibility, but current formatting is driven bybeanz-format.json.
Codex Plugin
This repo is also a Codex plugin marketplace. The marketplace manifest is in .agents/plugins/marketplace.json, the plugin manifest is in .codex-plugin/plugin.json, the MCP server bundle is declared by .mcp.json, and the local interactive chart app is served from the MCP resource ui://beanz/ledger-chart.html.
Install from the compatible.systems Git repo:
codex plugin marketplace add https://compatible.systems/conrad/beanz.git --ref main
codex plugin add beanz@compatible-systems
For local development, add this checkout as a marketplace instead:
codex plugin marketplace add .
codex plugin add beanz@compatible-systems
The MCP tools include:
set_ledger_file: loads the Beancount file to manage whenBEANZ_LEDGERwas not configured.ledger_sankey: builds a transaction-posting Sankey locally from the active ledger.ledger_chart: runs a ledger query and opens the bundled chart app for Sankey, bar, or table views.query_ledger,document_snapshot,account_state, diagnostics, quick fixes, and deterministic edit tools.
For local plugin debugging in a second Codex desktop instance:
scripts/codex-debug-sidecar --detach
That launcher creates an isolated CODEX_HOME, a separate Electron user-data directory via CODEX_ELECTRON_USER_DATA_PATH, and a local compatible-systems marketplace that points back at this checkout. It does not copy login state unless BEANZ_CODEX_DEBUG_COPY_AUTH=1 is set.
Native Python
The Python compatibility layer is in python/ and loads the native shared library in-process through ctypes.
Build the shared library first:
zig build native
Then run the Python tests:
PYTHONPATH=python/src python3 -m unittest discover -s python/tests
Optional upstream compatibility suite:
python3 tools/run_upstream_python_suite.py --sync-only
python3 tools/run_upstream_python_suite.py --no-sync --limit 10
python3 tools/run_upstream_python_suite.py --no-sync --summary-only
python3 tools/run_upstream_beanquery_suite.py --sync-only
python3 tools/run_upstream_beanquery_suite.py --no-sync --summary-only
python3 tools/report_compat_progress.py --include-upstream-python --include-upstream-beanquery
Current upstream compatibility baselines:
- Python API suite:
76 / 76modules passing - Beanquery parser/execution suite:
414 / 414captured cases passing, with95value-only passes and40skipped helper-incompatible methods in the broader upstream test tree
Quick smoke:
PYTHONPATH=python/src python3 - <<'PY'
from beancount.loader import load_string
entries, errors, options = load_string("""
2013-05-18 open Assets:Cash USD
2013-05-18 open Expenses:Food USD
2013-05-19 * "Coffee"
Assets:Cash -4 USD
Expenses:Food 4 USD
""".strip() + "\n", filename="example.beancount")
print(len(entries), len(errors), sorted(options.keys()))
PY
The Python distribution is beanz, but it vends the upstream-facing module and CLI names:
beancount.*beanquery.*beanz.*bean-checkbean-querybean-formatbean-doctor
Current compatibility also includes the internal BeanQuery-facing modules needed by downstream code that imports the Python API directly:
beanquery.connect,Connection,Cursor,Columnbeanquery.query.run_querybeanquery.numberifybeanquery.query_compile,query_env,query_render,hashablebeanquery.sources.beancount,beanquery.sources.test
The wheel includes Provides-Dist/Obsoletes-Dist metadata for beancount and beanquery, but current pip installers do not treat that as a hard conflict or automatic replacement. In practice, this means:
- use
beanzas the installed Python distribution name - treat Nix/system packaging as the place to enforce replacement/conflict rules
- keep the module names and CLI names compatible inside the single distribution
For older local Python environments that still stumble on modern editable-install paths, python/setup.py develop is included as a compatibility bridge.
Editor Surfaces
LSP
beanz lsp
Native Neovim
The repo ships a native Neovim plugin under nvim/beanz/. The host is Zig, not Lua.
Smoke tests:
bash tools/run_nvim_plugin_smoke.sh
bash tools/run_nvim_completion_smoke.sh
bash tools/run_nvim_syntax_smoke.sh
VS Code
The repo includes a bundled VS Code extension in vscode-extension/ that launches beanz lsp and renders account state in a sidebar.
Obsidian
The mobile-safe Obsidian plugin lives in obsidian-plugin/ and uses the wasm host. The installable bundle is produced under built/obsidian-plugin/beanz/.
Reference Trees
The workspace is bootstrapped around three local references:
upstream/beancountupstream/zigupstream/zig-overlay
third_party/beancount-python/ is vendored as the spec/reference tree for fixtures, command/API parity work, and compatibility evaluation.
third_party/beanquery-python/ is vendored as the query-language spec/reference tree for optional compatibility evaluation.
Repository Layout
src/ast.zig: compact AST/token storagesrc/tokenizer.zig: zero-copy lexersrc/parse.zig: syntax buildersrc/lower.zig: typed loweringsrc/check.zig: balance/assertion checkingsrc/edit.zig: editor/workspace statesrc/cli.zig: batch CLI surfacesrc/native.zig: native C ABI for Pythonsrc/wasm.zig: wasm host APIpython/: native Python compatibility packagenvim/: native Neovim pluginvscode-extension/: VS Code extensionobsidian-plugin/: Obsidian plugintools/: fixture extraction, smoke tests, and reports
Governance
- Constitution: CONSTITUTION.md
- Proposals: evolution/proposals/README.md
Non-trivial architectural changes should land with a BZP-XXXX proposal.