Adds the plugin-dev plugin to public marketplace. A comprehensive toolkit for
developing Claude Code plugins with 7 expert skills, 3 AI-assisted agents, and
extensive documentation covering the complete plugin development lifecycle.
Key features:
- 7 skills: hook-development, mcp-integration, plugin-structure, plugin-settings,
command-development, agent-development, skill-development
- 3 agents: agent-creator (AI-assisted generation), plugin-validator (structure
validation), skill-reviewer (quality review)
- 1 command: /plugin-dev:create-plugin (guided 8-phase workflow)
- 10 utility scripts for validation and testing
- 21 reference docs with deep-dive guidance (~11k words)
- 9 working examples demonstrating best practices
Changes for public release:
- Replaced all references to internal repositories with "Claude Code"
- Updated MCP examples: internal.company.com → api.example.com
- Updated token variables: ${INTERNAL_TOKEN} → ${API_TOKEN}
- Reframed agent-creation-system-prompt as "proven in production"
- Preserved all ${CLAUDE_PLUGIN_ROOT} references (186 total)
- Preserved valuable test blocks in core modules
Validation:
- All 3 agents validated successfully with validate-agent.sh
- All JSON files validated with jq
- Zero internal references remaining
- 59 files migrated, 21,971 lines added
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
8.1 KiB
Migrating from Basic to Advanced Hooks
This guide shows how to migrate from basic command hooks to advanced prompt-based hooks for better maintainability and flexibility.
Why Migrate?
Prompt-based hooks offer several advantages:
- Natural language reasoning: LLM understands context and intent
- Better edge case handling: Adapts to unexpected scenarios
- No bash scripting required: Simpler to write and maintain
- More flexible validation: Can handle complex logic without coding
Migration Example: Bash Command Validation
Before (Basic Command Hook)
Configuration:
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash validate-bash.sh"
}
]
}
]
}
Script (validate-bash.sh):
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command')
# Hard-coded validation logic
if [[ "$command" == *"rm -rf"* ]]; then
echo "Dangerous command detected" >&2
exit 2
fi
Problems:
- Only checks for exact "rm -rf" pattern
- Doesn't catch variations like
rm -frorrm -r -f - Misses other dangerous commands (
dd,mkfs, etc.) - No context awareness
- Requires bash scripting knowledge
After (Advanced Prompt Hook)
Configuration:
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "prompt",
"prompt": "Command: $TOOL_INPUT.command. Analyze for: 1) Destructive operations (rm -rf, dd, mkfs, etc) 2) Privilege escalation (sudo) 3) Network operations without user consent. Return 'approve' or 'deny' with explanation.",
"timeout": 15
}
]
}
]
}
Benefits:
- Catches all variations and patterns
- Understands intent, not just literal strings
- No script file needed
- Easy to extend with new criteria
- Context-aware decisions
- Natural language explanation in denial
Migration Example: File Write Validation
Before (Basic Command Hook)
Configuration:
{
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bash validate-write.sh"
}
]
}
]
}
Script (validate-write.sh):
#!/bin/bash
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
# Check for path traversal
if [[ "$file_path" == *".."* ]]; then
echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2
exit 2
fi
# Check for system paths
if [[ "$file_path" == "/etc/"* ]] || [[ "$file_path" == "/sys/"* ]]; then
echo '{"decision": "deny", "reason": "System file"}' >&2
exit 2
fi
Problems:
- Hard-coded path patterns
- Doesn't understand symlinks
- Missing edge cases (e.g.,
/etcvs/etc/) - No consideration of file content
After (Advanced Prompt Hook)
Configuration:
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "prompt",
"prompt": "File path: $TOOL_INPUT.file_path. Content preview: $TOOL_INPUT.content (first 200 chars). Verify: 1) Not system directories (/etc, /sys, /usr) 2) Not credentials (.env, tokens, secrets) 3) No path traversal 4) Content doesn't expose secrets. Return 'approve' or 'deny'."
}
]
}
]
}
Benefits:
- Context-aware (considers content too)
- Handles symlinks and edge cases
- Natural understanding of "system directories"
- Can detect secrets in content
- Easy to extend criteria
When to Keep Command Hooks
Command hooks still have their place:
1. Deterministic Performance Checks
#!/bin/bash
# Check file size quickly
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
size=$(stat -f%z "$file_path" 2>/dev/null || stat -c%s "$file_path" 2>/dev/null)
if [ "$size" -gt 10000000 ]; then
echo '{"decision": "deny", "reason": "File too large"}' >&2
exit 2
fi
Use command hooks when: Validation is purely mathematical or deterministic.
2. External Tool Integration
#!/bin/bash
# Run security scanner
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
scan_result=$(security-scanner "$file_path")
if [ "$?" -ne 0 ]; then
echo "Security scan failed: $scan_result" >&2
exit 2
fi
Use command hooks when: Integrating with external tools that provide yes/no answers.
3. Very Fast Checks (< 50ms)
#!/bin/bash
# Quick regex check
command=$(echo "$input" | jq -r '.tool_input.command')
if [[ "$command" =~ ^(ls|pwd|echo)$ ]]; then
exit 0 # Safe commands
fi
Use command hooks when: Performance is critical and logic is simple.
Hybrid Approach
Combine both for multi-stage validation:
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/quick-check.sh",
"timeout": 5
},
{
"type": "prompt",
"prompt": "Deep analysis of bash command: $TOOL_INPUT",
"timeout": 15
}
]
}
]
}
The command hook does fast deterministic checks, while the prompt hook handles complex reasoning.
Migration Checklist
When migrating hooks:
- Identify the validation logic in the command hook
- Convert hard-coded patterns to natural language criteria
- Test with edge cases the old hook missed
- Verify LLM understands the intent
- Set appropriate timeout (usually 15-30s for prompt hooks)
- Document the new hook in README
- Remove or archive old script files
Migration Tips
- Start with one hook: Don't migrate everything at once
- Test thoroughly: Verify prompt hook catches what command hook caught
- Look for improvements: Use migration as opportunity to enhance validation
- Keep scripts for reference: Archive old scripts in case you need to reference the logic
- Document reasoning: Explain why prompt hook is better in README
Complete Migration Example
Original Plugin Structure
my-plugin/
├── .claude-plugin/plugin.json
├── hooks/hooks.json
└── scripts/
├── validate-bash.sh
├── validate-write.sh
└── check-tests.sh
After Migration
my-plugin/
├── .claude-plugin/plugin.json
├── hooks/hooks.json # Now uses prompt hooks
└── scripts/ # Archive or delete
└── archive/
├── validate-bash.sh
├── validate-write.sh
└── check-tests.sh
Updated hooks.json
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "prompt",
"prompt": "Validate bash command safety: destructive ops, privilege escalation, network access"
}
]
},
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "prompt",
"prompt": "Validate file write safety: system paths, credentials, path traversal, content secrets"
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "Verify tests were run if code was modified"
}
]
}
]
}
Result: Simpler, more maintainable, more powerful.
Common Migration Patterns
Pattern: String Contains → Natural Language
Before:
if [[ "$command" == *"sudo"* ]]; then
echo "Privilege escalation" >&2
exit 2
fi
After:
"Check for privilege escalation (sudo, su, etc)"
Pattern: Regex → Intent
Before:
if [[ "$file" =~ \.(env|secret|key|token)$ ]]; then
echo "Credential file" >&2
exit 2
fi
After:
"Verify not writing to credential files (.env, secrets, keys, tokens)"
Pattern: Multiple Conditions → Criteria List
Before:
if [ condition1 ] || [ condition2 ] || [ condition3 ]; then
echo "Invalid" >&2
exit 2
fi
After:
"Check: 1) condition1 2) condition2 3) condition3. Deny if any fail."
Conclusion
Migrating to prompt-based hooks makes plugins more maintainable, flexible, and powerful. Reserve command hooks for deterministic checks and external tool integration.