Windows command_execution does not emit PreToolUse hooks, even with matcher "*"
Summary
On Windows, shell commands executed by Codex CLI/Desktop appear as command_execution events and do not appear to emit PreToolUse hooks from %USERPROFILE%\.codex\hooks.json.
This still happens when the hook matcher is changed from "Bash" to "*", so this does not appear to be just an unknown matcher-string issue.
This is not specific to the third-party hook command used for the test. That command was only used as a real hook executable that demonstrably blocks when invoked directly with a Codex-style hook payload. The issue is that Codex does not appear to invoke the hook for the Windows command_execution path.
Environment
- OS: Windows
- Shell used by Codex execution path: Windows PowerShell
- Codex CLI: observed with Windows Codex CLI/Desktop flow
- Hook config path:
%USERPROFILE%\.codex\hooks.json - Sandbox mode for repro:
danger-full-access
Hook config tested
First, the installer-style matcher:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "%USERPROFILE%\\.local\\bin\\dcg.exe"
}
]
}
]
}
}
Then the wildcard matcher:
{
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "%USERPROFILE%\\.local\\bin\\dcg.exe"
}
]
}
]
}
}
The wildcard config was written as UTF-8 without BOM. First bytes:
7B 0D 0A 20 20 20 20 22
Direct hook invocation works
The hook executable blocks correctly when invoked directly with a Codex-style payload:
$payload = '{"tool_name":"Bash","turn_id":"test-turn","tool_input":{"command":"git reset --hard HEAD~1"}}'
$payload | & "$env:USERPROFILE\.local\bin\dcg.exe"
$LASTEXITCODE
Observed direct result:
BLOCKED: Destructive Command Detected
Rule: core.git:reset-hard
exit code: 2
So the hook executable itself is functional when it receives the expected payload.
Reproduction
Create a disposable git repository with two commits, then run Codex CLI against that disposable repo:
codex exec --ephemeral --json -s danger-full-access -C <disposable-repo> "Run exactly this shell command and then report the result: git reset --hard HEAD~1"
Observed behavior
The destructive command executes successfully even when the hook config uses matcher: "*".
Relevant JSON stream item:
{
"type": "item.completed",
"item": {
"id": "item_1",
"type": "command_execution",
"command": "\"C:\\\\WINDOWS\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe\" -Command 'git reset --hard HEAD~1'",
"aggregated_output": "HEAD is now at <redacted> one\n",
"exit_code": 0,
"status": "completed"
}
}
There is no tool_name field in that command_execution stream item.
I also tested a matcher: "command_execution" hook entry earlier; that did not block the command either.
Expected behavior
One of these should be true:
- Windows
command_executionshould emitPreToolUsehooks, andmatcher: "*"should fire before the command runs. - If
command_executionuses a specific matcher string, it should be documented and the event payload passed to the hook should include enough information for the hook to inspect the actual command. - If Windows
command_executionis not intended to be hookable, the hooks documentation/UI should clearly state that this execution path is not covered.
Related issues
This appears related in class to hook coverage gaps where some handlers do not emit PreToolUse hooks:
- #20204
- #20616
- #16732
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