Skip to main content

Hooks — ตะขอรันสคริปต์

Hooks

Hooks คือระบบ Extensibility ของ Codex ที่ให้คุณแทรกสคริปต์ของตัวเองเข้าไปใน Agentic Loop

Run deterministic scripts during the Codex lifecycle

Hooks are an extensibility framework for Codex. They allow you to inject your own scripts into the agentic loop, enabling features such as:

Send the conversation to a custom logging/analytics engine

Scan your team’s prompts to block accidentally pasting API keys

Summarize conversations to create persistent memories automatically

Run a custom validation check when a conversation turn stops, enforcing standards

Customize prompting when in a certain directory

Hooks are enabled by default. If you need to turn them off in config.toml, set:

[features] hooks = false Use hooks as the canonical feature key. codex_hooks still works as a deprecated alias.

Admins can force hooks off the same way in requirements.toml with [features].hooks = false.

Runtime behavior to keep in mind:

Matching hooks from multiple files all run.

Multiple matching command hooks for the same event are launched concurrently, so one hook cannot prevent another matching hook from starting.

Non-managed command hooks must be reviewed and trusted before they run.

PreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, and Stop run at turn scope. SessionStart and SubagentStart run at thread or subagent-start scope.

Where Codex looks for hooks

Codex discovers hooks next to active config layers in either of these forms:

hooks.json

inline [hooks] tables inside config.toml

Installed plugins can also bundle lifecycle config through their plugin manifest or a default hooks/hooks.json file. See Build plugins for the plugin packaging rules.

In practice, the four most useful locations are:

~/.codex/hooks.json

~/.codex/config.toml

<repo>/.codex/hooks.json

<repo>/.codex/config.toml

If more than one hook source exists, Codex loads all matching hooks. Higher-precedence config layers don’t replace lower-precedence hooks. If a single layer contains both hooks.json and inline [hooks], Codex merges them and warns at startup. Prefer one representation per layer.

Codex can also discover hooks bundled with enabled plugins. Plugin-bundled hooks load alongside other hook sources and use the same trust-review flow as other non-managed hooks.

Project-local hooks load only when the project .codex/ layer is trusted. In untrusted projects, Codex still loads user and system hooks from their own active config layers.

Review and trust hooks

Codex lists configured hooks before deciding which ones can run. Before a non-managed command hook can run, Codex requires you to review and trust the exact hook definition. Codex records trust against the hook’s current hash, so new or changed hooks are marked for review and skipped until trusted.

Use /hooks in the CLI to inspect hook sources, review new or changed hooks, trust hooks, or disable individual non-managed hooks. If hooks need review at startup, Codex prints a warning that tells you to open /hooks.

Managed hooks from system, MDM, cloud, or requirements.toml sources are marked as managed, trusted by policy, and can’t be disabled from the user hook browser.

For one-off automation that already vets hook sources outside Codex, pass --dangerously-bypass-hook-trust to run enabled hooks without requiring persisted hook trust for that invocation.

Config shape

Hooks are organized in three levels:

A hook event such as PreToolUse, PostToolUse, PreCompact, SubagentStart, or Stop

A matcher group that decides when that event matches

One or more hook handlers that run when the matcher group matches

{ "hooks": { "SessionStart": [ { "matcher": "startup|resume", "hooks": [ { "type": "command", "command": "python3 ~/.codex/hooks/session_start.py", "statusMessage": "Loading session notes" } ] } ], "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py"", "statusMessage": "Checking Bash command" } ] } ], "PermissionRequest": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/permission_request.py"", "statusMessage": "Checking approval request" } ] } ], "PostToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py"", "statusMessage": "Reviewing Bash output" } ] } ], "UserPromptSubmit": [ { "hooks": [ { "type": "command", "command": "/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/user_prompt_submit_data_flywheel.py"" } ] } ], "Stop": [ { "hooks": [ { "type": "command", "command": "/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/stop_continue.py"", "timeout": 30 } ] } ] } } Notes:

timeout is in seconds.

