Create Hook
Use this skill when the user wants Codex lifecycle hooks created, updated, audited, or verified. Keep the loop tight: inspect current hook state first, write the smallest deterministic hook, validate it locally, and call out any trust or live-trigger step that remains.
Ground Rules
- Start read-only. Inspect existing hook sources before changing files.
- Treat hooks as behavior-changing automation. Blocking, rewriting, approval, prompt-shaping, or continuation hooks require explicit user intent.
- Prefer
hooks.jsonfor new hook config. If a layer already uses inline[hooks]inconfig.toml, preserve that representation instead of mixing formats unless the user asks to migrate. - Use the current feature key:
[features] hooks = true. Do not addcodex_hooks; it is deprecated. Do not add any feature flag when hooks are already enabled or unspecified. - Keep hook scripts deterministic, fast, and dependency-light. Default to Python stdlib, JSON stdin parsing, explicit timeout, and no network calls.
- Never log secrets, raw credentials, private keys, tokens, full prompts, raw transcripts, or unredacted tool payloads unless the user explicitly asks for a narrow local log and the script redacts sensitive values.
- Do not install watchers, cron jobs, daemons, background agents, or model calls inside hook runtime.
Workflow
Audit the target.
- Run
python3 <skill>/scripts/audit_hooks.py --scope globalfor global hooks. - Run
python3 <skill>/scripts/audit_hooks.py --scope project --repo <repo-root>for project hooks. - Read any reported
hooks.json, inline[hooks], hook script folder, and feature flag state. - Completion criterion: you know the active layer, existing hook representation, whether hooks are disabled, and whether a same-event or same-script hook already exists.
- Run
Determine scope.
- If the user says
global, use~/.codex. - If the user says
project, resolve the repository root withgit rev-parse --show-topleveland use<repo>/.codex. - If scope is missing and the behavior is personal across repos, default to global only if that is clearly implied.
- If scope is still ambiguous, ask:
Should this be global or project-scoped?
- If the user says
Determine event and matcher.
- If the event is missing or unclear, ask which hook event they want.
- Supported events:
SessionStart,SubagentStart,PreToolUse,PermissionRequest,PostToolUse,PreCompact,PostCompact,UserPromptSubmit,SubagentStop,Stop. - Ask for a matcher only for events that honor matchers:
SessionStart,SubagentStart,PreToolUse,PermissionRequest,PostToolUse,PreCompact,PostCompact,SubagentStop. - Do not ask for a matcher for
UserPromptSubmitorStop; Codex ignores matchers for those events. - If the user wants multiple events, keep them as separate matcher groups and scripts unless shared code clearly reduces duplication.
Determine behavior and risk.
- Logging or context-only hooks: proceed after behavior is clear.
- Blocking or denial hooks: confirm the exact deny condition and message.
PermissionRequesthooks: confirm whether the hook can allow, deny, or only log approval requests.- Rewriting hooks with
updatedInput: confirm the exact rewrite and keep it narrow. StoporSubagentStopcontinuation hooks: confirm the stopping condition so they cannot create a noisy loop.- Completion criterion: the trigger, allowed side effects, output contract, and failure mode are explicit.
Write the hook config and script.
- Global config path:
~/.codex/hooks.json; scripts under~/.codex/hooks/. - Project config path:
<repo>/.codex/hooks.json; scripts under<repo>/.codex/hooks/. - Use an explicit command path. For project hooks, prefer git-root resolution:
/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/<script>.py". - For global hooks, prefer an absolute home path:
/usr/bin/python3 "$HOME/.codex/hooks/<script>.py". - Set
timeoutexplicitly; use10seconds for simple log/context hooks and30seconds for validation hooks unless the user requests otherwise. - Use
statusMessageonly for short visible signals likeChecking Bash commandorRecording hook event.
- Global config path:
Use the correct output contract.
- Read
references/hook-contracts.mdbefore writing event-specific output. - For
PreToolUsedenial, preferhookSpecificOutput.permissionDecision = "deny"withpermissionDecisionReason. - For
PermissionRequest, usehookSpecificOutput.decision.behaviorwithallowordeny. - For
UserPromptSubmit, plain stdout orhookSpecificOutput.additionalContextbecomes developer context. - For
Stop,decision: "block"means "continue the turn with this reason", not "reject the final answer".
- Read
Validate locally.
- Compile Python scripts with
python3 -m py_compile <script>. - Parse
hooks.jsonwithpython3 -m json.tool <hooks.json>. - Run the hook script with a minimal sample JSON payload for the chosen event.
- Re-run the audit script and confirm the expected hook appears.
- If project-scoped, confirm the repo is trusted or say that project hooks will not load until the project layer is trusted.
- Compile Python scripts with
Verify Codex trust and runtime behavior.
- Non-managed hooks must be reviewed and trusted in
/hooks; changed hook definitions get a new hash. - Do not claim live hook execution until a trusted hook has actually fired.
- If live triggering is not possible in the current session, close with: standalone validation passed, Codex trust/live trigger still pending.
- Non-managed hooks must be reviewed and trusted in
Practical Defaults
- Use
PreToolUsefor prevention before supported tool calls. - Use
PermissionRequestfor approval-policy automation around escalation requests. - Use
PostToolUsefor logging, review notes, formatting hints, or follow-up checks after supported tools. - Use
UserPromptSubmitfor prompt shaping or context injection. - Use
SessionStartfor startup context. - Use
PreCompactandPostCompactfor compacting workflows. - Use
Stoponly for narrow end-of-turn continuation checks.
Closeout
Report these states explicitly:
- Scope and paths changed.
- Event, matcher, script command, and timeout.
- Validation performed.
- Whether Codex trust review is still required.
- Whether behavior was validated live or only standalone.