Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:47:48 +08:00
commit c8b2901108
25 changed files with 5579 additions and 0 deletions

View File

@@ -0,0 +1,333 @@
# Hooks Schema
Hooks allow you to run commands at specific lifecycle events. Define them in `hooks/hooks.json`.
## Location
`hooks/hooks.json` (at plugin root, referenced in `plugin.json`)
## Structure
```json
{
"description": "Optional description of what these hooks do",
"hooks": {
"EventName": [
{
"matcher": "pattern",
"hooks": [
{
"type": "command",
"command": "path/to/script.sh",
"timeout": 30
}
]
}
]
}
}
```
## Event Types
### PreToolUse
Runs **before** Claude uses a tool. Can block tool execution.
```json
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 30
}
]
}
]
}
```
**Matcher**: Regex pattern matching tool names (e.g., `Write`, `Read`, `Bash.*`)
**Exit codes**:
- `0`: Allow (stdout visible to Claude)
- `2`: **Block** (stderr shown to Claude as feedback)
- Other: Warning (non-blocking)
### PostToolUse
Runs **after** a tool completes. Cannot block.
```json
{
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
"timeout": 30
}
]
}
]
}
```
### SessionStart
Runs when Claude Code session starts.
```json
{
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "echo 'Plugin loaded!'"
}
]
}
]
}
```
**Matchers**:
- `startup` - Invoked from startup
- `resume` - Invoked from `--resume`, `--continue`, or `/resume`
- `clear` - Invoked from `/clear`
- `compact` - Invoked from auto or manual compact
**Note**: SessionStart stdout is added to context automatically for Claude.
### SessionEnd
Runs when a Claude Code session ends.
```json
{
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/cleanup.sh"
}
]
}
]
}
```
### UserPromptSubmit
Runs when user submits a prompt. Can block prompt processing.
```json
{
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/context-injector.sh"
}
]
}
]
}
```
**Exit codes**:
- `0`: Allow (stdout added to context)
- `2`: **Block** (stderr shown to user)
### Stop / SubagentStop
Runs when Claude attempts to stop (main agent or subagent).
```json
{
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/check-continuation.sh"
}
]
}
]
}
```
## Environment Variables
Available in hook commands:
- `${CLAUDE_PLUGIN_ROOT}`: Absolute path to plugin root
- `${CLAUDE_PROJECT_DIR}`: Project root directory (where Claude Code started)
- Standard shell environment variables
## Timeouts
- Default: No timeout
- Recommended: 10-30 seconds for validation
- Max: Keep under 60 seconds for good UX
## Common Patterns
### Validation Hook (Blocking)
```json
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 30
}
]
}
]
}
```
**validate.sh**:
```bash
#!/usr/bin/env bash
if [ validation_fails ]; then
echo "Error: validation failed" >&2
exit 2 # Block the tool
fi
exit 0 # Allow
```
**Advanced JSON output** (alternative to exit codes):
```json
{
"permissionDecision": "deny",
"permissionDecisionReason": "File violates security policy",
"suppressOutput": true
}
```
### Formatting Hook (Non-blocking)
```json
{
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
"timeout": 30
}
]
}
]
}
```
### Startup Message
```json
{
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "echo '✓ My Plugin loaded'"
}
]
}
]
}
```
## Best Practices
- **Use `${CLAUDE_PLUGIN_ROOT}`** for portable paths
- **Set timeouts** to prevent hanging (10-30 seconds recommended)
- **Exit code 2** to block (PreToolUse/UserPromptSubmit)
- **Keep scripts fast** (< 1 second ideally)
- **Make scripts executable** (`chmod +x`)
- **Test hooks** before distributing
- **Handle JSON output** for advanced control (see advanced examples)
## Common Mistakes
**Absolute paths** (not portable)
```json
{
"command": "/Users/you/plugin/scripts/validate.sh"
}
```
**Plugin-relative paths**
```json
{
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh"
}
```
**No timeout** on slow operations
```json
{
"command": "npm install"
// Missing timeout!
}
```
**Set appropriate timeout**
```json
{
"command": "npm install",
"timeout": 300000
}
```
**Missing required matcher**
```json
{
"SessionStart": [
{
"hooks": [...] // No matcher!
}
]
}
```
**Include appropriate matcher**
```json
{
"SessionStart": [
{
"matcher": "startup",
"hooks": [...]
}
]
}
```
## Debugging
Use `claude --debug` to see:
- Hook registration
- Hook execution timing
- Exit codes and output
- Blocking decisions

