# CLI

`fp` — headless, scriptable, capture-first, with an exit-code contract you can rely on.

## What it is

A real headless CLI, not a remote control for a running window. It works on a server, in CI and in a container with no display.

The exit code is a published API surface, as stable as the JSON output. Changing what a code means is a breaking change.

```bash
fp login
fp capture "the thing I will otherwise forget"
fp query "meeting notes" --ndjson | jq .title
```

## Commands

| Command | Does |
| --- | --- |
| `fp capture [TEXT…]` | capture to the inbox from arguments, a file or stdin |
| `fp add --last` | capture the previous shell command, its output, exit code and timing |
| `fp ask "QUESTION"` | an answer with block-level citations you can fetch |
| `fp query TEXT` | text search, one page; `--ndjson` streams. Structured filters are MCP-only (`fastpad_query`) |
| `fp watch` | the workspace change feed, as a pipe |
| `fp turns` | proposals waiting for review |
| `fp diff TURN_ID` | one proposal as a per-block diff |
| `fp turn-abandon TURN_ID` | drop a proposal; accepting one needs a grant (below) |
| `fp pull` / `fp push` | the Markdown mirror, out and back |
| `fp mcp` | a local stdio MCP bridge and keychain credential broker |
| `fp agents` | agent principals, scopes, autonomy, last seen |
| `fp commands --json` | the command table, as data |

`--json`, `--ndjson`, `--quiet`, `--dry-run`, `--profile` and `--timeout` work on every command. Prose never touches stdout in either JSON mode.

## Exit codes

| Code | Means |
| --- | --- |
| `0` | the requested effect happened |
| `1` | unclassified failure |
| `2` | usage error — unknown command, bad flag, missing stdin |
| `3` | not authenticated, or the credential was rejected |
| `4` | the target does not exist, or is no longer visible |
| `5` | conflict — stale state, idempotency, ambiguity |
| `6` | rate limited; `retry_after_ms` is on the problem document |
| `7` | accepted locally and spooled for replay. Not a failure |
| `8` | policy denied, including every attempt to approve a proposal |

```bash
fp --exit-codes     # prints the contract
```

## Capture never fails

`fp capture` does not fail on a network error. It writes to a durable local spool with an idempotency key and exits 7, and the next successful command drains it. Capture is the interaction that decides whether you keep using a notes app, so it has to work on a plane.

```bash
fp capture --status    # pending / delivered / failed
fp capture --flush     # drain now; safe from cron or a shell hook
```

## `fp accept` needs a grant

It accepts a turn your credential opened, and only if that credential carries `turns:accept` — which a plain `fp login` never asks for. Log in with `fp login --scope turns:accept` to get it, and the approval page names it. Without the scope, `fp accept` exits 8 with `approve-not-permitted`; run `fp diff` to read the proposal and hand the URL it ends with to a person.

Every accept from the terminal is recorded like any other agent accept: attributed to the credential, marked `agent_accepted`, and listed on the Agent activity page for someone to review or revert.
