Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:20:31 +08:00
commit 6bee51f8ec
20 changed files with 4266 additions and 0 deletions

View File

@@ -0,0 +1,223 @@
#!/usr/bin/env python3
"""
Script: generate-metadata.py
Purpose: Generate plugin.json metadata from parameters
Version: 1.0.0
Last Modified: 2025-10-13
Usage:
./generate-metadata.py --name <name> --author <author> --description <desc> --license <license>
Returns:
0 - Success, JSON written to stdout
1 - Validation error
2 - Missing required arguments
Dependencies:
- python3 (3.7+)
- json module (standard library)
"""
import json
import sys
import argparse
import re
from datetime import datetime
# OpenPlugins standard categories
VALID_CATEGORIES = [
"development", "testing", "deployment", "documentation",
"security", "database", "monitoring", "productivity",
"quality", "collaboration"
]
VALID_LICENSES = ["MIT", "Apache-2.0", "GPL-3.0", "BSD-3-Clause", "ISC", "LGPL-3.0"]
def parse_author(author_string):
"""Parse author string into name and email components."""
# Pattern: "Name <email>" or "Name" or "Name (email)"
email_pattern = r'<([^>]+)>'
paren_pattern = r'\(([^\)]+)\)'
name = author_string
email = None
# Check for <email> format
email_match = re.search(email_pattern, author_string)
if email_match:
email = email_match.group(1)
name = author_string[:email_match.start()].strip()
else:
# Check for (email) format
paren_match = re.search(paren_pattern, author_string)
if paren_match:
email = paren_match.group(1)
name = author_string[:paren_match.start()].strip()
result = {"name": name}
if email:
result["email"] = email
return result
def generate_keywords(name, description, category):
"""Generate relevant keywords from name, description, and category."""
keywords = []
# Add category
keywords.append(category)
# Extract keywords from name
name_parts = name.split('-')
for part in name_parts:
if len(part) > 3 and part not in ['plugin', 'claude', 'code']:
keywords.append(part)
# Extract keywords from description (simple approach)
desc_words = re.findall(r'\b[a-z]{4,}\b', description.lower())
common_words = {'with', 'from', 'this', 'that', 'have', 'will', 'your', 'for'}
for word in desc_words[:3]: # Take first 3 meaningful words
if word not in common_words and word not in keywords:
keywords.append(word)
# Ensure 3-7 keywords
return keywords[:7]
def validate_name(name):
"""Validate plugin name format."""
pattern = r'^[a-z][a-z0-9-]*[a-z0-9]$|^[a-z]$'
if not re.match(pattern, name):
return False, "Plugin name must be lowercase with hyphens only"
if '--' in name:
return False, "Plugin name cannot contain consecutive hyphens"
if len(name) < 3 or len(name) > 50:
return False, "Plugin name must be 3-50 characters"
return True, None
def validate_description(description):
"""Validate description length and content."""
length = len(description)
if length < 50:
return False, f"Description too short ({length} chars, minimum 50)"
if length > 200:
return False, f"Description too long ({length} chars, maximum 200)"
return True, None
def generate_metadata(args):
"""Generate complete plugin.json metadata."""
# Validate name
valid, error = validate_name(args.name)
if not valid:
print(f"ERROR: {error}", file=sys.stderr)
return None
# Validate description
valid, error = validate_description(args.description)
if not valid:
print(f"ERROR: {error}", file=sys.stderr)
return None
# Validate license
if args.license not in VALID_LICENSES:
print(f"ERROR: Invalid license. Must be one of: {', '.join(VALID_LICENSES)}", file=sys.stderr)
return None
# Validate category
if args.category not in VALID_CATEGORIES:
print(f"ERROR: Invalid category. Must be one of: {', '.join(VALID_CATEGORIES)}", file=sys.stderr)
return None
# Parse author
author = parse_author(args.author)
# Generate or use provided keywords
if args.keywords:
keywords = [k.strip() for k in args.keywords.split(',')]
else:
keywords = generate_keywords(args.name, args.description, args.category)
# Build metadata object
metadata = {
"name": args.name,
"version": args.version,
"description": args.description,
"author": author,
"license": args.license
}
# Add optional fields if provided
if args.repository:
metadata["repository"] = {
"type": "git",
"url": args.repository
}
elif args.github_username:
metadata["repository"] = {
"type": "git",
"url": f"https://github.com/{args.github_username}/{args.name}"
}
metadata["homepage"] = f"https://github.com/{args.github_username}/{args.name}"
if args.homepage:
metadata["homepage"] = args.homepage
if keywords:
metadata["keywords"] = keywords
if args.category:
metadata["category"] = args.category
return metadata
def main():
parser = argparse.ArgumentParser(
description='Generate plugin.json metadata',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# Required arguments
parser.add_argument('--name', required=True, help='Plugin name (lowercase-hyphen)')
parser.add_argument('--author', required=True, help='Author name or "Name <email>"')
parser.add_argument('--description', required=True, help='Plugin description (50-200 chars)')
parser.add_argument('--license', required=True, help='License type (MIT, Apache-2.0, etc.)')
# Optional arguments
parser.add_argument('--version', default='1.0.0', help='Initial version (default: 1.0.0)')
parser.add_argument('--category', default='development', help='Plugin category')
parser.add_argument('--keywords', help='Comma-separated keywords')
parser.add_argument('--repository', help='Repository URL')
parser.add_argument('--homepage', help='Homepage URL')
parser.add_argument('--github-username', help='GitHub username (auto-generates repo URL)')
parser.add_argument('--output', help='Output file path (default: stdout)')
parser.add_argument('--pretty', action='store_true', help='Pretty-print JSON')
args = parser.parse_args()
# Generate metadata
metadata = generate_metadata(args)
if metadata is None:
return 1
# Format JSON
if args.pretty:
json_output = json.dumps(metadata, indent=2, ensure_ascii=False)
else:
json_output = json.dumps(metadata, ensure_ascii=False)
# Output
if args.output:
try:
with open(args.output, 'w', encoding='utf-8') as f:
f.write(json_output)
print(f"✅ Metadata written to {args.output}", file=sys.stderr)
except IOError as e:
print(f"ERROR: Could not write to {args.output}: {e}", file=sys.stderr)
return 1
else:
print(json_output)
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,91 @@
#!/bin/bash
# Script: validate-name.sh
# Purpose: Validate plugin name follows Claude Code conventions
# Version: 1.0.0
# Last Modified: 2025-10-13
#
# Usage:
# ./validate-name.sh <plugin-name>
#
# Arguments:
# plugin-name: Name to validate
#
# Returns:
# 0 - Valid name
# 1 - Invalid name format
# 2 - Missing argument
#
# Dependencies:
# - grep (for regex matching)
# Check for argument
if [ $# -eq 0 ]; then
echo "ERROR: Plugin name required"
echo "Usage: $0 <plugin-name>"
exit 2
fi
PLUGIN_NAME="$1"
# Validation pattern: lowercase letters, numbers, hyphens
# Must start with letter, cannot end with hyphen
VALID_PATTERN='^[a-z][a-z0-9-]*[a-z0-9]$|^[a-z]$'
# Check pattern match
if echo "$PLUGIN_NAME" | grep -Eq "$VALID_PATTERN"; then
# Additional checks
# Check for consecutive hyphens
if echo "$PLUGIN_NAME" | grep -q '\-\-'; then
echo "ERROR: Plugin name cannot contain consecutive hyphens"
echo "Invalid: $PLUGIN_NAME"
echo "Try: $(echo "$PLUGIN_NAME" | sed 's/--/-/g')"
exit 1
fi
# Check length (reasonable limit)
NAME_LENGTH=${#PLUGIN_NAME}
if [ $NAME_LENGTH -gt 50 ]; then
echo "ERROR: Plugin name too long ($NAME_LENGTH characters, max 50)"
echo "Consider a shorter name"
exit 1
fi
if [ $NAME_LENGTH -lt 3 ]; then
echo "ERROR: Plugin name too short ($NAME_LENGTH characters, min 3)"
echo "Use a more descriptive name"
exit 1
fi
# Check for common mistakes
if echo "$PLUGIN_NAME" | grep -iq 'plugin$'; then
echo "WARNING: Plugin name ends with 'plugin' - this is redundant"
echo "Consider: $(echo "$PLUGIN_NAME" | sed 's/-plugin$//' | sed 's/plugin$//')"
fi
# Success
echo "✅ Valid plugin name: $PLUGIN_NAME"
exit 0
else
# Invalid format
echo "ERROR: Invalid plugin name format: $PLUGIN_NAME"
echo ""
echo "Plugin names must:"
echo " - Start with a lowercase letter"
echo " - Contain only lowercase letters, numbers, and hyphens"
echo " - Not end with a hyphen"
echo " - Be between 3-50 characters"
echo ""
echo "Valid examples:"
echo " - code-formatter"
echo " - test-generator"
echo " - deploy-automation"
echo ""
echo "Invalid examples:"
echo " - CodeFormatter (uppercase)"
echo " - test_generator (underscores)"
echo " - -my-plugin (starts with hyphen)"
echo " - plugin- (ends with hyphen)"
exit 1
fi

View File

@@ -0,0 +1,445 @@
---
description: Generate complete plugin structure with all necessary files in one operation
---
# Complete Plugin Scaffolding
## Parameters
**Required**:
- `name`: Plugin name in lowercase-hyphen format
- `description`: Brief description (50-200 characters)
- `author`: Author name or "name:email" format
- `license`: License type (MIT|Apache-2.0|GPL-3.0|BSD-3-Clause)
**Optional**:
- `category`: OpenPlugins category (default: "development")
- `keywords`: Comma-separated keywords (default: auto-generated)
- `version`: Initial version (default: "1.0.0")
- `with_agent`: Include agent template (format: true|false, default: false)
- `with_hooks`: Include hooks template (format: true|false, default: false)
## Workflow
### Step 1: Validate Parameters
**Plugin Name Validation**:
- Must match pattern: `^[a-z][a-z0-9-]*$`
- Must be lowercase with hyphens only
- Cannot start with numbers
- Should be descriptive and memorable
Execute validation script:
```bash
.scripts/validate-name.sh "{name}"
```
**Description Validation**:
- Length: 50-200 characters
- Must be specific and actionable
- Should answer "What does this do?"
**Author Format**:
- Simple: "John Doe"
- With email: "John Doe <john@example.com>"
- Object format: handled automatically
### Step 2: Create Directory Structure
**Base Structure**:
```bash
mkdir -p {name}/.claude-plugin
mkdir -p {name}/commands
mkdir -p {name}/agents # if with_agent:true
mkdir -p {name}/hooks # if with_hooks:true
```
**Subdirectories Based on Pattern**:
- Simple plugin: No subdirectories needed
- Moderate plugin: May create command namespace directories
- Complex plugin: Create skill directories with .scripts/
### Step 3: Generate plugin.json
Create `plugin.json` at the plugin root with complete metadata:
```json
{
"name": "{name}",
"version": "{version}",
"description": "{description}",
"author": {
"name": "{author_name}",
"email": "{author_email}"
},
"license": "{license}",
"repository": {
"type": "git",
"url": "https://github.com/{username}/{name}"
},
"homepage": "https://github.com/{username}/{name}",
"keywords": [{keywords}],
"category": "{category}"
}
```
Execute metadata generation script:
```bash
.scripts/generate-metadata.py --name "{name}" --author "{author}" --description "{description}" --license "{license}" --category "{category}"
```
### Step 4: Create README.md
Generate comprehensive README with sections:
1. **Title and Badge**
2. **Description** (expanded from metadata)
3. **Features** (bullet list)
4. **Installation** (multiple methods)
5. **Usage** (with examples)
6. **Configuration** (if applicable)
7. **Commands** (document each command)
8. **Agents** (if with_agent:true)
9. **Hooks** (if with_hooks:true)
10. **Examples** (multiple real scenarios)
11. **Troubleshooting**
12. **Development** (testing, contributing)
13. **Changelog** (link to CHANGELOG.md)
14. **License**
15. **Resources**
### Step 5: Add LICENSE File
Based on license parameter, add appropriate LICENSE file:
**MIT**:
```
MIT License
Copyright (c) {year} {author}
Permission is hereby granted, free of charge, to any person obtaining a copy...
```
**Apache-2.0**:
```
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
...
```
**GPL-3.0**:
```
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
...
```
**BSD-3-Clause**:
```
BSD 3-Clause License
Copyright (c) {year}, {author}
...
```
### Step 6: Create CHANGELOG.md
Initialize changelog with version 1.0.0:
```markdown
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0] - {date}
### Added
- Initial release
- {feature 1}
- {feature 2}
```
### Step 7: Create .gitignore
Add plugin development .gitignore:
```
# OS files
.DS_Store
Thumbs.db
# Editor directories
.vscode/
.idea/
*.swp
*.swo
*~
# Logs
*.log
npm-debug.log*
# Environment
.env
.env.local
# Dependencies
node_modules/
__pycache__/
*.pyc
# Build outputs
dist/
build/
*.egg-info/
# Test coverage
coverage/
.coverage
.nyc_output
# Temporary files
tmp/
temp/
*.tmp
```
### Step 8: Create Template Files
**If with_agent:true**, create `agents/specialist.md`:
```markdown
---
name: {name}-specialist
description: Expert in {domain}. Use when users need guidance with {functionality}.
capabilities: [capability1, capability2, capability3]
tools: Read, Write, Bash, Grep, Glob
model: inherit
---
# {Name} Specialist Agent
You are an expert in {domain}. Your role is to provide guidance and automation for {functionality}.
## When to Invoke
This agent is automatically invoked when users:
- {trigger condition 1}
- {trigger condition 2}
## Approach
{Agent's methodology and best practices}
```
**If with_hooks:true**, create `hooks/hooks.json`:
```json
{
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "echo 'Hook triggered after file modification'"
}
]
}
]
}
```
**Create sample command** in `commands/example.md`:
```markdown
---
description: Example command demonstrating plugin functionality
---
# Example Command
This is a sample command. Replace with your actual implementation.
## Usage
/{name} [arguments]
## Parameters
- `arg1`: Description
- `arg2`: Description (optional)
## Implementation
{Implementation instructions}
```
### Step 9: Verify Structure
Run structure validation:
```bash
# Check all required files exist
test -f {name}/plugin.json && echo "✅ plugin.json" || echo "❌ plugin.json missing"
test -f {name}/README.md && echo "✅ README.md" || echo "✅ README.md" || echo "❌ README.md missing"
test -f {name}/LICENSE && echo "✅ LICENSE" || echo "❌ LICENSE missing"
test -f {name}/CHANGELOG.md && echo "✅ CHANGELOG.md" || echo "❌ CHANGELOG.md missing"
test -d {name}/commands && echo "✅ commands/" || echo "❌ commands/ missing"
```
### Step 10: Generate Success Report
Provide comprehensive report with:
- Structure created confirmation
- Files generated list
- Next steps for development
- Testing instructions
- Marketplace submission readiness checklist
## Output Format
```markdown
## ✅ Plugin Scaffolding Complete
### Generated Structure
```
{name}/
├── .claude-plugin/
│ └── plugin.json ✅
├── .gitignore ✅
├── CHANGELOG.md ✅
├── LICENSE ({license}) ✅
├── README.md ✅
├── commands/
│ └── example.md ✅
├── agents/ {if applicable}
│ └── specialist.md ✅
└── hooks/ {if applicable}
└── hooks.json ✅
```
### Files Created
- ✅ **plugin.json**: Complete metadata with all required fields
- ✅ **README.md**: Comprehensive documentation template
- ✅ **LICENSE**: {license} license file
- ✅ **CHANGELOG.md**: Initialized with version 1.0.0
- ✅ **.gitignore**: Plugin development exclusions
- ✅ **commands/example.md**: Sample command template
{Additional files if created}
### Metadata Summary
**Name**: {name}
**Version**: {version}
**Description**: {description}
**Author**: {author}
**License**: {license}
**Category**: {category}
**Keywords**: {keywords}
### Next Steps
1. **Implement Functionality**
- Edit `commands/example.md` with your actual implementation
- Add more commands as needed
- Implement agent logic if included
2. **Update Documentation**
- Customize README.md with real examples
- Document all parameters and usage
- Add troubleshooting section
3. **Local Testing**
```bash
# Create test marketplace
mkdir -p test-marketplace/.claude-plugin
# Add marketplace.json
# Install and test plugin
```
4. **Prepare for Release**
- Update CHANGELOG.md with features
- Verify all documentation is complete
- Run quality checks
- Test all functionality
5. **Submit to OpenPlugins**
- Fork OpenPlugins repository
- Add plugin entry to marketplace.json
- Create pull request
### Quality Checklist
Before submission, ensure:
- ✅ Plugin name follows lowercase-hyphen format
- ✅ Description is 50-200 characters
- ✅ All required metadata fields present
- ✅ README has real content (no placeholders)
- ✅ LICENSE file included
- ✅ At least one functional command
- ✅ No hardcoded secrets
- ✅ Examples are concrete and realistic
### Testing Commands
```bash
# Navigate to plugin directory
cd {name}
# Validate plugin.json
python3 -m json.tool plugin.json
# Check file structure
ls -la
# Initialize git repository
git init
git add .
git commit -m "Initial plugin structure"
```
### Resources
- Plugin Documentation: https://docs.claude.com/en/docs/claude-code/plugins
- OpenPlugins Guide: https://github.com/dhofheinz/open-plugins
- Submit Plugin: Fork repository and add to marketplace.json
---
**Plugin scaffolding complete!** Your plugin is ready for implementation.
```
## Error Handling
- **Invalid plugin name** → Show valid pattern and suggest correction
- **Directory exists** → Ask for confirmation to overwrite or use different name
- **Invalid license type** → List supported licenses: MIT, Apache-2.0, GPL-3.0, BSD-3-Clause
- **Missing required parameter** → Request parameter with expected format
- **Git not available** → Skip git initialization, provide manual instructions
- **File creation fails** → Report specific file and error, provide recovery steps
## Examples
### Example 1: Simple Plugin
**Input**:
```
/plugin-scaffold complete name:hello-world author:"Jane Smith" license:MIT description:"Simple greeting plugin for Claude Code"
```
**Output**: Complete plugin structure with all files
### Example 2: Plugin with Agent
**Input**:
```
/plugin-scaffold complete name:code-reviewer author:"John Doe <john@example.com>" license:Apache-2.0 description:"Automated code review with security analysis" with_agent:true category:quality
```
**Output**: Plugin structure including agent template
**Request**: $ARGUMENTS

View File

@@ -0,0 +1,58 @@
---
description: Generate complete plugin directory structures with all necessary files and proper scaffolding
---
# Plugin Scaffold Skill
Expert plugin structure generation with complete directory scaffolding, metadata creation, and file templates.
## Operations
- **create** - Generate complete plugin directory structure
- **manifest** - Create or update plugin.json with validation
- **readme** - Generate comprehensive README template
- **license** - Add LICENSE file with selected license type
- **complete** - All-in-one: create full plugin structure with all files
## Usage Examples
```bash
# Generate complete structure
/plugin-scaffold create name:my-plugin author:"John Doe"
# Create plugin.json
/plugin-scaffold manifest name:my-plugin version:1.0.0 description:"Plugin description" license:MIT
# Generate README
/plugin-scaffold readme name:my-plugin description:"Full description"
# Add license
/plugin-scaffold license type:MIT plugin:my-plugin
# All-in-one scaffolding
/plugin-scaffold complete name:my-plugin author:"John Doe" license:MIT description:"Complete plugin"
```
## Router Logic
Parse the first word of $ARGUMENTS to determine the requested operation:
1. Extract operation from first word of $ARGUMENTS
2. Parse remaining arguments as key:value parameters
3. Route to appropriate operation file:
- "create" → Read and execute `{plugin-path}/commands/plugin-scaffold/create-structure.md`
- "manifest" → Read and execute `{plugin-path}/commands/plugin-scaffold/generate-manifest.md`
- "readme" → Read and execute `{plugin-path}/commands/plugin-scaffold/create-readme.md`
- "license" → Read and execute `{plugin-path}/commands/plugin-scaffold/add-license.md`
- "complete" → Read and execute `{plugin-path}/commands/plugin-scaffold/complete-scaffold.md`
**Error Handling**:
- If operation unknown → List available operations with usage
- If required parameters missing → Request with expected format
- If plugin name invalid → Suggest valid naming pattern (lowercase-hyphen)
- If directory exists → Warn and ask for confirmation to overwrite
**Base directory**: Plugin commands directory
**Current request**: $ARGUMENTS
Parse operation and route to appropriate instruction file now.