You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Repository-level hook files (.github/hooks/*.json and inline hooks in .github/copilot/settings.json) are not loaded when Copilot CLI is started in non-interactive print mode (copilot -p "..."). The exact same hook configuration loads and fires correctly when the CLI is started in interactive mode (TTY).
User-level hooks (~/.copilot/hooks/*.json and inline hooks in ~/.copilot/settings.json) load and fire correctly in both modes, as do plugin-defined hooks.
This is not documented as a -p-specific limitation in the Hooks reference, which lists .github/hooks/*.json as a supported source.
→ /tmp/copilot-hook-log.txt is not created. ~/.copilot/logs/process-*.log shows only Loaded N hook(s) from N plugin(s) (user-level hooks and plugin hooks). No discovery message for .github/hooks/ appears.
Run interactive (same directory, same hook file):
copilot --allow-all-tools
# Then type: view README.md
→ /tmp/copilot-hook-log.txt is created with HOOK FIRED at .... ~/.copilot/logs/process-*.log shows the .github/hooks/ discovery and load messages.
Variations tested (all reproduce the bug in -p mode)
❌ Repo hook not loaded (logs confirm absence of discovery)
preToolUse event
❌ Not loaded
permissionRequest event
❌ Not loaded
.github/copilot/settings.json with inline hooks
❌ Not loaded
Same config via ~/.copilot/hooks/*.json (user-level)
✅ Loaded and fired
Same config via ~/.copilot/settings.json (user-level inline)
✅ Loaded and fired
Plugin-defined hooks.json
✅ Loaded and fired
Expected behavior
.github/hooks/*.json and .github/copilot/settings.json should be loaded in both interactive and non-interactive (-p) modes, matching the official documentation. CI / automation pipelines that use copilot -p "..." rely on repository-level hooks for security policy enforcement, audit logging, and tool gating.
Actual behavior
Repository-level hooks are silently ignored in -p mode. There is no warning, error, or log message indicating that the hook configuration was found but skipped.
Security implication
Teams using .github/hooks/ to enforce security policies (e.g., blocking access to local credential caches, secret patterns; gating destructive shell commands; logging tool calls for audit) lose all of that protection when the CLI is invoked non-interactively. This is especially impactful for:
GitHub Actions workflows that run copilot -p "..."
Cron jobs and scheduled automation
CI hook test workflows (the hook tests themselves may pass via interactive simulation while the actual production -p calls bypass hooks)
The asymmetry between interactive and -p modes is also a footgun — users testing hooks interactively will see them work, then have them silently fail when deployed.
Workaround
Move all security-critical hooks to user-level (~/.copilot/hooks/*.json or ~/.copilot/settings.json) and distribute via dotfiles or setup scripts. Use --deny-tool 'kind(...)' on the CLI invocation for guaranteed enforcement, since --deny-tool is not affected by the hook loading bug.
Additional context
Confirmed with logging-only hooks (return {"permissionDecision":"allow"}) and policy hooks (return {"permissionDecision":"deny"}).
The process-*.log for a non-interactive run contains plugin and user-level hook discovery messages but never any .github/hooks/ line.
Sub-agent hook firing (a related issue preToolUse hooks are not enforced in subagents #2392) was correctly fixed in v1.0.49-0 — this -p-mode loading bug is a separate, narrower problem on the discovery/configuration-load path, not on the runtime dispatch.
Describe the bug
Repository-level hook files (
.github/hooks/*.jsonand inlinehooksin.github/copilot/settings.json) are not loaded when Copilot CLI is started in non-interactive print mode (copilot -p "..."). The exact same hook configuration loads and fires correctly when the CLI is started in interactive mode (TTY).User-level hooks (
~/.copilot/hooks/*.jsonand inlinehooksin~/.copilot/settings.json) load and fire correctly in both modes, as do plugin-defined hooks.This is not documented as a
-p-specific limitation in the Hooks reference, which lists.github/hooks/*.jsonas a supported source.Affected version
--log-level allSteps to reproduce
Create
.github/hooks/pre-tool-use.jsonin a repo:{ "version": 1, "hooks": { "preToolUse": [ { "type": "command", "bash": "echo \"HOOK FIRED at $(date)\" >> /tmp/copilot-hook-log.txt; echo '{\"permissionDecision\":\"allow\"}'", "timeoutSec": 10 } ] } }Run non-interactive:
copilot -p "view README.md" --allow-all-tools→
/tmp/copilot-hook-log.txtis not created.~/.copilot/logs/process-*.logshows onlyLoaded N hook(s) from N plugin(s)(user-level hooks and plugin hooks). No discovery message for.github/hooks/appears.Run interactive (same directory, same hook file):
copilot --allow-all-tools # Then type: view README.md→
/tmp/copilot-hook-log.txtis created withHOOK FIRED at ....~/.copilot/logs/process-*.logshows the.github/hooks/discovery and load messages.Variations tested (all reproduce the bug in
-pmode)-pcopilot -p "...")env -i HOME=$HOME PATH=$PATH copilot -p "...")cwd--log-level allpreToolUseeventpermissionRequestevent.github/copilot/settings.jsonwith inlinehooks~/.copilot/hooks/*.json(user-level)~/.copilot/settings.json(user-level inline)hooks.jsonExpected behavior
.github/hooks/*.jsonand.github/copilot/settings.jsonshould be loaded in both interactive and non-interactive (-p) modes, matching the official documentation. CI / automation pipelines that usecopilot -p "..."rely on repository-level hooks for security policy enforcement, audit logging, and tool gating.Actual behavior
Repository-level hooks are silently ignored in
-pmode. There is no warning, error, or log message indicating that the hook configuration was found but skipped.Security implication
Teams using
.github/hooks/to enforce security policies (e.g., blocking access to local credential caches, secret patterns; gating destructive shell commands; logging tool calls for audit) lose all of that protection when the CLI is invoked non-interactively. This is especially impactful for:copilot -p "..."-pcalls bypass hooks)The asymmetry between interactive and
-pmodes is also a footgun — users testing hooks interactively will see them work, then have them silently fail when deployed.Workaround
Move all security-critical hooks to user-level (
~/.copilot/hooks/*.jsonor~/.copilot/settings.json) and distribute via dotfiles or setup scripts. Use--deny-tool 'kind(...)'on the CLI invocation for guaranteed enforcement, since--deny-toolis not affected by the hook loading bug.Additional context
{"permissionDecision":"allow"}) and policy hooks (return{"permissionDecision":"deny"}).process-*.logfor a non-interactive run contains plugin and user-level hook discovery messages but never any.github/hooks/line.-p-mode loading bug is a separate, narrower problem on the discovery/configuration-load path, not on the runtime dispatch.Related
-ploading bug reported here may be a more general manifestation of the same code path)