Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ff8763d
docs(plan): add agent plugin system design
May 28, 2026
c2546f4
docs(plan): add agent plugin system implementation plan
May 28, 2026
ef8dd59
build: add semver for agent plugin dependency resolution
May 28, 2026
717b054
feat(plugins): add frontmatter + example parsing helpers
May 28, 2026
f2caeac
feat(plugins): add catalog builder and semver range check
May 28, 2026
ef96341
feat(plugins): add transitive dependency resolver with cycle detection
May 28, 2026
3c794ca
fix(plugins): harden frontmatter list parsing and null-safe example c…
May 28, 2026
f97f9d6
feat(plugins): add agent plugin manifest JSON schema
May 28, 2026
16b5570
test(plugins): add agent plugin fixtures
May 28, 2026
b9cfded
feat(plugins): add manifest + structural validator
May 28, 2026
4352a72
fix(plugins): harden validator IO handling, sibling dep resolution, a…
May 28, 2026
6be51d3
feat(plugins): add registry with dependency resolution, install/unins…
May 28, 2026
83b50de
fix(plugins): persist install state incrementally, guard installed.js…
May 28, 2026
ea49fda
feat(plugins): add scaffolding CLI for new agent plugins
May 28, 2026
739b541
fix(plugins): guard scaffolder against non-interactive hang, normaliz…
May 28, 2026
1aa904d
feat(plugins): add static assertion test runner
May 28, 2026
b75f3a8
refactor(plugins): share plugin-dir scan, surface validator detail in…
May 28, 2026
073de7f
docs(plugins): document the agent plugin system
May 28, 2026
4e1182b
ci(plugins): wire agent-plugin validate + test into verify-all
May 28, 2026
a30f43f
fix(plugins): track plugin sources in git and validate before install
May 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .claude/agent-plugin.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://aurelius.dev/agent-plugin.schema.json",
"title": "Agent Plugin Manifest",
"type": "object",
"additionalProperties": false,
"required": ["name", "version", "description"],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z][a-z0-9-]*$",
"description": "kebab-case; must match directory and agent.md frontmatter name"
},
"version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$"
},
"description": { "type": "string", "minLength": 1 },
"author": { "type": "string" },
"license": { "type": "string" },
"agent": { "type": "string", "default": "agent.md" },
"dependencies": {
"type": "object",
"additionalProperties": false,
"properties": {
"agents": {
"type": "object",
"additionalProperties": { "type": "string", "minLength": 1 }
},
"skills": { "type": "array", "items": { "type": "string" } },
"tools": { "type": "array", "items": { "type": "string" } }
}
},
"hooks": {
"type": "object",
"additionalProperties": false,
"properties": {
"preInstall": { "type": "string" },
"postInstall": { "type": "string" },
"preUninstall": { "type": "string" },
"postUninstall": { "type": "string" }
}
},
"tests": { "type": "string" }
}
}
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
!.claude/settings.json
!.claude/pipeline.config.json
!.claude/pipeline.config.schema.json
!.claude/agent-plugin.schema.json
!.claude/agent-plugins/
!.claude/test-fixtures/

