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
| Command | Purpose |
|---|---|
saggar <path…> | Add or focus a repository in the project menu; the one verb that can launch Saggar |
saggar init | Create .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 handoff | Hand 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 approve | Answer 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|clear | Inspect 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 fire | Accept a versioned local event from stdin |
saggar event status <uuid> | Inspect a durable event receipt |
saggar onboarding | Take 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 capabilities | Alias for saggar schema |
saggar --reason <text> <workspace command…> | Explain why a workspace change is requested |
saggar --version | Print the installed version |
saggar help | Show 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
| Code | Meaning |
|---|---|
| 0 | Done |
| 1 | The command was understood, and Saggar refused or couldn't do it |
| 2 | The arguments didn't parse |
| 3 | Nobody 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.
| Option | Effect |
|---|---|
--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 |
--team | Experimental, Claude only: show Claude teammates as Saggar terminals |
--queue | Start once the project's active terminals, this one included, settle |
--observe | Codex only: link an observer told not to edit |
--pair | Codex 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
| Option | Effect |
|---|---|
--name <name> | Label the command; the command text is the default |
--primary, --companion, --monitor, --background, or --quick | Choose where it opens |
--browser or --simulator | Reveal its live output on the phone app |
--pin | Pin 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.