> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qoder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Permissions

## Permission Modes

Permission modes determine how Qoder handles tool calls — each mode balances automation and security differently.

| Mode                        | Best for                            | Behavior                                                                                                                                                                                                                                                                                                                                                                |
| :-------------------------- | :---------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `default`                   | Normal interactive use              | Safe reads and internal actions run automatically; sensitive actions require confirmation.                                                                                                                                                                                                                                                                              |
| `accept_edits`              | Routine coding tasks                | Automatically approves safe file edits inside working directories. Shell commands, external actions, and sensitive paths still go through normal checks.                                                                                                                                                                                                                |
| `auto`                      | Unattended execution                | Authorization is decided by the agent.                                                                                                                                                                                                                                                                                                                                  |
| `bypass_permissions` (YOLO) | Trusted local experiments only      | Skips approval prompts. A small set of path-shape protections stays active — see Path Shapes. An `rm` that would remove the working directory or one of its parents, the home directory, the filesystem root or a top-level directory still asks (so does one whose target is a possibly-empty variable or an unresolvable glob); that prompt offers no "always allow". |
| `dont_ask`                  | Headless flows that must not prompt | Never prompts. Any action that would normally ask is denied instead.                                                                                                                                                                                                                                                                                                    |

### Mode Cycling

In interactive sessions, press **Shift+Tab** to cycle through all permission modes. Press **Ctrl+Y** to jump directly to YOLO mode.

### Startup Parameters

Use `--permission-mode` to set the default behavior for the current session:

```shell
qoder --permission-mode default
qoder --permission-mode accept_edits
qoder --permission-mode plan           # Legacy compat: translates to default + enters Plan work state
qoder --permission-mode auto
qoder --permission-mode bypass_permissions
qoder --permission-mode yolo           # Equivalent to bypass_permissions
qoder --permission-mode dont_ask

# Shortcuts
qoder --yolo                         # Equivalent to --permission-mode bypass_permissions
qoder --dangerously-skip-permissions # Same as above
```

In interactive sessions, you can also press **Ctrl+Y** to quickly switch to YOLO mode.

Multiple naming formats are supported (case-insensitive):

| Standard (snake\_case) | camelCase           | Alias          |
| :--------------------- | :------------------ | :------------- |
| `accept_edits`         | `acceptEdits`       | —              |
| `bypass_permissions`   | `bypassPermissions` | `yolo`, `YOLO` |
| `dont_ask`             | `dontAsk`           | —              |

Example:

```shell
# The following are equivalent
qoder --permission-mode bypass_permissions
qoder --permission-mode bypassPermissions
qoder --permission-mode yolo
qoder --yolo
qoder --dangerously-skip-permissions
```

Non-default modes only take effect in trusted directories. If the current directory is not trusted, Qoder falls back to `default`.

## How Decisions Are Made

Qoder checks permissions before every tool call. The result is always one of three outcomes:

- **`allow`**: Execute the tool immediately.
- **`ask`**: Requires external confirmation before execution.
- **`deny`**: Block the tool call.

Permissions apply to file reads and edits, Bash commands, web fetches, MCP tools, subagents, and other built-in tools.

### Decision Order

Qoder evaluates permissions in a fixed order:

1. Check `deny` rules first — if matched, deny immediately.
2. Tool's own safety checks (e.g., dangerous command detection, sensitive path detection).
3. `ask` rules — if matched, mark as requiring confirmation.
4. Tool-level `allow` rules and mode-based auto-allow behavior.
5. If the final result is still `ask`, the runtime environment determines how to consume it.

Broad allow rules do not mean all actions execute silently — safety checks and ask rules have higher priority.

### How `ask` Is Consumed in Different Environments

| Environment                    | `ask` outcome                        | Notes                                           |
| :----------------------------- | :----------------------------------- | :---------------------------------------------- |
| **TUI** (interactive terminal) | Confirmation prompt                  | User selects allow/deny in the terminal.        |
| **Headless** (`-p`/`--prompt`) | Auto-deny                            | No interaction available; `ask` becomes `deny`. |
| **SDK** (stdio protocol)       | Sends `canUseTool` callback to host  | Host program decides allow/deny.                |
| **ACP** (IDE integration)      | Sends `requestPermission` RPC to IDE | IDE prompts or auto-decides.                    |

Headless mode (`-p`/`--prompt`) can be combined with permission modes:

