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-approval**. It has three states.

## The three modes

### off

crystl auto-approves nothing. Every tool call waits for you. A floating approval card 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 the subagent tools `Agent` and `Explore`. 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, and so do writes into `.git/`.
- **Safe git auto-approves.** `git add`, `commit`, `status`, `diff`, `log`, `show`, `rev-parse`, `git stash list` / `git stash show`, and `git branch` either in its listing forms or creating a branch by name, which is additive. Anything that can discard work asks.
- **Everything else asks.** Any other command, any other tool. The one exception is a worker’s own reporting helpers.

#### Writes into `.git/` still ask

`.git/` sits inside the tree and is not workspace. A write to `.git/config` or `.git/hooks/*` installs something a _later_ git run executes, often outside the run that wrote it, so it raises a panel even under **in gem**.

#### One plain invocation only

The safe-git list covers one plain git command and nothing else. A shell operator anywhere disqualifies the request whatever the subcommand: `&`, `||`, `;`, `|`, a backtick, `$(`, `=(`, `>`, `<`, or a newline.

### 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. Now your gem’s mode takes precedence:

- Gem **off**: nothing auto-approves, whatever the repo declares.
- Gem **in gem**: `bypassPermissions` and `dontAsk` grant nothing extra.
- Gem **all**: everything auto-approves, including a declared bypass.

**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.

## Pause

**pause auto-approvals** in the crystl app menu is a global kill-switch. It overrides every mode: per-gem, per-shard, and global settings alike.

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

## Approval cards

Each approval card 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**, **always**, and **deny** buttons, plus **show ›** to jump to the shard

### 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, and **dismiss** clears the panel.

## Per-gem auto-approval

You can override the global mode per gem. Open the gem menu and choose **auto-approval ›** to set the mode directly.

### Priority chain

crystl resolves the active mode in this order:

1. **pause auto-approvals**: the app-menu kill-switch.
2. **Session override**: a temporary override supplied programmatically for an individual shard.
3. **Per-project config**: the nearest `.crystl/project.json` at or above the shard’s working directory.
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.

When you set a per-gem mode, the terminal prints a confirmation.

### Global default

Set the global default in settings, **agents > defaults**, under **AUTO-APPROVAL (DEFAULT)**. This is the fallback used when a gem has no override of its own.

## 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.

## Which agents get cards

| Agent | Cards | What is different |
| --- | --- | --- |
| [Claude Code](/content/docs/agent-claude-code/index.html) | yes | the full behaviour this page describes |
| [Codex](/content/docs/agent-codex/index.html) | yes | **option+deny** denies the one call |
| opencode | yes | cards start from opencode’s own permission event |
| goose | yes, in `auto` mode | goose resolves approvals before the hook runs |
| [Kimi](/content/docs/agent-kimi/index.html) | yes | **allow** releases the hold rather than pre-approving |
| GitHub Copilot CLI | yes | **allow** fully replaces Copilot’s own prompt. |

## A worker’s approval goes to its orchestrator first

When a shard you [fanned out](/content/docs/fanout/index.html) hits an approval, the card does not land on you straight away. crystl dispatches one line into the **lead** shard’s terminal instead, naming the request and the two commands that decide it.

## From the CLI

Approvals are also reachable via the [crystl CLI](/content/docs/cli/index.html). 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
```

## Switching modes

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