> ## 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`                      | Autonomous runs, Goal execution     | Zero prompts. Safe reads and workspace edits are auto-approved; risky actions are denied or evaluated by the AI classifier.                              |
| `bypass_permissions` (YOLO) | Trusted local experiments only      | Skips all approval prompts. All tool calls are allowed automatically.                                                                                    |
| `dont_ask`                  | Headless flows that must not prompt | Never prompts. Any action that would normally ask is denied instead.                                                                                     |

### Plan Mode

Plan is an independent **work state** (not a permission policy) that can coexist with any permission mode above. Toggle it with the `/plan` command. While active, Qoder explores code in read-only mode and outputs proposals; writes are restricted to plan files. On exit you can choose a follow-up permission mode or start a Goal execution.

### Goal Mode

Goal is an autonomous execution state. Enter via `/goal set <objective>` — this automatically switches to `auto` mode and locks Shift+Tab switching to ensure zero-interruption execution. Goal can coexist with Plan (plan first, execute second). Exit with `/goal clear` or `/goal pause`.

### 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 theme={null}
qodercli --permission-mode default
qodercli --permission-mode accept_edits
qodercli --permission-mode plan           # Legacy compat: translates to default + enters Plan work state
qodercli --permission-mode auto
qodercli --permission-mode bypass_permissions
qodercli --permission-mode yolo           # Equivalent to bypass_permissions
qodercli --permission-mode dont_ask

# Shortcuts
qodercli --yolo                         # Equivalent to --permission-mode bypass_permissions
qodercli --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 theme={null}
# The following are equivalent
qodercli --permission-mode bypass_permissions
qodercli --permission-mode bypassPermissions
qodercli --permission-mode yolo
qodercli --yolo
qodercli --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 theme={null}
# Headless + accept_edits: file edits auto-approved, Bash denied
qodercli -p "refactor the utils module" --permission-mode accept_edits

# Headless + bypass_permissions: all allowed (trusted scenarios only)
qodercli -p "run the migration" --yolo

# Headless + precise allow rules: only specific tools allowed
qodercli -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)**: `qodercli --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 theme={null}
{
  "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`               | AI classifier evaluates action safety                               | —                           |
| `bypass_permissions` | Skip all permission checks (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 theme={null}
{
  "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 theme={null}
{
  "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 theme={null}
{
  "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 only read from trusted sources (user global settings and localSettings); project settings are excluded to prevent malicious privilege escalation.

### Permission Rule Configuration

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

```json theme={null}
{
  "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 theme={null}
{
  "permissions": {
    "allow": [
      "Bash(python -c \"print\\(1\\)\")"
    ]
  }
}
```

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

### Command-Line Overrides

```shell theme={null}
qodercli --allowed-tools 'Read,Grep,Bash(git status)'
qodercli --disallowed-tools 'Bash(rm -rf:*),mcp__github__delete_repo'
qodercli --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 theme={null}
qodercli --add-dir ../shared
```

```json theme={null}
{
  "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; in `auto` mode they are denied.

## 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.                                                                            |
| `*.secret`       | Rootless filename pattern that matches at any location.                                                                                        |

Examples:

```json theme={null}
{
  "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 theme={null}
{
  "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. In `auto` mode, dangerous shell commands are denied.

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 theme={null}
{
  "permissions": {
    "ask": [
      "WebFetch"
    ],
    "deny": [
      "WebSearch"
    ]
  }
}
```

MCP tools use fully qualified names:

```text theme={null}
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 theme={null}
{
  "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 theme={null}
qodercli --allowed-mcp-server-names context7,github
```

## 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 theme={null}
{
  "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 theme={null}
{
  "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 theme={null}
{
  "hooks": {
    "PermissionRequest": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node ./scripts/auto-approve-safe-ops.js"
          }
        ]
      }
    ]
  }
}
```

Output format:

```json theme={null}
{
  "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)