```shell
# Headless + accept_edits: file edits auto-approved, Bash denied
qoder -p "refactor the utils module" --permission-mode accept_edits

# Headless + bypass_permissions: all allowed (trusted scenarios only)
# Exception: an rm that would remove the working directory, the home directory,
# the root or a top-level directory is denied — nobody can confirm it — unless
# a PermissionRequest hook explicitly allows it
qoder -p "run the migration" --yolo

# Headless + precise allow rules: only specific tools allowed
qoder -p "check status" --allowed-tools 'Read,Bash(git status)'
```

## Permission Configuration

### Configuration Sources (8-Layer Priority)

Rules merge from multiple sources, from lowest to highest priority:

| Layer | Source            | Description                                                                 |
| :---- | :---------------- | :-------------------------------------------------------------------------- |
| 1     | `userSettings`    | `~/.qoder/settings.json` (user global)                                      |
| 2     | `projectSettings` | `<project>/.qoder/settings.json` (project-level, team-shared)               |
| 3     | `localSettings`   | `<project>/.qoder/settings.local.json` (machine-local, add to `.gitignore`) |
| 4     | `flagSettings`    | `--settings <path>` CLI argument specifying an additional file              |
| 5     | `cliArg`          | `--allowed-tools` / `--disallowed-tools` CLI arguments                      |
| 6     | `command`         | `/allow`, `/deny` in-session commands                                       |
| 7     | `session`         | Runtime temporary rules ("Allow for this session" in prompts)               |

Higher-priority sources override lower ones. If organization policy enables `allowManagedPermissionRulesOnly`, only policy-managed rules are used.

**How each source is configured:**

- **Layers 1-3 (settings files)**: Write `permissions.allow` / `permissions.deny` / `permissions.ask` arrays in the corresponding JSON file. `settings.local.json` is ideal for machine-local approval rules; add it to `.gitignore`.
- **Layer 4 (flagSettings)**: `qoder --settings ./custom-settings.json` specifies an additional settings file. Same format as standard settings.json.
- **Layer 5 (cliArg)**: Configured via `--permission-mode`, `--allowed-tools`, `--disallowed-tools`, `--tools` CLI arguments; applies to current session only.
- **Layer 6 (command)**: Type `/allow Bash(npm test)` or `/deny WebFetch` in-session; persisted to `settings.local.json`.
- **Layer 7 (session)**: Temporary rules from selecting "Allow for this session" in prompts; lost when the process exits.

### Mode Configuration

**Set default mode** — configure `general.defaultPermissionMode` in settings:

```json
{
  "general": {
    "defaultPermissionMode": "accept_edits"
  }
}
```

Supported values:

| Value                | Behavior                                                            | Aliases                     |
| -------------------- | ------------------------------------------------------------------- | --------------------------- |
| `default`            | Prompts for approval on every action                                | —                           |
| `accept_edits`       | Auto-approves file edits, shell commands still require confirmation | `acceptEdits`               |
| `plan`               | Read-only mode (legacy compat: maps to default + Plan work state)   | —                           |
| `auto`               | Authorization is decided by the agent                               | —                           |
| `bypass_permissions` | Skip permission prompts (YOLO)                                      | `yolo`, `bypassPermissions` |
| `dont_ask`           | Non-interactive: deny anything requiring approval                   | `dontAsk`                   |

Case-insensitive. All aliases are accepted.

**Disable YOLO mode** — organization admins can prevent users from entering bypass\_permissions mode:

```json
{
  "security": {
    "disableYoloMode": true
  }
}
```

When set, `--yolo`, `--permission-mode bypass_permissions`, and Ctrl+Y are all disabled, and the Shift+Tab cycle skips this mode. Sub-agents declaring bypass are also downgraded to acceptEdits.

**Disable Plan mode** — if the Plan workflow is not needed:

```json
{
  "general": {
    "plan": {
      "enabled": false
    }
  }
}
```

When set, the `/plan` command is unavailable, `--permission-mode plan` falls back to default, and EnterPlanMode/ExitPlanMode tools are not registered.

**Auto mode classifier configuration** — guide the AI classifier's decisions with natural language rules:

```json
{
  "autoMode": {
    "allow": [
      "running npm/yarn/pnpm scripts defined in package.json",
      "creating or editing test files"
    ],
    "soft_deny": [
      "deleting files outside the test directory",
      "modifying CI/CD configuration"
    ],
    "environment": [
      "This is a Node.js monorepo with pnpm workspaces",
      "The project uses Vitest for testing"
    ]
  }
}
```

