Skip to main content

saggar CLI

Install the command from Settings ▸ CLI. Most control commands need Saggar to be running and must run inside a Saggar terminal, where SAGGAR_SESSION names the terminal the command speaks for.

The CLI's own help calls each session a terminal, and so does this page. Elsewhere in these docs the same thing is a session. Literal names such as saggar session, --session-id, and SAGGAR_SESSION keep their spelling.

Commands​

CommandPurpose
saggar <path…>Add or focus a repository in the project menu; the one verb that can launch Saggar
saggar initCreate .saggar/project.json in the current folder
saggar config [--docs <path>|--clear-docs]Print the project's shared configuration, or set or clear its documentation folder
saggar config command [options] -- <command…>Add or update a project command
saggar settings [--json]List every app preference
saggar settings get <key> [--json]Inspect one app preference
saggar settings set <key> <value>Set a plain string or JSON value
saggar settings reset <key>Restore one preference to its default
saggar hooks [--json]Show each provider's hooks, their config file, and anything blocking them
saggar hooks install|remove <provider>Install or remove Saggar's hooks for one provider
saggar attention [--note] [message]Flag this terminal as needing you, or leave a quiet note
saggar monitor [--discreet] <command…>Dock a monitor running the command
saggar quick <command…>Run the command in a one-shot window
saggar agent <agent> [options] [--] <task…>Run an agent in a new terminal, or reuse an idle one
saggar message <text…>Message the linked pairing counterpart
saggar message --to <id|name> <text…>Message any terminal
saggar handoffHand the worktree to the pairing partner
saggar add <path>Register a project without focusing it
saggar close [--force] <id|name>Close an idle terminal, or a busy one with --force
saggar list [--json]List terminals with their stable short ids
saggar status [--json]Alias for saggar list
saggar read [id|name] [options]Read bounded output from one terminal
saggar explain [id|name] [--json]Say why a terminal wears its status
saggar wait [id|name] [options]Block until a terminal settles, reaches a status, or prints a line
saggar focus [id|name]Bring a terminal to the main pane
saggar approveAnswer the prompt waiting in this terminal
saggar session [id|name] [--json]Inspect a terminal's name, presentation, and turbo setting
saggar session [id|name] set <setting> <value>Change one of those settings
saggar resume show|clearInspect or remove this terminal's custom resume binding
saggar resume set [options] -- <command…>Set this terminal's custom resume binding
saggar turns [--json]List this terminal's recorded agent turns
saggar turn <event>Record a hook payload from stdin; installed hooks call this for you
saggar event fireAccept a versioned local event from stdin
saggar event status <uuid>Inspect a durable event receipt
saggar onboardingTake a one-minute tour that shows each surface working
saggar schema [command…]Describe the CLI as clispec v0.2 JSON, optionally narrowed to one command
saggar capabilitiesAlias for saggar schema
saggar --reason <text> <workspace command…>Explain why a workspace change is requested
saggar --versionPrint the installed version
saggar helpShow current verbs and flags

Run saggar help for the current option details. init and config work on the current folder, settings and hooks change this Mac's own files, and turn, turns, and schema need no running app. Everything else needs Saggar running, and terminal actions also need SAGGAR_SESSION.

Exit codes​

CodeMeaning
0Done
1The command was understood, and Saggar refused or couldn't do it
2The arguments didn't parse
3Nobody to ask: Saggar isn't running, or this isn't a Saggar terminal

Two kinds of permission​

A CLI command that changes what you see, or runs something, waits for the Mac to allow it. The CLI blocks until the action runs or is refused. Requests expire after two minutes, and rejection, cancellation, and expiry all exit with code 1. A second request is refused while one is still waiting. Every completed action shows a card with its source and result. Both permission settings are local to this Mac and can only be changed in Settings.

Workspace changes​

Settings ▸ CLI ▸ CLI workspace changes governs navigation: focus, project opens, close and close --force, renaming or changing the presentation of another terminal with session set, and teammate focus or close.

  • Ask first is the default. Choose Allow or Reject in the bottom-left card.
  • Allow after 10 idle seconds offers Allow now and Cancel, with elapsed time along the card's bottom edge. The countdown pauses while the card isn't visible in the active main window, and restarts when you type or navigate.
  • Allow immediately runs the action as soon as Saggar receives it.

Go back on the result card restores the previous navigation when it's still available. It doesn't reverse commands that have already run. If another attention prompt occupies the bottom-left corner, the workspace card appears beside it.

Command execution​

monitor, quick, agent, message, message --to, handoff, and resume set need execution consent, whatever the workspace setting says. The Mac offers Allow once or Allow for this session. A session grant ends when the terminal closes or restarts, or when Saggar quits, and you can revoke it under Settings ▸ CLI ▸ Command execution. The countdown and immediate settings never grant execution.

Reads, saggar add, attention, approve, session without set, session set on the calling terminal, resume show, resume clear, and onboarding don't prompt. Opening a project launches Saggar in the background before requesting consent.

An optional reason explains a workspace request:

saggar --reason "The test failure needs your input" focus "API tests"

Project and directory scope​

monitor, quick, and agent inherit the calling terminal's project and recorded working directory. They identify that terminal through SAGGAR_SESSION, not the CLI process's current directory, so a shell cd doesn't retarget them.

