Se você já entregou a um colega, a um laptop novo ou a um cron job noturno uma configuração funcional do Beancount, sabe que a contabilidade nunca foi a parte difícil. A parte difícil era a toolchain: um Python compatível, bean-check e bean-query no path, uma biblioteca de relatórios puxada para um único balanço patrimonial e um formatador que reescreve seus arquivos no momento em que você lhe faz uma pergunta. O bea 0.2.0, lançado em 12 de setembro de 2026, substitui essa lista de verificação por uma única instalação. O comando bea agora carrega a toolchain nativa completa do Beancount, executa-a dentro de um motor gerenciado que ele mesmo provisiona e mantém o contrato legível por máquina do qual scripts e agentes de IA já dependem.
Esta é a nota de lançamento do 0.2.0, escrita do jeito que acompanhamos um lançamento internamente: o que foi entregue, o que mudou por baixo, como foi verificado antes de chegar a um índice de pacotes, o que ele deliberadamente ainda não faz e como atualizar. Se você prefere a história da primeira execução, o post de lançamento do 0.1.0 e o guia rápido da CLI são as leituras mais curtas.
O lançamento em resumo
Dois canais publicam o mesmo comando. Escolha um e depois confirme que ele responde com sua versão:
$ brew install bex-co/tap/bea # macOS e Linuxbrew
$ uv tool install beancount-io # em qualquer lugar com uv e Python 3.12 ou mais recente
$ 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.14O cartão de lançamento do 0.2.0: a tag e a data de publicação, as versões do Beancount e do Beanquery que o motor gerenciado fixa, os dois recursos opcionais do motor e as versões do Python nas quais o lançamento foi instalado e testado.
| Campo | Valor |
|---|---|
| Versão | 0.2.0, tag cli-v0.2.0, publicada no PyPI e no tap Homebrew bex-co/homebrew-tap em 2026-09-12 |
| Lançamento anterior | 0.1.0, com tag de 2026-09-09, três dias antes |
| Conjunto de mudanças | 27 commits tocando a CLI, 119 arquivos alterados, cerca de 12.300 linhas adicionadas e 2.100 removidas |
| Versões fixadas do motor | Beancount 3.2.3 e Beanquery 0.2.0 no motor base; Beangulp 0.2.0 e Beanprice 2.1.0 como recursos opcionais |
| Destaque | Toda ferramenta nativa do Beancount sob um único prefixo, servida por um motor gerenciado; o envelope JSON e o contrato de códigos de saída do 0.1.0 permanecem inalterados |
O que mudou por baixo: o motor gerenciado
No 0.1.0, o bea importava o Beancount para dentro do seu próprio processo, como qualquer ferramenta Python faria. Funcionava, mas fazia do grafo de dependências da CLI o grafo de dependências do Beancount, e deixava "instale o Beancount primeiro" como um passo não escrito em todos os guias.
O 0.2.0 traça uma linha pelo meio do programa. O frontend bea, a parte que é dona dos comandos, das opções e da renderização, nunca carrega o Beancount, o Beanquery ou o código de relatórios do Fava incorporado. O trabalho com o razão local roda em um motor gerenciado: um ambiente Python separado que o bea provisiona a partir de um lock fixado por hash e inicia como interpretador filho. O frontend envia uma requisição JSON através dessa fronteira e renderiza o que volta. Você não instala o Beancount, não coloca ferramentas bean-* no seu path nem pensa em qual Python elas encontraram.
Como o motor chega depende do canal:
- Homebrew cria os ambientes do frontend e do motor durante a instalação. Comandos locais usam o motor local do keg, sem nenhum download adicional.
- PyPI (
uv tool installou pipx) provisiona no primeiro uso. O primeiro comando local que precisa do motor baixa a combinação fixada, o que exige acesso à rede euvno path uma única vez. Os comandos seguintes o reutilizam offline a partir de~/.local/share/bea/engine/<version>, ou sobXDG_DATA_HOMEse você o definir.
Três propriedades decorrem desse design, e cada uma elimina um chamado de suporte que já vimos:
- As atualizações permanecem pareadas.
bea upgradedelega a atualização ao gerenciador de pacotes que instalou esta cópia e depois reconstrói o motor correspondente, de modo que frontend e motor nunca podem divergir para versões diferentes. - Um motor quebrado se cura sozinho. Se um provisionamento falha no meio do caminho, o ambiente gerenciado é descartado e reconstruído na próxima tentativa bem-sucedida. Binários
bean-checkperdidos em outro lugar do path são ignorados em vez de capturados por acidente. - As peças opcionais pesadas continuam opcionais. O framework de importação Beangulp precisa da biblioteca de sistema
libmagic, e o Beanprice traz dependências de busca de cotações. Nenhum dos dois está no motor base. Você os habilita explicitamente, apenas no motor.
$ bea engine status
$ bea engine enable beangulp # auxiliares de ingestão; precisa da biblioteca de sistema libmagic
$ bea engine enable beanprice # busca de cotações do bean-pricebea engine status informa se o motor está provisionado e quais recursos opcionais estão habilitados, e não precisa de rede para dizê-lo. Se um provisionamento de primeiro uso falhar, corrija a rede ou o uv e execute novamente qualquer comando local, como bea check. Não faça pip install beancount ao lado dele: o frontend não vai usá-lo.
Toda ferramenta nativa, um único prefixo
O motor é o mecanismo. A mudança visível ao usuário é a paridade: todo executável que o projeto Beancount upstream distribui agora tem um equivalente bea, com os mesmos argumentos repassados e a mesma saída preservada.
$ bea check # bean-check, mais o envelope --json do bea
$ bea format main.bean -o clean.bean # bean-format: stdout por padrão, -i reescreve
$ bea query "SELECT account, sum(position) GROUP BY account"
$ bea doctor context main.bean 2026-01-02 # todas as onze operações do bean-doctor
$ bea example --seed 1 -o example.beancount # bean-example
$ bea treeify < balances.txt # treeify
$ bea ingest identify --config ingest.py inbox # Beangulp, após engine enable
$ bea price -e USD:yahoo/AAPL # bean-price, após engine enablebean-checkbea checkbean-formatbea formatbean-querybea querybean-doctorbea doctorbean-examplebea exampletreeifybea treeifybeangulpbea ingest bea engine enable beangulpbean-pricebea price bea engine enable beanpriceO mapa de paridade: os seis executáveis nativos do Beancount acima da linha tracejada funcionam de imediato; os dois abaixo dela são repassados ao Beangulp e ao Beanprice depois que você habilita esse recurso no motor.
Alguns deles merecem mais do que uma linha em uma tabela.
bea check é o bean-check com o envelope JSON do bea sobreposto: a mesma validação, as mesmas mensagens de erro e, sob --json, os mesmos campos valid e errors que os scripts já interpretam.
bea format mudou de comportamento, e é a única mudança deste lançamento que pode surpreender um script. No 0.1.0, bea format PATH reescrevia o arquivo. Agora ele imprime o texto formatado em stdout e deixa o arquivo intocado. --in-place (-i) é o que reescreve, --output FILE (-o) grava em outro lugar, --check é o portão de CI que sai com 1 quando há arquivos precisando de formatação, e --dry-run lista o que mudaria. Isso segue o bean-format, cujo padrão é o seguro: um comando que lê um caminho e o reescreve silenciosamente não pode ser experimentado antes. Formatar é uma transformação de texto, não um parse, então ele não recusa mais um arquivo com erro de sintaxe; alinha o que reconhece e deixa o resto como está. Execute bea check para validar.
bea query ganhou toda a superfície nativa. Aceita BQL como argumento, via stdin ou no shell interativo, que agora é o shell upstream do Beanquery iniciado como processo filho, com seus comandos .format, .output, .run e .set intactos. --format seleciona a renderização em text, csv ou beancount, --numberify divide os valores em uma coluna por moeda, -o grava em um arquivo e --source URI repassa uma fonte nativa do Beanquery diretamente.
bea doctor expõe todas as onze operações do bean-doctor: lex, parse, roundtrip, directories, list-options, print-options, context, linked, region, missing-open e display-context. Se você já depurou um problema de booking de lotes com bean-doctor context, é a mesma ferramenta no mesmo endereço.
bea example e bea treeify são o gerador nativo e o renderizador de árvore nativo, repassados como estão.
bea ingest e bea price repassam para identify, extract e archive do Beangulp e para o bean-price, respectivamente, após bea engine enable. O caminho CSV sem Python, bea import --csv, não precisa de nenhum dos dois e permanece inalterado.
Uma regra une os comandos repassados: doctor, example, treeify, price e ingest entregam seus argumentos ao upstream sem alteração e mantêm a saída e o status de saída do upstream. Isso também significa que eles recebem o razão como seu próprio argumento posicional, como em bea doctor lex main.bean, em vez de pelo --file global. O envelope e as categorias de códigos de saída abaixo descrevem os comandos próprios do bea.
O contrato em que os scripts podem continuar confiando
Nada na superfície legível por máquina mudou de lugar. O --json global ainda coloca um único envelope em stdout com bea, target, data e truncated, mais limit em listas limitadas e page em listas hospedadas paginadas. Valores são strings decimais, nunca floats, e datas são ISO YYYY-MM-DD. --json implica --no-input; o mesmo vale para um stdin que não é terminal ou para uma variável CI verdadeira, de modo que um job não supervisionado nunca fica esperando por um humano. --strict recusa respostas parciais mesmo em um terminal, e o --allow-errors de cada comando de leitura permite optar de volta.
Uma falha não escreve nada em stdout e exatamente um objeto em 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)"]
}
}Os cinco códigos de saída e a string category que cada um carrega no objeto de erro JSON. Um script ramifica pelo número; um humano lê a categoria.
| Código | Categoria | Significado |
|---|---|---|
| 0 | nenhuma | Sucesso, incluindo pré-visualizações e omissões intencionais de duplicatas |
| 1 | validation | Erro de razão ou de validação, e o coletor geral para qualquer outra falha em tempo de execução |
| 2 | usage | Argumentos inválidos, um alvo ausente ou a mais, ou entrada necessária sob --no-input |
| 3 | auth | Falha de autenticação ou de permissão, incluindo um destino somente leitura |
| 4 | conflict | Uma alteração concorrente, uma importação que precisa de revisão de duplicatas, ou uma gravação cujo resultado é desconhecido |
Dois detalhes importam para quem repete a operação após uma falha. Uma saída diferente de zero não significa universalmente que nada mudou: add transactions --partial pode gravar as linhas aceitas, format -i sobre vários arquivos pode reescrever alguns antes de falhar em um, e cloud ledger create --clone pode criar o razão antes que o clone falhe. Leia error.result antes de repetir uma mutação. E os comandos hospedados mapeiam o status HTTP do servidor para a mesma tabela, preservando a própria mensagem do servidor: 401 e 403 saem com 3, 400 sai com 2, 409 sai com 4, e todo o resto, incluindo limitação de taxa, sai com 1. Uma gravação cujo resultado a CLI não pode conhecer, como um timeout no meio de uma exclusão, sai com 4 e diz isso em vez de adivinhar.
O guia de automação percorre um pipeline jq por esse envelope de ponta a ponta.
Correções que vieram junto
Um lançamento de paridade também é uma oportunidade de fechar os defeitos que um primeiro lançamento revela. Estas entraram entre as duas tags, cada uma com um teste de regressão:
- Números são escritos como texto de ponto fixo, nunca em notação científica, incluindo os saldos de abertura que
bea initrenderiza. Um razão que diz1E+3é tecnicamente válido e praticamente ilegível. - Lotes de custo sobrevivem à serialização JSON com suas datas e rótulos intactos, e os rótulos de lote são escapados corretamente quando uma transação é gravada.
- Postings explícitos com zero são valores reais durante a importação, em vez de serem lidos como "omitido, por favor me balanceie".
- Importações CSV passam por um único leitor estrito. A descoberta de cabeçalho costumava remover os espaços dos nomes das colunas enquanto a extração mantinha as chaves brutas, então um cabeçalho com espaços extras que a documentação prometia aceitar falhava como coluna ausente. Agora os nomes têm os espaços removidos uma única vez, uma coluna mapeada deve aparecer exatamente uma vez, e uma aspa não fechada falha com o número da linha antes que qualquer coisa seja gravada.
- O BQL carrega o caminho exato do razão em vez de uma string de conexão interpretada como URL, de modo que caminhos incomuns são resolvidos do mesmo jeito que o resto da CLI os resolve.
bea balance <term>totaliza apenas o que mostra. Um pai retido não informa mais os totais de irmãos excluídos, uma posição sem preço não relacionada não faz mais falhar uma seleção em USD, e o envelope informa o filtro que foi aplicado. Um padrão--accountmalformado em relatórios sai com 2, o erro de uso que ele é.- O stderr em modo JSON é sempre um único objeto, mesmo quando avisos tolerados precedem a falha.
- Credenciais hospedadas falham cedo e de forma consistente: um
BEA_TOKENcontendo espaços em branco é rejeitado antes de qualquer requisição, uma credencial revogada é relatada da mesma maneira porcloud statuse pelos comandos de razão, eowner/nameé validado antes de um prompt de confirmação ou de uma chamada autenticada.cloud logoutdeixaBEA_TOKENem paz, ecloud ledger list --jsonecoa a página que realmente serviu. - A fórmula do Homebrew fixa a URL exata do artefato do PyPI, de modo que uma instalação pelo tap e uma instalação pelo PyPI são comprovadamente os mesmos bytes.
Como foi verificado antes de chegar até você
Um lançamento é uma afirmação, e o pipeline é a evidência. Uma tag cli-v0.2.0 precisa nomear um commit em main cuja versão no pyproject.toml coincida exatamente; o workflow recusa qualquer outra coisa, incluindo sufixos de pré-lançamento. A partir daí:
- A suíte completa de verificações roda primeiro.
make check-allcobre lint, formatação, mypy estrito, detecção de código morto, a verificação de drift da referência gerada e a suíte de testes. O pull request do lançamento registra 635 testes passando. - O lock do motor é exportado e fixado por hash, e a distribuição de código-fonte e o wheel são construídos uma única vez. Cada passo posterior testa exatamente esses artefatos, não uma reconstrução.
- Instalações limpas em três sistemas operacionais e dois Pythons. O wheel é instalado via
uv toole o sdist viapipno Linux, macOS e Windows, em Python 3.12 e 3.14, incluindo o extra opcional de IA. Um job do Homebrew instala o sdist por meio de um tap temporário no macOS e no Linux. - A publicação é sequencial e sem tokens. O PyPI recebe os artefatos por publicação confiável, então não existe nenhum token de API de longa duração para vazar; o GitHub Release é criado com atestações de publicação anexadas; e
Formula/bea.rbé enviado ao tap público com a URL e o hash do sdist que o PyPI de fato serviu. - Testes de fumaça pós-publicação instalam a partir dos índices reais. Jobs separados instalam a versão fixada a partir do PyPI e do tap público e executam os mesmos testes de fumaça de cliente contra o executável instalado. Uma falha ali não reverte nada, mas significa que o lançamento precisa de atenção antes que alguém seja avisado sobre ele.
Este post está sendo escrito do outro lado do passo cinco.
Atualizando a partir do 0.1.0
Execute a atualização pelo gerenciador que instalou sua cópia, ou deixe o bea fazer isso:
$ bea upgrade --check # informa as versões instalada e mais recente e o comando que seria executado
$ bea upgrade # brew upgrade bea, uv tool upgrade beancount-io ou pipx upgrade beancount-ioDepois que o gerenciador termina, bea upgrade atualiza o motor gerenciado para que os dois permaneçam pareados. Em seguida, verifique três coisas:
- Qualquer script que executava
bea format PATHpara reescrever um arquivo agora precisa debea format -i PATH. O padrão antigo não podia ser pré-visualizado; o novo pode. - Qualquer script que dependia do
formatpara capturar um erro de sintaxe deve chamarbea checkpara isso, porque a formatação não faz mais parse. - Instalações via PyPI precisam de rede e de
uvuma única vez para o primeiro comando local após a atualização, para que o motor possa ser provisionado. Instalações via Homebrew não precisam de nada.
Tudo o que seus scripts já interpretam, as chaves do envelope, as strings decimais e os códigos de saída, permanece inalterado. O campo bea no envelope agora lê 0.2.0.
O que este lançamento não faz
- O direcionamento a razões hospedados não está implementado. Não existe flag
--ledger; comandos locais leem arquivos locais e nunca enviam um implicitamente. Razões hospedados são gerenciados sobbea cloude trabalhados como clones git. bea askainda precisa do extraaske de credenciais do Beancount.io, e não suporta--json. A instalação padrão não carrega dependências de IA.- Beangulp e Beanprice são opcionais, e o Beangulp precisa da biblioteca de sistema
libmagic.bea import --csvcobre extratos bancários sem nenhum dos dois. - Comandos nativos repassados não emitem o envelope. Se você precisa de saída estruturada de uma operação do doctor, esse é um pedido que gostaríamos de ouvir.
Desde a tag, main já incorporou a primeira rodada de QA do 0.2.0, que seguirá no próximo lançamento: bea format lê stdin como filtro e seu modo -o FILE responde com um envelope nomeando o que gravou; --json check recusa flags exclusivas do bean-check, e --json é recusado de imediato em doctor, example e treeify, para que um script não confunda texto nativo com um envelope; --json query -o FILE grava o envelope no arquivo atomicamente, com --numberify aplicado também ao JSON; bea engine status nomeia qual camada de motor está servindo; uma consulta BQL que começa com um comentário é executada; o --help de repasse nativo funciona antes que o motor seja provisionado; e o .output do shell de consulta restaura o fluxo original após um redirecionamento falho.
Para onde ir em seguida
- Guia rápido da CLI: instalação, primeiro razão, primeira compra, primeira verificação de saldo.
- Seu primeiro mês com o bea: do
inita um relatório de fechamento mensal reconciliado. - Importar extratos bancários: o caminho CSV sem Python, arquivos de regras e importadores em Python.
- Automatizar a escrituração com o bea: resolver o razão, ler o envelope, ramificar por códigos de saída, agendar.
- Referência da CLI do Beancount: todos os comandos, opções, variáveis de ambiente e códigos de saída, conferidos contra a referência gerada da CLI.
- Dê um razão ao seu agente de IA: o passo a passo com foco em agentes do lançamento do 0.1.0.
- Changelog: todos os lançamentos, do mais recente ao mais antigo.
Mantenha seus livros como código
Uma toolchain que você instala em uma linha é uma toolchain que você pode entregar a qualquer pessoa: um cofundador, um contador, um runner de CI, um agente de IA. O Beancount.io oferece contabilidade em texto simples que permanece transparente, versionada e reproduzível, com o bea como o comando que mantém um razão local honesto e o serviço hospedado como o lugar onde sua equipe, seu telefone e seu assistente encontram os mesmos livros. Instale o bea e execute sua primeira verificação, e se o lançamento fizer algo que você não esperava, o repositório no GitHub é onde queremos ouvir sobre isso.





