| name | kassiber |
| description | Use this skill when the user wants to use the Kassiber CLI for local-first Bitcoin accounting, wallet onboarding, transaction imports, journal processing, metadata cleanup, or tax and portfolio reports, or when the user wants to research, draft, or publish a Kassiber GitHub feature request. Applies to requests about Kassiber books, internal workspaces/profiles, accounts, wallets, backends, rates, attachments, BIP329 labels, quarantines, generic tax reporting, Austrian-support planning/questions, and Kassiber issue planning, even when the user does not say Kassiber by name. |
Kassiber
Use this skill for Kassiber CLI workflows. Kassiber has its own command surface, so agents should not guess flags or reuse commands from analogous tools.
All scripts/ paths in this skill are relative to the directory containing this SKILL.md file. Resolve <skill-dir> first, then use paths like <skill-dir>/scripts/verify-state.sh.
Kassiber accounts are wallet/reporting buckets, not a double-entry chart of accounts. Keep explanations simple unless the user explicitly asks for accounting theory.
Kassiber's CLI and daemon output are English and machine-deterministic, independent of any GUI language. The desktop app (ui-tauri/) is separately localized (English + Austrian German); the CLI is not. Don't translate CLI/--machine output or expect localized CLI responses. If you change UI strings, see docs/reference/i18n.md.
Fast Paths
Use these without opening extra references when the request clearly matches:
| User asks for... | First command |
|---|
| Current balances by account, bucket, asset, or wallet | kassiber --format plain reports balance-sheet |
| Exact summary totals, counts, fees, PnL | kassiber --machine reports summary |
| Rebuild stale reports after imports/metadata/rates | kassiber --machine journals process |
| Largest inbound transactions | kassiber --machine transactions list --direction inbound --sort amount --order desc --limit 10 |
| Largest outbound transactions | kassiber --machine transactions list --direction outbound --sort amount --order desc --limit 10 |
| Smallest inbound transactions | kassiber --machine transactions list --direction inbound --sort amount --order asc --limit 10 |
| Smallest outbound transactions | kassiber --machine transactions list --direction outbound --sort amount --order asc --limit 10 |
| Ask the in-product assistant with tools | kassiber chat "<question>" |
| Run the fast local regtest harness | ./scripts/integration-harness.sh fast |
| Run the live Bitcoin Core smoke harness | ./scripts/integration-harness.sh bitcoin-core |
| Build the disposable full regtest accounting demo | ./scripts/integration-harness.sh demo-full |
| Start/reuse the persistent regtest demo book | ./scripts/integration-harness.sh demo-up |
If a fast-path command returns a structured error, inspect the envelope and take the hinted next step. For example, stale reports usually mean running kassiber --machine journals process once, then retrying the same report.
Rules
- Prefer
kassiber when it is on PATH. If it is not, fall back to uv run --locked kassiber or uv run --locked python -m kassiber from the Kassiber repo root.
- When falling back from
kassiber to uv run --locked kassiber or uv run --locked python -m kassiber, keep the subcommand, flags, and operands identical. Only the launcher changes.
- When the chat includes pasted Kassiber output or docs, identify the live user request separately from the quoted material before running commands.
- Use fast paths for common read-only workflows. For wallet sync or other operations that contact configured backends, read the relevant reference first and avoid feeding raw command output into remote AI context unless it is documented as public-safe.
- Before concluding a reference is missing, verify that you resolved it from
<skill-dir> rather than the repo root or the current working directory.
- If a Kassiber command fails with
unrecognized arguments, stop and check --help or references/command-templates.md before retrying. Do not keep guessing positional versus flagged forms.
--machine, --format, and --output are global flags and must come before the subcommand tree, for example kassiber --format plain reports balance-sheet.
- Use
--machine whenever the output needs to be parsed or piped into later steps.
- Use
--format plain when the user wants report output shown in the terminal. Let Kassiber format financial values; do not recompute or restyle them.
- Use
--format csv --output <path> for spreadsheet-style exports.
- Never perform your own arithmetic on Kassiber financial values. Do not sum, subtract, average, or convert amounts from raw JSON when Kassiber already has a command or output format for the answer.
- For current balances by account, bucket, asset, or wallet, use
kassiber --format plain reports balance-sheet first. Do not detour through reports summary.
- For rollups like totals, fees, counts, realized gains, unrealized PnL, or "give me the summary", use
kassiber --machine reports summary first. Quote Kassiber's returned fields directly.
- If Kassiber returns both BTC and
*_msat fields, quote those fields as-is. Never derive one from the other in your response.
- If the exact answer is not exposed by an existing Kassiber command, say that the CLI surface is missing it and stop there instead of approximating.
- For "largest transaction", "smallest transaction", biggest inbound/outbound, or similar raw transaction rankings, use
kassiber --machine transactions list --sort amount --order desc|asc --direction inbound|outbound --limit <n> so SQLite ranks the full dataset before limiting. Amounts are unsigned and direction is a separate field, so "largest inbound" and "largest outbound" both use --order desc; "smallest" uses --order asc. Do not fetch the default recent page and sort it client-side. If a machine response includes next_cursor, keep following it only when the user asked for the full list; a correctly sorted first row is already the largest/smallest row for top-N questions.
- Processing order is: wallet sync or import -> review likely transfer / swap pairs when relevant ->
kassiber rates sync when pricing is needed -> kassiber journals process -> reports.
- Re-run
kassiber journals process after any transaction import, transfer pairing, note or tag change, exclusion change, rate sync, or rate override before trusting reports.
- Do not confuse
kassiber init with onboarding. It only creates the local state tree; books records (workspace/profile internally), bucket, and wallet records are created with their own commands.
- Prefer explicit workspace and profile flags until context is verified; these are the CLI names for the active books set and book. Use
kassiber context show or kassiber status before assuming the active scope.
- For Liquid descriptor wallets, require an explicit backend and private blinding keys. If either is missing, stop and fix that before sync.
- If the user already provided a secret-bearing descriptor or token, do not ask them to paste it again and do not quote it back in summaries. Use a local file or direct CLI input once, then rely on allowlisted safe views after creation.
- For wallet-connection setup, ask for the wallet or backend type if it is unclear, but otherwise assume a mainnet connection. Only ask about network when the user explicitly says testnet, signet, regtest, or another non-mainnet environment.
- For desktop wallet or backend setup, prefer the Connections / Settings setup modals so users enter descriptors, file paths, and backend fields locally in the app. For CLI-only handoff, use placeholders or
--*-stdin / --*-fd FD; do not collect descriptors, API tokens, or cookie values in chat.
- If a BTCPay or CSV export belongs to the same real wallet as an existing Kassiber wallet, import it into that wallet instead of creating a duplicate wallet record.
- On errors, inspect the machine envelope first. Kassiber success responses are
{kind, schema_version, data} and errors use kind: "error" with structured fields.
- Treat normal
backends ... and wallets ... success output as safe-to-record only for secret-bearing config values. Do not ask users to paste raw backend credentials, raw private descriptor material, or suppressed config blobs into chat just because backends get or wallets get returns an allowlisted safe view.
- For BTCPay and other secret-bearing backends, do not ask users to paste raw API tokens into chat. Prefer
--token-stdin (then have them pipe the secret in locally), --token-fd FD, or — only as a fallback for older scripts — a local backends.env entry. The argv form --token <value> still works but warns and leaks to shell history; do not recommend it.
- Do not persist backend or wallet config changes just to work around a sync failure unless the user requested that mutation or explicitly agrees after you explain the tradeoff.
wallets update --backend ..., --gap-limit, and backends set-default change durable state.
- Never claim a BTC ↔ LBTC swap is already paired, carrying-value, or reflected in reports unless
kassiber --machine journals transfers list shows the pair or kassiber transfers pair just succeeded and you reprocessed journals.
- When quarantines remain, distinguish processed holdings from raw transaction-net estimates. Reports show processed journal state only; any netting from
transactions list must be labeled as an approximate diagnostic rather than a Kassiber holding.
- For rate coverage, do not infer the covered time window from
samples or days alone. Use kassiber rates range with RFC3339 timestamps around the missing transactions.
- Treat Kassiber accounts as wallet/reporting buckets. Do not recommend double-entry charts of accounts, automatic fee expense postings, or external equity counterpart accounts unless the product gains an explicit ledger model.
- For planning or codebase work, treat
TODO.md as the executable backlog and docs/plan/ as orientation/guardrails. Verify current behavior against code before acting on a plan doc.
- If
kassiber status (or any other command) returns passphrase_required, the local DB is SQLCipher-encrypted. Either prompt the user interactively, or have them re-run the command with --db-passphrase-fd <FD> from a parent process. Never embed a passphrase in argv — there is no --db-passphrase <value> flag and there will not be one.
kassiber secrets init is a one-time migration from plaintext to SQLCipher. After it runs, the original plaintext file is preserved as kassiber.pre-encryption.sqlite3.bak; Kassiber refuses to overwrite an existing rollback file at that path. Advise the user to verify the encrypted DB opens (kassiber secrets verify) and then rm the .bak themselves once they trust the new file. Forgetting the passphrase means data loss — there is no recovery path and .kassiber backups do not help.
.kassiber backup files are tar | age envelopes that wrap a SQLCipher copy of the DB plus the attachments tree and backends.env. They are recoverable with stock age + tar + sqlcipher even without Kassiber installed. Backup decryption uses an outer age passphrase (--backup-passphrase-fd) that is independent of the DB passphrase.
- The dotenv bootstrap (
backends.env) is for non-secret addressing only: URLs, KIND, chain, network, batch sizes. Tokens, passwords, auth headers, and basic-auth usernames belong in the encrypted DB. If Kassiber warns at startup that the dotenv still has plaintext secrets, run kassiber secrets migrate-credentials (or --dry-run first) to lift them into the encrypted backends table; the file is rewritten with non-secret rows preserved and a .pre-credentials-migration-<ts>.bak snapshot is saved.
- Use
kassiber chat for the daemon-backed assistant when the user wants tool-aware AI help from the CLI. It is the only chat command and uses the same ai.chat / ai.tool_call.consent loop as the GUI. kassiber chat --no-tools covers provider-only exchanges; kassiber chat --stream-json "<prompt>" emits the raw daemon stream records as NDJSON for scripting; kassiber chat - reads the one-shot prompt from stdin; --transcript <path> appends the session's daemon records as a plaintext NDJSON audit log. Piped stdout carries only the answer text — progress and tool chrome go to stderr.
- For scripted
kassiber chat runs, prefer --allow-tool <daemon-tool-name> over broad --yes. Machine (--machine) and --stream-json runs never prompt for consent even on a TTY; there, and without a TTY in rendered mode, unapproved mutating tool requests are denied and fed back to the model as user_denied.
- Chat history persists inside the SQLCipher database under the
auto policy only when the DB is encrypted (kassiber chats config --history auto|on|off). kassiber chat --continue resumes the latest session, --session <id> a specific one, --incognito skips persistence once. Manage stored sessions with kassiber chats {list,show,delete,clear}. Machine chat envelopes carry session_id (null when nothing persisted).
- For local regtest harness work, use
scripts/integration-harness.sh from the repo root. fast is the no-Docker replay lane; bitcoin-core starts a disposable Core/Elements/Fulcrum/local-backend Compose stack; demo-full builds a throwaway full accounting book and proves the post-sync refresh path; demo-up keeps a persistent demo node/book and must leave reports immediately readable; demo-tick [N] adds fresh confirmed business activity to that persistent book; demo-down [--purge] stops or removes it.
Gotchas
- Empty or stale reports usually mean journals have not been processed since the last change.
- Reports do not auto-pair BTC ↔ LBTC peg-ins / peg-outs or submarine swaps. If the user has cross-asset swap activity, inspect for likely pairs and use
kassiber transfers pair before trusting reports.
- Use
kassiber --machine journals transfers list when you need the exact transfer / swap links Kassiber computed. Do not infer them from journals process counts alone.
--machine implies JSON mode. Use it alone or with --format json; do not combine it with any other explicit --format value.
wallets sync uses either --wallet <label-or-id> or --all, never both; transactions needs the list subcommand and ranks raw rows with --sort amount --order asc|desc; journals quarantined has no --limit; rates range --start/--end expect RFC3339 UTC strings and supports --order asc|desc.
backends get/list and wallets get/create/update intentionally return allowlisted safe views. Look for presence and state flags instead of expecting raw credentials, raw descriptors, or arbitrary config keys back.
- For new desktop wallet connections, default to mainnet unless the user says otherwise and use the setup modal flow. For CLI-only handoff, keep secrets out of chat with placeholders or fd/stdin forms.
- Quarantined transactions are omitted from accurate downstream reporting until resolved or excluded.
- Paginated list commands keep rows under command-specific keys such as
.data.records and .data.events. Do not assume every list response uses the same field name.
- Follow
next_cursor only when the user asks for all/full/export/audit output. For top-N, largest/smallest, or summary questions, stop after the correctly sorted first page.
- Cross-asset
--policy carrying-value pairing is Austrian-only right now. Outside Austrian books, BTC ↔ LBTC manual pairs still stay on the normal SELL + BUY path, so do not describe them as carrying-value.
kassiber status may resolve to a legacy XDG path on machines with older state trees. Use status output, not assumptions, to find the live database.
- If
journals transfers list reports cross_asset_pairs: 0, no cross-asset swap pair is active yet. Do not describe Austrian carry-value as already applied until that changes.
- Coinbase
rates sync normally uses missing transaction minutes and cached checked-minute state before falling back to a continuous --days warm-cache request. Verify actual coverage with rates range instead of hand-mathing sample counts.
- If a skill reference lookup fails, the most common mistake is resolving
references/... from repo root instead of <skill-dir>/references/....
- Kassiber already has
reports export-pdf; do not invent bespoke render scripts unless the user specifically wants a custom format beyond the built-in export.
- Accounts are not a double-entry chart of accounts today.
account_type and asset are descriptive bucket metadata; fees and external counterparties do not auto-post to separate accounts.
Data Model
Kassiber organizes data as:
books set -> book -> buckets + wallets -> transactions -> journals -> reports
Related notes:
- A books set is the top-level container for one local person, business,
client, or related set of books. The CLI/API name is
workspace.
- A book is one accounting and tax scope inside that set. The CLI/API name
is
profile.
wallet is a transaction source that Kassiber syncs or imports; map it to the real underlying wallet, not every external store or export.
account is a wallet/reporting bucket that wallets can belong to.
backends define sync transport endpoints.
metadata covers notes, tags, exclusions, and BIP329 labels.
attachments are managed separately from wallet config and transaction rows.
- Cost basis is pooled per asset across all wallets in one set of books.
- Balance-sheet output groups holdings by the wallet's assigned bucket, not by account-type rollups or counterpart postings.
- If multiple BTCPay stores point at the same real wallet, keep them in one Kassiber wallet or holdings will be duplicated.
Workflow Routing
- For fragile CLI command shapes and safe invocation patterns, read references/command-templates.md.
- For first-run setup, roots, context, and books creation, read references/onboarding.md.
- For wallet kinds, descriptor setup, backend selection, and imports, read references/wallets-backends.md.
- For onboarding a brand-new exchange / broker / custodial platform Kassiber does not support yet (the structured intake interview plus the importer implementation checklist), read references/add-exchange.md. The user-facing entry point is the
/add-exchange command.
- For researching, drafting, and publishing a Kassiber GitHub feature request, read references/feature-request.md. The user-facing entry point is the
/feature-request command.
- For journal processing, quarantine handling, and transfer pairing, read references/journal-processing.md.
- For swap-candidate matching (Lightning ↔ Liquid, BTC ↔ LBTC peg, Boltz submarine swaps), the auto-pair rules engine, and saved review-queue views, read references/swap-matching.md.
- For notes, tags, exclusions, BIP329 labels, and attachments, read references/metadata.md.
- For balance sheet, portfolio, capital gains, balance history, PDF export, and rates, read references/reports.md.
- For quick state checks and smoke validation, read references/verification.md and use
scripts/verify-state.sh when helpful.
- For common failure modes and path confusion, read references/troubleshooting.md.
- For SQLCipher encryption (
kassiber secrets ...), tar | age backups (kassiber backup ...), passphrase entry, and the --*-stdin / --*-fd secret-input channels, read references/secrets-and-backup.md.
- For the local regtest integration harness and persistent demo book, read ../../docs/reference/testing.md.
Report Selection
| User is asking about... | Report |
|---|
| Cost basis, unrealized gains, portfolio value, average cost, wallet allocation | portfolio-summary |
| Realized gains/losses, disposals, tax reporting, capital gains | capital-gains |
| Yearly gain/loss buckets, long vs short summary, tax totals by year | tax-summary |
| Austrian E 1kv handoff, FinanzOnline Kennzahlen, Steuerberater review package | austrian-e1kv / austrian-tax-summary / export-austrian / export-austrian-e1kv-pdf / export-austrian-e1kv-xlsx / export-austrian-e1kv-csv |
| Exit tax / Wegzugsbesteuerung, deemed disposal on leaving the country, departure-date liability | exit-tax / export-exit-tax-pdf / export-exit-tax-xlsx (--departure-date, --destination eu_eea|third_country) |
| Current balances by bucket, asset, or wallet | balance-sheet |
| Balance changes over time, trends, history | balance-history |
| Raw journal export, journal rows, bookkeeping output | journal-entries |
| Exact rollups: fees, counts, totals, realized/unrealized summary | summary |
Fallback
If command shape is unclear, consult:
kassiber --help
kassiber <command> --help
kassiber <command> <subcommand> --help