
Developing Claude Code Plugins
- 103 installs
- 134 repo stars
- Updated December 3, 2025
- obra/superpowers-developing-for-claude-code
Build and test Claude Code plugins with Superpowers-style agent workflows.
About
Developing Claude Code Plugins guides building Claude Code extensions with disciplined agent workflows from OBRA Superpowers for Claude Code repo.
- Claude Code plugin development patterns.
- Superpowers-compatible workflow.
- Test plugins before publishing.
Developing Claude Code Plugins by the numbers
- 103 all-time installs (skills.sh)
- +2 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #4,164 of 16,659 AI & Agent Building skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 26, 2026 (Skillselion catalog sync)
npx skills add https://github.com/obra/superpowers-developing-for-claude-code --skill developing-claude-code-pluginsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 103 |
|---|---|
| repo stars | ★ 134 |
| Security audit | 2 / 3 scanners passed |
| Last updated | December 3, 2025 |
| Repository | obra/superpowers-developing-for-claude-code ↗ |
What it does
Build and test Claude Code plugins with Superpowers-style agent workflows.
Files
Developing Claude Code Plugins
Overview
This skill provides efficient workflows for creating Claude Code plugins. Use it to make plugin development fast and correct - it synthesizes official docs into actionable steps and provides working examples.
When to Use
Use this skill when:
- Creating a new Claude Code plugin from scratch
- Adding components to an existing plugin (skills, commands, hooks, MCP servers)
- Setting up a development marketplace for testing
- Troubleshooting plugin structure issues
- Understanding plugin architecture and patterns
- Releasing a plugin (versioning, tagging, marketplace distribution)
- Publishing updates or maintaining existing plugins
For comprehensive official documentation, use the working-with-claude-code skill to access full docs.
Quick Reference
| Need to... | Read This | Official Docs |
|---|---|---|
| Understand directory structure | references/plugin-structure.md | plugins.md |
| Choose a plugin pattern | references/common-patterns.md | plugins.md |
| Make hooks work cross-platform | references/polyglot-hooks.md | hooks.md |
| Debug plugin issues | references/troubleshooting.md | Various |
| See working examples | examples/ directory | N/A |
Plugin Development Workflow
Phase 1: Plan
Before writing code:
1. Define your plugin's purpose
- What problem does it solve?
- Who will use it?
- What components will it need?
2. Choose your pattern (read references/common-patterns.md)
- Simple plugin with one skill?
- MCP integration with guidance?
- Command collection?
- Full-featured platform?
3. Review examples
examples/simple-greeter-plugin/- Minimal pluginexamples/full-featured-plugin/- All components- Installed plugins in
~/.claude/plugins/
Phase 2: Create Structure
1. Create directories (see references/plugin-structure.md for details):
mkdir -p my-plugin/.claude-plugin
mkdir -p my-plugin/skills
# Add other component directories as needed2. Write plugin.json (required):
{
"name": "my-plugin",
"version": "1.0.0",
"description": "What your plugin does",
"author": {"name": "Your Name"}
}See references/plugin-structure.md for complete format.
3. Create development marketplace (for local testing):
Create .claude-plugin/marketplace.json:
{
"name": "my-dev",
"plugins": [{
"name": "my-plugin",
"source": "./"
}]
}See references/plugin-structure.md for complete format.
Phase 3: Add Components
Use TodoWrite to track component creation:
Example:
- Create skill: main-workflow
- Add command: /hello
- Configure hooks
- Write README
- Test installationFor each component type, see:
- Format/syntax:
references/plugin-structure.md - When to use:
references/common-patterns.md - Working code:
examples/directory
Phase 4: Test Locally
1. Install for testing:
/plugin marketplace add /path/to/my-plugin
/plugin install my-plugin@my-devThen restart Claude Code.
2. Test each component:
- Skills: Ask for tasks matching skill descriptions
- Commands: Run
/your-command - MCP servers: Check tools are available
- Hooks: Trigger relevant events
3. Iterate:
/plugin uninstall my-plugin@my-dev
# Make changes
/plugin install my-plugin@my-dev
# Restart Claude CodePhase 5: Debug and Refine
If something doesn't work, read references/troubleshooting.md for:
- Plugin not loading
- Skill not triggering
- Command not appearing
- MCP server not starting
- Hooks not firing
Common issues are usually:
- Wrong directory structure
- Hardcoded paths (use
${CLAUDE_PLUGIN_ROOT}) - Forgot to restart Claude Code
- Missing executable permissions on scripts
Phase 6: Release and Distribute
1. Write README with:
- What the plugin does
- Installation instructions
- Usage examples
- Component descriptions
2. Version your release using semantic versioning:
- Update
versionin.claude-plugin/plugin.json - Document changes in CHANGELOG.md or RELEASE-NOTES.md
- Example:
"version": "1.2.1"(major.minor.patch)
3. Commit and tag your release:
git add .
git commit -m "Release v1.2.1: [brief description]"
git tag v1.2.1
git push origin main
git push origin v1.2.14. Choose distribution method:
Option A: Direct GitHub distribution
- Users add:
/plugin marketplace add your-org/your-plugin-repo - Your plugin.json serves as the manifest
Option B: Marketplace distribution (recommended for multi-plugin collections)
- Create separate marketplace repository
- Add
.claude-plugin/marketplace.jsonwith plugin references:
{
"name": "my-marketplace",
"owner": {"name": "Your Name"},
"plugins": [{
"name": "your-plugin",
"source": {
"source": "url",
"url": "https://github.com/your-org/your-plugin.git"
},
"version": "1.2.1",
"description": "Plugin description"
}]
}- Users add:
/plugin marketplace add your-org/your-marketplace - Update marketplace manifest for each plugin release
Option C: Private/team distribution
- Configure in team's
.claude/settings.json:
{
"extraKnownMarketplaces": {
"team-tools": {
"source": {"source": "github", "repo": "your-org/plugins"}
}
}
}5. Test the release:
# Test fresh installation
/plugin marketplace add your-marketplace-source
/plugin install your-plugin@marketplace-name
# Verify functionality, then clean up
/plugin uninstall your-plugin@marketplace-name6. Announce and maintain:
- GitHub releases (optional)
- Team notifications
- Monitor for issues and user feedback
- Plan maintenance updates
Critical Rules
Always follow these (from references/plugin-structure.md):
1. `.claude-plugin/` contains ONLY manifests (plugin.json and optionally marketplace.json)
- ❌ Don't put skills, commands, or other components inside
- ✅ Put them at plugin root
2. Use `${CLAUDE_PLUGIN_ROOT}` for all paths in config files
- Makes plugin portable across systems
- Required for hooks, MCP servers, scripts
3. Use relative paths in `plugin.json`
- Start with
./ - Relative to plugin root
4. Make scripts executable
chmod +x script.sh- Required for hooks and MCP servers
Resources in This Skill
- `references/plugin-structure.md` - Directory layout, file formats, component syntax
- `references/common-patterns.md` - When to use each plugin pattern, examples
- `references/polyglot-hooks.md` - Cross-platform hook wrapper for Windows/macOS/Linux
- `references/troubleshooting.md` - Debug guide for common issues
- `examples/simple-greeter-plugin/` - Minimal working plugin (one skill)
- `examples/full-featured-plugin/` - Complete plugin with all components (includes
run-hook.cmd)
Cross-References
For deep dives into official documentation, use the working-with-claude-code skill to access:
plugins.md- Plugin development overviewplugins-reference.md- Complete API referenceskills.md- Skill authoring guideslash-commands.md- Command formathooks.md,hooks-guide.md- Hook systemmcp.md- MCP server integrationplugin-marketplaces.md- Distribution
Best Practices
1. Start simple - Begin with minimal structure, add complexity when needed 2. Test frequently - Install → test → uninstall → modify → repeat 3. Use examples - Copy patterns from working plugins 4. Follow conventions - Match style of existing plugins 5. Document everything - Clear README helps users and future you 6. Version properly - Use semantic versioning (major.minor.patch)
Workflow Summary
Plan → Choose pattern, review examples
Create → Make structure, write manifests
Add → Build components (skills, commands, etc.)
Test → Install via dev marketplace
Debug → Use troubleshooting guide
Release → Version, tag, distribute via marketplace
Maintain → Monitor, update, support usersThe correct path is the fast path. Use references, follow patterns, test frequently.
Common Plugin Patterns
Pattern: Simple Plugin with One Skill
Use when: Creating a focused plugin with documentation/reference material
Structure:
my-plugin/
├── .claude-plugin/
│ ├── plugin.json
│ └── marketplace.json
├── skills/
│ └── my-skill/
│ ├── SKILL.md
│ ├── scripts/ # Optional - Executable helpers
│ └── references/ # Optional - Documentation files
└── README.mdReal example: superpowers-developing-for-claude-code
- Single skill with comprehensive documentation
- Scripts for self-updating
- 40+ reference files
- No MCP servers, commands, or hooks
When to use:
- Teaching Claude about a specific topic/domain
- Providing process workflows (TDD, debugging, code review)
- Bundling documentation for easy reference
- Creating reusable knowledge bases
---
Pattern: MCP Plugin with Skill
Use when: Providing both a tool integration (MCP) and guidance on using it (skill)
Structure:
my-plugin/
├── .claude-plugin/
│ └── plugin.json # Includes mcpServers config
├── skills/
│ └── using-the-tools/
│ └── SKILL.md # How to use the MCP tools
├── mcp/
│ └── dist/
│ └── index.js # MCP server implementation
└── README.mdReal example: superpowers-chrome
- MCP server provides browser control tools
- Skill teaches Claude how to use those tools effectively
- Skill includes patterns, examples, workflows
When to use:
- Adding new tools/capabilities to Claude
- Integrating external APIs or services
- Tools need guidance on when/how to use them
- Want Claude to use tools idiomatically, not just technically
Key insight: MCP provides capability, skill provides judgment. The MCP server exposes click(), the skill teaches "await elements before clicking them".
---
Pattern: Command Collection
Use when: Providing multiple custom slash commands for common tasks
Structure:
my-plugin/
├── .claude-plugin/
│ └── plugin.json
├── commands/
│ ├── status.md
│ ├── logs.md
│ ├── deploy.md
│ └── rollback.md
└── README.mdWhen to use:
- Project-specific workflows (deploy, test, build)
- Common task shortcuts
- Standardized responses (greetings, status reports)
- Quick context injection
Example use cases:
/deploy-staging- Step-by-step deployment workflow/incident-report- Template for incident documentation/code-review- Checklist for reviewing PRs/security-check- Security audit workflow
---
Pattern: Hook-Enhanced Workflow
Use when: Automating actions in response to Claude's behavior
Structure:
my-plugin/
├── .claude-plugin/
│ └── plugin.json
├── hooks/
│ └── hooks.json
├── scripts/
│ ├── format-code.sh
│ ├── run-linter.sh
│ └── update-docs.sh
└── README.mdCommon hooks:
PostToolUse[Write|Edit]→ Auto-format codeSessionStart→ Load project contextUserPromptSubmit→ Enforce conventionsPreCompact→ Save conversation summaries
When to use:
- Enforcing code style automatically
- Injecting project-specific context
- Running validations or checks
- Maintaining project conventions
Warning: Hooks that block operations can disrupt workflow. Use sparingly and make failure messages clear.
---
Pattern: Full-Featured Plugin
Use when: Building a comprehensive plugin with multiple integration points
Structure:
my-plugin/
├── .claude-plugin/
│ └── plugin.json
├── skills/
│ ├── main-workflow/
│ └── advanced-techniques/
├── commands/
│ ├── start.md
│ └── help.md
├── hooks/
│ └── hooks.json
├── agents/
│ └── specialist.md
├── mcp/
│ └── dist/
│ └── server.js
└── README.mdReal example: See examples/full-featured-plugin/ in this repo
When to use:
- Complete domain coverage (e.g., "the definitive AWS plugin")
- Multiple related workflows
- Tools + guidance + automation all needed
- Building a "platform" plugin
Caution: Start simple, add complexity only when justified. Most plugins don't need all components.
---
Pattern: Skill with Bundled Resources
Use when: A skill needs reference material, scripts, or templates
Structure:
skills/my-skill/
├── SKILL.md # Main skill instructions
├── scripts/
│ ├── helper.py # Executable utilities
│ └── generator.js
├── references/
│ ├── api-docs.md # Documentation to load
│ ├── examples.md
│ └── cheatsheet.md
└── assets/
├── template.txt # Files for output
└── config-example.jsonHow resources are used:
- SKILL.md tells Claude to "read references/api-docs.md for complete API reference"
- Scripts can be executed via Bash tool
- Assets can be copied/modified for output
- References are loaded into context when skill is invoked
When to use:
- Skill needs detailed technical reference
- Want to separate workflow (SKILL.md) from reference (docs)
- Need executable helpers
- Providing templates or examples
---
Choosing the Right Pattern
| Your Goal | Use This Pattern |
|---|---|
| Teach Claude a process/workflow | Simple Plugin with One Skill |
| Add new tools + guidance | MCP Plugin with Skill |
| Provide project shortcuts | Command Collection |
| Enforce conventions automatically | Hook-Enhanced Workflow |
| Comprehensive domain coverage | Full-Featured Plugin |
| Skill needs reference docs | Skill with Bundled Resources |
Combining Patterns
Patterns are composable:
Example: "superpowers" plugin
- Multiple skills (brainstorming, TDD, debugging) ← Skill collection
- Each skill has references/ ← Bundled resources
- Could add hooks for enforcement ← Add hooks pattern
- Could add MCP for new tools ← Add MCP pattern
Start simple, grow intentionally. Add components when you have a clear reason, not because they exist.
Plugin Structure Reference
Standard Directory Layout
All paths relative to plugin root:
my-plugin/
├── .claude-plugin/
│ ├── plugin.json # REQUIRED - Plugin metadata
│ └── marketplace.json # Optional - For local dev/distribution
├── skills/ # Optional - Agent Skills
│ └── skill-name/
│ ├── SKILL.md # Required for each skill
│ ├── scripts/ # Optional - Executable helpers
│ ├── references/ # Optional - Documentation
│ └── assets/ # Optional - Templates/files
├── commands/ # Optional - Custom slash commands
│ └── command-name.md
├── agents/ # Optional - Specialized subagents
│ └── agent-name.md
├── hooks/ # Optional - Event handlers
│ └── hooks.json
├── .mcp.json # Optional - MCP server config
├── LICENSE
└── README.mdCritical Rules
1. .claude-plugin/ Contains ONLY Manifests
❌ WRONG:
.claude-plugin/
├── plugin.json
├── skills/ # NO! Skills don't go here
└── commands/ # NO! Commands don't go here✅ CORRECT:
.claude-plugin/
├── plugin.json # Only manifests
└── marketplace.json # Only manifests
skills/ # Skills at plugin root
commands/ # Commands at plugin root2. Always Use ${CLAUDE_PLUGIN_ROOT} for Paths in Config
❌ WRONG - Hardcoded paths:
{
"mcpServers": {
"my-server": {
"command": "/Users/name/plugins/my-plugin/server.js"
}
}
}✅ CORRECT - Variable paths:
{
"mcpServers": {
"my-server": {
"command": "${CLAUDE_PLUGIN_ROOT}/server.js"
}
}
}3. Use Relative Paths in plugin.json
All paths in plugin.json must:
- Start with
./ - Be relative to plugin root
❌ WRONG:
{
"mcpServers": {
"server": {
"args": ["server/index.js"] // Missing ./
}
}
}✅ CORRECT:
{
"mcpServers": {
"server": {
"args": ["${CLAUDE_PLUGIN_ROOT}/server/index.js"]
}
}
}Plugin Manifest (plugin.json)
Minimal Version
{
"name": "my-plugin",
"version": "1.0.0",
"description": "Brief description of what the plugin does",
"author": {
"name": "Your Name"
}
}Complete Version
{
"name": "my-plugin",
"version": "1.0.0",
"description": "Comprehensive plugin description",
"author": {
"name": "Your Name",
"email": "you@example.com",
"url": "https://github.com/you"
},
"homepage": "https://github.com/you/my-plugin",
"repository": "https://github.com/you/my-plugin",
"license": "MIT",
"keywords": ["keyword1", "keyword2"],
"mcpServers": {
"server-name": {
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/path/to/server.js"],
"env": {
"ENV_VAR": "value"
}
}
}
}Development Marketplace (marketplace.json)
For local testing, create in .claude-plugin/:
{
"name": "my-plugin-dev",
"description": "Development marketplace for my plugin",
"owner": {
"name": "Your Name"
},
"plugins": [
{
"name": "my-plugin",
"description": "Plugin description",
"version": "1.0.0",
"source": "./",
"author": {
"name": "Your Name"
}
}
]
}Installation:
/plugin marketplace add /path/to/my-plugin
/plugin install my-plugin@my-plugin-devComponent Formats
Skills (skills/skill-name/SKILL.md)
---
name: skill-name
description: Use when [triggering conditions] - [what it does]
---
# Skill Name
## Overview
What this skill does in 1-2 sentences.
## When to Use
- Specific scenario 1
- Specific scenario 2
## Workflow
1. Step one
2. Step two
3. Step threeCommands (commands/command-name.md)
---
description: Brief description of what this command does
---
# Command Instructions
Tell Claude what to do when this command is invoked.
Be specific and clear about the expected behavior.Hooks (hooks/hooks.json)
⚠️ WARNING: Duplicate hooks file error
The hooks/hooks.json file is automatically loaded by Claude Code. Do NOT reference it in plugin.json's manifest.hooks field or you'll get "Duplicate hooks file detected" errors.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" format.sh"
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" init.sh"
}
]
}
]
}
}Cross-platform hooks: Use the polyglot run-hook.cmd wrapper to run shell scripts on Windows, macOS, and Linux. See references/polyglot-hooks.md for details.
Available hook events:
PreToolUse,PostToolUseUserPromptSubmitSessionStart,SessionEndStop,SubagentStopPreCompactNotification
MCP Servers
Option 1: In plugin.json
{
"name": "my-plugin",
"mcpServers": {
"server-name": {
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/server/index.js"],
"env": {
"API_KEY": "${PLUGIN_ENV_API_KEY}"
}
}
}
}Option 2: Separate .mcp.json file
{
"mcpServers": {
"server-name": {
"command": "${CLAUDE_PLUGIN_ROOT}/bin/server",
"args": ["--config", "${CLAUDE_PLUGIN_ROOT}/config.json"]
}
}
}Agents (agents/agent-name.md)
---
description: What this agent specializes in
capabilities: ["capability1", "capability2"]
---
# Agent Name
Detailed description of when to invoke this specialized agent.
## Expertise
- Specific domain knowledge
- Specialized techniques
- When to use vs other agentsFile Permissions
Scripts must be executable:
chmod +x scripts/helper.sh
chmod +x bin/serverCross-Platform Polyglot Hooks for Claude Code
Claude Code plugins need hooks that work on Windows, macOS, and Linux. This document explains the polyglot wrapper technique that makes this possible.
The Problem
Claude Code runs hook commands through the system's default shell:
- Windows: CMD.exe
- macOS/Linux: bash or sh
This creates several challenges:
1. Script execution: Windows CMD can't execute .sh files directly - it tries to open them in a text editor 2. Path format: Windows uses backslashes (C:\path), Unix uses forward slashes (/path) 3. Environment variables: $VAR syntax doesn't work in CMD 4. No `bash` in PATH: Even with Git Bash installed, bash isn't in the PATH when CMD runs
The Solution: Polyglot .cmd Wrapper
A polyglot script is valid syntax in multiple languages simultaneously. Our wrapper is valid in both CMD and bash:
: << 'CMDBLOCK'
@echo off
"C:\Program Files\Git\bin\bash.exe" -l -c "\"$(cygpath -u \"$CLAUDE_PLUGIN_ROOT\")/hooks/session-start.sh\""
exit /b
CMDBLOCK
# Unix shell runs from here
"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"How It Works
On Windows (CMD.exe)
1. : << 'CMDBLOCK' - CMD sees : as a label (like :label) and ignores << 'CMDBLOCK' 2. @echo off - Suppresses command echoing 3. The bash.exe command runs with:
-l(login shell) to get proper PATH with Unix utilitiescygpath -uconverts Windows path to Unix format (C:\foo→/c/foo)
4. exit /b - Exits the batch script, stopping CMD here 5. Everything after CMDBLOCK is never reached by CMD
On Unix (bash/sh)
1. : << 'CMDBLOCK' - : is a no-op, << 'CMDBLOCK' starts a heredoc 2. Everything until CMDBLOCK is consumed by the heredoc (ignored) 3. # Unix shell runs from here - Comment 4. The script runs directly with the Unix path
File Structure
hooks/
├── hooks.json # Points to the .cmd wrapper
├── run-hook.cmd # Polyglot wrapper (cross-platform entry point)
├── session-start.sh # Actual hook logic (bash script)
└── validate-bash.sh # Another hook scripthooks.json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|clear|compact",
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start.sh"
}
]
}
]
}
}Note: The path must be quoted because ${CLAUDE_PLUGIN_ROOT} may contain spaces on Windows (e.g., C:\Program Files\...).
The Reusable Wrapper
Instead of creating a polyglot wrapper for each hook, use a single run-hook.cmd that takes the script name as an argument:
run-hook.cmd
: << 'CMDBLOCK'
@echo off
REM Polyglot wrapper: runs .sh scripts cross-platform
REM Usage: run-hook.cmd <script-name> [args...]
REM The script should be in the same directory as this wrapper
"C:\Program Files\Git\bin\bash.exe" -l "%~dp0%~1"
exit /b
CMDBLOCK
# Unix shell runs from here
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SCRIPT_NAME="$1"
shift
"${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"Note: Line 101 uses $0 instead of ${BASH_SOURCE[0]:-$0} for POSIX compliance. The latter causes "Bad substitution" errors on Ubuntu/Debian systems where /bin/sh is dash (not bash). Using $0 works correctly on all platforms.
Using run-hook.cmd in hooks.json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start.sh"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" validate-bash.sh"
}
]
}
]
}
}Requirements
Windows
- Git for Windows must be installed (provides
bash.exeandcygpath) - Default installation path:
C:\Program Files\Git\bin\bash.exe - If Git is installed elsewhere, the wrapper needs modification
Unix (macOS/Linux)
- Standard bash or sh shell
- The
.cmdfile must have execute permission (chmod +x)
Writing Cross-Platform Hook Scripts
Your actual hook logic goes in the .sh file. To ensure it works on Windows (via Git Bash):
Do:
- Use pure bash builtins when possible
- Use
$(command)instead of backticks - Quote all variable expansions:
"$VAR" - Use
printfor here-docs for output
Avoid:
- External commands that may not be in PATH (sed, awk, grep)
- If you must use them, they're available in Git Bash but ensure PATH is set up (use
bash -l)
Example: JSON Escaping Without sed/awk
Instead of:
escaped=$(echo "$content" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | awk '{printf "%s\\n", $0}')Use pure bash:
escape_for_json() {
local input="$1"
local output=""
local i char
for (( i=0; i<${#input}; i++ )); do
char="${input:$i:1}"
case "$char" in
$'\\') output+='\\' ;;
'"') output+='\"' ;;
$'\n') output+='\n' ;;
$'\r') output+='\r' ;;
$'\t') output+='\t' ;;
*) output+="$char" ;;
esac
done
printf '%s' "$output"
}Troubleshooting
"bash is not recognized"
CMD can't find bash. The wrapper uses the full path C:\Program Files\Git\bin\bash.exe. If Git is installed elsewhere, update the path.
"cygpath: command not found" or "dirname: command not found"
Bash isn't running as a login shell. Ensure -l flag is used.
Path has weird \/ in it
${CLAUDE_PLUGIN_ROOT} expanded to a Windows path ending with backslash, then /hooks/... was appended. Use cygpath to convert the entire path.
Script opens in text editor instead of running
The hooks.json is pointing directly to the .sh file. Point to the .cmd wrapper instead.
Works in terminal but not as hook
Claude Code may run hooks differently. Test by simulating the hook environment:
$env:CLAUDE_PLUGIN_ROOT = "C:\path\to\plugin"
cmd /c "C:\path\to\plugin\hooks\run-hook.cmd" session-start.shRelated Issues
- anthropics/claude-code#9758 - .sh scripts open in editor on Windows
- anthropics/claude-code#3417 - Hooks don't work on Windows
- anthropics/claude-code#6023 - CLAUDE_PROJECT_DIR not found
Troubleshooting Plugin Development
Plugin Not Loading
Symptom
Plugin doesn't appear in /plugin list or components aren't available.
Debug Steps
1. Check plugin.json syntax
# Validate JSON
cat .claude-plugin/plugin.json | jq .If this errors, you have invalid JSON.
2. Verify directory structure
# Ensure .claude-plugin/ exists at plugin root
ls -la .claude-plugin/
# Should show plugin.json (required)3. Check all paths
- Search for hardcoded paths:
grep -r "/Users/" .claude-plugin/ - Should use
${CLAUDE_PLUGIN_ROOT}instead - Relative paths in plugin.json must start with
./
4. Restart Claude Code
- Changes only take effect after restart
- Exit and relaunch the application
5. Check installation
/plugin list
# Your plugin should appear hereCommon Causes
| Problem | Solution |
|---|---|
.claude-plugin/ missing | Create directory with plugin.json |
Invalid JSON in plugin.json | Validate with jq or JSON linter |
| Hardcoded paths | Replace with ${CLAUDE_PLUGIN_ROOT} |
| Forgot to restart | Always restart after install/changes |
---
Skill Not Triggering
Symptom
Skill exists but Claude doesn't use it when expected.
Debug Steps
1. Check YAML frontmatter format
---
name: skill-name
description: Use when [clear trigger] - [what it does]
---- Must have
---delimiters - Must include
nameanddescription - No tabs, only spaces for indentation
2. Verify description clarity
- Description should clearly state WHEN to use skill
- Use "Use when..." format
- Be specific about triggering conditions
❌ Bad: description: A helpful skill
✅ Good: description: Use when debugging test failures - systematic approach to finding root causes
3. Test explicitly Ask for a task that exactly matches the description:
"I need to debug a test failure"
# Should trigger systematic-debugging skill4. Check skill location
- Must be in
skills/skill-name/SKILL.md - NOT in
.claude-plugin/skills/
Common Causes
| Problem | Solution |
|---|---|
| Vague description | Make description specific and action-oriented |
| Skill in wrong location | Move to skills/ at plugin root |
| Missing frontmatter | Add YAML with name and description |
| Malformed YAML | Check for tabs, missing dashes, etc. |
---
Command Not Appearing
Symptom
Custom slash command doesn't show up or can't be executed.
Debug Steps
1. Verify location
ls -la commands/
# Should show command-name.md filesCommands must be at commands/ in plugin root, NOT in .claude-plugin/
2. Check markdown format
---
description: Brief description of what this command does
---
# Command Instructions
Content here...3. Restart Claude Code Commands are loaded at startup.
4. Test directly
/your-command-nameCommon Causes
| Problem | Solution |
|---|---|
Commands in .claude-plugin/ | Move to commands/ at root |
| Missing description frontmatter | Add YAML with description |
| No restart after adding | Restart Claude Code |
| Wrong file extension | Must be .md not .txt |
---
MCP Server Not Starting
Symptom
MCP server tools not available, or server fails silently.
Debug Steps
1. Verify path variables All paths in MCP config must use ${CLAUDE_PLUGIN_ROOT}:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/server/index.js"]
}
}
}2. Check executable permissions
chmod +x server/index.js
# Or for shell scripts:
chmod +x bin/server.sh3. Test server independently
# Run server outside Claude Code to check for errors
node ${PLUGIN_ROOT}/server/index.js4. Check logs
claude --debug
# Look for MCP server startup errors5. Verify command exists
which node # Or whatever command you're usingCommon Causes
| Problem | Solution |
|---|---|
| Hardcoded paths | Use ${CLAUDE_PLUGIN_ROOT} |
| Not executable | chmod +x on scripts |
| Command not in PATH | Use full path or ensure command available |
| Server crashes on startup | Test independently, check logs |
| Missing dependencies | npm install or equivalent |
---
Hooks Not Firing
Symptom
Hook scripts exist but don't execute when events occur.
Debug Steps
1. Check hooks.json location Must be at hooks/hooks.json in plugin root.
2. Verify JSON format
cat hooks/hooks.json | jq .3. Check matcher syntax
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit", // Regex - matches Write OR Edit
"hooks": [...]
}
]
}
}4. Verify script paths
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh"
}5. Check script permissions
chmod +x scripts/format.sh6. Test script directly
# Run the hook script manually
./scripts/format.shCommon Causes
| Problem | Solution |
|---|---|
| Wrong hooks.json location | Move to hooks/ at plugin root |
| Script not executable | chmod +x on all scripts |
| Invalid matcher regex | Test regex syntax |
| Script fails silently | Add error handling, test independently |
| Hardcoded paths | Use ${CLAUDE_PLUGIN_ROOT} |
---
Development Workflow Issues
Can't Install Plugin Locally
Problem: /plugin install my-plugin@my-dev fails
Solutions: 1. Check marketplace.json exists in .claude-plugin/ 2. Verify marketplace is added:
/plugin marketplace add /full/path/to/plugin
/plugin marketplace list # Should show your marketplace3. Check marketplace.json format:
{
"name": "my-dev",
"plugins": [{
"name": "my-plugin",
"source": "./" // Points to plugin root
}]
}Changes Not Taking Effect
Problem: Modified plugin but changes don't appear
Solutions: 1. Uninstall → modify → reinstall → restart:
/plugin uninstall my-plugin@my-dev
# Make your changes
/plugin install my-plugin@my-dev
# Restart Claude Code2. For hook/MCP changes, restart is mandatory 3. For skill/command content changes, sometimes works without restart (but safer to restart)
---
Common Pitfalls Summary
| Mistake | Why It Fails | How to Fix |
|---|---|---|
Skills in .claude-plugin/skills/ | Claude looks in skills/ at root | Move to plugin root |
| Hardcoded absolute paths | Breaks on other systems | Use ${CLAUDE_PLUGIN_ROOT} |
| Forgot to restart | Changes load at startup | Always restart after changes |
| Script not executable | Shell can't run it | chmod +x script.sh |
| Invalid JSON | Parser fails silently | Validate with jq or linter |
| Vague skill description | Claude doesn't know when to use it | Be specific about triggers |
| Missing YAML frontmatter | Metadata not parsed | Add --- delimiters and fields |
| Testing without uninstall | Old version still cached | Uninstall before reinstalling |
---
Debugging Workflow
When something isn't working:
1. Validate all JSON files
jq . .claude-plugin/plugin.json
jq . .claude-plugin/marketplace.json
jq . hooks/hooks.json2. Check all paths use variables
grep -r "Users/" .
# Should return nothing in config files3. Verify permissions
find . -name "*.sh" -o -name "*.js" | xargs ls -l
# Check executable bit (x) is set4. Test components independently
- Run MCP servers directly
- Execute hook scripts manually
- Validate YAML frontmatter with a parser
5. Clean reinstall
/plugin uninstall my-plugin@my-dev
/plugin marketplace remove /path/to/plugin
# Fix issues
/plugin marketplace add /path/to/plugin
/plugin install my-plugin@my-dev
# Restart Claude Code6. Check logs
claude --debug
# Look for error messages during startup---
Getting Help
If you're still stuck:
1. Read official docs via working-with-claude-code skill 2. Check example plugins in this repo and ~/.claude/plugins/ 3. Simplify - Remove components until it works, then add back one at a time 4. Report issues at https://github.com/anthropics/claude-code/issues
Pro tip: When asking for help, include:
- Your plugin.json
- Directory structure (
tree -L 3 -a) - Exact error messages
- What you've already tried
Related skills
FAQ
Is Developing Claude Code Plugins safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.