This guide covers developing custom plugins for the unleash repository, which is a unified CLI wrapper for AI code agents.
Plugins extend Claude Code with custom commands, agents, skills, hooks, and MCP (Model Context Protocol) integrations. The plugin-first approach ensures minimal merge conflicts when syncing with upstream Claude Code updates.
- Plugin Architecture
- Directory Structure
- Component Types
- Step-by-Step Plugin Creation
- Testing Plugins Locally
- Example Plugin Structure
- Commit and PR Process
Claude Code plugins are self-contained extensions that follow a standardized structure:
- Automatic Discovery: Components are discovered automatically based on directory structure
- Portable Paths: Use
${CLAUDE_PLUGIN_ROOT}for all internal plugin references - Manifest-Driven: Plugin metadata defined in
.claude-plugin/plugin.json - Component-Based: Mix and match commands, agents, skills, hooks, and MCP servers
- Hot-Loadable: Plugins can be enabled/disabled without rebuilding
The unleash repository maintains a plugin-first philosophy:
- Minimal Conflicts: Plugins live in isolated directories, avoiding merge conflicts with upstream
- Easy Updates: Sync with upstream Claude Code without breaking custom functionality
- Modular Design: Enable/disable features without modifying core code
- Shareable: Plugins can be distributed independently
- Maintainable: Clear separation between core and extensions
Every plugin follows this structure:
plugin-name/
├── .claude-plugin/
│ └── plugin.json # Required: Plugin manifest
├── commands/ # Optional: Slash commands
│ └── my-command.md
├── agents/ # Optional: Specialized agents
│ └── my-agent.md
├── skills/ # Optional: Auto-activating skills
│ └── skill-name/
│ └── SKILL.md
├── hooks/ # Optional: Event handlers
│ ├── hooks.json
│ └── scripts/
│ └── validate.sh
├── .mcp.json # Optional: MCP server config
├── scripts/ # Optional: Utility scripts
└── README.md # Required: Plugin documentation
- Manifest Location: The
plugin.jsonMUST be in.claude-plugin/directory - Component Locations: All component directories (commands, agents, skills, hooks) MUST be at plugin root level
- Optional Components: Only create directories for components you actually use
- Naming Convention: Use kebab-case for all directory and file names
hello-world/
├── .claude-plugin/
│ └── plugin.json
└── commands/
└── hello.md
database-toolkit/
├── .claude-plugin/
│ └── plugin.json
├── commands/
│ ├── query.md
│ └── migrate.md
├── agents/
│ └── schema-designer.md
├── skills/
│ ├── query-optimization/
│ │ └── SKILL.md
│ └── migration-patterns/
│ └── SKILL.md
├── hooks/
│ ├── hooks.json
│ └── scripts/
│ └── validate-query.sh
├── .mcp.json
└── README.md
Slash commands that users invoke explicitly.
Location: commands/ directory
Format: Markdown files with YAML frontmatter
Usage: /command-name [args]
Example (commands/review.md):
---
name: review
description: Review code changes in current branch
argument-hint: "[--detailed]"
allowed-tools: ["Read", "Bash", "Grep"]
---
# Code Review Command
Review all code changes in the current git branch.
## Instructions
1. Use `git diff main...HEAD` to see all changes
2. Analyze each file for:
- Code quality issues
- Security vulnerabilities
- Best practice violations
- Performance concerns
3. Provide structured feedback
4. Suggest improvements
## Optional Arguments
- `--detailed`: Provide line-by-line analysisFrontmatter Fields:
name(required): Command name (use kebab-case)description(required): Brief description shown in helpargument-hint(optional): Shows expected argumentsallowed-tools(optional): Restrict which tools Claude can use
Specialized AI agents for specific tasks.
Location: agents/ directory
Format: Markdown files with YAML frontmatter
Usage: Auto-invoked by Claude or manually called
Example (agents/test-generator.md):
---
name: Test Generator
description: |
This agent specializes in creating comprehensive test suites. Use when:
<example>the user asks to "write tests" or "add test coverage"</example>
<example>implementing a new feature that needs tests</example>
<example>refactoring code and ensuring no regressions</example>
model: claude-sonnet-4-5-20250929
color: green
allowed-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
---
# Test Generation Expert
You are an expert at writing comprehensive, maintainable test suites.
## Your Responsibilities
1. **Analyze Code**: Understand the code being tested
2. **Identify Test Cases**: Consider edge cases, error conditions, and happy paths
3. **Write Tests**: Create clear, maintainable tests following project conventions
4. **Coverage**: Ensure comprehensive coverage of functionality
## Test Patterns
- Use descriptive test names
- Follow AAA pattern (Arrange, Act, Assert)
- Test one thing per test
- Include both positive and negative cases
- Mock external dependencies appropriately
## Testing Frameworks
Detect and use the project's testing framework:
- JavaScript: Jest, Mocha, Vitest
- Python: pytest, unittest
- TypeScript: Jest, Vitest
- Go: testing package
Always check existing tests to match the project's style and conventions.Frontmatter Fields:
name(optional): Agent display namedescription(required): When to use this agent (include<example>blocks)model(optional): Specific Claude model to usecolor(optional): UI color hintallowed-tools(optional): Restrict available tools
Description Best Practices:
- Include
<example>blocks with trigger phrases - Be specific about when to use the agent
- Describe capabilities clearly
Auto-activating knowledge modules triggered by context.
Location: skills/skill-name/SKILL.md
Format: Markdown with YAML frontmatter in subdirectory
Usage: Automatically loaded when description matches task context
Example (skills/api-design/SKILL.md):
---
name: API Design
description: This skill should be used when the user asks to "design an API", "create REST endpoints", "build GraphQL schema", or needs guidance on API architecture, versioning, authentication patterns, or API documentation.
version: 1.0.0
---
# API Design Best Practices
## Overview
Comprehensive guidance for designing robust, scalable APIs.
## REST API Design
### Resource Naming
- Use nouns, not verbs: `/users`, not `/getUsers`
- Use plural forms: `/users`, not `/user`
- Nest resources logically: `/users/123/orders`
- Avoid deep nesting (max 2-3 levels)
### HTTP Methods
- `GET`: Retrieve resources (idempotent, cacheable)
- `POST`: Create resources (non-idempotent)
- `PUT`: Update entire resource (idempotent)
- `PATCH`: Partial update (idempotent)
- `DELETE`: Remove resource (idempotent)
### Status Codes
- `200 OK`: Successful GET, PUT, PATCH
- `201 Created`: Successful POST
- `204 No Content`: Successful DELETE
- `400 Bad Request`: Invalid client data
- `401 Unauthorized`: Authentication required
- `403 Forbidden`: Authenticated but not authorized
- `404 Not Found`: Resource doesn't exist
- `500 Internal Server Error`: Server error
### Versioning Strategies
1. **URL versioning**: `/v1/users` (recommended for simplicity)
2. **Header versioning**: `Accept: application/vnd.api.v1+json`
3. **Query parameter**: `/users?version=1` (not recommended)
## Authentication Patterns
### JWT (JSON Web Tokens)Authorization: Bearer
- Stateless, scalable
- Include only public claims
- Set appropriate expiration
- Use HTTPS only
### API Keys
X-API-Key:
- Simple for service-to-service
- Rotate regularly
- Scope permissions appropriately
### OAuth 2.0
- For third-party access
- Use authorization code flow for web apps
- Use PKCE for mobile/SPAs
- Implement proper scope management
## Documentation
Always document:
- Available endpoints and methods
- Request/response schemas
- Authentication requirements
- Rate limits
- Error responses
- Example requests
Consider using:
- OpenAPI/Swagger specification
- Postman collections
- API Blueprint
Supporting Files:
Skills can include additional resources:
skills/api-design/
├── SKILL.md
├── references/
│ ├── openapi-template.yaml
│ └── authentication-guide.md
├── examples/
│ └── rest-api-example.md
└── scripts/
└── validate-openapi.sh
Frontmatter Fields:
name(required): Skill namedescription(required): When to activate (include trigger phrases)version(optional): Semantic version
Event-driven automation that executes during Claude Code lifecycle.
Location: hooks/hooks.json and hooks/scripts/
Format: JSON configuration + executable scripts
Usage: Automatically triggered by events
Example (hooks/hooks.json):
{
"description": "Security validation hooks",
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "prompt",
"prompt": "Check if this file write involves sensitive data like API keys, passwords, or credentials. Return 'approve' if safe, 'deny' if dangerous.",
"timeout": 30
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate-bash.sh",
"timeout": 10
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "Verify the task is complete: tests have run, build succeeded, all questions answered. Return 'approve' to allow stopping or 'block' with reason to continue working."
}
]
}
],
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/load-context.sh",
"timeout": 15
}
]
}
]
}
}Hook Types:
- Prompt-Based Hooks (recommended for complex logic):
{
"type": "prompt",
"prompt": "Evaluate this action and decide approve/deny/ask",
"timeout": 30
}- Command Hooks (for deterministic checks):
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/check.sh",
"timeout": 60
}Available Events:
PreToolUse: Before tool execution (validation)PostToolUse: After tool completes (feedback)Stop: When Claude wants to stop (completeness check)SubagentStop: When subagent completesUserPromptSubmit: When user submits inputSessionStart: Session begins (context loading)SessionEnd: Session ends (cleanup)PreCompact: Before context compactionNotification: When notifications sent
Example Hook Script (hooks/scripts/validate-bash.sh):
#!/bin/bash
set -euo pipefail
# Read input JSON from stdin
input=$(cat)
# Extract command
command=$(echo "$input" | jq -r '.tool_input.command // ""')
# Check for dangerous patterns
if [[ "$command" =~ rm[[:space:]]+-rf[[:space:]]+/ ]] || \
[[ "$command" =~ dd[[:space:]]+if= ]] || \
[[ "$command" =~ mkfs ]] || \
[[ "$command" =~ :(){:\|:}; ]]; then
# Block dangerous command
cat <<EOF >&2
{
"decision": "deny",
"reason": "Dangerous command detected: $command",
"systemMessage": "This command could cause data loss. Please verify the exact parameters."
}
EOF
exit 2
fi
# Allow command
cat <<EOF
{
"decision": "approve",
"systemMessage": "Command validated"
}
EOF
exit 0Model Context Protocol servers for external tool integration.
Location: .mcp.json at plugin root
Format: JSON configuration
Usage: Automatically started when plugin loads
Example (.mcp.json):
{
"mcpServers": {
"database": {
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/servers/database-server.js"],
"env": {
"DB_HOST": "${DB_HOST}",
"DB_PORT": "${DB_PORT}",
"DB_NAME": "${DB_NAME}",
"DB_USER": "${DB_USER}",
"DB_PASSWORD": "${DB_PASSWORD}"
}
},
"slack": {
"command": "python",
"args": ["-m", "slack_mcp_server"],
"env": {
"SLACK_TOKEN": "${SLACK_TOKEN}"
}
}
}
}Server Types:
- stdio: Local process communication
- SSE: Server-Sent Events (for hosted/OAuth)
- HTTP: REST-based integration
- WebSocket: Real-time communication
- Define Purpose: What problem does this plugin solve?
- Identify Components: Which component types do you need?
- Commands: User-invoked actions
- Agents: Specialized AI assistance
- Skills: Auto-activating knowledge
- Hooks: Automation and validation
- MCP: External integrations
- Design Structure: Map out directory layout
- List Dependencies: External tools, APIs, credentials
- Create Plugin Directory:
cd plugins/bundled
mkdir my-plugin
cd my-plugin- Create Manifest:
mkdir .claude-plugin
cat > .claude-plugin/plugin.json <<'EOF'
{
"name": "my-plugin",
"version": "1.0.0",
"description": "Brief description of plugin functionality",
"author": {
"name": "Your Name",
"email": "you@example.com"
}
}
EOF- Create Component Directories (as needed):
mkdir -p commands
mkdir -p agents
mkdir -p skills
mkdir -p hooks/scripts
mkdir -p scripts- Create Command File:
cat > commands/my-command.md <<'EOF'
---
name: my-command
description: Does something useful
---
# My Command Implementation
Instructions for Claude on how to execute this command...
EOF- Test Command:
cc --plugin-dir plugins/bundled/my-plugin
# In Claude: /my-command- Create Agent File:
cat > agents/my-agent.md <<'EOF'
---
name: My Agent
description: |
This agent does X. Use when:
<example>user asks to "do X"</example>
<example>implementing feature Y</example>
model: claude-sonnet-4-5-20250929
---
# Agent System Prompt
You are an expert at X...
EOF- Create Skill Directory:
mkdir -p skills/my-skill- Create SKILL.md:
cat > skills/my-skill/SKILL.md <<'EOF'
---
name: My Skill
description: This skill should be used when the user asks to "X" or "Y"
version: 1.0.0
---
# Skill Knowledge
Detailed information about this skill...
EOF- Create hooks.json:
cat > hooks/hooks.json <<'EOF'
{
"description": "My plugin hooks",
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate.sh"
}
]
}
]
}
}
EOF- Create Hook Script:
cat > hooks/scripts/validate.sh <<'EOF'
#!/bin/bash
set -euo pipefail
input=$(cat)
# Validation logic here
echo '{"decision": "approve"}'
exit 0
EOF
chmod +x hooks/scripts/validate.shCreate comprehensive README.md:
# My Plugin
Brief description of what this plugin does.
## Features
- Feature 1
- Feature 2
- Feature 3
## Installation
Install from local development:
```bash
cc --plugin-dir /path/to/my-pluginDescription of command and usage examples.
Description of agent capabilities and when it's invoked.
Description of skill knowledge and trigger conditions.
Environment variables or settings needed:
MY_VAR: Description
Common usage examples with expected output.
Common issues and solutions.
### Phase 5: Testing
See [Testing Plugins Locally](#testing-plugins-locally) section.
## Testing Plugins Locally
### Local Testing Workflow
1. **Use --plugin-dir Flag**:
```bash
cc --plugin-dir plugins/bundled/my-plugin
This loads only your plugin for isolated testing.
- Test Commands:
# In Claude Code session
/my-command arg1 arg2
- Test Agents:
# Trigger agent by matching description
"Can you help me with [agent's domain]?"
- Test Skills:
# Skills auto-activate based on context
"I need to [trigger phrase from skill description]"
- Test Hooks with Debug Mode:
cc --plugin-dir /path/to/plugin --debugLook for hook execution logs in output.
- Plugin manifest is valid JSON
- All component files have valid YAML frontmatter
- Commands execute without errors
- Agents trigger correctly
- Skills activate on expected triggers
- Hooks validate/execute as expected
- All scripts are executable (
chmod +x) - ${CLAUDE_PLUGIN_ROOT} used for all internal paths
- README.md is comprehensive
- No hardcoded absolute paths
Load multiple plugins for integration testing:
cc --plugin-dir /path/to/plugin1 --plugin-dir /path/to/plugin2Enable detailed logging:
cc --plugin-dir /path/to/plugin --debugLook for:
- Plugin loading messages
- Component registration
- Hook execution logs
- Error messages
- Tool invocations
Test hook scripts independently:
echo '{"tool_name": "Write", "tool_input": {"file_path": "/test.txt"}}' | \
bash hooks/scripts/validate.sh
echo "Exit code: $?"Test plugin in realistic scenarios:
- Create test project
- Load plugin
- Perform realistic tasks
- Verify expected behavior
- Check for errors or warnings
Here's a complete example: github-issue-toolkit
github-issue-toolkit/
├── .claude-plugin/
│ └── plugin.json
├── commands/
│ ├── create-issue.md
│ ├── list-issues.md
│ └── close-issue.md
├── agents/
│ ├── issue-triager.md
│ └── issue-analyzer.md
├── skills/
│ ├── issue-templates/
│ │ ├── SKILL.md
│ │ └── templates/
│ │ ├── bug-report.md
│ │ └── feature-request.md
│ └── issue-automation/
│ └── SKILL.md
├── hooks/
│ ├── hooks.json
│ └── scripts/
│ └── validate-issue.sh
├── .mcp.json
├── scripts/
│ └── common.sh
└── README.md
.claude-plugin/plugin.json:
{
"name": "github-issue-toolkit",
"version": "1.0.0",
"description": "Comprehensive GitHub issue management toolkit",
"author": {
"name": "Plugin Developer",
"email": "dev@example.com"
},
"keywords": ["github", "issues", "automation"]
}commands/create-issue.md:
---
name: create-issue
description: Create a new GitHub issue
argument-hint: "[title] [--body TEXT] [--labels LABELS]"
allowed-tools: ["Bash"]
---
# Create GitHub Issue
Create a new issue in the current repository.
## Usage
/create-issue "Bug in login" --body "Description here" --labels "bug,urgent"
## Implementation
1. Parse arguments
2. Use `gh issue create` command
3. Set title, body, labels
4. Return issue URL
agents/issue-triager.md:
---
name: Issue Triager
description: |
Analyzes GitHub issues for triage. Use when:
<example>user asks to "triage issues"</example>
<example>analyzing issue priority</example>
<example>categorizing issues</example>
model: claude-sonnet-4-5-20250929
color: blue
---
# Issue Triage Expert
Analyze issues and provide triage recommendations.
## Analysis Process
1. **Fetch Issue**: Get issue details with `gh issue view`
2. **Analyze Content**: Review title, body, comments
3. **Determine Priority**: Based on severity, impact, frequency
4. **Suggest Labels**: Appropriate categorization
5. **Recommend Assignment**: Suggest who should handle it
## Priority Levels
- **Critical**: System down, data loss, security issue
- **High**: Major feature broken, significant impact
- **Medium**: Minor bugs, feature requests
- **Low**: Nice-to-have, documentation, cleanupskills/issue-templates/SKILL.md:
---
name: Issue Templates
description: This skill should be used when the user asks to "create an issue template", "standardize issue format", or needs guidance on GitHub issue templates and best practices.
version: 1.0.0
---
# GitHub Issue Template Best Practices
## Bug Report Template
```markdown
## Description
A clear description of the bug.
## Steps to Reproduce
1. Step one
2. Step two
3. Step three
## Expected Behavior
What should happen.
## Actual Behavior
What actually happens.
## Environment
- OS:
- Browser:
- Version:
## Additional Context
Screenshots, logs, etc.## Feature Description
Clear description of proposed feature.
## Use Case
Why is this needed? What problem does it solve?
## Proposed Solution
How should this work?
## Alternatives Considered
Other approaches considered.
## Additional Context
Mockups, examples, etc.
**`hooks/hooks.json`**:
```json
{
"description": "Validate issue operations",
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate-issue.sh",
"timeout": 10
}
]
}
]
}
}
.mcp.json:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}- Use Conventional Commits:
feat: add new issue creation command
fix: correct issue label parsing
docs: update README with usage examples
refactor: simplify hook validation logic
test: add tests for issue triager agent- Commit Scope:
feat(commands): add create-issue command
fix(hooks): correct validation script path
docs(readme): add installation instructions- Commit Message Format:
<type>(<scope>): <subject>
<body>
<footer>
Example:
feat(agents): add issue triager agent
The issue triager agent analyzes GitHub issues and provides
triage recommendations including priority, labels, and
assignment suggestions.
Closes #123
- Create Feature Branch:
cd /home/me/unleash
git checkout -b feature/my-plugin-name- Develop and Test:
# Create plugin in plugins/bundled/
# Test locally
cc --plugin-dir plugins/bundled/my-plugin
# Run any tests
# Verify functionality- Commit Changes:
git add plugins/bundled/my-plugin/
git commit -m "feat(plugins): add my-plugin for X functionality"- Push Branch:
git push -u origin feature/my-plugin-name- Create Pull Request:
Use GitHub CLI or web interface:
gh pr create --title "feat(plugins): add my-plugin" --body "$(cat <<'EOF'
## Summary
- Adds new plugin for X functionality
- Includes commands for Y
- Provides agent for Z
## Components
- **Commands**: /command1, /command2
- **Agents**: agent-name
- **Skills**: skill-name
- **Hooks**: PreToolUse validation
## Testing
- [ ] Tested locally with --plugin-dir
- [ ] All commands work as expected
- [ ] Agents trigger correctly
- [ ] Hooks validate properly
- [ ] Documentation is complete
## Related Issues
Closes #123
EOF
)"Reviewers should verify:
- Plugin follows standard directory structure
- Plugin manifest is complete and valid
- All components have proper frontmatter
- ${CLAUDE_PLUGIN_ROOT} used (no hardcoded paths)
- README.md is comprehensive
- Commands documented with examples
- Agents have clear trigger descriptions
- Skills have focused, specific descriptions
- Hooks include proper error handling
- No sensitive data in code/config
- Plugin tested locally
- Follows naming conventions (kebab-case)
After merge:
- Plugin available in plugins/bundled/
- Users can load with
--plugin-dir - Consider adding to marketplace
- Monitor for issues/feedback
- Iterate based on usage
For existing plugins:
- Increment version in plugin.json
- Document breaking changes
- Update README.md
- Test thoroughly
- Create PR with changelog
Version bump guidelines:
- Patch (1.0.0 → 1.0.1): Bug fixes, minor improvements
- Minor (1.0.0 → 1.1.0): New features, backward compatible
- Major (1.0.0 → 2.0.0): Breaking changes
- ✅ Use plugin-first approach for all extensions
- ✅ Follow standard directory structure
- ✅ Use ${CLAUDE_PLUGIN_ROOT} for portability
- ✅ Write comprehensive README.md
- ✅ Include usage examples
- ✅ Test thoroughly before submitting PR
- ✅ Use conventional commits
- ✅ Document breaking changes
- ✅ Validate JSON configurations
- ✅ Make scripts executable
- ❌ Don't modify core Claude Code files
- ❌ Don't use hardcoded absolute paths
- ❌ Don't commit sensitive credentials
- ❌ Don't skip documentation
- ❌ Don't create monolithic plugins (separate concerns)
- ❌ Don't use global state without documentation
- ❌ Don't assume OS-specific features
- ❌ Don't skip testing
- Testing Guide - Comprehensive testing strategies
- Review existing plugins in
plugins/bundled/ - Ask in project discussions
- Review Claude Code documentation: https://docs.claude.com/claude-code