Als je ooit een collega, een nieuwe laptop of een nachtelijke cronjob een werkende Beancount-opstelling hebt moeten geven, weet je dat de boekhouding nooit het moeilijke deel was. Het moeilijke deel was de toolchain: een Python die past, bean-check en bean-query op het pad, een rapportagebibliotheek die je binnenhaalt voor één balans, en een formatter die je bestanden herschrijft zodra je hem een vraag stelt. bea 0.2.0, uitgebracht op 12 september 2026, vervangt die checklist door één installatie. Het bea-commando bevat nu de complete native Beancount-toolchain, draait die in een beheerde engine die het zelf inricht, en behoudt het machineleesbare contract waar scripts en AI-agents al op vertrouwen.
Dit is de release note voor 0.2.0, geschreven zoals we een release intern bijhouden: wat er is uitgebracht, wat er onder de motorkap is veranderd, hoe het is geverifieerd voordat het een package index bereikte, wat het bewust nog niet doet, en hoe je upgradet. Wil je liever het verhaal van de eerste run, dan zijn de lanceringspost van 0.1.0 en de CLI-snelstart de kortere leesstukken.
De release in één oogopslag
Twee kanalen publiceren hetzelfde commando. Kies er één en controleer daarna of het met zijn versie antwoordt:
$ brew install bex-co/tap/bea # macOS en Linuxbrew
$ uv tool install beancount-io # overal met uv en Python 3.12 of nieuwer
$ bea --version
bea 0.2.0cli-v0.2.02026-09-12beancount 3.2.3 beanquery 0.2.0beangulp 0.2.0 beanprice 2.1.03.12 3.14De releasekaart van 0.2.0: de tag en de publicatiedatum, de Beancount- en Beanquery-versies die de beheerde engine vastpint, de twee optionele engine-features en de Python-versies waarop de release is geïnstalleerd en getest.
| Veld | Waarde |
|---|---|
| Versie | 0.2.0, tag cli-v0.2.0, gepubliceerd op PyPI en in de Homebrew-tap bex-co/homebrew-tap op 2026-09-12 |
| Vorige release | 0.1.0, getagd op 2026-09-09, drie dagen eerder |
| Wijzigingsset | 27 commits die de CLI raken, 119 gewijzigde bestanden, ongeveer 12,300 regels toegevoegd en 2,100 verwijderd |
| Engine-pins | Beancount 3.2.3 en Beanquery 0.2.0 in de basisengine; Beangulp 0.2.0 en Beanprice 2.1.0 als optionele features |
| Hoofdpunt | Elke native Beancount-tool onder één prefix, geleverd door een beheerde engine; de JSON-envelop en het exitcode-contract uit 0.1.0 zijn ongewijzigd |
Wat er onder de motorkap veranderde: de beheerde engine
In 0.1.0 importeerde bea Beancount in zijn eigen proces, zoals elke Python-tool dat zou doen. Dat werkte, maar het maakte de afhankelijkheidsgraaf van de CLI tot die van Beancount, en het liet "installeer eerst Beancount" als ongeschreven stap in elke handleiding staan.
0.2.0 trekt een lijn dwars door het midden van het programma. De bea-frontend, het deel dat de commando's, de opties en de weergave beheert, laadt nooit Beancount, Beanquery of de meegeleverde Fava-rapportagecode. Lokaal grootboekwerk draait in een beheerde engine: een aparte Python-omgeving die bea inricht vanuit een hash-vastgepinde lock en start als een child-interpreter. De frontend stuurt een JSON-verzoek over die grens en toont wat terugkomt. Je installeert geen Beancount, zet geen bean-*-tools op je pad en denkt niet na over welke Python ze hebben gevonden.
Hoe de engine aankomt, hangt af van het kanaal:
- Homebrew maakt de frontend- en engine-omgevingen aan tijdens de installatie. Lokale commando's gebruiken de keg-lokale engine zonder verdere download.
- PyPI (
uv tool installof pipx) richt de engine in bij het eerste gebruik. Het eerste lokale commando dat de engine nodig heeft, downloadt de vastgepinde combinatie, wat eenmalig netwerktoegang enuvop het pad vereist. Latere commando's hergebruiken die offline vanuit~/.local/share/bea/engine/<version>, of onderXDG_DATA_HOMEals je dat instelt.
Uit dat ontwerp volgen drie eigenschappen, en elk ervan schrapt een supportticket dat we al hebben gezien:
- Upgrades blijven gekoppeld.
bea upgradegeeft de update door aan de package manager die deze kopie heeft geïnstalleerd en bouwt daarna de bijpassende engine opnieuw op, zodat een frontend en een engine nooit naar verschillende versies kunnen afdrijven. - Een kapotte engine herstelt zichzelf. Als een inrichting halverwege mislukt, wordt de beheerde omgeving weggegooid en bij de volgende geslaagde poging opnieuw opgebouwd. Losse
bean-check-binaries elders op het pad worden genegeerd in plaats van per ongeluk opgepikt. - Zware optionele onderdelen blijven optioneel. Het Beangulp-importframework heeft de systeembibliotheek
libmagicnodig, en Beanprice haalt afhankelijkheden voor het ophalen van koersen binnen. Geen van beide zit in de basisengine. Je schakelt ze expliciet in, alleen in de engine.
$ bea engine status
$ bea engine enable beangulp # ingest-helpers; heeft de systeembibliotheek libmagic nodig
$ bea engine enable beanprice # bean-price koersen ophalenbea engine status meldt of de engine is ingericht en welke optionele features zijn ingeschakeld, en heeft geen netwerk nodig om dat te zeggen. Als een inrichting bij het eerste gebruik mislukt, los dan het netwerk of uv op en voer een willekeurig lokaal commando zoals bea check opnieuw uit. Doe geen pip install beancount ernaast: de frontend zal het niet gebruiken.
Elke native tool, één prefix
De engine is het mechanisme. De verandering voor de gebruiker is pariteit: elk uitvoerbaar bestand dat het upstream Beancount-project levert, heeft nu een bea-tegenhanger, met dezelfde argumenten doorgegeven en dezelfde uitvoer behouden.
$ bea check # bean-check, plus de --json-envelop van bea
$ bea format main.bean -o clean.bean # bean-format: standaard naar stdout, -i herschrijft
$ bea query "SELECT account, sum(position) GROUP BY account"
$ bea doctor context main.bean 2026-01-02 # alle elf bean-doctor-bewerkingen
$ bea example --seed 1 -o example.beancount # bean-example
$ bea treeify < balances.txt # treeify
$ bea ingest identify --config ingest.py inbox # Beangulp, na engine enable
$ bea price -e USD:yahoo/AAPL # bean-price, na engine enablebean-checkbea checkbean-formatbea formatbean-querybea querybean-doctorbea doctorbean-examplebea exampletreeifybea treeifybeangulpbea ingest bea engine enable beangulpbean-pricebea price bea engine enable beanpriceDe pariteitskaart: de zes native Beancount-executables boven de streepjeslijn werken direct; de twee eronder worden doorgestuurd naar Beangulp en Beanprice zodra je die feature in de engine inschakelt.
Een paar hiervan verdienen meer dan een rij in een tabel.
bea check is bean-check met daarbovenop de JSON-envelop van bea: dezelfde validatie, dezelfde foutmeldingen, en onder --json dezelfde velden valid en errors die scripts al parsen.
bea format heeft ander gedrag gekregen, en het is de ene verandering in deze release die een script kan verrassen. In 0.1.0 herschreef bea format PATH het bestand. Nu drukt het de geformatteerde tekst af op stdout en laat het bestand ongemoeid. --in-place (-i) is wat herschrijft, --output FILE (-o) schrijft ergens anders naartoe, --check is de CI-poort die met 1 afsluit wanneer bestanden formattering nodig hebben, en --dry-run toont wat er zou veranderen. Dit volgt bean-format, waarvan de standaard de veilige is: een commando dat een pad leest en het stilzwijgend herschrijft, kun je niet eerst uitproberen. Formatteren is een teksttransformatie, geen parse, dus het weigert niet langer een bestand met een syntaxfout; het lijnt uit wat het herkent en laat de rest staan. Voer bea check uit voor geldigheid.
bea query heeft het hele native oppervlak gekregen. Het neemt BQL als argument, via stdin of in de interactieve shell, die nu de upstream Beanquery-shell is, gestart als childproces met zijn .format-, .output-, .run- en .set-commando's intact. --format kiest de weergave text, csv of beancount, --numberify splitst bedragen in één kolom per valuta, -o schrijft naar een bestand, en --source URI geeft een native Beanquery-bron rechtstreeks door.
bea doctor stelt alle elf bean-doctor-bewerkingen beschikbaar: lex, parse, roundtrip, directories, list-options, print-options, context, linked, region, missing-open en display-context. Als je ooit een boekingsprobleem hebt gedebugd met bean-doctor context, is het dezelfde tool op hetzelfde adres.
bea example en bea treeify zijn de native generator en de native boomweergave, ongewijzigd doorgestuurd.
bea ingest en bea price sturen respectievelijk door naar Beangulps identify, extract en archive en naar bean-price, na bea engine enable. Het Python-loze CSV-pad, bea import --csv, heeft geen van beide nodig en is ongewijzigd.
Eén regel bindt de doorgestuurde commando's samen: doctor, example, treeify, price en ingest geven hun argumenten ongewijzigd door aan upstream en behouden de uitvoer en exitstatus van upstream. Dat betekent ook dat ze het grootboek als hun eigen positionele argument nemen, zoals in bea doctor lex main.bean, in plaats van via het globale --file. De envelop en de exitcode-categorieën hieronder beschrijven de eigen commando's van bea.
Het contract waar scripts op kunnen blijven vertrouwen
Niets aan het machineleesbare oppervlak is verschoven. Het globale --json zet nog steeds één envelop op stdout met bea, target, data en truncated, plus limit op begrensde lijsten en page op gepagineerde hosted lijsten. Bedragen zijn decimale strings, nooit floats, en datums zijn ISO YYYY-MM-DD. --json impliceert --no-input; dat doen een niet-terminal stdin of een CI-variabele met een truthy waarde ook, zodat een onbeheerde job nooit op een mens wacht. --strict weigert gedeeltelijke antwoorden, zelfs in een terminal, en het --allow-errors van elk leescommando kiest daar weer voor terug.
Een mislukking schrijft niets naar stdout en precies één object naar stderr:
{
"error": {
"category": "validation",
"message": "Ledger has 3 error(s). Pass --allow-errors to report anyway.",
"exit_code": 1,
"details": ["main.bean:1: Transaction does not balance: (2.50 USD)"]
}
}De vijf exitcodes en de category-string die elk ervan in het JSON-foutobject draagt. Een script vertakt op het getal; een mens leest de categorie.
| Code | Categorie | Betekenis |
|---|---|---|
| 0 | geen | Succes, inclusief previews en bewust overgeslagen duplicaten |
| 1 | validation | Grootboek- of validatiefout, en de vergaarbak voor elke andere runtime-fout |
| 2 | usage | Foute argumenten, een ontbrekend doel of extra, of invoer nodig onder --no-input |
| 3 | auth | Authenticatie- of rechtenfout, inclusief een alleen-lezen bestemming |
| 4 | conflict | Een gelijktijdige wijziging, een import die duplicaatbeoordeling nodig heeft, of een schrijfactie waarvan de uitkomst onbekend is |
Twee details zijn van belang voor wie na een mislukking opnieuw probeert. Een niet-nul exit betekent niet altijd dat er niets is veranderd: add transactions --partial kan de geaccepteerde rijen schrijven, format -i over meerdere bestanden kan er enkele herschrijven voordat het op één ervan mislukt, en cloud ledger create --clone kan het grootboek aanmaken voordat het klonen mislukt. Lees error.result voordat je een mutatie opnieuw probeert. En hosted commando's projecteren de HTTP-status van de server op dezelfde tabel, met behoud van de eigen melding van de server: 401 en 403 sluiten af met 3, 400 met 2, 409 met 4, en alles overige, inclusief rate limiting, met 1. Een schrijfactie waarvan de CLI de uitkomst niet kan weten, zoals een time-out midden in een delete, sluit af met 4 en zegt dat ook, in plaats van te gokken.
De automatiseringsgids loopt met een jq-pijplijn van begin tot eind door deze envelop.
Fixes die meeliftten
Een pariteitsrelease is ook een kans om de defecten te sluiten die een eerste release aan het licht brengt. Deze landden tussen de twee tags, elk met een regressietest:
- Getallen worden geschreven als vaste-komma-tekst, nooit in wetenschappelijke notatie, inclusief de beginsaldi die
bea initweergeeft. Een grootboek dat1E+3zegt, is technisch geldig en praktisch onleesbaar. - Kostenlots overleven JSON-serialisatie met hun datums en labels intact, en lotlabels worden correct ge-escaped wanneer een transactie wordt geschreven.
- Expliciete nulposten zijn echte bedragen tijdens import, in plaats van te worden gelezen als "weggelaten, breng mij maar in balans".
- CSV-imports gaan door één strikte lezer. Headerdetectie stripte vroeger kolomnamen terwijl extractie de ruwe sleutels hield, zodat een header met opvulling die de docs beloofden te accepteren, mislukte als ontbrekende kolom. Nu worden namen één keer gestript, moet een toegewezen kolom precies één keer voorkomen, en mislukt een niet-gesloten aanhalingsteken met zijn regelnummer voordat er iets wordt geschreven.
- BQL laadt het exacte grootboekpad in plaats van een als URL geparsede verbindingsstring, zodat ongebruikelijke paden op dezelfde manier worden opgelost als in de rest van de CLI.
bea balance <term>telt alleen op wat het toont. Een behouden bovenliggende rekening meldt niet langer de totalen van uitgesloten zusterrekeningen, een niet-gerelateerde ongeprijsde positie laat een USD-selectie niet langer mislukken, en de envelop meldt het toegepaste filter. Een misvormd--account-patroon op rapporten sluit af met 2, als de usage-fout die het is.- Stderr in JSON-modus is altijd één object, zelfs wanneer getolereerde waarschuwingen aan de mislukking voorafgaan.
- Hosted credentials falen vroeg en consistent: een
BEA_TOKENmet witruimte wordt vóór elk verzoek geweigerd, een ingetrokken credential wordt doorcloud statusen door grootboekcommando's op dezelfde manier gemeld, enowner/namewordt gevalideerd vóór een bevestigingsprompt of een geauthenticeerde aanroep.cloud logoutlaatBEA_TOKENongemoeid, encloud ledger list --jsongeeft de pagina terug die het daadwerkelijk heeft geleverd. - De Homebrew-formule pint de exacte PyPI-artefact-URL vast, zodat een tap-installatie en een PyPI-installatie aantoonbaar dezelfde bytes zijn.
Hoe het is geverifieerd voordat jij het zag
Een release is een bewering, en de pijplijn is het bewijs. Een cli-v0.2.0-tag moet een commit op main aanwijzen waarvan de versie in pyproject.toml exact overeenkomt; de workflow weigert alles anders, inclusief prerelease-suffixen. Van daaruit:
- De volledige controlesuite draait eerst.
make check-allomvat lint, formattering, strikte mypy, dead-code-detectie, de driftcontrole van de gegenereerde referentie en de testsuite. De release-pull-request noteert 635 geslaagde tests. - De engine-lock wordt geëxporteerd en hash-vastgepind, en de brondistributie en wheel worden één keer gebouwd. Elke latere stap test precies die artefacten, geen herbouw.
- Schone installaties op drie besturingssystemen en twee Pythons. De wheel wordt geïnstalleerd via
uv toolen de sdist viapipop Linux, macOS en Windows, op Python 3.12 en 3.14, inclusief de optionele AI-extra. Een Homebrew-job installeert de sdist via een tijdelijke tap op macOS en Linux. - Publicatie is sequentieel en tokenloos. PyPI ontvangt de artefacten via trusted publishing, zodat er geen langlevend API-token bestaat dat kan lekken; de GitHub Release wordt aangemaakt met bijgevoegde publicatie-attestaties; en
Formula/bea.rbwordt naar de publieke tap gepusht met de sdist-URL en hash die PyPI daadwerkelijk heeft geleverd. - Rooktests na publicatie installeren vanaf de echte indexen. Aparte jobs installeren de vastgepinde versie vanaf PyPI en vanaf de publieke tap en draaien dezelfde klant-rooktests tegen het geïnstalleerde uitvoerbare bestand. Een mislukking daar rolt niets terug, maar betekent wel dat de release aandacht nodig heeft voordat iemand erover wordt ingelicht.
Deze post wordt geschreven aan de overkant van stap vijf.
Upgraden vanaf 0.1.0
Voer de upgrade uit via de manager die jouw kopie heeft geïnstalleerd, of laat bea het doen:
$ bea upgrade --check # meld de geïnstalleerde en nieuwste versie en het commando dat zou draaien
$ bea upgrade # brew upgrade bea, uv tool upgrade beancount-io of pipx upgrade beancount-ioNadat de manager klaar is, vernieuwt bea upgrade de beheerde engine zodat de twee gekoppeld blijven. Controleer daarna drie dingen:
- Elk script dat
bea format PATHuitvoerde om een bestand te herschrijven heeft nubea format -i PATHnodig. De oude standaard kon niet worden gepreviewd, de nieuwe wel. - Elk script dat op
formatvertrouwde om een syntaxfout op te vangen moet daarvoorbea checkaanroepen, omdat formatteren niet langer parset. - PyPI-installaties hebben eenmalig netwerk en
uvnodig voor het eerste lokale commando na het upgraden, zodat de engine kan worden ingericht. Homebrew-installaties hebben niets nodig.
Alles wat je scripts al parsen, de envelopsleutels, de decimale strings en de exitcodes, is ongewijzigd. Het veld bea in de envelop leest nu 0.2.0.
Wat deze release niet doet
- Hosted targeting is niet geïmplementeerd. Er is geen
--ledger-vlag; lokale commando's lezen lokale bestanden en uploaden er nooit impliciet één. Hosted grootboeken worden beheerd onderbea clouden bewerkt als git-clones. bea askheeft nog steeds deask-extra en Beancount.io-credentials nodig, en ondersteunt--jsonniet. De standaardinstallatie bevat geen AI-afhankelijkheden.- Beangulp en Beanprice zijn opt-in, en Beangulp heeft de systeembibliotheek
libmagicnodig.bea import --csvdekt bankexports zonder een van beide. - Doorgestuurde native commando's zenden de envelop niet uit. Als je gestructureerde uitvoer van een doctor-bewerking nodig hebt, is dat een verzoek dat we graag horen.
Sinds de tag heeft main al de eerste ronde QA op 0.2.0 opgepikt, en die rijdt mee met de volgende release: bea format leest stdin als filter en zijn -o FILE-modus antwoordt met een envelop die noemt wat het heeft geschreven; --json check weigert vlaggen die alleen voor bean-check gelden, en --json wordt ronduit geweigerd op doctor, example en treeify, zodat een script native tekst niet voor een envelop kan houden; --json query -o FILE schrijft de envelop atomair naar het bestand, met --numberify ook toegepast op JSON; bea engine status noemt welke engine-laag bedient; een BQL-query die met een commentaar opent, draait; native pass-through --help werkt voordat de engine is ingericht; en de .output van de query-shell herstelt de oorspronkelijke stream na een mislukte omleiding.
Waar nu naartoe
- CLI-snelstart: installeren, eerste grootboek, eerste aankoop, eerste saldocontrole.
- Je eerste maand met bea: van
inittot een afgestemd maandafsluitingsrapport. - Bankexports importeren: het Python-loze CSV-pad, regelbestanden en Python-importers.
- Boekhouding automatiseren met bea: het grootboek vinden, de envelop lezen, vertakken op exitcodes, inplannen.
- Beancount CLI-referentie: elk commando, elke optie, omgevingsvariabele en exitcode, gecontroleerd tegen de gegenereerde referentie van de CLI.
- Geef je AI-agent een grootboek: de agent-first walkthrough uit de lancering van 0.1.0.
- Changelog: elke release, nieuwste eerst.
Houd je boeken als code
Een toolchain die je in één regel installeert, is een toolchain die je aan iedereen kunt overhandigen: een medeoprichter, een boekhouder, een CI-runner, een AI-agent. Beancount.io biedt plain-text boekhouding die transparant, versiebeheerd en reproduceerbaar blijft, met bea als het commando dat een lokaal grootboek eerlijk houdt en de hosted dienst als de plek waar je team, je telefoon en je assistent dezelfde boeken tegenkomen. Installeer bea en voer je eerste controle uit, en als de release iets doet wat je niet verwachtte, is de GitHub-repository de plek waar we dat willen horen.





