Follow the create-plugin skill workflow to scaffold a new Claude Code plugin.
Inputs
$ARGUMENTS — optional plugin name in kebab-case. Omit to start with discovery.
Steps
- If
$ARGUMENTS provides a plugin name, use it to seed Phase 1
- Follow the create-plugin phased workflow: discover purpose and plugin type,
plan component table (skills / commands / agents / hooks / MCP), ask clarifying
questions per component, scaffold directory structure and
plugin.json (ensuring "author" is an object with "name" and "email"), implement
each component using the appropriate sub-skill, validate, test, and document
- plugin.json Template & Minimal Metadata Standard (MANDATORY — do not skip):
- Ensure
.claude-plugin/plugin.json uses the clean, minimal metadata format:{
"name": "<plugin-name>",
"version": "0.1.0",
"description": "<description>",
"author": {
"name": "richfrem",
"email": "connect.richfrem@gmail.com"
},
"repository": "https://github.com/richfrem/agent-plugins-skills",
"license": "MIT",
"keywords": [
"<keyword>"
]
}
- Strict Rules:
.claude-plugin/plugin.json must NEVER contain skills, agents, hooks, or commands arrays (these are auto-discovered by Claude Code).
"author" must ALWAYS be an object with "name" (and optionally "email"), never a plain string.
- No duplicate top-level keys.
- plugin.yaml (Hermes compatibility — always generate): After
plugin.json is finalized, scaffold a plugin.yaml at the plugin root for hermes-agent compatibility. Format:name: <plugin-name>
version: <version>
description: "<description>"
author: <author>
kind: backend # or standalone (no Python scripts)
platforms:
- linux
- macos
- windows
provides_tools: # list script basenames (no .py) that expose callable tools
- script_name
skills: # list skill directory names under skills/
- skill-name
kind: standalone — plugin has no Python scripts that hermes calls directly
kind: backend — plugin has scripts in scripts/ that hermes invokes as tools
- Only include
provides_tools if scripts/ contains callable tool scripts
- Skills list must match actual directory names under
skills/
- Report: "
plugin.yaml created for hermes compatibility. ✅"
__init__.py (Hermes tool/hook wiring — generate when plugin has scripts): If the plugin has callable Python scripts in scripts/, scaffold a root-level __init__.py with a register(ctx) function following this pattern:from __future__ import annotations
from pathlib import Path
_HERE = Path(__file__).resolve().parent
def register(ctx) -> None:
# Register skills
ctx.register_skill(
name="<skill-name>", # bare name only — hermes auto-prefixes plugin name as namespace
path=_HERE / "skills" / "<skill-name>",
)
# Register tools (if scripts expose callable tools)
# ctx.register_tool(name, toolset, schema, handler)
# Register hooks (if plugin needs lifecycle hooks)
# ctx.register_hook("post_tool_call", handler)
- Always include
register_skill() calls for every skill in the plugin
- Only add
register_tool() if the plugin provides callable Python tools
- Only add
register_hook() if the plugin needs lifecycle hooks
- Without
__init__.py, hermes shows "No __init__.py" warning and the plugin won't activate
- Report: "
__init__.py created with register() function. ✅"
- Report the created plugin directory and verification checklist results
Output
Plugin directory with .claude-plugin/plugin.json, component directories, README.md,
and a .claude/settings.json stub for reliable local discovery.
Edge Cases
- If
$ARGUMENTS is empty: begin with Phase 1 discovery — do not pre-fill plugin name
- If similar plugin already exists: reference it as a starting point
- If MCP integrations are needed: invoke
create-mcp-integration for each one
- After scaffolding: run
/agent-scaffolders:audit-plugin to validate structure
Symlink Standards for Shared Scripts
When a skill needs to call a Python helper script that is shared across skills in the same
plugin, always create a file-level symlink in the skill's scripts/ folder pointing to the
canonical copy at the plugin root — never duplicate the file.
Standard pattern:
plugins/<plugin>/scripts/<canonical_name>.py ← canonical source (real file)
plugins/<plugin>/skills/<skill>/scripts/<name>.py ← symlink → ../../../scripts/<canonical_name>.py
The symlink name and target name may differ (e.g. execute.py → exploration_optimizer_execute.py).
The bridge installer resolves all symlinks to physical copies when deploying via the marketplace.
Creating symlinks correctly:
# From the skill's scripts/ directory:
ln -s ../../../scripts/<canonical_name>.py <symlink_name>.py
# Or via symlink_manager.py:
python plugins/dev-utils/scripts/symlink_manager.py create \
--src plugins/<plugin>/scripts/<canonical_name>.py \
--dst plugins/<plugin>/skills/<skill>/scripts/<symlink_name>.py
⚠️ Windows / core.symlinks warning: If git config core.symlinks is false, git checks
out symlinks as plain-text "stand-in" files. These are silently broken — the bridge installer
copies the path string, not the script. After checkout on Windows or any machine where
symlinks may have degraded, run:
python plugins/dev-utils/scripts/bulk_symlink_fixer.py plugins/<plugin-name>
Then manually verify: find plugins/<plugin-name>/skills -path "*/scripts/*" -type f ! -type l
should return nothing (all script references should be real symlinks, not plain files).
Marketplace Compatibility Note
When this plugin will be distributed via a marketplace.json, the marketplace entry defaults to strict: true, which requires the plugin to have its own plugin.json. A missing plugin.json silently prevents the entire plugin from loading.
Always:
- Scaffold
.claude-plugin/plugin.json inside the plugin directory (this skill does this by default)
- When adding the plugin to a marketplace entry, explicitly set
"strict": true — never rely on the default
- See
manage-marketplace skill for the correct marketplace entry format
Plugin Architecture Conventions
- Hub-and-spoke shared scripts: a script used by two or more skills within the same plugin belongs at the plugin root (
scripts/script.py), not duplicated per-skill. A script used by only one skill lives inside that skill's own scripts/ directory.
- No cross-plugin script imports: a plugin must never import or execute another plugin's Python code directly. Cross-plugin coordination happens via agent delegation (natural-language instructions to invoke another skill), never hardcoded imports.
- File-level symlinks only, never directories: shared resources are mirrored into each consuming skill via a real file-level symlink, never a directory-level symlink or a duplicated copy.
- Self-contained installed skills: an installed skill must function correctly with zero runtime dependency on the source repository, another plugin, or a sibling Python package.
1---2name: create-plugin3plugin: agent-scaffolders4description: >5 Scaffolds a new top-level agent plugin directory. NOT for scaffolding single skills (use `create-skill`) and NOT for adding MCP integrations to existing plugins (use `create-mcp-integration`).6argument-hint: "[plugin-name]"7allowed-tools: Bash, Read, Write8---910Follow the `create-plugin` skill workflow to scaffold a new Claude Code plugin.1112## Inputs1314- `$ARGUMENTS` — optional plugin name in kebab-case. Omit to start with discovery.1516## Steps17181. If `$ARGUMENTS` provides a plugin name, use it to seed Phase 1192. Follow the create-plugin phased workflow: discover purpose and plugin type,20 plan component table (skills / commands / agents / hooks / MCP), ask clarifying21 questions per component, scaffold directory structure and `plugin.json` (ensuring `"author"` is an object with `"name"` and `"email"`), implement22 each component using the appropriate sub-skill, validate, test, and document233. **plugin.json Template & Minimal Metadata Standard (MANDATORY — do not skip):**24 - Ensure `.claude-plugin/plugin.json` uses the clean, minimal metadata format:25 ```json26 {27 "name": "<plugin-name>",28 "version": "0.1.0",29 "description": "<description>",30 "author": {31 "name": "richfrem",32 "email": "connect.richfrem@gmail.com"33 },34 "repository": "https://github.com/richfrem/agent-plugins-skills",35 "license": "MIT",36 "keywords": [37 "<keyword>"38 ]39 }40 ```41 - **Strict Rules**:42 - `.claude-plugin/plugin.json` must **NEVER** contain `skills`, `agents`, `hooks`, or `commands` arrays (these are auto-discovered by Claude Code).43 - `"author"` must **ALWAYS** be an object with `"name"` (and optionally `"email"`), never a plain string.44 - No duplicate top-level keys.454. **plugin.yaml (Hermes compatibility — always generate):** After `plugin.json` is finalized, scaffold a `plugin.yaml` at the plugin root for hermes-agent compatibility. Format:46 ```yaml47 name: <plugin-name>48 version: <version>49 description: "<description>"50 author: <author>51 kind: backend # or standalone (no Python scripts)52 platforms:53 - linux54 - macos55 - windows56 provides_tools: # list script basenames (no .py) that expose callable tools57 - script_name58 skills: # list skill directory names under skills/59 - skill-name60 ```61 - `kind: standalone` — plugin has no Python scripts that hermes calls directly62 - `kind: backend` — plugin has scripts in `scripts/` that hermes invokes as tools63 - Only include `provides_tools` if `scripts/` contains callable tool scripts64 - Skills list must match actual directory names under `skills/`65 - Report: *"`plugin.yaml` created for hermes compatibility. ✅"*665. **`__init__.py` (Hermes tool/hook wiring — generate when plugin has scripts):** If the plugin has callable Python scripts in `scripts/`, scaffold a root-level `__init__.py` with a `register(ctx)` function following this pattern:67 ```python68 from __future__ import annotations69 from pathlib import Path7071 _HERE = Path(__file__).resolve().parent7273 def register(ctx) -> None:74 # Register skills75 ctx.register_skill(76 name="<skill-name>", # bare name only — hermes auto-prefixes plugin name as namespace77 path=_HERE / "skills" / "<skill-name>",78 )79 # Register tools (if scripts expose callable tools)80 # ctx.register_tool(name, toolset, schema, handler)81 # Register hooks (if plugin needs lifecycle hooks)82 # ctx.register_hook("post_tool_call", handler)83 ```84 - Always include `register_skill()` calls for every skill in the plugin85 - Only add `register_tool()` if the plugin provides callable Python tools86 - Only add `register_hook()` if the plugin needs lifecycle hooks87 - Without `__init__.py`, hermes shows "No `__init__.py`" warning and the plugin won't activate88 - Report: *"`__init__.py` created with register() function. ✅"*896. Report the created plugin directory and verification checklist results9091## Output9293Plugin directory with `.claude-plugin/plugin.json`, component directories, `README.md`,94and a `.claude/settings.json` stub for reliable local discovery.9596## Edge Cases9798- If `$ARGUMENTS` is empty: begin with Phase 1 discovery — do not pre-fill plugin name99- If similar plugin already exists: reference it as a starting point100- If MCP integrations are needed: invoke `create-mcp-integration` for each one101- After scaffolding: run `/agent-scaffolders:audit-plugin` to validate structure102103## Symlink Standards for Shared Scripts104105When a skill needs to call a Python helper script that is shared across skills in the same106plugin, always create a **file-level symlink** in the skill's `scripts/` folder pointing to the107canonical copy at the plugin root — never duplicate the file.108109**Standard pattern:**110```111plugins/<plugin>/scripts/<canonical_name>.py ← canonical source (real file)112plugins/<plugin>/skills/<skill>/scripts/<name>.py ← symlink → ../../../scripts/<canonical_name>.py113```114115The symlink name and target name may differ (e.g. `execute.py` → `exploration_optimizer_execute.py`).116The bridge installer resolves all symlinks to physical copies when deploying via the marketplace.117118**Creating symlinks correctly:**119```bash120# From the skill's scripts/ directory:121ln -s ../../../scripts/<canonical_name>.py <symlink_name>.py122123# Or via symlink_manager.py:124python plugins/dev-utils/scripts/symlink_manager.py create \125 --src plugins/<plugin>/scripts/<canonical_name>.py \126 --dst plugins/<plugin>/skills/<skill>/scripts/<symlink_name>.py127```128129**⚠️ Windows / core.symlinks warning:** If `git config core.symlinks` is `false`, git checks130out symlinks as plain-text "stand-in" files. These are silently broken — the bridge installer131copies the path string, not the script. After checkout on Windows or any machine where132symlinks may have degraded, run:133```bash134python plugins/dev-utils/scripts/bulk_symlink_fixer.py plugins/<plugin-name>135```136Then manually verify: `find plugins/<plugin-name>/skills -path "*/scripts/*" -type f ! -type l`137should return nothing (all script references should be real symlinks, not plain files).138139## Marketplace Compatibility Note140141When this plugin will be distributed via a `marketplace.json`, the marketplace entry defaults to `strict: true`, which **requires** the plugin to have its own `plugin.json`. A missing `plugin.json` silently prevents the entire plugin from loading.142143Always:1441. Scaffold `.claude-plugin/plugin.json` inside the plugin directory (this skill does this by default)1452. When adding the plugin to a marketplace entry, explicitly set `"strict": true` — never rely on the default1463. See `manage-marketplace` skill for the correct marketplace entry format147148## Plugin Architecture Conventions149150- **Hub-and-spoke shared scripts:** a script used by two or more skills within the same plugin belongs at the plugin root (`scripts/script.py`), not duplicated per-skill. A script used by only one skill lives inside that skill's own `scripts/` directory.151- **No cross-plugin script imports:** a plugin must never import or execute another plugin's Python code directly. Cross-plugin coordination happens via agent delegation (natural-language instructions to invoke another skill), never hardcoded imports.152- **File-level symlinks only, never directories:** shared resources are mirrored into each consuming skill via a real file-level symlink, never a directory-level symlink or a duplicated copy.153- **Self-contained installed skills:** an installed skill must function correctly with zero runtime dependency on the source repository, another plugin, or a sibling Python package.