If timeout is omitted, Codex uses 600 seconds.

statusMessage is optional.

commandWindows is an optional Windows-only command override. In TOML, use command_windows or commandWindows.

async is parsed, but async command hooks aren’t supported yet. Codex skips handlers with async: true.

Only type: "command" handlers run today. prompt and agent handlers are parsed but skipped.

Commands run with the session cwd as their working directory.

For repo-local hooks, prefer resolving from the git root instead of using a relative path such as .codex/hooks/.... Codex may be started from a subdirectory, and a git-root-based path keeps the hook location stable.

Equivalent inline TOML in config.toml:

[[hooks.PreToolUse]] matcher = "^Bash$" [[hooks.PreToolUse.hooks]] type = "command" command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py"' timeout = 30 statusMessage = "Checking Bash command" [[hooks.PostToolUse]] matcher = "^Bash$" [[hooks.PostToolUse.hooks]] type = "command" command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py"' timeout = 30 statusMessage = "Reviewing Bash output" Managed hooks from requirements.toml Enterprise-managed requirements can also define hooks inline under [hooks]. This is useful when admins want to enforce the hook configuration while delivering the actual scripts through MDM or another device-management system. To enforce managed hooks even for users who disabled hooks locally, pin [features].hooks = true in requirements.toml alongside [hooks]. To ignore user, project, session, and plugin hooks while still allowing administrator managed hooks, set allow_managed_hooks_only = true.

allow_managed_hooks_only = true [features] hooks = true [hooks] managed_dir = "/enterprise/hooks" windows_managed_dir = 'C:\enterprise\hooks' [[hooks.PreToolUse]] matcher = "^Bash$" [[hooks.PreToolUse.hooks]] type = "command" command = "python3 /enterprise/hooks/pre_tool_use_policy.py" command_windows = 'py -3 C:\enterprise\hooks\pre_tool_use_policy.py' timeout = 30 statusMessage = "Checking managed Bash command" Notes for managed hooks:

managed_dir is used on macOS and Linux.

windows_managed_dir is used on Windows.

Codex doesn’t distribute the scripts in managed_dir; your enterprise tooling must install and update them separately.

Managed hook commands should use absolute script paths under the configured managed directory.

allow_managed_hooks_only = true skips hooks from user, project, session, and plugin sources, but still loads managed hooks from requirements.toml and other managed config layers.

Plugin-bundled hooks

When a plugin is enabled, Codex can load lifecycle hooks from that plugin alongside user, project, and managed hooks.

By default, Codex looks for hooks/hooks.json inside the plugin root. A plugin manifest can override that default with a hooks entry in .codex-plugin/plugin.json. The manifest entry can be a ./-prefixed path, an array of ./-prefixed paths, an inline hooks object, or an array of inline hooks objects.

{ "name": "repo-policy", "hooks": "./hooks/hooks.json" } Manifest hook paths are resolved relative to the plugin root and must stay inside that root. If a manifest defines hooks, Codex uses those manifest entries instead of the default hooks/hooks.json.

Plugin hook commands receive these environment variables:

PLUGIN_ROOT is a Codex-specific extension that points to the installed plugin root.

PLUGIN_DATA is a Codex-specific extension that points to the plugin’s writable data directory.

Codex also sets CLAUDE_PLUGIN_ROOT and CLAUDE_PLUGIN_DATA for compatibility with existing plugin hooks.

Plugin hooks use the same event schema as other hooks. Installing or enabling a plugin doesn’t automatically trust its hooks; Codex skips plugin-bundled hooks until you review and trust the current hook definition.

Matcher patterns

The matcher field is a regex string that filters when hooks fire. Use "*", "", or omit matcher entirely to match every occurrence of a supported event.

Only some current Codex events honor matcher:

Event

What matcher filters

Notes

PermissionRequest

tool name

Support includes Bash, apply_patch*, and MCP tool names

PostToolUse

tool name

Support includes Bash, apply_patch*, and MCP tool names

PostCompact

compaction trigger

Values are manual or auto