cheat sheet

Codex Tips & AGENTS.md

Advanced Codex CLI workflows — AGENTS.md authoring, profiles, experimental hooks, notify callbacks, non-interactive exec scripting, session resume and fork, and codex cloud.

Codex Tips & AGENTS.md

What it is

This page covers the high-leverage Codex CLI features that go beyond basic usage: writing effective AGENTS.md project memory files, using profiles to switch contexts instantly, enabling the experimental hooks system for automation, scripting with codex exec, and using session management (resume, fork) for long-running tasks.


AGENTS.md — project memory

AGENTS.md is a Markdown file that Codex reads automatically when it starts in a project directory. It provides repo-specific context — build commands, conventions, off-limits files — so you don't have to repeat yourself every session. Think of it as a "README for the agent" — anything you'd put in onboarding docs for a new human contributor belongs here too.

Discovery and merge order

Codex walks upward from the cwd, collecting every AGENTS.md it finds, then merges them with the closest file's content taking precedence. A user-level ~/AGENTS.md is also merged last (lowest priority).

bash
# Inspect which AGENTS.md files Codex sees
codex --print-agents-md

Output:

text
/home/alice/myproject/AGENTS.md       (project, 1842 bytes)
/home/alice/AGENTS.md                  (user, 412 bytes)
merged total: 2254 bytes

Scaffold with /init

bash
codex
# then type: /init

Output: (AGENTS.md created in the current directory with scaffolded sections)

Effective AGENTS.md structure

markdown
# <Project name>

## Build & test
- Install: `pip install -e ".[dev]"`
- Unit tests: `pytest -x -q`
- Lint: `ruff check . && mypy src/`
- Format: `ruff format .`

## Architecture
- `src/api/` — FastAPI routers; each file = one resource
- `src/db/` — SQLAlchemy models; do not edit migrations directly
- `src/services/` — business logic; no DB calls here

## Conventions
- Docstrings: Google style
- Line length: 100
- No `print()` in library code — use `logging`
- All public functions need type annotations

## Off-limits
- Never edit `migrations/` — run `alembic revision --autogenerate`
- Do not modify `pyproject.toml` unless asked
- `src/legacy/` is frozen — read-only

## Useful one-liners
- Find TODO comments: `rg "TODO|FIXME" src/`
- Check coverage: `pytest --cov=src --cov-report=term-missing`

Config knobs

toml
# ~/.codex/config.toml
project_doc_max_bytes = 65536          # default 32768
project_doc_fallback_filenames = ["AGENTS.md", "CLAUDE.md"]

Anti-patterns in AGENTS.md

  • Don't include secrets. AGENTS.md is read into the prompt on every session. Tokens, passwords, and API keys end up in the model's context.
  • Don't restate what's obvious from the file tree. "We have a src/ directory" wastes tokens.
  • Don't write essays. Tight bullet lists outperform paragraphs for steerability. Aim for under 2,000 bytes per file.
  • Don't list every dependency. Mention only the ones whose conventions differ from defaults.
  • Don't repeat global standards. "Use 4 spaces, not tabs" belongs in .editorconfig. AGENTS.md should describe this project, not industry norms.

Profiles — instant context switching

Define profiles in config.toml and activate with -p <name>:

toml
[profiles.review]
model              = "gpt-4o"
approval_policy    = "untrusted"
sandbox_mode       = "read-only"

[profiles.sprint]
model                  = "gpt-5"
model_reasoning_effort = "high"
approval_policy        = "on-request"
sandbox_mode           = "workspace-write"

[profiles.ci]
model           = "gpt-4o-mini"
approval_policy = "never"
sandbox_mode    = "workspace-write"
bash
codex -p review "Is this code safe to merge?"
codex -p sprint "Implement the new auth flow"
codex exec -p ci "Fix all type errors in src/"