View File

@@ -0,0 +1,359 @@
# Marketplace Schema
The `marketplace.json` file defines a collection of plugins that users can install.
## Location
`.claude-plugin/marketplace.json` (at marketplace root)
## Structure
```json
{
"name": "marketplace-name",
"owner": {
"name": "Your Organization",
"email": "team@your-org.com"
},
"metadata": {
"description": "Marketplace description",
"version": "1.0.0"
},
"plugins": [
{
"name": "plugin-name",
"description": "What the plugin does",
"version": "1.0.0",
"author": {
"name": "Author Name"
},
"source": "./plugins/plugin-name",
"category": "utilities",
"tags": ["tag1", "tag2"],
"keywords": ["keyword1", "keyword2"]
}
]
}
```
## Required Fields
### Marketplace Level
- **name**: kebab-case string, unique marketplace identifier
- **owner**: Object with `name` (required) and optional `email`
- **plugins**: Array of plugin entries
### Plugin Entry
- **name**: Plugin name (must match `plugin.json`)
- **source**: Relative path to plugin directory OR git URL
## Optional Fields
### Marketplace Level
- **metadata**: Object with:
- **description**: Brief marketplace description
- **version**: Marketplace version
- **pluginRoot**: Base path for relative plugin sources (allows resolving relative plugin paths)
### Plugin Entry
**Standard metadata fields:**
- **description**: Brief description
- **version**: SemVer version
- **author**: String or object with `name`, `email`, `url`
- **category**: Category string (e.g., "utilities", "productivity")
- **tags**: Array of tags for filtering
- **keywords**: Array of keywords for search
- **homepage**: Plugin homepage URL
- **repository**: Plugin repository URL
- **license**: License identifier (e.g., "MIT")
- **strict**: Boolean (default: true) - Require plugin.json in plugin folder
**Component configuration fields:**
- **commands**: String or array - Custom paths to command files or directories
- **agents**: String or array - Custom paths to agent files
- **hooks**: String or object - Custom hooks configuration or path to hooks file
- **mcpServers**: String or object - MCP server configurations or path to MCP config
## Source Types
### Local Path (Development)
```json
{
"source": "./plugins/my-plugin"
}
```
### Git Repository
Simple string format:
```json
{
"source": "https://github.com/user/repo"
}
```
Object format for advanced configuration:
```json
{
"source": {
"source": "github",
"repo": "owner/plugin-repo"
}
}
```
### Git URL Source
```json
{
"source": {
"source": "url",
"url": "https://gitlab.com/team/plugin.git"
}
}
```
### Git with Subdirectory
```json
{
"source": "https://github.com/user/repo/tree/main/plugins/my-plugin"
}
```
## Examples
### Local Dev Marketplace
```json
{
"name": "dev-marketplace",
"owner": {
"name": "Developer",
"email": "dev@localhost"
},
"metadata": {
"description": "Local development marketplace",
"version": "0.1.0"
},
"plugins": [
{
"name": "my-plugin",
"description": "Plugin in development",
"source": "../my-plugin"
}
]
}
```
### Team Marketplace
```json
{
"name": "acme-tools",
"owner": {
"name": "ACME Corp",
"email": "tools@acme.com"
},
"metadata": {
"description": "ACME internal tools for Claude Code",
"version": "1.0.0"
},
"plugins": [
{
"name": "code-review",
"description": "ACME code review standards",
"version": "2.1.0",
"author": {
"name": "DevTools Team"
},
"source": "./plugins/code-review",
"category": "development",
"tags": ["code-review", "standards"],
"keywords": ["review", "quality", "standards"]
},
{
"name": "deploy-tools",
"description": "Deployment automation",
"version": "1.5.0",
"author": {
"name": "DevOps Team"
},
"source": "./plugins/deploy-tools",
"category": "devops",
"tags": ["deployment", "automation"],
"keywords": ["deploy", "ci", "cd"]
}
]
}
```
### Advanced Plugin Entry
Plugin entries can override default component locations and provide inline configuration:
```json
{
"name": "enterprise-tools",
"source": {
"source": "github",
"repo": "company/enterprise-plugin"
},
"description": "Enterprise workflow automation tools",
"version": "2.1.0",
"author": {
"name": "Enterprise Team",
"email": "enterprise@company.com"
},
"homepage": "https://docs.company.com/plugins/enterprise-tools",
"repository": "https://github.com/company/enterprise-plugin",
"license": "MIT",
"keywords": ["enterprise", "workflow", "automation"],
"category": "productivity",
"commands": [
"./commands/core/",
"./commands/enterprise/",
"./commands/experimental/preview.md"
],
"agents": [
"./agents/security-reviewer.md",
"./agents/compliance-checker.md"
],
"hooks": "./config/hooks.json",
"mcpServers": {
"enterprise-db": {
"command": "${CLAUDE_PLUGIN_ROOT}/servers/db-server",
"args": ["--config", "${CLAUDE_PLUGIN_ROOT}/config.json"]
}
},
"strict": false
}
```
<Note>
**Schema relationship**: Plugin entries are based on the *plugin manifest schema* (with all fields made optional) plus marketplace-specific fields (`source`, `strict`, `category`, `tags`). This means any field valid in a `plugin.json` file can also be used in a marketplace entry. When `strict: false`, the marketplace entry serves as the complete plugin manifest if no `plugin.json` exists. When `strict: true` (default), marketplace fields supplement the plugin's own manifest file.
</Note>
<Note>
**Environment variables**: Use `${CLAUDE_PLUGIN_ROOT}` in hooks and mcpServers configurations. This variable resolves to the plugin's installation directory and ensures paths work correctly regardless of where the plugin is installed.
</Note>
## Usage
### Add Marketplace (Local)
```bash
/plugin marketplace add /path/to/marketplace
```
### Add Marketplace (GitHub)
```bash
/plugin marketplace add your-org/your-repo
```
### Install Plugin from Marketplace
```bash
/plugin install plugin-name@marketplace-name
```
### Team Auto-Install
In project `.claude/settings.json`:
```json
{
"extraKnownMarketplaces": {
"team-tools": {
"source": {
"source": "github",
"repo": "your-org/marketplace-repo"
}
}
},
"enabledPlugins": {
"plugin-name@team-tools": true
}
}
```
## Testing Workflow
1. Create dev marketplace:
```bash
mkdir -p dev-marketplace/.claude-plugin
# Create marketplace.json pointing to ../your-plugin
```
2. Add to Claude Code:
```bash
/plugin marketplace add ./dev-marketplace
```
3. Install plugin:
```bash
/plugin install your-plugin@dev-marketplace
```
4. After changes, reinstall:
```bash
/plugin uninstall your-plugin@dev-marketplace
/plugin install your-plugin@dev-marketplace
```
## Best Practices
- **Use kebab-case** for marketplace and plugin names
- **Keep descriptions concise** (< 100 chars)
- **Add meaningful tags** for discoverability
- **Version consistently** using SemVer
- **Test locally** before publishing to GitHub
- **Document plugins** in their README files
## Common Mistakes
❌ **Plugin name mismatch**
```json
// marketplace.json
{ "name": "my-plugin", "source": "./plugins/other-plugin" }
// plugin.json (in other-plugin/)
{ "name": "other-plugin" } // Names don't match!
```
✅ **Names must match**
```json
// marketplace.json
{ "name": "my-plugin", "source": "./plugins/my-plugin" }
// plugin.json
{ "name": "my-plugin" } // Matches!
```
❌ **Absolute source paths**
```json
{
"source": "/Users/you/plugins/my-plugin"
}
```
✅ **Relative source paths**
```json
{
"source": "./plugins/my-plugin"
}
```
## Validation
Use `/plugin-development:validate` to check marketplace structure.

