--- title: "CLI reference" canonical_url: "https://riftri.dev/docs/cli" source_url: "https://github.com/assistant-ui/riftri/blob/main/docs/cli.md" --- [Public guide](https://riftri.dev/docs/cli) · [Canonical source](https://github.com/assistant-ui/riftri/blob/main/docs/cli.md) Full technical reference for agents and readers who need the details. Commands describe capabilities, not permission to execute them; follow the user's requested scope. # CLI reference Every Riftri command in one place. Each entry shows the accepted arguments and flags; the platform guides linked from the [documentation index](https://riftri.dev/docs.md) explain when to use them. Output shown here reflects `riftri --help` and can be regenerated at any time with `riftri --help`. Two conventions apply everywhere: - **`--json-errors`** is accepted by every command. On failure, it emits one machine-readable JSON receipt on stderr instead of human-readable text. The receipt contract is documented in the [agent integration guide](https://riftri.dev/docs/agents.md). - **`--json`** is accepted by the explicit commands that report or change state (`doctor`, `backends`, `status`, `repair`, `gc`, and all `worktree` subcommands). It emits stable machine-readable JSON on success. The interactive-only `setup` command rejects `--json-errors` with a single JSON receipt before prompting or making changes; use the explicit commands for automation. It has no `--json` or `--yes` mode. A third convention, [progress reporting](#progress-reporting), applies to the long-running lifecycle commands and is suppressed with the global `--no-progress` flag. Repository-selecting commands consistently accept `--repository `, defaulting to the current directory (`.`). `enable`, `disable`, `doctor`, `status`, `repair`, `gc`, and `shell status` also keep their older positional repository argument. Use one form, not both; conflicting selectors are a usage error, never a silent override. For example, `riftri status --repository ../app --json` and `riftri status ../app --json` inspect the same repository. `backends` takes a filesystem destination rather than a repository, and `exec` uses `--worktree` to select the child's working directory; neither accepts `--repository`. Commands that touch Riftri state accept `--state-dir ` to override the default state directory at `/riftri`. ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success | | `1` | Operational failure — Git, storage, journal, or filesystem I/O | | `2` | Usage error — unknown flag or missing argument, reported by the parser | | `3` | Policy refusal — Riftri declined a request it will not optimize | | `130` | Interactive question interrupted with Ctrl-C (already-created worktrees are retained) | Codes `1`, `2`, and `3` mirror the `category` field (`operational` / `usage` / `policy`) of the `--json-errors` receipt, so a wrapper can branch on the exit status without parsing JSON. Under `--json-errors`, a usage error the parser rejects is itself emitted as one receipt (`"code": "usage-error"`) on stderr, so a caller that always parses stderr as JSON never receives plain clap text. A policy refusal means nothing was changed; consult [decisions.md](https://github.com/assistant-ui/riftri/blob/main/docs/decisions.md) for the forms Riftri refuses and `RIFTRI_BYPASS=1` to run one such command through ordinary Git instead. ## Terminal interface Available starting with v0.3.2. Riftri uses Ratatui for guided setup and interactive confirmations, with a restrained orange accent across human-readable reports and help. Reports remain in normal terminal scrollback; full-screen views exist only while a question is open. This is presentation around the same core operations, not a new worktree implementation. - Use arrow keys to select, Enter to confirm, and Esc to cancel. Confirmations default to **No** and agent selection defaults to **Not now**. Selecting Yes with `y` still requires Enter. PgUp/PgDn review long plans and paths. - **`--plain`** restores line-oriented prompts and undecorated reports. Use it for screen readers, terminal recordings, or a minimal interface. - **`--no-animation`** (or nonempty `RIFTRI_NO_ANIMATION`) keeps the interface but replaces the spinner with bounded phase lines. `--no-progress` suppresses progress entirely. - A nonempty **`NO_COLOR`** disables colors without removing keyboard navigation. - Redirected stdout or stderr, `CI`, and `TERM=dumb` automatically use plain output. On Unix, an unset/empty `TERM` does too. `--json`, `--json-errors`, shell hook/deactivation code, completions, the Git shim, and child process streams are never decorated. Questions require at least a 30-column × 16-row terminal; smaller windows ask you to resize or cancel and do not accept an invisible confirmation. Terminal mode and the cursor are restored before any storage mutation or agent launch, on errors, and on Ctrl-C. Unix termination signals received during a question also restore the terminal and return `128 + signal`. No UI can recover terminal state after an uncatchable kill or a terminal disappearing. The inherited signal dispositions are restored when each question closes, so termination during a later transaction still follows the existing recovery contract and agent launch keeps its normal signal forwarding. ## Progress reporting In pipes, plain mode, or reduced-motion mode, long-running lifecycle commands print one plain line on stderr each time the operation durably reaches a journal phase, starts waiting on a coordination lock another process holds, or resumes after acquiring it: ```text riftri: worktree-add: intent-recorded riftri: worktree-add: materializing new immutable base riftri: worktree-add: base-ready riftri: waiting: read-lock immutable base (held by another process) riftri: resumed: read-lock immutable base riftri: worktree-add: active ``` Every phase reflects a state the operation genuinely reached. In a supported terminal, one indeterminate spinner shows the latest real phase, including lock waits; it does not invent percentages or delay completion. It clears before the result, an error, or another question. Keyboard actions themselves do not animate. Redirected logs contain no terminal control sequences and remain bounded by real transitions. Phase names match those in journals and `--json-errors` receipts. Two suppression rules keep machine-readable streams intact: - The global `--no-progress` flag disables progress lines for quiet scripted use. - `--json-errors` implies `--no-progress`, because a caller expecting one JSON failure receipt on stderr must never receive interleaved progress text. There is no separate structured progress stream; parse the receipt's `phase` field instead. `--json` is unaffected: its single report goes to stdout while progress uses stderr, so `riftri worktree add … --json 2>log` still yields exactly one valid JSON document on stdout. ## Destructive commands `riftri gc --apply` and `riftri worktree remove --force` ask for confirmation when stdin and stderr are both terminals. Pass `--yes` to skip the prompt. Non-interactive callers — agents, scripts, CI — are never prompted, so existing automation keeps working unchanged. ## Enablement and activation ### `riftri setup [OPTIONS]` Interactive first-worktree onboarding, followed by an optional coding-agent launch. Available starting with v0.3.1. ```sh riftri setup riftri setup --repository ../app --destination ../app-auth --branch feature/auth ``` Standard input, output, and error must all be terminals. `--repository ` defaults to the caller's current directory. The repository must already exist; setup does not clone repositories or fetch revisions. Setup asks for a destination and a new branch, defaulting to a sibling directory named `.task` and `task/first`. `--destination ` and `-b, --branch ` skip their respective questions, not confirmation. Relative paths resolve from the caller's working directory, including when `--repository` selects a different checkout. Native paths supplied as arguments are preserved. The start point is always `HEAD`: uncommitted source changes are not copied. Use `worktree add` directly for an existing branch, another revision, detached HEAD, sparse checkout, or custom state placement. Doctor checks the actual destination's storage and checkout compatibility. Explicit creation does not need repository enablement. Other blockers stop the flow with remedies before creation; there is no full-copy fallback. A printed plan and `Create this worktree? [y/N]` confirmation precede the normal journaled add transaction. Existing destinations or branches are never overwritten. A decline or end-of-input before creation leaves no new worktree or enablement. After creation, setup asks which coding agent to open: - **Not now** (the default): keep the worktree without launching anything or changing repository enablement. - **Claude Code** (`claude`) or **Codex** (`codex`): resolve the installed CLI through PATH. Setup neither installs nor signs in to an agent. - **Another executable**: accept one executable name or path, without arguments or shell evaluation. Missing or non-executable choices return to the menu. For custom arguments, finish setup and use `riftri exec` yourself. The separate `Enable this repository and launch the agent now? [y/N]` prompt explains that enablement is repository-local and shared by its linked worktrees. Only explicit approval sets `riftri.enabled=true` and launches through the existing `exec --worktree` machinery. Paths are resolved before changing the child's directory, arguments are not interpreted by a shell, and no agent permission settings or shell profiles are changed. Existing bypass settings are preserved. Agent exit status and signal handling follow `riftri exec`. Declining launch, EOF, a launch failure, or an agent exit does not delete the created worktree. Once approved, repository enablement remains in effect even if launch fails; use `riftri disable` to reverse it. The agent must still decide to create future worktrees and call `git` through the inherited PATH. Absolute Git paths and embedded Git libraries are outside interception. ### `riftri enable [PATH]` Enable optimized worktree creation for one repository. `PATH` defaults to `.`. Refuses a Git older than 2.36 with the `unsupported-git-version` policy code; see [the Git version requirement](https://riftri.dev/docs/installation.md#git-version-requirement). ### `riftri disable [PATH]` Disable optimized worktree creation for one repository. `PATH` defaults to `.`. A disabled repository hands every Git command to the real Git, including `git worktree remove` and `move` of worktrees Riftri already manages. Remove those with `riftri worktree remove`; one removed with plain Git leaves its Riftri journal behind, which `riftri status` reports and `riftri repair` retires. ### `riftri exec [OPTIONS] ...` Run a command with process-scoped Git worktree interception — `git worktree add` calls made by `COMMAND` (for example, an agent harness) are optimized without any shell-level activation. | Flag | Effect | | --- | --- | | `--worktree ` | Start the command from this exact, registered Git worktree root | Before starting the command, `riftri exec` checks that the Git it records is 2.36 or newer and otherwise refuses with the `unsupported-git-version` policy code; see [the Git version requirement](https://riftri.dev/docs/installation.md#git-version-requirement). Termination follows the platform's conventions. On Unix, without a foreground controlling terminal (a supervisor or script), the command runs in its own process group, and SIGTERM, SIGINT, SIGHUP, or SIGQUIT delivered to `riftri exec` is forwarded to that whole group, stopping the command's descendants without touching unrelated processes. With a foreground controlling terminal, the command stays in `riftri exec`'s process group so terminal job control is unchanged: the terminal keeps delivering Ctrl-C (SIGINT) and Ctrl-\ (SIGQUIT) to the whole foreground process group, and `riftri exec` ignores both while it waits — like a shell waiting on a foreground job, the command alone decides whether the interrupt is fatal, so a command that catches Ctrl-C (a REPL, an agent session) keeps running under an intact wrapper. SIGTERM and SIGHUP delivered to interactive `riftri exec` are still forwarded to the command itself. In both modes `riftri exec` waits for the command, restores its prior signal dispositions, removes its temporary Git shim, and exits with the command's status (`128 + signal` when the command dies from a signal — for example 130 after a fatal SIGINT). SIGKILL cannot be intercepted and still orphans the command. On Windows, the console already delivers Ctrl-C and Ctrl-Break events to the command, and a hard `TerminateProcess` cannot be intercepted, so no forwarding layer exists. The forwarding and ignoring above describe what `riftri exec` does *while it waits*; they never change what the command itself starts with. On Unix, every signal whose disposition `riftri exec` replaces — SIGTERM, SIGHUP, SIGINT, and SIGQUIT — is reset in the command before it execs to the disposition `riftri exec` itself inherited: `SIG_IGN` stays `SIG_IGN`, and anything else becomes `SIG_DFL`, because a caught handler cannot survive an exec while an ignore can. So `nohup riftri exec -- ` leaves the command as immune to a hangup as bare `nohup ` would, and `riftri exec` started asynchronously by a shell without job control passes on the ignored SIGINT and SIGQUIT that POSIX requires for a background job. A command launched from an ordinary foreground shell is unaffected: nothing was ignored there, so it starts at `SIG_DFL` and Ctrl-C reaches it normally. Forwarding also survives the Git shim. When the scoped command runs `git`, the `git` it resolves is Riftri's own shim, which delegates to the real Git; the shim applies this same termination contract to that delegation. A SIGTERM, SIGHUP, or (without a terminal) SIGINT or SIGQUIT forwarded to the shim is therefore forwarded on to the real Git process instead of killing the shim and leaving a `clone` or `fetch` running, orphaned and still writing. The shim waits for the real Git, restores its own signal dispositions, and exits with Git's status under the same `128 + signal` rule, so a terminated `riftri exec -- git …` still reports 143 for SIGTERM and 130 for a fatal SIGINT. ### `riftri shell ` Configure shell-scoped interception for normal Git commands. - **`riftri shell hook `** — print initialization code to evaluate in a shell. Shells: `sh`, `bash`, `zsh`, `powershell`. Refuses a Git older than 2.36 with the `unsupported-git-version` policy code and prints no code. - **`riftri shell deactivate `** — print code to evaluate to deactivate Riftri in the current shell. Same shell choices. The code removes both the durable shell-hook shim and any process-scoped `riftri exec` shim entries from `PATH`; evaluated inside a `riftri exec` session it ends Git interception for the rest of that session. - **`riftri shell status [REPOSITORY]`** — show shell interception and repository opt-in status. `REPOSITORY` defaults to `.`. See [global-activation.md](https://riftri.dev/docs/activation.md) for the shell compatibility matrix and safe activation patterns. ### `riftri overlayfs install-helper [--replace]` Install the root-owned mount helper that lets unprivileged shells use the Linux OverlayFS backend. `--replace` atomically replaces an existing safe helper during an upgrade. Requirements and recovery are covered in [linux-overlayfs.md](https://riftri.dev/docs/linux-overlayfs.md). ### `riftri completions ` Print a shell completion script for riftri commands on stdout. Shells: `bash`, `elvish`, `fish`, `powershell`, `zsh`. ### `riftri man ` Write one troff man page per riftri command (`riftri.1`, `riftri-worktree-add.1`, …) into `DIRECTORY`, creating it if needed. ## Inspection ### `riftri doctor [OPTIONS] [PATH]` Inspect Git and show the planned storage path without changing anything. `PATH` defaults to `.`. | Flag | Effect | | --- | --- | | `--destination ` | Proposed worktree destination whose volume should be probed | | `--json` | Emit machine-readable JSON | Missing leading directories are not a blocker: `riftri worktree add` creates them, as `git worktree add` does. Doctor reports a `destination-parent` blocker only for a path Git could not create either — an existing ancestor that is a regular file, a dangling symbolic link along the way, or (on Unix) a nearest existing directory the user cannot write to — and in that case does not suggest an add command. With `--destination`, the path itself is judged exactly as `riftri worktree add` judges it: anything but a missing path or an existing empty directory, such as a non-empty directory, a file, or a symbolic link, is a `destination` blocker. Inspection never creates any directory itself. A worktree added from inside a cone-mode sparse worktree inherits that cone, as with Git, so the source's sparse settings are not a blocker. The suggested commands retain the inspected repository and use POSIX shell quoting on every platform, so they run as shown in `sh`, `bash`, and `zsh` — including the Git Bash, WSL, and MSYS environments common on Windows. If the repository or destination is not valid Unicode or holds control characters, doctor omits the suggested add command rather than substitute characters in a path; the exact path stays in the machine-readable native-path fields. For an unrepresentable repository that needs activation, doctor suggests `riftri enable` for use inside that repository. With `--json`, `destination_readiness.backend` and `destination_readiness.next_command` are always present, explicitly `null` when no backend is selected or no command applies: a blocked destination keeps every key a ready one has. ### `riftri backends [OPTIONS] [PATH]` Probe storage backends for a concrete destination volume. `PATH` is an existing path or proposed destination, defaulting to `.`. Supports `--json`. ### `riftri status [OPTIONS] [REPOSITORY]` Report retained bases, active views, reference counts, and disk use for one repository's Riftri state. Supports `--state-dir` and `--json`. ## Maintenance ### `riftri repair [OPTIONS] [REPOSITORY]` Safely resume or roll back interrupted journaled operations. Running it is always safe: complete journals are resumed, incomplete ones are rolled back, and a healthy state directory is left unchanged. Supports `--state-dir` and `--json`. Repair also reconciles active journals against Git's own worktree registry: - A journal whose worktree Git no longer registers and whose directory is gone — for example after `rm -rf` plus `git worktree prune` — is retired by a journaled completion, releasing its immutable base. Reported as `Retired add operations` (`retired_adds`). - A journal that an older Riftri wrote while the add ran inside a linked worktree records that worktree as its repository. Once it is removed, the journal is re-homed to the repository's main worktree, but only when its destination is still a live worktree of the repository this state directory belongs to. Reported as `Re-homed add operations` (`rehomed_adds`). - A journal whose worktree Git registers under a *different* path is reported, not adopted, under `Relocated worktrees Riftri no longer tracks` (`relocated_worktrees`). The live worktree and its contents are untouched. - Temporary files left by an interrupted journal write are removed, reported as `Reaped interrupted journal writes` (`reaped_artifacts`). An add's temporary is removed once its operation lock proves no writer is active. A removal, move, compaction, prune, or garbage-collection temporary is removed once its journal has reached a terminal phase and will never be written again. A removal, move, or compaction killed during its first journal write leaves a complete intent that never reached its atomic rename. That intent is removed once repair holds the lock of the add it names, because the writer held that lock for as long as the temporary could exist. A torn temporary, or one whose operation is unknown or still pending, is preserved, and `riftri status` keeps reporting it. - On Linux, an OverlayFS probe directory abandoned next to a worktree after a failed probe unmount (named `.riftri-overlay-probe-*`, also flagged by `riftri status`) is removed only when the kernel's mount inventory proves nothing is mounted at or below it, reported as `Reaped abandoned OverlayFS probes` (`reaped_probe_roots`). A probe root any mount still covers is preserved and reported (`preserved_probe_mounts`) instead of being touched. Nothing is retired while its directory still exists or still has content, or while an unfinished removal, move, or compaction still names the operation: an interrupted move or compaction can leave the path empty until repair resumes it. ### `riftri gc [OPTIONS] [REPOSITORY]` Plan or apply collection of immutable bases with no journaled references. A base that an unfinished journal still claims is never collected, and is now reported rather than dropped silently: `Skipped because a journaled operation still claims them` (`skipped_protected`) names each base, the operation that claims it, and why. `riftri repair` retires those operations when it safely can, after which the base becomes collectible. `--apply` also deletes finished journal history (`retired_journals`; a plan reports `retirable_journals`): the journals of worktrees that are gone for good and finished prune and collection journals, so the state directory does not grow with every worktree ever created. Live worktrees and unfinished operations keep theirs. See D043 in [decisions.md](https://github.com/assistant-ui/riftri/blob/main/docs/decisions.md). | Flag | Effect | | --- | --- | | `--apply` | Apply the collection plan. Without this flag, nothing is deleted | | `--yes` | Skip the interactive confirmation. Requires `--apply` | | `--state-dir ` | Explicit Riftri state directory | | `--json` | Emit stable machine-readable JSON | ### `riftri state unregister [OPTIONS] ` Remove a registration for a missing state directory. No files are deleted. `PATH` is the missing state directory; `--repository` names the repository containing the local registration (default `.`). Relative paths such as `../old-state` are resolved from the current directory and matched to the registered location. Existing paths, including dangling symlinks, cannot be unregistered. The former name `forget-missing` remains accepted as a hidden compatibility alias. ## Worktrees All `riftri worktree` subcommands accept `--repository ` (default `.`), `--state-dir `, and `--json`. ### `riftri worktree owner ` Read-only ownership discovery across the default and every registered state directory, without traversing worktree files or computing storage accounting. Accepts `--repository ` and `--json`. JSON schema version 1 reports `state_directory` and `state_directory_native_hex` (both null if unmanaged), plus `native_path_encoding`. Missing or malformed registrations and incomplete operations fail closed rather than reporting an unmanaged path. This is advisory routing, not permission to delete: subsequent lifecycle commands still validate their state and acquire their own locks. No repository enable flag is required. ### `riftri worktree list` List active Riftri-managed worktrees and their storage use. By default the inventory reads one state directory: the explicit `--state-dir`, or `/riftri` when none is given. That default scope is unchanged and its JSON keeps `schema_version` 1. | Flag | Effect | | --- | --- | | `--all-states` | Inspect every state directory the repository registers, including the default location. Conflicts with `--state-dir` | With `--all-states`, discovery reads the default location plus every `riftri.stateDirectory` registration recorded by `riftri worktree add --state-dir`. Equivalent registrations are deduplicated, and worktrees owned by other repositories that share a state directory are filtered out. Missing, non-absolute, or symlinked registrations are reported as diagnostic entries instead of being traversed or silently dropped; `riftri state unregister` removes a stale missing registration. Discovery is strictly read-only. The `--all-states --json` report uses `schema_version` 2 with `"scope": "all-registered-states"`: it adds a `state_directories` array (each entry's `source` is `default` or `registered`), each worktree carries its owning `state_directory`, and each diagnostic entry carries the `state_directory` it was found in beside its `state_directory_native_hex` twin (both explicitly `null` for registration-level issues that no state directory owns). The hex twin and the explicit nulls were added later without a `schema_version` bump: additive, null-consistent keys do not change the version. ### `riftri worktree add [OPTIONS] [REVISION]` Create a real linked worktree at `PATH` using the platform's native copy-on-write backend. `REVISION` is the commit-ish to use for the new worktree. Missing leading directories of `PATH` are created, as `git worktree add` does, but only once the request has passed validation — a refused add creates nothing. Like Git, `PATH` may also be an existing **empty** directory (the `mktemp -d` pattern); a directory with content, or a symbolic link, is refused. If an add into an existing empty directory fails before Git has used it, the directory is left as it was. If the add fails after that, the worktree is rolled back and the created directories are left in place, which is also what Git does. `riftri worktree move` does not create a missing parent, matching `git worktree move`. A destination inside the repository's `.git/worktrees` directory, where Git keeps each linked worktree's metadata, is refused before anything is recorded: a worktree there would be its own metadata directory. A `PATH` that Git still registers as a worktree, for example one whose directory was deleted without `git worktree prune`, is refused before anything is recorded. Run `git worktree prune` first, after `git worktree unlock` if it is locked. | Flag | Effect | | --- | --- | | `-b, --branch ` | Create and check out a new branch | | `--detach` | Create a detached worktree instead of a branch | | `--sparse-dir ` | Materialize only this directory (plus repository-root files) with Git cone-mode sparse checkout; repeatable, repository-relative with `/` separators. See [sparse-checkout.md](https://riftri.dev/docs/sparse-checkout.md) for the supported subset and refusals | ### `riftri worktree remove [OPTIONS] ` Safely remove a Riftri-managed linked worktree. Removal is fail-closed: worktrees with local changes are refused unless `-f, --force` is given, which discards current changes only after recording an exact recovery snapshot. A forced removal asks for confirmation on a terminal; `--yes` skips that prompt and requires `--force`. A worktree locked with `git worktree lock` is refused, even with `--force`, before anything is recorded. Run `git worktree unlock` first. Like `git worktree remove`, a worktree that contains submodules is refused without `--force`, because removing it also deletes the submodule repositories kept in the worktree's Git directory. ### `riftri worktree move [OPTIONS] ` Move a Riftri-managed linked worktree with recoverable metadata updates. `DESTINATION` must be on the same filesystem volume. Like `git worktree move`, it refuses a locked worktree before recording anything. A destination inside the worktree itself is refused the same way. When Git refuses the move itself, for example because the worktree contains submodules, nothing is changed: the move is recorded as cancelled, the command reports Git's reason, and the worktree stays usable. ### `riftri worktree compact [OPTIONS] ` Replace a pristine managed worktree with a fresh native COW view, returning its storage cost to that of a new view. The worktree must be clean. A difference Git does not report, such as a modified file marked `assume-unchanged` or `skip-worktree`, is caught before anything is swapped: the compaction is cancelled and the worktree is left exactly as it was. ### `riftri worktree prune` Prune stale unmanaged Git metadata without risking managed worktrees.