| Field         | Purpose                                                     |
| :------------ | :---------------------------------------------------------- |
| `allow`       | Operation descriptions the classifier tends to auto-approve |
| `soft_deny`   | Operation descriptions the classifier tends to deny         |
| `environment` | Environment context provided to the classifier              |

These rules are **soft guidance** — injected into the classifier prompt as reference; the final decision is still made by the AI classifier. For security, `autoMode` configuration is read only from your own global settings. Workspace files are ignored, including the local settings file: any file under the workspace can arrive with a checkout, and a repository able to add classifier `allow` entries could bias auto mode toward approving dangerous actions.

The user-level `AGENTS.md` is also injected into the classifier context, as evidence of what the session was asked to accomplish. Only the user-level file is read. Its content describes intent and does not constitute authorization: an action it appears to sanction is still blocked when it conflicts with a safety rule.

**Actions adjudicated without the classifier**

- Read-only tools (file reads, search, glob) are approved by tool name. The target path is not evaluated, so paths outside the working directory are approved as well.
- Writes and edits inside the working directory, and inside directories added with `/add-dir`, are approved directly.
- All other actions are submitted to the classifier, including writes to paths outside those directories.

The classifier runs in two stages. The first stage is a fast pass that can only approve; actions it does not approve are submitted to the second stage, which is the only stage that can conclude an action must be blocked. Adjudication latency therefore differs between tool calls.

**Circuit breaker**

In `auto` mode the agent decides authorization for tool calls, without asking about each one. When consecutive block verdicts reach the limit, the circuit breaker trips: the next action becomes an authorization prompt carrying the limit warning and the most recent block reason. The mode itself remains available, and the count clears once an action is approved.

The circuit breaker is the only condition under which a permission verdict produces an authorization prompt in `auto` mode. When the classifier is unreachable or returns no usable verdict, the action is denied rather than prompted; the message states that this is not a safety verdict on the action, the same action may be retried, and read-only tools are unaffected. When no classifier model is available, switching into `auto` mode is refused.

Separately, tools that require user input (such as AskUserQuestion) and entering or leaving Plan always request confirmation, as does a session grown too long for the classifier to read its full context. None of these are outcomes of a permission verdict.

### Permission Rule Configuration

Rules are grouped under `allow`, `ask`, and `deny`:

```json
{
  "permissions": {
    "allow": [
      "Read(/src/**)",
      "Edit(/src/**)",
      "Bash(npm run test:*)"
    ],
    "ask": [
      "Bash(npm publish:*)",
      "WebFetch"
    ],
    "deny": [
      "Read(*.pem)",
      "Bash(rm -rf:*)"
    ]
  }
}
```

### Rule Syntax

| Form                | Meaning                                                                        |
| :------------------ | :----------------------------------------------------------------------------- |
| `ToolName`          | Applies to the entire tool.                                                    |
| `ToolName(content)` | Applies to a specific path, command, agent type, or other tool-specific value. |
| `*`                 | Matches all tools.                                                             |

Use canonical tool names: `Read`, `Edit`, `Write`, `Bash`, `Grep`, `Glob`, `WebFetch`, `WebSearch`, `Agent`, and MCP names like `mcp__github__create_issue`.

If the content contains parentheses, escape them:

```json
{
  "permissions": {
    "allow": [
      "Bash(python -c \"print\\(1\\)\")"
    ]
  }
}
```

`ToolName(*)` is equivalent to `ToolName` (tool-level rule).

### Command-Line Overrides

```shell
qoder --allowed-tools 'Read,Grep,Bash(git status)'
qoder --disallowed-tools 'Bash(rm -rf:*),mcp__github__delete_repo'
qoder --tools 'Read,Grep,Edit'
```

`--allowed-tools` and `--disallowed-tools` use the same rule syntax as settings. `--tools` restricts the available built-in tool set for the current run (unlisted tools are denied).

## Trust Directories

Qoder treats the startup current working directory (CWD) as the **main trust directory**. Within trusted directories:

- File reads are allowed by default
- File writes can be auto-approved in `accept_edits` and `auto` modes
- Non-default permission modes (auto, bypass, etc.) are allowed to take effect

If the current directory is not trusted, Qoder forces a fallback to `default` mode.

### Extending Trust

Add additional trusted working directories via `--add-dir`, the `/add-dir` command, or `permissions.additionalDirectories`:

```shell
qoder --add-dir ../shared
```

```json
{
  "permissions": {
    "additionalDirectories": ["../shared"]
  }
}
```