View File

@@ -0,0 +1,258 @@
# Plugin Manifest Schema
The `plugin.json` file in `.claude-plugin/` defines your plugin's metadata and optionally custom component paths.
## Location
`.claude-plugin/plugin.json` (at plugin root)
## Required Fields
```json
{
"name": "plugin-name"
}
```
- **name**: kebab-case string, unique identifier (REQUIRED)
## Optional Fields
### Standard Metadata
```json
{
"version": "1.0.0",
"description": "What your plugin does",
"author": {
"name": "Your Name",
"email": "you@example.com",
"url": "https://github.com/your-username"
},
"homepage": "https://your-plugin-homepage.com",
"repository": "https://github.com/your-org/your-repo",
"license": "MIT",
"keywords": ["tag1", "tag2"]
}
```
- **version**: Semantic version format (optional metadata)
- **description**: Brief explanation of plugin purpose (optional metadata)
- **author**: Can be string or object with name, email, url
- **homepage**: Documentation URL (optional metadata)
- **repository**: Source code URL (optional metadata)
- **license**: License identifier like "MIT" (optional metadata)
- **keywords**: Array of tags for discovery (optional metadata)
### Component Configuration (Custom Paths Only)
**IMPORTANT**: Only include these fields if you're using **non-standard** paths. If using standard directory structure (`commands/`, `agents/`, `skills/`, `hooks/`), omit these fields entirely.
```json
{
"commands": ["./custom/path/cmd1.md", "./custom/path/cmd2.md"],
"agents": ["./custom/agents/reviewer.md", "./custom/agents/tester.md"],
"hooks": "./custom/hooks/hooks.json",
"mcpServers": {
"server-name": {
"command": "node",
"args": ["path/to/server.js"]
}
}
}
```
### Component Path Rules
- **commands**: Array of paths to individual `.md` command files, OR string path to a directory
- **agents**: Array of paths to individual `.md` agent files (NOT a directory path)
- **hooks**: Path to `hooks.json` configuration file OR inline hooks object
- **mcpServers**: MCP server configurations object OR path to MCP config file
All paths must be **relative to plugin root** (where `.claude-plugin/` lives) and start with `./`
**Note**: Custom paths supplement default directories - they don't replace them. If `commands/` exists, it's loaded in addition to custom command paths.
### Skills Configuration
For Skills (Agent Skills) provided by your plugin, you can restrict which tools Claude can use:
```json
{
"name": "my-skill-plugin",
"skills": [
{
"name": "safe-reader",
"allowed-tools": ["Read", "Grep", "Glob"]
}
]
}
```
However, the recommended approach is to specify `allowed-tools` directly in the `SKILL.md` frontmatter:
```yaml
---
name: safe-reader
description: Read files without making changes
allowed-tools: Read, Grep, Glob
---
```
### Environment Variables
**`${CLAUDE_PLUGIN_ROOT}`** is a special environment variable available in your plugin that contains the absolute path to your plugin directory. Use this in hooks, MCP servers, and scripts to ensure correct paths regardless of installation location.
```json
{
"name": "my-plugin",
"hooks": "./hooks/hooks.json"
}
```
Where `hooks/hooks.json` contains:
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format-code.sh"
}
]
}
]
}
}
```
Or inline hooks:
```json
{
"name": "my-plugin",
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh"
}
]
}
]
}
}
```
Use `${CLAUDE_PLUGIN_ROOT}` for:
- Scripts executed by hooks
- MCP server paths
- Config files referenced by components
- Any file paths in your plugin configuration
## Examples
### Standard Directory Structure (Recommended)
```json
{
"name": "my-dev-tools"
}
```
**Minimal plugin** - The simplest possible plugin. Claude Code automatically discovers `commands/`, `agents/`, `skills/`, and `hooks/` directories.
```json
{
"name": "my-dev-tools",
"version": "1.2.0",
"description": "Developer productivity tools for Claude Code",
"author": {
"name": "Dev Team",
"email": "dev@company.com"
},
"license": "MIT",
"keywords": ["productivity", "tools"]
}
```
**With metadata** - Adding optional metadata for better discovery and documentation.
### Custom Paths
```json
{
"name": "enterprise-plugin",
"description": "Enterprise development tools",
"author": {
"name": "Your Name"
},
"commands": [
"./specialized/deploy.md",
"./utilities/batch-process.md"
],
"agents": [
"./custom-agents/reviewer.md",
"./custom-agents/tester.md"
]
}
```
**Note**: Using custom paths to organize components. The `description` and `author` fields are optional metadata.
## Common Mistakes
**Wrong**: Including component fields with standard paths
```json
{
"name": "my-plugin",
"commands": "./commands/",
"agents": "./agents/"
}
```
**Correct**: Omit component fields for standard paths
```json
{
"name": "my-plugin"
}
```
**Wrong**: agents as directory path
```json
{
"agents": "./agents/"
}
```
**Correct**: agents as array of file paths
```json
{
"agents": ["./agents/reviewer.md", "./agents/tester.md"]
}
```
**Wrong**: Absolute paths
```json
{
"commands": "/Users/you/plugins/my-plugin/commands/"
}
```
**Correct**: Relative paths
```json
{
"commands": ["./custom/cmd.md"]
}
```
## Validation
Use `/plugin-development:validate` to check your manifest structure.