--- numbering: title: true --- # `means.cli`: Command-Line Tools The `cli` subpackage implements the console scripts declared in `pyproject.toml`, one module per command. Each module exposes a `main()` entry point (the target of its `[project.scripts]` entry) plus the domain functions that do the work. Remote telemetry is structural and content-free: operation names are stable `means.*` identifiers, and attributes are limited to bounded kinds, counts, and error codes. Account and user names, file paths, memos, dates, amounts, report contents, model responses, and ledger command output remain absent from telemetry. `tests/test_telemetry.py` enforces that boundary across every direct Logfire call in the `means` package; shared-library and exporter behavior is covered in `my-basis` and the fleet observability contract. Supplying a Logfire token is the explicit opt-in that enables these structural events in local or development runs. ## `I` `load` — Parse Configured Ledgers ```{py:currentmodule} means.cli.load ``` ```{eval-rst} .. autofunction:: means.cli.load.load_ledgers ``` ```{eval-rst} .. autofunction:: means.cli.load.parse_args ``` ```{eval-rst} .. autofunction:: means.cli.load.main ``` ## `II` `grow` — Regenerate the Account Tree ```{py:currentmodule} means.cli.grow ``` ```{eval-rst} .. autofunction:: means.cli.grow.process_seed ``` ```{eval-rst} .. autofunction:: means.cli.grow.load_seed ``` ```{eval-rst} .. autofunction:: means.cli.grow.grow_user_section ``` ```{eval-rst} .. autofunction:: means.cli.grow.grow ``` ```{eval-rst} .. autofunction:: means.cli.grow.parse_args ``` ```{eval-rst} .. autofunction:: means.cli.grow.main ``` ## `III` `ingest` — Import Bank Statements ```{py:currentmodule} means.cli.ingest ``` ```{eval-rst} .. autofunction:: means.cli.ingest.ingest_file ``` ```{eval-rst} .. autofunction:: means.cli.ingest.ingest ``` ```{eval-rst} .. autofunction:: means.cli.ingest.parse_args ``` ```{eval-rst} .. autofunction:: means.cli.ingest.main ``` ## `IV` `categorize` — Resolve Unknown Transactions ```{py:currentmodule} means.cli.categorize ``` ```{eval-rst} .. autofunction:: means.cli.categorize.categorize_ledger ``` ```{eval-rst} .. autofunction:: means.cli.categorize.categorize_transaction ``` ```{eval-rst} .. autofunction:: means.cli.categorize.parse_args ``` ```{eval-rst} .. autofunction:: means.cli.categorize.main ``` ## `V` `suggest` — LLM-Assisted Templates `suggest` is an explicitly remote, advisory layer over the deterministic template system. Install the optional SDK and provide the standard Anthropic API key environment variable: ```sh uv tool install 'my-means[llms]' export ANTHROPIC_API_KEY=your-api-key export MEANS_LLM_MODEL=claude-haiku-4-5-20251001 # optional default ``` Preview proposals without changing `templates.yaml`, then run again and confirm a reviewed merge: ```sh suggest --directory /path/to/means --dry-run suggest --directory /path/to/means ``` The request contains uncategorized memo strings, current templates, and the account structure. It contains no API key and no telemetry content. `--dry-run` prevents the local write, not the remote request. Before confirmation, proposals must use known account paths, add rather than replace patterns, and contain bounded literal pattern text without regex metacharacters plus text labels. Existing hand-authored regex templates remain supported; the restriction applies only to untrusted model additions. The default Claude Haiku 4.5 model is economical and can be replaced through `MEANS_LLM_MODEL` or `--model` without changing code. ```{py:currentmodule} means.cli.suggest ``` ```{eval-rst} .. autofunction:: means.cli.suggest.build_prompt ``` ```{eval-rst} .. autofunction:: means.cli.suggest.suggest ``` ```{eval-rst} .. autofunction:: means.cli.suggest.parse_args ``` ```{eval-rst} .. autofunction:: means.cli.suggest.main ``` ## `VI` `means-plan` — Conditional Cashflow and Debt Planning `means-plan` projects explicit YAML or JSON assumptions without reading or mutating a Ledger. It is a conditional deterministic projection, not a prediction or financial advice. Project one strategy or compare every implemented strategy: ```console means-plan project --scenario fictional-plan.yaml --strategy avalanche --json means-plan compare --scenario fictional-plan.yaml --csv --output comparison.csv ``` JSON is the default machine-readable output; CSV is a rectangular spreadsheet-oriented subset. Reports go to stdout unless `--output` names an explicit owner-only file. The comparison's `recommended` field is a rule-selected strategy under the disclosed comparison objective, not advice. Zero warnings means no implemented rule fired, not that the scenario is safe or complete. The complete fictional scenario schema, exact-cent and date rules, warning meanings, selection objective, privacy boundary, and limitations are in the [conditional planning guide](planning.md). ```{py:currentmodule} means.cli.plan ``` ```{eval-rst} .. autoclass:: means.cli.plan.ExitCode :members: :undoc-members: ``` ```{eval-rst} .. autofunction:: means.cli.plan.parse_args ``` ```{eval-rst} .. autofunction:: means.cli.plan.run ``` ```{eval-rst} .. autofunction:: means.cli.plan.main ``` ## `VII` `report` — Tabular Finance Reports ```{py:currentmodule} means.cli.report ``` ```{eval-rst} .. autoclass:: means.cli.report.Editor :members: :undoc-members: ``` ```{eval-rst} .. autofunction:: means.cli.report.register ``` ```{eval-rst} .. autofunction:: means.cli.report.parse_args ``` ```{eval-rst} .. autofunction:: means.cli.report.main ``` ## `VIII` `means-bank` — Stage Read-Only Provider Changes `means-bank` separates the networked fetch boundary from deterministic planning and explicit local apply. Provider credentials and access tokens are injected through environment variables; they are never command-line options. The generated sidecar is the only Ledger file the command may replace. The first connection is an explicit human-gated setup operation. Prepare an owner-only Proton Pass environment manifest containing `PLAID_CLIENT_ID`, `PLAID_SECRET`, and `PLAID_ENV`, then run the wrapper through `pass-cli run` (`pp` is an optional shell alias): ```console pass-cli run --env-file ~/.local/state/means/bank-sync.env.pp -- \ means-bank-pp-connect \ --directory ~/my/self/_0_resources/means \ --connection fictional-primary \ --access-token-env PLAID_ACCESS_FICTIONAL \ --vault Personal \ --item-title plaid_fictional_primary ``` The wrapper opens Hosted Link, waits for the operator to finish authorization, writes the exchanged access token directly to a new Proton Pass custom item over standard input, and records only its `pass://` reference and protected connection identity in owner-only local state. It refuses reconnect-in-place so a different provider Item cannot silently inherit old source history. The personal means directory declares only non-secret local policy in `parameters/parameters.yaml`. `unsupported_accounts` is the explicit gap ledger; every account that cannot use the active provider remains visible in `status` rather than disappearing from coverage: ```yaml bank_sync: state: ~/.local/state/means/bank-sync.sqlite3 manual_ledger: robb.ledger root_ledger: root.ledger sidecar: generated/bank-sync.ledger since: 2026-04-22 unsupported_accounts: - debts:r:unsupported_card ``` The recurring workflow is deliberately staged: 1. Inspect connection readiness and explicit gaps without injecting credentials. ```console means-bank --directory ~/my/self/_0_resources/means status --json ``` A new connection reports `fetch_required`; only a historically complete committed fetch makes every configured connection `ready`. 2. Inject references and fetch provider source changes, then inspect status again. ```console pass-cli run --env-file ~/.local/state/means/bank-sync.env.pp -- \ means-bank --directory ~/my/self/_0_resources/means fetch --json means-bank --directory ~/my/self/_0_resources/means status --json ``` 3. List locally observed accounts by opaque handle and bind each one to an existing Ledger account. ```console means-bank --directory ~/my/self/_0_resources/means accounts --json means-bank --directory ~/my/self/_0_resources/means bind \ --account account-opaque-example \ --ledger-account funds:r:checking ``` 4. Create an owner-read-only preview without changing the live sidecar. ```console means-bank --directory ~/my/self/_0_resources/means plan --json ``` 5. Inspect the returned `preview_path`, then apply its exact immutable `plan_id`. ```console means-bank --directory ~/my/self/_0_resources/means apply \ --plan plan-opaque-example --json ``` Applying performs no network calls. It rejects unresolved account, transfer, currency, or manual-ledger overlap blockers; changed source or ledger hashes; altered previews; invalid Ledger output; and unknown crash-recovery state. A repeated plan and apply over unchanged source is a no-op. ```{py:currentmodule} means.cli.bank ``` ```{eval-rst} .. autoclass:: means.cli.bank.ExitCode :members: :undoc-members: ``` ```{eval-rst} .. autoclass:: means.cli.bank.BankSyncPaths :members: :undoc-members: ``` ```{eval-rst} .. autofunction:: means.cli.bank.build_parser ``` ```{eval-rst} .. autofunction:: means.cli.bank.parse_args ``` ```{eval-rst} .. autofunction:: means.cli.bank.connect ``` ```{eval-rst} .. autofunction:: means.cli.bank.status ``` ```{eval-rst} .. autofunction:: means.cli.bank.accounts ``` ```{eval-rst} .. autofunction:: means.cli.bank.bind_account ``` ```{eval-rst} .. autofunction:: means.cli.bank.fetch ``` ```{eval-rst} .. autofunction:: means.cli.bank.plan ``` ```{eval-rst} .. autofunction:: means.cli.bank.apply ``` ```{eval-rst} .. autofunction:: means.cli.bank.recover ``` ```{eval-rst} .. autofunction:: means.cli.bank.main ``` ```{eval-rst} .. autofunction:: means.cli.bank_pp.build_parser ``` ```{eval-rst} .. autofunction:: means.cli.bank_pp.main ```