Skip to main content
smolbren always writes a single line of compact JSON to stdout. There is no pretty-printing, no progress text, and no status messages mixed in — stdout is reserved exclusively for machine-readable output. This makes every command trivially composable with jq, shell pipelines, and AI agent tool calls without any pre-processing.

Success output

On success, one line of compact JSON is printed to stdout and the process exits with code 0.
  • List commands (search, links, backlinks, types, edges, vault list) return a JSON array, one element per result.
  • Single-item commands (get, index, vault add, vault remove) return a JSON object.
For example, smolbren search "zettelkasten" might emit:
[{"id":"202401-zettelkasten","title":"Zettelkasten method","type":"blog","score":0.91},{"id":"202312-linking","title":"Linking your thinking","type":"note","score":0.74}]
And smolbren get 202401-zettelkasten returns a single object:
{"id":"202401-zettelkasten","title":"Zettelkasten method","type":"blog","created_at":"2024-01-15","body":null}
Fields that are absent or inapplicable are included as null rather than being omitted. This keeps output shapes stable across notes so downstream code can rely on a consistent schema.

Error output

When a command fails, the error is written to stderr as a single line of compact JSON with two fields — error (a human-readable message) and code (a stable machine-readable identifier):
{"error":"vault 'personal' not found in config","code":"vault_not_found"}
stdout remains completely empty on error. Scripts can parse stdout unconditionally without first checking for embedded error strings — if stdout has content, the command succeeded.

Error codes

CodeMeaning
internalAn unexpected internal error occurred. Check the error field for details.
vault_not_foundNo vault with the given name is registered in config, or no default vault has been set and --vault was not provided.
note_not_foundThe note ID passed to get, links, or backlinks does not exist in the index.
index_missingThe vault is registered but has never been indexed — run smolbren index first.
The code field is guaranteed to be one of these four values. Use it (rather than the error string) for programmatic branching; the human-readable message may change between releases.

Parsing with jq

Because every command emits valid JSON on a single line, jq works with no special flags:
# Pull the id of the top search hit
smolbren search "notes" | jq '.[0].id'

# List all note type names from the types command
smolbren types | jq '[.[].type]'

# Get the rows returned by a Cypher query
smolbren query "MATCH (n:blog) RETURN n.id, n.title" | jq '.rows'

# Get every vault that has been indexed
smolbren vault list | jq '[.[] | select(.indexed_at_ms != null) | .name]'
All JSON is compact — no indentation or line breaks. Pipe through jq . to get human-readable formatted output when inspecting results at the terminal:
smolbren get 202401-zettelkasten | jq .
To suppress error output in scripts where you don’t care about the failure reason, redirect stderr to /dev/null:
smolbren search "query" 2>/dev/null
stdout will still be empty on failure, so checking the exit code or testing whether stdout is non-empty is enough to detect errors. See Exit Codes for the full list of non-zero exit codes and how to branch on them.