You can also configure `permissions.trustDirectories` in global settings to permanently trust frequently-used directories.

### Protected Paths

Some paths are protected because editing them can change execution behavior, credentials, or tool behavior. Examples include `.git`, `.vscode`, `.idea`, `.husky`, most `.qoder` configuration files, shell startup files like `.bashrc`/`.zshrc`, Git config, `.mcp.json`, and `.ripgreprc`. In normal interactive modes these paths require explicit approval.

### Path Shapes

Some path spellings can reach resources outside the local filesystem, or bypass path comparison. These are handled separately from rules, on two independent axes.

- **Network locations** — paths beginning with two separators (`\server\share`, `//server/share`), extended UNC forms (`\?\UNC\server\share`), WebDAV spellings, and `/net/<host>/…` automount paths. On Windows, reading, searching, writing and shell access there ask for confirmation every time. The prompt offers no persistent rule, because a saved rule would permanently authorize disclosing your credentials to that host. A network location may be the working directory or an added directory; startup names it in a warning.
- **Local mounts** — `\wsl$\…`, `\wsl.localhost\…`, `\?\C:\…` and `\?\Volume{…}\…` name local storage and follow the normal rules. A loopback host such as `\127.0.0.1\share` is still a network location.
- **Bypass-prone spellings** — alternate data streams (`file.txt:stream`), 8.3 short names (`PROGRA~1`), device paths (`\.\…`), trailing dots or spaces, three or more consecutive dots, and reserved device names (`CON`, `NUL`, `COM1`) require explicit approval.

## File Access Rules

Path-scoped read rules use `Read(...)`. Path-scoped write rules use `Edit(...)`; they cover file editing and writing checks for `Edit`, `Write`, and `NotebookEdit`. An `Edit(...)` allow rule also implies read permission for the same path.

File rules use gitignore-style matching.

| Pattern          | Meaning                                                                                                                                                                                                 |
| :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `/src/**`        | Rooted at the rule source's root directory. In project/local settings, relative to project root; in user settings, relative to home directory.                                                          |
| `~/Documents/**` | Home-directory-based path.                                                                                                                                                                              |
| `//tmp/data/**`  | Absolute path from system `/`. Use double slash for absolute paths. In a rule pattern a leading `//` means "absolute from the filesystem root" and is unrelated to the network-location handling above. |
| `*.secret`       | Rootless filename pattern that matches at any location.                                                                                                                                                 |

Examples:

```json
{
  "permissions": {
    "allow": [
      "Read(/src/**)",
      "Edit(/src/**)",
      "Read(~/Documents/specs/**)"
    ],
    "ask": [
      "Edit(/package.json)",
      "Read(~/Downloads/**)"
    ],
    "deny": [
      "Read(*.pem)",
      "Edit(/.git/**)",
      "Edit(//etc/**)"
    ]
  }
}
```

## Bash Rules

`Bash(...)` rules can match exact commands, command prefixes, or wildcard patterns.

| Rule                   | Matches                                                     |
| :--------------------- | :---------------------------------------------------------- |
| `Bash(npm run build)`  | Exactly `npm run build`.                                    |
| `Bash(npm run test:*)` | `npm run test` and commands beginning with `npm run test `. |
| `Bash(git log *)`      | Glob-style wildcard matching.                               |
| `Bash(git status)`     | Exactly `git status`.                                       |

Examples:

```json
{
  "permissions": {
    "allow": [
      "Bash(git status)",
      "Bash(git log:*)",
      "Bash(npm run test:*)"
    ],
    "ask": [
      "Bash(npm publish:*)",
      "Bash(git push:*)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(sudo:*)"
    ]
  }
}
```

Shell matching is conservative:

- `deny` and `ask` rules see through common wrappers and environment variable prefixes, so `Bash(rm -rf:*)` still catches wrapped destructive commands.
- Prefix and wildcard `allow` rules do not silently approve compound commands unless every top-level command segment is independently allowed.
- Some provably read-only shell commands can be automatically allowed after deny, ask, and path checks.
- Dangerous commands (destructive deletes, force pushes) can still force confirmation even with broad allow rules.

Avoid broad rules like `Bash` or `Bash(*)` unless you fully trust the session.

## Web and MCP Rules

Web tools can be controlled at the tool level. Use `ask` when every web fetch should require confirmation, or `deny` when web access should be blocked for a session or project.

