Se hai mai consegnato a un collega, a un portatile nuovo o a un cron job notturno una configurazione Beancount funzionante, sai che la contabilità non è mai stata la parte difficile. La parte difficile era la toolchain: un Python della versione giusta, bean-check e bean-query nel path, una libreria di reportistica tirata dentro per un solo bilancio e un formattatore che riscrive i tuoi file nel momento stesso in cui gli fai una domanda. bea 0.2.0, pubblicata il 12 settembre 2026, sostituisce quella checklist con una sola installazione. Il comando bea ora porta con sé l'intera toolchain nativa di Beancount, la esegue dentro un motore gestito che predispone da solo e mantiene il contratto leggibile dalle macchine su cui script e agenti AI già fanno affidamento.
Queste sono le note di rilascio della 0.2.0, scritte nel modo in cui tracciamo un rilascio internamente: cosa è stato distribuito, cosa è cambiato sotto il cofano, come è stato verificato prima di raggiungere un indice di pacchetti, cosa deliberatamente non fa ancora e come aggiornare. Se invece cerchi la storia del primo avvio, il post di lancio della 0.1.0 e l'avvio rapido della CLI sono le letture più brevi.
Il rilascio in sintesi
Due canali pubblicano lo stesso comando. Scegline uno, poi verifica che risponda con la sua versione:
$ brew install bex-co/tap/bea # macOS e Linuxbrew
$ uv tool install beancount-io # ovunque ci siano uv e Python 3.12 o successivo
$ 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.14La scheda del rilascio 0.2.0: il tag e la data di pubblicazione, le versioni di Beancount e Beanquery che il motore gestito ancora, le due funzionalità opzionali del motore e le versioni di Python su cui il rilascio è stato installato e testato.
| Campo | Valore |
|---|---|
| Versione | 0.2.0, tag cli-v0.2.0, pubblicata su PyPI e sul tap Homebrew bex-co/homebrew-tap il 2026-09-12 |
| Rilascio precedente | 0.1.0, taggata il 2026-09-09, tre giorni prima |
| Insieme delle modifiche | 27 commit che toccano la CLI, 119 file modificati, circa 12,300 righe aggiunte e 2,100 rimosse |
| Versioni ancorate | Beancount 3.2.3 e Beanquery 0.2.0 nel motore di base; Beangulp 0.2.0 e Beanprice 2.1.0 come funzionalità opzionali |
| In breve | Ogni strumento nativo di Beancount sotto un unico prefisso, servito da un motore gestito; l'envelope JSON e il contratto sui codici di uscita della 0.1.0 sono invariati |
Cosa è cambiato sotto il cofano: il motore gestito
Nella 0.1.0, bea importava Beancount nel proprio processo, come farebbe qualsiasi strumento Python. Funzionava, ma rendeva il grafo delle dipendenze della CLI il grafo delle dipendenze di Beancount, e lasciava «installa prima Beancount» come passaggio non scritto in ogni guida.
La 0.2.0 traccia una linea nel mezzo del programma. Il frontend bea, la parte che possiede i comandi, le opzioni e il rendering, non carica mai Beancount, Beanquery né il codice di reportistica di Fava incorporato. Il lavoro sul libro mastro locale gira in un motore gestito: un ambiente Python separato che bea predispone a partire da un lock con hash ancorati e avvia come interprete figlio. Il frontend invia una richiesta JSON attraverso quel confine e renderizza ciò che torna indietro. Non devi installare Beancount, mettere gli strumenti bean-* nel path né chiederti quale Python abbiano trovato.
Il modo in cui il motore arriva dipende dal canale:
- Homebrew crea gli ambienti del frontend e del motore durante l'installazione. I comandi locali usano il motore interno al keg senza ulteriori download.
- PyPI (
uv tool installo pipx) predispone il motore al primo utilizzo. Il primo comando locale che ne ha bisogno scarica la combinazione ancorata, cosa che richiede accesso alla rete euvnel path una sola volta. I comandi successivi lo riutilizzano offline da~/.local/share/bea/engine/<version>, oppure sottoXDG_DATA_HOMEse lo hai impostato.
Da questo design derivano tre proprietà, e ognuna elimina un ticket di supporto che abbiamo già visto:
- Gli aggiornamenti restano accoppiati.
bea upgradeaffida l'aggiornamento al gestore di pacchetti che ha installato questa copia, poi ricostruisce il motore corrispondente, così frontend e motore non possono mai scivolare su versioni diverse. - Un motore rotto si ripara da solo. Se la predisposizione fallisce a metà, l'ambiente gestito viene scartato e ricostruito al successivo tentativo riuscito. Eventuali binari
bean-checksparsi altrove nel path vengono ignorati anziché raccolti per sbaglio. - I componenti opzionali pesanti restano opzionali. Il framework di importazione Beangulp ha bisogno della libreria di sistema
libmagic, e Beanprice si porta dietro le dipendenze per il recupero delle quotazioni. Nessuno dei due è nel motore di base. Li abiliti esplicitamente, solo dentro il motore.
$ bea engine status
$ bea engine enable beangulp # helper di importazione; richiede la libreria di sistema libmagic
$ bea engine enable beanprice # recupero quotazioni di bean-pricebea engine status riferisce se il motore è predisposto e quali funzionalità opzionali sono abilitate, e non ha bisogno della rete per dirlo. Se la predisposizione al primo utilizzo fallisce, sistema la rete o uv e riesegui un qualsiasi comando locale come bea check. Non fare pip install beancount accanto a esso: il frontend non lo userà.
Ogni strumento nativo, un solo prefisso
Il motore è il meccanismo. Il cambiamento visibile all'utente è la parità: ogni eseguibile distribuito dal progetto Beancount upstream ha ora una controparte bea, con gli stessi argomenti inoltrati e lo stesso output preservato.
$ bea check # bean-check, più l'envelope --json di bea
$ bea format main.bean -o clean.bean # bean-format: stdout per default, -i riscrive
$ bea query "SELECT account, sum(position) GROUP BY account"
$ bea doctor context main.bean 2026-01-02 # tutte le undici operazioni di bean-doctor
$ bea example --seed 1 -o example.beancount # bean-example
$ bea treeify < balances.txt # treeify
$ bea ingest identify --config ingest.py inbox # Beangulp, dopo engine enable
$ bea price -e USD:yahoo/AAPL # bean-price, dopo engine enablebean-checkbea checkbean-formatbea formatbean-querybea querybean-doctorbea doctorbean-examplebea exampletreeifybea treeifybeangulpbea ingest bea engine enable beangulpbean-pricebea price bea engine enable beanpriceLa mappa della parità: i sei eseguibili nativi di Beancount sopra la linea tratteggiata funzionano subito; i due sotto vengono inoltrati a Beangulp e Beanprice una volta abilitata quella funzionalità nel motore.
Alcuni di questi meritano più di una riga in una tabella.
bea check è bean-check con l'envelope JSON di bea sovrapposto: la stessa validazione, gli stessi messaggi di errore e, sotto --json, gli stessi campi valid ed errors che gli script già analizzano.
bea format ha cambiato comportamento, ed è l'unico cambiamento di questo rilascio che può sorprendere uno script. Nella 0.1.0, bea format PATH riscriveva il file. Ora stampa il testo formattato su stdout e lascia il file intatto. --in-place (-i) è ciò che riscrive, --output FILE (-o) scrive altrove, --check è il gate per la CI che esce con 1 quando i file hanno bisogno di formattazione, e --dry-run elenca cosa cambierebbe. Questo segue bean-format, il cui default è quello sicuro: un comando che legge un percorso e lo riscrive silenziosamente non può essere provato prima. La formattazione è una trasformazione del testo, non un parsing, quindi non rifiuta più un file con un errore di sintassi; allinea ciò che riconosce e lascia il resto. Esegui bea check per la validità.
bea query ha acquisito l'intera superficie nativa. Accetta BQL come argomento, da stdin o nella shell interattiva, che ora è la shell Beanquery upstream avviata come processo figlio con i suoi comandi .format, .output, .run e .set intatti. --format seleziona il rendering text, csv o beancount, --numberify divide gli importi in una colonna per valuta, -o scrive su file e --source URI passa direttamente una sorgente Beanquery nativa.
bea doctor espone tutte le undici operazioni di bean-doctor: lex, parse, roundtrip, directories, list-options, print-options, context, linked, region, missing-open e display-context. Se hai mai fatto il debug di un problema di booking con bean-doctor context, è lo stesso strumento allo stesso indirizzo.
bea example e bea treeify sono il generatore nativo e il renderer nativo ad albero, inoltrati così come sono.
bea ingest e bea price inoltrano rispettivamente a identify, extract e archive di Beangulp e a bean-price, dopo bea engine enable. Il percorso CSV senza Python, bea import --csv, non ha bisogno di nessuno dei due ed è invariato.
Una regola lega insieme i comandi inoltrati: doctor, example, treeify, price e ingest passano i loro argomenti all'upstream senza modifiche e conservano l'output e lo stato di uscita dell'upstream. Ciò significa anche che prendono il libro mastro come proprio argomento posizionale, come in bea doctor lex main.bean, anziché tramite il --file globale. L'envelope e le categorie di codici di uscita qui sotto descrivono i comandi propri di bea.
Il contratto su cui gli script possono continuare a contare
Nulla della superficie leggibile dalle macchine si è spostato. Il --json globale mette ancora un solo envelope su stdout con bea, target, data e truncated, più limit sulle liste limitate e page sulle liste hosted paginate. Gli importi sono stringhe decimali, mai float, e le date sono ISO YYYY-MM-DD. --json implica --no-input; lo stesso fa uno stdin non terminale o una variabile CI con valore vero, così un job non presidiato non aspetta mai un umano. --strict rifiuta le risposte parziali anche in un terminale, e il --allow-errors di ogni comando di lettura consente di riammetterle.
Un fallimento non scrive nulla su stdout ed esattamente un oggetto su 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)"]
}
}I cinque codici di uscita e la stringa category che ciascuno porta nell'oggetto di errore JSON. Uno script si dirama sul numero; un umano legge la categoria.
| Codice | Categoria | Significato |
|---|---|---|
| 0 | nessuna | Successo, incluse le anteprime e i salti intenzionali di duplicati |
| 1 | validation | Errore del libro mastro o di validazione, e il contenitore generico per ogni altro errore a runtime |
| 2 | usage | Argomenti errati, un target mancante o in eccesso, oppure input richiesto sotto --no-input |
| 3 | auth | Errore di autenticazione o di permessi, inclusa una destinazione di sola lettura |
| 4 | conflict | Una modifica concorrente, un'importazione che richiede la revisione dei duplicati o una scrittura dal risultato sconosciuto |
Due dettagli contano per chiunque riprovi dopo un fallimento. Un'uscita diversa da zero non significa universalmente che nulla sia cambiato: add transactions --partial può scrivere le righe accettate, format -i su più file può riscriverne alcuni prima di fallire su uno, e cloud ledger create --clone può creare il libro mastro prima che il clone fallisca. Leggi error.result prima di riprovare una mutazione. E i comandi hosted mappano lo stato HTTP del server sulla stessa tabella, conservando il messaggio del server: 401 e 403 escono con 3, 400 esce con 2, 409 esce con 4 e tutto il resto, incluso il rate limiting, esce con 1. Una scrittura di cui la CLI non può conoscere l'esito, come un timeout a metà di una cancellazione, esce con 4 e lo dice invece di tirare a indovinare.
La guida all'automazione percorre una pipeline jq attraverso questo envelope da un capo all'altro.
Le correzioni arrivate insieme
Un rilascio di parità è anche un'occasione per chiudere i difetti che un primo rilascio fa emergere. Queste sono arrivate tra i due tag, ciascuna con un test di regressione:
- I numeri vengono scritti come testo a virgola fissa, mai in notazione scientifica, inclusi i saldi di apertura che
bea initgenera. Un libro mastro che dice1E+3è tecnicamente valido e praticamente illeggibile. - I lotti di costo sopravvivono alla serializzazione JSON con date ed etichette intatte, e le etichette dei lotti vengono correttamente escapate quando una transazione viene scritta.
- Le registrazioni esplicite a zero sono importi reali durante l'importazione, anziché essere lette come «omessa, per favore bilanciami».
- Le importazioni CSV passano da un unico lettore rigoroso. La scoperta delle intestazioni ripuliva i nomi delle colonne mentre l'estrazione conservava le chiavi grezze, così un'intestazione con spaziatura che la documentazione prometteva di accettare falliva come colonna mancante. Ora i nomi vengono ripuliti una sola volta, una colonna mappata deve comparire esattamente una volta e una virgoletta non chiusa fallisce con il suo numero di riga prima che qualcosa venga scritto.
- BQL carica il percorso esatto del libro mastro anziché una stringa di connessione analizzata come URL, così i percorsi insoliti si risolvono nello stesso modo del resto della CLI.
bea balance <term>somma solo ciò che mostra. Un genitore mantenuto non riporta più i totali dei fratelli esclusi, una posizione non correlata senza prezzo non fa più fallire una selezione in USD, e l'envelope riporta il filtro applicato. Un pattern--accountmalformato sui report esce con 2, l'errore d'uso che è.- Lo stderr in modalità JSON è sempre un solo oggetto, anche quando avvisi tollerati precedono il fallimento.
- Le credenziali hosted falliscono presto e in modo coerente: un
BEA_TOKENcontenente spazi viene rifiutato prima di qualsiasi richiesta, una credenziale revocata viene segnalata nello stesso modo dacloud statuse dai comandi sul libro mastro, eowner/nameviene validato prima di una richiesta di conferma o di una chiamata autenticata.cloud logoutlasciaBEA_TOKENintatto, ecloud ledger list --jsonrestituisce la pagina che ha effettivamente servito. - La formula Homebrew ancora l'URL esatto dell'artefatto PyPI, così un'installazione dal tap e una da PyPI sono dimostrabilmente gli stessi byte.
Come è stato verificato prima che lo vedessi
Un rilascio è un'affermazione, e la pipeline è la prova. Un tag cli-v0.2.0 deve indicare un commit su main la cui versione in pyproject.toml corrisponda esattamente; il workflow rifiuta qualsiasi altra cosa, inclusi i suffissi di prerelease. Da lì:
- Prima gira l'intera suite di controlli.
make check-allcopre lint, formattazione, mypy in modalità strict, il rilevamento del codice morto, il controllo di deriva del riferimento generato e la suite di test. La pull request di rilascio registra 635 test superati. - Il lock del motore viene esportato e ancorato con hash, e la distribuzione sorgente e la wheel vengono costruite una sola volta. Ogni passaggio successivo testa esattamente quegli artefatti, non una ricompilazione.
- Installazioni pulite su tre sistemi operativi e due Python. La wheel viene installata tramite
uv toole la sdist tramitepipsu Linux, macOS e Windows, su Python 3.12 e 3.14, incluso l'extra opzionale AI. Un job Homebrew installa la sdist tramite un tap temporaneo su macOS e Linux. - La pubblicazione è sequenziale e senza token. PyPI riceve gli artefatti tramite trusted publishing, così non esiste alcun token API a lunga vita che possa trapelare; la GitHub Release viene creata con le attestazioni di pubblicazione allegate; e
Formula/bea.rbviene pushata sul tap pubblico con l'URL e l'hash della sdist che PyPI ha effettivamente servito. - Gli smoke test post-pubblicazione installano dagli indici reali. Job separati installano la versione ancorata da PyPI e dal tap pubblico ed eseguono gli stessi smoke test lato cliente contro l'eseguibile installato. Un fallimento lì non annulla nulla, ma significa che il rilascio ha bisogno di attenzione prima che qualcuno ne venga informato.
Questo post viene scritto dall'altra parte del passo cinque.
Aggiornare dalla 0.1.0
Esegui l'aggiornamento tramite il gestore che ha installato la tua copia, oppure lascia che lo faccia bea:
$ bea upgrade --check # riporta la versione installata, l'ultima disponibile e il comando che verrebbe eseguito
$ bea upgrade # brew upgrade bea, uv tool upgrade beancount-io oppure pipx upgrade beancount-ioQuando il gestore ha finito, bea upgrade aggiorna il motore gestito così che i due restino accoppiati. Poi controlla tre cose:
- Qualsiasi script che eseguiva
bea format PATHper riscrivere un file ora ha bisogno dibea format -i PATH. Il vecchio default non poteva essere visto in anteprima, il nuovo sì. - Qualsiasi script che si affidava a
formatper intercettare un errore di sintassi dovrebbe chiamarebea checkper quello, perché la formattazione non esegue più il parsing. - Le installazioni da PyPI hanno bisogno della rete e di
uvuna sola volta per il primo comando locale dopo l'aggiornamento, così che il motore possa essere predisposto. Le installazioni Homebrew non hanno bisogno di nulla.
Tutto ciò che i tuoi script già analizzano, le chiavi dell'envelope, le stringhe decimali e i codici di uscita, è invariato. Il campo bea nell'envelope ora riporta 0.2.0.
Cosa questo rilascio non fa
- Il targeting hosted non è implementato. Non esiste un flag
--ledger; i comandi locali leggono file locali e non ne caricano mai uno implicitamente. I libri mastri hosted si gestiscono sottobea cloude si lavorano come cloni git. bea askrichiede ancora l'extraaske le credenziali Beancount.io, e non supporta--json. L'installazione predefinita non contiene dipendenze AI.- Beangulp e Beanprice sono opzionali, e Beangulp ha bisogno della libreria di sistema
libmagic.bea import --csvcopre gli export bancari senza nessuno dei due. - I comandi nativi inoltrati non emettono l'envelope. Se hai bisogno di output strutturato da un'operazione di doctor, è una richiesta che vorremmo sentire.
Dal tag in poi, main ha già raccolto il primo giro di QA sulla 0.2.0, che viaggerà con il prossimo rilascio: bea format legge stdin come filtro e la sua modalità -o FILE risponde con un envelope che nomina ciò che ha scritto; --json check rifiuta i flag esclusivi di bean-check, e --json viene rifiutato del tutto su doctor, example e treeify così che uno script non possa confondere testo nativo con un envelope; --json query -o FILE scrive l'envelope nel file atomicamente, con --numberify applicato anche al JSON; bea engine status indica quale livello di motore sta servendo; una query BQL che inizia con un commento viene eseguita; il --help nativo passante funziona prima che il motore sia predisposto; e l'.output della shell di query ripristina lo stream originale dopo una redirezione fallita.
Dove andare adesso
- Avvio rapido della CLI: installazione, primo libro mastro, primo acquisto, primo controllo del saldo.
- Il tuo primo mese con bea: da
inita un report di fine mese riconciliato. - Importare gli export bancari: il percorso CSV senza Python, i file di regole e gli importer Python.
- Automatizzare la contabilità con bea: risolvere il libro mastro, leggere l'envelope, diramarsi sui codici di uscita, pianificare.
- Riferimento della CLI Beancount: ogni comando, opzione, variabile d'ambiente e codice di uscita, verificato contro il riferimento generato dalla CLI.
- Dai un libro mastro al tuo agente AI: la guida pensata per gli agenti dal lancio della 0.1.0.
- Changelog: ogni rilascio, dal più recente.
Tieni i tuoi libri come codice
Una toolchain che puoi installare con una riga è una toolchain che puoi consegnare a chiunque: un cofondatore, un contabile, un runner CI, un agente AI. Beancount.io offre una contabilità in testo semplice che resta trasparente, versionata e riproducibile, con bea come comando che tiene onesto un libro mastro locale e il servizio hosted come luogo in cui il tuo team, il tuo telefono e il tuo assistente incontrano gli stessi libri. Installa bea ed esegui il tuo primo controllo, e se il rilascio fa qualcosa che non ti aspettavi, il repository GitHub è dove vogliamo saperlo.





