Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:28:02 +08:00
commit 4ea8c8d0b0
15 changed files with 4003 additions and 0 deletions

View File

@@ -0,0 +1,357 @@
---
name: marketplace-update
description: Updates the .claude-plugin/marketplace.json file when plugins are added, modified, or removed. Use when creating or updating plugin entries in the marketplace catalog.
---
# Marketplace Update Skill
This skill provides functionality to update the `.claude-plugin/marketplace.json` file when plugins are added, modified, or removed from the marketplace.
## Purpose
Maintain the marketplace catalog by:
- Adding new plugin entries
- Updating existing plugin metadata
- Removing obsolete plugins
- Validating marketplace structure
- Ensuring consistency across the catalog
## When to Use
Use this skill when:
- A new plugin is created and needs to be registered
- An existing plugin's components change (agents, commands, skills added/removed)
- Plugin metadata needs updating (version, description, keywords, etc.)
- A plugin is being removed from the marketplace
- Validating marketplace.json structure
## Marketplace Structure
The marketplace.json file follows this schema:
```json
{
"name": "marketplace-name",
"owner": {
"name": "Owner Name",
"email": "email@example.com",
"url": "https://github.com/username"
},
"metadata": {
"description": "Marketplace description",
"version": "1.0.0"
},
"plugins": [
{
"name": "plugin-name",
"source": "./plugins/plugin-name",
"description": "Plugin description",
"version": "1.0.0",
"author": {
"name": "Author Name",
"url": "https://github.com/username"
},
"homepage": "https://github.com/username/repo",
"repository": "https://github.com/username/repo",
"license": "MIT",
"keywords": ["keyword1", "keyword2"],
"category": "category-name",
"strict": false,
"commands": ["./commands/command-name.md"],
"agents": ["./agents/agent-name.md"],
"skills": ["./skills/skill-name"]
}
]
}
```
## Operations
### Add Plugin
Add a new plugin entry to the marketplace:
```python
# Use the provided Python script
python marketplace_update.py add \
--name "plugin-name" \
--description "Plugin description" \
--version "1.0.0" \
--category "category-name" \
--agents "agent1.md,agent2.md" \
--commands "command1.md,command2.md" \
--skills "skill1,skill2"
```
**Required Fields:**
- `name` - Plugin name (hyphen-case)
- `description` - Brief plugin description
- `version` - Semantic version (e.g., "1.0.0")
**Optional Fields:**
- `category` - Plugin category (default: "general")
- `agents` - Comma-separated list of agent files
- `commands` - Comma-separated list of command files
- `skills` - Comma-separated list of skill directories
- `keywords` - Comma-separated list of keywords
- `license` - License type (default: "MIT")
- `strict` - Strict mode flag (default: false)
### Update Plugin
Update an existing plugin entry:
```python
python marketplace_update.py update \
--name "plugin-name" \
--description "Updated description" \
--version "1.1.0" \
--add-agent "new-agent.md" \
--remove-command "old-command.md"
```
**Update Operations:**
- `--description` - Update description
- `--version` - Update version
- `--category` - Update category
- `--keywords` - Update keywords (replaces all)
- `--add-agent` - Add agent file
- `--remove-agent` - Remove agent file
- `--add-command` - Add command file
- `--remove-command` - Remove command file
- `--add-skill` - Add skill directory
- `--remove-skill` - Remove skill directory
### Remove Plugin
Remove a plugin from the marketplace:
```python
python marketplace_update.py remove --name "plugin-name"
```
### Validate Marketplace
Validate the marketplace.json structure:
```python
python marketplace_update.py validate
```
This checks:
- JSON syntax validity
- Required fields presence
- File path existence
- Component reference validity
- Duplicate plugin names
## Python Script
The skill includes a Python script at `marketplace_update.py` that provides command-line interface for all operations.
### Usage from Claude Code
When invoked as a skill:
1. **Read Plugin Structure**
- Scan plugin directory for components
- Extract metadata from frontmatter
- Build component lists
2. **Execute Python Script**
- Call marketplace_update.py with appropriate arguments
- Pass plugin details
- Handle success/error responses
3. **Validate Result**
- Verify marketplace.json is valid
- Confirm plugin entry is correct
- Report success or errors
## Examples
### Example 1: Add New Plugin
```python
# Plugin: golang-development
# Components: 3 agents, 1 command, 4 skills
python marketplace_update.py add \
--name "golang-development" \
--description "Go language development tools" \
--version "1.0.0" \
--category "languages" \
--keywords "golang,go,development" \
--agents "golang-pro.md,gin-pro.md,charm-pro.md" \
--commands "golang-scaffold.md" \
--skills "async-golang-patterns,golang-testing-patterns,golang-packaging,golang-performance-optimization"
```
### Example 2: Update Plugin Version
```python
# Update version and add new agent
python marketplace_update.py update \
--name "golang-development" \
--version "1.1.0" \
--add-agent "gorm-pro.md"
```
### Example 3: Remove Plugin
```python
python marketplace_update.py remove --name "obsolete-plugin"
```
## Integration with Commands
The `/claude-plugin:create` and `/claude-plugin:update` commands should invoke this skill automatically:
### From /claude-plugin:create Command
After creating a new plugin:
```
1. Scan plugin directory for components
2. Extract metadata from agent/command frontmatter
3. Invoke marketplace-update skill:
- Operation: add
- Plugin name: [from user input]
- Components: [scanned from directory]
- Metadata: [extracted from frontmatter]
```
### From /claude-plugin:update Command
After updating a plugin:
```
1. Determine what changed (added/removed/modified)
2. Invoke marketplace-update skill:
- Operation: update
- Plugin name: [from user input]
- Changes: [specific updates]
```
## Error Handling
### Plugin Already Exists (Add)
```
Error: Plugin 'plugin-name' already exists in marketplace.
Suggestion: Use 'update' operation instead.
```
### Plugin Not Found (Update/Remove)
```
Error: Plugin 'plugin-name' not found in marketplace.
Suggestion: Use 'add' operation to create it.
```
### Invalid JSON
```
Error: marketplace.json contains invalid JSON.
Suggestion: Fix JSON syntax before proceeding.
```
### Component File Missing
```
Warning: Component file './agents/agent-name.md' not found.
Suggestion: Create the file or remove from plugin entry.
```
### Validation Failure
```
Error: Marketplace validation failed:
- Plugin 'plugin-a' missing required field 'description'
- Plugin 'plugin-b' references non-existent agent 'missing.md'
Suggestion: Fix errors and validate again.
```
## Best Practices
1. **Always Validate After Changes**
- Run validate after add/update/remove
- Fix any warnings or errors
- Ensure all referenced files exist
2. **Scan Plugin Directory**
- Don't manually list components
- Scan directory to detect agents/commands/skills
- Extract metadata from frontmatter
3. **Semantic Versioning**
- Patch: Bug fixes, documentation updates (1.0.0 → 1.0.1)
- Minor: New components, enhancements (1.0.0 → 1.1.0)
- Major: Breaking changes, removals (1.0.0 → 2.0.0)
4. **Consistent Metadata**
- Keep descriptions concise (< 100 chars)
- Use relevant keywords
- Maintain consistent author information
- Use appropriate categories
5. **Backup Before Changes**
- Create backup of marketplace.json
- Test changes in development first
- Validate before committing
## Categories
Common plugin categories:
- `languages` - Language-specific tools (Python, Go, Rust, etc.)
- `development` - General development tools
- `security` - Security scanning and analysis
- `testing` - Test generation and automation
- `operations` - DevOps and operations tools
- `infrastructure` - Cloud and infrastructure tools
- `documentation` - Documentation generation
- `architecture` - Architecture and design tools
- `workflow` - Workflow orchestration
- `general` - General purpose tools
## File Structure
```
plugins/claude-plugin/skills/marketplace-update/
├── SKILL.md # This file
├── marketplace_update.py # Python implementation
└── references/ # Optional examples
└── examples.md
```
## Requirements
- Python 3.8+
- No external dependencies (uses standard library only)
- Access to `.claude-plugin/marketplace.json`
- Read/write permissions on marketplace file
## Success Criteria
After running this skill:
- ✓ marketplace.json is valid JSON
- ✓ Plugin entry is correct and complete
- ✓ All referenced files exist
- ✓ No duplicate plugin names
- ✓ Required fields are present
- ✓ Validation passes without errors

