| name | codex-orchestration |
| description | Authoritative reference for OpenAI Codex CLI's orchestration surface - skills discovery paths, agents/openai.yaml schema, hooks.json events and stdin JSON payload, notify config, and AGENTS.md precedence. Use when installing skills, wiring hooks, debugging why a skill or hook isn't firing, or explaining Codex-vs-Claude-Code differences. Do NOT use for general Codex usage tips, prompt engineering, or non-orchestration configuration like models/approvals. |
Codex Orchestration
How OpenAI Codex CLI discovers skills, fires hooks, and loads instructions. Use this skill instead of web-searching the docs each time.
Source of truth: developers.openai.com/codex/{skills,hooks,config-advanced,config-reference,guides/agents-md}. Verify against live docs if behavior seems off - orchestration features evolve.
When to Activate
- Install a skill and need the correct target directory
- Wire a Codex hook and need the event name, matcher, or payload shape
- Debug why an implicit skill isn't triggering on a matching prompt
- Author or review
agents/openai.yaml for a skill
- Explain to a teammate why format-on-edit hooks don't work in Codex
- Migrate hook or skill conventions from Claude Code to Codex
- Add a
notify program for turn-complete events
- Write or update this repo's README/hooks documentation
Discovery: Skills
Skills are folders containing SKILL.md (required) plus optional scripts/, references/, assets/, and agents/openai.yaml. Codex scans these paths (earlier wins on activation; duplicate names do NOT merge - both appear in selector):
| Priority | Path | Scope |
|---|
| 1 | $CWD/.agents/skills | Current folder |
| 2 | $CWD/../.agents/skills | Parent folder |
| 3 | $REPO_ROOT/.agents/skills | Repo root |
| 4 | $HOME/.agents/skills | User |
| 5 | /etc/codex/skills | Admin |
| 6 | Bundled | System |
Common mistake: copying skills to ~/.codex/skills/ - that path does nothing. Use ~/.agents/skills/.
SKILL.md Format
---
name: kebab-case-name
description: What it does, when to use it, AND when NOT to use it. Codex matches this for implicit invocation.
---
The body loads only after the skill activates. Front-load triggers and scope boundaries in description.
agents/openai.yaml (Optional)
Put it at skills/<name>/agents/openai.yaml:
interface:
display_name: "User-facing name"
short_description: "User-facing description"
icon_small: "./assets/small-logo.svg"
icon_large: "./assets/large-logo.png"
brand_color: "#3B82F6"
default_prompt: "Optional surrounding prompt"
policy:
allow_implicit_invocation: true
dependencies:
tools:
- type: "mcp"
value: "serverName"
description: "Required MCP server"
Set allow_implicit_invocation: false for skills that should only fire on explicit /skills or $skillname invocation.
Activation Methods
| Method | How |
|---|
| Implicit | Codex auto-selects based on description matching the user prompt |
| Explicit (menu) | User runs /skills |
| Explicit (inline) | User types $skillname in a prompt |
Hooks (Experimental)
Enable with:
[features]
codex_hooks = true
Config lives at ~/.codex/hooks.json or <repo>/.codex/hooks.json.
Events
| Event | Fires | Matcher matches |
|---|
SessionStart | Session start or resume | "startup" or "resume" |
PreToolUse | Before tool execution | Tool name - Bash only currently |
PostToolUse | After tool completion | Tool name - Bash only currently |
UserPromptSubmit | User submits prompt | N/A - use "*" or omit |
Stop | Turn concludes | N/A - use "*" or omit |
Critical limit: PreToolUse/PostToolUse match Bash only. File-edit hooks (format-on-save) cannot fire through Codex hooks. Use pre-commit, husky, or a Makefile target.
hooks.json Schema
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "hooks/scripts/guard_force_push.sh",
"statusMessage": "Checking force-push",
"timeout": 10
}
]
}
]
}
}
Stdin JSON Payload
Hook scripts receive JSON on stdin, not as $1. Common fields:
| Field | Meaning |
|---|
session_id | UUID |
transcript_path | Path to the session transcript |
cwd | Working directory |
hook_event_name | Matches the event |
model | Model name |
turn_id | Current turn UUID |
tool_input | Tool-specific - for Bash, has command |
Parse with jq:
payload="$(cat)"
cmd="$(printf '%s' "$payload" | jq -r '.tool_input.command // empty')"
Exit Codes
| Code | Meaning |
|---|
0 | Allow / continue |
2 | Block the tool call (PreToolUse) |
| Other non-zero | Error surfaced to operator |
For SessionStart, UserPromptSubmit, Stop, scripts can emit a JSON object on stdout with: continue (bool), stopReason (string), systemMessage (string), suppressOutput (bool).
notify (Stable, simpler)
Separate from hooks.json. Lives in config.toml:
notify = ["python3", "/path/to/notify.py"]
Fires on agent-turn-complete only. Receives JSON as argv[1] (single string arg), NOT stdin. Fields: type, thread-id, turn-id, cwd, input-messages, last-assistant-message.
AGENTS.md Precedence
Codex builds an instruction chain from AGENTS.md files:
~/.codex/AGENTS.override.md (if present, else ~/.codex/AGENTS.md)
- Repo root
AGENTS.md
- Subdirectory
AGENTS.md files closer to $CWD
@file references inside AGENTS.md resolve and inline, so AGENTS.md can pull in skill bodies directly:
## Domain Guidance
- Security audits: @.agents/skills/security/SKILL.md
BAD / GOOD: Installing a Skill
BAD
cp -r my-skill ~/.codex/skills/
GOOD
mkdir -p ~/.agents/skills
cp -r my-skill ~/.agents/skills/
mkdir -p ./.agents/skills
cp -r my-skill ./.agents/skills/
BAD / GOOD: Writing a PreToolUse Hook
BAD
#!/usr/bin/env bash
if [[ "$1" == *"rm -rf"* ]]; then exit 2; fi
GOOD
#!/usr/bin/env bash
set -euo pipefail
payload="$(cat)"
cmd="$(printf '%s' "$payload" | jq -r '.tool_input.command // empty')"
if [[ "$cmd" == *"rm -rf /"* ]]; then
echo "Blocking dangerous rm -rf /" >&2
exit 2
fi
exit 0
BAD / GOOD: Format-on-edit
BAD
{
"hooks": {
"PostToolUse": [
{ "matcher": "Edit", "hooks": [{ "type": "command", "command": "prettier --write" }] }
]
}
}
Edit is not a matchable tool name for PostToolUse today. The hook never fires.
GOOD
Move it to pre-commit:
repos:
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
hooks: [{ id: prettier }]
Or expose it via make fmt and document it in AGENTS.md under ## Environment Setup.
Codex vs Claude Code Differences
| Concern | Codex | Claude Code |
|---|
| Skill install dir | .agents/skills/ | .claude/skills/ or plugin packs |
| Skill frontmatter | name, description | name, description, allowed-tools, etc. |
| Hooks config | hooks.json (experimental) | settings.json hooks block (GA) |
| Pre/PostToolUse matchers | Bash only | All tools including Edit, Write, etc. |
| Hook input | stdin JSON | stdin JSON |
| Turn-complete hook | notify in config.toml | Stop hook |
| Instructions file | AGENTS.md | CLAUDE.md |
Debugging Checklist
When a skill or hook isn't firing:
- Confirm the skill folder lives under one of the six
.agents/skills paths. Duplicate names surface in the selector, they don't merge.
- Check
description for triggering vocabulary. A vague description will miss implicit activation.
- For explicit invocation, run
/skills and verify the skill is listed.
- For hooks, verify
[features] codex_hooks = true is set and hooks.json parses as valid JSON.
- For
PreToolUse/PostToolUse, confirm the matcher is Bash - Edit/Write tools won't fire it.
- Add a trace line to the hook script:
echo "fired: $(date)" >> /tmp/codex-hook.log. If nothing appears, Codex is not invoking the hook.
- Tail the transcript path from the hook payload if you need to see what Codex saw.
Checklist