Codex CLI¶
AgentLint runs as Codex native command hooks that check Bash commands and every file touched by apply_patch.
Install¶
agentlint setup codex # project: .codex/hooks.json
agentlint setup codex --global # user: ~/.codex/hooks.json
Setup merges AgentLint entries into hooks.json, keeps your other hooks, and uses the absolute path of the agentlint binary. If the project has no agentlint.yml, it creates one. --dry-run prints the result without writing.
Codex also needs hooks enabled in ~/.codex/config.toml. Setup adds this under the existing [features] table (or creates it) and removes legacy codex_hooks keys:
[features]
hooks = true
Do not append hooks = true to the end of the file by hand. TOML assigns a bare key to the most recent table, so it can land in an unrelated section and stop Codex from loading its config.
Restart Codex from a terminal that has any AgentLint or AgentChute environment variables you rely on.
Verify¶
- Start Codex and open
/hooks. Review and trust the AgentLint hook definitions. Codex will not run new or changed hooks until you do, and AgentLint never writes trust state for you. - Make one tool call (for example, ask Codex to run
ls). - Run:
agentlint status
The codex line reports configured -> enabled -> observed. "Enabled" reflects [features].hooks in ~/.codex/config.toml. "Observed" comes from a heartbeat written on every hook call. If it stays "never observed" after a tool call, the hook is not reaching AgentLint: check /hooks trust and reload the session.
status and doctor recognize user-scope installs and hook commands that delegate to your own wrapper script. In those cases doctor --fix does not add a second project-level integration.
What is checked¶
| Event | Matcher | Command |
|---|---|---|
PreToolUse |
^(Bash\|apply_patch)$ |
agentlint check --event PreToolUse --adapter codex |
PostToolUse |
^(Bash\|apply_patch)$ |
agentlint check --event PostToolUse --adapter codex |
UserPromptSubmit |
all | agentlint check --event UserPromptSubmit --adapter codex |
SessionStart |
all | agentlint check --event SessionStart --adapter codex |
Stop |
all | agentlint report --adapter codex |
All hooks have a 30-second timeout. The Codex formatter always exits 0; decisions are carried in JSON:
- PreToolUse, blocking:
hookSpecificOutput.permissionDecision: "deny"withpermissionDecisionReason. - PreToolUse, advisory:
hookSpecificOutput.additionalContext. - PostToolUse:
additionalContextwith all findings; ERRORs add"decision": "block"and areason. - UserPromptSubmit:
additionalContext; ERRORs add"decision": "block". - SessionStart:
additionalContext; ERRORs add"continue": false.
AgentLint validates its own Codex response before printing it. An invalid response, malformed hook input or a configuration error exits 2 with a message on stderr.
apply_patch inspection¶
Codex sends patches in tool_input.command. AgentLint splits a patch into per-file checks for additions, updates, deletions and renames (both paths of a rename are checked) and reports findings across all files. Before execution it reads existing files and applies the hunks in memory; it never writes the patch. After execution, PostToolUse reads the real resulting files and compares them with the cached pre-edit content.
A patch is denied if it:
- touches a path outside the project, including through a symlink;
- has a missing or ambiguous hunk, or an unsupported envelope;
- overwrites an existing file with an Add or Move operation;
- exceeds 2 MB, targets a file over 5 MB, or touches more than 200 files.
Relative paths resolve against the tool's working directory, bounded by the project directory. A denied ambiguous hunk names the file, the hunk, the repeated context line and the candidate line numbers. Add more unique context lines; an @@ anchor only skips matches before the anchor line. Do not reroute a rejected patch through a shell write.
Preview a patch with the same validator and rules, without writing anything:
agentlint check-patch change.patch --project-dir .
agentlint check-patch change.patch --json # {decision, rules_evaluated, violations}
It exits 1 when the patch would be denied. MCP clients can use the check_patch tool instead (see MCP).
Configuration notes¶
- Project directory:
--project-dir, thenAGENTLINT_PROJECT_DIR, thenCODEX_PROJECT_DIR, then the tool's working directory from the hook payload. - Session state is keyed on the Codex
session_idfrom the payload unlessAGENTLINT_SESSION_IDis set. AGENTLINT_WORKSPACE_CONFIGpoints at shared workspace defaults and required rules; see Configuration.- Only native
Bashandapply_patchare interpreted. Arbitrary MCP tool calls have their own argument semantics and are not treated as shell or file operations. Hooks are not a security sandbox; keep running repository CI checks (CI). - If you use a custom wrapper script, update its matchers and project/session routing yourself when AgentLint's hook definitions change, then re-trust it in
/hooks. Do not write trust hashes or use hook-trust bypass flags.
Codex hook reference: https://developers.openai.com/codex/hooks
Uninstall¶
agentlint uninstall codex # project scope
agentlint uninstall codex --global # user scope
Only AgentLint entries are removed. [features].hooks in ~/.codex/config.toml is left as is.
Troubleshooting¶
- Never observed: trust the hooks in
/hooks, confirmhooks = trueis under[features], and start a fresh Codex session. - Edits not checked: make sure the matcher includes
apply_patch. Codex reportsapply_patch, notWrite/Edit. Rerunagentlint setup codexin the scope you use and re-trust. - Patch denied as ambiguous or missing: follow the line numbers in the denial and preview with
agentlint check-patch. - General issues: Diagnostics. No events in the cloud dashboard: AgentChute and
agentlint agentchute status.