Hooks and Plugins
Customize and extend Claude Code behavior with hooks and plugins.
Hooks System
Hooks are shell commands that execute in response to events.
Hook Types
Pre-tool hooks: Execute before tool calls Post-tool hooks: Execute after tool calls User prompt submit hooks: Execute when user submits prompts
Configuration
Hooks are configured in .claude/hooks.json:
{
"hooks": {
"pre-tool": {
"bash": "echo 'Running: $TOOL_ARGS'",
"write": "./scripts/validate-write.sh"
},
"post-tool": {
"write": "./scripts/format-code.sh",
"edit": "prettier --write $FILE_PATH"
},
"user-prompt-submit": "./scripts/validate-request.sh"
}
}
Environment Variables
Available in hook scripts:
All hooks:
$TOOL_NAME: Name of the tool being called$TOOL_ARGS: JSON string of tool arguments
Post-tool only:
$TOOL_RESULT: Tool execution result
User-prompt-submit only:
$USER_PROMPT: User's prompt text
Hook Examples
Pre-tool: Security Validation
#!/bin/bash
# .claude/scripts/validate-bash.sh
# Block dangerous commands
if echo "$TOOL_ARGS" | grep -E "rm -rf /|format|mkfs"; then
echo "❌ Dangerous command blocked"
exit 1
fi
echo "✓ Command validated"
Configuration:
{
"hooks": {
"pre-tool": {
"bash": "./.claude/scripts/validate-bash.sh"
}
}
}
Post-tool: Auto-format
#!/bin/bash
# .claude/scripts/format-code.sh
# Extract file path from tool args
FILE_PATH=$(echo "$TOOL_ARGS" | jq -r '.file_path')
# Format based on file type
case "$FILE_PATH" in
*.js|*.ts|*.jsx|*.tsx)
prettier --write "$FILE_PATH"
;;
*.py)
black "$FILE_PATH"
;;
*.go)
gofmt -w "$FILE_PATH"
;;
esac
Configuration:
{
"hooks": {
"post-tool": {
"write": "./.claude/scripts/format-code.sh",
"edit": "./.claude/scripts/format-code.sh"
}
}
}
User-prompt-submit: Cost Tracking
#!/bin/bash
# .claude/scripts/track-usage.sh
# Log prompt
echo "$(date): $USER_PROMPT" >> .claude/usage.log
# Estimate tokens (rough)
TOKEN_COUNT=$(echo "$USER_PROMPT" | wc -w)
echo "Estimated tokens: $TOKEN_COUNT"
Configuration:
{
"hooks": {
"user-prompt-submit": "./.claude/scripts/track-usage.sh"
}
}
Hook Best Practices
Performance: Keep hooks fast (<100ms) Reliability: Handle errors gracefully Security: Validate all inputs Logging: Log important actions Testing: Test hooks thoroughly
Hook Errors
When a hook fails:
- Pre-tool hook failure blocks tool execution
- Post-tool hook failure is logged but doesn't block
- User can configure strict mode to block on all failures
Plugins System
Plugins are packaged collections of extensions.
Plugin Structure
my-plugin/
├── plugin.json # Plugin metadata
├── commands/ # Slash commands
│ ├── my-command.md
│ └── another-command.md
├── skills/ # Agent skills
│ └── my-skill/
│ ├── skill.md
│ └── skill.json
├── hooks/ # Hook scripts
│ ├── hooks.json
│ └── scripts/
├── mcp/ # MCP server configurations
│ └── mcp.json
└── README.md # Documentation
plugin.json
{
"name": "my-plugin",
"version": "1.0.0",
"description": "Plugin description",
"author": "Your Name",
"homepage": "https://github.com/user/plugin",
"license": "MIT",
"commands": ["commands/*.md"],
"skills": ["skills/*/"],
"hooks": "hooks/hooks.json",
"mcpServers": "mcp/mcp.json",
"dependencies": {
"node": ">=18.0.0"
}
}
Installing Plugins
From GitHub
claude plugin install gh:username/repo
claude plugin install gh:username/repo@v1.0.0
From npm
claude plugin install npm:package-name
claude plugin install npm:@scope/package-name
From Local Path
claude plugin install ./path/to/plugin
claude plugin install ~/plugins/my-plugin
From URL
claude plugin install https://example.com/plugin.zip
Managing Plugins
List Installed Plugins
claude plugin list
Update Plugin
claude plugin update my-plugin
claude plugin update --all
Uninstall Plugin
claude plugin uninstall my-plugin
Enable/Disable Plugin
claude plugin disable my-plugin
claude plugin enable my-plugin
Creating Plugins
Initialize Plugin
mkdir my-plugin
cd my-plugin
Create plugin.json
{
"name": "my-plugin",
"version": "1.0.0",
"description": "My awesome plugin",
"author": "Your Name",
"commands": ["commands/*.md"],
"skills": ["skills/*/"]
}
Add Components
# Add slash command
mkdir -p commands
cat > commands/my-command.md <<EOF
# My Command
Do something awesome with {{input}}.
EOF
# Add skill
mkdir -p skills/my-skill
cat > skills/my-skill/skill.json <<EOF
{
"name": "my-skill",
"description": "Does something",
"version": "1.0.0"
}
EOF
Package Plugin
# Create archive
tar -czf my-plugin.tar.gz .
# Or zip
zip -r my-plugin.zip .
Publishing Plugins
To GitHub
git init
git add .
git commit -m "Initial commit"
git tag v1.0.0
git push origin main --tags
To npm
npm init
npm publish
Plugin Marketplaces
Organizations can create private plugin marketplaces.
Configure Marketplace
{
"marketplaces": [
{
"name": "company-internal",
"url": "https://plugins.company.com/catalog.json",
"auth": {
"type": "bearer",
"token": "${COMPANY_PLUGIN_TOKEN}"
}
}
]
}
Marketplace Catalog Format
{
"plugins": [
{
"name": "company-plugin",
"version": "1.0.0",
"description": "Internal plugin",
"downloadUrl": "https://plugins.company.com/company-plugin-1.0.0.zip",
"checksum": "sha256:abc123..."
}
]
}
Install from Marketplace
claude plugin install company-internal:company-plugin
Example Plugin: Code Quality
Structure
code-quality-plugin/
├── plugin.json
├── commands/
│ ├── lint.md
│ └── format.md
├── skills/
│ └── code-review/
│ ├── skill.md
│ └── skill.json
└── hooks/
├── hooks.json
└── scripts/
└── auto-lint.sh
plugin.json
{
"name": "code-quality",
"version": "1.0.0",
"description": "Code quality tools and automation",
"commands": ["commands/*.md"],
"skills": ["skills/*/"],
"hooks": "hooks/hooks.json"
}
commands/lint.md
# Lint
Run linter on {{files}} and fix all issues automatically.
hooks/hooks.json
{
"hooks": {
"post-tool": {
"write": "./scripts/auto-lint.sh"
}
}
}
Security Considerations
Hook Security
- Validate all inputs
- Use whitelists for allowed commands
- Implement timeouts
- Log all executions
- Review hook scripts regularly
Plugin Security
- Verify plugin sources
- Review code before installation
- Use signed packages when available
- Monitor plugin behavior
- Keep plugins updated
Best Practices
- Install plugins from trusted sources only
- Review plugin permissions
- Use plugin sandboxing when available
- Monitor resource usage
- Regular security audits
Troubleshooting
Hooks Not Running
- Check hooks.json syntax
- Verify script permissions (
chmod +x) - Check script paths
- Review logs in
.claude/logs/
Plugin Installation Failures
- Verify internet connectivity
- Check plugin URL/path
- Review error messages
- Clear cache:
claude plugin cache clear
Plugin Conflicts
- Check for conflicting commands
- Review plugin load order
- Disable conflicting plugins
- Update plugins to compatible versions
See Also
- Creating slash commands:
references/slash-commands.md - Agent skills:
references/agent-skills.md - Configuration:
references/configuration.md - Best practices:
references/best-practices.md