Plugins
August 28, 2026 · View on GitHub
Distributable bundles that package skills, commands, hooks, MCP servers, and permissions under a single versioned manifest.
Overview
A plugin is a directory in ~/.agents/plugins/ containing a .claude-plugin/plugin.json manifest. When you install or sync a plugin, agents-cli copies its contents into a synthetic per-user marketplace inside the agent version home and enables it via enabledPlugins in settings.json. Plugins are a superset of individual resources: a single plugin can ship skills, slash commands, subagent definitions, hooks, MCP servers, LSP servers, monitors, bin scripts, and permission sets — installed atomically as a unit.
Only agents with plugins: true (or a version-gated since) in the capability matrix participate — today Claude, OpenClaw, Antigravity, Grok, Kimi, Cursor, Goose, Droid, and Codex >= 0.128.0 (capableAgents('plugins') in src/lib/agents.ts). Plugins can narrow further by declaring agents: [...] in their manifest. Plugins that ship executable surfaces (hooks, .mcp.json, bin/, scripts/, settings.json, permissions/) require explicit consent via --allow-exec-surfaces to be enabled after installation.
For the layered resource model that governs plugin resolution, see resource-sync.md.
Architecture
~/.agents/plugins/<name>/ Central source (user-authored, git-tracked)
.claude-plugin/
plugin.json Required manifest: name, version, description
skills/<skill>/SKILL.md Slash-command knowledge packs
commands/*.md Slash commands (converted to skills on Codex >= 0.117.0)
agents/*.md Subagent definitions
hooks/hooks.json ◄ exec Hook registrations — triggers exec-surface gate
.mcp.json ◄ exec MCP server declarations
bin/ ◄ exec Executable binaries
scripts/ ◄ exec Arbitrary shell scripts
settings.json ◄ exec Agent settings merge (non-permissions keys)
permissions/ ◄ exec Permission group YAML files
.user-config.json Per-install user config values (runtime, not shipped)
.source Git remote recorded at install time
On agents plugins sync / install
│
▼
<version-home>/.claude/
plugins/
known_marketplaces.json "agents-cli" → marketplaces/agents-cli (registered)
marketplaces/agents-cli/
.claude-plugin/marketplace.json Lists every discovered plugin
plugins/<name>/ Copy of source (user-config placeholders resolved)
settings.json
enabledPlugins["<name>@agents-cli"] = true (only when exec gate passes)
Command Reference
| Command | Description |
|---|---|
agents plugins list | Table view of all plugins with sync status across agent versions |
agents plugins view <name> | Metadata, resources, and installation status for one plugin |
agents plugins info <name> | Alias for view |
agents plugins install <spec> | Install from a git URL or local path |
agents install plugin:<spec> | Same install path (Phase 5 umbrella); same --allow-exec-surfaces gate |
agents plugins update [name] | Re-pull from original source and re-sync (all plugins if no name given) |
agents plugins sync <name> [agent] | Apply a plugin to the default version of an agent (all supported agents if none given) |
agents plugins remove [name] | Unsync from all agent versions; optionally delete source directory |
Options
| Command | Flag | Effect |
|---|---|---|
install | --allow-exec-surfaces | Enable the plugin even when it ships hooks, MCP, bin, scripts, settings, or permissions |
sync | --allow-exec-surfaces | Same gate override for the sync path |
remove | --keep-source | Unsync from agents but leave ~/.agents/plugins/<name>/ on disk |
Manifest Schema
.claude-plugin/plugin.json is the required entry point. Every field maps directly to PluginManifest in src/lib/types.ts:378.
{
"name": "git",
"version": "1.0.0",
"description": "Git workflow commands — atomic grouped commits and merged-branch cleanup.",
"agents": ["claude", "openclaw"],
"dependencies": ["other-plugin"],
"userConfig": [
{
"key": "api_url",
"description": "Base URL for the API",
"required": true,
"default": "https://api.example.com"
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Plugin identifier; must match the directory name |
version | string | yes | SemVer string shown in agents plugins list |
description | string | yes | One-line description |
agents | AgentId[] | no | Limit to specific agents; omit to support all capable agents |
dependencies | string[] | no | Other plugin names required; missing deps produce a warning at install |
userConfig | PluginUserConfigField[] | no | Interactive fields prompted at install time; values stored in .user-config.json |
Directory Layout
my-plugin/
.claude-plugin/
plugin.json # Required manifest
skills/
my-skill/
SKILL.md # Skill definition
commands/
my-command.md # Slash command
agents/
my-subagent.md # Subagent definition
hooks/
hooks.json # Hook registrations (exec surface)
.mcp.json # MCP server config (exec surface)
bin/
my-binary # Executable (exec surface)
scripts/
setup.sh # Setup scripts (exec surface)
settings.json # Settings to merge (exec surface)
permissions/
my-perms.yaml # Permission group (exec surface)
Exec-Surface Consent Gate
Plugins that ship any of hooks/, .mcp.json, bin/, scripts/, a non-permissions settings.json, or permissions/ are installed (copied to the marketplace) but not enabled unless you pass --allow-exec-surfaces.
The gate is implemented at src/commands/plugins.ts:68:
export function shouldRefusePluginInstall(
capabilities: PluginCapabilities,
allowExecSurfaces: boolean
): boolean {
return hasPluginExecSurfaces(capabilities) && !allowExecSurfaces;
}
PluginCapabilities maps each surface to a boolean flag (hasHooks, hasMcp, hasBin, hasScripts, hasSettings, hasPermissions). hasPluginExecSurfaces() returns true if any flag is true.
Without the flag, the plugin is placed in the marketplace and listed in known_marketplaces.json, but enabledPlugins["<name>@agents-cli"] is never set to true in settings.json. The plugin is present but inert until you re-run with consent.
This prevents automated sync flows (agents use claude@<v>) from silently activating third-party code that runs shell scripts or registers MCP servers in every new session.
Recipes
1. Install a plugin from GitHub
agents plugins install rush-toolkit@https://github.com/user/rush-toolkit.git
# If the plugin ships hooks or MCP and you trust the source:
agents plugins install rush-toolkit@https://github.com/user/rush-toolkit.git --allow-exec-surfaces
2. Install from a local path
agents plugins install ~/Projects/my-plugin
# or with an explicit name:
agents plugins install rush-toolkit@~/Projects/rush-toolkit
3. Sync after pulling the repo
# After git pull or manual edits to ~/.agents/plugins/my-plugin/:
agents plugins sync my-plugin
agents plugins sync my-plugin claude --allow-exec-surfaces
4. List what is installed and enabled
agents plugins list
agents plugins view rush-toolkit
5. Remove a plugin
# Remove from all agents and delete source:
agents plugins remove rush-toolkit
# Unsync only, keep source directory:
agents plugins remove rush-toolkit --keep-source
Demo
See Also
- resource-sync.md — how plugins participate in the layered resource sync model
- docs/subagents.md — subagent definitions that plugins can bundle
- docs/hooks.md — hook manifests that plugins can ship
- docs/workflows.md — workflow bundles that can reference plugins