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,467 @@
---
name: documentation-update
description: Regenerates documentation files (agents.md, agent-skills.md, plugins.md, usage.md) from marketplace data using Jinja templates. Use when plugins are added, updated, or removed to keep documentation in sync.
---
# Documentation Update Skill
This skill automatically regenerates documentation files in the `docs/` directory by reading the marketplace catalog and applying Jinja2 templates.
## Purpose
Maintain synchronized documentation by:
- Generating agent reference documentation
- Creating skill catalog documentation
- Building plugin directory
- Updating usage guides
- Ensuring consistency across all docs
## When to Use
Use this skill when:
- A new plugin is added to the marketplace
- An existing plugin is updated (components added/removed)
- Agent or skill metadata changes
- Documentation needs to be regenerated
- Ensuring docs match marketplace state
## Documentation Files
This skill generates four main documentation files:
### 1. agents.md
Complete reference of all agents across all plugins:
- Organized by plugin
- Lists agent name, description, and model
- Includes links to agent files
- Shows agent capabilities and use cases
### 2. agent-skills.md
Catalog of all skills with progressive disclosure details:
- Organized by plugin
- Lists skill name and description
- Shows "Use when" triggers
- Includes skill structure information
### 3. plugins.md
Directory of all plugins in the marketplace:
- Organized by category
- Shows plugin name, description, and version
- Lists components (agents, commands, skills)
- Provides installation and usage information
### 4. usage.md
Usage guide and command reference:
- Getting started instructions
- Command usage examples
- Workflow patterns
- Integration guides
## Template Structure
Templates are stored in `assets/` using Jinja2 syntax:
```
assets/
├── agents.md.j2
├── agent-skills.md.j2
├── plugins.md.j2
└── usage.md.j2
```
### Template Variables
All templates receive the following context:
```python
{
"marketplace": {
"name": "marketplace-name",
"owner": {...},
"metadata": {...},
"plugins": [...]
},
"plugins_by_category": {
"category-name": [plugin1, plugin2, ...]
},
"all_agents": [
{
"plugin": "plugin-name",
"name": "agent-name",
"file": "agent-file.md",
"description": "...",
"model": "..."
}
],
"all_skills": [
{
"plugin": "plugin-name",
"name": "skill-name",
"path": "skill-path",
"description": "..."
}
],
"all_commands": [
{
"plugin": "plugin-name",
"name": "command-name",
"file": "command-file.md",
"description": "..."
}
],
"stats": {
"total_plugins": 10,
"total_agents": 25,
"total_commands": 15,
"total_skills": 30
}
}
```
## Python Script
The skill includes a Python script `doc_generator.py` that:
1. **Loads marketplace.json**
- Reads the marketplace catalog
- Validates structure
- Builds component index
2. **Scans Plugin Files**
- Reads agent/command frontmatter
- Extracts skill metadata
- Builds comprehensive component list
3. **Prepares Template Context**
- Organizes plugins by category
- Creates component indexes
- Calculates statistics
4. **Renders Templates**
- Applies Jinja2 templates
- Generates documentation files
- Writes to docs/ directory
### Usage
```bash
# Generate all documentation files
python doc_generator.py
# Generate specific file only
python doc_generator.py --file agents
# Dry run (show output without writing)
python doc_generator.py --dry-run
# Specify custom paths
python doc_generator.py \
--marketplace .claude-plugin/marketplace.json \
--templates plugins/claude-plugin/skills/documentation-update/assets \
--output docs
```
## Integration with Commands
The `/claude-plugin:create` and `/claude-plugin:update` commands should invoke this skill automatically after marketplace updates:
### Workflow
```
1. Plugin operation completes (add/update/remove)
2. Marketplace.json is updated
3. Invoke documentation-update skill
4. Documentation files regenerated
5. Changes ready to commit
```
### Example Integration
```python
# After creating/updating plugin
print("Updating documentation...")
# Run doc generator
import subprocess
result = subprocess.run(
["python", "plugins/claude-plugin/skills/documentation-update/doc_generator.py"],
capture_output=True,
text=True
)
if result.returncode == 0:
print("✓ Documentation updated")
else:
print(f"❌ Documentation update failed: {result.stderr}")
```
## Template Examples
### agents.md.j2
```jinja2
# Agent Reference
This document lists all agents available across plugins in the marketplace.
{% for category, plugins in plugins_by_category.items() %}
## {{ category|title }}
{% for plugin in plugins %}
### {{ plugin.name }}
{{ plugin.description }}
**Agents:**
{% for agent in all_agents %}
{% if agent.plugin == plugin.name %}
- **{{ agent.name }}** (`{{ agent.model }}`)
- {{ agent.description }}
- File: `plugins/{{ plugin.name }}/agents/{{ agent.file }}`
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
---
*Last updated: {{ now }}*
*Total agents: {{ stats.total_agents }}*
```
### agent-skills.md.j2
```jinja2
# Agent Skills Reference
This document catalogs all skills with progressive disclosure patterns.
{% for plugin in marketplace.plugins %}
## {{ plugin.name }}
{{ plugin.description }}
**Skills:**
{% for skill in all_skills %}
{% if skill.plugin == plugin.name %}
### {{ skill.name }}
{{ skill.description }}
- **Location:** `plugins/{{ plugin.name }}/skills/{{ skill.path }}/`
- **Structure:** SKILL.md + assets/ + references/
{% endif %}
{% endfor %}
{% endfor %}
---
*Last updated: {{ now }}*
*Total skills: {{ stats.total_skills }}*
```
## Error Handling
### Marketplace Not Found
```
Error: Marketplace file not found: .claude-plugin/marketplace.json
Suggestion: Ensure marketplace.json exists
```
### Template Not Found
```
Error: Template file not found: assets/agents.md.j2
Suggestion: Ensure all template files exist in assets/
```
### Invalid Plugin Structure
```
Warning: Plugin 'plugin-name' missing components
Suggestion: Verify plugin has agents or commands
```
### Frontmatter Parse Error
```
Warning: Could not parse frontmatter in agents/agent-name.md
Suggestion: Check YAML frontmatter syntax
```
## Best Practices
1. **Always Regenerate After Changes**
- Run after every plugin add/update/remove
- Ensure docs stay synchronized
- Commit documentation with plugin changes
2. **Validate Before Generation**
- Run marketplace validation first
- Fix any errors or warnings
- Ensure all files exist
3. **Review Generated Output**
- Check generated files for correctness
- Verify formatting and links
- Test any code examples
4. **Template Maintenance**
- Keep templates simple and readable
- Use consistent formatting
- Document template variables
5. **Version Control**
- Commit documentation changes
- Include in pull requests
- Document significant changes
## Template Customization
### Adding New Sections
To add a new section to a template:
1. **Modify Template**
```jinja2
## New Section
{% for plugin in marketplace.plugins %}
### {{ plugin.name }}
[Your content here]
{% endfor %}
```
2. **Update Context (if needed)**
- Add new data to template context in doc_generator.py
- Process additional metadata
3. **Test Output**
- Run generator with dry-run
- Verify formatting
- Check for errors
### Creating New Templates
To add a new documentation file:
1. **Create Template**
- Add `assets/newdoc.md.j2`
- Define structure and content
2. **Update Script**
- Add to doc_generator.py template list
- Define output path
3. **Test Generation**
- Run generator
- Verify output
- Commit template and output
## File Structure
```
plugins/claude-plugin/skills/documentation-update/
├── SKILL.md # This file
├── doc_generator.py # Python implementation
├── assets/ # Jinja2 templates
│ ├── agents.md.j2
│ ├── agent-skills.md.j2
│ ├── plugins.md.j2
│ └── usage.md.j2
└── references/ # Optional examples
└── template-examples.md
```
## Requirements
- Python 3.8+
- No external dependencies (uses standard library only)
- Access to `.claude-plugin/marketplace.json`
- Read access to plugin directories
- Write access to `docs/` directory
## Success Criteria
After running this skill:
- ✓ All documentation files generated
- ✓ Content matches marketplace state
- ✓ All links are valid
- ✓ Formatting is consistent
- ✓ Statistics are accurate
- ✓ No template rendering errors
## Maintenance
### Updating Templates
When marketplace structure changes:
1. **Assess Impact**
- Identify affected templates
- Determine required changes
2. **Update Templates**
- Modify Jinja2 templates
- Test with current data
3. **Update Script**
- Adjust context preparation if needed
- Add new data processing
4. **Validate Output**
- Regenerate all docs
- Review changes
- Test links and formatting
### Version Compatibility
- Templates should handle missing fields gracefully
- Use Jinja2 default filters for optional data
- Validate marketplace version compatibility
## Example Output
The skill generates comprehensive, well-formatted documentation:
- **agents.md**: ~500-1000 lines for 20-30 agents
- **agent-skills.md**: ~300-600 lines for 30-50 skills
- **plugins.md**: ~400-800 lines for 10-20 plugins
- **usage.md**: ~200-400 lines of usage information
All files include:
- Clear structure and headings
- Formatted tables where appropriate
- Links to source files
- Statistics and metadata
- Last updated timestamp

