/plugin-authoring
The authoring procedures for this marketplace: how to create a skill, how to create a plugin, what to update when either changes, and how to delete one without leaving dangling metadata.
Promoted out of CLAUDE.md (issue #2140) because all four are procedures with
a clear trigger — they do not need to be resident when the user is debugging a
hook. CLAUDE.md keeps only the repo blurb, the rules index, and the gotchas.
Detailed patterns live in the rules this skill names; it is the sequence, not a second copy of them.
Creating New Skills
See .claude/rules/skill-development.md for detailed patterns.
Note (Claude Code 2.1.157): plugins placed in
.claude/skillsare now auto-loaded without a marketplace entry — handy for local or quick one-off plugins. This repo's published plugins still use the full marketplace + release-please lifecycle described in Plugin Lifecycle below.
Quick Start
- Create skill directory:
mkdir -p <plugin>/skills/<skill-name> - Create
skill.mdwith YAML frontmatter:--- name: <Skill Name> description: <1-2 sentence description> allowed-tools: Bash, Read, Grep, Glob, TodoWrite created: YYYY-MM-DD modified: YYYY-MM-DD reviewed: YYYY-MM-DD --- - Follow content structure: Core Expertise → Commands → Patterns → Quick Reference
- Include agentic optimizations table
- Update all metadata files (see Plugin Lifecycle section)
Skill Granularity Decision
| Choose... | When... |
|---|---|
| Single skill | Operations are related and share context |
| Multiple skills | Distinct workflows, different user intents |
Example: bun-package-manager (deps) vs bun-development (run/test/build)
Creating User-Invocable Skills
Skills are invocable via /plugin:skill-name syntax. See .claude/rules/skill-naming.md for naming conventions.
- Create skill directory:
mkdir -p <plugin>/skills/<skill-name> - Create
SKILL.mdwith YAML frontmatter:--- name: <skill-name> description: What it does. Use when... args: <arg-spec> allowed-tools: Bash, Read argument-hint: human hint created: YYYY-MM-DD modified: YYYY-MM-DD reviewed: YYYY-MM-DD --- - Include: Context → Execution → Post-actions
Plugin Lifecycle
Files to Update
When creating, modifying, or deleting a plugin, update these files:
| File | Location | Action |
|---|---|---|
plugin.json | <plugin>/.claude-plugin/plugin.json | Create/update plugin manifest |
README.md | <plugin>/README.md | Create/update plugin documentation |
marketplace.json | .claude-plugin/marketplace.json | Add/update/remove plugin entry |
release-please-config.json | Root | Add/remove plugin package config |
.release-please-manifest.json | Root | Add/remove plugin version entry |
PLUGIN-MAP.md | docs/PLUGIN-MAP.md | Add/remove plugin from navigation map |
settings.json | .claude/settings.json | Add/remove the plugin in enabledPlugins (<plugin>@laurigates-claude-plugins) — enforced by the Plugin: Enablement drift check |
Creating a New Plugin
Quick scaffold (Claude Code 2.1.157):
claude plugin init <name>scaffolds a new plugin in.claude/skills(auto-loaded, no marketplace entry needed). Use it for local/quick plugins; for plugins published from this repo, follow the full marketplace + release-please steps below.
- Create plugin directory structure (see Project Structure in
CLAUDE.md) - Create
.claude-plugin/plugin.json:{ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "new-plugin", "version": "1.0.0", "description": "Plugin description", "author": { "name": "Lauri Gates" }, "license": "MIT", "keywords": ["keyword1", "keyword2"] }license(from the allowlist["MIT"]) andauthor.nameare required here and in the marketplace entry below;bash scripts/plugin-compliance-check.sh --license-onlyfails the PR without them. The repo-root MITLICENSEcovers every plugin, so no per-pluginLICENSEfile is needed. - Create
README.mdwith plugin documentation - Add entry to
.claude-plugin/marketplace.json(under thepluginsarray), carrying the sameauthorandlicenseas the plugin'splugin.json:
Note: marketplace.json has structure{ "name": "new-plugin", "source": "./new-plugin", "description": "Plugin description", "version": "1.0.0", "author": { "name": "Lauri Gates" }, "license": "MIT", "keywords": ["keyword1", "keyword2"], "category": "category-name" }{ "name": "...", "plugins": [...] }— add to thepluginsarray.python3 scripts/sync-plugin-configs.py --fixgenerates this entry from the manifest. - Add to
release-please-config.json:"new-plugin": { "component": "new-plugin", "release-type": "simple", "extra-files": [ {"type": "json", "path": ".claude-plugin/plugin.json", "jsonpath": "$.version"} ], "changelog-sections": [ {"type": "feat", "section": "Features"}, {"type": "fix", "section": "Bug Fixes"}, {"type": "perf", "section": "Performance"}, {"type": "refactor", "section": "Code Refactoring"}, {"type": "docs", "section": "Documentation"} ] } - Add to
.release-please-manifest.json:"new-plugin": "1.0.0" - Enable it in
.claude/settings.jsonso the repo dogfoods it:
The"enabledPlugins": { "new-plugin@laurigates-claude-plugins": true }Plugin: Enablement driftcheck (scripts/check-enabled-plugins-drift.sh) fails CI if a marketplace plugin is left disabled.
Deleting a Plugin
- Remove plugin directory
- Remove entry from
.claude-plugin/marketplace.json - Remove package from
release-please-config.json - Remove version from
.release-please-manifest.json - Remove the
<plugin>@laurigates-claude-pluginskey from.claude/settings.jsonenabledPlugins
Development Workflow
- Research documentation - Use context7, web search
- Plan skill structure - Decide granularity, scope
- Write skills - Follow standard structure
- Update all metadata files - See Plugin Lifecycle section
- Commit early - Use conventional commit format (see
.claude/rules/conventional-commits.md) - Test - Verify skills load and work
- Create PR - Use conventional commit format for title (drives automation)
Verify
After a plugin add or delete, the two guards that catch dangling metadata:
bash scripts/check-docs-index.sh
bash scripts/plugin-compliance-check.sh
check-docs-index.sh cross-checks the plugin set and per-plugin skill/agent
counts against disk across README.md, docs/PLUGIN-MAP.md, and the d2
diagram — use /docs-refresh to repair count drift it reports. It also gates
two name-level invariants at ERROR severity (--strict therefore fails CI):
the committed docs/diagrams/plugin-relationships.svg must render the same
per-plugin labels its .d2 source states (re-render with
d2 docs/diagrams/plugin-relationships.d2 docs/diagrams/plugin-relationships.svg
— never hand-edit the .svg), and every leading-cell /<ns>:<name> row in a
plugin README must resolve to a skill directory (#2453).
Related
.claude/rules/skill-development.md— skill creation patterns.claude/rules/skill-naming.md— namespace conventions for user-invocable skills.claude/rules/skill-quality.md— size limits, required sections, quality checklist.claude/rules/plugin-structure.md— plugin.json schema and directory layout.claude/rules/release-please.md— version management and changelog automation.claude/rules/conventional-commits.md— the commit/PR-title format that drives release-please.claude/rules/skill-consolidation.md— merging or deleting skills (distinct from the plugin-level checklist here)/docs-refresh— repairs catalog count drift after a skill or plugin lands