To run monitor or quick in another project, invoke it from one of that project's terminals. To start or reuse an agent elsewhere, pass an absolute path with saggar agent --cwd /path/to/project. saggar add <path> registers another project without changing focus or the calling terminal's scope.

Agents​

saggar agent codex "investigate the relay test failure; report findings only"
saggar agent claude.opus55 --title "Auth review" --cwd /repos/api -- review the authentication change

The agent is a provider name, or provider.model when the model matters. The task is the rest of the line, or everything after -- when options are present. Saggar shell-quotes it.

OptionEffect
--session-id <id|name>Reuse an existing idle terminal from saggar list
--title <title>Name the terminal
--cwd <path>Start from this directory
--flags <provider flags>Pass a trusted shell fragment to the provider
--teamExperimental, Claude only: show Claude teammates as Saggar terminals
--queueStart once the project's active terminals, this one included, settle
--observeCodex only: link an observer told not to edit
--pairCodex only: link a pairing partner

--queue starts a new terminal, so it can't take --session-id. --observe and --pair are exclusive, need a new terminal, and can't take --queue. Reuse refuses a terminal with a running child process.

--flags is inserted after the provider and model arguments, before the quoted task. The fragment is trusted shell syntax, so operators inside it are live:

saggar agent claude --flags '--permission-mode plan --add-dir ../shared' -- "Fix the tests"

Messages and handoff​

saggar message --to <id|name> <text…> types one attributed line into another terminal. --to has to come first; everything after the target is the message. The line arrives as [<sender>] <message>, where the sender is the calling terminal's name. An agent reads it as a prompt and queues it if it's mid-turn, a shell runs it as a command, and a foreground process gets it on stdin.

Three targets are refused: a terminal that has gone, the calling terminal itself, and a terminal waiting on a permission prompt. Answering a prompt is saggar approve's job.

A bare saggar message <text…> goes to the linked pairing counterpart, and only while it's idle. Observers can't message. saggar handoff is lead-only and moves both terminals into the finishing phase, telling the Codex partner it may make the final changes, run checks, and commit.

Closing terminals​

saggar close refuses a terminal that still has a running child process. saggar close --force closes it anyway and ends whatever is running. It still asks on the Mac like any close, and the audit log records it as a forced close.

Reading and waiting​

saggar read prints plain text: the calling terminal's last 80 lines by default, or another terminal's by id or unique name. --lines <1…500> chooses the tail length, --screen reads only the rows currently painted and can't take --lines, and --json prints metadata and text as JSON. Terminal output may contain private material, so request the smallest useful window.

saggar wait blocks until a terminal is anything but working, or until the statuses named by --until (needs-you, working, idle, finished, or failed, repeatable) or a line matching --match <text>. The two can't be combined. --timeout is in seconds, 120 by default and 3600 at most. A timeout exits 1 and names the current status, and a terminal that closes fails the wait rather than letting a replacement satisfy it.

saggar explain prints the rule that decided a terminal's status, such as a prompt cue, a hook claim, or recent output, with the evidence behind it. All three are reads: they never type or answer anything.

Approving your own prompt​

saggar approve is refused until you turn on Let agents approve their own prompts in Settings ▸ CLI. Destructive commands are held back either way.

Sessions​

Dragging a terminal from the project menu into a chat with ⌥ held types saggar session <id> there, so a terminal can be named in a sentence to an agent. The terminal's ⋯ menu copies the same line.

saggar session defaults to the calling terminal. Its presentation setting accepts automatic, conversation, or terminal; automatic clears the live override. Setting turbo to true needs Let agents approve their own prompts turned on, and a terminal can never arm turbo on itself. Turning it off always works.

Project configuration​

saggar config prints the current folder's .saggar/project.json. --docs <path> sets the documentation folder, which must be a relative path inside the project, and --clear-docs clears it. Run saggar init first when the folder has no manifest.

saggar config command writes .saggar/commands.md:

saggar config command --name "Example site" --monitor --browser --pin -- npm run start
OptionEffect
--name <name>Label the command; the command text is the default
--primary, --companion, --monitor, --background, or --quickChoose where it opens
--browser or --simulatorReveal its live output on the phone app
--pinPin it to the project's terminal headers; the project must already be open in Saggar

Repeating a name replaces its command, role, and surface.

App settings​

saggar settings works without a running app. Its keys match Saggar's stored preference keys; use reset to return one to its built-in default. set accepts plain strings or JSON values such as false, 0.5, or ["claude", "codex"]. Keys that widen what an agent or a paired device can do, such as self-approval, remote access, turbo defaults, and enabled providers, can be read here but only changed in Settings.

Provider hooks​

saggar hooks is the command-line form of the hook install in Settings ▸ Providers, and works without a running app. A bare saggar hooks only reads: it prints one row per provider, installed, not-installed, or no-cli, with the file an install would edit, and lists any remaining blocker underneath, such as Codex waiting for you to trust its entries in /hooks.

install and remove each name exactly one provider. An install refuses when the provider's own CLI isn't on this Mac and names the command that installs it. Claude's install stops rather than replacing a marketplace named saggar registered from somewhere else; pass --replace-marketplace to replace it, the same choice the Settings pane asks you to confirm.