Approval Modes

Updated August 23, 2026

When an agent needs to run a tool (editing a file, running a command, searching your codebase), it asks for permission. crystl intercepts that request and decides whether to auto-approve it or float it to you as a glass panel.

The setting is called auto-approve. It has three states.

The three modes

off

crystl auto-approves nothing. Every tool call waits for you. A floating approval panel appears with Allow and Deny buttons.

Best for: unfamiliar code, sensitive operations, watching what an agent does step by step.

in gem

Reads anything. Makes changes only inside this gem. Runs git that cannot lose work. Asks before anything else.

That sentence is the whole contract. In detail:

  • Reads auto-approve. Read, Glob, Grep, WebSearch, WebFetch, and spawning a subagent. Reads change nothing, so they never wait.
  • Writes auto-approve inside the tree. Edit, Write, and NotebookEdit go through when the target path is inside the shard’s own worktree or repo root. A write outside that tree asks. Codex edits arrive as apply_patch and follow the same rule: an in-tree add or update goes through, a deletion or an out-of-tree path asks.
  • Safe git auto-approves. git add, commit, status, diff, log, show, rev-parse, git branch in its listing forms, and git stash list / git stash show. Anything that can discard work asks. That includes bare git stash, stash drop, stash clear, and git branch -D.
  • Everything else asks. Any other command, any other tool.

“In gem” describes where changes happen, not where every tool reaches. WebFetch and WebSearch still reach the internet. They alter nothing, and reads are what make the middle mode livable.

Best for: leaving a working agent alone without handing over the machine.

all

Every tool call is approved. Nothing waits.

Best for: a task you defined tightly, in a gem you are happy to hand over.

The gem is the ceiling

A permission mode declared inside your project may narrow what crystl auto-approves. It may never widen it.

Agents read permission settings out of the repo: a .claude/agents/*.md file can declare permissionMode: bypassPermissions, and a CLI flag can ask for the same thing. crystl used to honour that before it looked at your gem’s mode, so a file in the repo could auto-approve arbitrary commands under a gem you had deliberately set to the middle mode. Anyone who could write that file, including an agent with write access, had unattended command execution.

Now your gem’s mode wins:

  • Gem off: nothing auto-approves, whatever the repo declares.
  • Gem in gem: bypassPermissions and dontAsk grant nothing extra. The request gets exactly what in gem grants on its own. acceptEdits is narrower than the gem, so it still applies and limits auto-approval to edit-safe tools.
  • Gem all: everything auto-approves, including a declared bypass. That is your choice to make, and crystl honours it.

You may notice this as “crystl started asking me things.” If a repo you work in declares a bypass mode, work that used to run unattended under in gem now raises panels. That is the fix, not a regression. Set the gem to all if you want that repo’s declaration to hold.

Pause

pause auto-approvals in the crystl app menu is a global kill-switch. It is not a mode. It sits above every mode: per-gem, per-shard, and global settings all lose to it. Agents keep running, and every tool request escalates to a panel until you choose resume auto-approvals from the same menu.

Use it when you want to take the wheel back for a minute without editing any gem’s setting.

Approval panels

Each approval panel is a floating glass panel that shows:

  • The tool name (Edit, Bash, Read, etc.)
  • The target (file path or command)
  • A preview of what will change
  • Allow and Deny buttons

Panels are non-activating. They appear on screen without stealing focus from your current window. You can click Allow or Deny and keep working.

No panel for the shard you’re watching

When crystl is focused and the requesting shard is on screen, the agent’s own approval menu is already in that terminal, so crystl doesn’t float a second copy of it over the top. Answer it in the terminal, or switch away and the panel appears. An approval that has no in-terminal menu always gets its panel, since that panel would be the only way to answer. Full rule: Panels you’re already watching.

Stop the whole session

Deny rejects just the one tool call. Your agent keeps going and can try something else. To stop the entire session instead, hold Option while clicking Deny. A confirmation appears first (“stop the whole session?”) so an accidental Option press can’t kill a run. There’s no separate button for this; it’s a deliberate power-user shortcut.

Allow all

When two or more approval requests are queued, an Allow All bar appears so you can batch-approve them in one click.

Denial panels

When crystl denies a tool under in gem, an orange denial panel appears showing what was blocked. A Retry button lets you override the auto-deny without switching modes.

Per-gem auto-approve

You can override the global mode per gem. Open the gem menu (the on the gem’s tab) and choose auto-approve › to set the mode directly, or settings for the full panel where AUTO-APPROVE (CLAUDE / CODEX) sits on the general tab. Either way you get four options:

  • Default (Global): use the global mode
  • off: auto-approve nothing in this gem
  • in gem: reads, in-tree changes, and safe git
  • all: auto-approve everything in this gem

The per-gem setting is saved in .crystl/project.json and persists across sessions.

Priority chain

crystl resolves the active mode in this order:

  1. pause auto-approvals: the app-menu kill-switch. Nothing auto-approves while it is on.
  2. Session override: a temporary override supplied programmatically for an individual shard. The shard menu does not expose this.
  3. Per-project config: the nearest .crystl/project.json at or above the shard’s working directory, up to the gem root. Narrowest wins, so an isolated shard’s own worktree mode applies to that shard alone, and a sub-project you cd into can set its own mode without the gem root overriding it.
  4. Crystl session default: the fallback a quest or fanout launch sets for its own shards.
  5. Global mode: the default set on the agents settings page, defaults tab.

When you set a per-gem mode, the terminal prints a confirmation: Approval mode changed from X to Y.

Global default

Set the global default in Settings → agents → defaults, under AUTO-APPROVE (DEFAULT). This is the fallback used when a gem has no override of its own. A gem-level mode always wins over it.

Upgrading from the old names

The modes used to be called Ask Every Time, Smart, and Auto. They are now off, in gem, and all. Same three modes, clearer names.

Nothing you have configured changes. The stored values are still manual, smart, and all, so an existing .crystl/project.json keeps working untouched, and a gem set to Smart is a gem set to in gem. There is nothing to migrate.

The CLI accepts both spellings with no deprecation warning. --approval smart and --approval in-gem produce the same shard, as do --approval manual and --approval off.

From the CLI

Approvals are also reachable via the crystl CLI. This helps when you’re triaging from another terminal, scripting a workflow, or running an agent that needs to coordinate with whoever holds the approval seat.

crystl pending                       # list every pending request
crystl approve 3                     # approve request id 3
crystl deny 4                        # deny request id 4
crystl wait pending --timeout 60     # block until the next one appears, then exit
crystl events --type pending_changed # stream approval lifecycle as JSON lines

crystl wait pending is built on the bridge’s SSE stream: no polling, exits cleanly on Ctrl-C, returns exit 1 on timeout.

Switching modes

You can change the mode at any time from the gem menu’s auto-approve › shortcut or the Gem Settings panel. The change takes effect immediately for pending and future requests in that gem. The individual shard menu has no auto-approve setting.