View File

@@ -0,0 +1,406 @@
#!/usr/bin/env python3
"""
Marketplace Update Script
Updates the .claude-plugin/marketplace.json file when plugins are added,
modified, or removed.
"""
import json
import os
import sys
from pathlib import Path
from typing import Dict, List, Optional, Any
import argparse
class MarketplaceUpdater:
"""Handles marketplace.json updates"""
def __init__(self, marketplace_path: str = ".claude-plugin/marketplace.json"):
self.marketplace_path = Path(marketplace_path)
self.marketplace_data: Dict[str, Any] = {}
def load(self) -> None:
"""Load marketplace.json file"""
if not self.marketplace_path.exists():
raise FileNotFoundError(f"Marketplace file not found: {self.marketplace_path}")
try:
with open(self.marketplace_path, 'r') as f:
self.marketplace_data = json.load(f)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in marketplace file: {e}")
def save(self) -> None:
"""Save marketplace.json file"""
with open(self.marketplace_path, 'w') as f:
json.dump(self.marketplace_data, f, indent=2)
f.write('\n') # Add trailing newline
def add_plugin(
self,
name: str,
description: str,
version: str,
category: str = "general",
agents: Optional[List[str]] = None,
commands: Optional[List[str]] = None,
skills: Optional[List[str]] = None,
keywords: Optional[List[str]] = None,
license: str = "MIT",
strict: bool = False,
author_name: Optional[str] = None,
author_url: Optional[str] = None,
) -> None:
"""Add a new plugin to the marketplace"""
self.load()
# Check if plugin already exists
if "plugins" not in self.marketplace_data:
self.marketplace_data["plugins"] = []
existing_plugin = self._find_plugin(name)
if existing_plugin:
raise ValueError(f"Plugin '{name}' already exists in marketplace")
# Build plugin entry
plugin_entry: Dict[str, Any] = {
"name": name,
"source": f"./plugins/{name}",
"description": description,
"version": version,
"category": category,
"license": license,
"strict": strict,
}
# Add author if provided, otherwise use marketplace owner
if author_name or author_url:
plugin_entry["author"] = {}
if author_name:
plugin_entry["author"]["name"] = author_name
if author_url:
plugin_entry["author"]["url"] = author_url
elif "owner" in self.marketplace_data:
plugin_entry["author"] = {
"name": self.marketplace_data["owner"].get("name", ""),
"url": self.marketplace_data["owner"].get("url", ""),
}
# Add homepage and repository from owner if available
if "owner" in self.marketplace_data and "url" in self.marketplace_data["owner"]:
base_url = self.marketplace_data["owner"]["url"]
# Extract repo name from URL if it's a GitHub URL
if "github.com" in base_url:
plugin_entry["homepage"] = base_url
plugin_entry["repository"] = base_url
# Add optional fields
if keywords:
plugin_entry["keywords"] = keywords
if agents:
plugin_entry["agents"] = [f"./agents/{agent}" for agent in agents]
if commands:
plugin_entry["commands"] = [f"./commands/{cmd}" for cmd in commands]
if skills:
plugin_entry["skills"] = [f"./skills/{skill}" for skill in skills]
# Add plugin to marketplace
self.marketplace_data["plugins"].append(plugin_entry)
self.save()
print(f"✓ Added plugin '{name}' to marketplace")
def update_plugin(
self,
name: str,
description: Optional[str] = None,
version: Optional[str] = None,
category: Optional[str] = None,
keywords: Optional[List[str]] = None,
add_agent: Optional[str] = None,
remove_agent: Optional[str] = None,
add_command: Optional[str] = None,
remove_command: Optional[str] = None,
add_skill: Optional[str] = None,
remove_skill: Optional[str] = None,
) -> None:
"""Update an existing plugin"""
self.load()
plugin = self._find_plugin(name)
if not plugin:
raise ValueError(f"Plugin '{name}' not found in marketplace")
# Update basic fields
if description:
plugin["description"] = description
if version:
plugin["version"] = version
if category:
plugin["category"] = category
if keywords:
plugin["keywords"] = keywords
# Update agents
if add_agent:
if "agents" not in plugin:
plugin["agents"] = []
agent_path = f"./agents/{add_agent}"
if agent_path not in plugin["agents"]:
plugin["agents"].append(agent_path)
if remove_agent:
if "agents" in plugin:
agent_path = f"./agents/{remove_agent}"
if agent_path in plugin["agents"]:
plugin["agents"].remove(agent_path)
# Update commands
if add_command:
if "commands" not in plugin:
plugin["commands"] = []
cmd_path = f"./commands/{add_command}"
if cmd_path not in plugin["commands"]:
plugin["commands"].append(cmd_path)
if remove_command:
if "commands" in plugin:
cmd_path = f"./commands/{remove_command}"
if cmd_path in plugin["commands"]:
plugin["commands"].remove(cmd_path)
# Update skills
if add_skill:
if "skills" not in plugin:
plugin["skills"] = []
skill_path = f"./skills/{add_skill}"
if skill_path not in plugin["skills"]:
plugin["skills"].append(skill_path)
if remove_skill:
if "skills" in plugin:
skill_path = f"./skills/{remove_skill}"
if skill_path in plugin["skills"]:
plugin["skills"].remove(skill_path)
self.save()
print(f"✓ Updated plugin '{name}' in marketplace")
def remove_plugin(self, name: str) -> None:
"""Remove a plugin from the marketplace"""
self.load()
plugin = self._find_plugin(name)
if not plugin:
raise ValueError(f"Plugin '{name}' not found in marketplace")
self.marketplace_data["plugins"].remove(plugin)
self.save()
print(f"✓ Removed plugin '{name}' from marketplace")
def validate(self) -> bool:
"""Validate marketplace structure"""
self.load()
errors = []
warnings = []
# Check required top-level fields
required_fields = ["name", "owner", "metadata", "plugins"]
for field in required_fields:
if field not in self.marketplace_data:
errors.append(f"Missing required field: {field}")
# Validate plugins
if "plugins" in self.marketplace_data:
plugin_names = set()
for i, plugin in enumerate(self.marketplace_data["plugins"]):
# Check required plugin fields
plugin_required = ["name", "source", "description", "version"]
for field in plugin_required:
if field not in plugin:
errors.append(f"Plugin {i}: Missing required field '{field}'")
# Check for duplicate names
if "name" in plugin:
if plugin["name"] in plugin_names:
errors.append(f"Duplicate plugin name: {plugin['name']}")
plugin_names.add(plugin["name"])
# Validate component file paths
plugin_dir = Path(f"plugins/{plugin['name']}")
if "agents" in plugin:
for agent in plugin["agents"]:
agent_path = plugin_dir / agent
if not agent_path.exists():
warnings.append(
f"Plugin '{plugin['name']}': Agent file not found: {agent_path}"
)
if "commands" in plugin:
for command in plugin["commands"]:
cmd_path = plugin_dir / command
if not cmd_path.exists():
warnings.append(
f"Plugin '{plugin['name']}': Command file not found: {cmd_path}"
)
if "skills" in plugin:
for skill in plugin["skills"]:
skill_path = plugin_dir / skill / "SKILL.md"
if not skill_path.exists():
warnings.append(
f"Plugin '{plugin['name']}': Skill file not found: {skill_path}"
)
# Report results
if errors:
print("❌ Validation failed with errors:")
for error in errors:
print(f" - {error}")
else:
print("✓ Validation passed with no errors")
if warnings:
print("\n⚠️ Warnings:")
for warning in warnings:
print(f" - {warning}")
return len(errors) == 0
def _find_plugin(self, name: str) -> Optional[Dict[str, Any]]:
"""Find a plugin by name"""
if "plugins" not in self.marketplace_data:
return None
for plugin in self.marketplace_data["plugins"]:
if plugin.get("name") == name:
return plugin
return None
def main():
"""Main entry point"""
parser = argparse.ArgumentParser(description="Update marketplace.json")
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
# Add command
add_parser = subparsers.add_parser("add", help="Add a new plugin")
add_parser.add_argument("--name", required=True, help="Plugin name")
add_parser.add_argument("--description", required=True, help="Plugin description")
add_parser.add_argument("--version", required=True, help="Plugin version")
add_parser.add_argument("--category", default="general", help="Plugin category")
add_parser.add_argument("--agents", help="Comma-separated list of agent files")
add_parser.add_argument("--commands", help="Comma-separated list of command files")
add_parser.add_argument("--skills", help="Comma-separated list of skill directories")
add_parser.add_argument("--keywords", help="Comma-separated list of keywords")
add_parser.add_argument("--license", default="MIT", help="License type")
add_parser.add_argument("--strict", action="store_true", help="Enable strict mode")
add_parser.add_argument("--author-name", help="Author name")
add_parser.add_argument("--author-url", help="Author URL")
add_parser.add_argument(
"--marketplace",
default=".claude-plugin/marketplace.json",
help="Path to marketplace.json",
)
# Update command
update_parser = subparsers.add_parser("update", help="Update an existing plugin")
update_parser.add_argument("--name", required=True, help="Plugin name")
update_parser.add_argument("--description", help="Updated description")
update_parser.add_argument("--version", help="Updated version")
update_parser.add_argument("--category", help="Updated category")
update_parser.add_argument("--keywords", help="Updated keywords (comma-separated)")
update_parser.add_argument("--add-agent", help="Agent file to add")
update_parser.add_argument("--remove-agent", help="Agent file to remove")
update_parser.add_argument("--add-command", help="Command file to add")
update_parser.add_argument("--remove-command", help="Command file to remove")
update_parser.add_argument("--add-skill", help="Skill directory to add")
update_parser.add_argument("--remove-skill", help="Skill directory to remove")
update_parser.add_argument(
"--marketplace",
default=".claude-plugin/marketplace.json",
help="Path to marketplace.json",
)
# Remove command
remove_parser = subparsers.add_parser("remove", help="Remove a plugin")
remove_parser.add_argument("--name", required=True, help="Plugin name")
remove_parser.add_argument(
"--marketplace",
default=".claude-plugin/marketplace.json",
help="Path to marketplace.json",
)
# Validate command
validate_parser = subparsers.add_parser("validate", help="Validate marketplace.json")
validate_parser.add_argument(
"--marketplace",
default=".claude-plugin/marketplace.json",
help="Path to marketplace.json",
)
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
try:
updater = MarketplaceUpdater(
getattr(args, "marketplace", ".claude-plugin/marketplace.json")
)
if args.command == "add":
updater.add_plugin(
name=args.name,
description=args.description,
version=args.version,
category=args.category,
agents=args.agents.split(",") if args.agents else None,
commands=args.commands.split(",") if args.commands else None,
skills=args.skills.split(",") if args.skills else None,
keywords=args.keywords.split(",") if args.keywords else None,
license=args.license,
strict=args.strict,
author_name=args.author_name,
author_url=args.author_url,
)
elif args.command == "update":
updater.update_plugin(
name=args.name,
description=args.description,
version=args.version,
category=args.category,
keywords=args.keywords.split(",") if args.keywords else None,
add_agent=args.add_agent,
remove_agent=args.remove_agent,
add_command=args.add_command,
remove_command=args.remove_command,
add_skill=args.add_skill,
remove_skill=args.remove_skill,
)
elif args.command == "remove":
updater.remove_plugin(name=args.name)
elif args.command == "validate":
if not updater.validate():
sys.exit(1)
except Exception as e:
print(f"❌ Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()