📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# Plugin Caching Reference
|
||||
|
||||
How Claude Code caches plugins and implications for plugin structure.
|
||||
|
||||
## How Plugin Caching Works
|
||||
|
||||
When plugins are installed, Claude Code copies them to a cache directory for security. This affects how you structure shared resources.
|
||||
|
||||
## Path Traversal Limitation
|
||||
|
||||
Paths that traverse outside the plugin root will not work after installation:
|
||||
|
||||
```
|
||||
# BROKEN after install - traverses outside plugin
|
||||
../../shared-utils/helper.sh
|
||||
../other-plugin/rules/FORMATTING.md
|
||||
```
|
||||
|
||||
Only files within the plugin directory are copied to the cache.
|
||||
|
||||
## Shared Resources Within a Plugin
|
||||
|
||||
Organize shared resources inside your plugin:
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json
|
||||
├── rules/ # Shared rules
|
||||
│ └── FORMATTING.md
|
||||
├── scripts/ # Shared scripts
|
||||
│ └── validate.sh
|
||||
└── skills/
|
||||
└── my-skill/
|
||||
└── SKILL.md # Can reference ../../rules/FORMATTING.md
|
||||
```
|
||||
|
||||
Skills can reference `../../rules/FORMATTING.md` because it stays within the plugin.
|
||||
|
||||
## Cross-Plugin Dependencies
|
||||
|
||||
If plugins need to share resources across plugin boundaries, you have three options:
|
||||
|
||||
### Option 1: Symlinks
|
||||
|
||||
Create symlinks within your plugin that point to external files. Symlinks are followed during the copy:
|
||||
|
||||
```bash
|
||||
# Inside your plugin directory
|
||||
ln -s /path/to/shared-utils ./shared-utils
|
||||
```
|
||||
|
||||
### Option 2: Restructure Marketplace
|
||||
|
||||
Set the marketplace source to a parent directory containing all plugins:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-plugin",
|
||||
"source": "./",
|
||||
"description": "Plugin with access to sibling directories",
|
||||
"commands": ["./plugins/my-plugin/commands/"],
|
||||
"skills": ["./plugins/my-plugin/skills/"],
|
||||
"strict": false
|
||||
}
|
||||
```
|
||||
|
||||
This copies the entire marketplace root, giving plugins access to siblings.
|
||||
|
||||
### Option 3: Skill Invocation (Recommended)
|
||||
|
||||
Instead of file references, use skill invocation for cross-plugin patterns:
|
||||
|
||||
```markdown
|
||||
## Related Skills
|
||||
|
||||
- **outfitter:tdd** - Test-driven development patterns
|
||||
- **outfitter:debugging** - Systematic debugging methodology
|
||||
```
|
||||
|
||||
Reference skills by `plugin:skill-name` and invoke with the Skill tool.
|
||||
|
||||
## Best Practice
|
||||
|
||||
**Prefer Option 3** (skill invocation) when possible. It:
|
||||
- Avoids caching complexity
|
||||
- Works regardless of installation method
|
||||
- Maintains clean plugin boundaries
|
||||
- Enables proper versioning of dependencies
|
||||
@@ -0,0 +1,406 @@
|
||||
# Plugin Distribution Reference
|
||||
|
||||
Packaging, versioning, and release automation for Claude Code plugins.
|
||||
|
||||
## Distribution Checklist
|
||||
|
||||
Before distributing:
|
||||
|
||||
- [ ] Plugin structure is correct
|
||||
- [ ] plugin.json is complete and valid
|
||||
- [ ] All components tested
|
||||
- [ ] Documentation complete (README, CHANGELOG)
|
||||
- [ ] License file included
|
||||
- [ ] Version number updated
|
||||
- [ ] Git tags created
|
||||
- [ ] GitHub release published
|
||||
|
||||
## Required Files for Distribution
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── plugin.json # Required: metadata
|
||||
├── README.md # Required: documentation
|
||||
├── LICENSE # Required: license
|
||||
├── CHANGELOG.md # Recommended: history
|
||||
└── [components] # Commands, agents, etc.
|
||||
```
|
||||
|
||||
## README Template
|
||||
|
||||
```markdown
|
||||
# Plugin Name
|
||||
|
||||
Brief description of what this plugin does.
|
||||
|
||||
## Installation
|
||||
|
||||
\`\`\`bash
|
||||
/plugin marketplace add owner/plugin-repo
|
||||
/plugin install plugin-name@owner
|
||||
\`\`\`
|
||||
|
||||
Or locally:
|
||||
\`\`\`bash
|
||||
/plugin marketplace add ./path/to/plugin
|
||||
/plugin install plugin-name@plugin-name
|
||||
\`\`\`
|
||||
|
||||
## Features
|
||||
|
||||
- Feature 1
|
||||
- Feature 2
|
||||
|
||||
## Usage
|
||||
|
||||
### Commands
|
||||
|
||||
- `/command-name` - Description
|
||||
|
||||
### Agents
|
||||
|
||||
Describe custom agents.
|
||||
|
||||
## Configuration
|
||||
|
||||
Required environment variables:
|
||||
\`\`\`bash
|
||||
export VAR_NAME=value
|
||||
\`\`\`
|
||||
|
||||
## Requirements
|
||||
|
||||
- Claude Code
|
||||
- Node.js 18+ (if applicable)
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
```
|
||||
|
||||
## CHANGELOG Template
|
||||
|
||||
```markdown
|
||||
# Changelog
|
||||
|
||||
Format based on [Keep a Changelog](https://keepachangelog.com/).
|
||||
|
||||
## [1.0.0] - 2025-01-20
|
||||
|
||||
### Added
|
||||
- Initial release
|
||||
- Command X for feature Y
|
||||
|
||||
### Changed
|
||||
- Updated behavior of command A
|
||||
|
||||
### Fixed
|
||||
- Fixed bug in agent B
|
||||
|
||||
## [0.1.0] - 2025-01-10
|
||||
|
||||
### Added
|
||||
- Initial development version
|
||||
```
|
||||
|
||||
## Semantic Versioning
|
||||
|
||||
**Format:** `MAJOR.MINOR.PATCH`
|
||||
|
||||
| Type | When | Example |
|
||||
|------|------|---------|
|
||||
| MAJOR | Breaking changes | 1.0.0 -> 2.0.0 |
|
||||
| MINOR | New features (compatible) | 1.0.0 -> 1.1.0 |
|
||||
| PATCH | Bug fixes | 1.0.0 -> 1.0.1 |
|
||||
|
||||
### Version Bump Workflow
|
||||
|
||||
```bash
|
||||
# 1. Update plugin.json version
|
||||
# 2. Update CHANGELOG.md
|
||||
# 3. Commit
|
||||
git add plugin.json CHANGELOG.md
|
||||
git commit -m "chore: bump version to 1.1.0"
|
||||
|
||||
# 4. Tag
|
||||
git tag v1.1.0
|
||||
|
||||
# 5. Push
|
||||
git push origin main --tags
|
||||
```
|
||||
|
||||
## Packaging
|
||||
|
||||
### ZIP Distribution
|
||||
|
||||
**Correct structure:**
|
||||
|
||||
```
|
||||
my-plugin.zip
|
||||
└── my-plugin/ # Plugin folder at root
|
||||
├── plugin.json
|
||||
├── README.md
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Creating Package
|
||||
|
||||
```bash
|
||||
# From parent directory
|
||||
zip -r my-plugin.zip my-plugin/ \
|
||||
-x "*.git*" "*.DS_Store" "node_modules/*" "test/*"
|
||||
|
||||
# Verify
|
||||
unzip -l my-plugin.zip
|
||||
```
|
||||
|
||||
### Package Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
VERSION=$(jq -r '.version' plugin.json)
|
||||
PLUGIN_NAME=$(jq -r '.name' plugin.json)
|
||||
|
||||
cd ..
|
||||
zip -r "${PLUGIN_NAME}-v${VERSION}.zip" "${PLUGIN_NAME}/" \
|
||||
-x "*.git*" "*.github*" "*.DS_Store" "node_modules/*" "test/*"
|
||||
|
||||
echo "Created ${PLUGIN_NAME}-v${VERSION}.zip"
|
||||
```
|
||||
|
||||
## GitHub Releases
|
||||
|
||||
### Manual Release
|
||||
|
||||
```bash
|
||||
# Tag and push
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
|
||||
# Create release
|
||||
gh release create v1.0.0 \
|
||||
--title "v1.0.0 - Initial Release" \
|
||||
--notes "Release notes here"
|
||||
```
|
||||
|
||||
### Release with Artifact
|
||||
|
||||
```bash
|
||||
# Create package
|
||||
zip -r my-plugin-v1.0.0.zip my-plugin/
|
||||
|
||||
# Create release with artifact
|
||||
gh release create v1.0.0 \
|
||||
--title "v1.0.0" \
|
||||
--notes-file CHANGELOG.md \
|
||||
my-plugin-v1.0.0.zip
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions - Validation
|
||||
|
||||
**.github/workflows/validate.yml:**
|
||||
|
||||
```yaml
|
||||
name: Validate Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Validate JSON
|
||||
run: jq empty plugin.json
|
||||
|
||||
- name: Check required files
|
||||
run: |
|
||||
test -f README.md || exit 1
|
||||
test -f LICENSE || exit 1
|
||||
|
||||
- name: Validate commands
|
||||
run: |
|
||||
if [ -d commands ]; then
|
||||
for f in commands/**/*.md; do
|
||||
grep -q "^---$" "$f" || exit 1
|
||||
done
|
||||
fi
|
||||
```
|
||||
|
||||
### GitHub Actions - Release
|
||||
|
||||
**.github/workflows/release.yml:**
|
||||
|
||||
```yaml
|
||||
name: Release Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Validate
|
||||
run: |
|
||||
test -f plugin.json
|
||||
test -f README.md
|
||||
test -f LICENSE
|
||||
jq empty plugin.json
|
||||
|
||||
- name: Get version
|
||||
id: version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Verify version match
|
||||
run: |
|
||||
PLUGIN_VERSION=$(jq -r '.version' plugin.json)
|
||||
if [ "$PLUGIN_VERSION" != "${{ steps.version.outputs.VERSION }}" ]; then
|
||||
echo "Version mismatch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Create package
|
||||
run: |
|
||||
PLUGIN_NAME=$(jq -r '.name' plugin.json)
|
||||
cd ..
|
||||
zip -r "${PLUGIN_NAME}-v${{ steps.version.outputs.VERSION }}.zip" \
|
||||
"${PLUGIN_NAME}/" -x "*.git*" "*.github*"
|
||||
mv *.zip "${PLUGIN_NAME}/"
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: '*-v${{ steps.version.outputs.VERSION }}.zip'
|
||||
generate_release_notes: true
|
||||
```
|
||||
|
||||
## Distribution Methods
|
||||
|
||||
### Method 1: GitHub Repository
|
||||
|
||||
```bash
|
||||
# Users install:
|
||||
/plugin marketplace add username/my-plugin
|
||||
/plugin install my-plugin@username
|
||||
```
|
||||
|
||||
**Advantages:** Version control, issues, free hosting
|
||||
|
||||
### Method 2: Git URL
|
||||
|
||||
```bash
|
||||
# For GitLab, Bitbucket, self-hosted
|
||||
/plugin marketplace add https://gitlab.com/user/my-plugin.git
|
||||
```
|
||||
|
||||
### Method 3: Marketplace Entry
|
||||
|
||||
Add to existing marketplace:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": [
|
||||
{
|
||||
"name": "my-plugin",
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "username/my-plugin"
|
||||
},
|
||||
"description": "Plugin description",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Method 4: Direct Download
|
||||
|
||||
Host ZIP and provide instructions:
|
||||
|
||||
```bash
|
||||
wget https://example.com/my-plugin.zip
|
||||
unzip my-plugin.zip
|
||||
/plugin marketplace add ./my-plugin
|
||||
/plugin install my-plugin@my-plugin
|
||||
```
|
||||
|
||||
## Pre-Release Testing
|
||||
|
||||
### Beta Release
|
||||
|
||||
```bash
|
||||
# Update to pre-release version
|
||||
jq '.version = "2.0.0-beta.1"' plugin.json > temp.json
|
||||
mv temp.json plugin.json
|
||||
|
||||
# Tag and release as prerelease
|
||||
git tag v2.0.0-beta.1
|
||||
git push origin --tags
|
||||
|
||||
gh release create v2.0.0-beta.1 \
|
||||
--title "v2.0.0-beta.1" \
|
||||
--prerelease \
|
||||
--notes "Beta release for testing"
|
||||
```
|
||||
|
||||
## Hotfix Process
|
||||
|
||||
```bash
|
||||
# 1. Create hotfix branch
|
||||
git checkout -b hotfix/critical-fix
|
||||
|
||||
# 2. Fix and test
|
||||
# 3. Bump patch version
|
||||
# 4. Merge to main
|
||||
git checkout main
|
||||
git merge hotfix/critical-fix
|
||||
|
||||
# 5. Tag and release
|
||||
git tag v1.0.1
|
||||
git push origin main --tags
|
||||
|
||||
gh release create v1.0.1 \
|
||||
--title "v1.0.1 - Critical Fix" \
|
||||
--latest
|
||||
```
|
||||
|
||||
## Version Bump Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
BUMP_TYPE="${1:-patch}"
|
||||
CURRENT=$(jq -r '.version' plugin.json)
|
||||
|
||||
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
|
||||
|
||||
case $BUMP_TYPE in
|
||||
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
|
||||
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
|
||||
patch) PATCH=$((PATCH + 1)) ;;
|
||||
esac
|
||||
|
||||
NEW="${MAJOR}.${MINOR}.${PATCH}"
|
||||
jq --arg v "$NEW" '.version = $v' plugin.json > temp.json
|
||||
mv temp.json plugin.json
|
||||
|
||||
echo "Bumped to $NEW"
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
./bump-version.sh patch # 1.0.0 -> 1.0.1
|
||||
./bump-version.sh minor # 1.0.1 -> 1.1.0
|
||||
./bump-version.sh major # 1.1.0 -> 2.0.0
|
||||
```
|
||||
@@ -0,0 +1,633 @@
|
||||
# Marketplace Reference
|
||||
|
||||
Complete schema, hosting strategies, and team configuration for Claude Code plugin marketplaces.
|
||||
|
||||
## What is a Marketplace?
|
||||
|
||||
A marketplace is a catalog of plugins defined in `.claude-plugin/marketplace.json` that enables:
|
||||
- Plugin discovery
|
||||
- One-command installation
|
||||
- Version management
|
||||
- Team distribution
|
||||
|
||||
## Marketplace Schema
|
||||
|
||||
### Required Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | string | Marketplace identifier (kebab-case, no spaces) |
|
||||
| `owner` | object | Maintainer information |
|
||||
| `plugins` | array | List of plugin entries |
|
||||
|
||||
### Owner Fields
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `owner.name` | Yes | Name of maintainer or team |
|
||||
| `owner.email` | No | Contact email |
|
||||
|
||||
### Reserved Names
|
||||
|
||||
The following marketplace names are reserved and cannot be used:
|
||||
|
||||
- `claude-code-marketplace`
|
||||
- `claude-code-plugins`
|
||||
- `claude-plugins-official`
|
||||
- `anthropic-marketplace`
|
||||
- `anthropic-plugins`
|
||||
- `agent-skills`
|
||||
- `life-sciences`
|
||||
|
||||
Names that impersonate official marketplaces (like `official-claude-plugins` or `anthropic-tools-v2`) are also blocked.
|
||||
|
||||
### Optional Metadata
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `metadata.description` | string | Brief marketplace description |
|
||||
| `metadata.version` | string | Marketplace version |
|
||||
| `metadata.pluginRoot` | string | Documentation hint for where plugins live. Does NOT affect schema validation—always use explicit `./` prefix in source paths. |
|
||||
|
||||
### Complete Example
|
||||
|
||||
For local plugins (relative paths), use `strict: false` to consolidate metadata. Always use explicit `./` prefix for source paths:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "company-tools",
|
||||
"owner": {
|
||||
"name": "Engineering Team",
|
||||
"email": "eng@company.com"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Internal development tools",
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"strict": false,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "code-formatter",
|
||||
"source": "./code-formatter",
|
||||
"version": "1.0.0",
|
||||
"description": "Auto-format code on save",
|
||||
"license": "MIT"
|
||||
},
|
||||
{
|
||||
"name": "deployment-tools",
|
||||
"source": "./deployment",
|
||||
"version": "2.1.0",
|
||||
"description": "Deploy to staging and production",
|
||||
"license": "MIT"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
With `strict: false`, plugins don't need their own `.claude-plugin/plugin.json`—the marketplace is the single source of truth.
|
||||
|
||||
## Plugin Entry Schema
|
||||
|
||||
### Local Plugins (Consolidated)
|
||||
|
||||
For plugins in the same repo as the marketplace, define all metadata in the marketplace entry:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "code-formatter",
|
||||
"source": "./code-formatter",
|
||||
"version": "1.0.0",
|
||||
"description": "Auto-format code on save",
|
||||
"license": "MIT",
|
||||
"keywords": ["formatting", "linting"]
|
||||
}
|
||||
```
|
||||
|
||||
Set `strict: false` at the marketplace level. Plugins don't need their own `.claude-plugin/plugin.json`.
|
||||
|
||||
**Benefits:** Single source of truth, prevents version/metadata drift between marketplace and plugin manifests.
|
||||
|
||||
### External Plugins (Distributed)
|
||||
|
||||
For plugins in external repos, use minimal entries—let the external repo own its manifest:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "enterprise-tools",
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "company/enterprise-plugin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The external repo should have its own `.claude-plugin/plugin.json` with metadata.
|
||||
|
||||
**Why:** External plugins may be used outside your marketplace. They should be self-contained.
|
||||
|
||||
### Entry Fields
|
||||
|
||||
**Required:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | string | Plugin identifier (kebab-case, no spaces) |
|
||||
| `source` | string\|object | Where to fetch plugin (relative path, GitHub, or git URL) |
|
||||
|
||||
**Standard metadata** (optional):
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `description` | string | Brief plugin description |
|
||||
| `version` | string | Plugin version |
|
||||
| `author` | object | Author info (`name` required, `email` optional) |
|
||||
| `homepage` | string | Documentation URL |
|
||||
| `repository` | string | Source code URL |
|
||||
| `license` | string | SPDX identifier (MIT, Apache-2.0) |
|
||||
| `keywords` | array | Tags for discovery |
|
||||
| `category` | string | Plugin category |
|
||||
| `tags` | array | Additional searchability tags |
|
||||
|
||||
**Behavior control:**
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `strict` | boolean | `true` | When `false`, plugins don't need their own `.claude-plugin/plugin.json`—marketplace defines everything. Use for local plugins (relative paths). When `true`, plugins must have their own manifest. |
|
||||
|
||||
**When to use each mode:**
|
||||
|
||||
| Pattern | `strict` | Use when |
|
||||
|---------|----------|----------|
|
||||
| Consolidated | `false` | All plugins are local (relative paths in same repo) |
|
||||
| Distributed | `true` | Plugins are external repos that may be used elsewhere |
|
||||
| Mixed | `false` | Local plugins consolidated, external plugins own their manifests |
|
||||
|
||||
**Component configuration** (optional):
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `commands` | string\|array | Custom paths to command files or directories |
|
||||
| `agents` | string\|array | Custom paths to agent files |
|
||||
| `hooks` | string\|object | Hook configuration or path to hooks file |
|
||||
| `mcpServers` | string\|object | MCP server configuration or path to MCP config |
|
||||
| `lspServers` | string\|object | LSP server configuration or path to LSP config |
|
||||
|
||||
## Plugin Source Types
|
||||
|
||||
### Relative Path
|
||||
|
||||
For plugins in the same repository, always use explicit `./` prefix paths:
|
||||
|
||||
**Plugins at repo root:**
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": [
|
||||
{"name": "my-plugin", "source": "./my-plugin"},
|
||||
{"name": "another", "source": "./another"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Plugins in a subdirectory:**
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": [
|
||||
{"name": "my-plugin", "source": "./packages/my-plugin"},
|
||||
{"name": "another", "source": "./packages/another"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The schema requires the `./` prefix for relative paths. Bare names like `"source": "my-plugin"` fail validation.
|
||||
|
||||
### GitHub Repository
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "owner/plugin-repo"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With specific version:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "owner/plugin-repo",
|
||||
"ref": "v1.5.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Pin to exact commit:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "owner/plugin-repo",
|
||||
"ref": "v2.0.0",
|
||||
"sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Monorepo pattern (plugin in a subdirectory):
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "owner/my-project",
|
||||
"path": "./packages/claude-plugin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `path` when the plugin lives alongside application code in a monorepo. Common patterns:
|
||||
|
||||
| Monorepo Structure | `path` Value |
|
||||
|--------------------|--------------|
|
||||
| `packages/claude-plugin/` | `./packages/claude-plugin` |
|
||||
| `.claude-plugin/` at root | (omit — this is the default) |
|
||||
| `tools/claude-plugin/` | `./tools/claude-plugin` |
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `repo` | string | Required. GitHub repository in `owner/repo` format |
|
||||
| `path` | string | Optional. Subdirectory containing `.claude-plugin/plugin.json` |
|
||||
| `ref` | string | Optional. Branch name or tag (omit to use default branch) |
|
||||
| `sha` | string | Optional. Full 40-character commit SHA for exact version pinning |
|
||||
|
||||
### Git URL
|
||||
|
||||
For GitLab, Bitbucket, or self-hosted:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"source": "url",
|
||||
"url": "https://gitlab.com/team/plugin.git"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With specific branch or SHA pinning:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"source": "url",
|
||||
"url": "https://gitlab.com/team/plugin.git",
|
||||
"ref": "develop",
|
||||
"sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `url` | string | Required. Full git repository URL (must end with `.git`) |
|
||||
| `path` | string | Optional. Subdirectory containing `.claude-plugin/plugin.json` |
|
||||
| `ref` | string | Optional. Branch name or tag (omit to use default branch) |
|
||||
| `sha` | string | Optional. Full 40-character commit SHA for exact version pinning |
|
||||
|
||||
### Private Repository Authentication
|
||||
|
||||
Claude Code supports installing plugins from private repositories. Set the appropriate authentication token in your environment:
|
||||
|
||||
| Provider | Environment Variables | Notes |
|
||||
|----------|----------------------|-------|
|
||||
| GitHub | `GITHUB_TOKEN` or `GH_TOKEN` | Personal access token or GitHub App token |
|
||||
| GitLab | `GITLAB_TOKEN` or `GL_TOKEN` | Personal access token or project token |
|
||||
| Bitbucket | `BITBUCKET_TOKEN` | App password or repository access token |
|
||||
|
||||
Set the token in your shell configuration (`.bashrc`, `.zshrc`) or pass it when running Claude Code:
|
||||
|
||||
```bash
|
||||
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
Authentication tokens are only used when a repository requires authentication. Public repositories work without tokens.
|
||||
|
||||
## Marketplace Types
|
||||
|
||||
### Team/Organization
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "company-internal",
|
||||
"owner": {
|
||||
"name": "Engineering",
|
||||
"email": "eng@company.com"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Internal development tools"
|
||||
},
|
||||
"plugins": [
|
||||
{"name": "deploy-tools", "source": "./plugins/deploy"},
|
||||
{"name": "compliance", "source": "./plugins/compliance"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Hosting:** Private GitHub repo or internal Git
|
||||
|
||||
### Project-Specific
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "project-tools",
|
||||
"owner": {
|
||||
"name": "Project Team",
|
||||
"email": "project@company.com"
|
||||
},
|
||||
"plugins": [
|
||||
{"name": "project-workflow", "source": "./plugins/workflow"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Hosting:** In project at `.claude-plugin/marketplace.json`
|
||||
|
||||
### Public/Community
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "awesome-plugins",
|
||||
"owner": {
|
||||
"name": "Community"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Curated Claude Code plugins"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "markdown-tools",
|
||||
"source": {"source": "github", "repo": "user/markdown-tools"},
|
||||
"license": "MIT"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Team Configuration
|
||||
|
||||
### Automatic Installation
|
||||
|
||||
Configure in `.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"extraKnownMarketplaces": {
|
||||
"team-tools": {
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "company/claude-plugins"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Automatically installed when team members trust the folder.
|
||||
|
||||
### Multi-Environment
|
||||
|
||||
```json
|
||||
{
|
||||
"extraKnownMarketplaces": {
|
||||
"development": {
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "company/plugins",
|
||||
"ref": "develop"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "company/plugins"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `ref` to point development at a non-default branch. Production uses the default branch (no `ref` needed).
|
||||
|
||||
## Marketplace Commands
|
||||
|
||||
### Adding Marketplaces
|
||||
|
||||
```bash
|
||||
# GitHub (short form)
|
||||
/plugin marketplace add owner/repo
|
||||
|
||||
# GitHub (full URL)
|
||||
/plugin marketplace add https://github.com/owner/repo
|
||||
|
||||
# Git repository
|
||||
/plugin marketplace add https://gitlab.com/company/plugins.git
|
||||
|
||||
# Local directory
|
||||
/plugin marketplace add ./path/to/marketplace
|
||||
|
||||
# Remote JSON URL
|
||||
/plugin marketplace add https://example.com/marketplace.json
|
||||
```
|
||||
|
||||
### Management
|
||||
|
||||
```bash
|
||||
# List marketplaces
|
||||
/plugin marketplace list
|
||||
|
||||
# Update specific
|
||||
/plugin marketplace update marketplace-name
|
||||
|
||||
# Update all
|
||||
/plugin marketplace update --all
|
||||
|
||||
# Remove (also uninstalls plugins)
|
||||
/plugin marketplace remove marketplace-name
|
||||
|
||||
# View details
|
||||
/plugin marketplace info marketplace-name
|
||||
```
|
||||
|
||||
### Plugin Installation
|
||||
|
||||
```bash
|
||||
# From marketplace
|
||||
/plugin install plugin-name@marketplace-name
|
||||
|
||||
# Specific version
|
||||
/plugin install plugin-name@marketplace-name@1.2.0
|
||||
|
||||
# List available
|
||||
/plugin list marketplace-name
|
||||
|
||||
# Search across marketplaces
|
||||
/plugin search keyword
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
### Validate JSON
|
||||
|
||||
```bash
|
||||
# Syntax check
|
||||
jq empty .claude-plugin/marketplace.json
|
||||
|
||||
# Required fields
|
||||
jq -e '.name, .owner, .plugins' .claude-plugin/marketplace.json
|
||||
|
||||
# Plugin entries
|
||||
jq -e '.plugins[] | .name, .source' .claude-plugin/marketplace.json
|
||||
```
|
||||
|
||||
### Validate Sources
|
||||
|
||||
```bash
|
||||
# Check relative paths
|
||||
for plugin in $(jq -r '.plugins[] | select(.source | type == "string") | .source' .claude-plugin/marketplace.json); do
|
||||
if [[ ! -d "$plugin" ]]; then
|
||||
echo "Missing: $plugin"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check GitHub repos
|
||||
for repo in $(jq -r '.plugins[] | select(.source.source == "github") | .source.repo' .claude-plugin/marketplace.json); do
|
||||
gh repo view "$repo" > /dev/null || echo "Invalid: $repo"
|
||||
done
|
||||
```
|
||||
|
||||
## Hosting Strategies
|
||||
|
||||
### GitHub (Recommended)
|
||||
|
||||
**Advantages:**
|
||||
- Version control
|
||||
- Issue tracking
|
||||
- Collaboration
|
||||
- Free hosting
|
||||
- Easy sharing
|
||||
|
||||
**Setup:**
|
||||
1. Create repository
|
||||
2. Add `.claude-plugin/marketplace.json`
|
||||
3. Push
|
||||
4. Share: `/plugin marketplace add owner/repo`
|
||||
|
||||
### GitLab/Bitbucket
|
||||
|
||||
```bash
|
||||
/plugin marketplace add https://gitlab.com/company/plugins.git
|
||||
```
|
||||
|
||||
**Advantages:**
|
||||
- Self-hosted options
|
||||
- Enterprise integration
|
||||
|
||||
### Local Development
|
||||
|
||||
```bash
|
||||
/plugin marketplace add ./my-marketplace
|
||||
```
|
||||
|
||||
**Advantages:**
|
||||
- Fast iteration
|
||||
- No network required
|
||||
- Easy testing
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml
|
||||
name: Validate Marketplace
|
||||
|
||||
on:
|
||||
push:
|
||||
paths: ['.claude-plugin/marketplace.json']
|
||||
pull_request:
|
||||
paths: ['.claude-plugin/marketplace.json']
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Validate JSON
|
||||
run: jq empty .claude-plugin/marketplace.json
|
||||
|
||||
- name: Check fields
|
||||
run: jq -e '.name, .owner.name, .plugins' .claude-plugin/marketplace.json
|
||||
|
||||
- name: Check sources
|
||||
run: |
|
||||
for plugin in $(jq -r '.plugins[] | select(.source | type == "string") | .source' .claude-plugin/marketplace.json); do
|
||||
if [[ ! -d "$plugin" ]]; then
|
||||
echo "Missing: $plugin"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Organization
|
||||
|
||||
- Group related plugins together
|
||||
- Use categories for discovery
|
||||
- Maintain consistent naming
|
||||
- Document plugin purposes
|
||||
|
||||
### Versioning
|
||||
|
||||
- Use semantic versioning
|
||||
- Track versions in entries
|
||||
- Maintain CHANGELOG
|
||||
- Tag releases in Git
|
||||
|
||||
### Security
|
||||
|
||||
- Review plugins before adding
|
||||
- Verify sources
|
||||
- Document requirements
|
||||
- Use private repos for sensitive tools
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Keep versions updated
|
||||
- Remove deprecated plugins
|
||||
- Test after updates
|
||||
- Monitor feedback
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Marketplace not loading:**
|
||||
- Verify URL accessible
|
||||
- Check `.claude-plugin/marketplace.json` exists
|
||||
- Validate JSON syntax
|
||||
- Confirm access for private repos
|
||||
|
||||
**Plugin installation failures:**
|
||||
- Verify source URLs accessible
|
||||
- Check plugin directories exist
|
||||
- Test sources manually
|
||||
- Review error messages
|
||||
|
||||
**Team configuration not working:**
|
||||
- Verify `.claude/settings.json` syntax
|
||||
- Check marketplace sources accessible
|
||||
- Ensure folder trusted
|
||||
- Restart Claude Code
|
||||
@@ -0,0 +1,502 @@
|
||||
# Plugin Structure Reference
|
||||
|
||||
Complete directory layout and configuration schemas for Claude Code plugins.
|
||||
|
||||
## Plugin Structure
|
||||
|
||||
How you structure plugins depends on how they're distributed.
|
||||
|
||||
### Single Plugin (Standalone)
|
||||
|
||||
Standalone plugins need their own `.claude-plugin/plugin.json`:
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json # Plugin manifest (required)
|
||||
├── commands/
|
||||
├── agents/
|
||||
├── hooks/
|
||||
│ └── hooks.json # Auto-discovered hooks (or inline in plugin.json)
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### Marketplace with Local Plugins (Consolidated)
|
||||
|
||||
When all plugins live in the same repo as the marketplace, use `strict: false` to consolidate metadata in `marketplace.json`. Plugins don't need their own manifests:
|
||||
|
||||
```
|
||||
my-marketplace/
|
||||
├── .claude-plugin/
|
||||
│ └── marketplace.json # All metadata here (strict: false)
|
||||
├── plugin-a/
|
||||
│ ├── commands/
|
||||
│ ├── agents/
|
||||
│ └── README.md
|
||||
├── plugin-b/
|
||||
│ ├── skills/
|
||||
│ └── README.md
|
||||
└── README.md
|
||||
```
|
||||
|
||||
**Benefits:** Single source of truth, no version drift, simpler structure.
|
||||
|
||||
### Marketplace with External Plugins (Distributed)
|
||||
|
||||
When referencing plugins from external repos (GitHub, GitLab, etc.), let each plugin own its manifest:
|
||||
|
||||
```
|
||||
my-marketplace/
|
||||
├── .claude-plugin/
|
||||
│ └── marketplace.json # Points to external repos
|
||||
└── README.md
|
||||
|
||||
# External repos each have:
|
||||
external-plugin/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json # Plugin owns its manifest
|
||||
├── commands/
|
||||
└── README.md
|
||||
```
|
||||
|
||||
**Key principle:** External plugins are self-contained. The marketplace.json points to them via `source` but doesn't define their metadata.
|
||||
|
||||
### Mixed Approach
|
||||
|
||||
Marketplaces can combine both patterns—consolidated for local plugins, distributed for external:
|
||||
|
||||
```json
|
||||
{
|
||||
"strict": false,
|
||||
"plugins": [
|
||||
{"name": "local-plugin", "source": "./local-plugin", "version": "1.0.0"},
|
||||
{"name": "external-plugin", "source": {"source": "github", "repo": "owner/plugin"}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
### Minimal Standalone Plugin
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json # Required: metadata
|
||||
└── README.md # Required for distribution
|
||||
```
|
||||
|
||||
### Complete Standalone Plugin
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json # Plugin metadata
|
||||
├── README.md # Documentation
|
||||
├── CHANGELOG.md # Version history
|
||||
├── LICENSE # License file
|
||||
├── .gitignore # Git ignore patterns
|
||||
├── commands/ # Slash commands
|
||||
│ ├── core/ # Core commands
|
||||
│ │ └── help.md
|
||||
│ └── advanced/ # Advanced features
|
||||
│ └── deploy.md
|
||||
├── agents/ # Custom agents
|
||||
│ ├── reviewer.md
|
||||
│ └── analyzer.md
|
||||
├── skills/ # Reusable skills
|
||||
│ └── my-skill/
|
||||
│ └── SKILL.md
|
||||
├── hooks/ # Event hooks
|
||||
│ └── hooks.json # Auto-discovered (required format)
|
||||
├── servers/ # MCP servers
|
||||
│ └── my-server/
|
||||
│ ├── server.py
|
||||
│ └── pyproject.toml
|
||||
└── scripts/ # Utility scripts
|
||||
└── setup.sh
|
||||
```
|
||||
|
||||
## plugin.json Schema
|
||||
|
||||
### Required Fields
|
||||
|
||||
| Field | Type | Description | Example |
|
||||
|-------|------|-------------|---------|
|
||||
| `name` | string | Unique identifier (kebab-case, no spaces) | `"deployment-tools"` |
|
||||
|
||||
### Recommended Metadata Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `version` | string | Semantic version (e.g., "1.0.0") |
|
||||
| `description` | string | Brief plugin description |
|
||||
|
||||
### Optional Standard Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `author` | object | Creator information |
|
||||
| `author.name` | string | Author name |
|
||||
| `author.email` | string | Author email |
|
||||
| `homepage` | string | Documentation URL |
|
||||
| `repository` | string | Source code URL |
|
||||
| `license` | string | SPDX identifier (MIT, Apache-2.0) |
|
||||
| `keywords` | array | Search tags |
|
||||
| `category` | string | Plugin category |
|
||||
| `tags` | array | Additional searchability tags |
|
||||
|
||||
### Component Configuration Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `commands` | string\|array | Custom paths to command files or directories |
|
||||
| `agents` | string\|array | Custom paths to agent files |
|
||||
| `hooks` | string\|object | Hook config path or inline config |
|
||||
| `mcpServers` | string\|object | MCP config path or inline config |
|
||||
| `lspServers` | string\|object | LSP config path or inline config |
|
||||
|
||||
### Behavior Control
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `strict` | boolean | `true` | Set at marketplace level. When `false`, local plugins don't need `.claude-plugin/plugin.json`—marketplace defines all metadata. Use `false` for consolidated local plugins, `true` (or omit) for external plugins. |
|
||||
|
||||
> **Note:** Hooks can be defined inline in plugin.json OR in a separate `hooks/hooks.json` file.
|
||||
|
||||
### Complete Example
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "enterprise-tools",
|
||||
"version": "2.1.0",
|
||||
"description": "Enterprise workflow automation tools",
|
||||
"author": {
|
||||
"name": "Enterprise Team",
|
||||
"email": "team@company.com"
|
||||
},
|
||||
"homepage": "https://docs.company.com/plugins",
|
||||
"repository": "https://github.com/company/enterprise-tools",
|
||||
"license": "MIT",
|
||||
"keywords": ["enterprise", "workflow", "automation"],
|
||||
"category": "productivity",
|
||||
"commands": [
|
||||
"./commands/core/",
|
||||
"./commands/enterprise/"
|
||||
],
|
||||
"agents": [
|
||||
"./agents/security-reviewer.md",
|
||||
"./agents/compliance-checker.md"
|
||||
],
|
||||
"mcpServers": {
|
||||
"database": {
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/servers/db-server",
|
||||
"args": ["--config", "${CLAUDE_PLUGIN_ROOT}/config.json"],
|
||||
"env": {
|
||||
"DB_HOST": "${DATABASE_HOST}",
|
||||
"DB_PASSWORD": "${DATABASE_PASSWORD}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** Hooks can be inlined in plugin.json (add a `"hooks"` key) or defined in `hooks/hooks.json`. See [Event Hooks](#event-hooks) below.
|
||||
|
||||
## Slash Commands
|
||||
|
||||
### Command File Structure
|
||||
|
||||
Commands are markdown files with YAML frontmatter in `commands/`.
|
||||
|
||||
```markdown
|
||||
---
|
||||
description: "Brief description shown in /help"
|
||||
---
|
||||
|
||||
Command instructions here.
|
||||
Use {{0}}, {{1}} for parameters.
|
||||
```
|
||||
|
||||
### Parameter Syntax
|
||||
|
||||
| Syntax | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `{{0}}` | First parameter | `/cmd value` |
|
||||
| `{{1}}` | Second parameter | `/cmd val1 val2` |
|
||||
| `{{0:name}}` | Named (documentation) | `{{0:environment}}` |
|
||||
| `{{...}}` | All remaining | `/cmd arg1 arg2 arg3` |
|
||||
|
||||
### Example Command
|
||||
|
||||
```markdown
|
||||
---
|
||||
description: "Deploy to specified environment"
|
||||
---
|
||||
|
||||
Deploy application to {{0:environment}}.
|
||||
|
||||
Steps:
|
||||
1. Validate environment configuration
|
||||
2. Run pre-deployment checks
|
||||
3. Deploy application
|
||||
4. Verify deployment
|
||||
```
|
||||
|
||||
## Custom Agents
|
||||
|
||||
### Agent File Structure
|
||||
|
||||
Agents are markdown files with YAML frontmatter in `agents/`.
|
||||
|
||||
```markdown
|
||||
---
|
||||
description: "What this agent specializes in"
|
||||
capabilities: ["task1", "task2", "task3"]
|
||||
allowed-tools: Read, Grep, Glob
|
||||
---
|
||||
|
||||
# Agent Name
|
||||
|
||||
Detailed description of the agent's role, expertise, and when Claude should invoke it.
|
||||
|
||||
## Capabilities
|
||||
|
||||
- Specific task the agent excels at
|
||||
- Another specialized capability
|
||||
- When to use this agent vs others
|
||||
|
||||
## Context and examples
|
||||
|
||||
Provide examples of when this agent should be used.
|
||||
```
|
||||
|
||||
### Agent Frontmatter Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `description` | string | Brief explanation of what the agent does |
|
||||
| `capabilities` | array | List of tasks the agent can perform (aids discovery) |
|
||||
| `allowed-tools` | string | Comma-separated list of allowed tools (optional) |
|
||||
|
||||
### Tool Restrictions
|
||||
|
||||
| Restriction | Tools | Use Case |
|
||||
|-------------|-------|----------|
|
||||
| Read-only | `Read, Grep, Glob` | Analysis only |
|
||||
| With execution | `Read, Grep, Glob, Bash` | Analysis + commands |
|
||||
| No restriction | (omit field) | Full capabilities |
|
||||
|
||||
## Event Hooks
|
||||
|
||||
Two ways to define hooks in a plugin:
|
||||
|
||||
1. **Inline in plugin.json** — Add a `"hooks"` key directly
|
||||
2. **File-based** — Auto-discovered from `hooks/hooks.json`
|
||||
|
||||
### Option 1: Inline in plugin.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-plugin",
|
||||
"version": "1.0.0",
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Option 2: Separate hooks.json File
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json # Can also have hooks inline here
|
||||
└── hooks/
|
||||
└── hooks.json # Auto-discovered if present
|
||||
```
|
||||
|
||||
### Hook Types
|
||||
|
||||
| Type | When | Use Cases |
|
||||
|------|------|-----------|
|
||||
| `PreToolUse` | Before tool | Validation, permissions |
|
||||
| `PostToolUse` | After tool | Logging, formatting |
|
||||
| `UserPromptSubmit` | Before prompt | Input validation |
|
||||
| `Stop` | After response | Cleanup, notifications |
|
||||
| `SessionStart` | Session begins | Context loading |
|
||||
| `SessionEnd` | Session ends | Cleanup |
|
||||
|
||||
### hooks/hooks.json Format
|
||||
|
||||
When using a separate file, it requires a root-level `"hooks"` wrapper:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
|
||||
"timeout": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write(*.ts)",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "biome check --write \"$file\"",
|
||||
"timeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Key Points
|
||||
|
||||
- Hooks can be inline in plugin.json OR in `hooks/hooks.json`
|
||||
- Both formats use a `"hooks"` object containing event types
|
||||
- Use `${CLAUDE_PLUGIN_ROOT}` for paths relative to plugin
|
||||
- Use `$file` for the affected file path in PostToolUse
|
||||
|
||||
### Hook Script Interface
|
||||
|
||||
**Input (stdin):**
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "/path/to/transcript.jsonl",
|
||||
"cwd": "/current/working/directory",
|
||||
"hook_event_name": "PreToolUse",
|
||||
"tool_name": "Write",
|
||||
"tool_input": {
|
||||
"file_path": "/project/src/file.ts",
|
||||
"content": "export const foo = 'bar';"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Output (stdout):**
|
||||
|
||||
Allow:
|
||||
|
||||
```json
|
||||
{"allowed": true}
|
||||
```
|
||||
|
||||
Block:
|
||||
|
||||
```json
|
||||
{
|
||||
"allowed": false,
|
||||
"message": "Validation failed: reason"
|
||||
}
|
||||
```
|
||||
|
||||
Modify:
|
||||
|
||||
```json
|
||||
{
|
||||
"allowed": true,
|
||||
"modified_parameters": {
|
||||
"content": "modified content"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example Hook Script
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
input=$(cat)
|
||||
|
||||
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
|
||||
content=$(echo "$input" | jq -r '.tool_input.content')
|
||||
|
||||
# Check for secrets
|
||||
if echo "$content" | grep -qiE 'api[_-]?key.*=.*[a-zA-Z0-9]{16,}'; then
|
||||
echo '{"allowed": false, "message": "Potential secret detected"}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo '{"allowed": true}'
|
||||
```
|
||||
|
||||
## MCP Servers
|
||||
|
||||
### Server Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"server-name": {
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/servers/my-server",
|
||||
"args": ["--flag", "value"],
|
||||
"env": {
|
||||
"API_KEY": "${MY_API_KEY}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Variable Substitution
|
||||
|
||||
| Variable | Resolves To |
|
||||
|----------|-------------|
|
||||
| `${CLAUDE_PLUGIN_ROOT}` | Plugin installation directory |
|
||||
| `${VAR_NAME}` | Environment variable |
|
||||
|
||||
### Python MCP Server Example
|
||||
|
||||
```python
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("my-server")
|
||||
|
||||
@mcp.tool()
|
||||
async def my_tool(param: str) -> str:
|
||||
"""Tool description"""
|
||||
return f"Result: {param}"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport='stdio')
|
||||
```
|
||||
|
||||
## Platform Considerations
|
||||
|
||||
### macOS
|
||||
|
||||
- Config: `~/Library/Application Support/Claude/`
|
||||
- Logs: `~/Library/Logs/Claude/`
|
||||
|
||||
### Windows
|
||||
|
||||
- Config: `%APPDATA%\Claude\`
|
||||
- Use forward slashes or double backslashes
|
||||
|
||||
### Linux
|
||||
|
||||
- Config: `~/.config/claude/`
|
||||
- Check shebang and permissions
|
||||
Reference in New Issue
Block a user