📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-05-29 08:33:53 +00:00
parent fdb52f1e96
commit 06e0d13d57
1615 changed files with 232858 additions and 0 deletions
@@ -0,0 +1,420 @@
---
name: claude-plugins
description: This skill should be used when creating plugins, publishing to marketplaces, or when "plugin.json", "marketplace", "create plugin", or "distribute plugin" are mentioned.
metadata:
version: "1.0.0"
related-skills:
- claude-plugin-audit
- claude-agents
- claude-commands
- claude-hooks
- skills-dev
- claude-rules
- claude-config
---
# Claude Plugin Development
Complete lifecycle for developing, validating, and distributing Claude Code plugins.
## Steps
1. Define plugin scope and components needed
2. Initialize plugin structure with `plugin.json`
3. If adding commands, load the `outfitter:claude-commands` skill
4. If adding agents, load the `outfitter:claude-agents` skill
5. If adding hooks, load the `outfitter:claude-hooks` skill
6. If adding skills, load the `outfitter:skills-dev` skill
7. Delegate by loading the `outfitter:claude-plugin-audit` skill for validation
8. Fix issues and distribute
## Quick Start
```bash
# 1. Scaffold plugin
./scripts/scaffold-plugin.sh my-plugin --with-commands
# 2. Add components (commands, agents, hooks, skills)
# 3. Test locally
/plugin marketplace add ./my-plugin
/plugin install my-plugin@my-plugin
# 4. Distribute
git push origin main --tags
```
## Lifecycle Overview
```
Discovery -> Init -> Components -> Validate -> Distribute -> Marketplace
| | | | | |
v v v v v v
Purpose Scaffold Commands Structure Package Catalog
Scope plugin.json Agents Testing Version Publish
Type README Hooks Quality Release Share
```
## Stage 1: Discovery
Before creating a plugin, clarify:
| Question | Impact |
|----------|--------|
| What problem does this solve? | Plugin scope and features |
| Who will use it? | Distribution method |
| What components are needed? | Commands, agents, hooks, MCP servers |
| Where will it live? | Personal, project, or marketplace |
## Stage 2: Initialization
### Standalone Plugin
Standalone plugins need their own `.claude-plugin/plugin.json`:
```
my-plugin/
├── .claude-plugin/
│ └── plugin.json # Required for standalone
├── README.md # Required for distribution
├── commands/ # Optional components
├── agents/
├── skills/
└── hooks/
```
### plugin.json (Standalone)
```json
{
"name": "my-plugin",
"version": "1.0.0",
"description": "Brief description of what this plugin does",
"author": {
"name": "Your Name",
"email": "you@example.com"
},
"license": "MIT"
}
```
### Marketplace with Local Plugins (Consolidated)
For marketplaces where all plugins live in the same repo, use `strict: false` to consolidate metadata. Plugins don't need their own manifests:
```
my-marketplace/
├── .claude-plugin/
│ └── marketplace.json # All metadata here (strict: false)
├── plugin-a/
│ └── commands/
├── plugin-b/
│ └── skills/
└── README.md
```
### marketplace.json (Consolidated)
```json
{
"name": "my-marketplace",
"owner": {
"name": "Team Name",
"email": "team@example.com"
},
"strict": false,
"plugins": [
{"name": "plugin-a", "source": "./plugin-a", "version": "1.0.0", "description": "Plugin A", "license": "MIT"},
{"name": "plugin-b", "source": "./plugin-b", "version": "1.0.0", "description": "Plugin B", "license": "MIT"}
]
}
```
**Benefits:** Single source of truth, no version drift between marketplace and plugin manifests.
For external plugins (GitHub repos), use minimal entries and let the external repo own its manifest.
See [structure.md](references/structure.md) for complete plugin.json schema.
## Stage 3: Components
Add components based on plugin needs. See Steps section for which skills to load.
### Slash Commands
Create custom commands in `commands/` directory:
```markdown
---
description: "Review code for quality issues"
---
Review the following code: {{0}}
Check for: code style, bugs, performance, security
```
For complex commands, load the `outfitter:claude-commands` skill.
### Custom Agents
Define specialized agents in `agents/` directory:
```markdown
---
name: security-reviewer
description: "Security-focused code reviewer"
---
You are a security expert. When reviewing code:
1. Check for vulnerabilities
2. Verify input validation
3. Report issues with severity levels
```
For agent design patterns, load the `outfitter:claude-agents` skill.
### Event Hooks
Two ways to define hooks:
**File-based** (auto-discovered from `hooks/hooks.json`):
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh"}]
}
]
}
}
```
**Inline in plugin.json** - same structure, add `"hooks"` key directly.
Hook types: PreToolUse, PostToolUse, UserPromptSubmit, Stop, SessionStart, SessionEnd
For hook implementation, load the `outfitter:claude-hooks` skill. See [structure.md](references/structure.md) for hook JSON format and script interface.
### Skills
Add reusable methodology patterns in `skills/` directory. For skill authoring, load the `outfitter:skills-dev` skill.
### MCP Servers
```json
{
"mcpServers": {
"my-server": {
"command": "${CLAUDE_PLUGIN_ROOT}/servers/my-server",
"args": ["--config", "${CLAUDE_PLUGIN_ROOT}/config.json"],
"env": {"API_KEY": "${MY_API_KEY}"}
}
}
}
```
Path variables: `${CLAUDE_PLUGIN_ROOT}` (plugin directory), `${VAR_NAME}` (env var)
## Plugin Caching
When plugins are installed, Claude Code copies them to a cache directory. This has implications:
- **Path traversal breaks**: `../../shared/file.md` will not work after install
- **Keep resources inside plugin**: Shared scripts, rules, and assets must be within plugin directory
- **Cross-plugin dependencies**: Use skill invocation (`plugin:skill-name`) instead of file references
See [caching.md](references/caching.md) for workarounds and best practices.
## Stage 4: Validation
Before distribution, validate the plugin.
### Checklist
**Structure:**
- [ ] Standalone: plugin.json exists and is valid JSON
- [ ] Marketplace (consolidated): metadata in marketplace.json with `strict: false`
- [ ] Required fields present (name, version, description)
- [ ] Plugin name matches directory name (kebab-case)
**Components:**
- [ ] Commands have YAML frontmatter with description
- [ ] Agents have YAML frontmatter with name and description
- [ ] Hook scripts are executable (`chmod +x`)
- [ ] Hook matchers are valid regex
**Documentation:**
- [ ] README.md with installation instructions
- [ ] LICENSE file included
### Local Testing
```bash
# Add as local marketplace
/plugin marketplace add ./my-plugin
# Install and test
/plugin install my-plugin@my-plugin
# Test commands
/my-command arg1 arg2
```
See [structure.md](references/structure.md) for validation commands and detailed component schemas.
## Stage 5: Distribution
### Semantic Versioning
Follow semver (MAJOR.MINOR.PATCH):
- **MAJOR**: Breaking changes
- **MINOR**: New features (backward compatible)
- **PATCH**: Bug fixes
### Release Workflow
```bash
# 1. Update version in plugin.json
# 2. Update CHANGELOG.md
# 3. Commit and tag
git add plugin.json CHANGELOG.md
git commit -m "chore: release v1.0.0"
git tag v1.0.0
git push origin main --tags
# 4. Create GitHub release
gh release create v1.0.0 --title "v1.0.0" --notes "Initial release"
```
### Distribution Methods
| Method | Best For | Setup |
|--------|----------|-------|
| GitHub repo | Public/team plugins | Push to GitHub |
| Git URL | GitLab, Bitbucket | Full URL in source |
| Local path | Development/testing | Relative path |
See [distribution.md](references/distribution.md) for packaging, CI/CD, and release automation.
## Stage 6: Marketplace
A marketplace catalogs plugins for discovery and installation.
### Creating a Marketplace
Create `.claude-plugin/marketplace.json`:
```json
{
"name": "my-marketplace",
"owner": {"name": "Team Name", "email": "team@example.com"},
"plugins": [
{"name": "my-plugin", "source": "./plugins/my-plugin"}
]
}
```
### Plugin Sources
```json
// Relative path
{"source": "./plugins/my-plugin"}
// GitHub
{"source": {"source": "github", "repo": "owner/plugin-repo", "ref": "v1.0.0"}}
// Git URL
{"source": {"source": "url", "url": "https://gitlab.com/team/plugin.git"}}
```
### Commands
```bash
/plugin marketplace add owner/repo # Add marketplace
/plugin marketplace list # List available
/plugin install plugin-name@marketplace # Install from marketplace
/plugin marketplace update marketplace # Update
```
See [marketplace.md](references/marketplace.md) for full schema, team configuration, and hosting strategies.
## Best Practices
### Naming Conventions
- **Plugin name**: kebab-case (e.g., `dev-tools`)
- **Commands**: kebab-case (e.g., `review-pr`)
- **Agents**: kebab-case (e.g., `security-reviewer`)
### Security
- Never hardcode secrets in plugin files
- Use environment variables for sensitive data
- Validate all user inputs in hooks
- Document security requirements
### Documentation
- **README.md**: Overview, installation, usage examples
- **CHANGELOG.md**: Version history with semver
- **LICENSE**: Appropriate license file
## Troubleshooting
**Plugin not loading:**
- Standalone: verify plugin.json syntax: `jq empty .claude-plugin/plugin.json`
- Marketplace: verify marketplace.json syntax and `strict: false` if no plugin.json
- Check plugin name matches directory
- Ensure required fields present (name, version, description)
**Commands not appearing:**
- Verify YAML frontmatter exists
- Check files in `commands/` directory
**Hooks not executing:**
- Check scripts executable: `chmod +x`
- Verify matcher regex correct
- Test hook script independently
**MCP servers failing:**
- Verify server binary exists
- Check environment variables set
- Review logs: `~/Library/Logs/Claude/`
<references>
- [structure.md](references/structure.md) - Directory layout, plugin.json schema, component formats
- [distribution.md](references/distribution.md) - Packaging, versioning, CI/CD, release automation
- [marketplace.md](references/marketplace.md) - Marketplace schema, hosting, team configuration
- [caching.md](references/caching.md) - Plugin caching behavior and cross-plugin dependencies
</references>
<rules>
ALWAYS:
- Standalone plugins: create `.claude-plugin/plugin.json`
- Marketplace local plugins: use `strict: false` and consolidate metadata in marketplace.json
- External plugins: let the external repo own its manifest
- Keep plugin resources within plugin directory (caching limitation)
- Use kebab-case for all names
- Include README.md and LICENSE for distribution
- Follow semantic versioning
NEVER:
- Hardcode secrets in plugin files
- Use path traversal (`../`) for cross-plugin resources
- Skip validation before distribution
- Omit description (in plugin.json or marketplace entry)
</rules>
## Related Skills
- **claude-commands** - Slash command development
- **claude-agents** - Custom agent design
- **claude-hooks** - Event hook implementation
- **skills-dev** - Skill creation patterns
@@ -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
@@ -0,0 +1,217 @@
#!/usr/bin/env bash
# create-marketplace.sh - Create a new Claude Code plugin marketplace
set -euo pipefail
# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
print_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
print_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
usage() {
cat << EOF
Usage: $0 [options]
Create a new Claude Code plugin marketplace.
Options:
-n, --name NAME Marketplace name (kebab-case)
-o, --owner NAME Owner name
-e, --email EMAIL Owner email
-d, --description DESC Marketplace description
-r, --plugin-root PATH Base path for relative plugin sources
--dir DIRECTORY Output directory (default: current)
-h, --help Show this help message
Example:
$0 --name my-marketplace --owner "John Doe" --email "john@example.com"
EOF
exit 1
}
# Default values
MARKETPLACE_NAME=""
OWNER_NAME=""
OWNER_EMAIL=""
DESCRIPTION=""
PLUGIN_ROOT=""
OUTPUT_DIR="."
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-n|--name)
MARKETPLACE_NAME="$2"
shift 2
;;
-o|--owner)
OWNER_NAME="$2"
shift 2
;;
-e|--email)
OWNER_EMAIL="$2"
shift 2
;;
-d|--description)
DESCRIPTION="$2"
shift 2
;;
-r|--plugin-root)
PLUGIN_ROOT="$2"
shift 2
;;
--dir)
OUTPUT_DIR="$2"
shift 2
;;
-h|--help)
usage
;;
*)
print_error "Unknown option: $1"
usage
;;
esac
done
# Validate marketplace name
if [[ -z "$MARKETPLACE_NAME" ]]; then
print_error "Marketplace name is required"
usage
fi
if ! echo "$MARKETPLACE_NAME" | grep -qE '^[a-z][a-z0-9-]*$'; then
print_error "Marketplace name must be in kebab-case: $MARKETPLACE_NAME"
exit 1
fi
# Get owner info if not provided
if [[ -z "$OWNER_NAME" ]]; then
OWNER_NAME=$(git config user.name 2>/dev/null || echo "")
if [[ -z "$OWNER_NAME" ]]; then
read -p "Owner name: " OWNER_NAME
fi
fi
if [[ -z "$OWNER_EMAIL" ]]; then
OWNER_EMAIL=$(git config user.email 2>/dev/null || echo "")
if [[ -z "$OWNER_EMAIL" ]]; then
read -p "Owner email: " OWNER_EMAIL
fi
fi
# Create output directory
MARKETPLACE_DIR="$OUTPUT_DIR/.claude-plugin"
mkdir -p "$MARKETPLACE_DIR"
print_info "Creating marketplace: $MARKETPLACE_NAME"
# Build marketplace JSON
MARKETPLACE_JSON=$(jq -n \
--arg name "$MARKETPLACE_NAME" \
--arg owner_name "$OWNER_NAME" \
--arg owner_email "$OWNER_EMAIL" \
'{
name: $name,
owner: {
name: $owner_name,
email: $owner_email
},
plugins: []
}')
# Add optional fields
if [[ -n "$DESCRIPTION" ]]; then
MARKETPLACE_JSON=$(echo "$MARKETPLACE_JSON" | jq \
--arg desc "$DESCRIPTION" \
'.metadata.description = $desc')
fi
if [[ -n "$PLUGIN_ROOT" ]]; then
MARKETPLACE_JSON=$(echo "$MARKETPLACE_JSON" | jq \
--arg root "$PLUGIN_ROOT" \
'.metadata.pluginRoot = $root')
fi
# Write marketplace.json
echo "$MARKETPLACE_JSON" | jq '.' > "$MARKETPLACE_DIR/marketplace.json"
print_info "Created marketplace.json at $MARKETPLACE_DIR/marketplace.json"
# Create README
README_PATH="$OUTPUT_DIR/README.md"
if [[ ! -f "$README_PATH" ]]; then
cat > "$README_PATH" << EOF
# $MARKETPLACE_NAME
$DESCRIPTION
## Installation
\`\`\`bash
/plugin marketplace add path/to/$MARKETPLACE_NAME
\`\`\`
## Available Plugins
[List your plugins here]
## Usage
\`\`\`bash
# Install a plugin
/plugin install plugin-name@$MARKETPLACE_NAME
\`\`\`
## Contributing
[Add contribution guidelines]
## License
[Specify license]
EOF
print_info "Created README.md"
fi
# Create .gitignore if it doesn't exist
GITIGNORE_PATH="$OUTPUT_DIR/.gitignore"
if [[ ! -f "$GITIGNORE_PATH" ]]; then
cat > "$GITIGNORE_PATH" << EOF
*.log
.DS_Store
node_modules/
__pycache__/
.env
EOF
print_info "Created .gitignore"
fi
# Initialize git if not already a repo
if [[ ! -d "$OUTPUT_DIR/.git" ]] && command -v git &> /dev/null; then
print_info "Initializing git repository"
cd "$OUTPUT_DIR"
git init -q
git add .
git commit -q -m "feat: initial marketplace structure"
cd - > /dev/null
fi
# Success message
print_info "Marketplace created successfully!"
echo ""
echo "Next steps:"
echo " 1. Add plugins to $MARKETPLACE_DIR/marketplace.json"
echo " 2. Create plugin directories"
echo " 3. Test locally:"
echo " /plugin marketplace add $OUTPUT_DIR"
echo " 4. Push to Git hosting:"
echo " git remote add origin <url>"
echo " git push -u origin main"
@@ -0,0 +1,396 @@
#!/usr/bin/env bash
# scaffold-plugin.sh - Create a new Claude Code plugin structure
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Print colored output
print_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
print_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Usage information
usage() {
cat << EOF
Usage: $0 [options] <plugin-name>
Create a new Claude Code plugin with proper structure.
Options:
-d, --directory DIR Create plugin in specified directory (default: current)
-a, --author NAME Author name
-e, --email EMAIL Author email
-l, --license LICENSE License (default: MIT)
--with-commands Include sample command
--with-agent Include sample agent
--with-hooks Include sample hooks
--with-mcp Include MCP server template
-h, --help Show this help message
Example:
$0 my-plugin --author "John Doe" --with-commands
$0 my-plugin -d ./plugins --with-agent --with-hooks
EOF
exit 1
}
# Default values
PLUGIN_DIR="."
AUTHOR_NAME=""
AUTHOR_EMAIL=""
LICENSE="MIT"
WITH_COMMANDS=false
WITH_AGENT=false
WITH_HOOKS=false
WITH_MCP=false
# Parse arguments
PLUGIN_NAME=""
while [[ $# -gt 0 ]]; do
case $1 in
-d|--directory)
PLUGIN_DIR="$2"
shift 2
;;
-a|--author)
AUTHOR_NAME="$2"
shift 2
;;
-e|--email)
AUTHOR_EMAIL="$2"
shift 2
;;
-l|--license)
LICENSE="$2"
shift 2
;;
--with-commands)
WITH_COMMANDS=true
shift
;;
--with-agent)
WITH_AGENT=true
shift
;;
--with-hooks)
WITH_HOOKS=true
shift
;;
--with-mcp)
WITH_MCP=true
shift
;;
-h|--help)
usage
;;
-*)
print_error "Unknown option: $1"
usage
;;
*)
if [[ -z "$PLUGIN_NAME" ]]; then
PLUGIN_NAME="$1"
else
print_error "Unexpected argument: $1"
usage
fi
shift
;;
esac
done
# Validate plugin name
if [[ -z "$PLUGIN_NAME" ]]; then
print_error "Plugin name is required"
usage
fi
# Validate plugin name format (kebab-case)
if ! echo "$PLUGIN_NAME" | grep -qE '^[a-z][a-z0-9-]*$'; then
print_error "Plugin name must be in kebab-case (lowercase with hyphens): $PLUGIN_NAME"
exit 1
fi
# Get author info if not provided
if [[ -z "$AUTHOR_NAME" ]]; then
# Try to get from git config
AUTHOR_NAME=$(git config user.name 2>/dev/null || echo "")
if [[ -z "$AUTHOR_NAME" ]]; then
read -p "Author name: " AUTHOR_NAME
fi
fi
if [[ -z "$AUTHOR_EMAIL" ]]; then
AUTHOR_EMAIL=$(git config user.email 2>/dev/null || echo "")
if [[ -z "$AUTHOR_EMAIL" ]]; then
read -p "Author email: " AUTHOR_EMAIL
fi
fi
# Create plugin directory
PLUGIN_PATH="$PLUGIN_DIR/$PLUGIN_NAME"
if [[ -d "$PLUGIN_PATH" ]]; then
print_error "Directory already exists: $PLUGIN_PATH"
exit 1
fi
print_info "Creating plugin: $PLUGIN_NAME"
mkdir -p "$PLUGIN_PATH"
# Create plugin.json
print_info "Creating plugin.json"
cat > "$PLUGIN_PATH/plugin.json" << EOF
{
"name": "$PLUGIN_NAME",
"version": "0.1.0",
"description": "A Claude Code plugin",
"author": {
"name": "$AUTHOR_NAME",
"email": "$AUTHOR_EMAIL"
},
"license": "$LICENSE"
}
EOF
# Create README.md
print_info "Creating README.md"
cat > "$PLUGIN_PATH/README.md" << EOF
# $PLUGIN_NAME
A Claude Code plugin.
## Installation
\`\`\`bash
/plugin marketplace add path/to/$PLUGIN_NAME
/plugin install $PLUGIN_NAME@$PLUGIN_NAME
\`\`\`
## Features
- Feature 1
- Feature 2
## Usage
Describe how to use this plugin.
## License
$LICENSE
EOF
# Create CHANGELOG.md
print_info "Creating CHANGELOG.md"
cat > "$PLUGIN_PATH/CHANGELOG.md" << EOF
# Changelog
## [0.1.0] - $(date +%Y-%m-%d)
### Added
- Initial release
EOF
# Create LICENSE
print_info "Creating LICENSE"
if [[ "$LICENSE" == "MIT" ]]; then
cat > "$PLUGIN_PATH/LICENSE" << EOF
MIT License
Copyright (c) $(date +%Y) $AUTHOR_NAME
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
EOF
else
touch "$PLUGIN_PATH/LICENSE"
print_warn "Please add $LICENSE license text to LICENSE file"
fi
# Create .gitignore
cat > "$PLUGIN_PATH/.gitignore" << EOF
*.log
.DS_Store
.env
node_modules/
__pycache__/
*.pyc
.venv/
EOF
# Create sample command if requested
if [[ "$WITH_COMMANDS" == true ]]; then
print_info "Creating sample command"
mkdir -p "$PLUGIN_PATH/commands"
cat > "$PLUGIN_PATH/commands/hello.md" << EOF
---
description: "Say hello with a friendly greeting"
---
Generate a friendly greeting for {{0:name}}.
Make the greeting:
- Warm and welcoming
- Include time of day
- Professional yet friendly
EOF
fi
# Create sample agent if requested
if [[ "$WITH_AGENT" == true ]]; then
print_info "Creating sample agent"
mkdir -p "$PLUGIN_PATH/agents"
cat > "$PLUGIN_PATH/agents/helper.md" << EOF
---
name: helper
description: "A helpful assistant for common tasks"
---
You are a helpful assistant specialized in [your domain].
Your responsibilities:
1. Task 1
2. Task 2
3. Task 3
Guidelines:
- Be clear and concise
- Provide examples
- Explain your reasoning
EOF
# Update plugin.json to reference agent
tmp=$(mktemp)
jq '.agents = ["./agents/helper.md"]' "$PLUGIN_PATH/plugin.json" > "$tmp"
mv "$tmp" "$PLUGIN_PATH/plugin.json"
fi
# Create sample hooks if requested
if [[ "$WITH_HOOKS" == true ]]; then
print_info "Creating sample hooks"
mkdir -p "$PLUGIN_PATH/hooks"
cat > "$PLUGIN_PATH/hooks/pre-write.sh" << 'EOF'
#!/usr/bin/env bash
# Pre-write validation hook
input=$(cat)
file_path=$(echo "$input" | jq -r '.parameters.file_path')
content=$(echo "$input" | jq -r '.parameters.content')
# Add your validation logic here
# Allow the operation
echo '{"allowed": true}'
EOF
chmod +x "$PLUGIN_PATH/hooks/pre-write.sh"
# Update plugin.json to reference hooks
tmp=$(mktemp)
jq '.hooks = {"PreToolUse": [{"matcher": "Write", "hooks": [{"type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-write.sh"}]}]}' "$PLUGIN_PATH/plugin.json" > "$tmp"
mv "$tmp" "$PLUGIN_PATH/plugin.json"
fi
# Create MCP server template if requested
if [[ "$WITH_MCP" == true ]]; then
print_info "Creating MCP server template"
mkdir -p "$PLUGIN_PATH/servers/$PLUGIN_NAME-server"
cat > "$PLUGIN_PATH/servers/$PLUGIN_NAME-server/server.py" << 'EOF'
"""MCP server for PLUGIN_NAME"""
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("PLUGIN_NAME")
@mcp.tool()
async def example_tool(param: str) -> str:
"""
Example tool implementation.
Args:
param: Parameter description
Returns:
Result description
"""
return f"Processed: {param}"
if __name__ == "__main__":
mcp.run(transport='stdio')
EOF
# Replace PLUGIN_NAME in template
sed -i.bak "s/PLUGIN_NAME/$PLUGIN_NAME/g" "$PLUGIN_PATH/servers/$PLUGIN_NAME-server/server.py"
rm "$PLUGIN_PATH/servers/$PLUGIN_NAME-server/server.py.bak"
cat > "$PLUGIN_PATH/servers/$PLUGIN_NAME-server/pyproject.toml" << EOF
[project]
name = "$PLUGIN_NAME-server"
version = "0.1.0"
description = "MCP server for $PLUGIN_NAME"
requires-python = ">=3.10"
dependencies = [
"mcp>=1.2.0",
]
EOF
# Update plugin.json to reference MCP server
tmp=$(mktemp)
jq --arg name "$PLUGIN_NAME" '.mcpServers = {($name): {"command": "uv", "args": ["--directory", "${CLAUDE_PLUGIN_ROOT}/servers/\($name)-server", "run", "server.py"]}}' "$PLUGIN_PATH/plugin.json" > "$tmp"
mv "$tmp" "$PLUGIN_PATH/plugin.json"
fi
# Initialize git repository
if command -v git &> /dev/null; then
print_info "Initializing git repository"
cd "$PLUGIN_PATH"
git init -q
git add .
git commit -q -m "feat: initial plugin structure"
cd - > /dev/null
fi
# Success message
print_info "Plugin created successfully!"
echo ""
echo "Next steps:"
echo " cd $PLUGIN_PATH"
echo " # Edit plugin.json to update description"
if [[ "$WITH_COMMANDS" == true ]]; then
echo " # Customize commands in commands/"
fi
if [[ "$WITH_AGENT" == true ]]; then
echo " # Customize agent in agents/"
fi
if [[ "$WITH_HOOKS" == true ]]; then
echo " # Implement hooks in hooks/"
fi
if [[ "$WITH_MCP" == true ]]; then
echo " # Implement MCP server in servers/"
fi
echo ""
echo "Test locally:"
echo " /plugin marketplace add $PLUGIN_PATH"
echo " /plugin install $PLUGIN_NAME@$PLUGIN_NAME"