# Command reference

`bf --help` lists the commands from setup to repair; `bf COMMAND --help` (or `-h`) lists a command's arguments, defaults and choices. Help reads no brain, runs no program and answers even when other values on the line are invalid. Start with [Getting started](<https://fmind.github.io/brain-framework/docs/getting-started/index.md>) for a first session, and [Troubleshooting](<https://fmind.github.io/brain-framework/docs/troubleshooting/index.md>) for recovery.

## Commands

| Command | Purpose |
| --- | --- |
| `init PATH [--name NAME] [--full]` | Create a brain in a new, empty or freshly cloned folder; `--full` adds optional folders. |
| `register [PATH]` | Add or move a brain's entry in the optional [machine registry](<https://fmind.github.io/brain-framework/docs/configuration/#optional-machine-registration>). |
| `mcp` | Serve `search` and `read` over MCP stdio. |
| `skills DIR [--check] [--force]` | Install or update the packaged agent skills; see [skills](<https://fmind.github.io/brain-framework/docs/agents/#install-the-skills>). |
| `schema [--kind KIND]` | Print a configuration or [reply schema](<https://fmind.github.io/brain-framework/docs/retrieval/#reply-schemas>); defaults to `bf.yaml`. |
| `search QUERY [--scope SCOPE] [--limit N] [--offset N]` | Find words or an identity, optionally within one scope. |
| `read [REF] [--rel RELATION] [--offset N]` | Open home, a page, note, section, record or identity; `--rel` lists one relation's links. |
| `export [--kind edges\\|identities]` | Print the graph of the selected brains as JSON Lines, from their caches. |
| `collect SENSOR [--since TIME] [--until TIME] [--dry-run] [--allow-removal]` | Run one sensor now. |
| `run [PROGRAM] [ARGS]... [--hook EVENT] [--stdin] [--dry-run]` | Run one routine or [prompt](<https://fmind.github.io/brain-framework/docs/prompts/index.md>), or every routine of a hook, now; see [routines](<https://fmind.github.io/brain-framework/docs/routines/index.md>). |
| `update [--sensor NAME] [--routine NAME] [--prompt NAME] [--kind KIND] [--dry-run]` | Run due sensors, then due routines, then due prompts, then refresh the cache. |
| `watch [--interval N] [--poll-interval N] [--notify MODE] [--json]` | Run due programs until you quit, with a live dashboard; see [watch](<https://fmind.github.io/brain-framework/docs/schedule/index.md>). |
| `schedule [--backend NAME] [--every N] [--name NAME] [--output DIR] [--executable PATH]` | Generate native scheduler files and their installation commands; run nothing. |
| `status [--check] [--watch]` | Report caches, records, program health, logs and local usage. |
| `validate` | Check notes, links, actions, record files and program files. |
| `eval [--path evals] [--baseline FILE]` | Run retrieval cases; compare ranks with a saved reply. |
| `build [--reproject SENSOR [--dry-run]]` | Recover interrupted record writes and rebuild the search cache; `--reproject` [re-applies mappings](<https://fmind.github.io/brain-framework/docs/schema/#reproject-stored-records>). |

Every command taking `--brain NAME|PATH` selects brains as [Configuration](<https://fmind.github.io/brain-framework/docs/configuration/#select-a-brain>) describes. `collect`, `run`, `update`, `watch` and `schedule` act on exactly one brain; `update`, `watch` and `schedule` also accept repeatable `--sensor`, `--routine` and `--prompt` [selectors](<https://fmind.github.io/brain-framework/docs/schedule/#select-programs>).

### Preview an update or a collection

```bash
bf update --dry-run
bf collect local-documents --dry-run
```

`update --dry-run` lists due programs with their windows, names enabled manual programs under `manual`, and runs nothing. `collect --dry-run` **runs** the sensor and returns up to three sample records without saving them or its run. `run --dry-run` **runs** its routines or prompt but writes no action or record and records no run; a prompt's reply still returns as `text`. Both can contact providers and still append to the program's log. The second example needs the [local-documents sensor](<https://fmind.github.io/brain-framework/docs/sensors/#your-first-sensor>).

Without time options, `collect` covers the sensor's `lookback` through now. `--since` and `--until` accept `now`, `today`, `yesterday`, `12h`, `7d`, `2w`, `YYYY-MM-DD` and ISO 8601 timestamps with a timezone. A snapshot sensor still returns its whole list, and `--allow-removal` accepts [a large removal](<https://fmind.github.io/brain-framework/docs/sensors/#define-the-scope-before-adding-a-sensor>) once.

### Export the graph

`bf export` prints one JSON object per line for each claim in the selected brains and their direct references, for tools such as DuckDB or networkx. Like search, it reads the caches offline. On the [example brain](<https://github.com/fmind/brain-framework/blob/main/examples/brain/README.md#export-the-graph>):

```bash
bf export | head -1
```

```json
{
  "brain": "example",
  "origin": "bf://example/actions/2026-09-19_retention/ACTION.md",
  "relation": "tagged-with",
  "subject": "bf://example/actions/2026-09-19_retention/ACTION.md",
  "target": "bf://example/tags/retention"
}
```

Each line has `brain`, `subject`, `relation`, `target` and `origin`, plus the origin's `date` for a note or `time` for a record, and `observed` for a record. `relation` is a declared relation, `cites`, `tagged-with` or `links` for an untyped link. Lines are sorted by subject, relation, target and origin within each brain.

`bf export --kind identities` prints one line for each note or record that answers to more than its own address, such as through an alias, with every name it answers to, so duplicate subjects stand out:

```json
{
  "brain": "example",
  "kind": "note",
  "names": ["bf://example/projects/example", "bf://example/projects/example.md", "repo:example/project"],
  "ref": "projects/example.md",
  "status": "stable",
  "type": "project"
}
```

A busy cache is noted on stderr. A skipped file prints `bf: BRAIN: FILE: ERROR` on stderr and makes the command exit 1 after the other lines.

### Shell completion

Load the completion script for your shell, for example from its startup file:

```bash
eval "$(_BF_COMPLETE=source_bash bf)"   # bash, in ~/.bashrc
eval "$(_BF_COMPLETE=source_zsh bf)"    # zsh, in ~/.zshrc
_BF_COMPLETE=source_fish bf | source    # fish, in ~/.config/fish/config.fish
```

Typing `bf sea` and Tab then completes `search`.

## Exit codes and errors

Results are compact UTF-8 JSON on stdout; help and version are plain text. Dashboards use the full terminal, and `watch --json` streams JSON Lines. Each diagnostic is one stderr line starting with `bf:`; it names files and positions without quoting your content.

Collected text and file names are data. Results, `export` lines, `watch --json` snapshots and MCP text escape DEL, C1 controls and Unicode format characters, such as soft hyphens and zero-width, bidi or tag characters, as JSON `\uXXXX` escapes, with a surrogate pair beyond U+FFFF; decoded values are unchanged. Diagnostics and MCP errors also escape the other control characters, including newlines, so a file name cannot forge another `bf:` line.

| Code | Meaning |
| --- | --- |
| `0` | Success. |
| `1` | Operation or check failed, including standard output that is closed, full or stops reading before the end. |
| `2` | Invalid command-line input. |
| `130` | Cancelled by Ctrl-C, SIGTERM or a closed terminal. |

Invalid input exits 2 with one line naming the argument, the reason and the help to read, before any brain is read when possible:

```text
bf: invalid input: --limit: 0 is not in the range 1<=x<=50 (see bf search -h)
```

Other examples: `bf search "!!!"` (no word to search), `bf read 2026-13` (no such month), `bf read projects/../bf.yaml`, `bf read tasks#next` (pages have no sections), `bf read projects --rel cites` (a page lists no relation), `bf read projects/new-website.md --rel nope`, which lists the valid relations, and an empty value choosing where or on what a command acts, such as `--brain ""`, `bf init ""`, `bf collect ""`, `run --hook ""`, `--sensor ""`, `collect SENSOR --since ""`, `build --reproject ""` or `schedule --output ""`. An option that takes one value may appear once: `bf search launch --scope projects --scope concepts` is invalid instead of searching only the last scope. Only the program selectors repeat: `--sensor`, `--routine`, `--prompt` and `update --kind`.

A well-formed ref that names nothing exits 1 and suggests what exists: a close page, note, sensor, routine or prompt name (`did you mean projects?`), or the note's sections for a missing `#section`. `collect` and `run` suggest only enabled programs and fail on a disabled one with `sensor NAME is disabled in bf.yaml; set enabled: true to run it`.

`update` and `run` exit 1 when a program fails, and `update` and `build` when the refreshed cache skipped files; successful programs keep their results. `status --check` exits 1 on problems, unavailable brains or references, or scheduled programs that failed, are `overdue` or `never` succeeded, or fell silent. `validate` exits 1 on problems, never on warnings. `skills` exits 1 when it keeps a folder holding your edits, foreign files or a newer bf's copy, and `--check` unless every skill is current; see [skills](<https://fmind.github.io/brain-framework/docs/agents/#install-the-skills>).

## Using replies

Every reply states times with your local offset, and dates and field values as the [retrieval reference](<https://fmind.github.io/brain-framework/docs/retrieval/index.md>) describes, while files, run history and the cache keep UTC. Search and read replies follow that reference; `problems` is always a list of objects with `error` and, when known, `brain` and `file`.

An `update` reply has `ok`, `dry_run`, its `brain`, the `sensors`, `routines` and, when the brain declares any, `prompts` it ran or found due, `manual` for skipped manual programs, and the refreshed `index`. A `run` reply has `ok`, `dry_run`, any `hook` and its `routines`, each with a `status` of `ran`, `skipped` or `failed`; a prompt's has `prompts`: one entry with its `status` (`ran` or `failed`), the `record` it wrote (none on a dry run) and the reply `text`, or an `error`.

### Program and source status

`bf status` groups results under `brains`. Each brain has its `cache`, `notes`, `problems`, `attention`, `sources`, `routines`, [`prompts`](<https://fmind.github.io/brain-framework/docs/prompts/#schedule-and-overlap>), `coverage` totals and `usage`. `healthy` is false when any brain has a problem, an `attention` entry, an unavailable cache or cannot load. A brain that cannot load appears with only `brain`, `error` and, when known, `path`.

| Field | Meaning |
| --- | --- |
| `cache` | `ready`, or `busy` while a writer updates it. `stale` (the last cache) or `missing` (none) while an interrupted write awaits recovery, unlike the `stale` of search and read replies. |
| `pending_transaction` | `true` when `memories/.pending` holds an interrupted write; a problem names the command to recover it. |
| `runtime` | The `installed` bf version and, read as data from the brain's `uv.lock`, the `pinned` one, when the brain pins a runtime: compare their major versions before trusting either. |
| `attention` | Scheduled programs this machine runs that `failed`, are `overdue` or `never` succeeded, or are [`silent`](<https://fmind.github.io/brain-framework/docs/schedule/#silent-sources>), such as `{"sensor":"mail","freshness":"never","failed":true}`, as on the home page. |
| `state` | As in [source coverage](<https://fmind.github.io/brain-framework/docs/retrieval/#source-coverage>); a routine or prompt is `active` or `disabled`. |
| `freshness` | `fresh`, `overdue`, `never`, `manual` or `unknown`; see [timing and health](<https://fmind.github.io/brain-framework/docs/schedule/#timing-and-health>). |
| `records`, `bytes`, `latest` | Indexed records of the source, their size on disk and the newest event time. |
| `changed`, `silent` | When this brain last gained a new or changed record of the source, and `true` when that is older than its [`silent_after`](<https://fmind.github.io/brain-framework/docs/schedule/#silent-sources>). |
| `mode`, `window` | Sensor mode and, for window sensors, the contiguous collected interval `{since, until}`. |
| `last_collected` | The last collection that brought the source up to date, which freshness counts from; a [backfill](<https://fmind.github.io/brain-framework/docs/sensors/#backfills-and-coverage>) never does. Routines and prompts report `last_success`. |
| `last_run` | Counters of the last committed run: `records`, `added`, `updated`, `unchanged`, `removed`, `requested_start`, `requested_end`, `reconcile`, `elapsed_seconds`, `output_bytes`. |
| `reconciled` | The last scheduled [reconciliation](<https://fmind.github.io/brain-framework/docs/sensors/#frequent-updates-and-periodic-reconciliation>). |
| `action` | A routine's latest action. |
| `runs`, `record` | A prompt's successful runs and the record its latest run wrote; the `last_run` of its latest scheduled run holds `at`, `elapsed_seconds` and `output_bytes`. |
| `failed`, `error`, `failures`, `log` | The last attempt failed: its diagnostic, consecutive failures and `logs/NAME.log`. |
| `usage` | `7d` and `30d` counts of `search`, `empty` (searches without results) and `read`; `since`, the oldest event counted, when [rotation](<https://fmind.github.io/brain-framework/docs/limits/#processes-and-logs>) cut that window. |

For example, an hourly `mail` sensor that collected one message reports:

```json
{
  "state": "active",
  "freshness": "fresh",
  "mode": "window",
  "last_collected": "2026-09-29T15:00:00+02:00",
  "window": { "since": "2026-09-28T15:00:00+02:00", "until": "2026-09-29T15:00:00+02:00" },
  "records": 1,
  "bytes": 612,
  "latest": "2026-09-29T14:20:00+02:00",
  "last_run": { "records": 1, "added": 1, "updated": 0, "unchanged": 0, "removed": 0, "elapsed_seconds": 0.02 }
}
```

A brain also lists `warnings` when a source or authored folder holds more than 80% of the [100,000-entry scan limit](<https://fmind.github.io/brain-framework/docs/limits/#size-bounds>), such as `{"warning":"directory nears the scan limit","directory":"memories/mail","entries":81234,"limit":100000}`. Warnings never fail `status --check`.

With [jq](<https://jqlang.org/>), list projects needing review:

```bash
(
  set -e
  offset=0
  while [ -n "$offset" ]; do
    page=$(bf read projects --offset "$offset")
    jq -r '.items[] | select(.review) | [.ref, .next // ""] | @tsv' <<<"$page"
    offset=$(jq -r '.next_offset // empty' <<<"$page")
  done
)
```

Each line holds a project ref and its first open task. Pages end near 32 KiB, often after a few dozen projects, so the loop follows `next_offset` until it is absent. The subshell stops at the first failed command and keeps its exit status without closing your shell.
