Module 5: Plugins — Creating and Distributing
3. Creating a Plugin from a Scaffold — Structure, Manifest, and Local Testing
3. Creating a Plugin from a Scaffold — Structure, Manifest, and Local Testing
Description
You understand the anatomy of a plugin. Now you build it. In this capsule you create a plugin from scratch — from npm init to verifying that its agents load correctly in a Claude Code session. You're not going to publish it yet (that's capsule 04). First you create it, structure it, and test it locally.
The process follows a natural sequence: initialize the npm package, create the directory structure, write the manifest with the correct fields, create the agent files designed for distribution, add skills, install locally with claude plugins add ./, and verify that everything works. Each step has its verification — you don't move to the next until the current one works.
By the end you'll have a functional plugin installed locally, with its agents available in your Claude Code session and its skill preloaded. Ready to publish in capsule 04.
⚠️ EXPERIMENTAL FEATURE
The
claude plugins addcommands and the automatic loading of plugin components reflect the functionality available as of March 2026. If the commands change, the principles (create an npm package with a standard structure, test locally before publishing) hold.Last check: March 2026
Step 1: Initialize the Package
Create the directory and the package.json
mkdir -p ~/plugins-workshop/my-quality-plugin
cd ~/plugins-workshop/my-quality-plugin
npm init -y
npm init -y generates a basic package.json. Now adapt it for a plugin:
{
"name": "@your-org/code-quality-plugin",
"version": "1.0.0",
"description": "Code quality agents: reviewer and implementer with team conventions",
"claudeCodePlugin": true,
"files": [
"agents",
"skills"
],
"keywords": [
"claude-code",
"plugin",
"code-quality",
"reviewer"
],
"author": "Your Name",
"license": "MIT"
}
Verify the manifest
cat package.json | grep claudeCodePlugin
If you see "claudeCodePlugin": true, the manifest is correct.
Fields you can omit
The package.json generated by npm init -y includes fields a plugin doesn't need:
{
"main": "index.js", // ← Not needed (it's not a JS library)
"scripts": {
"test": "echo ..." // ← Optional (useful for CI but not required)
}
}
You can remove them or leave them — they don't affect the plugin's operation.
Step 2: Create the Directory Structure
mkdir -p agents skills
Resulting structure:
my-quality-plugin/
├── package.json
├── agents/ ← Empty for now
└── skills/ ← Empty for now
Verify
ls -la
You should see package.json, agents/, and skills/.
Step 3: Create the First Agent File
The reviewer agent
Create agents/reviewer.md — a read-only agent that analyzes code without modifying it:
---
name: reviewer
description: Reviews code for quality, security issues, and convention compliance. Read-only agent that produces structured reports with severity levels.
tools: Read, Glob, Grep
model: sonnet
maxTurns: 20
---
## Role
You are a code reviewer. You analyze code for quality issues,
security vulnerabilities, and convention compliance. You NEVER
modify files — you only read and report.
## What You Review
1. **Code Quality** — Duplication, complexity, naming, error handling
2. **Security** — Hardcoded secrets, injection, unvalidated input
3. **Conventions** — Read CLAUDE.md, check file organization and naming
## Process
1. Read CLAUDE.md for project conventions
2. Use Glob to discover project structure
3. Analyze each file against quality, security, and convention criteria
4. Produce structured report
## Output Format
### Code Review Report
**Scope:** [directories/files reviewed]
**Issues found:** [critical: N, warning: N, info: N]
#### Critical Issues
- **[file:line]** — [issue] — Risk: [risk] — Fix: [action]
#### Warnings
- **[file:line]** — [issue] — Recommendation: [suggestion]
#### Info
- **[file:line]** — [observation]
**Overall Assessment:** PASS | NEEDS_WORK | CRITICAL_ISSUES
Why this agent file is plugin-ready
- No hardcoded paths — It discovers the structure with Glob and reads CLAUDE.md
- Not framework-specific — It works with Python, JavaScript, Go, any language
- Read-only — Only
Read, Glob, Grepin tools, never modifies files - Structured output — Consistent format any team can use
- Self-contained — It doesn't reference other agent files or external files
Step 4: Create the Second Agent File
The implementer agent
Create agents/implementer.md — an agent that implements changes following conventions:
---
name: implementer
description: Implements code changes following project conventions. Reports all changes with rationale.
tools: Read, Write, Edit, Glob, Grep, Bash
model: sonnet
maxTurns: 25
---
## Role
You implement code changes following project conventions.
You report every change with rationale.
## Before Implementing
1. Read CLAUDE.md for project conventions
2. Examine existing code patterns in the relevant directory
3. Check for existing utilities to reuse
## Standards
1. Follow existing patterns — match surrounding code style
2. One responsibility per function
3. Error handling for every operation that can fail
4. Type safety where the project uses it
5. No dead code — no commented-out code or unused imports
## Output Format
### Implementation Report
**Task:** [description]
**Status:** DONE | PARTIAL | BLOCKED
**Files created:** [path] — [purpose]
**Files modified:** [path] — [what and why]
**Decisions made:** [decision] — [rationale]
Step 5: Create the Skill File
api-conventions skill
Create skills/api-conventions.md — a domain knowledge file that agents consume as context:
---
name: api-conventions
description: Team API conventions covering endpoint design, response format, error handling, and authentication patterns.
---
## Endpoint Design
- URLs: kebab-case (`/user-profiles`), plural nouns (`/users`)
- Nested resources: `/users/{id}/posts`
- HTTP methods: GET (read), POST (create→201), PUT (full update), PATCH (partial), DELETE (→204)
## Response Format
- Success: `{ "data": {...}, "meta": { "timestamp": "...", "request_id": "..." } }`
- Error: `{ "error": { "code": "VALIDATION_ERROR", "message": "...", "details": [...] } }`
- Status codes: 200/201/204 for success, 400/401/403/404/422/500 for errors
## Authentication
- Bearer token in Authorization header (JWT)
- Claims: sub, role, exp, iat
## Pagination
- Cursor-based: `?cursor=<opaque>&limit=20` (max 100)
- Response includes `next_cursor` in meta
## Validation
- Validate all input at the API boundary with schema validation
- Return 400 with field-level error details
Step 6: Verify the Structure
find . -type f | head -20
You should see:
./package.json
./agents/reviewer.md
./agents/implementer.md
./skills/api-conventions.md
Structure checklist
✅ package.json with claudeCodePlugin: true
✅ package.json with files: ["agents", "skills"]
✅ agents/reviewer.md with valid YAML frontmatter
✅ agents/implementer.md with valid YAML frontmatter
✅ skills/api-conventions.md with valid YAML frontmatter
✅ No hardcoded paths in the agent files
✅ No specific-project dependencies
Step 7: Install Locally
The local installation command
cd ~/your-test-project
claude plugins add ~/plugins-workshop/my-quality-plugin
This command tells Claude Code: "load this plugin from a local path." It's not published to any registry — it's a direct installation from the filesystem.
Verify the installation
claude plugins list
You should see your plugin listed:
Installed plugins:
@your-org/code-quality-plugin (1.0.0) — local
agents: reviewer, implementer
skills: api-conventions
Verify that the agents are available
Start a Claude Code session:
claude
Inside the session, verify:
/agents
You should see reviewer and implementer in the list of available agents, marked as coming from the plugin.
Step 8: Test the Plugin's Agents
Testing the reviewer
In the Claude Code session:
Use the reviewer agent to analyze this project.
Focus on the most recently modified files.
What you expect: The reviewer reads CLAUDE.md, discovers the structure, analyzes files, and produces a report with the format defined in its agent file.
What you verify:
- ✅ The reviewer is invoked correctly
- ✅ It reads CLAUDE.md for context
- ✅ It produces the defined report format
- ✅ It doesn't try to modify files (only Read, Glob, Grep)
Testing the implementer
Use the implementer agent to create a helper function
that validates email format. Place it where it makes sense
according to the project structure.
What you expect: The implementer reads the structure, identifies the correct directory, creates the file with project conventions, and reports what it did.
What you verify:
- ✅ The implementer is invoked correctly
- ✅ It reads the project before implementing
- ✅ It follows the existing conventions
- ✅ It produces the defined report format
Testing the skill
Use the reviewer agent to analyze this project's API endpoints
against the team's conventions.
What you expect: The reviewer applies the api-conventions conventions (kebab-case, response format, status codes) when analyzing the endpoints.
What you verify:
- ✅ The reviewer mentions the skill's conventions (kebab-case, response format)
- ✅ The skill's rules are applied in the review
- ✅ The report references the team's specific conventions
Step 9: Iterate on the Plugin
Local development cycle
1. Edit the agent file in ~/plugins-workshop/my-quality-plugin/agents/
2. Reinstall: claude plugins add ~/plugins-workshop/my-quality-plugin
3. Test in a new Claude Code session
4. Repeat until satisfied
Common adjustments after the first test
If the reviewer is too verbose:
## Output Rules
- Maximum 5 critical issues, 10 warnings, 5 info
- Each issue description: maximum 2 lines
- Skip issues with severity below WARNING unless asked
If the implementer doesn't follow the project conventions:
## MANDATORY First Steps
1. Read CLAUDE.md COMPLETELY before any implementation
2. List at least 3 conventions found in CLAUDE.md
3. Read 2 existing files similar to what you'll create
4. Match their patterns EXACTLY
If the skill is too generic:
Add specific sections to the skill with more concrete rules. Skills can be as detailed as you need — there's no length limit.
Comparison: Local Plugin vs Published Plugin
| Aspect | Local plugin (./path) | Published plugin (registry) |
|---|---|---|
| Installation | claude plugins add ./path | claude plugins add @org/name |
| Update | Reinstall from path | Automatic npm update |
| Distribution | Share the directory | npm install from registry |
| Versioning | Manual (you change files directly) | Semver in package.json |
| Ideal use | Development and testing | Distribution to a team |
| Dependencies | The path must exist on the machine | Accessible registry |
When to stick with local
- You're iterating quickly on the agent files
- Only you use the plugin
- You don't have access to an npm registry
- It's a prototype that may change drastically
When to publish
- The team needs access
- You want formal versioning
- You need reproducibility across machines
- The plugin is stable and tested
Manual Alternative: Setup Script
If claude plugins add isn't available, create an installation script:
#!/bin/bash
# install-quality-plugin.sh
PLUGIN_DIR="$(dirname "$0")"
TARGET="${1:-.}"
echo "Installing code quality plugin to $TARGET"
mkdir -p "$TARGET/.claude/agents"
mkdir -p "$TARGET/.claude/skills"
cp "$PLUGIN_DIR/agents/"*.md "$TARGET/.claude/agents/"
cp "$PLUGIN_DIR/skills/"*.md "$TARGET/.claude/skills/"
echo "Installed:"
echo " Agents: $(ls "$TARGET/.claude/agents/"*.md | wc -l) files"
echo " Skills: $(ls "$TARGET/.claude/skills/"*.md | wc -l) files"
echo ""
echo "Done. Start Claude Code to use the new agents."
Usage:
bash ~/plugins-workshop/my-quality-plugin/install-quality-plugin.sh ~/my-project
It's not a formal plugin, but it fulfills the basic distribution function. You lose automatic versioning and dynamic loading, but you gain immediate portability.
Exercises
Exercise 1: Minimal viable plugin (Easy)
Create a plugin with a single agent file that does basic linting (checks naming conventions) and nothing else. Install it locally and verify that the agent appears available.
See solution
mkdir -p ~/mini-plugin/agents
cd ~/mini-plugin
package.json:
{
"name": "mini-lint-plugin",
"version": "1.0.0",
"claudeCodePlugin": true,
"files": ["agents"]
}
agents/linter.md:
---
name: linter
description: Checks naming conventions across the project. Read-only.
tools: Read, Glob, Grep
model: haiku
maxTurns: 10
---
## Role
Check file names and variable names follow conventions.
## Rules
- Files: kebab-case (my-component.tsx, user-service.py)
- Functions: camelCase (JS/TS) or snake_case (Python)
- Classes: PascalCase
- Constants: UPPER_SNAKE_CASE
## Output
List files/functions that violate conventions with recommended names.
cd ~/your-project
claude plugins add ~/mini-plugin
claude # start session
# inside: /agents → verify that "linter" appears
Exercise 2: Add a third agent (Easy)
To the quality plugin you created in this capsule, add a third agent file: test-writer.md that generates unit tests based on the existing code. Reinstall and verify.
See solution
agents/test-writer.md:
---
name: test-writer
description: Generates unit tests for existing code. Reads source files and creates corresponding test files.
tools: Read, Write, Glob, Grep, Bash
model: sonnet
maxTurns: 20
---
## Role
Write unit tests for existing functions and classes.
## Process
1. Read CLAUDE.md for testing conventions
2. Discover test framework (pytest, jest, vitest, etc.)
3. Read the source file to test
4. Create test file following project structure
5. Write tests covering: happy path, edge cases, error cases
## Standards
- One test file per source file
- Follow existing test patterns in the project
- Descriptive test names: test_[function]_[scenario]_[expected]
- No mocking unless necessary
## Output
**Source:** [file tested]
**Test file:** [created test file]
**Tests written:** [count]
**Coverage:** [functions/methods covered]
Update the package.json files (it already includes "agents", no change needed).
claude plugins add ~/plugins-workshop/my-quality-plugin
Exercise 3: Multi-section skill (Medium)
Create a skill file python-standards.md covering: naming conventions, import ordering, type hints, docstrings, and error handling — all specific to Python. Add it to the plugin and verify that the reviewer uses it when analyzing Python code.
See solution
skills/python-standards.md:
---
name: python-standards
description: Python coding standards covering naming, imports, type hints, docstrings, and error handling.
---
## Naming
- Functions/variables: snake_case
- Classes: PascalCase
- Constants: UPPER_SNAKE_CASE
- Private: _prefixed
- Dunder methods: __name__
## Import Ordering
1. Standard library (os, sys, pathlib)
2. Third-party (fastapi, pydantic, sqlalchemy)
3. Local imports (from . import, from app import)
Separate each group with a blank line. Use isort.
## Type Hints
- All function parameters: typed
- All return values: typed (use -> None explicitly)
- Use Optional[X] for nullable, not X | None (for 3.9 compat)
- Complex types: define TypeAlias
## Docstrings
- Google style: Args, Returns, Raises sections
- All public functions must have docstrings
- One-line docstrings for obvious helpers
## Error Handling
- Catch specific exceptions, never bare except
- Custom exceptions inherit from app base exception
- Always log before re-raising
- Use contextlib.suppress for intentional ignoring
Verify: claude plugins add ./ → open a session → ask the reviewer to analyze a Python file → it should mention these conventions.
Exercise 4: Plugin with an incorrect structure — diagnosis (Medium)
This plugin doesn't work. Identify all the errors without running anything:
broken-plugin/
├── package.json
├── agent/ ← note: singular
│ └── reviewer.md
├── skill/ ← note: singular
│ └── conventions.md
└── src/
└── helpers.js
{
"name": "@team/broken plugin",
"version": "1",
"claudeCodePlugin": "true",
"files": ["agent", "skill", "src"]
}
reviewer.md:
name: reviewer
description: Reviews code
You are a reviewer. Read and analyze code.
See solution
Errors found:
agent/singular → It must beagents/(plugin convention)skill/singular → It must beskills/(plugin convention)"name": "@team/broken plugin"→ Space in the name not valid →"@team/broken-plugin""version": "1"→ Semver requires 3 numbers →"1.0.0""claudeCodePlugin": "true"→ It's a string, must be a boolean →truewithout quotes"files": ["agent", "skill", "src"]→ Incorrect directories andsrcisn't a plugin component →["agents", "skills"]- reviewer.md without YAML frontmatter → Missing the
---delimiter block - reviewer.md without
toolsfield → The agent has no tools defined src/helpers.js→ Not a valid plugin component, shouldn't be distributed
Corrected version:
Rename the directories, fix package.json, add frontmatter to reviewer.md, remove src/.
Exercise 5: Plugin with coordinated reviewer + implementer (Hard)
Modify the plugin's agent files so they work as a flow: the reviewer produces an "Actionable Fixes" section (a table with File, Line, Current, Required, Convention), and the implementer has a "When Receiving a Review Report" section that parses that table and applies fixes in priority order (Critical → Warning).
See solution
Add to the reviewer:
### Actionable Fixes
| File | Line | Current | Required | Convention |
Add to the implementer:
## When Receiving a Review Report
1. Parse "Actionable Fixes" table
2. Prioritize: CRITICAL first, then WARNING
3. For each: read file, apply fix per convention, verify no breaks
4. Report each fix with before/after
Flow: "Use the reviewer to analyze src/" → "Use the implementer to fix these problems: [paste review]"
Exercise 6: Create a professional README.md (Hard)
Write a README.md for the plugin including: description, installation (claude plugins add and local path), agents (capabilities + usage example), skills, workflow (review → fix cycle), and requirements. Maximum 50 lines.
See solution
Include: title, one-liner description, Installation section with both methods, Agents section with a usage example per agent, Skills section with a description, Workflow section with the review→fix cycle, and Requirements (Claude Code + Node.js 18+).
Troubleshooting
Problem 1: "claude plugins add ./path gives a path error"
Symptom: An error indicating the path isn't a valid plugin.
Solution:
- Use an absolute path:
claude plugins add /Users/you/plugins-workshop/my-plugin - Verify that
package.jsonexists at the root of the path - Verify that
claudeCodePlugin: trueis present (not as a string"true")
Problem 2: "The plugin's agent doesn't receive the skill's context"
Symptom: The reviewer doesn't apply the api-conventions conventions when analyzing.
Solution:
- Verify that
skills/is in the package.jsonfilesfield - Verify that the skill has YAML frontmatter with
nameanddescription - In the agent file, explicitly reference the skill in the system prompt
Problem 3: "After editing an agent file, the changes aren't reflected"
Symptom: You edited agents/reviewer.md but Claude Code uses the previous version.
Solution:
- Reinstall the plugin:
claude plugins add ./path(overwrites the previous version) - Start a new Claude Code session (plugins load at startup)
- Verify the reinstallation:
claude plugins list
Summary
- The process is linear: init → structure → agents → skills → local install → test → iterate
npm init -y+claudeCodePlugin: trueis all you need for the basic manifest- Plugin agent files must be generic — no hardcoded paths, they discover the project via Glob and CLAUDE.md
claude plugins add ./pathinstalls from a local directory — ideal for development- Testing before publishing is mandatory — verify that the agents load, that the skills preload, and that the reports have the expected format
- The development cycle is: edit → reinstall → new session → test → repeat
- A local plugin works identically to a published one — the difference is only the distribution mechanism
- The manual alternative (setup script + copy) works when
claude pluginsisn't available
Additional Resources
- Claude Code Sub-Agents (Anthropic Docs) — Agent files and YAML frontmatter
- Create Custom Subagents — Agent file reference
- Claude Code CLI Reference — Plugin commands
- npm init Documentation — npm package initialization
- npm package.json Files Field — Controlling what gets distributed
- Claude Code Best Practices — Best practices for agent files
- Semantic Versioning — Plugin versioning
- Claude Code Settings — Configuration and permissions
Next capsule: In capsule 04 you'll learn how Claude Code loads plugins dynamically at the start of a session, how version pinning works with semver, how to publish to an npm registry (local and remote), and how to handle updates and breaking changes. Your local plugin becomes a distributable package.