--- url: /guide/introduction.md description: Learn how AgentPlugins solves plugin fragmentation across AI agent frameworks --- # Introduction AgentPlugins is a toolchain for AI agent plugins. Write a plugin once, ship it to every supported agent harness from a single manifest. ## The problem Every AI agent framework ships its own plugin system with its own manifest format, hook lifecycle, and handler conventions: | Framework | Manifest | Handler types | | ------------------ | ---------------------------- | --------------------- | | Claude Code | `.claude-plugin/plugin.json` | command, http, prompt | | Codex CLI | `.codex-plugin/plugin.json` | command only | | GitHub Copilot CLI | `plugin.json` | command, http, prompt | | Gemini CLI | `gemini-extension.json` | command only | | Kimi | `kimi.plugin.json` | command only | | OpenCode | TypeScript plugins | inline only | | Pi Mono | TypeScript extensions | inline only | **Seven frameworks, seven different APIs.** A plugin author who wants reach across the ecosystem maintains seven forks of the same logic. Users who switch harnesses lose every plugin they configured. ## The solution AgentPlugins introduces a **universal manifest** ([`agentplugins.config.ts`](/guide/manifest)) and a **universal store** (`~/.agents/plugins/`). You declare hooks, skills, tools, MCP servers, and commands once. The CLI compiles that manifest down to each platform's native format and symlinks the result into every detected agent. ```mermaid flowchart LR plugin["Your Plugin"] --> core["AgentPlugins"] core --> a1["Claude Code"] core --> a2["Codex CLI"] core --> a3["GitHub Copilot"] core --> a4["Gemini CLI"] core --> a5["+ 3 more"] ``` ## Supported platforms Seven harnesses are supported as first-class compile targets: | Agent | Binary | Skill path | | ------------------ | ---------- | --------------------------- | | Claude Code | `claude` | `~/.claude/skills` | | Codex CLI | `codex` | `~/.codex/skills` | | GitHub Copilot CLI | `copilot` | `~/.copilot/skills` | | Gemini CLI | `gemini` | `~/.gemini/skills` | | Kimi | `kimi` | `~/.kimi/skills` | | OpenCode | `opencode` | `~/.config/opencode/skills` | | Pi Mono | `pi` | `~/.pi/extensions` | See the [agent paths reference](/reference/agent-paths) for the full registry and the [adapters reference](/reference/adapters) for what each platform emits. ## Where to go next * [Install](/guide/installation) the CLI. * Walk through the [quick start](/guide/quick-start). * Learn the [manifest format](/guide/manifest). --- --- url: /guide/installation.md description: 'Install the AgentPlugins CLI via npm, Homebrew, curl, or mise' --- # Installation Pick whichever install method fits your environment. ## npm / bun The CLI is published to npm as `@agentplugins/cli`. Install globally: ::: code-group ```bash [npm] npm install -g @agentplugins/cli ``` ```bash [bun] bun add -g @agentplugins/cli ``` ::: Or run it ad-hoc with `npx`: ```bash npx @agentplugins/cli@latest --version ``` ## Homebrew Install from the\_sigilco tap: ```bash brew install sigilco/tap/agentplugins ``` Or install directly from the formula URL: ```bash brew install https://raw.githubusercontent.com/sigilco/agentplugins/main/homebrew/Formula/agentplugins.rb ``` Upgrade with `brew upgrade agentplugins`. ## curl The install script downloads the correct prebuilt binary for your platform and drops it into `/usr/local/bin`: ```bash curl -fsSL __DOCS_SITE__/install.sh | bash ``` ::: tip Inspect the script before running it: `curl -fsSL __DOCS_SITE__/install.sh | less`. ::: ## mise Manage AgentPlugins as a version-pinned tool with [mise](https://mise.jdx.dev/) (uses [ubi](https://github.com/houseabsolute/ubi) under the hood to pull GitHub releases): ```bash mise use -g ubi:sigilco/agentplugins ``` This pins the latest release globally. Use `mise use ubi:sigilco/agentplugins@1.2.0` to pin a specific version per project. ## Verify the install Whichever method you chose, confirm the binary is on your `PATH`: ```bash agentplugins --version # agentplugins 0.2.0 ``` Then run `doctor` to verify AgentPlugins can detect every installed agent harness on your machine: ```bash agentplugins doctor ``` ```text AgentPlugins doctor ──────────────────────────────────────── CLI version 0.2.0 Store path ~/.agents/plugins ✓ Skills path ~/.agents/skills ✓ Detected agents claude ~/.claude/skills ✓ codex ~/.codex/skills ✓ opencode ~/.config/opencode ✓ gemini ~/.gemini/skills ✗ (not installed) copilot ~/.copilot/skills ✓ kimi ~/.kimi/skills ✗ (not installed) pimono ~/.pi/extensions ✗ (not installed) 4 agents detected. Plugins will fan out to those harnesses. ``` ::: warning `doctor` only reports detection. Plugins still install to the universal store (`~/.agents/plugins/`) regardless of how many agents are found. Symlinks are created only for detected agents. ::: ## Next steps * [Quick start](/guide/quick-start) — install your first plugin. * [Creating plugins](/guide/creating-plugins) — scaffold a new plugin from a template. --- --- url: /guide/quick-start.md description: Get a working AgentPlugin in five commands --- # Quick Start This walkthrough takes you from zero to a working plugin in five commands. Make sure you've [installed](/guide/installation) the CLI first. ## 1. Install the CLI Verify `agentplugins` is on your `PATH`: ```bash agentplugins --version # agentplugins 1.0.0 ``` ## 2. Add a plugin from GitHub Install any plugin hosted on GitHub with `agentplugins add`. The argument is `owner/repo`: ```bash agentplugins add user/my-plugin ``` ```text ✓ Cloned user/my-plugin → ~/.agents/plugins/my-plugin ✓ Detected manifest: agentplugins.config.ts ✓ Symlinked to: ~/.claude/skills/my-plugin ~/.codex/skills/my-plugin ~/.config/opencode/skills/my-plugin ~/.copilot/skills/my-plugin Installed my-plugin@1.2.0 to 4 agent(s). ``` ::: tip You can also pass a full URL (`https://github.com/user/my-plugin`), a local path, or a `gist:` reference. ::: ## 3. List installed plugins See everything in the universal store and which agents each one is linked into: ```bash agentplugins list ``` ```text Plugins in ~/.agents/plugins my-plugin 1.2.0 claude, codex, opencode, copilot security-guard 0.4.1 claude, codex, opencode, gemini, copilot format-on-save 2.0.0 claude, opencode 3 plugins installed. ``` ## 4. Scaffold a new plugin Bootstrap a plugin from a template with `agentplugins init`: ```bash agentplugins init ``` ```text ? Plugin name (kebab-case) › my-awesome-plugin ? Description › Does awesome things across every agent ? Template › - Use arrow-keys. Return to submit. > minimal Bare manifest + SKILL.md logger Logs every hook event security-guard preToolUse block-list formatter postToolUse auto-format ? Targets › claude, codex, copilot, gemini, kimi, opencode, pimono ✓ Created my-awesome-plugin/ my-awesome-plugin/agentplugins.config.ts my-awesome-plugin/SKILL.md my-awesome-plugin/hooks/pre-tool-use.sh my-awesome-plugin/README.md ``` ## 5. Build for every target Compile the manifest into each platform's native format: ```bash cd my-awesome-plugin agentplugins build ``` ```text Building my-awesome-plugin@1.0.0 claude → dist/claude/.claude-plugin/plugin.json ✓ codex → dist/codex/.codex-plugin/plugin.json ✓ copilot → dist/copilot/plugin.json ✓ gemini → dist/gemini/gemini-extension.json ✓ kimi → dist/kimi/kimi.plugin.json ✓ opencode → dist/opencode/plugin.ts + opencode.json ✓ pimono → dist/pimono/index.ts + package.json ✓ Built 7 targets in 142ms. ``` ## Next steps * Read the [manifest reference](/guide/manifest) for every field. * Walk through [creating plugins](/guide/creating-plugins) end-to-end. * Lint your plugin before publishing with [`agentplugins lint`](/guide/linting). --- --- url: /guide/manifest.md description: Define your plugin with the AgentPlugins universal manifest format --- # Manifest Every plugin is described by a single manifest file: `agentplugins.config.ts` at the plugin root. The manifest is the universal contract — adapters compile it into each platform's native format at build time. ```typescript import { definePlugin } from '@agentplugins/core' export default definePlugin({ // ...fields }) ``` Use `definePlugin` for editor autocomplete and compile-time validation. You can also author the manifest as static JSON (`agentplugins.json`) — both forms are supported. ::: tip Add `"$schema": "__DOCS_SITE__/schema/v1.json"` to JSON manifests for editor autocomplete in VS Code, JetBrains, and any JSON-Schema-aware editor. See the [JSON Schema reference](/reference/schema). ::: ## Required fields | Field | Type | Rule | |---|---|---| | `name` | `string` | Kebab-case (`^[a-z][a-z0-9-]*$`), max 64 chars. MUST NOT be prefixed with `agentplugin`. | | `version` | `string` | Semantic version ([semver](https://semver.org/)). | | `description` | `string` | Short human-readable description, minimum 10 characters. | ## Metadata fields | Field | Type | Notes | |---|---|---| | `displayName` | `string` | Human-readable name shown in UIs. | | `author` | `string \| { name, email?, url? }` | Author or organization. | | `homepage` | `string` (URL) | Landing page. | | `repository` | `string` (URL) | Source repository. | | `license` | `string` | SPDX identifier (e.g. `MIT`, `Apache-2.0`). | | `keywords` | `string[]` | Discovery tags. | ## Targets `targets` restricts which platforms the plugin compiles for. Omit it to target every supported platform. ```typescript targets: ['claude', 'codex', 'copilot', 'gemini', 'kimi', 'opencode', 'pimono'] ``` See the [adapters reference](/reference/adapters) for what each target emits. ## Hooks The `hooks` object maps universal lifecycle event names to handlers. There are **19 universal hook names** covering the entire agent lifecycle: | Category | Hooks | |---|---| | Session | `sessionStart`, `sessionEnd`, `setup` | | Prompt | `userPromptSubmit`, `userPromptExpansion` | | Tool | `preToolUse`, `postToolUse`, `postToolUseFailure` | | Permission | `permissionRequest`, `permissionDenied` | | Subagent | `subagentStart`, `subagentStop` | | Context | `preCompact`, `postCompact` | | Lifecycle | `stop`, `stopFailure`, `notification` | | File | `fileChanged`, `cwdChanged` | ```typescript hooks: { preToolUse: { matcher: 'bash', handler: { /* ... */ }, }, sessionStart: { handler: { /* ... */ }, }, } ``` See the [Hooks guide](/guide/hooks) for handler types, matchers, and worked examples. ## Skills An array of [SKILL.md](/guide/skills)-compatible skill definitions. Each skill is namespaced as `{plugin}:{skill}` when installed. ```typescript skills: [ { name: 'security-guard', description: 'Security policy enforcement', path: './skills/security-guard/SKILL.md', tags: ['security', 'safety'], }, ], ``` | Field | Type | Notes | |---|---|---| | `name` | `string` | Skill identifier. | | `description` | `string` | Short description shown to the agent. | | `path` | `string` | Relative path to the `SKILL.md` body (or use `content` inline). | | `tags` | `string[]` | Optional discovery tags. | ## MCP servers The `mcpServers` object declares [Model Context Protocol](https://modelcontextprotocol.io/) servers to start. Keys are server names. ```typescript mcpServers: { filesystem: { command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem', '${HOME}/projects'], env: { NODE_ENV: 'production', }, }, }, ``` | Field | Type | Notes | |---|---|---| | `command` | `string` | Executable to run. Required. | | `args` | `string[]` | Arguments passed to the command. | | `env` | `Record` | Environment variables. | See [MCP Servers](/guide/mcp-servers) for transport options and placeholders. ## Tools The `tools` array declares tools the agent can call. Parameters follow JSON Schema. ```typescript tools: [ { name: 'lookup-user', description: 'Look up a user by ID', parameters: { type: 'object', properties: { id: { type: 'string', description: 'User identifier' }, }, required: ['id'], }, }, ], ``` | Field | Type | Notes | |---|---|---| | `name` | `string` | Tool identifier. | | `description` | `string` | What the tool does. The agent reads this. | | `parameters` | JSON Schema | JSON Schema describing the tool input. | See [Tools](/guide/tools) for the full parameter schema. ## Commands The `commands` array declares slash commands the agent can invoke. Each command maps to a handler. ```typescript commands: [ { name: 'format', description: 'Format the current file', handler: { type: 'command', command: '${PLUGIN_ROOT}/scripts/format.sh', }, }, ], ``` | Field | Type | Notes | |---|---|---| | `name` | `string` | Command name (without leading `/`). | | `description` | `string` | Short help text. | | `handler` | `HookHandler` | Same handler types as hooks. | ## Agents The `agents` array declares subagents the plugin provides. Each subagent has its own prompt and tool allow-list. ```typescript agents: [ { name: 'reviewer', description: 'Reviews code changes', tools: ['read', 'diff'], }, ], ``` ## Rules The `rules` array declares behavioral rules — allow/deny/warn patterns applied to tool calls. ```typescript rules: [ { name: 'no-root-rm', description: 'Block recursive root deletion', pattern: 'rm\\s+-rf\\s+/', action: 'deny', }, ], ``` ## LSP servers The `lspServers` array declares Language Server Protocol servers to attach. ```typescript lspServers: [ { name: 'eslint', command: 'vscode-eslint-language-server', args: ['--stdio'], languages: ['javascript', 'typescript'], }, ], ``` ## Complete example ```typescript import { definePlugin } from '@agentplugins/core' export default definePlugin({ name: 'my-security-guard', version: '1.0.0', description: 'Blocks dangerous commands across all agents', displayName: 'Security Guard', author: { name: 'Jane Doe', url: 'https://janedoe.dev' }, homepage: 'https://github.com/user/my-security-guard', repository: 'https://github.com/user/my-security-guard', license: 'Apache-2.0', keywords: ['security', 'safety', 'guard'], targets: ['claude', 'codex', 'copilot', 'gemini', 'kimi', 'opencode', 'pimono'], hooks: { preToolUse: { matcher: 'bash', handler: { type: 'command', command: '${PLUGIN_ROOT}/hooks/pre-tool-use.sh', }, }, sessionStart: { handler: { type: 'command', command: '${PLUGIN_ROOT}/hooks/session-start.sh', }, }, userPromptSubmit: { handler: { type: 'reference', reference: 'my-security-guard:prompt-guard', }, }, }, skills: [ { name: 'security-guard', description: 'Security policy enforcement', path: './skills/security-guard/SKILL.md', tags: ['security', 'safety'], }, ], mcpServers: { vault: { command: '${PLUGIN_ROOT}/bin/vault-mcp', args: ['--stdio'], env: { VAULT_ADDR: 'https://vault.example.com' }, }, }, tools: [ { name: 'scan-secret', description: 'Scan a file for committed secrets', parameters: { type: 'object', properties: { path: { type: 'string', description: 'File to scan' }, }, required: ['path'], }, }, ], commands: [ { name: 'audit', description: 'Run a full security audit on the workspace', handler: { type: 'command', command: '${PLUGIN_ROOT}/scripts/audit.sh', }, }, ], agents: [ { name: 'auditor', description: 'Dedicated security auditor subagent', tools: ['read', 'scan-secret'], }, ], rules: [ { name: 'no-root-rm', pattern: 'rm\\s+-rf\\s+/', action: 'deny', }, ], }) ``` ## Next steps * [Hooks](/guide/hooks) — the 19 lifecycle events in depth. * [Creating plugins](/guide/creating-plugins) — scaffold and build a real plugin. * [JSON Schema](/reference/schema) — validate manifests programmatically. --- --- url: /guide/hooks.md description: Intercept agent lifecycle events with universal hooks --- # Hooks Hooks are the core extensibility primitive. A hook is a named lifecycle event that fires at a specific point in the agent's turn, paired with a handler that runs when the event fires. AgentPlugins defines **19 universal hooks** that compile down to each platform's native equivalent. ## The 19 universal hooks | Category | Hook | Fires when | |---|---|---| | Session | `sessionStart` | A new agent session begins. | | Session | `sessionEnd` | A session ends. | | Prompt | `userPromptSubmit` | The user submits a prompt. | | Prompt | `userPromptExpansion` | A prompt is expanded before sending. | | Tool | `preToolUse` | Before any tool call (filtered by `matcher`). | | Tool | `postToolUse` | After a tool call returns successfully. | | Tool | `postToolUseFailure` | After a tool call throws or exits non-zero. | | Permission | `permissionRequest` | The agent requests permission for an action. | | Permission | `permissionDenied` | A permission request is denied. | | Subagent | `subagentStart` | A subagent is spawned. | | Subagent | `subagentStop` | A subagent finishes. | | Context | `preCompact` | Before context compaction runs. | | Context | `postCompact` | After context compaction completes. | | Lifecycle | `stop` | The agent stops generating. | | Lifecycle | `stopFailure` | The agent's stop handler errors. | | Lifecycle | `notification` | The agent emits a notification. | | File | `fileChanged` | A file on disk changes. | | File | `cwdChanged` | The working directory changes. | | Lifecycle | `setup` | Plugin setup/installation hook. | ::: warning Not every platform implements every hook. At build time the adapter reports which hooks are unsupported for each target — those hooks are silently ignored on that platform. See the [adapters reference](/reference/adapters) for the coverage matrix. ::: ## Hook shape Each hook is an object with an optional `matcher` and a required `handler`: ```typescript { matcher?: string handler: HookHandler } ``` ## Handler types There are three handler types. Every platform supports at least one; the build step auto-wraps where needed. ### 1. `command` — shell command Runs a shell command. Supported by every platform. Paths must be `./`-prefixed or use placeholders. ```typescript hooks: { preToolUse: { matcher: 'bash', handler: { type: 'command', command: '${PLUGIN_ROOT}/hooks/pre-tool-use.sh', statusMessage: 'Scanning command...', }, }, } ``` | Field | Type | Notes | |---|---|---| | `type` | `'command'` | Always the literal `command`. | | `command` | `string` | Shell command to run. Supports `${PLUGIN_ROOT}`, `${PLUGIN_DATA}`, `${HOME}` placeholders. | | `statusMessage` | `string` | Optional message shown to the user while the hook runs. | | `shell` | `'bash' \| 'powershell' \| 'cmd'` | Override the default shell. | The hook receives context on stdin as JSON. Exit code `0` allows the action, exit code `2` blocks it. ### 2. `http` — POST endpoint POSTs the hook context to a URL. Supported by Claude and Copilot. ```typescript hooks: { preToolUse: { matcher: 'bash', handler: { type: 'http', url: 'https://hooks.example.com/pre-tool-use', headers: { Authorization: 'Bearer ${PLUGIN_DATA}/token', }, }, }, } ``` | Field | Type | Notes | |---|---|---| | `type` | `'http'` | Always the literal `http`. | | `url` | `string` (URL) | Endpoint to POST to. | | `headers` | `Record` | Optional HTTP headers. | The response body determines allow/block semantics, mirroring the command handler. ### 3. `reference` / `inline` — TypeScript function References a named handler in the plugin's handler module, or inlines a TypeScript function directly. Natively supported by OpenCode and Pi Mono; auto-wrapped as a shell command for other platforms. ```typescript hooks: { preToolUse: { matcher: 'bash', handler: { type: 'inline', handler: async (ctx) => { const input = JSON.stringify(ctx.toolInput) if (input.includes('rm -rf /')) { return { block: true, reason: 'Root deletion blocked' } } }, }, }, } ``` Or by namespaced reference: ```typescript hooks: { preToolUse: { matcher: 'bash', handler: { type: 'reference', reference: 'my-plugin:guard', }, }, } ``` | Field | Type | Notes | |---|---|---| | `type` | `'reference' \| 'inline'` | Handler kind. | | `reference` | `string` | Namespaced as `{plugin}:{component}`. Required for `reference`. | | `handler` | `(ctx) => Promise` | Inline function. Required for `inline`. | ## Matchers The `matcher` field narrows when a hook fires. Without a matcher, the hook fires for every occurrence of the event. ```typescript hooks: { preToolUse: { matcher: 'bash', // fires only for the bash tool handler: { /* ... */ }, }, postToolUse: { matcher: 'edit|write', // regex-style alternation handler: { /* ... */ }, }, } ``` Matchers are matched against the tool name for tool-related hooks and against the command string for command hooks. ## Worked example: security guard A plugin that blocks `rm -rf /` regardless of which agent runs it: ```typescript import { definePlugin } from '@agentplugins/core' export default definePlugin({ name: 'security-guard', version: '1.0.0', description: 'Blocks dangerous shell commands across all agents', targets: ['claude', 'codex', 'copilot', 'gemini', 'kimi', 'opencode', 'pimono'], hooks: { preToolUse: { matcher: 'bash', handler: { type: 'inline', handler: async (ctx) => { const cmd = JSON.stringify(ctx.toolInput) const dangerous = [/rm\s+-rf\s+\//, /:\(\)\s*\{\s*:\|/, /dd\s+if=\/dev\/zero/] for (const pattern of dangerous) { if (pattern.test(cmd)) { return { block: true, reason: `Blocked: command matched ${pattern}`, } } } }, }, }, permissionRequest: { handler: { type: 'command', command: '${PLUGIN_ROOT}/hooks/check-permission.sh', }, }, sessionStart: { handler: { type: 'inline', handler: async () => ({ additionalContext: 'Security guard plugin active. Dangerous commands will be blocked.', }), }, }, }, }) ``` ## Hook context Every handler receives a context object. The exact shape varies slightly per event, but the common fields are: ```typescript interface HookContext { sessionId: string cwd: string toolName?: string // for tool hooks toolInput?: unknown // for tool hooks prompt?: string // for prompt hooks agentName?: string // for subagent hooks error?: string // for onError } ``` ## Hook result Returning an object lets you influence the agent's behavior: ```typescript interface HookResult { block?: boolean // stop the action reason?: string // explanation (shown to the agent) additionalContext?: string// inject context into the turn modifiedInput?: unknown // rewrite the tool input } ``` ## Next steps * Read the [manifest reference](/guide/manifest) for the full hook schema. * Learn [creating plugins](/guide/creating-plugins) end-to-end. * See the [adapters reference](/reference/adapters) for per-platform hook coverage. --- --- url: /guide/skills.md description: Reusable agent capabilities via SKILL.md --- # Skills A **skill** is a markdown document with YAML frontmatter that teaches an agent a reusable capability. Existing `SKILL.md` files work without modification. ## SKILL.md format A skill is a markdown file named `SKILL.md` with a YAML frontmatter block: ```markdown --- name: security-guard description: Enforces security policies when executing shell commands. tags: - security - safety --- # Security Guard When executing shell commands, validate against the following policies: 1. Never run `rm -rf /` or any recursive deletion of system paths. 2. Never pipe untrusted input into `eval` or `sh -c`. 3. Prompt for confirmation before any network egress to an unknown host. ``` ### Frontmatter fields | Field | Type | Required | Notes | |---|---|---|---| | `name` | `string` | yes | Skill identifier. Kebab-case. | | `description` | `string` | yes | One-line description shown to the agent. | | `tags` | `string[]` | no | Discovery tags. | The body of the file is free-form markdown. Agents read it as instructions when the skill is active. ## How `agentplugins add` reads skills When you run `agentplugins add user/my-plugin`, the CLI looks for skills in this order: 1. A declared manifest (`agentplugins.config.ts` or `agentplugins.json`) with a `skills` array. 2. A `SKILL.md` file at the plugin root. 3. A `skills/` directory containing nested `SKILL.md` files. If only a `SKILL.md` is present, AgentPlugins **synthesizes a manifest** from the frontmatter: * `name` ← `SKILL.md` frontmatter `name` (or the directory name). * `version` ← `0.1.0` if no `package.json` is present. * `description` ← `SKILL.md` frontmatter `description`. * `skills[0]` ← the `SKILL.md` itself. This means a bare `SKILL.md` repo is a valid AgentPlugins plugin — no manifest authoring required. ```bash agentplugins add user/cool-skill ``` ```text ✓ No manifest found — synthesizing from SKILL.md ✓ cool-skill@0.1.0 (description: "Cool skill does cool things") ✓ Symlinked to 4 detected agents. ``` ## Symlink behavior Installed skills live in the universal store and are symlinked into each agent's skill path: ``` ~/.agents/plugins// └── skills/ └── / └── SKILL.md ~/.claude/skills/ → symlink ~/.codex/skills/ → symlink ~/.config/opencode/skills/ → symlink ... ``` ::: tip AgentPlugins also picks up skills placed in `~/.agents/skills/`, so existing setups require no migration. ::: ## Declaring skills in a manifest For plugins with multiple skills or non-default metadata, declare them explicitly in `agentplugins.config.ts`: ```typescript import { definePlugin } from '@agentplugins/core' export default definePlugin({ name: 'my-bundle', version: '1.0.0', description: 'A bundle of related skills', skills: [ { name: 'security-guard', description: 'Security policy enforcement', path: './skills/security-guard/SKILL.md', tags: ['security', 'safety'], }, { name: 'format-on-save', description: 'Auto-format files after edit', path: './skills/format-on-save/SKILL.md', tags: ['formatting'], }, ], }) ``` | Field | Type | Notes | |---|---|---| | `name` | `string` | Skill identifier. | | `description` | `string` | One-line description shown to the agent. | | `path` | `string` | Relative path to the `SKILL.md` body. | | `tags` | `string[]` | Optional discovery tags. | | `content` | `string` | Inline markdown body (alternative to `path`). | You can use `path` (preferred for multi-file plugins) or `content` (preferred for single-file plugins). ## Namespacing All skills are namespaced as `{plugin}:{skill}` when installed to avoid collisions across plugins. A skill named `guard` in plugin `my-bundle` is exposed as `my-bundle:guard`. ## Next steps * [Creating plugins](/guide/creating-plugins) — scaffold a plugin with skills. * [Manifest reference](/guide/manifest) — every manifest field. * [Agent paths](/reference/agent-paths) — where skills are symlinked per platform. --- --- url: /guide/mcp-servers.md description: 'Configure MCP (Model Context Protocol) servers once, deploy to all harnesses' --- # MCP Servers [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) servers extend an agent with external tools and data sources. Declare them once in the manifest and AgentPlugins wires them into every supported harness. ## Declaration MCP servers are keyed by name under `mcpServers`: ```typescript import { definePlugin } from '@agentplugins/core' export default definePlugin({ name: 'my-plugin', version: '1.0.0', description: 'Wires MCP servers into every agent', mcpServers: { filesystem: { command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem', '${HOME}/projects'], env: { NODE_ENV: 'production', }, }, github: { command: 'npx', args: ['-y', '@modelcontextprotocol/server-github'], env: { GITHUB_TOKEN: '${PLUGIN_DATA}/github-token', }, }, }, }) ``` ## Fields | Field | Type | Required | Notes | |---|---|---|---| | `command` | `string` | yes | Executable to run. | | `args` | `string[]` | no | Arguments passed to the command. | | `env` | `Record` | no | Environment variables. | | `transport` | `'stdio' \| 'http'` | no | Transport mode. Defaults to `stdio`. | ## Placeholders Commands, arguments, and environment values support placeholder expansion: | Placeholder | Resolves to | |---|---| | `${PLUGIN_ROOT}` | The plugin's directory in the universal store. | | `${PLUGIN_DATA}` | Per-plugin data directory (`~/.agents/plugins//data`). | | `${HOME}` | User home directory. | Use `${PLUGIN_DATA}` for secrets and per-install state — that directory is never overwritten on plugin update. ::: warning Never hard-code secrets into the manifest. Commit a reference to `${PLUGIN_DATA}` and have a `setup` hook write the actual value on first run. The [linting](/guide/linting) rule `secrets` catches common leak patterns. ::: ## Transport modes ### `stdio` (default) The agent spawns the MCP server as a subprocess and communicates over stdin/stdout. This is the universal default — every supported agent implements it. ```typescript mcpServers: { myServer: { command: '${PLUGIN_ROOT}/bin/my-server', args: ['--stdio'], transport: 'stdio', }, } ``` ### `http` The agent connects to a long-running MCP server over HTTP. Supported by Claude and Copilot; other platforms fall back to `stdio`. ```typescript mcpServers: { remote: { command: '${PLUGIN_ROOT}/bin/my-server', args: ['--port', '3000'], transport: 'http', }, } ``` ## Per-platform behavior | Platform | stdio | http | Notes | |---|---|---|---| | Claude | ✓ | ✓ | Native MCP support. | | Codex | ✓ | — | stdio only. | | Copilot | ✓ | ✓ | Native MCP support. | | Gemini | ✓ | — | stdio only. | | Kimi | ✓ | — | stdio only. | | OpenCode | ✓ | ✓ | Native MCP support. | | Pi Mono | ✓ | — | stdio only. | Unsupported transports are dropped at build time with a warning. ## Next steps * [Manifest reference](/guide/manifest) for the full `mcpServers` schema. * [Linting](/guide/linting) to catch leaked secrets before publish. * [Adapters reference](/reference/adapters) for per-platform MCP support. --- --- url: /guide/tools.md description: Define callable tools that agents can invoke --- # Tools Tools are callable functions the agent can invoke during a turn. Declare them once in the manifest; AgentPlugins compiles them into each platform's native tool format. ## Declaration The `tools` array lists tool definitions. Each tool has a name, a description the agent reads, and a parameters schema. ```typescript import { definePlugin } from '@agentplugins/core' export default definePlugin({ name: 'my-tools', version: '1.0.0', description: 'A bundle of agent-callable tools', tools: [ { name: 'lookup-user', description: 'Look up a user by ID. Returns name and email.', parameters: { type: 'object', properties: { id: { type: 'string', description: 'The user identifier', }, verbose: { type: 'boolean', description: 'Include extended profile fields', }, }, required: ['id'], }, }, { name: 'create-issue', description: 'Create a GitHub issue in the current repo', parameters: { type: 'object', properties: { title: { type: 'string', description: 'Issue title' }, body: { type: 'string', description: 'Issue body (markdown)' }, labels: { type: 'array', items: { type: 'string' }, description: 'Labels to apply', }, }, required: ['title'], }, }, ], }) ``` ## Fields | Field | Type | Required | Notes | |---|---|---|---| | `name` | `string` | yes | Tool identifier. Namespaced as `{plugin}:{tool}` when installed. | | `description` | `string` | yes | What the tool does. The agent reads this to decide when to call the tool. | | `parameters` | JSON Schema | yes | JSON Schema describing the tool input. | ## Parameters schema `parameters` is a standard [JSON Schema](https://json-schema.org/) object describing the tool's input. The required fields are `type` and `properties`: ```typescript parameters: { type: 'object', properties: { // ...property definitions }, required: ['fieldName'], } ``` ### Property types | Type | Example | |---|---| | `string` | `{ type: 'string', description: 'A name' }` | | `number` | `{ type: 'number', description: 'An age' }` | | `boolean` | `{ type: 'boolean', description: 'Is active' }` | | `array` | `{ type: 'array', items: { type: 'string' } }` | | `object` | `{ type: 'object', properties: { /* nested */ } }` | ### Enums Constrain a property to a fixed set of values: ```typescript role: { type: 'string', enum: ['admin', 'member', 'guest'], description: 'User role', } ``` ### Nested objects ```typescript parameters: { type: 'object', properties: { filter: { type: 'object', properties: { field: { type: 'string' }, op: { type: 'string', enum: ['eq', 'ne', 'gt', 'lt'] }, value: { type: 'string' }, }, required: ['field', 'op'], }, }, required: ['filter'], } ``` ## Writing descriptions The agent uses `description` fields to decide when and how to call a tool. Treat them as documentation for a smart but literal reader: * Be specific about what the tool does: `"Look up a user by ID"` beats `"User lookup"`. * Document side effects: `"Deletes the file permanently"` is better than `"Removes a file"`. * Mention units and formats: `"ISO 8601 timestamp"`, `"size in bytes"`. ## Tool handlers The manifest declares the **shape** of a tool. The implementation — what runs when the agent invokes the tool — is provided by an MCP server or an inline handler referenced from the plugin module. See [MCP servers](/guide/mcp-servers) and [Hooks](/guide/hooks) for handler details. ## Per-platform behavior `tools[]` is natively emitted by **OpenCode** and **Pi Mono** only. For all other platforms — including the Tier-1 harnesses Claude Code and Codex — **`mcpServers` is the recommended universal tool delivery mechanism**. | Platform | `tools[]` | `mcpServers` | Notes | |---|:---:|:---:|---| | Claude Code | ⚠️ | ✅ | `tools[]` not emitted; use `mcpServers` — full Tier-1 tool parity | | Codex | ⚠️ | ✅ | Same as Claude Code | | OpenCode | ✅ | ✅ | First-class `tools[]` + `mcpServers` both supported | | Pi Mono | ✅ | ✅ | First-class `tools[]` + `mcpServers` both supported | | Copilot | ⚠️ | ❌ | Neither emitted; Tier-2 only | | Gemini | ⚠️ | ❌ | Neither emitted; Tier-2 only | | Kimi | ⚠️ | ❌ | Neither emitted; Tier-2 only | > ⚠️ When `tools[]` is declared and the target platform does not natively emit it, `agentplugins validate` emits a **WARNING** (not an error) with a pointer to `mcpServers`. The build still succeeds. ### Recommended cross-harness pattern For plugins targeting all Tier-1 harnesses, back tools with an MCP server: ```typescript export default definePlugin({ name: 'my-tools', version: '1.0.0', // Tier-1 universal tool path: all four harnesses consume MCP servers mcpServers: { 'my-tools-server': { command: 'npx', args: ['my-tools-mcp-server'], }, }, // Optional: declare tool shapes for OpenCode/Pi Mono native emission tools: [ { name: 'lookup-user', description: 'Look up a user by ID. Returns name and email.', parameters: { type: 'object', properties: { id: { type: 'string', description: 'The user identifier' }, }, required: ['id'], }, }, ], }) ``` See the [Tier-1 Capability Matrix](/reference/compat-matrix) for full cross-harness tool support details. ## Next steps * [MCP Servers](/guide/mcp-servers) — recommended universal tool mechanism for all Tier-1 harnesses. * [Manifest reference](/guide/manifest) for the full `tools` schema. * [Tier-1 Capability Matrix](/reference/compat-matrix) for cross-harness support details. --- --- url: /guide/creating-plugins.md description: Build and publish a universal AgentPlugin --- # Creating Plugins This guide walks through creating a plugin from scratch: scaffolding, writing hooks, adding skills and MCP servers, building, testing, and publishing. ## 1. Scaffold Run `agentplugins init` to bootstrap a plugin from a template: ```bash agentplugins init ``` You'll be prompted for: * **Plugin name** — kebab-case identifier. * **Description** — minimum 10 characters. * **Template** — one of the starter templates. * **Targets** — which platforms to compile for (default: all). ### Templates | Template | What you get | |---|---| | `minimal` | Bare manifest + `SKILL.md`. Good starting point. | | `logger` | A plugin that logs every hook event to `${PLUGIN_DATA}/log.jsonl`. | | `security-guard` | A `preToolUse` block-list for dangerous commands. | | `formatter` | A `postToolUse` hook that runs your formatter of choice. | Pick `minimal` if you're not sure — you can add hooks later. ```text ? Plugin name (kebab-case) › my-plugin ? Description › Does awesome things across every agent ? Template › minimal ? Targets › claude, codex, copilot, gemini, kimi, opencode, pimono ✓ Created my-plugin/ my-plugin/agentplugins.config.ts my-plugin/SKILL.md my-plugin/hooks/ my-plugin/README.md ``` ## 2. Write hooks Open `agentplugins.config.ts` and add hooks to the `hooks` object. See the [Hooks guide](/guide/hooks) for the 19 universal hooks and the three handler types. ```typescript import { definePlugin } from '@agentplugins/core' export default definePlugin({ name: 'my-plugin', version: '1.0.0', description: 'Does awesome things across every agent', hooks: { preToolUse: { matcher: 'bash', handler: { type: 'command', command: '${PLUGIN_ROOT}/hooks/pre-tool-use.sh', }, }, sessionStart: { handler: { type: 'inline', handler: async () => ({ additionalContext: 'my-plugin is active.', }), }, }, }, }) ``` ::: tip Place hook scripts under `hooks/` and reference them with `${PLUGIN_ROOT}/hooks/...`. The placeholder resolves to the plugin's directory in the universal store at runtime. ::: ## 3. Add skills Drop a `SKILL.md` file into `skills//`: ```bash mkdir -p skills/my-skill $EDITOR skills/my-skill/SKILL.md ``` ```markdown --- name: my-skill description: Teaches the agent how to do something specific. tags: - productivity --- # My Skill When the user asks to do X, follow these steps: 1. ... 2. ... ``` Then declare it in the manifest: ```typescript skills: [ { name: 'my-skill', description: 'Teaches the agent how to do something specific.', path: './skills/my-skill/SKILL.md', tags: ['productivity'], }, ], ``` See the [Skills guide](/guide/skills) for the full `SKILL.md` spec. ## 4. Add MCP servers Wire external tools and data sources into the agent with [MCP servers](/guide/mcp-servers): ```typescript mcpServers: { filesystem: { command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem', '${HOME}/projects'], }, }, ``` ## 5. Build Compile the manifest into each target platform's native format: ```bash agentplugins build ``` ```text Building my-plugin@1.0.0 claude → dist/claude/.claude-plugin/plugin.json ✓ codex → dist/codex/.codex-plugin/plugin.json ✓ copilot → dist/copilot/plugin.json ✓ gemini → dist/gemini/gemini-extension.json ✓ kimi → dist/kimi/kimi.plugin.json ✓ opencode → dist/opencode/plugin.ts + opencode.json ✓ pimono → dist/pimono/index.ts + package.json ✓ Built 7 targets in 142ms. ``` Inspect `dist/` to see exactly what each adapter emitted. ## 6. Test locally Install the plugin from your local working copy to verify the symlinks land in the right place: ```bash agentplugins add ./my-plugin ``` ```text ✓ Installed my-plugin@1.0.0 from local path ✓ Symlinked to 4 detected agents. ``` Then run `list` to confirm: ```bash agentplugins list ``` Validate the manifest and surface cross-platform issues: ```bash agentplugins validate ``` And run the linter to catch common mistakes: ```bash agentplugins lint ``` See [Linting](/guide/linting) for the full rule set. ## 7. Publish Push the plugin to a public GitHub repository. Once it's on GitHub, anyone can install it: ```bash agentplugins add your-username/my-plugin ``` ### Recommended repository layout ```text my-plugin/ ├── agentplugins.config.ts # manifest (or agentplugins.json) ├── SKILL.md # optional root skill ├── skills/ │ └── my-skill/ │ └── SKILL.md ├── hooks/ │ ├── pre-tool-use.sh │ └── session-start.sh ├── bin/ │ └── my-server # MCP server binary (if any) ├── README.md └── LICENSE ``` ::: tip Tag releases with semver (`v1.0.0`, `v1.1.0`, ...). `agentplugins update` resolves to the latest tag, and users can pin a specific version with `agentplugins add user/repo@1.0.0`. ::: ## Next steps * [Manifest reference](/guide/manifest) — every field. * [Hooks](/guide/hooks) — the 19 lifecycle events. * [CLI reference](/reference/commands) — every command and flag. --- --- url: /guide/linting.md description: Validate your plugin manifest before publishing --- # Linting `agentplugins lint` checks your manifest against eight rules that catch the most common publishing mistakes. Run it before every release. ```bash agentplugins lint ``` ```text Linting my-plugin@1.0.0 naming ✓ name is kebab-case versioning ✓ version is valid semver description ✓ description is descriptive (42 chars) license ✓ license declared: Apache-2.0 target-hygiene ✓ 7 targets, all recognized hook-coverage ⚠ 2 hooks unsupported on kimi (sessionEnd, userPromptSubmit) handler-safety ✓ all command handlers use ./-prefixed paths secrets ✓ no plaintext secrets detected 7 passed, 1 warning. ``` ## Rules ### `naming` Checks that `name` is kebab-case (`^[a-z][a-z0-9-]*$`), max 64 chars, and not prefixed with `agentplugin`. ```text ✗ naming: name "AgentPlugin" must be kebab-case ✗ naming: name "agentplugin-foo" must not be prefixed with "agentplugin" ``` ### `versioning` Checks that `version` is valid [semver](https://semver.org/). ```text ✗ versioning: version "1.0" is not valid semver (need MAJOR.MINOR.PATCH) ``` ### `description` Checks that `description` is at least 10 characters and not a placeholder. ```text ✗ description: description "todo" is too short (min 10 chars) ✗ description: description matches placeholder pattern ("my plugin") ``` ### `license` Checks that `license` is declared and matches a known [SPDX identifier](https://spdx.org/licenses/). ```text ✗ license: license field is missing ✗ license: "MIT2" is not a recognized SPDX identifier ``` ### `target-hygiene` Checks that every entry in `targets` is a recognized platform and warns on duplicates or empty arrays. ```text ✗ target-hygiene: unknown target "claude2" ⚠ target-hygiene: target list contains duplicates ``` ### `hook-coverage` Warns when a hook you've declared isn't supported by one of your targets. The hook will be silently ignored on that platform. ```text ⚠ hook-coverage: hooks.sessionEnd is not supported by codex — will be ignored ⚠ hook-coverage: hooks.userPromptSubmit is not supported by kimi — will be ignored ``` See the [adapters reference](/reference/adapters) for the per-platform coverage matrix. ### `handler-safety` Checks that every `command` handler uses `./`-prefixed paths or placeholders, and rejects path traversal (`..`). ```text ✗ handler-safety: handler command uses absolute path "/usr/local/bin/foo" ✗ handler-safety: handler command contains ".." traversal ``` ### `secrets` Scans the manifest for common secret patterns (API keys, tokens, private keys) and flags them. Use `${PLUGIN_DATA}` placeholders instead. ```text ✗ secrets: possible AWS access key in mcpServers.github.env.GITHUB_TOKEN ✗ secrets: possible private key in hooks.preToolUse.handler.command ``` ## JSON output Pass `--json` to get machine-readable output for CI: ```bash agentplugins lint --json ``` ```json { "plugin": "my-plugin", "version": "1.0.0", "rules": { "naming": { "status": "pass" }, "versioning": { "status": "pass" }, "description": { "status": "pass" }, "license": { "status": "pass" }, "target-hygiene": { "status": "pass" }, "hook-coverage": { "status": "warn", "warnings": [ "hooks.sessionEnd is not supported by codex — will be ignored", "hooks.userPromptSubmit is not supported by kimi — will be ignored" ] }, "handler-safety": { "status": "pass" }, "secrets": { "status": "pass" } }, "summary": { "passed": 7, "warnings": 1, "failed": 0 } } ``` ## Exit codes | Code | Meaning | |---|---| | `0` | All rules passed (warnings allowed). | | `1` | At least one rule failed. | | `2` | Manifest could not be loaded or parsed. | ## CI integration A typical CI step: ```yaml - name: Lint plugin run: agentplugins lint ``` Fail the build on any error. Treat `hook-coverage` warnings as informational unless the hook is critical to your plugin's behavior — in which case, narrow `targets` to the platforms that support it. ## Next steps * [Creating plugins](/guide/creating-plugins) — the full authoring workflow. * [CLI reference](/reference/commands) — every command and flag. * [Adapters reference](/reference/adapters) — why some hooks are unsupported per target. --- --- url: /guide/ecosystem.md description: >- Community plugins built on the AgentPlugins universal manifest, installable across all Tier-1 harnesses. --- # Ecosystem Community plugins rewritten on the AgentPlugins universal manifest. Each targets **Tier-1 functional parity** — the same functionality across Claude Code, Codex, OpenCode, and Pi Mono. Install any plugin with a single command: ```bash agentplugins add owner/repo ``` This installs the plugin into `~/.agents/plugins//` and symlinks it to every detected agent on your machine. *** ## Available plugins ### caveman Why use many token when few do trick. Ultra-compressed communication mode — cuts ~75% of output tokens while preserving full technical accuracy. Includes the cavecrew of subagent presets for compressed delegation. **Functionality:** skills + `agents[]` cavecrew (investigator / builder / reviewer) + lifecycle hooks\ **Replaces:** the 850-line custom caveman installer — `agentplugins add` handles detection and symlinking across all tier-1 harnesses ```bash agentplugins add JuliusBrussee/caveman ``` | Tier-1 | Claude Code | Codex | OpenCode | Pi Mono | |---|:---:|:---:|:---:|:---:| | Skills (caveman + 6 companions) | ✅ | ✅ | ✅ | ✅ | | Cavecrew agents\[] | ✅ | ✅ | ✅ | ✅ | | sessionStart hook | ✅ | ✅ | ✅ | ✅ | > **Note:** The `caveman-shrink` MCP proxy utility is not declared in `mcpServers` — it wraps an existing server rather than acting as a standalone one. See `src/mcp-servers/caveman-shrink/README.md` for manual setup. *** ### ponytail He says nothing. He writes one line. It works. Lazy senior dev mode — forces the minimum solution that works: YAGNI → stdlib → native → one line. **Functionality:** skills + lifecycle hooks + 6 slash commands + `subagentStart`\ **Replaces:** ponytail's per-harness install scripts; promptfoo benchmark preserved and unchanged ```bash agentplugins add DietrichGebert/ponytail ``` | Tier-1 | Claude Code | Codex | OpenCode | Pi Mono | |---|:---:|:---:|:---:|:---:| | Skills (ponytail + 5 sub-skills) | ✅ | ✅ | ✅ | ✅ | | sessionStart + userPromptSubmit | ✅ | ✅ | ⚠️ | ✅ | | subagentStart | ✅ | ✅ | ⚠️ | ✅ | | Commands (/ponytail + 5 variants) | ✅ | ✅ | ✅ | ✅ | ⚠️ OpenCode: `subagentStart` and `userPromptSubmit` have no native equivalent — these hooks are ignored with a WARN. See [compat matrix](/reference/compat-matrix). *** ## Coming in v0.4.0 | Plugin | Description | Primitive | |---|---|---| | agentplugins-goal | Autonomous goal-loop across all Tier-1 | `continueWith` (4.1) | | agentplugins-btw | Parallel side-thread thoughts | native passthrough (4.2) | | agentplugins-flow | Subagent orchestration via provider protocol | `spawnChild` (4.3) | *** ## Adding your plugin 1. Build on the AgentPlugins universal manifest (see [Creating Plugins](/guide/creating-plugins)) 2. Target all four Tier-1 harnesses and confirm functional parity 3. Open a PR or issue to add your plugin to this page See [Rewriting for Tier-1 Parity](/guide/porting) for the step-by-step process. --- --- url: /guide/porting.md description: >- How to port an existing plugin to the AgentPlugins universal manifest with functional parity across all Tier-1 harnesses. --- # Rewriting for Tier-1 Parity This guide walks through porting an existing per-harness plugin to AgentPlugins so it delivers the **same functionality** across all four Tier-1 harnesses: Claude Code, Codex, OpenCode, and Pi Mono. > **Rule:** A capability must work across all Tier-1 harnesses at the functionality level — not necessarily with identical TUI chrome, but the same underlying behaviour. *** ## The decision tree For each capability in your existing plugin, work through this tree: ``` 1. Does universal codegen cover it across all Tier-1? YES → declare it in the manifest; the adapter handles the rest NO → continue ↓ 2. Can all Tier-1 harnesses support it via a per-harness escape hatch? YES → implement guided per-harness (see below); emit WARN on build NO → continue ↓ 3. Is the gap TUI-grade fidelity only (overlays, widgets)? YES → acceptable degradation; note in compat matrix; ship it NO → open a primitive proposal (v0.4.0+ scope; don't block the rewrite) ``` *** ## Step 1 — Audit your existing plugin List every capability your plugin provides: | Capability | What it does | Hook / mechanism | |---|---|---| | Session banner | Prints a welcome message | `sessionStart` | | Tool guard | Blocks dangerous commands | `preToolUse` | | Custom command | `/reset` clears state | `commands[]` | | Overlay UI | Side panel in Pi TUI | Pi-specific | For each row, check the [Tier-1 Capability Matrix](/reference/compat-matrix) to see whether it's universal-codegen, guided-per-harness, or unsupported. *** ## Step 2 — Universal codegen (most capabilities) Capabilities expressed through hooks, skills, commands, and mcpServers map directly to the manifest. This covers the majority of real-world plugins: ```typescript import { definePlugin } from '@agentplugins/core' export default definePlugin({ name: 'my-plugin', version: '1.0.0', description: 'My cross-harness plugin', hooks: { sessionStart: { handler: { type: 'command', command: './hooks/session-start.sh' }, }, preToolUse: { handler: { type: 'command', command: './hooks/tool-guard.sh' }, }, }, commands: [ { name: 'reset', description: 'Reset plugin state', command: './scripts/reset.sh' }, ], // Tools via MCP — works on all Tier-1 mcpServers: { 'my-tools': { command: 'npx', args: ['my-tools-mcp-server'] }, }, }) ``` Build and verify on each Tier-1: ```bash agentplugins build agentplugins validate ``` *** ## Step 3 — Guided per-harness (escape hatch) Some capabilities have no universal primitive but can be implemented per-harness. The build succeeds with a WARN; you implement the harness-specific path yourself. **Example: subagentStart/Stop on OpenCode** OpenCode has no native subagent lifecycle event. Instead, intercept the subagent tool call via `preToolUse`/`postToolUse` and filter on tool name: ```typescript hooks: { preToolUse: { handler: { type: 'command', command: './hooks/tool-intercept.sh', // tool-intercept.sh checks $TOOL_NAME == "subagent" and acts accordingly }, }, }, ``` The WARN emitted on `agentplugins validate` for OpenCode points here. Record the gap in the [compat matrix](/reference/compat-matrix). **Per-harness fallback pattern (v0.4.0+):** Once `nativeEntry` lands (4.2), you can provide a hand-written TS file for code-emitting adapters: ```typescript // agentplugins.config.ts (v0.4.0+) nativeEntry: { pimono: './src/pimono-native.ts', opencode: './src/opencode-native.ts', } ``` This file is copied verbatim into the dist and has full access to the harness's own SDK. *** ## Step 4 — TUI-only features (acceptable degradation) Overlays, side panels, and interactive widgets that use Pi's TUI system (`@earendil-works/pi-tui`) are Pi-only by nature. This is the **one allowed degradation** category: * Implement the TUI feature on Pi via `nativeEntry` (v0.4.0+) * On other Tier-1: omit the widget; the underlying functionality (data, hooks, state) must still work * Record in compat matrix as "TUI fidelity — Pi only" *** ## Step 5 — Verify parity For each Tier-1 harness: ```bash # Build agentplugins build --target claude agentplugins build --target codex agentplugins build --target opencode agentplugins build --target pimono # Validate agentplugins validate # Install and smoke-test locally agentplugins add ./ # installs from local path ``` Confirm the **same observable behaviour** on all four: same hooks fire, same commands work, same tools are reachable. *** ## What stays out * **Universal orchestration runtime** — don't build one. Subagent spawning = per-harness primitives (v0.4.0) + userland provider protocol. * **Mechanical ports** — don't translate existing config files 1:1. Rewrite on the manifest; let adapters generate the platform-native output. * **v0.4.0 primitives** — `continueWith`, `nativeEntry`, `spawnChild` are not yet released. If your plugin needs them, note the gap and open a primitive proposal. *** ## See also * [Ecosystem](/guide/ecosystem) — plugins already rewritten for Tier-1 parity * [Tier-1 Capability Matrix](/reference/compat-matrix) — full capability table * [Creating Plugins](/guide/creating-plugins) — authoring guide from scratch --- --- url: /reference/commands.md --- # CLI Commands The `agentplugins` CLI exposes 11 commands. Run `agentplugins --help` to see them all, or `agentplugins --help` for details on a specific command. ```text Usage: agentplugins [options] [command] Write AI agent plugins once, ship to any harness. Options: -V, --version output the version number -h, --help display help for command Commands: add Install a plugin from a source remove Remove an installed plugin list List installed plugins update Update one or all plugins info Show details about an installed plugin doctor Diagnose the local install build Compile a plugin to target platforms validate Validate a manifest against the schema init Scaffold a new plugin lint Lint a manifest against the rule set preview Preview the compiled output for a target ``` ## `add` Install a plugin from a source and symlink it into every detected agent. ```bash agentplugins add ``` | Flag | Description | |---|---| | `--target ` | Restrict fan-out to a specific agent (repeatable). | | `--force` | Overwrite an existing install. | | `--no-symlink` | Copy files instead of symlinking. | | `--version ` | Pin a specific version (GitHub sources only). | ### Sources | Source form | Example | |---|---| | `owner/repo` | `agentplugins add user/my-plugin` | | Full GitHub URL | `agentplugins add https://github.com/user/my-plugin` | | `owner/repo@version` | `agentplugins add user/my-plugin@1.2.0` | | Local path | `agentplugins add ./my-plugin` | | `gist:` | `agentplugins add gist:abcdef123456` | ### Examples ```bash # Latest release from GitHub agentplugins add user/my-plugin # Specific version agentplugins add user/my-plugin@1.2.0 # Local working copy agentplugins add ./my-plugin # Restrict to Claude only agentplugins add user/my-plugin --target claude ``` ## `remove` Remove a plugin from the universal store and delete its symlinks. ```bash agentplugins remove ``` | Flag | Description | |---|---| | `--target ` | Remove the symlink for one agent only. | | `--keep-data` | Preserve `${PLUGIN_DATA}` contents. | ### Examples ```bash agentplugins remove my-plugin agentplugins remove my-plugin --target codex ``` ## `list` List every plugin in the universal store and which agents each is linked into. ```bash agentplugins list ``` | Flag | Description | |---|---| | `--json` | Emit JSON instead of a table. | | `--target ` | Filter to plugins linked into a specific agent. | ### Example ```bash agentplugins list --json ``` ```json [ { "name": "my-plugin", "version": "1.2.0", "targets": ["claude", "codex", "opencode", "copilot"] } ] ``` ## `update` Update one plugin or all plugins to the latest version available from its source. ```bash agentplugins update [name] ``` | Flag | Description | |---|---| | `--all` | Update every installed plugin. | | `--version ` | Pin to a specific version. | | `--dry-run` | Show what would change without writing. | ### Examples ```bash agentplugins update my-plugin agentplugins update --all agentplugins update my-plugin --version 1.3.0 ``` ## `info` Show details about an installed plugin: manifest, declared hooks, skills, MCP servers, and current symlinks. ```bash agentplugins info ``` | Flag | Description | |---|---| | `--json` | Emit JSON instead of formatted text. | ### Example ```bash agentplugins info my-plugin ``` ```text my-plugin@1.2.0 Description: Does awesome things across every agent Author: Jane Doe License: Apache-2.0 Repository: https://github.com/user/my-plugin Hooks: preToolUse matcher=bash command sessionStart inline Skills: my-plugin:my-skill MCP servers: filesystem Symlinks: ~/.claude/skills/my-plugin ~/.codex/skills/my-plugin ~/.config/opencode/skills/my-plugin ~/.copilot/skills/my-plugin ``` ## `doctor` Diagnose the local install: verify the binary, the universal store, the skills compatibility directory, and detected agents. ```bash agentplugins doctor ``` | Flag | Description | |---|---| | `--json` | Emit JSON instead of formatted text. | See the sample output in [Installation](/guide/installation#verify-the-install). ## `build` Compile a plugin manifest into each target platform's native format. ```bash agentplugins build [path] ``` | Flag | Description | |---|---| | `--target ` | Build only one target (repeatable). | | `--out ` | Output directory. Defaults to `dist/`. | | `--watch` | Rebuild on file change. | ### Examples ```bash agentplugins build agentplugins build --target claude --target codex agentplugins build --out build/ ``` ## `validate` Validate a manifest against the JSON Schema and report per-target compatibility issues. ```bash agentplugins validate [path] ``` | Flag | Description | |---|---| | `--target ` | Validate against a specific target's constraints. | | `--json` | Emit JSON instead of formatted text. | ### Example ```bash agentplugins validate ``` ```text 🔍 AgentPlugins Validation claude: ✓ No issues found codex: ⚠ hooks.sessionEnd is not supported by codex — this hook will be ignored ✅ All checks passed! ``` ## `init` Scaffold a new plugin from a template. ```bash agentplugins init [name] ``` | Flag | Description | |---|---| | `--template ` | Template to use: `minimal`, `logger`, `security-guard`, `formatter`. | | `--target ` | Declare a compile target (repeatable). Defaults to all. | | `--no-skill` | Skip creating a root `SKILL.md`. | ### Examples ```bash agentplugins init my-plugin --template security-guard agentplugins init my-plugin --target claude --target opencode ``` ## `lint` Lint a manifest against the eight built-in rules. See the [Linting guide](/guide/linting) for the full rule set. ```bash agentplugins lint [path] ``` | Flag | Description | |---|---| | `--json` | Emit JSON instead of formatted text. | | `--max-warnings ` | Fail when warning count exceeds `n`. | | `--rule ` | Run only one rule (repeatable). | ### Examples ```bash agentplugins lint agentplugins lint --json agentplugins lint --rule secrets --rule naming ``` ## `preview` Preview the compiled output for a specific target without writing files. Useful for debugging adapter behavior. ```bash agentplugins preview --target [path] ``` | Flag | Description | |---|---| | `--target ` | Target to preview (required). | | `--out ` | Write the preview to a file instead of stdout. | ### Example ```bash agentplugins preview --target opencode ``` ```typescript // opencode preview of my-plugin@1.0.0 import type { Plugin } from 'opencode' export default function (): Plugin { return { name: 'my-plugin', hooks: { 'tool.execute.before': async (ctx) => { if (ctx.tool.name === 'bash') { // ...preToolUse handler } }, }, } } ``` ## Global flags These work on every command: | Flag | Description | |---|---| | `-V, --version` | Print the CLI version and exit. | | `-h, --help` | Print help text and exit. | | `--no-color` | Disable colored output. | | `--store ` | Override the universal store path (defaults to `~/.agents/plugins`). | ## Next steps * [Quick start](/guide/quick-start) — a five-command walkthrough. * [Linting](/guide/linting) — the eight rules in depth. * [JSON Schema](/reference/schema) — programmatic validation. --- --- url: /reference/schema.md --- # JSON Schema The AgentPlugins manifest is defined by a published JSON Schema. Use it for editor autocomplete, programmatic validation, and CI checks. ## Locations | Source | URL / package | |---|---| | Hosted | `__DOCS_SITE__/schema/v1.json` | | Raw (GitHub) | `https://raw.githubusercontent.com/sigilco/agentplugins/main/spec/v1/manifest.schema.json` | | npm | `@agentplugins/schema` | | Agent paths schema | `__DOCS_SITE__/schema/v1/agent-paths.json` | ## Editor autocomplete Add `$schema` to any JSON manifest to get autocomplete, hover docs, and inline validation in VS Code, JetBrains, Zed, and any other JSON-Schema-aware editor: ```json { "$schema": "__DOCS_SITE__/schema/v1.json", "name": "my-plugin", "version": "1.0.0", "description": "Does awesome things across every agent" } ``` ## `@agentplugins/schema` package The npm package bundles the JSON Schema, generated TypeScript types, and a ready-to-use [Ajv](https://ajv.js.org/) validator. ```bash npm install @agentplugins/schema ``` ### Validate a manifest ```typescript import { validateManifest } from '@agentplugins/schema' const { valid, errors } = validateManifest(manifest) if (!valid) { for (const error of errors) { console.error(`${error.instancePath}: ${error.message}`) } } ``` `validateManifest` returns `{ valid: boolean, errors: Ajv.ErrorObject[] }`. On a valid manifest, `valid` is `true` and `errors` is empty. ### Types ```typescript import type { Manifest, Hook, Skill, MCPServerConfig } from '@agentplugins/schema' const manifest: Manifest = { name: 'my-plugin', version: '1.0.0', description: 'Does awesome things across every agent', } ``` ## Schema highlights The schema enforces the manifest contract. Highlights: ### `name` ```jsonc { "type": "string", "pattern": "^[a-z][a-z0-9-]*$", "maxLength": 64 } ``` Kebab-case, lowercase, max 64 chars. ### `version` ```jsonc { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-...)?$" } ``` Full [semver](https://semver.org/) regex including pre-release and build metadata. ### `description` ```jsonc { "type": "string", "minLength": 10 } ``` Minimum 10 characters. ### `targets` ```jsonc { "type": "array", "items": { "type": "string", "enum": ["claude", "codex", "copilot", "gemini", "kimi", "opencode", "pimono"] } } ``` Only the seven supported platforms. ### `hooks` An object whose keys are the 19 universal hook names. Each value is a `{ matcher?, handler }` object whose `handler` is a `command`, `http`, or `reference` handler. See the [Hooks guide](/guide/hooks) for details. ### `additionalProperties: false` The root object rejects unknown fields. This catches typos like `discription` or `verson` at validation time. ## Ajv usage example If you'd rather bring your own Ajv instance: ```typescript import Ajv from 'ajv' import addFormats from 'ajv-formats' import schema from '@agentplugins/schema/manifest.schema.json' with { type: 'json' } const ajv = addFormats(new Ajv({ allErrors: true })) const validate = ajv.compile(schema) const ok = validate(manifest) if (!ok) { console.error(validate.errors) } ``` ## Versioning The schema follows SemVer. The `v1` series is backwards-compatible — fields may be added but never removed or renamed in a backwards-incompatible way. Breaking changes ship as `v2`. ::: tip Pin to `__DOCS_SITE__/schema/v1.json` (not a specific commit) to pick up non-breaking additions automatically. Pin to the raw GitHub URL at a specific commit SHA if you need absolute reproducibility. ::: ## Next steps * [Manifest reference](/guide/manifest) — what every field does. * [Linting](/guide/linting) — the eight lint rules. * [Adapters](/reference/adapters) — what each target platform emits. --- --- url: /reference/agent-paths.md --- # Agent Paths AgentPlugins scans the local machine for installed agent harnesses and fans plugin symlinks out to each one. This page is the authoritative registry of those paths. ## The universal store Every installed plugin lives in one place: | Path | Purpose | |---|---| | `~/.agents/plugins/` | Universal plugin store — the source of truth. | | `~/.agents/skills/` | Skills compatibility directory (also scanned). | `~/.agents/plugins//` is canonical. Removing a plugin from the store removes every symlink. Updating a plugin in the store updates every agent that links to it. ::: tip Skills placed in `~/.agents/skills/` are picked up and fanned out exactly like plugins in the universal store. Existing setups require no migration. ::: ## Detected agents Seven agent harnesses are supported: | Agent | Display name | Skill path | Binary | Manifest path | |---|---|---|---|---| | Claude Code | Claude Code | `~/.claude/skills` | `claude` | `~/.claude.json` | | Codex CLI | Codex CLI | `~/.codex/skills` | `codex` | `~/.codex/config.json` | | OpenCode | OpenCode | `~/.config/opencode/skills` | `opencode` | `~/.config/opencode/config.json` | | Kimi | Kimi | `~/.kimi/skills` | `kimi` | `~/.kimi/config.json` | | Gemini CLI | Gemini CLI | `~/.gemini/skills` | `gemini` | `~/.gemini/settings.json` | | GitHub Copilot CLI | GitHub Copilot CLI | `~/.copilot/skills` | `copilot` | `~/.copilot/config.json` | | Pi Mono | Pi Mono | `~/.pi/extensions` | `pi` | `~/.pi/config.json` | ## How detection works `agentplugins doctor` and `agentplugins add` detect agents by checking for the binary on `PATH` and the skill path on disk. An agent is considered **installed** if either the binary is resolvable or the skill path exists. ```text AgentPlugins doctor ──────────────────────────────────────── CLI version 0.2.0 Store path ~/.agents/plugins ✓ Skills path ~/.agents/skills ✓ Detected agents claude ~/.claude/skills ✓ codex ~/.codex/skills ✓ opencode ~/.config/opencode ✓ gemini ~/.gemini/skills ✗ (not installed) copilot ~/.copilot/skills ✓ kimi ~/.kimi/skills ✗ (not installed) pimono ~/.pi/extensions ✗ (not installed) 4 agents detected. ``` ::: warning `agentplugins add` only symlinks into detected agents. If you install a new harness later, run `agentplugins update --all` to fan existing plugins out to the new agent. ::: ## Symlink layout After installing a plugin, the universal store and the per-agent skill paths look like this: ``` ~/.agents/plugins/ └── my-plugin/ # source of truth ├── agentplugins.config.ts ├── SKILL.md └── hooks/ ~/.claude/skills/my-plugin → symlink to ~/.agents/plugins/my-plugin ~/.codex/skills/my-plugin → symlink ~/.config/opencode/skills/my-plugin → symlink ~/.copilot/skills/my-plugin → symlink ~/.gemini/skills/my-plugin → symlink ~/.kimi/skills/my-plugin → symlink ~/.pi/extensions/my-plugin → symlink ``` Each agent reads from its own skill path, unaware that the contents are shared. ## Overriding paths You can override the store path with the `--store` global flag: ```bash agentplugins --store /custom/store add user/my-plugin ``` There is no override for per-agent skill paths — those are determined by each agent's own conventions and are not configurable. ## Registry source The path registry is published as a machine-readable JSON document: | Source | URL | |---|---| | Hosted | `__DOCS_SITE__/schema/v1/agent-paths.json` | | Raw (GitHub) | `https://raw.githubusercontent.com/sigilco/agentplugins/main/spec/v1/agent-paths.json` | ```json { "store": { "path": "~/.agents/plugins" }, "skillsCompat": { "path": "~/.agents/skills" }, "agents": [ { "name": "claude", "displayName": "Claude Code", "skillPath": "~/.claude/skills", "binary": "claude", "manifestPath": "~/.claude.json" } // ... ] } ``` ## Next steps * [Adapters reference](/reference/adapters) — what each agent's adapter emits. * [Skills guide](/guide/skills) — the `SKILL.md` format. * [Installation](/guide/installation) — installing the CLI. --- --- url: /reference/adapters.md --- # Adapters An adapter compiles the universal manifest into one target platform's native format. AgentPlugins ships seven adapters — one per supported agent. This page documents what each emits and the trade-offs. ## Adapter matrix | Adapter | Output type | Native handlers | Hooks supported | `tools[]` | `mcpServers` | |---|---|---|---|:---:|:---:| | `claude` | JSON manifest + `commands.json` | command, http, reference | full | ⚠️ | ✅ | | `codex` | JSON manifest | command | subset | ⚠️ | ✅ | | `copilot` | JSON manifest | command, http, reference | subset | ⚠️ | ❌ | | `gemini` | JSON manifest | command | subset | ⚠️ | ❌ | | `kimi` | JSON manifest | command | subset | ⚠️ | ❌ | | `opencode` | TypeScript plugin + `opencode.json` | inline (reference) | subset | ✅ | ✅ | | `pimono` | TypeScript extension + `package.json` | inline (reference) | subset | ✅ | ✅ | ⚠️ = WARN emitted; `tools[]` is not natively emitted — use `mcpServers` for Claude/Codex (Tier-1 universal tool path). Two families: **JSON-emitting** adapters (claude, codex, copilot, gemini, kimi) produce static manifest files the host reads at startup. **Code-emitting** adapters (opencode, pimono) produce real TypeScript modules the host imports and calls. See the [Tier-1 Capability Matrix](/reference/compat-matrix) for full cross-harness details. ## JSON-emitting adapters ### claude Emits a Claude Code plugin directory: ``` dist/claude/ .claude-plugin/ plugin.json # manifest (name, version, description, ...) commands.json # slash commands hooks/ pre-tool-use.sh # command handlers wrapped as scripts session-start.sh skills/ /SKILL.md ``` * All handler types are supported natively. * Inline/reference handlers are auto-wrapped as shell scripts that invoke the plugin module via the Bun runtime. ### codex Emits a Codex CLI plugin directory: ``` dist/codex/ .codex-plugin/ plugin.json hooks/ pre-tool-use.sh ``` * Supports `command` handlers only. * Unsupported hooks are dropped at build time with a warning. * Exit code `2` from a pre-tool handler blocks the action. ### copilot Emits a GitHub Copilot CLI plugin directory: ``` dist/copilot/ plugin.json hooks.json hooks/ pre-tool-use.sh ``` * Supports `command` and `http` handlers natively. * `preToolUse` is **fail-closed**: if the handler errors, the tool call is blocked. ### gemini Emits a Gemini CLI extension directory: ``` dist/gemini/ gemini-extension.json hooks/ pre-tool-use.sh ``` * Supports `command` handlers only. * Inline handlers are auto-wrapped as command scripts. ### kimi Emits a Kimi (Moonshot) plugin directory: ``` dist/kimi/ kimi.plugin.json hooks/ pre-tool-use.sh ``` * Supports `command` handlers only. * Hooks are **fail-open**: handler errors do not block the action. ## Code-emitting adapters ### opencode Emits a real OpenCode plugin as TypeScript: ``` dist/opencode/ plugin.ts # typed Plugin export with native hooks opencode.json # manifest (skills, mcpServers, commands) ``` * Hooks are mapped to OpenCode's native lifecycle (`tool.execute.before`, `session.start`, etc.). * Inline/reference handlers are emitted as native async functions — no shell wrapping. * Skills and MCP servers are declared in `opencode.json` and picked up by OpenCode's config loader. ::: tip OpenCode runs on Bun, so inline handlers run in-process with no startup overhead. Prefer inline handlers when targeting OpenCode. ::: ### pimono Emits a Pi Mono extension as a TypeScript module plus a `package.json`: ``` dist/pimono/ index.ts # extension entry point package.json # declares the `pi` key with extension metadata ``` * Hooks are mapped to Pi Mono's event system. * Inline/reference handlers are emitted as native functions, loaded via [jiti](https://github.com/unjs/jiti). * The `package.json` declares the extension under the `pi` key. ## Choosing an `emitLanguage` Code-emitting adapters respect the `emitLanguage` field on the manifest: ```typescript { emitLanguage: 'typescript' // default — also: 'javascript', 'go' } ``` | Value | Effect | |---|---| | `typescript` | Emit `.ts` files (default). Best for editor support. | | `javascript` | Emit `.js` files. Skip type checking. | | `go` | Emit `.go` files (Pi Mono only, experimental). | JSON-emitting adapters ignore this field. ## Hook coverage Not every platform supports every universal hook. The build step reports dropped hooks: ```text Building my-plugin@1.0.0 codex: ⚠ hooks.sessionEnd is not supported by codex — will be ignored ⚠ hooks.userPromptSubmit is not supported by codex — will be ignored kimi: ⚠ hooks.userPromptSubmit is not supported by kimi — will be ignored Built 7 targets. ``` Run [`agentplugins lint`](/guide/linting) to catch these before publishing — the `hook-coverage` rule surfaces every mismatch. ## Handler wrapping When a JSON-emitting adapter encounters an `inline` or `reference` handler, it wraps the TypeScript function as a shell script that invokes the plugin module through the Bun runtime: ```bash #!/usr/bin/env bash exec bun "${PLUGIN_ROOT}/dist/handler.js" pre-tool-use "$@" ``` This means inline handlers work everywhere — at the cost of a Bun startup on platforms that don't support them natively. For latency-sensitive hooks on Claude/Codex/Gemini/Kimi, prefer `command` handlers. ## Adding a new adapter An adapter is any executable that implements the **JSON process ABI**: read a manifest (stdin or `--manifest `), compile platform-specific output, write files, and exit `0` (success) or non-zero (failure). This enables any-language adapters without SDK lock-in. See [`adapter.schema.json`](__DOCS_SITE__/schema/v1/adapter.schema.json) for the contract. ## Next steps * [Manifest reference](/guide/manifest) — what every field means. * [Hooks](/guide/hooks) — the 19 universal events. * [Agent paths](/reference/agent-paths) — where each adapter writes its output. --- --- url: /reference/compat-matrix.md description: >- What each Tier-1 harness supports — universal codegen, guided per-harness, or unsupported. --- # Tier-1 Capability Matrix **Tier-1 harnesses:** Claude Code · Codex · OpenCode · Pi Mono **Tier-2 harnesses (tracked, not blocking):** Copilot · Gemini · Kimi Legend: * ✅ **Universal codegen** — adapter emits native output automatically from the manifest * ⚠️ **Guided per-harness** — no native primitive; author follows a documented escape-hatch pattern; emits a WARN (not error) on portable manifests * ❌ **Unsupported** — no viable path; recorded here, not blocking * n/a — not applicable (mechanism differs but functionality is covered) ## Capability table | Capability | Claude Code | Codex | OpenCode | Pi Mono | Notes | |---|:---:|:---:|:---:|:---:|---| | `skills` | ✅ | ✅ | ✅ | ✅ | Universal codegen | | `hooks` (lifecycle) | ✅ | ✅ | ✅ | ✅ | Universal codegen | | `commands` | ✅ | ✅ | ✅ | ✅ | Universal codegen | | `mcpServers` | ✅ | ✅ | ✅ | ✅ | **Recommended universal tool path** | | `agents[]` | ✅ | ✅ | ✅ | ✅ | Universal codegen | | `subagentStart` | ✅ | ✅ | ⚠️ | ✅ | OpenCode: no native event; emits WARN; guided path: intercept via `preToolUse` for subagent tool | | `subagentStop` | ✅ | ✅ | ⚠️ | ✅ | Same as above; Pi `stop`↔`subagentStop` collision fixed in v0.3.0 | | `tools[]` (first-class) | ⚠️ | ⚠️ | ✅ | ✅ | WARN emitted; use `mcpServers` for Claude/Codex (Tier-1 universal tool path) | | `stop` / `continueWith` | ⚠️ | ⚠️ | ⚠️ | ⚠️ | New primitive — v0.4.0; all-Tier-1 design | | Native-entry passthrough | n/a (JSON) | n/a (JSON) | ⚠️ | ⚠️ | `nativeEntry` escape hatch — v0.4.0 | | `spawnChild` subprocess | ✅ via cmd | ✅ via cmd | ⚠️ | ⚠️ | Primitive set — v0.4.0 | ## Tier-2 footnotes | Capability | Copilot | Gemini | Kimi | |---|:---:|:---:|:---:| | `skills` | ✅ | ✅ | ✅ | | `hooks` (lifecycle) | ⚠️ | ⚠️ | ❌ | | `subagentStart` / `subagentStop` | ❌ | ❌ | ❌ | | `tools[]` | ✅ | ✅ | ❌ | | `mcpServers` | ❌ | ❌ | ❌ | Kimi `hooks` remain in `UNSUPPORTED_HOOKS` (`packages/adapter-kimi/src/index.ts`); tracked here, not blocking v0.3.0. ## Decision tree for authors ``` Does universal codegen cover this capability across all Tier-1? YES → use it; adapter handles the rest NO → is there a custom (escape-hatch) path on all Tier-1? YES → follow the guided per-harness pattern (see "Rewriting for tier-1 parity" guide) NO → is the gap TUI-grade fidelity only? YES → acceptable degradation; note in this matrix NO → open a primitive proposal (v0.4.0+ scope) ``` *** *This matrix is the living contract for the project. Update it as capabilities land or gaps are discovered. See the [PRD roadmap](/guide/introduction) for full context.*