Three Plugins, Zero Standards
Codex CLI’s plugin system launched two days ago. I already have the comparison table.
I was adding plugin support to something I’m building and needed to figure out what format to target. So I put the three main CLI plugin systems side by side: Claude Code plugins, Codex CLI plugins, Gemini CLI extensions. The full table is below. The short version: nearly identical in architecture, completely incompatible in format.
Back in February I wrote that the plugin bundle pattern was clearly right but nobody had made it a standard. That was a hypothesis. This is the receipts.
| Claude Code Plugin |
Codex CLI Plugin |
Gemini CLI Extension |
|
|---|---|---|---|
| PACKAGE IDENTITY | |||
| Term used | Plugin | Plugin | Extension |
| Manifest file | .claude-plugin/plugin.json |
.codex-plugin/plugin.json |
gemini-extension.jsonAt root, no subdirectory |
| Install command | /plugin install <url> |
codex plugins install <url> |
gemini extensions install <url> |
| Install source | GitHub URL or local path | GitHub URL or local path | GitHub URL or local path |
| Install location | ~/.claude/plugins/ |
~/.codex/plugins/ |
~/.gemini/extensions/ |
| Local dev workflow | --plugin-dir flag + /reload-plugins |
@plugin-creator scaffold skill |
gemini extensions link <path>Symlink — live reload on save |
| Official marketplace | Anthropic-curated + community repos | OpenAI plugin directory | geminicli.com gallery, 70+ extensions, verified partner track |
| FOLDER STRUCTURE | |||
| Top-level layout |
.claude-plugin/plugin.jsonskills/<name>/SKILL.mdagents/<name>.mdcommands/<name>.mdhooks/hooks.json.mcp.json.lsp.jsonsettings.json
|
.codex-plugin/plugin.jsonskills/<name>/SKILL.md.app.json.mcp.jsonNo agents/, hooks/, or lsp/ dirs |
gemini-extension.jsoncommands/<name>.tomlskills/agents/hooks/themes/GEMINI.md
|
| Manifest constraint | Only plugin.json inside .claude-plugin/; everything else at root |
Only plugin.json inside .codex-plugin/; everything else at root |
gemini-extension.json at root — no special subdirectory required |
| BUNDLEABLE COMPONENTS | |||
| Skills | ✓ Supported Markdown + frontmatter in skills/ |
✓ Supported SKILL.md files in skills/ |
✓ Supported Agent skills in skills/ |
| Custom / slash commands | ✓ Supported Markdown in commands/ (legacy) or skills/ |
✓ Supported Via skills — no separate commands dir |
✓ Supported TOML files in commands/ |
| MCP servers | ✓ Supported.mcp.json at plugin root |
✓ Supported.mcp.json at plugin root |
✓ SupportedmcpServers key inside manifest JSON |
| Subagent definitions | ✓ Supported Markdown + YAML in agents/ — first-class, auto-namespaced |
✗ Not supported Agent TOMLs live in .codex/agents/ only — not bundleable in plugin |
~ Experimental Sub-agents in agents/ — in reference docs, flagged 🔬 |
| Hooks / lifecycle events | ✓ Supportedhooks/hooks.json — 17 named events |
✗ Not supported No hooks component in plugin spec |
✓ Supportedhooks/ directory in extension |
| Context file (project instructions) | ≈ Partial Via settings.json; main context is CLAUDE.md |
≈ Partial Via skill prompts; main context is AGENTS.md |
✓ SupportedGEMINI.md bundled in extension, loaded as model context |
| App / connector integrations | ✗ Not supported | ✓ Supported.app.json — unique to Codex; maps to ChatGPT connectors |
✗ Not supported |
| LSP / language servers | ✓ Supported.lsp.json — unique to Claude Code |
✗ Not supported | ✗ Not supported |
| Themes / UI customization | ✗ Not supported | ✗ Not supported | ✓ Supportedthemes/ dir — unique to Gemini |
| Tool restrictions / exclusions | ≈ Partial Per-agent tool scoping via settings.json |
≈ Partial Per-agent sandbox mode in agent TOML |
✓ SupportedexcludeTools in manifest — blocks built-in tools globally |
| Policy engine | ✗ Not supported | ✗ Not supported | ~ Experimental Policy engine component in extension reference |
| NAMESPACING & CONFLICT RESOLUTION | |||
| Skill namespace | plugin-name:skill-name |
plugin-name:skill-name |
/cmd → /ext.cmd on conflict |
| Agent namespace | @plugin-name:agent-name |
N/A — not bundleable | TBD — spec is experimental |
| Conflict precedence | Local --plugin-dir > marketplace plugin > built-ins |
Kebab-case name enforced; latest install wins | User/project commands > extension commands (lowest precedence) |
| STANDALONE AGENT DEFINITION (OUTSIDE PLUGIN) | |||
| File format | Markdown + YAML frontmatter (.md) |
TOML (.toml) |
Markdown — spec experimental |
| Project-scoped path | .claude/agents/ |
.codex/agents/ |
.gemini/agents/ 🔬 |
| User-scoped path | ~/.claude/agents/ |
~/.codex/agents/ |
~/.gemini/agents/ 🔬 |
| Per-agent model selection | ✓ Supported Opus, Sonnet, or Haiku per agent |
✓ Supported Any model incl. gpt-5.4-mini for subagents |
~ Experimental |
| ECOSYSTEM MATURITY | |||
| Plugin system launched | Late 2025 | March 25–26, 2026 | October 2025 |
| Scaffold tooling | /plugin create |
@plugin-creator skill |
gemini extensions new <path> <template> |
| Unique differentiator | LSP server bundling + first-class agent packaging + richest hook system (17 events) | App connector integrations (.app.json) tied into ChatGPT ecosystem | Themes, policy engine, excludeTools, live-link dev workflow, GEMINI.md as bundled context |
Sources: Claude Code docs (code.claude.com), Codex CLI docs (developers.openai.com/codex), Gemini CLI docs (geminicli.com) — March 2026
Look at the agent file format row. All three landed on markdown files with YAML frontmatter. That’s not a coincidence (markdown-plus-frontmatter is just the obvious answer, and skills got standardized at the file level because of it). The architectural instinct is identical across all three teams. Then look at the manifest filename row. Three different filenames, three slightly different key names for what is functionally the same schema.
This is not a technical disagreement. Nobody at Anthropic, OpenAI, and Google independently concluded that JSON with a commands array was the right call, but then had principled architectural reasons to name the keys differently. They just each built their own version without talking to each other. The fragmentation is a coordination failure, not a design one.
The cost is real and I’m paying it right now. To support all three formats I need to either write a converter, maintain three separate manifests, or pick one and leave two platforms worse than they could be. None of those are good options. The glue code I’m writing this week exists entirely because three teams arrived at the same answer and couldn’t be bothered to agree on the spelling.
BUDDY: Three trillion-dollar companies looked at the same problem, built the same solution, and named their JSON keys differently. Open standards died for this.
The architecture has converged. The formats haven’t. In the standards world that usually means one of two things: someone ships a compatibility layer that becomes the de facto standard, or one platform gets dominant enough that everyone else quietly adopts their format. The third option (an actual coordinated standard) requires a body and a process and about four years of meeting minutes. Given that timeline, the compatibility layer is probably what happens first.
When the architecture is this similar, incompatibility is a timing problem, not a fundamental one. The question is just who’s going to get tired of the glue code first. Until then? I write the glue code. And so does everyone else building in this space.