# Claude Code temporary/user-specific files
Expand Down Expand Up @@ -37,7 +39,8 @@ htmlcov/
playwright-report/
.playwright/
test-results/
scripts/__tests__/fixtures/
scripts/__tests__/fixtures/*
!scripts/__tests__/fixtures/agent-plugins/

# Environment
.env
Expand Down
18 changes: 16 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ node scripts/validate-pipeline-config.js --config <path> --schema <path>
# Export generated components as a publishable design-system workspace
./scripts/export-design-system.sh [--scope @org] [--output dir] [--framework <react|vue|svelte|react-native>] [--dry-run] [--json]

# Author / validate / install / test custom agents as versioned plugins
node scripts/create-agent-plugin.js <name> [--description ...] [--model ...] [--tools ...] [--with-hooks]
node scripts/validate-agent-plugin.js --dir <plugin-dir> # or --all [--plugins-root <dir>]
node scripts/agent-registry.js (list | resolve <name> | install <name> | uninstall <name>)
node scripts/test-agent-plugin.js --dir <plugin-dir> # or --all [--plugins-root <dir>]

# Incremental build with caching and profiling
./scripts/incremental-build.sh [phase|all] [--force] [--parallel]

Expand Down Expand Up @@ -551,6 +557,14 @@ gh issue create # Create issue
./scripts/verify-all.sh --ci # CI mode: JSON output, exit 1 on any failure
```

**Agent Plugins** (custom agents as versioned plugins — see `docs/guides/agent-plugins.md`):
```bash
node scripts/create-agent-plugin.js <name> [--description ...] [--model ...] [--tools ...] [--with-hooks]
node scripts/validate-agent-plugin.js --dir <plugin-dir> # or --all
node scripts/agent-registry.js (list | resolve <name> | install <name> | uninstall <name>)
node scripts/test-agent-plugin.js --dir <plugin-dir> # or --all
```

**Build Performance & Caching:**
```bash
./scripts/incremental-build.sh # Incremental build with caching
Expand All @@ -565,7 +579,7 @@ node scripts/metrics-dashboard.js summary # Quick metrics summary

---

**Last Updated:** 2026-05-23
**Architecture:** 53 agents, 20 skills, 4 plugins + gh CLI, Figma + Canva + Playwright MCP, 33 scripts, 8 hooks
**Last Updated:** 2026-05-28
**Architecture:** 53 agents, 20 skills, 4 plugins + gh CLI, Figma + Canva + Playwright MCP, 38 scripts, 8 hooks

> **Keeping counts in sync:** When adding or removing agents, skills, scripts, or hooks, update all count references across the project. Search for the old count number in `*.md` files to find all references: `CLAUDE.md`, `README.md`, `CONTRIBUTING.md`, `docs/onboarding/`, `docs/react-development/`, and `.claude/AGENT-NAMING-GUIDE.md`. The agent and skill counts are enforced automatically by `scripts/check-doc-counts.sh` (run in CI and on pre-commit), which recounts `.claude/agents/` and `.claude/skills/` and fails on any documented count that disagrees.
184 changes: 184 additions & 0 deletions docs/guides/agent-plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Agent Plugin System

A plugin packages a **custom Claude Code agent** plus the metadata needed to
validate, install, and dependency-resolve it. This lets you author agents in a
standardized, versioned way and install them (with their dependencies) into
`.claude/agents/` where Claude Code reads them.

> Design and rationale: `docs/plans/2026-05-27-agent-plugin-system-design.md`.

## What a plugin is

A plugin is a directory under `.claude/agent-plugins/<name>/`:

```
.claude/agent-plugins/<name>/
plugin.json # manifest (validated against a JSON Schema)
agent.md # the agent: YAML frontmatter + prompt (the deliverable)
hooks/ # optional management-lifecycle scripts (.sh)
tests/
plugin.test.json # static assertions run by the test runner
```

Authoring lives here. **Installing** copies `agent.md` to
`.claude/agents/<name>.md` (where Claude Code reads it) and records state in
`.claude/agent-plugins/installed.json`.

## The four CLIs

Each is an ESM Node script with a `.sh` wrapper, a `--json` mode, and exit codes
`0` (ok) / `1` (failure) / `2` (usage/IO). They share `scripts/agent-plugin-lib.js`.

| Script | Purpose |
|--------|---------|
| `create-agent-plugin.js` | Scaffold a new plugin directory |
| `validate-agent-plugin.js` | Validate a manifest (schema + structural checks) |
| `agent-registry.js` | List / resolve / install / uninstall plugins |
| `test-agent-plugin.js` | Run a plugin's static assertions |

### Create

```bash
node scripts/create-agent-plugin.js <name> [--description "..."] \
[--model opus|sonnet|haiku] [--tools "Read,Write"] [--with-hooks] [--force] [--json]
```

Scaffolds `plugin.json` (version `0.1.0`), an `agent.md` skeleton in the house
style (core expertise, *When to Use*, two `<example>` blocks), and default test
assertions. With `--with-hooks`, also writes `hooks/pre-install.sh` and
`hooks/post-install.sh` stubs. Refuses to overwrite without `--force`. Prompts
for missing fields only on an interactive TTY (non-interactive runs need the
flags or `--json`).

### Validate

```bash
node scripts/validate-agent-plugin.js --dir <plugin-dir> [--json]
node scripts/validate-agent-plugin.js --all [--plugins-root <dir>] [--json]
```

Validates `plugin.json` against `.claude/agent-plugin.schema.json` (collecting
all errors), then runs structural checks the schema cannot express:

- `name` matches the directory name **and** the `agent.md` frontmatter `name`.
- The agent file exists; its frontmatter has `name`/`description`/`tools`, and
`model`/`permissionMode` are valid if present.
- Declared hook scripts exist.
- `skills` deps exist under `.claude/skills/`; `tools` deps exist as repo files.
- Agent dependencies resolve (in `--dir` mode the catalog is seeded from sibling
plugins, so siblings resolve without being installed).

### Registry (install / uninstall)

```bash
node scripts/agent-registry.js list [--json]
node scripts/agent-registry.js resolve <name> [--json] # dry-run install order
node scripts/agent-registry.js install <name> [--json]
node scripts/agent-registry.js uninstall <name> [--force] [--json]
```

`install` resolves the dependency graph, installs in topological order (deps
first), runs lifecycle hooks, copies each `agent.md` into `.claude/agents/`, and
records `{version, sourceHash, installedAt}` in `installed.json`. Already-installed
plugins at the same version are skipped (idempotent). `uninstall` refuses to
remove a plugin another installed plugin depends on unless `--force`.

### Test

```bash
node scripts/test-agent-plugin.js --dir <plugin-dir> [--json]
node scripts/test-agent-plugin.js --all [--plugins-root <dir>] [--json]
node scripts/test-agent-plugin.js --dir <d> --assert "manifest.valid" [--assert ...]
```

Runs the assertions in the plugin's `tests/plugin.test.json` (or inline
`--assert` overrides). Assertions are **pure, deterministic predicates over the
plugin's files** — no Claude invocation. An unknown assertion string is a hard
error (exit 2) so typos can't silently pass.

## Manifest reference (`plugin.json`)

```jsonc
{
"name": "react-perf-expert", // kebab-case ^[a-z][a-z0-9-]*$; matches dir + agent.md name
"version": "1.2.0", // semver
"description": "...", // required
"author": "...", // optional
"license": "MIT", // optional
"agent": "agent.md", // path to the prompt file (default "agent.md")
"dependencies": {
"agents": { "asset-cataloger": "^1.0.0" }, // name -> semver range; resolved + version-checked
"skills": ["react-testing-workflows"], // existence-checked under .claude/skills/
"tools": ["scripts/visual-diff.js"] // existence-checked, repo-relative
},
"hooks": { // all optional; paths relative to the plugin dir
"preInstall": "hooks/check-deps.sh",
"postInstall": "hooks/register.sh",
"preUninstall": "hooks/cleanup.sh",
"postUninstall":"hooks/unregister.sh"
},
"tests": "tests/plugin.test.json"
}
```

The schema is strict (`additionalProperties: false` at the root and in `hooks`),
so typo'd keys are rejected.

## Dependencies

- **Agent deps** are versioned (`name → semver range`). The registry resolves
them transitively, topologically sorts the install order (deps before
dependents), and reports **missing** deps, **version** mismatches, and
**cycles** — all in one pass.
- **Skill and tool deps** are existence-checked only (they are not installed):
skills must exist under `.claude/skills/`, tools as repo-relative file paths.

## Lifecycle hooks

Hooks attach to the **plugin-management lifecycle** (not agent invocation —
Claude Code has no per-agent runtime hooks). The registry runs them via `bash`
with the environment variables `PLUGIN_NAME`, `PLUGIN_DIR`, and `PLUGIN_VERSION`:

| Hook | When | On failure |
|------|------|------------|
| `preInstall` | before copying the agent | **aborts** the install |
| `postInstall` | after the agent is recorded | warns only |
| `preUninstall` | before removing the agent | **aborts** the uninstall |
| `postUninstall` | after the agent is removed | warns only |

Hooks require `bash` on PATH (Git Bash/WSL on Windows). Follow the defensive
skeleton: `set -u`, `trap 'exit 0' ERR`, explicit `exit`.

## Assertion catalog (`tests/plugin.test.json`)

```jsonc
{ "assert": [
"manifest.valid", // delegates to validate-agent-plugin.js
"frontmatter.has(name,description,tools)",
"frontmatter.model in (opus,sonnet,haiku)", // passes if the field is unset
"deps.resolve", // dependencies resolve with no errors
"hooks.executable", // declared hook files exist and are readable
"prompt.section('When to Use This Agent')", // a matching markdown heading exists
"description.examples >= 2" // counts <example> blocks in the description
]}
```

The catalog is fixed; extend it by adding a predicate to `test-agent-plugin.js`.

## Typical workflow

```bash
# 1. Scaffold
node scripts/create-agent-plugin.js my-expert --description "Does the thing" --with-hooks

# 2. Edit .claude/agent-plugins/my-expert/agent.md, then validate + test
node scripts/validate-agent-plugin.js --dir .claude/agent-plugins/my-expert
node scripts/test-agent-plugin.js --dir .claude/agent-plugins/my-expert

# 3. Install it (and any dependencies) into .claude/agents/
node scripts/agent-registry.js install my-expert
```

`validate-agent-plugin.js --all` and `test-agent-plugin.js --all` validate/test
every plugin under `.claude/agent-plugins/` and are wired into `verify-all.sh`
(they no-op when no plugins exist).
Loading
Loading