```json
{
  "permissions": {
    "ask": [
      "WebFetch"
    ],
    "deny": [
      "WebSearch"
    ]
  }
}
```

MCP tools use fully qualified names:

```text
mcp__<server>__<tool>
```

Supported MCP patterns include:

| Rule                        | Meaning                                 |
| :-------------------------- | :-------------------------------------- |
| `mcp__github__create_issue` | A single MCP tool.                      |
| `mcp__github__*`            | All tools from the `github` MCP server. |
| `mcp__github`               | All tools from the `github` MCP server. |
| `mcp__*`                    | All MCP tools.                          |

Example:

```json
{
  "permissions": {
    "allow": [
      "mcp__context7__*"
    ],
    "ask": [
      "mcp__github__create_issue"
    ]
  }
}
```

MCP server configs can also set `alwaysAllow` for tools from that server. To enable only selected MCP servers for a run, use `--allowed-mcp-server-names`.

```shell
qoder --allowed-mcp-server-names context7,github
```

## Subagent Rules

`Agent` rules control Subagent launches. The rule content is a Subagent name, matched case-insensitively. All three behaviors are supported and evaluated in the order `deny` > `ask` > `allow`. When no matching rule is configured, the launch proceeds without a confirmation prompt.

| Rule                     | Meaning                                           |
| :----------------------- | :------------------------------------------------ |
| `Agent`                  | Applies to every Subagent launch.                 |
| `Agent(general-purpose)` | Applies to one Subagent name, case-insensitively. |

Example:

```json
{
  "permissions": {
    "allow": [
      "Agent(explore)"
    ],
    "ask": [
      "Agent(deploy-helper)"
    ],
    "deny": [
      "Agent(code-review)"
    ]
  }
}
```

- A matching `deny` rule blocks the launch.
- A matching `ask` rule shows a confirmation prompt before the Subagent starts.
- A matching `allow` rule explicitly records the approval; the outcome is the same as having no rule.

Use `/agents` to view the built-in Subagent list and your installed Subagents, whose names are the values usable in `Agent(...)` rules.

## Hooks and Permissions

Qoder's Hook system has two injection points in the permission decision pipeline, allowing custom scripts to influence allow/deny behavior.

### Hook Events That Affect Permissions

| Hook Event          | Trigger                                        | Permission Impact                                                               |        |                                                  |
| :------------------ | :--------------------------------------------- | :------------------------------------------------------------------------------ | ------ | ------------------------------------------------ |
| `PreToolUse`        | Before tool execution (permission check phase) | Can return \`permissionDecision: "allow"                                        | "deny" | "ask"\` to directly override the pipeline result |
| `PermissionRequest` | After pipeline produces `ask`, before prompt   | Can return `decision.behavior` as `allow` or `deny` to replace user interaction |        |                                                  |

Other hook events (`PostToolUse`, `SessionStart`, `Stop`, etc.) do not participate in permission decisions.

### PreToolUse Hook

Triggered before tool execution. The hook script can inspect the tool name and parameters, returning a permission decision:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python ./scripts/check-bash-command.py"
          }
        ]
      }
    ]
  }
}
```

Hook scripts receive JSON input via stdin (containing `tool_name`, `tool_input`, `session_id`, etc.) and output JSON results via stdout:

```json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Command blocked by security policy"
  }
}
```

`permissionDecision` values:

- `"allow"`: Skip permission pipeline, approve directly
- `"deny"`: Skip permission pipeline, deny directly
- `"ask"`: Continue through normal permission pipeline (default behavior)

### PermissionRequest Hook

Triggered after the permission pipeline produces `ask`, before the prompt/callback. Suitable for automated approval systems or external notifications (e.g., Slack/email alerts):

```json
{
  "hooks": {
    "PermissionRequest": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node ./scripts/auto-approve-safe-ops.js"
          }
        ]
      }
    ]
  }
}
```

Output format:

```json
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {},
      "updatedPermissions": []
    }
  }
}
```

### Hook Priority vs. Permission Modes

Hook permission decisions have **higher priority** than permission modes — even in `bypass_permissions` mode, a PreToolUse hook returning `deny` will still block execution. This provides an unbypassable interception capability for organization-level security policies.

Execution order:

1. Hook `PreToolUse` → if returns allow/deny, short-circuit
2. Permission pipeline (rules + mode + safety checks)
3. If result is `ask` → Hook `PermissionRequest` → if returns allow/deny, short-circuit
4. Finally, the runtime environment consumes `ask` (prompt/deny/callback)
