bus — dispatcher and `.bus` command-file runner

bus — dispatcher for modules and .bus files

Synopsis

bus [<command> [args...]]
bus [--check] [--transaction <provider>] [--scope <scope>] [--trace] <file.bus> [<file2.bus> ...]
./file.bus

Use bus in two modes:

Normal dispatch uses bus <module> [args...] and calls bus-<module> from PATH. Busfile mode uses bus <file>.bus [...] and executes one or more .bus command files.

Description

bus is the single CLI entrypoint for BusDK. It does not implement accounting logic itself; it routes commands to module CLIs (bus-journal, bus-bank, and so on), and it can execute deterministic .bus files.

If you run bus with no arguments, it prints usage and a quick tip suggesting bus shell for interactive command entry. Run bus --help when you need the current syntax-first global help with grouped dispatcher usage and the current available-command list.

When running busfiles, bus always does a full syntax preflight across all provided files before executing any command. If preflight fails, nothing is executed. Dispatcher-level -C/--chdir applies to busfile mode too, so relative busfile paths are resolved after switching into the selected workspace and the executed module commands inherit that same effective working directory.

For first use, start with .bus files — getting started step by step, then continue with the full .bus script files (writing and execution guide).

If you edit .bus files regularly, install the Bus language package for VS Code compatible editors as described in Editor support for .bus files.

That same editor-support surface now also includes a Tree-sitter grammar and a lightweight stdio language server, so .bus files can be highlighted and semantically tokenized outside VS Code-compatible editors too.

This feature is available under FSL-1.1-MIT (Functional Source License 1.1, MIT Future License), and source code is already available.

Normal dispatch

bus <module> <args...> dispatches to bus-<module>. For example, bus journal add ... runs bus-journal add .... bus run ... is treated like any other module dispatch target.

That same rule applies to BusDK installer and package-manager flows. bus update ... and bus update package ... both delegate to bus-update; the dispatcher does not embed package download, package database, or installer logic itself. In a bootstrap-installed setup, this keeps bus as the stable entrypoint while bus-update remains the owning module for release checks, managed executable packages, and bootstrap-root behavior.

Busfile mode

A path is treated as a busfile when:

it ends with .bus, or it is executable and starts with #!/usr/bin/bus or #!/usr/bin/env bus.

Busfile line rules:

Blank lines are ignored. Lines whose first non-whitespace character is # are comments. Lines ending with .bus are treated as nested includes. Other lines are parsed as a single command line with shell-like quoting.

A line that contains only dispatcher global flags acts as a sticky directive for subsequent commands in the same .bus session. This uses the same global-flag parser as normal dispatch, so later single-value flags replace earlier ones. For example, --chdir data followed later by --chdir reports means later commands receive only --chdir reports. Sticky state can be reset with --no-perf, --no-quiet, --no-verbose, --no-chdir, --no-output, and --no-format. Color uses --color ... and --no-color. Sticky directives also propagate into included .bus files that are expanded after the directive.

Variable expansion, command substitution, pipes and redirection, and ; separators are not interpreted.

Busfile options

These flags are interpreted only in busfile mode:

--check runs preflight (and optional validation) only and does not apply changes. --transaction <provider> accepts none (default), fs, git, snapshot, or copy. --scope <scope> accepts file (default) or batch. --trace prints file:line traces before execution.

--check should rely on module-native non-mutating validation where available. Dispatcher also rejects clearly invalid common patterns during preflight. For journal add, that preflight accepts the same posting token shapes as the module help, including ACCOUNT=AMOUNT=ROW_DESCRIPTION with quoted UTF-8 row text in .bus files.

Execution model

  1. Global syntax preflight: read/tokenize all provided busfiles first.
  2. Optional data validation (workspace/module support dependent).
  3. Apply in order with fail-fast semantics.

Scope file is the default and applies each file independently after global preflight. Scope batch treats all files as one apply unit, with all-or-nothing behavior only when the chosen provider supports it.

Exit status

Exit 0 means success. Exit 1 means command execution failed. Exit 2 means usage error such as invalid flags, missing file, or missing subcommand in normal dispatch usage. Exit 65 means busfile syntax or tokenization error. Exit 127 means missing normal-dispatch target (bus-<command> not found on PATH).

If a dispatched module returns a specific non-zero code, bus returns that code.

Examples

Normal dispatch:

bus init all
bus accounts list
bus journal add --date 2024-02-29 --desc "Example" --debit 1910=10 --credit 3000=10
bus update package install --module bus-ledger

Single busfile:

#!/usr/bin/env bus

# 2024-02-29 Bank import-bank-00001
bank add transactions \
  --set bank_txn_id=import-bank-00001 \
  --set import_id=import-bank-2024 \
  --set booked_date=2024-02-29 \
  --set value_date=2024-02-29 \
  --set amount=-861.6800000000 \
  --set currency=EUR \
  --set counterparty_name='Example Vendor' \
  --set counterparty_iban='' \
  --set reference='REF-00001' \
  --set message='EXAMPLE PAYMENT MESSAGE' \
  --set end_to_end_id=import-e2e-00001 \
  --set source_id='bank_row:00001'

journal add \
  --date 2024-02-29 \
  --desc 'Bank import-bank-00001 Example Vendor payment' \
  --debit 2949=861.68 \
  --credit 1910=861.68 \
  --source-id bank_row:00001:journal:1

Run monthly files directly:

bus 2024-01.bus 2024-02.bus 2024-03.bus

Example month runner file:

#!/usr/bin/env bus

2024-01.bus
2024-02.bus
2024-03.bus

Check-only and trace:

bus --check --trace 2024-01.bus 2024-02.bus 2024-03.bus

Force transaction settings for one run:

bus --transaction git --scope batch 2024-01.bus 2024-02.bus 2024-03.bus

Using from .bus files

bus is the dispatcher itself. In .bus files you normally call module targets directly and include other .bus files.

# include another .bus file
2026-02.bus

# module command line (same as: bus journal add --date 2026-02-28 --desc "Adjust" --debit 1910=100 --credit 3000=100)
journal add --date 2026-02-28 --desc "Adjust" --debit 1910=100 --credit 3000=100

# module command line (same as: bus run send-feedback --message "Close run complete")
run send-feedback --message "Close run complete"

Notes

Atomicity is configurable and the default transaction provider is none. Prefer the .bus extension for deterministic recognition. For safer batch runs, keep --check support enabled in referenced modules.

Dispatch during busfile runs is automatic: an in-process runner is used when available, otherwise shell lookup (bus-<module> on PATH) is used when enabled. Shell lookup can be disabled with bus.busfile.dispatch.shell_lookup_enabled=false in preferences or workspace config.

For deeper transaction/dispatch internals, see Module reference: bus (dispatcher).

Sources

The dispatcher also accepts --perf before the subcommand. It does not forward that flag as argv; instead it enables timing for the dispatched command, prints a stderr timing line in form INFO perf bus-<module> <op> <duration>, and sets BUS_PERF=1 for instrumented modules so they can emit nested timings too. The duration token uses Go’s standard duration format such as 412µs, 3.2ms, or 1.25s. The same timing format is used for each busfile command when sticky --perf is active.