Plugin manifest cannot ship agent roles

Open 💬 0 comments Opened Aug 4, 2026 by qwang07

Plugins can ship skills, MCP servers, apps, and hooks, but not agent roles. A plugin whose value is partly a set of specialized subagents can only ask users to copy .toml files into each project by hand.

Where it breaks

RawPluginManifest accepts name, version, description, keywords, skills, mcp_servers, apps, hooks, and interface. There is no agents field, so nothing in a plugin bundle can register an agent role.

Agent roles come only from config layers — load_agent_roles walks config_layer_stack.layers_low_to_high() and, for each layer, scans config_folder.join("agents"). config_folder() resolves to a directory for three sources:

ConfigLayerSource::System { file } => file.parent(),
ConfigLayerSource::User { file, .. } => file.parent(),
ConfigLayerSource::Project { dot_codex_folder } => Some(dot_codex_folder.clone()),

System, user, and project. Installed plugins are not a config layer, so a plugin's directory is never scanned for agent roles no matter how it is laid out.

Why it matters

Skills and agent roles are complementary halves of the same workflow. A skill describes a phase of work; an agent role gives that phase a model tier, a reasoning effort, a sandbox mode, and instructions scoped to it. Splitting their distribution means a plugin can ship half of itself:

  • the skills install and work everywhere
  • the agent roles have to be copied into <project>/.codex/agents/ for every project, by every user, and re-copied whenever the plugin updates

There is no version linkage — a stale hand-copied role silently pairs with an updated skill.

Claude Code distributes both through the plugin: agents/*.md alongside skills/, both resolved from the plugin root. A plugin targeting both clients ships working subagents on one and a manual-copy instruction on the other.

Suggestion

Add an agents path field to the manifest, resolved from the plugin root the same way skills is:

{
  "name": "example",
  "skills": "./skills/",
  "agents": "./agents/"
}

Plugin-provided roles would slot into the layer precedence below project-level roles, so a project's own .codex/agents/ still wins on a name collision. Namespacing them by plugin name — as skills already are — would avoid collisions between plugins.

Context

Hit this shipping a plugin that pairs six skills with six agent roles. Claude Code installs all twelve. Codex installs the six skills; the six .toml roles have to be copied into each project by hand.

Versions: codex-cli 0.146.0, source read at main.

View original on GitHub ↗