📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -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
|
||||
+217
@@ -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"
|
||||
+396
@@ -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"
|
||||
Reference in New Issue
Block a user