# GitHub Copilot CLI permissions and hooks reference

How Copilot CLI decides what it may run: approval prompts, --allow-tool and --deny-tool, allow-all and assisted modes, sandboxing, hooks and -p in CI.

GitHub Copilot CLI asks before anything that changes your machine. Reads and read-only shell commands run without a prompt; destructive shell commands, file edits and URL access prompt, unless a rule, a saved approval or an allow-all mode already covers them. Deny rules beat every allow. In `-p` mode there is no prompt, and a request that needs approval is denied.



This page is the CLI only: the terminal agent you start with `copilot`, not the editor extension. For VS Code agent mode and the cloud coding agent, see [the Copilot controls page](/controls/copilot). For wiring a check into Copilot's hooks, see [the Copilot install guide](/integrations/copilot).

## The default: what prompts and what does not

From GitHub's [Allowing and denying tool use](https://docs.github.com/en/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools): searching, reading files and read-only shell commands are "generally allowed automatically, subject to applicable policy." Destructive shell commands, file edits and URL access "need permission before Copilot can use them."

At a prompt, [About Copilot CLI](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/about-copilot-cli) lists the choices:

1. **Yes.** Once. Copilot asks again next time.
2. **Yes, and approve the tool for the rest of the session.** This is broader than it looks. GitHub's own example: approve `rm ./this-file.txt` this way and Copilot "can run any `rm` command (for example, `rm -rf ./*`)" for the rest of that session.
3. **No, and tell Copilot what to do differently.** Cancels the call and lets you redirect it.

For a shell command, file write, MCP tool or memory update there is also "don't ask again in this repo" (or "in this directory"). That approval is saved to `~/.copilot/permissions-config.json`, scoped to the git root, or the working directory outside a repository. A permanently approved URL goes to `allowedUrls` in `~/.copilot/settings.json` and applies to every session.

