Quick Take

Three Plugins, Zero Standards

ai-toolingpluginsstandardsclaude-codecodex-cligemini-cliecosystem

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.json
At 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.json
skills/<name>/SKILL.md
agents/<name>.md
commands/<name>.md
hooks/hooks.json
.mcp.json
.lsp.json
settings.json
.codex-plugin/plugin.json
skills/<name>/SKILL.md
.app.json
.mcp.json
No agents/, hooks/, or lsp/ dirs
gemini-extension.json
commands/<name>.toml
skills/
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
✓ Supported
mcpServers 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 ✓ Supported
hooks/hooks.json — 17 named events
✗ Not supported
No hooks component in plugin spec
✓ Supported
hooks/ 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
✓ Supported
GEMINI.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 ✓ Supported
themes/ dir — unique to Gemini
Tool restrictions / exclusions ≈ Partial
Per-agent tool scoping via settings.json
≈ Partial
Per-agent sandbox mode in agent TOML
✓ Supported
excludeTools 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.