Codex CLI — feature surface & doc map¶
Purpose. A living index of OpenAI Codex CLI features, where each one is authoritatively documented (on-disk + official URL), and how it relates to anvil. Use it to decide which Codex surfaces to exploit next without re-discovering them. Companion to the point-in-time research brief
docs/research/2026-06-19-maximize-anvil-codex.md(ranked opportunities) — this file is the map, that file is the bets.
Astra integration (2026-09-09): Anvil now supports llm_provider: codex
with gpt-6-astra through a ChatGPT subscription. Work-queue and reconciliation
templates pin Astra with high effort and remain paused on installation.
Use LLM configuration for provider selection and
current-harness execution for local or
subscription sessions. The inventory below retains its original observation
date; it is not evidence of current host configuration.
Historical inventory verified against: codex-cli 0.130.0, macOS, 2026-06-19. The recorded model was gpt-5.5 (reasoning low|medium|high|xhigh). Codex moves fast —
re-verify flags with codex <cmd> --help and re-read ~/.codex/ before relying on
any "needs smoke test" item below.
How to refresh this doc: fan out parallel research agents (one per section), each reading the on-disk sources + the official URL, then reconcile and adversarially fact-check the claims before updating here.
Where to find the truth¶
| Kind | Location |
|---|---|
| Official docs root | https://developers.openai.com/codex/ |
| Config reference | https://developers.openai.com/codex/config-reference · https://developers.openai.com/codex/config-basic |
| MCP | https://developers.openai.com/codex/mcp |
| Sandbox / approvals / trust | https://developers.openai.com/codex/agent-approvals-security |
Non-interactive (exec) |
https://developers.openai.com/codex/noninteractive |
| CLI reference (resume/fork) | https://developers.openai.com/codex/cli/reference |
| App-server (JSON-RPC) | https://developers.openai.com/codex/app-server |
| Cloud environments | https://developers.openai.com/codex/cloud/environments |
| Memories · AGENTS.md · custom prompts | https://developers.openai.com/codex/memories · https://developers.openai.com/codex/guides/agents-md · https://developers.openai.com/codex/custom-prompts |
| notify / advanced config | https://developers.openai.com/codex/config-advanced |
| On-disk authoritative state | ~/.codex/ (a.k.a. CODEX_HOME) |
| ↳ config | ~/.codex/config.toml (TOML; never text-edit — see constraints) |
| ↳ valid models + reasoning levels | ~/.codex/models_cache.json |
| ↳ installed plugins / marketplaces | ~/.codex/plugins/cache/, ~/.codex/.tmp/marketplaces/ |
| ↳ a pulled plugin (anvil) | ~/.codex/worktrees/<hash>/anvil/ |
| ↳ installed skills | ~/.codex/skills/<name>/agents/openai.yaml |
| ↳ automations | ~/.codex/automations/<id>/automation.toml |
| ↳ sessions (rollouts) | ~/.codex/sessions/YYYY/MM/DD/rollout-<ISO>-<UUID>.jsonl + ~/.codex/session_index.jsonl |
| ↳ memories (experimental) | ~/.codex/memories/ (MEMORY.md, raw_memories.md, …) + ~/.codex/memories_1.sqlite (store, at CODEX_HOME root) |
| Loader source of truth (schemas) | strings compiled into the codex binary (core-skills/src/loader.rs etc.) |
anvil ↔ Codex at a glance¶
| Surface | anvil status | Notes |
|---|---|---|
| Plugin / marketplace | ✅ shipped | codex plugin marketplace add fakoli/anvil pulls the repo root as the plugin |
| MCP server | ✅ shipped | codex mcp add anvil -- uv run --quiet --project …/bin python -m anvil.mcp_server (36 tools); never via config edit |
Skills (openai.yaml) |
✅ this PR (B41) | 8 skills now named in the picker/Plugins panel |
| Hooks | ✅ free via root plugin | root hooks/hooks.json uses shell-free uv run --quiet … anvil.cli hook dispatch … commands |
| Automations | ✅ shipped, PAUSED | anvil install codex --automations; never auto-activated |
Headless exec/review runners |
⬜ B41 follow-on | packaging/codex/loops/ (not built yet) |
| PostToolUse lease heartbeat | ✅ shipped (B41) | anvil hook heartbeat, wired in hooks.json (non-blocking, cross-harness) |
| Stop-hook evidence gate | ✅ verb shipped, OPT-IN (B41) | anvil hook stop-gate; NOT auto-wired (blocking) — opt in + /hooks trust + verify (below) |
| Sessions / fork / cloud | ⬜ deferred | opt-in, experimental; see brief Phase 4 |
Feature surface¶
Status legend: ✓ verified (on-disk or CLI) · ▲ needs live smoke test · ✗ avoid / blocked.
Plugins & marketplaces¶
- What:
codex plugin marketplace add <owner/repo | URL | local-dir>registers a marketplace; the in-app/plugin install <plugin>@<marketplace>installs it. Codex reads a Claude-compatible marketplace at.claude-plugin/marketplace.jsonand the plugin manifest at.claude-plugin/plugin.json. ✓ - anvil: the install pulls the repo root (
source: "./"), soskills/,hooks/, and.mcp.jsonship from root —packaging/codex/.codex-plugin/is a staging artifact not on the real install path (install.py only consumespackaging/codex/automations/). ✓ (verified by localmarketplace add+ the pulled copy at~/.codex/worktrees/315d/anvil/). - Source:
codex plugin marketplace --help;~/.codex/config.toml[marketplaces.*]/[plugins.*].
Skills (agents/openai.yaml)¶
- What: per-skill metadata for the
/skillspicker + Plugins panel. Lives atskills/<name>/agents/openai.yaml; Claude Code ignores theagents/dir (dual-harness clean). ✓ - Schema (
interface:block; the loader recognizes exactly these keys):
| key | required | notes |
|---|---|---|
display_name |
conv. | picker chip label; every shipped skill sets it (a folder-name fallback exists, but no on-disk skill omits it) |
short_description |
conv. | should be 25–64 chars — a scaffolder guideline (init_skill.py), not enforced by the runtime loader: 170/760 shipped skills (incl. OpenAI's own) sit outside the range and load fine |
default_prompt |
no | seed prompt; official guidance says reference the skill as $skill-name |
icon_large |
no | ./assets/x.png relative to skill dir (only ~28% of skills set it) |
icon_small |
no | ./assets/x-small.svg |
brand_color |
no | hex e.g. "#60a5fa" — picker badge accent |
"conv." = conventional: the loader recognizes the key and effectively defaults it; it is not a
reproducible hard load error to omit it (no shipped skill does). Top-level (siblings of interface:,
not under it): policy.allow_implicit_invocation (auto-invoke without a pick — ~112 skills),
dependencies.tools (declare MCP deps — 27/760). anvil omits both (no MCP deps; don't want
auto-claim). Corpus counts are point-in-time. ✓
- Namespacing: plugin skills are auto-prefixed plugin_name: → anvil's appear as
anvil:claim, etc. Do not repeat "Anvil" in display_name (double-prefix). ✓
- anvil: 8 minimal files shipped this PR. Icons skipped (optional; avoids 8 duplicated
binaries). brand_color: "#60a5fa" (reused from the repo's own skills diagram).
- Source: ~/.codex/skills/{pdf,playwright}/agents/openai.yaml; loader strings in the binary.
Hooks¶
- What: plugin lifecycle hooks using the identical Claude Code
hooks.jsonschema (regexmatcheron tool name;command/timeout). Codex auto-discovershooks/hooks.jsonfrom the plugin root — no manifest key needed (ahookskey is supported but optional; Codex's own scaffolder emits"hooks": "./hooks.json"under.codex-plugin/). Every cached real plugin (handoff, fakoli-state, hookify, …) uses bare discovery. The runtime parser is strict: the root object ofhooks/hooks.jsonmust containhooksonly; top-level metadata such asdescriptionis rejected before hook review. ✓ - Plugin-root var: the binary honors
${CLAUDE_PLUGIN_ROOT}(alsoPLUGIN_ROOT; noCODEX_PLUGIN_ROOT) — anvil's existing var is correct, no fallback needed. ✓ - SessionStart output contract: Codex expects SessionStart hooks that inject context to emit
JSON on stdout, using
hookSpecificOutput.hookEventName="SessionStart"andhookSpecificOutput.additionalContext. Plain-text stdout is rejected as invalid SessionStart JSON output. ✓ - Events fired (0.130.0):
SessionStart,UserPromptSubmit,PreToolUse,PermissionRequest,PostToolUse,PreCompact,PostCompact,Stop("right before Codex ends its turn"). Matcher semantics match Claude Code. Not supported:SessionEnd,SubagentStart/SubagentStop,Notification,PostToolUseFailure,InstructionsLoaded(the fakolihooks.schema.jsonis a Claude superset, not Codex's runtime). ✓ - Trust (the #1 gotcha): plugin hooks are non-managed — they silently no-op until trusted once.
On startup Codex shows "N hook(s) need review before they can run. Open /hooks to review."; the
/hookspanel marks each "New hook — review required", and trust persists via atrusted_hashinhooks.state(re-prompts if the hook file changes; managed/config hooks are always on). Install + onboarding text MUST tell users to run/hooksand trust anvil's hooks. ✓ - anvil: SessionStart state-inject, PreToolUse claim-check, PostToolUse file-record + Bash
evidence-capture all port via the shared root plugin. The manifest launches
uv run --quiet --project "${CLAUDE_PLUGIN_ROOT}/bin" python -m anvil.cli hook dispatch …instead ofbash …/*.sh, so Windows Codex does not resolve the System32/WSLbash.exestub and hang. The legacy.shhooks remain in the repo as a Claude-compatible fallback/test surface. B41 (shipped): a PostToolUse lease heartbeat (anvil hook heartbeat, wired, non-blocking) keeps a lazy lease fresh on tool activity. - B41 Stop-gate (OPT-IN, blocking).
anvil hook stop-gateis the Codex/Claude analogue of the OpenClaw finish-gate: onStop, if a claimed task lacks submitted evidence it emits{"decision":"block","reason":…}+ exit 2 to force a continuation. It is NOT auto-wired — anvil's bundled hooks are non-blocking by design (docs/design.md), and the Codex Stop-block mechanism is unverified on codex-cli 0.130.0. To enable: add aStophook runninganvil hook stop-gateto your config, run/hooksto trust it, and verify it blocks as expected before relying on it (if exit-2 is treated as a hook error rather than a block signal, it could disrupt turns). Reusesgate-check's decision logic; default-OPEN; loop-guarded viastop_hook_active. - Source: root
hooks/hooks.json; cached examples under~/.codex/plugins/cache/*/hooks/hooks.json; hook loader/trust strings in thecodexbinary.
MCP servers¶
- What:
codex mcp add NAME -- CMD ARGS…(stdio) or--url URL(HTTP);codex mcp list|get|remove|login|logout. Stored as[mcp_servers.<NAME>]inconfig.toml— stdio keyscommand/args/env/cwd/startup_timeout_sec; HTTP keysurl/bearer_token_env_var/http_headers. ✓ - anvil: native, stable —
codex mcp add anvil -- uv run --quiet --project …/bin python -m anvil.mcp_server(36 tools). The MCP is wired out-of-band from the plugin (not via the manifest's mcpServers). ✓ - Source:
codex mcp --help;~/.codex/config.toml[mcp_servers.*]; https://developers.openai.com/codex/mcp.
Config, models & profiles¶
- What:
~/.codex/config.toml(TOML). Real top-level keys seen:model(gpt-5.5),model_reasoning_effort(high),sandbox_mode,notify,[projects],[marketplaces],[plugins],[features],[memories],[mcp_servers.*]. Profiles[profiles.NAME]bundle model/sandbox/approval defaults, invoked-p/--profile NAME. ✓ - Valid models / reasoning (
models_cache.json):gpt-5.5,gpt-5.4,gpt-5.4-mini,gpt-5.3-codex-spark,codex-auto-review; reasoninglow|medium|high|xhigh(docs also listminimal). Validate against the cache; don't hardcode. ✓ (note: automation templates still saygpt-5-codex/high— likely stale, re-check). -c key=valueoverrides any config via dotted TOML path, highest precedence, parsed as TOML (literal-string fallback). All per-run config goes through this — never text-edit config.toml. ✓- Source:
~/.codex/config.toml,~/.codex/models_cache.json; config-reference URL above.
Sandbox, approvals & trust¶
- Sandbox (
-s/--sandbox):read-only·workspace-write·danger-full-access. ✓ - Approval (
-a):untrusted·on-request·never·on-failure(deprecated);--dangerously-bypass-approvals-and-sandbox(alias--yolo) removes both. ✓ - Trust:
[projects]→"<path>" = { trust_level = "trusted" }, granted on first-run prompt or/permissions. The anvil repo path is already trusted on this machine. ✓ - anvil posture: unattended runs →
workspace-writewith the worktree as cwd (+--add-dirif the state dir is outside cwd); avoiddanger-full-access/--yolooutside a dedicated VM. - Source:
codex exec --help; agent-approvals-security URL above.
Automations (scheduled agent runs)¶
- What:
~/.codex/automations/<id>/automation.toml. Keys:version,id,kind="cron",name,prompt,status,rrule,model,reasoning_effort,execution_environment,cwds(array),created_at,updated_at. Schedule = iCal RRULE (e.g.FREQ=WEEKLY;BYDAY=MO,WE,FR;BYHOUR=9;BYMINUTE=0), not cron syntax.status="PAUSED"until the user activates. ✓ - anvil: ships
anvil-work-queue+anvil-sync-reconcilePAUSED, namespaced per project (anvil install codex --automations). Each is an isolated dir — no shared state to corrupt. - ▲ Skill sigil in an automation prompt (
$claim/$execute) is documented but unconfirmed on this build — no active automation uses it; skills must be marketplace-installed to resolve. Smoke-test before rewriting prompts to drive skills by name. - ▲ Scheduler "local-vs-UTC double-fire" is folklore in our brief (no upstream cite) — anvil's exclusive lease makes a double-fire idempotent regardless.
- Source:
packaging/codex/automations/*/automation.toml;~/.codex/automations/.
Headless runners — codex exec / codex review¶
codex execflags:-s/--sandbox,-c key=value,-p/--profile,-m/--model,-C/--cd,--add-dir <DIR>,--output-schema <FILE>,--json(JSONL events),-o/--output-last-message <FILE>,--ephemeral. No-aonexec(only--dangerously-bypass-…). ✓codex reviewflags:--uncommitted,--base <BRANCH>,--commit <SHA>,--title,-c(--enable/--disable). It has no--jsonand no-o.codex exec reviewis the capturable variant — it adds-m/--model,--json, and-o/--output-last-message. So to gate programmatically: runcodex exec review, ask the prompt to end with aVERDICT PASS|FAILline, and read the-o/--jsonoutput. Barecodex reviewmust be parsed from stdout. ✓- ▲
--output-schemamay be silently ignored when MCP servers are active (anvil's MCP is configured) — prefer calling the anvil CLI inside the run + parsing the last message. Verify live. - anvil: B41 Phase 3 —
packaging/codex/loops/(anvil-exec-queue.sh,anvil-review-branch.sh,anvil-sync.sh). Not built yet. - Source:
codex exec --help,codex review --help; noninteractive URL above.
Sessions, resume & fork¶
- Storage: JSONL rollout per session at
~/.codex/sessions/YYYY/MM/DD/rollout-<ISO>-<UUID>.jsonl; first linesession_metawhosepayload.id(= filename UUID) is the session id. Index at~/.codex/session_index.jsonl(id,thread_name,updated_at). ✓ - Resume:
codex resume [SESSION_ID|thread-name] [PROMPT],--last,--all; headlesscodex exec resume [SESSION_ID|thread-name] [PROMPT] --json -o <FILE>. ✓ - Session id from
exec --json: thethread.startedevent carriesthread_id. ✓ - Fork:
codex fork [SESSION_ID|--last]— stable; forks a prior session into a new branch. ✓ - ▲
--output-schema+resumeappears unsupported together (exec resume --helpomits the flag) — verify before relying on structured output across resumes. - anvil opportunity: persist
thread_idon a claim row → one claim ↔ one resumable session;codex forkmaps one base session → N parallel agent branches.
Cloud & apply (experimental)¶
codex cloud[EXPERIMENTAL]:exec(--env <ENV_ID>required,--branch,--attempts Nbest-of-N),status,list --json --limit 1-20 --cursor,apply,diff.cloud execneeds a target environment id; environments are an optional customization layer (a default universal image exists), and a pushed remote branch is likely but unverified. ▲codex apply <TASK_ID>(aliasa):git applyof the latest diff produced by a Codex agent for that task id (help wording — not explicitly Cloud-scoped in--help);codex cloud apply <TASK_ID> [--attempt N]is the cloud-scoped form. Cloud task ids appear to be a different namespace from local session UUIDs (plausible, not confirmed). ▲- anvil: keep docs-only / opt-in (fights local-first; separate id namespace). Brief Phase 4.
- Source:
codex cloud --help; cloud-environments URL above.
App-server & remote-control (experimental)¶
- What:
codex app-serverandcodex remote-controlexpose Codex over JSON-RPC 2.0 (MCP-like, bidirectional;thread/startednotification, identity viathread.sessionId). Transports:stdio://(default),unix://,ws://IP:PORT(experimental),off. Both[experimental]. Related:codex exec-server,codex mcp-server(Codex as an MCP server). ✓ - anvil opportunity: a single app-server as the headless orchestration backend with many thin agent clients — speculative, revisit when the surface stabilizes.
- Source:
codex app-server --help; app-server URL above.
Memory ("memories")¶
- What: Codex's native durable cross-thread memory — not a single growing transcript and
not prompt-bounded. A structured, SQLite-backed background job pipeline. Markdown lives in
~/.codex/memories/(MEMORY.md— structured# Task Group/## User preferences/## Reusable knowledge/## Failures— plusraw_memories.md,memory_summary.md,rollout_summaries/*.md); the store is~/.codex/memories_1.sqliteat the CODEX_HOME root (sibling ofmemories/, not inside it; tablesstage1_outputs,jobs). Generated async in the background by Codex, secret-redacted; read back viause_memories. Experimental, ON here. ✓ - Config:
[features].memories,[memories] generate_memories/use_memories. Inspect flags viacodex features list. - anvil: Codex's lossy, host-owned analog to anvil's authoritative cross-run state — informative, not a substitute. anvil should not depend on it.
- Source:
~/.codex/memories/; https://developers.openai.com/codex/memories.
Notify & instructions & prompts¶
- notify — a single, exclusive external program (
notify = ["prog","args…"]) invoked async on events (documented:agent-turn-complete); JSON payload; fire-and-forget, cannot block. It is already set on this machine (Computer Use owns it). A plugin must not overwrite it — chain instead. Fine for a finish-gate ping, useless as a gate. ✓ https://developers.openai.com/codex/config-advanced - AGENTS.md layering — directory-tree scoped; more-deeply-nested wins; a direct prompt beats
all. Chain:
~/.codex/AGENTS(.override).md(global) → git-root → … → cwd, concatenated root-first, later overrides earlier, capped atproject_doc_max_bytes(32 KiB). Inspect live withcodex debug prompt-input. anvil drops repo-rootAGENTS.md. ✓ https://developers.openai.com/codex/guides/agents-md - Custom prompts / slash commands — deprecated in favor of skills, user-home-only
(
~/.codex/prompts/), not repo/plugin/marketplace-distributable. Ship aSKILL.md, never a prompt file. ✗ https://developers.openai.com/codex/custom-prompts - Channels (Slack/Telegram/…) — not a Codex CLI feature. The official Slack integration targets
Codex cloud (
@Codex→ cloud task);slack_*strings in the binary are MCP connector tool titles, not a built-in channel. Native channels are an OpenClaw distinction, not Codex. So anvil can't use a Codex CLI channel for pings — usenotifyor an MCP connector. ✓
Other subcommands (lower-priority, captured for completeness)¶
codex login/logout(+login status) — ChatGPT auth.codex update— self-update.codex completion <shell>— shell completions.codex sandbox <macos|linux|windows> -- <CMD>— run a command under the platform sandbox (takes a per-OS subcommand, not a bare command).codex debug→prompt-input(introspect the assembled instruction layers — handy for verifying AGENTS.md precedence),models,app-server.codex features list|enable|disable— the canonical feature-flag inspector.codex mcp-server/codex exec-server— Codex as a server.
Hard constraints (do not violate)¶
- Never text-edit
~/.codex/config.toml. anvil corrupted it before (a~/.codex/config.toml.corrupt-*backup proves it — naive line-editing dropped the quotes on[projects]/[plugins]keys). All per-run config goes throughcodex … -c key=valueflags or Codex's ownmcp add/marketplace addcommands. codex reviewhas no--json/-o— for a capturable verdict usecodex exec review(which has both) and parse aVERDICT PASS|FAILline; never promise structured JSON from barecodex review.- Don't ship custom prompt files (deprecated, user-home-only) or claim the global
notifykey. - Validate model / reasoning_effort against
models_cache.json— don't hardcodegpt-5-codex.
Verify-before-building (live smoke tests still owed)¶
- Skill sigil resolution inside an active automation run.
--output-schemabehavior with anvil's MCP server active (exec) and withresume.mediumreasoning validity for the current model; whether automation templates'gpt-5-codexis stale.codex exec review --jsonverdict semantics.