Output: (each opens a session with that profile's settings)


Experimental hooks

Hooks let you run arbitrary scripts before or after the agent calls a tool — useful for audit logging, blocking dangerous commands, or transforming tool inputs. The hook system is experimental, gated behind a feature flag, and may change between Codex releases. The current API mirrors Claude Code's PreToolUse/PostToolUse hooks but uses TOML for the manifest. Codex has a PreToolUse hook system (experimental). Enable in config.toml:

toml
[features]
codex_hooks = true

Define hooks in the same file using [[hooks.PreToolUse]] tables:

toml
[[hooks.PreToolUse]]
matcher = "shell"           # matches tool name (regex)
command = ["bash", "-c", "echo '[hook] shell command: '\"$CODEX_TOOL_INPUT\""]
timeout = 5                 # seconds

[[hooks.PreToolUse]]
matcher = "write_file"
command = ["python3", "/home/alice/.codex/check_path.py"]
timeout = 10

Or define hooks in a separate hooks.json file:

json
{
  "PreToolUse": [
    {
      "matcher": "shell",
      "command": ["bash", "-c", "echo 'running: '\"$CODEX_TOOL_INPUT\""],
      "timeout": 5
    }
  ]
}

Output: (none — config files)

Project-level hooks live in <project>/.codex/hooks.json and are loaded only when the project is trusted.

Hook environment variables passed to the command include CODEX_TOOL_INPUT (JSON-encoded tool arguments) and CODEX_SESSION_ID.

Block a tool call from a hook

Exit code 2 from a PreToolUse hook denies the tool call. The agent sees a structured rejection and adjusts.

python
# ~/.codex/check_path.py
import json, os, sys
data = json.loads(os.environ.get("CODEX_TOOL_INPUT", "{}"))
path = data.get("path", "")
if path.startswith("/etc") or path.startswith("/usr"):
    print(f"Hook denied write to {path}", file=sys.stderr)
    sys.exit(2)
sys.exit(0)

Output: (none — runs as a hook)

PostToolUse hook for audit logging

toml
[[hooks.PostToolUse]]
matcher = ".*"   # match every tool
command = ["bash", "-c", "echo \"$(date -Iseconds) $CODEX_TOOL_NAME $CODEX_TOOL_INPUT\" >> ~/.codex/audit.log"]
timeout = 2

Output: (none — writes audit log entry after every tool call)

bash
tail -3 ~/.codex/audit.log

Output:

text
2026-05-25T14:02:01-07:00 shell {"cmd":"pytest -x"}
2026-05-25T14:02:11-07:00 write_file {"path":"src/auth/jwt.py"}
2026-05-25T14:02:13-07:00 shell {"cmd":"ruff check src/"}

Notify — turn-complete callbacks

notify is a per-turn callback that fires after every agent turn ends (success or failure). The callback receives a JSON payload on stdin describing the turn outcome. It is the simplest way to get desktop notifications, Slack pings, or audit metrics out of a long-running session. Run any script whenever an agent turn ends. Useful for desktop notifications, metrics, or logging:

toml
notify = ["python3", "/home/alice/.codex/notify.py"]

The script receives a JSON payload on stdin:

json
{
  "type":                   "agent-turn-complete",
  "thread-id":              "th_01abc",
  "turn-id":                "t_01xyz",
  "cwd":                    "/home/alice/myproject",
  "input-messages":         [...],
  "last-assistant-message": "Done — added 5 unit tests."
}

macOS notification example:

python
import json, sys, subprocess
data = json.load(sys.stdin)
msg = data.get("last-assistant-message", "Codex finished")[:120]
subprocess.run(["osascript", "-e", f'display notification "{msg}" with title "Codex"'])

Output: (none — invoked by Codex after each turn)

Linux notification via notify-send:

python
import json, sys, subprocess
data = json.load(sys.stdin)
msg = data.get("last-assistant-message", "Codex finished")[:120]
subprocess.run(["notify-send", "Codex", msg])

Output: (none — fires a desktop notification)

Slack webhook example:

python
import json, sys, urllib.request
data = json.load(sys.stdin)
payload = {"text": f"Codex turn complete: {data.get('last-assistant-message','')[:200]}"}
req = urllib.request.Request(
    "https://hooks.slack.com/services/T000/B000/XXX",
    data=json.dumps(payload).encode(),
    headers={"Content-Type": "application/json"},
)
urllib.request.urlopen(req)

Output: (none — posts to Slack)


codex exec — non-interactive scripting

codex exec runs the agent end-to-end without the TUI and exits when done. It is the workhorse for CI pipelines, git hooks, editor integrations, and shell scripts. See the dedicated exec mode page for the full reference; the recipes below cover the most common patterns. Run a task and exit — ideal for CI, git hooks, and shell pipelines.

bash
codex exec "Fix all ruff lint errors in src/"

Output:

text
[agent output, diffs, final summary]

NDJSON event stream for programmatic parsing:

bash
codex exec --json "Summarise the last 10 commits" | jq 'select(.type=="agent-turn-complete") | .["last-assistant-message"]'

Output:

text
"The last 10 commits added authentication, refactored the DB layer, and fixed 3 bugs."

Ephemeral session (no history saved):

bash
codex exec --ephemeral "Is there a memory leak in this diff?"

Output: (analysis output; session not persisted)

Combined with --full-auto for autonomous CI fix:

bash
codex exec --full-auto -p ci "Run pytest, fix any failing tests, stage the changes"

Output: (agent runs, edits files, prints summary)


Session resume and fork

Codex persists every interactive session as a thread under ~/.codex/history/. Resume loads the thread's full message history into a new TUI; fork creates a new thread that shares ancestry with the original but evolves independently. Forking is the recommended way to "try an alternative" without losing the current line of inquiry. List and resume past sessions:

bash
codex resume

Output: (interactive picker showing recent sessions)

Resume a specific session by ID:

bash
codex resume th_01abc

Output: (TUI opens at that session's last state)

Fork from a past point (useful for exploring alternatives):

bash
codex fork th_01abc

Output: (new session branched from th_01abc)

Inside the TUI, use /resume to open the session picker and /fork to fork the current session.


Replay a session as a transcript

Sometimes you want the conversation but not a live resume. Dump it as JSON:

bash
codex sessions show th_01abc --json > /tmp/session.json

Output: (none — writes JSON transcript)

bash
jq '.messages | length' /tmp/session.json

Output:

text
24

codex cloud — remote execution

Submit a task to OpenAI-managed remote infra. Useful for long-running tasks or environments without a local agent. Cloud sessions inherit your AGENTS.md (if uploaded with --with-agents-md) but do NOT inherit your local config.toml, MCP servers, or hooks — they run in OpenAI's container.

bash
codex cloud "Migrate the entire test suite from unittest to pytest"

Output:

text
Task submitted: task_id=tsk_abc123
Streaming progress…
[agent output]
Task complete. View at https://platform.openai.com/tasks/tsk_abc123

Apply a completed cloud task's changes locally:

bash
codex apply tsk_abc123

Output:

text
Applying patch from task tsk_abc123…
3 files modified.

List recent cloud tasks:

bash
codex cloud list

Output:

text
tsk_abc123  2026-05-25  RUNNING  "Migrate test suite to pytest"
tsk_def456  2026-05-24  DONE     "Refactor auth module"
tsk_ghi789  2026-05-22  FAILED   "Add JWT support"

Cancel a running cloud task:

bash
codex cloud cancel tsk_abc123

Output:

text
Cancelled tsk_abc123.

Power-user patterns

Pre-commit hook

bash
# .git/hooks/pre-commit (chmod +x)
#!/usr/bin/env bash
codex exec --full-auto -p ci "Fix any ruff or mypy errors in the staged files"
git add -u

Output: (agent fixes issues then exits; commit proceeds)

Summarise a PR diff

bash
git diff main...HEAD | codex exec --output-last-message "Summarise these changes for a PR description"

Output:

text
This PR refactors the authentication module to use JWT tokens instead of session cookies, adds rate limiting middleware, and updates the test suite.

Generate a changelog entry

bash
git log --oneline v1.2.0..HEAD | codex exec --output-last-message "Write a changelog entry for these commits"

Output:

text
## v1.3.0
- Added JWT authentication
- Rate limiting on all API endpoints
- Fixed flaky tests in the auth suite

Alias for quick tasks

bash
# ~/.bashrc or ~/.zshrc
alias cx='codex exec --full-auto'

Output: (none — exits 0 on success)

Then:

bash
cx "Add a README section documenting the config options"

Output: (agent writes to README.md and exits)

Bisect with Codex

Use Codex to explain why a specific commit broke a test, accelerating git bisect:

bash
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run bash -c 'pytest -x tests/test_foo.py || (codex exec --sandbox read-only --ask-for-approval never "Explain why the last commit broke tests/test_foo.py" && exit 1)'

Output:

text
Bisecting: 12 revisions left to test after this (roughly 4 steps)
[agent explains the breakage at each step]
abc1234 is the first bad commit

Auto-summarise an unfamiliar repo

A first-glance summary that touches nothing on disk:

bash
codex exec --sandbox read-only --ask-for-approval never \
  "What is this repo? List the main modules, their public API, and any non-obvious conventions."

Output:

text
This repository is …
Modules: src/api/, src/db/, src/services/
Public API: GET /users, POST /sessions, …
Conventions: dependency injection via constructors; no module-level state.

Codex as an editor pre-save formatter

Trigger Codex from your editor's save hook (here, Neovim's BufWritePost):

vim
autocmd BufWritePost *.py silent! !codex exec --full-auto -p ci "Format only the file <afile>" >/dev/null 2>&1

Output: (none — runs on save)

Audit log of all sessions

Aggregate every session's outcome into a single file via the notify hook:

python
# ~/.codex/audit.py
import json, sys, os, datetime
data = json.load(sys.stdin)
line = json.dumps({
    "ts":   datetime.datetime.now().isoformat(timespec="seconds"),
    "cwd":  data.get("cwd"),
    "turn": data.get("turn-id"),
    "msg":  data.get("last-assistant-message", "")[:300],
})
with open(os.path.expanduser("~/.codex/audit.jsonl"), "a") as f:
    f.write(line + "\n")
toml
notify = ["python3", "/home/alice/.codex/audit.py"]

Output: (none — every turn now appends a JSONL line to ~/.codex/audit.jsonl)


Common pitfalls

  1. AGENTS.md is parsed as Markdown but injected as text. Triple-backtick code blocks render visually in your editor but are sent verbatim to the model. Keep examples short.

  2. Profiles defined in a project's .codex/config.toml are merged, not replaced. A profile named quick in both global and project config will compose key-by-key, not overwrite wholesale. Use distinct profile names per scope to avoid confusion.

  3. Notify scripts that exceed 1 second block the next turn. Codex waits for the notify process to exit before signalling "ready for next turn." Use subprocess.Popen(..., start_new_session=True) and return immediately if you need long-running side effects.

  4. codex resume does not roll back disk state. A resumed session sees whatever is currently on disk. Use git stash + git stash pop if you need to roll back between turns.

  5. Hooks fire BEFORE sandbox enforcement. A PreToolUse hook can see a tool call that would have been denied by the sandbox. Don't rely on hooks to enforce the sandbox; rely on the sandbox to enforce the sandbox.

  6. codex cloud does NOT inherit local MCP servers. Cloud tasks run in OpenAI's container with a stock environment. Use cloud only for tasks that need no local tools.

  7. /init overwrites an existing AGENTS.md. It will prompt first, but accept-by-default behaviour is destructive. Back up the existing file first if it has any value.

  8. Custom commands cannot override built-in commands. A ~/.codex/commands/diff.md is silently ignored because /diff is built in. Pick a different name.


Real-world recipes (extended)

Per-directory cd hook to auto-load profile

Define a shell function that detects a profile marker file and re-launches Codex with the right profile.

bash
function cx() {
  if [ -f .codex/profile ]; then
    command codex -p "$(cat .codex/profile)" "$@"
  else
    command codex "$@"
  fi
}

Output: (none — defines a wrapper)

bash
echo "deep" > .codex/profile
cx

Output: (TUI opens with the deep profile)

Auto-run AGENTS.md scaffolder on git init

Add a ~/.gitconfig template that runs codex /init after git init in any new repo:

ini
[init]
templatedir = ~/.git_template
bash
mkdir -p ~/.git_template/hooks
cat > ~/.git_template/hooks/post-create-template <<'EOF'
#!/usr/bin/env bash
codex exec --full-auto "Scaffold an AGENTS.md based on the existing files."
EOF
chmod +x ~/.git_template/hooks/post-create-template

Output: (none — defines the template)

Drift detector — fail CI if uncommitted Codex edits

Useful for catching cases where a developer ran Codex locally but forgot to commit the edits:

bash
codex exec --sandbox read-only --ask-for-approval never \
  "List any files in the working tree that differ from origin/main. Output only the file paths."

Output:

text
src/auth.py
tests/test_auth.py

Stream agent output to syslog

Pipe to logger so every Codex turn lands in the system log:

bash
codex exec --json "Fix all type errors" | tee >(logger -t codex)

Output:

text
{"type":"agent-turn-complete","turn-id":"t_01","last-assistant-message":"Fixed 4 type errors in src/auth.py"}