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
Codex v0.114.0 added experimental hooks with SessionStart and Stop events (PR #13276). This is a great foundation, but the two most impactful hook events are missing: PreToolUse and PostToolUse.
Without these, it is impossible to enforce code quality gates, block destructive commands, or validate tool outputs - capabilities that are production-critical for teams with shared coding standards.
What I need
Event
Purpose
Example
PreToolUse
Validate/block BEFORE a tool runs
Block rm -rf ~/, git push --force main, git reset --hard. Block commits with secrets. Require plan approval before exiting plan mode.
PostToolUse
Validate/block AFTER a tool runs
Block shallow test patterns (assert True, XCTAssertTrue(true)). Block f-string loggers in Python. Warn about console.log in JS/TS.
Real hooks I have ready to go
I have 5 production enforcement hooks written and working in Claude Code that I cannot use in Codex:
block-shallow-tests.sh - PostToolUse on Write/Edit of test files. Catches 15+ worthless test patterns across Python, Swift, and TypeScript. Blocks the edit with exit code 2.
block-fstring-loggers.sh - PostToolUse on Write/Edit of .py files. Catches logger.info(f"...") (should use %s lazy format) and datetime.utcnow() (deprecated). Blocks the edit.
commit-guard.sh - PreToolUse on Bash git commit. Scans staged files for hardcoded secrets, http:// URLs, console.log. Hard-blocks on secrets (exit 2), warns on others.
require-plan-approval.sh - PreToolUse on ExitPlanMode. Blocks auto-exit from plan mode so user can review.
These all use the same JSON stdin/stdout protocol as the existing SessionStart/Stop hooks. The matcher syntax from Claude Code (tool == "Bash" && tool_input.command matches "^git\\s+commit") would be ideal but even a simple tool-name matcher would work.
Input schema for PreToolUse would need tool_name and tool_input fields. Output schema needs a decision field with "allow" or "block" values. Exit code 2 = block is the Claude Code convention and works well.
Why this matters
Codex runs with sandbox_mode = "danger-full-access" for trusted projects. Without PreToolUse hooks, there is no safety net for destructive commands.
Code quality enforcement (test patterns, logging standards) currently only works in Claude Code. Teams using both tools get inconsistent quality.
The hooks engine infrastructure from PR start of hooks engine #13276 already supports the pattern - this is adding two new event types to an existing framework.
Environment
Codex CLI: 0.114.0
macOS 15.4 (Darwin 25.3.0)
5 enforcement hooks ready, tested in Claude Code, waiting for Codex support
Summary
Codex v0.114.0 added experimental hooks with
SessionStartandStopevents (PR #13276). This is a great foundation, but the two most impactful hook events are missing:PreToolUseandPostToolUse.Without these, it is impossible to enforce code quality gates, block destructive commands, or validate tool outputs - capabilities that are production-critical for teams with shared coding standards.
What I need
PreToolUserm -rf ~/,git push --force main,git reset --hard. Block commits with secrets. Require plan approval before exiting plan mode.PostToolUseassert True,XCTAssertTrue(true)). Block f-string loggers in Python. Warn about console.log in JS/TS.Real hooks I have ready to go
I have 5 production enforcement hooks written and working in Claude Code that I cannot use in Codex:
logger.info(f"...")(should use %s lazy format) anddatetime.utcnow()(deprecated). Blocks the edit.rm -rf ~/,git push --force main/master,git reset --hard,git clean -f. Exit code 2.git commit. Scans staged files for hardcoded secrets, http:// URLs, console.log. Hard-blocks on secrets (exit 2), warns on others.These all use the same JSON stdin/stdout protocol as the existing SessionStart/Stop hooks. The matcher syntax from Claude Code (
tool == "Bash" && tool_input.command matches "^git\\s+commit") would be ideal but even a simple tool-name matcher would work.Proposed schema additions
{ "hooks": { "PreToolUse": [ { "matcher": "tool == \"Bash\"", "hooks": [ { "type": "command", "command": "/path/to/block-dangerous-bash.sh", "timeout": 5 } ] } ], "PostToolUse": [ { "matcher": "tool == \"Write\" && tool_input.file_path matches \"test_\"", "hooks": [ { "type": "command", "command": "/path/to/block-shallow-tests.sh", "timeout": 10 } ] } ] } }Input schema for PreToolUse would need
tool_nameandtool_inputfields. Output schema needs adecisionfield with"allow"or"block"values. Exit code 2 = block is the Claude Code convention and works well.Why this matters
sandbox_mode = "danger-full-access"for trusted projects. Without PreToolUse hooks, there is no safety net for destructive commands.Environment