Codex
Run uvx jes login first. See Setup.
Install
uvx jes codex-settings
Copy the hooks object it prints into ~/.codex/hooks.json. Codex ignores a
hook until you trust its exact text with /hooks.
uvx jes codex-settings
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "uvx jes@0.0.1 codex-hook",
"timeout": 60
}
]
}
],
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "uvx jes@0.0.1 codex-hook",
"timeout": 60
}
]
}
],
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "uvx jes@0.0.1 codex-hook",
"timeout": 60
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "uvx jes@0.0.1 codex-hook",
"timeout": 60
}
]
}
]
}
}
What each hook does
| Event | Stage | If jes blocks |
|---|---|---|
UserPromptSubmit | input | Returns decision: block with the refusal as the reason. |
PreToolUse | tool_call | Denies the call. The tool does not run. |
PostToolUse | tool_result | Returns decision: block, so the model reads the refusal instead of the result. |
Stop | output | Checks last_assistant_message and continues the turn with the refusal. |
Limits
PostToolUsefires after the command has run. It does not undo it. Codex doesn't applyupdatedToolOutput, so jes usesdecision: blockinstead.Stopdoes not change text already on screen. A secondStopwithstop_hook_activeset is skipped, so a refusal can't loop.