View File

@@ -0,0 +1,73 @@
# Agent Skills Reference
This document catalogs all agent skills with progressive disclosure patterns across the marketplace.
## Overview
- **Total Skills**: {{ stats.total_skills }}
- **Total Plugins**: {{ stats.total_plugins }}
- **Last Updated**: {{ now }}
---
## What are Agent Skills?
Agent skills are modular knowledge packages that use progressive disclosure architecture:
1. **Metadata** (Frontmatter) - Always loaded
2. **Instructions** - Core guidance loaded when activated
3. **Resources** (assets/) - Loaded on demand
All skills follow the [Anthropic Agent Skills Specification](https://github.com/anthropics/skills/blob/main/agent_skills_spec.md).
---
{% for plugin in marketplace.plugins %}
## {{ plugin.name }}
**Description**: {{ plugin.description }}
**Version**: {{ plugin.version }}
{% if plugin.skills %}
**Skills**:
{% for skill in all_skills %}
{% if skill.plugin == plugin.name %}
### {{ skill.name }}
{{ skill.description }}
**Location**: `plugins/{{ plugin.name }}/skills/{{ skill.path }}/`
**Structure**:
- `SKILL.md` - Skill definition with frontmatter
- `assets/` - Templates, configurations, examples
- `references/` - Additional documentation
{% endif %}
{% endfor %}
{% else %}
*No skills defined*
{% endif %}
---
{% endfor %}
## Progressive Disclosure Benefits
- **Token Efficiency**: Load only relevant knowledge when needed
- **Specialized Expertise**: Deep domain knowledge without bloat
- **Clear Activation**: Explicit triggers prevent unwanted invocation
- **Composability**: Mix and match skills across workflows
- **Maintainability**: Isolated updates don't affect other skills
## Using Skills
Skills are automatically invoked by agents when their trigger conditions are met. You can also manually invoke skills when needed for specific operations.
---
*This documentation is automatically generated from the marketplace catalog.*
*Last updated: {{ now }}*

View File

@@ -0,0 +1,64 @@
# Agent Reference
This document provides a comprehensive reference of all agents available across plugins in the marketplace.
## Overview
- **Total Agents**: {{ stats.total_agents }}
- **Total Plugins**: {{ stats.total_plugins }}
- **Last Updated**: {{ now }}
---
{% for category, plugins in plugins_by_category.items() %}
## {{ category|title }} Agents
{% for plugin in plugins %}
### {{ plugin.name }}
**Description**: {{ plugin.description }}
**Version**: {{ plugin.version }}
{% if plugin.agents %}
**Agents**:
{% for agent in all_agents %}
{% if agent.plugin == plugin.name %}
#### {{ agent.name }}
- **Model**: `{{ agent.model }}`
- **Description**: {{ agent.description }}
- **Location**: `plugins/{{ plugin.name }}/agents/{{ agent.file }}`
{% endif %}
{% endfor %}
{% else %}
*No agents defined*
{% endif %}
---
{% endfor %}
{% endfor %}
## Usage
To use an agent from the command line:
```bash
# Invoke with Task tool
Use Task tool with subagent_type="<agent-name>"
```
## Model Distribution
Agents are optimized for specific models based on their complexity:
- **Haiku**: Fast execution for deterministic tasks
- **Sonnet**: Complex reasoning and architecture decisions
---
*This documentation is automatically generated from the marketplace catalog.*
*Last updated: {{ now }}*

View File

@@ -0,0 +1,88 @@
# Plugin Directory
Complete catalog of all plugins available in the marketplace.
## Overview
- **Total Plugins**: {{ stats.total_plugins }}
- **Total Agents**: {{ stats.total_agents }}
- **Total Commands**: {{ stats.total_commands }}
- **Total Skills**: {{ stats.total_skills }}
- **Last Updated**: {{ now }}
---
{% for category, plugins in plugins_by_category.items() %}
## {{ category|title }}
{% for plugin in plugins %}
### {{ plugin.name }}
{{ plugin.description }}
**Version**: {{ plugin.version }}
**Author**: {% if plugin.author %}{{ plugin.author.name }}{% else %}{{ marketplace.owner.name }}{% endif %}
**License**: {{ plugin.license }}
{% if plugin.keywords %}
**Keywords**: {{ plugin.keywords|join(', ') }}
{% endif %}
**Components**:
{% if plugin.agents %}
- **Agents**: {{ plugin.agents|length }}
{% endif %}
{% if plugin.commands %}
- **Commands**: {{ plugin.commands|length }}
{% endif %}
{% if plugin.skills %}
- **Skills**: {{ plugin.skills|length }}
{% endif %}
**Location**: `{{ plugin.source }}`
{% if plugin.homepage %}
**Homepage**: {{ plugin.homepage }}
{% endif %}
{% if plugin.repository %}
**Repository**: {{ plugin.repository }}
{% endif %}
---
{% endfor %}
{% endfor %}
## Plugin Architecture
Each plugin follows these principles:
- **Single Responsibility**: One plugin does one thing well
- **Composability**: Mix and match plugins based on needs
- **Context Efficiency**: Smaller tools for better LLM performance
- **Maintainability**: Isolated updates, clear boundaries
## Installation
All plugins are included in this marketplace. To use a plugin:
1. Ensure the plugin directory exists in `plugins/`
2. Use agents via the Task tool
3. Use commands via slash commands
4. Skills are automatically loaded when needed
## Categories
Plugins are organized into the following categories:
{% for category in plugins_by_category.keys() %}
- **{{ category|title }}**: {{ plugins_by_category[category]|length }} plugin(s)
{% endfor %}
---
*This documentation is automatically generated from the marketplace catalog.*
*Last updated: {{ now }}*

View File

@@ -0,0 +1,250 @@
# Usage Guide
Comprehensive guide for using Claude Code plugins, agents, commands, and skills from this marketplace.
## Overview
This marketplace provides {{ stats.total_plugins }} plugin(s) with:
- {{ stats.total_agents }} specialized agent(s)
- {{ stats.total_commands }} command(s)
- {{ stats.total_skills }} skill(s)
**Last Updated**: {{ now }}
---
## Quick Start
### Using Agents
Agents are specialized domain experts invoked via the Task tool:
```
Use Task tool with subagent_type="<agent-name>"
```
**Example**:
```
Use Task tool with subagent_type="plugin-architect" to design a new plugin
```
### Using Commands
Commands are slash commands for specific workflows:
```bash
/<command-name> [arguments]
```
**Available Commands**:
{% for command in all_commands %}
- `{{ command.name }}` - {{ command.description }}
- Plugin: {{ command.plugin }}
- File: `plugins/{{ command.plugin }}/commands/{{ command.file }}`
{% endfor %}
### Using Skills
Skills are automatically invoked by agents when their trigger conditions are met. Skills provide:
- Modular knowledge packages
- Progressive disclosure (metadata → instructions → resources)
- Spec-compliant with Anthropic guidelines
**Available Skills**:
{% for skill in all_skills %}
- `{{ skill.name }}` - {{ skill.description }}
- Plugin: {{ skill.plugin }}
- Path: `plugins/{{ skill.plugin }}/skills/{{ skill.path }}/`
{% endfor %}
---
## Common Workflows
### Creating a New Plugin
Use the `claude-plugin` plugin to create new plugins:
```bash
# Create a new plugin
/create <plugin-name> "<description>" [components]
# Example
/create golang-advanced "Advanced Go development tools" agents,commands,skills
```
### Updating an Existing Plugin
Modify plugins by adding, updating, or removing components:
```bash
# Add a new agent
/update <plugin-name> add agent <agent-name>
# Modify a command
/update <plugin-name> modify command <command-name>
# Remove a skill
/update <plugin-name> remove skill <skill-name>
```
### Working with Agents
Invoke agents for specialized tasks:
```
# For architecture and design
Use Task tool with subagent_type="plugin-architect"
# Add your task description
[Describe what you need the agent to do]
```
---
## Plugin Architecture
### Directory Structure
```
plugins/
├── <plugin-name>/
│ ├── agents/ # Specialized agents (optional)
│ │ └── agent.md
│ ├── commands/ # Slash commands (optional)
│ │ └── command.md
│ └── skills/ # Agent skills (optional)
│ └── skill-name/
│ ├── SKILL.md
│ ├── assets/
│ └── references/
```
### Component Requirements
Each plugin must have:
- At least one agent OR one command
- Proper YAML frontmatter in all files
- Clear, focused purpose
- Entry in marketplace.json
---
## Agent Reference
### Available Agents
{% for agent in all_agents %}
#### {{ agent.name }}
- **Plugin**: {{ agent.plugin }}
- **Model**: {{ agent.model }}
- **Description**: {{ agent.description }}
- **Invocation**: `Use Task tool with subagent_type="{{ agent.name }}"`
{% endfor %}
### Model Selection
Agents use different models based on task complexity:
- **Haiku**: Fast execution for deterministic tasks
- Code generation from specs
- Test creation
- Documentation generation
- **Sonnet**: Complex reasoning and architecture
- System design
- Security audits
- Language expertise
- Multi-agent orchestration
---
## Best Practices
### When Creating Plugins
1. **Single Responsibility**: One plugin, one purpose
2. **Clear Naming**: Use hyphen-case, be descriptive
3. **Complete Documentation**: Include frontmatter and examples
4. **Spec Compliance**: Follow Anthropic guidelines
5. **Test Thoroughly**: Verify functionality before committing
### When Using Agents
1. **Choose the Right Agent**: Match agent expertise to task
2. **Provide Clear Context**: Detailed task descriptions
3. **Use Appropriate Models**: Haiku for speed, Sonnet for complexity
4. **Compose When Needed**: Combine multiple agents for complex workflows
### When Working with Skills
1. **Progressive Disclosure**: Load only what's needed
2. **Clear Triggers**: Use explicit activation criteria
3. **Modular Design**: Keep skills focused and reusable
4. **Document Well**: Include usage examples
---
## Marketplace Management
### Adding Plugins
Plugins are added via the marketplace update process:
1. Create plugin directory and components
2. Update `.claude-plugin/marketplace.json`
3. Regenerate documentation
### Updating Documentation
Documentation is automatically generated from the marketplace:
```bash
# Regenerate all docs
python plugins/claude-plugin/skills/documentation-update/doc_generator.py
# Generate specific file
python plugins/claude-plugin/skills/documentation-update/doc_generator.py --file agents
```
---
## Categories
Plugins are organized by category:
{% for category, plugins in plugins_by_category.items() %}
### {{ category|title }}
{% for plugin in plugins %}
- **{{ plugin.name }}** - {{ plugin.description }}
{% endfor %}
{% endfor %}
---
## Getting Help
- **Documentation**: See `docs/` directory for detailed references
- **Architecture**: See `docs/architecture.md` for design principles
- **Contributing**: See `.github/CONTRIBUTING.md` for contribution guidelines
---
## Resources
- [Architecture Documentation](./architecture.md)
- [Agent Reference](./agents.md)
- [Skills Reference](./agent-skills.md)
- [Plugin Directory](./plugins.md)
---
*This documentation is automatically generated from the marketplace catalog.*
*Last updated: {{ now }}*

View File

@@ -0,0 +1,476 @@
#!/usr/bin/env python3
"""
Documentation Generator
Generates documentation files from marketplace data using Jinja2 templates.
"""
import json
import os
import sys
import re
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Any, Optional
import argparse
# Try to use real Jinja2 if available, otherwise use SimpleTemplate fallback
try:
from jinja2 import Template as Jinja2Template
USE_JINJA2 = True
except ImportError:
USE_JINJA2 = False
class SimpleTemplate:
"""Minimal Jinja2-like template engine"""
def __init__(self, template_str: str):
self.template = template_str
def apply_filter(self, value: Any, filter_name: str) -> Any:
"""Apply a filter to a value"""
if filter_name == 'title':
return str(value).replace('-', ' ').replace('_', ' ').title()
elif filter_name == 'length':
return len(value) if hasattr(value, '__len__') else 0
elif filter_name.startswith('join'):
# Extract separator from filter (e.g., "join(', ')")
match = re.search(r"join\(['\"]([^'\"]*)['\"]\)", filter_name)
if match and isinstance(value, list):
separator = match.group(1)
return separator.join(str(v) for v in value)
return str(value)
return value
def resolve_value(self, expr: str, context: Dict[str, Any]) -> Any:
"""Resolve a variable expression with optional filters"""
# Split expression and filters
parts = expr.strip().split('|')
var_expr = parts[0].strip()
filters = [f.strip() for f in parts[1:]]
# Resolve the base variable
value = context
for key in var_expr.split('.'):
key = key.strip()
if isinstance(value, dict):
value = value.get(key, '')
elif hasattr(value, key):
value = getattr(value, key)
else:
value = ''
break
# Apply filters
for filter_name in filters:
value = self.apply_filter(value, filter_name)
return value
def render(self, context: Dict[str, Any]) -> str:
"""Render template with context"""
result = self.template
# Handle nested loops with .items(): {% for key, value in dict.items() %}...{% endfor %}
items_pattern = r'{%\s*for\s+(\w+)\s*,\s*(\w+)\s+in\s+([\w.]+)\.items\(\)\s*%}(.*?){%\s*endfor\s*%}'
def replace_items_loop(match):
key_var = match.group(1)
value_var = match.group(2)
dict_name = match.group(3)
loop_body = match.group(4)
dict_obj = self.resolve_value(dict_name, context)
if not isinstance(dict_obj, dict):
return ""
output = []
for key, value in dict_obj.items():
loop_context = context.copy()
loop_context[key_var] = key
loop_context[value_var] = value
# Recursively render the loop body
template = SimpleTemplate(loop_body)
body_result = template.render(loop_context)
output.append(body_result)
return "".join(output)
result = re.sub(items_pattern, replace_items_loop, result, flags=re.DOTALL)
# Handle loops with .keys(): {% for key in dict.keys() %}...{% endfor %}
keys_pattern = r'{%\s*for\s+(\w+)\s+in\s+([\w.]+)\.keys\(\)\s*%}(.*?){%\s*endfor\s*%}'
def replace_keys_loop(match):
var_name = match.group(1)
dict_name = match.group(2)
loop_body = match.group(3)
dict_obj = self.resolve_value(dict_name, context)
if not isinstance(dict_obj, dict):
return ""
output = []
for key in dict_obj.keys():
loop_context = context.copy()
loop_context[var_name] = key
# Recursively render the loop body
template = SimpleTemplate(loop_body)
body_result = template.render(loop_context)
output.append(body_result)
return "".join(output)
result = re.sub(keys_pattern, replace_keys_loop, result, flags=re.DOTALL)
# Handle regular loops: {% for item in items %}...{% endfor %}
for_pattern = r'{%\s*for\s+(\w+)\s+in\s+([\w.]+)\s*%}(.*?){%\s*endfor\s*%}'
def replace_for(match):
var_name = match.group(1)
list_name = match.group(2)
loop_body = match.group(3)
items = self.resolve_value(list_name, context)
if not isinstance(items, (list, dict)):
return ""
# Handle both lists and dict values
if isinstance(items, dict):
items = list(items.values())
output = []
for item in items:
loop_context = context.copy()
loop_context[var_name] = item
# If item is a dict, also add its keys directly to context for easier access
if isinstance(item, dict):
for key, value in item.items():
loop_context[f"{var_name}.{key}"] = value
# Recursively render the loop body
template = SimpleTemplate(loop_body)
body_result = template.render(loop_context)
output.append(body_result)
return "".join(output)
result = re.sub(for_pattern, replace_for, result, flags=re.DOTALL)
# Handle conditionals with comparison: {% if var1 == var2 %}...{% endif %}
if_compare_pattern = r'{%\s*if\s+([\w.]+)\s*==\s*([\w.]+)\s*%}(.*?){%\s*endif\s*%}'
def replace_if_compare(match):
left_expr = match.group(1)
right_expr = match.group(2)
body = match.group(3)
left_val = self.resolve_value(left_expr, context)
right_val = self.resolve_value(right_expr, context)
if left_val == right_val:
template = SimpleTemplate(body)
return template.render(context)
return ""
result = re.sub(if_compare_pattern, replace_if_compare, result, flags=re.DOTALL)
# Handle conditionals with else: {% if condition %}...{% else %}...{% endif %}
if_else_pattern = r'{%\s*if\s+([\w.]+)\s*%}(.*?){%\s*else\s*%}(.*?){%\s*endif\s*%}'
def replace_if_else(match):
condition = match.group(1)
true_body = match.group(2)
false_body = match.group(3)
cond_val = self.resolve_value(condition, context)
if cond_val:
template = SimpleTemplate(true_body)
return template.render(context)
else:
template = SimpleTemplate(false_body)
return template.render(context)
result = re.sub(if_else_pattern, replace_if_else, result, flags=re.DOTALL)
# Handle simple conditionals: {% if condition %}...{% endif %}
if_pattern = r'{%\s*if\s+([\w.]+)\s*%}(.*?){%\s*endif\s*%}'
def replace_if(match):
condition = match.group(1)
body = match.group(2)
cond_val = self.resolve_value(condition, context)
if cond_val:
template = SimpleTemplate(body)
return template.render(context)
return ""
result = re.sub(if_pattern, replace_if, result, flags=re.DOTALL)
# Replace variables with filters: {{ variable|filter }}
var_pattern = r'{{\s*([\w.|()\'",\s]+)\s*}}'
def replace_var(match):
expr = match.group(1)
value = self.resolve_value(expr, context)
return str(value) if value is not None else ''
result = re.sub(var_pattern, replace_var, result)
# Clean up any remaining template syntax
result = re.sub(r'{%.*?%}', '', result)
result = re.sub(r'{{.*?}}', '', result)
return result
class DocGenerator:
"""Generates documentation from marketplace data"""
def __init__(
self,
marketplace_path: str = ".claude-plugin/marketplace.json",
templates_dir: str = "plugins/claude-plugin/skills/documentation-update/assets",
output_dir: str = "docs",
):
self.marketplace_path = Path(marketplace_path)
self.templates_dir = Path(templates_dir)
self.output_dir = Path(output_dir)
self.marketplace_data: Dict[str, Any] = {}
def load_marketplace(self) -> None:
"""Load marketplace.json"""
if not self.marketplace_path.exists():
raise FileNotFoundError(f"Marketplace not found: {self.marketplace_path}")
with open(self.marketplace_path, 'r') as f:
self.marketplace_data = json.load(f)
def extract_frontmatter(self, file_path: Path) -> Dict[str, str]:
"""Extract YAML frontmatter from a markdown file"""
if not file_path.exists():
return {}
try:
with open(file_path, 'r') as f:
content = f.read()
# Match frontmatter between --- delimiters
match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL)
if not match:
return {}
frontmatter_text = match.group(1)
frontmatter = {}
# Simple YAML parsing (key: value)
for line in frontmatter_text.split('\n'):
if ':' in line:
key, value = line.split(':', 1)
frontmatter[key.strip()] = value.strip().strip('"\'')
return frontmatter
except Exception as e:
print(f"Warning: Could not parse frontmatter in {file_path}: {e}")
return {}
def build_context(self) -> Dict[str, Any]:
"""Build template context from marketplace data"""
context = {
"marketplace": self.marketplace_data,
"now": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"plugins_by_category": {},
"all_agents": [],
"all_skills": [],
"all_commands": [],
"stats": {
"total_plugins": 0,
"total_agents": 0,
"total_commands": 0,
"total_skills": 0,
},
}
if "plugins" not in self.marketplace_data:
return context
plugins = self.marketplace_data["plugins"]
context["stats"]["total_plugins"] = len(plugins)
# Organize plugins by category
for plugin in plugins:
category = plugin.get("category", "general")
if category not in context["plugins_by_category"]:
context["plugins_by_category"][category] = []
context["plugins_by_category"][category].append(plugin)
plugin_name = plugin.get("name", "")
plugin_dir = Path(f"plugins/{plugin_name}")
# Extract agent information
if "agents" in plugin:
for agent_path in plugin["agents"]:
agent_file = agent_path.replace("./agents/", "")
full_path = plugin_dir / agent_path.lstrip('./')
frontmatter = self.extract_frontmatter(full_path)
context["all_agents"].append({
"plugin": plugin_name,
"name": frontmatter.get("name", agent_file.replace(".md", "")),
"file": agent_file,
"description": frontmatter.get("description", ""),
"model": frontmatter.get("model", ""),
})
context["stats"]["total_agents"] += len(plugin["agents"])
# Extract command information
if "commands" in plugin:
for cmd_path in plugin["commands"]:
cmd_file = cmd_path.replace("./commands/", "")
full_path = plugin_dir / cmd_path.lstrip('./')
frontmatter = self.extract_frontmatter(full_path)
context["all_commands"].append({
"plugin": plugin_name,
"name": frontmatter.get("name", cmd_file.replace(".md", "")),
"file": cmd_file,
"description": frontmatter.get("description", ""),
})
context["stats"]["total_commands"] += len(plugin["commands"])
# Extract skill information
if "skills" in plugin:
for skill_path in plugin["skills"]:
skill_name = skill_path.replace("./skills/", "")
full_path = plugin_dir / skill_path.lstrip('./') / "SKILL.md"
frontmatter = self.extract_frontmatter(full_path)
context["all_skills"].append({
"plugin": plugin_name,
"name": frontmatter.get("name", skill_name),
"path": skill_name,
"description": frontmatter.get("description", ""),
})
context["stats"]["total_skills"] += len(plugin["skills"])
return context
def render_template(self, template_name: str, context: Dict[str, Any]) -> str:
"""Render a template with context"""
template_path = self.templates_dir / f"{template_name}.md.j2"
if not template_path.exists():
raise FileNotFoundError(f"Template not found: {template_path}")
with open(template_path, 'r') as f:
template_content = f.read()
if USE_JINJA2:
# Use real Jinja2 for full compatibility
template = Jinja2Template(template_content)
return template.render(**context)
else:
# Fallback to SimpleTemplate
template = SimpleTemplate(template_content)
return template.render(context)
def generate_all(self, dry_run: bool = False, specific_file: Optional[str] = None) -> None:
"""Generate all documentation files"""
self.load_marketplace()
context = self.build_context()
docs_to_generate = {
"agents": "agents.md",
"agent-skills": "agent-skills.md",
"plugins": "plugins.md",
"usage": "usage.md",
}
if specific_file:
if specific_file not in docs_to_generate:
raise ValueError(f"Unknown documentation file: {specific_file}")
docs_to_generate = {specific_file: docs_to_generate[specific_file]}
for template_name, output_file in docs_to_generate.items():
try:
print(f"Generating {output_file}...")
content = self.render_template(template_name, context)
if dry_run:
print(f"\n--- {output_file} ---")
print(content[:500] + "..." if len(content) > 500 else content)
print()
else:
output_path = self.output_dir / output_file
output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_path, 'w') as f:
f.write(content)
print(f"✓ Generated {output_path}")
except Exception as e:
print(f"❌ Error generating {output_file}: {e}")
def main():
"""Main entry point"""
parser = argparse.ArgumentParser(description="Generate documentation from marketplace")
parser.add_argument(
"--marketplace",
default=".claude-plugin/marketplace.json",
help="Path to marketplace.json",
)
parser.add_argument(
"--templates",
default="plugins/claude-plugin/skills/documentation-update/assets",
help="Path to templates directory",
)
parser.add_argument(
"--output",
default="docs",
help="Output directory for documentation",
)
parser.add_argument(
"--file",
choices=["agents", "agent-skills", "plugins", "usage"],
help="Generate specific file only",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show output without writing files",
)
args = parser.parse_args()
try:
generator = DocGenerator(
marketplace_path=args.marketplace,
templates_dir=args.templates,
output_dir=args.output,
)
generator.generate_all(dry_run=args.dry_run, specific_file=args.file)
if not args.dry_run:
print("\n✓ Documentation generation complete")
except Exception as e:
print(f"❌ Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

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()