- name
- conventions-codex
- description
- Codex CLI artifact schemas: config.toml, .codex-plugin, skills, hooks, AGENTS.md.
- version
- 0.3.2
# Codex CLI Conventions
Tool-specific overlay for OpenAI Codex CLI artifacts. Loaded by the scorer and checker when an artifact is classified as **Tier 2-Codex** (per `agents/scorer.md` step 3). The universal floor lives in `nlpm:conventions`; this overlay adds Codex-specific schemas on top.
**Last refreshed:** 2026-08-02 against Codex 0.146.0 (2026-07-29); §3/§4 field-type + policy-enum corrections 2026-08-04; §3 artifact-path, `commands` and symlink corrections 2026-10-01, observed on Codex 0.159.2.
**Primary authoritative sources:**
- <https://learn.chatgpt.com/docs> (the `developers.openai.com/codex/*` tree now 308-redirects here)
- <https://learn.chatgpt.com/docs/build-skills>
- <https://learn.chatgpt.com/docs/agent-configuration/agents-md>
- <https://learn.chatgpt.com/docs/agent-configuration/subagents>
- <https://learn.chatgpt.com/docs/config-file/config-reference>
- <https://learn.chatgpt.com/docs/hooks>
- <https://learn.chatgpt.com/docs/skills-and-plugins>
- <https://learn.chatgpt.com/docs/changelog>
- <https://github.com/openai/codex> (authoritative for `CLA.md`, `contributing.md`, and machine-readable spec samples like `plugin-json-spec.md`)
---
## 1. File system layout
Codex separates the **cross-tool surface** (`.agents/`) from the **Codex-private surface** (`.codex/`). The Claude Code mental model of "everything under one tool directory" does not transfer.
| Artifact | Project scope | User scope |
|---|---|---|
| Skills | `.agents/skills/<name>/SKILL.md` (CWD→repo-root scan) | `~/.agents/skills/`, admin `/etc/codex/skills/` |
| Plugin manifest | `<plugin-root>/.codex-plugin/plugin.json` | — |
| Marketplace | `.agents/plugins/marketplace.json` (+ legacy `.claude-plugin/marketplace.json`) | `~/.agents/plugins/marketplace.json` |
| AGENTS.md | Git root → CWD; per dir `AGENTS.override.md` → `AGENTS.md` → fallback (one file/dir; closer overrides earlier) | `~/.codex/AGENTS.override.md`, `~/.codex/AGENTS.md` |
| Config | `.codex/config.toml` (trust-gated) | `~/.codex/config.toml` |
| Hooks | `.codex/hooks.json` OR inline `[hooks]` in `config.toml` | `~/.codex/hooks.json` |
| Slash commands / prompts | — (not project-shareable) | `~/.codex/prompts/<name>.md` |
| MCP servers | `[mcp_servers.<id>]` table inside `config.toml` | same |
| Skill sidecar (Codex-specific) | `<skill>/agents/openai.yaml` next to `SKILL.md` | — |
**Trust gate:** Project hooks load only when `.codex/` is trusted. Trust is enforced via `/hooks` and `allow_managed_hooks_only` in `requirements.toml`.
---
## 2. SKILL.md (Tier 1, open spec — `name`, `description` required only)
Codex reads SKILL.md from `.agents/skills/`, **not** `.codex/skills/` (reconfirmed 2026-08-02 against `build-skills`: *"Codex reads skills from `.agents/skills` directories, not `.codex/skills`"*; it scans `.agents/skills` from CWD up to the repo root, plus `~/.agents/skills` and `/etc/codex/skills`). Skills placed under `.codex/skills/` — a common mistake in repos ported from Claude Code's `.claude/skills/` habit — are **not discovered** by Codex; flag them. The required frontmatter is the agentskills.io baseline — `name` and `description` — same as every other tool.
**Codex-specific extras live in a SIDECAR `agents/openai.yaml`**, not in SKILL.md frontmatter. Treat the sidecar as additive metadata, not a deviation from the open spec.
**Sidecar fields:**
```yaml
# <skill>/agents/openai.yaml
interface:
display_name: "My Skill"
short_description: "One-line summary" # added 2026-06
default_prompt: "Use this skill when..."
icon_small: "icon-16.png"
icon_large: "icon-512.png" # added 2026-06
brand_color: "#FF6B00"
policy:
allow_implicit_invocation: true # default true; set false to disable auto-selection
dependencies:
tools:
- type: "bash"
value: "jq"
description: "JSON processor"
transport: "stdio"
```
**Correction (2026-06-07):** `dependencies.tools` is an **array of tool objects** (`type`, `value`, `description`, `transport`), **not** a bare string list. The v0.1.0 `- bash` / `- jq` form was wrong.
Duplicate-name skills across scopes are NOT merged — both appear in selectors, repo-level wins for local workflows.
---
## 3. `.codex-plugin/plugin.json` (plugin manifest)
```json
{
"name": "my-codex-plugin",
"version": "1.0.0",
"description": "Short summary",
"skills": "./skills/",
"mcpServers": "./mcp/servers.json",
"apps": "./apps/",
"hooks": "./hooks.json",
"commands": [],
"interface": {
"displayName": "My Plugin",
"longDescription": "Detailed description for installer UI"
}
}
```
**Required field:** `name` (kebab-case) — and only when a `plugin.json` is present at all. **All other top-level fields are optional**, including `version`, `description`, `author`, and `interface` (corrected 2026-08-02 against the vendor's `plugin-json-spec.md`; the earlier "version + description required" claim was wrong). Codex 0.159.2 installs a plugin whose manifest lacks `name` but loads none of its skills, so a missing `name` fails silently.
**Optional artifact paths — each a relative-path STRING** (corrected 2026-08-04 against the vendor `plugin-json-spec.md`): `skills` (string), `hooks` (string), `apps` (string), `mcpServers` (string **or** object). A bare directory string like `"skills": "./skills/"` is the documented sample form. Do NOT flag a scalar-string `skills`/`hooks`/`apps`/`mcpServers` as wrong. (The v0.3.0 overlay showed only an array form; Codex 0.159.2 also accepts a one-element array for `skills`, but the string is the documented form.)
**Optional identity fields (added 2026-06):** `author` (`{name, email, url}`), `homepage`, `repository`, `license`, `keywords`.
**Optional UI block** `interface`:
- `displayName`, `shortDescription`, `longDescription`, `developerName`, `category`, `capabilities`
- `defaultPrompt` — an **array** of starter prompts (not a single string). **At most 3 entries** (extras ignored); **each capped at 128 characters** (longer entries truncated).
- `websiteURL`, `privacyPolicyURL`, `termsOfServiceURL`
- `brandColor`, `composerIcon`, `logo`, `logoDark` (dark-mode logo variant), `screenshots` (PNG files stored under `./assets/`, paths relative to plugin root)
**Notation:** `$+ARGUMENTS` and `$+{CLAUDE_PLUGIN_ROOT}` in this file are split by a `+` so Claude Code does not replace them when it loads this skill; the real tokens have no `+` (`nlpm:conventions-claude` §2.4).
**A manifest path REPLACES the default location; it does not add to it** (observed on Codex 0.159.2 on 2026-10-01 through the app-server `skills/list` and `hooks/list` methods; this corrects the earlier "paths supplement default discovery" claim):
- `skills` — with no `skills` key, Codex loads the plugin-root `skills/` directory. With `"skills": "./codex/skills/"`, root `skills/` is not loaded.
- `hooks` — with no `hooks` key, Codex loads the plugin-root `hooks/hooks.json`, including a Claude Code plugin's hooks file (it expands `$+{CLAUDE_PLUGIN_ROOT}` there). With `"hooks": "./codex/hooks.json"`, the root file is not loaded. To keep a plugin's Claude hooks out of Codex, point `hooks` at a file holding `{"hooks": {}}`.
- `apps`, `mcpServers` — replacement vs. addition not verified this pass.
**`"commands": []`** stops Codex turning a plugin's Claude Code `commands/*.md` into extra skills. Without it, Codex 0.159.2 converts commands into skills named `source-command-<command>` (written under `.codex-plugin/migrated-command-skills/` in the installed copy), which duplicate a Codex port's own skills. It converts only some commands (commands whose body uses `$+ARGUMENTS` were skipped in testing, and a few without it were too), so the duplicates are easy to miss. A plugin that ships a Claude `commands/` directory alongside a Codex skill tree should set `"commands": []`; do NOT flag the key as unknown.
**A symlinked skill directory is dropped on install** (Codex 0.159.2, local and Git marketplace sources alike): the installed copy omits it, so the skill never loads. Ship real directories in a Codex skill tree.
---
## 4. `.agents/plugins/marketplace.json` (marketplace manifest)
Three marketplace tiers exist in Codex:
- **Official Curated** — OpenAI-managed. Workspace plugin publishing and additional marketplaces (Amazon Bedrock, Claude Code interop) shipped in 0.146.0 (2026-07-29); the exact self-serve mechanics are still settling — verify before asserting.
- **Repository** — `<repo-root>/.agents/plugins/marketplace.json` aggregates plugins shipped from that repo.
- **Personal** — `~/.agents/plugins/marketplace.json`.
Schema:
```json
{
"name": "xiaolai",
"interface": {
"displayName": "xiaolai Marketplace"
},
"plugins": [
{
"name": "nlpm",
"source": {
"source": "github",
"repo": "xiaolai/nlpm"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_USE"
},
"category": "developer-tools",
"interface": {
"displayName": "nlpm"
}
}
]
}
```
Per-plugin `source` types: `"github"`, `"git"`, `"local"`.
**`policy` values are UPPERCASE enums** (corrected 2026-08-04 against nlpm's own shipping `.agents/plugins/marketplace.json` and observed real plugins): `installation` e.g. `"AVAILABLE"`; `authentication` e.g. `"ON_USE"` or `"ON_INSTALL"`. The lowercase `"auto"`/`"none"` the v0.3.0 overlay showed was wrong — do NOT flag `AVAILABLE`/`ON_USE`/`ON_INSTALL` as invalid.
---
## 5. `.codex/config.toml`
TOML — NOT JSON. Top-level sections nlpm cares about:
- `[features]` — feature flags. **Breaking change ~2026-04 (CLI 0.129+):** `[features].codex_hooks` was renamed to `[features].hooks` (boolean; enables hooks from `hooks.json` or inline `[hooks]`). Old key is a deprecated alias and emits a warning. Flag config files that still use `codex_hooks`.
- `[mcp_servers.<id>]` — MCP server registrations. Fields: `command`, `args`, `cwd`, `url`, `enabled`, `enabled_tools`, `disabled_tools`, `env`, `startup_timeout_sec`, `tool_timeout_sec` (per-tool, default 60s — added 2026-06).
- `[hooks.<event>]` — inline hook registrations (alternative to `.codex/hooks.json`).
- `[agents]` — **global** subagent settings ONLY: `enabled`, `max_concurrent_threads_per_session`, `default_subagent_model`, `default_subagent_reasoning_effort`, `interrupt_message`. Individual subagents are **not** `[agents.<name>]` tables — they are standalone TOML files at `.codex/agents/*.toml` (project) or `~/.codex/agents/*.toml` (personal), one file per agent, with required `name` + `description` + `developer_instructions` and optional `model`, `model_reasoning_effort`, `sandbox_mode`, `mcp_servers`, `skills.config` (corrected 2026-08-02 — the old `[agents.<name>]` / `config_file` / `nickname_candidates` schema is gone).
- `[permissions.*]` — permission policy.
- `project_doc_max_bytes`, `project_doc_fallback_filenames` — AGENTS.md controls (see §7).
- Optional model-catalog JSON path loaded at startup, overridable per profile.
**REMOVED — `[profiles.*]` table syntax is gone as of 0.134.0 (2026-05-26).** Profiles now live in dedicated per-profile files `~/.codex/<name>.config.toml`, selected with `--profile <name>`. A config still using `[profiles.foo]` tables is stale — flag it.
**No `.mcp.json` at repo root.** Codex does NOT read Claude's `.mcp.json` — MCP servers live inside `config.toml`. A bridge from `.mcp.json` → `.codex/config.toml` is a common port pattern (see `cc-suite:bridge-mcp` skill).
Ver en GitHub