Before any of this, Copilot asks whether you trust the folder you launched in. The [CLI overview](https://docs.github.com/en/copilot/how-tos/copilot-cli/use-copilot-cli/overview) offers this session only, remember the folder, or no. Access outside it prompts again, or you grant it with `/add-dir`. GitHub says this scoping "is heuristic and GitHub does not guarantee" files outside it are protected, and that you "should not launch Copilot CLI from your home directory."

## Allow and deny rules

Two layers, both set on the command line.

**Which tools exist.** `--available-tools` keeps only the tools you list. `--excluded-tools` removes the ones you list. If you pass both, the allowlist wins. A tool outside the available set cannot be used even if `--allow-tool` names it.

```bash
copilot --excluded-tools='web_fetch, web_search'
```

**Which tools run without a prompt.** `--allow-tool` pre-approves. `--deny-tool` blocks: the tool "will not prompt for permission", per the [command reference](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-command-reference). Patterns are `Kind(argument)`; leave the argument off to match every tool of that kind.

```bash
# all git subcommands except push
copilot --allow-tool='shell(git:*)' --deny-tool='shell(git push)'

# no file writes at all
copilot --deny-tool=write

# all reads, plus writes to one file
copilot --allow-tool='read, write(.github/copilot-instructions.md)'

# two tools from one MCP server
copilot --allow-tool='MyMCP(create_issue), MyMCP(delete_issue)'

# explore, edit and commit; no internet, no pushes
copilot --available-tools='bash,edit,view,grep,glob' \
  --allow-tool='shell(git:*)' --deny-tool='shell(git push)'
```

URLs have their own pair, `--allow-url` and `--deny-url`, and `--deny-url` "takes precedence over `--allow-url`." The [programmatic reference](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-programmatic-reference) lists `url(github.com)` as a filter.

**Precedence.** "Deny rules always take precedence over allow rules, even when `--allow-all` is set or a matching approval has been saved." That sentence is the strongest guarantee in Copilot CLI's model. A `--deny-tool` is a real block, not a prompt.

**Persistence.** `--allow-tool` and `--deny-tool` are session-only and "aren't written to `permissions-config.json`." The only persistent allows are the saved prompt approvals and `allowedUrls` above. To keep a rule set, put the flags in a script, not in a shell alias with allow-all on it; GitHub warns against the alias.

## Allow-all, assisted and autopilot

| Mode | How | What it does |
|---|---|---|
| Default | the starting mode, `/permissions default` | Prompts for anything not already allowed |
| Assisted | `/permissions assisted`, `--assisted-approval` for `-p` runs | A model reviews eligible requests and approves "those it judges to be safe"; the rest are put to you, or denied in `-p` runs |
| Allow all tools | `--allow-all-tools`, env `COPILOT_ALLOW_ALL` | Every available tool runs without a prompt |
| Allow all | `--allow-all` or `--yolo`; `/permissions allow-all` or `/yolo` mid-session | `--allow-all-tools --allow-all-paths --allow-all-urls` together |

Deny rules and enterprise restrictions still apply under `--allow-all-tools`. On Business and Enterprise licences the allow-all options "may be blocked by an enterprise administrator." `--assisted-approval` makes the CLI exit with an error if you start an interactive session with it, and when it is enabled it overrides `--allow-all-tools` but not the path or URL flags. You can set assisted as your default with `defaultPermissionMode: "assisted"` in the user config.

`/reset-allowed-tools` revokes in-session grants, returns to manual mode, and clears saved approvals for the current location. Startup flags such as `--allow-tool` and `--deny-tool` stay as the baseline.

[Autopilot](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/autopilot) is separate. It controls continuation, not approvals: the agent keeps going until the task is done or `--max-autopilot-continues` is reached. If you enter autopilot with manual approval, "Copilot will automatically deny any tool requests that require approval." GitHub's own example pairs it with `--yolo`:

```bash
copilot --autopilot --yolo --max-autopilot-continues 10 -p "YOUR PROMPT HERE"
```

## Sandboxing

Copilot CLI has two. **Local sandboxing**, turned on with `/sandbox enable`, does not sandbox the CLI itself; "the commands and tools that Copilot CLI runs on your behalf are restricted," limiting filesystem, network and system access. **Cloud sandboxing**, `copilot --cloud`, runs the whole session in a GitHub-hosted environment.

```text
/sandbox enable
/sandbox status
/sandbox policy npm install
copilot --sandbox
```

The [command reference](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-command-reference) adds the details that matter. `/sandbox policy` shows path grants, network settings and developer-tool access, and with a command appended it shows what that command could reach without running it. Launching with `--sandbox` turns it on for that session only. If your organization requires sandboxing, `disable` is refused unless bypass is allowed. `/add-dir` also grants the sandbox access to that directory. Under autopilot, a step that needs to escape the sandbox, such as writing outside the working directory, is denied.

## Hooks

Copilot CLI has a full hook system. The [hooks reference](https://docs.github.com/en/copilot/reference/hooks-reference) lists these events: `sessionStart`, `sessionEnd`, `userPromptSubmitted`, `userPromptTransformed`, `preToolUse`, `postToolUse`, `postToolUseFailure`, `permissionRequest`, `preCompact`, `subagentStart`, `subagentStop`, `agentStop`, `errorOccurred`, `notification`. PascalCase aliases such as `PreToolUse` are accepted and switch the payload to snake_case fields in Claude Code's format.

Hooks load in order, policy, then user, then project, then plugins, and are combined:

- Policy: `/etc/github-copilot/policy.d/*.json`, `C:\ProgramData\GitHub\Copilot\policy.d\*.json`, and the Windows registry key `HKLM\Software\Policies\GitHub\Copilot`
- Repository: `.github/hooks/*.json`
- User: `~/.copilot/hooks/` (or `$COPILOT_HOME/hooks/`)
- Inline `hooks` in `.github/copilot/settings.json`, `settings.local.json`, `~/.copilot/settings.json`, and `.claude/settings.json` / `.claude/settings.local.json`
- Plugin `hooks.json`

A minimal `preToolUse` hook in `.github/hooks/policy.json`:

```json
{
  "version": 1,
  "hooks": {
    "preToolUse": [
      { "type": "command", "bash": "./scripts/check-tool.sh", "timeoutSec": 10 }
    ]
  }
}
```

The script reads `toolName` and `toolArgs` on stdin and prints:

```json
{ "permissionDecision": "deny", "permissionDecisionReason": "force-push is blocked in this repo" }
```

`permissionDecision` is `allow`, `deny` or `ask`; the reason is required on a deny and shown to the agent. `modifiedArgs` substitutes the tool's arguments.

**What happens when a hook fails.**

| Case | Result |
|---|---|
| Exit `2` from `preToolUse` or `permissionRequest` | Deny |
| Exit `2` from other events | Warning; the run continues |
| Other non-zero exit from a command `preToolUse` | Deny ("hook errored"), even if stdout said allow |
| Timeout (default 30 s) | **Fail open**, "including for `preToolUse` and admin-deployed policy hooks" |
| HTTP `preToolUse` hook: network error, timeout, non-2xx | Fail open |

`disableAllHooks: true` in a hook file skips that file; in the repository `settings.json` it disables every source except policy hooks, which "continue to run."

**`permissionRequest`** fires before the permission service: before rule checks, saved approvals and prompting. Returning `behavior: "allow"` or `"deny"` short-circuits the normal flow. GitHub calls it "especially useful in CLI pipe mode (`-p`) and other CLI CI usages where no interactive prompt is available." It is CLI-only. For a sandbox-bypass request, only a deny propagates; an allow does not pre-approve the escape.

## Headless and CI

The [programmatic reference](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-programmatic-reference) treats a run as non-interactive when it uses `-p` or has no terminal attached, as in CI. The rule: "Without any permission options, requests that need approval are denied, and Copilot often can't finish the task." Under assisted approval, requests that still need you are denied too, with the reason passed back to the agent. A hook `ask` in autopilot is denied, per the hooks reference.

```bash
copilot -p "Show me this week's commits and summarize them" \
  --allow-tool='shell(git)' \
  --no-ask-user \
  --output-format json
```

`--no-ask-user` stops the agent pausing for input. `--output-format json` emits JSONL. `--secret-env-vars` keeps named variables out of logs; `GITHUB_TOKEN` and `COPILOT_GITHUB_TOKEN` are redacted by default. `--trust-folder` trusts hooks, MCP servers and plugins for one run but "does not grant tool, path, or URL permissions."

## Limits

- **Session approvals are broad.** Approving one `rm` for the session approves every `rm`.
- **Folder trust is a heuristic.** GitHub does not guarantee files outside it are protected.
- **Assisted mode is a model's judgment.** It means fewer prompts, but you cannot audit the rule in advance.
- **Hook timeouts fail open,** policy hooks included. A slow check is a skipped check.
- **Filed bugs.** One release did not honour a `preToolUse` deny ([copilot-cli#3874](https://github.com/github/copilot-cli/issues/3874)), and plugin-bundled `preToolUse` hooks did not fire ([copilot-cli#2540](https://github.com/github/copilot-cli/issues/2540)). Test a deny on the version you run.
- **No log of decisions.** The docs say where approvals are saved, not where each allowed, prompted or denied call is recorded. A `postToolUse` hook that writes somewhere you control gives you one.
- **Subagents.** How allow and deny rules carry into `/fleet` subagents is [not documented](/blog/who-controls-the-fleet).

ACP has a Copilot hook that covers the CLI and VS Code agent mode with one hook file. It checks shell commands, file edits and MCP calls, and it records each call. Where Copilot's `ask` has no prompt to show, the call is blocked and waits for your approval on the console or in the email; then Copilot retries. Copilot's cloud coding agent is not governed today, because its sandbox has no ACP credentials. Copilot hooks fail open on timeout, and the hook ACP installs fails open when ACP is unreachable unless you set `ACP_FAIL_MODE=closed` or write `closed` to `~/.acp/failmode`. See the [Copilot install guide](/integrations/copilot).

## Frequently asked questions



## Where to read more

- [About GitHub Copilot CLI](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/about-copilot-cli)
- [Allowing and denying tool use](https://docs.github.com/en/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools)
- [Copilot CLI command reference](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-command-reference)
- [Copilot CLI programmatic reference](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-programmatic-reference)
- [Autopilot mode](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/autopilot)
- [Copilot hooks reference](https://docs.github.com/en/copilot/reference/hooks-reference)
- [github/copilot-cli on GitHub](https://github.com/github/copilot-cli)
