Initial commit
This commit is contained in:
203
skills/build-assistant/scripts/add-skill-instructions.sh
Executable file
203
skills/build-assistant/scripts/add-skill-instructions.sh
Executable file
@@ -0,0 +1,203 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to add skill availability instructions to all agents and commands
|
||||
# Usage: bash add-skill-instructions.sh (can be run from anywhere)
|
||||
|
||||
set -e
|
||||
|
||||
# Find marketplace root by looking for plugins/ directory
|
||||
find_marketplace_root() {
|
||||
local current_dir="$PWD"
|
||||
|
||||
# Check if we're already in marketplace root
|
||||
if [ -d "$current_dir/plugins" ] && [ -d "$current_dir/scripts" ]; then
|
||||
echo "$current_dir"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if script is in scripts/ subdirectory
|
||||
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
local parent_dir="$(dirname "$script_dir")"
|
||||
if [ -d "$parent_dir/plugins" ] && [ -d "$parent_dir/scripts" ]; then
|
||||
echo "$parent_dir"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Search upwards for marketplace root
|
||||
while [ "$current_dir" != "/" ]; do
|
||||
if [ -d "$current_dir/plugins" ] && [ -d "$current_dir/scripts" ] && [ -f "$current_dir/.claude-plugin/marketplace.json" ]; then
|
||||
echo "$current_dir"
|
||||
return 0
|
||||
fi
|
||||
current_dir="$(dirname "$current_dir")"
|
||||
done
|
||||
|
||||
echo "ERROR: Could not find ai-dev-marketplace root directory" >&2
|
||||
echo "Please run this script from within the marketplace directory" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
MARKETPLACE_DIR=$(find_marketplace_root)
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$MARKETPLACE_DIR"
|
||||
echo "📍 Working in: $MARKETPLACE_DIR"
|
||||
echo ""
|
||||
|
||||
echo "🔍 Adding skill availability instructions to agents and commands..."
|
||||
echo ""
|
||||
|
||||
# Counters
|
||||
agents_updated=0
|
||||
commands_updated=0
|
||||
agents_skipped=0
|
||||
commands_skipped=0
|
||||
|
||||
# Function to get skills for a plugin
|
||||
get_plugin_skills() {
|
||||
local plugin_path="$1"
|
||||
local skills_dir="$plugin_path/skills"
|
||||
|
||||
if [ -d "$skills_dir" ]; then
|
||||
# List all skill directories
|
||||
ls "$skills_dir" 2>/dev/null | sort
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to add skills section to a file
|
||||
add_skills_section() {
|
||||
local file="$1"
|
||||
local plugin_name="$2"
|
||||
local plugin_path="plugins/$plugin_name"
|
||||
|
||||
# Check if file already has skill instructions
|
||||
if grep -q "## Available Skills" "$file"; then
|
||||
return 1 # Skip
|
||||
fi
|
||||
|
||||
# Get skills for this plugin
|
||||
local skills=$(get_plugin_skills "$plugin_path")
|
||||
local skill_count=$(echo "$skills" | grep -v "^$" | wc -l)
|
||||
|
||||
if [ "$skill_count" -eq 0 ]; then
|
||||
return 1 # No skills, skip
|
||||
fi
|
||||
|
||||
# Build skill list
|
||||
local skill_list=""
|
||||
while IFS= read -r skill; do
|
||||
if [ -n "$skill" ]; then
|
||||
# Read skill description from SKILL.md if available
|
||||
local skill_file="$plugin_path/skills/$skill/SKILL.md"
|
||||
if [ -f "$skill_file" ]; then
|
||||
local description=$(grep "^description:" "$skill_file" | sed 's/description: //')
|
||||
skill_list="${skill_list}- **$skill**: $description
|
||||
"
|
||||
else
|
||||
skill_list="${skill_list}- **$skill**
|
||||
"
|
||||
fi
|
||||
fi
|
||||
done <<< "$skills"
|
||||
|
||||
# Create skills section
|
||||
local skills_section="## Available Skills
|
||||
|
||||
This $(basename $(dirname "$file")) has access to the following skills from the $plugin_name plugin:
|
||||
|
||||
$skill_list
|
||||
**To use a skill:**
|
||||
\`\`\`
|
||||
!{skill skill-name}
|
||||
\`\`\`
|
||||
|
||||
Use skills when you need:
|
||||
- Domain-specific templates and examples
|
||||
- Validation scripts and automation
|
||||
- Best practices and patterns
|
||||
- Configuration generators
|
||||
|
||||
Skills provide pre-built resources to accelerate your work.
|
||||
|
||||
---
|
||||
|
||||
"
|
||||
|
||||
# Find insertion point (after security section, before main content)
|
||||
# Look for the first ## that's not "Security"
|
||||
local line_num=$(grep -n "^## " "$file" | grep -v "## Security" | grep -v "## Available Skills" | head -1 | cut -d: -f1)
|
||||
|
||||
if [ -z "$line_num" ]; then
|
||||
# No section found, add after frontmatter and security
|
||||
line_num=$(grep -n "^---$" "$file" | tail -1 | cut -d: -f1)
|
||||
line_num=$((line_num + 1))
|
||||
|
||||
# Skip past security section
|
||||
local security_end=$(tail -n +$line_num "$file" | grep -n "^---$" | head -1 | cut -d: -f1)
|
||||
if [ -n "$security_end" ]; then
|
||||
line_num=$((line_num + security_end))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Insert skills section
|
||||
if [ -n "$line_num" ]; then
|
||||
# Insert at line_num
|
||||
echo "$skills_section" | cat - <(tail -n +$line_num "$file") > "$file.tmp"
|
||||
head -n $((line_num - 1)) "$file" >> "$file.tmp.header"
|
||||
cat "$file.tmp.header" "$file.tmp" > "$file"
|
||||
rm "$file.tmp" "$file.tmp.header"
|
||||
return 0
|
||||
else
|
||||
# Couldn't find insertion point, append at end
|
||||
echo "" >> "$file"
|
||||
echo "$skills_section" >> "$file"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Process all agent files
|
||||
echo "📝 Processing agents..."
|
||||
for plugin_dir in plugins/*/; do
|
||||
plugin_name=$(basename "$plugin_dir")
|
||||
|
||||
for agent_file in "$plugin_dir"agents/*.md; do
|
||||
if [[ -f "$agent_file" ]]; then
|
||||
if add_skills_section "$agent_file" "$plugin_name"; then
|
||||
echo " ✅ Updated: $agent_file"
|
||||
agents_updated=$((agents_updated + 1))
|
||||
else
|
||||
agents_skipped=$((agents_skipped + 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "📝 Processing commands..."
|
||||
for plugin_dir in plugins/*/; do
|
||||
plugin_name=$(basename "$plugin_dir")
|
||||
|
||||
for command_file in "$plugin_dir"commands/*.md; do
|
||||
if [[ -f "$command_file" ]]; then
|
||||
if add_skills_section "$command_file" "$plugin_name"; then
|
||||
echo " ✅ Updated: $command_file"
|
||||
commands_updated=$((commands_updated + 1))
|
||||
else
|
||||
commands_skipped=$((commands_skipped + 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "✨ Summary:"
|
||||
echo " Agents updated: $agents_updated"
|
||||
echo " Agents skipped (no skills or already has section): $agents_skipped"
|
||||
echo " Commands updated: $commands_updated"
|
||||
echo " Commands skipped (no skills or already has section): $commands_skipped"
|
||||
echo ""
|
||||
echo "Total updated: $((agents_updated + commands_updated))"
|
||||
echo ""
|
||||
echo "✅ Done! Run 'git diff' to review changes."
|
||||
105
skills/build-assistant/scripts/add-skill-tool.sh
Executable file
105
skills/build-assistant/scripts/add-skill-tool.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to add Skill tool to all agents and commands that are missing it
|
||||
# Usage: bash add-skill-tool.sh (can be run from anywhere)
|
||||
|
||||
set -e
|
||||
|
||||
# Find marketplace root by looking for plugins/ directory
|
||||
find_marketplace_root() {
|
||||
local current_dir="$PWD"
|
||||
|
||||
# Check if we're already in marketplace root
|
||||
if [ -d "$current_dir/plugins" ] && [ -d "$current_dir/scripts" ]; then
|
||||
echo "$current_dir"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if script is in scripts/ subdirectory
|
||||
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
local parent_dir="$(dirname "$script_dir")"
|
||||
if [ -d "$parent_dir/plugins" ] && [ -d "$parent_dir/scripts" ]; then
|
||||
echo "$parent_dir"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Search upwards for marketplace root
|
||||
while [ "$current_dir" != "/" ]; do
|
||||
if [ -d "$current_dir/plugins" ] && [ -d "$current_dir/scripts" ] && [ -f "$current_dir/.claude-plugin/marketplace.json" ]; then
|
||||
echo "$current_dir"
|
||||
return 0
|
||||
fi
|
||||
current_dir="$(dirname "$current_dir")"
|
||||
done
|
||||
|
||||
echo "ERROR: Could not find ai-dev-marketplace root directory" >&2
|
||||
echo "Please run this script from within the marketplace directory" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
MARKETPLACE_DIR=$(find_marketplace_root)
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$MARKETPLACE_DIR"
|
||||
echo "📍 Working in: $MARKETPLACE_DIR"
|
||||
echo ""
|
||||
|
||||
echo "🔍 Adding Skill tool to agents and commands..."
|
||||
echo ""
|
||||
|
||||
# Counters
|
||||
agents_updated=0
|
||||
commands_updated=0
|
||||
agents_skipped=0
|
||||
commands_skipped=0
|
||||
|
||||
# Process all agent files
|
||||
echo "📝 Processing agents..."
|
||||
for agent_file in plugins/*/agents/*.md; do
|
||||
if [[ -f "$agent_file" ]]; then
|
||||
# Check if file has tools: line
|
||||
if grep -q "^tools:" "$agent_file"; then
|
||||
# Check if Skill is already present
|
||||
if grep -q "^tools:.*Skill" "$agent_file"; then
|
||||
agents_skipped=$((agents_skipped + 1))
|
||||
else
|
||||
# Add Skill to the tools line
|
||||
sed -i 's/^tools: \(.*\)$/tools: \1, Skill/' "$agent_file"
|
||||
echo " ✅ Updated: $agent_file"
|
||||
agents_updated=$((agents_updated + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "📝 Processing commands..."
|
||||
for command_file in plugins/*/commands/*.md; do
|
||||
if [[ -f "$command_file" ]]; then
|
||||
# Check if file has allowed-tools: line
|
||||
if grep -q "^allowed-tools:" "$command_file"; then
|
||||
# Check if Skill is already present
|
||||
if grep -q "^allowed-tools:.*Skill" "$command_file"; then
|
||||
commands_skipped=$((commands_skipped + 1))
|
||||
else
|
||||
# Add Skill to the allowed-tools line
|
||||
sed -i 's/^allowed-tools: \(.*\)$/allowed-tools: \1, Skill/' "$command_file"
|
||||
echo " ✅ Updated: $command_file"
|
||||
commands_updated=$((commands_updated + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "✨ Summary:"
|
||||
echo " Agents updated: $agents_updated"
|
||||
echo " Agents skipped (already have Skill): $agents_skipped"
|
||||
echo " Commands updated: $commands_updated"
|
||||
echo " Commands skipped (already have Skill): $commands_skipped"
|
||||
echo ""
|
||||
echo "Total updated: $((agents_updated + commands_updated))"
|
||||
echo ""
|
||||
echo "✅ Done! Run 'git diff' to review changes."
|
||||
466
skills/build-assistant/scripts/create-plugin-structure.py
Executable file
466
skills/build-assistant/scripts/create-plugin-structure.py
Executable file
@@ -0,0 +1,466 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Create New Plugin with Enterprise Structure
|
||||
|
||||
Creates a complete plugin scaffold following enterprise standards:
|
||||
- .claude-plugin/plugin.json
|
||||
- .mcp.json placeholder
|
||||
- hooks/hooks.json
|
||||
- LICENSE (MIT)
|
||||
- CHANGELOG.md
|
||||
- commands/
|
||||
- agents/
|
||||
- skills/{skill-name}/
|
||||
├── SKILL.md
|
||||
├── reference.md
|
||||
├── examples.md
|
||||
├── scripts/
|
||||
└── templates/
|
||||
|
||||
Usage:
|
||||
python create-plugin-structure.py <plugin-name> [--skill <skill-name>]
|
||||
|
||||
Examples:
|
||||
python create-plugin-structure.py multiagent-analytics --skill analytics-assistant
|
||||
python create-plugin-structure.py multiagent-testing --skill test-runner
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
MARKETPLACE_DIR = Path.home() / ".claude/marketplaces/multiagent-dev/plugins"
|
||||
|
||||
# Templates
|
||||
LICENSE_TEMPLATE = """MIT License
|
||||
|
||||
Copyright (c) {year} Multiagent Framework
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
CHANGELOG_TEMPLATE = """# Changelog
|
||||
|
||||
All notable changes to this plugin 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).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Planned
|
||||
- Initial plugin implementation
|
||||
- Core features and commands
|
||||
- Documentation
|
||||
|
||||
## [1.0.0] - {date}
|
||||
|
||||
### Added
|
||||
- Plugin structure created
|
||||
- Enterprise directory layout
|
||||
- Skill scaffolding for {skill_name}
|
||||
"""
|
||||
|
||||
PLUGIN_JSON_TEMPLATE = {
|
||||
"name": "{plugin_name}",
|
||||
"version": "1.0.0",
|
||||
"description": "{description}",
|
||||
"author": {
|
||||
"name": "Multiagent Framework",
|
||||
"email": "noreply@multiagent.dev"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": ["{subsystem}", "multiagent", "automation"],
|
||||
"components": {
|
||||
"commands": 0,
|
||||
"agents": 0,
|
||||
"skills": 1
|
||||
}
|
||||
}
|
||||
|
||||
MCP_JSON_TEMPLATE = {
|
||||
"mcpServers": {},
|
||||
"notes": "MCP server configurations for this plugin. Add servers as needed."
|
||||
}
|
||||
|
||||
HOOKS_JSON_TEMPLATE = {
|
||||
"hooks": {},
|
||||
"notes": "Event hooks for this plugin. Configure hook triggers and scripts as needed."
|
||||
}
|
||||
|
||||
SKILL_MD_TEMPLATE = """---
|
||||
name: {skill_display_name}
|
||||
description: {skill_description}
|
||||
allowed-tools: Read, Write, Bash
|
||||
---
|
||||
|
||||
# {skill_display_name}
|
||||
|
||||
## Instructions
|
||||
|
||||
TODO: Add detailed instructions for this skill.
|
||||
|
||||
### Features
|
||||
|
||||
1. Feature 1
|
||||
2. Feature 2
|
||||
3. Feature 3
|
||||
|
||||
### Trigger Patterns
|
||||
|
||||
This skill is automatically invoked when:
|
||||
- Pattern 1
|
||||
- Pattern 2
|
||||
- Pattern 3
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Basic Usage
|
||||
|
||||
TODO: Add example showing basic usage
|
||||
|
||||
```bash
|
||||
# Example command or code
|
||||
```
|
||||
|
||||
### Example 2: Advanced Usage
|
||||
|
||||
TODO: Add example showing advanced features
|
||||
|
||||
```bash
|
||||
# Example command or code
|
||||
```
|
||||
"""
|
||||
|
||||
REFERENCE_MD_TEMPLATE = """# {skill_display_name} - Reference
|
||||
|
||||
## API Reference
|
||||
|
||||
### Trigger Patterns
|
||||
|
||||
- Pattern 1: Description
|
||||
- Pattern 2: Description
|
||||
- Pattern 3: Description
|
||||
|
||||
### Input Requirements
|
||||
|
||||
- Input 1: Description and format
|
||||
- Input 2: Description and format
|
||||
|
||||
### Output Format
|
||||
|
||||
- Output 1: Description
|
||||
- Output 2: Description
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# VARIABLE_NAME=value # Description
|
||||
```
|
||||
|
||||
### Settings
|
||||
|
||||
TODO: Document configurable settings
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
TODO: Add performance notes
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue 1
|
||||
**Problem:** Description
|
||||
**Solution:** How to fix
|
||||
"""
|
||||
|
||||
EXAMPLES_MD_TEMPLATE = """# {skill_display_name} - Examples
|
||||
|
||||
## Example 1: Basic Use Case
|
||||
|
||||
TODO: Add first example
|
||||
|
||||
```bash
|
||||
# Example code
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Expected result
|
||||
```
|
||||
|
||||
## Example 2: Advanced Use Case
|
||||
|
||||
TODO: Add second example
|
||||
|
||||
```bash
|
||||
# Example code
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Expected result
|
||||
```
|
||||
|
||||
## Example 3: Real-World Scenario
|
||||
|
||||
TODO: Add real-world example
|
||||
|
||||
```bash
|
||||
# Example code
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Expected result
|
||||
```
|
||||
"""
|
||||
|
||||
README_TEMPLATE = """# {plugin_name}
|
||||
|
||||
{description}
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
/plugin install {plugin_name}@multiagent-dev
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
- **Commands**: 0 slash commands
|
||||
- **Agents**: 0 specialized agents
|
||||
- **Skills**: 1 skill
|
||||
|
||||
## Skills
|
||||
|
||||
### {skill_display_name}
|
||||
|
||||
{skill_description}
|
||||
|
||||
See `skills/{skill_slug}/SKILL.md` for details.
|
||||
|
||||
## Development
|
||||
|
||||
This plugin follows the multiagent enterprise plugin structure.
|
||||
|
||||
### Directory Layout
|
||||
|
||||
```
|
||||
{plugin_name}/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json
|
||||
├── .mcp.json
|
||||
├── hooks/
|
||||
│ └── hooks.json
|
||||
├── LICENSE
|
||||
├── CHANGELOG.md
|
||||
├── README.md
|
||||
├── commands/ # Slash commands
|
||||
├── agents/ # Specialized agents
|
||||
└── skills/ # Auto-discovered skills
|
||||
└── {skill_slug}/
|
||||
├── SKILL.md
|
||||
├── reference.md
|
||||
├── examples.md
|
||||
├── scripts/
|
||||
└── templates/
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT License - see LICENSE file for details
|
||||
"""
|
||||
|
||||
|
||||
def create_plugin_structure(plugin_name: str, skill_name: str = None):
|
||||
"""Create complete enterprise plugin structure."""
|
||||
|
||||
# Extract subsystem name
|
||||
subsystem = plugin_name.replace("multiagent-", "")
|
||||
|
||||
# Default skill name if not provided
|
||||
if not skill_name:
|
||||
skill_name = f"{subsystem}-assistant"
|
||||
|
||||
skill_slug = skill_name.lower().replace(" ", "-")
|
||||
skill_display_name = skill_name.replace("-", " ").title()
|
||||
|
||||
plugin_dir = MARKETPLACE_DIR / plugin_name
|
||||
|
||||
if plugin_dir.exists():
|
||||
print(f"❌ Plugin directory already exists: {plugin_dir}")
|
||||
print(f" Please remove it first or choose a different name.")
|
||||
return False
|
||||
|
||||
print("=" * 70)
|
||||
print(f"Creating Plugin: {plugin_name}")
|
||||
print("=" * 70)
|
||||
print(f"Skill: {skill_display_name} ({skill_slug})")
|
||||
print(f"Location: {plugin_dir}\n")
|
||||
|
||||
# Create directory structure
|
||||
print("Creating directory structure...")
|
||||
|
||||
dirs_to_create = [
|
||||
plugin_dir / ".claude-plugin",
|
||||
plugin_dir / "hooks",
|
||||
plugin_dir / "commands",
|
||||
plugin_dir / "agents",
|
||||
plugin_dir / "skills" / skill_slug / "scripts",
|
||||
plugin_dir / "skills" / skill_slug / "templates",
|
||||
]
|
||||
|
||||
for dir_path in dirs_to_create:
|
||||
dir_path.mkdir(parents=True, exist_ok=True)
|
||||
print(f" ✓ Created: {dir_path.relative_to(plugin_dir)}/")
|
||||
|
||||
# Create plugin.json
|
||||
print("\nCreating plugin.json...")
|
||||
plugin_json = PLUGIN_JSON_TEMPLATE.copy()
|
||||
plugin_json["name"] = plugin_name
|
||||
plugin_json["description"] = f"{subsystem.title()} functionality for multiagent framework"
|
||||
plugin_json["keywords"] = [subsystem, "multiagent", "automation"]
|
||||
|
||||
plugin_json_path = plugin_dir / ".claude-plugin" / "plugin.json"
|
||||
with open(plugin_json_path, 'w') as f:
|
||||
json.dump(plugin_json, f, indent=2)
|
||||
print(f" ✓ Created: .claude-plugin/plugin.json")
|
||||
|
||||
# Create .mcp.json
|
||||
print("\nCreating .mcp.json...")
|
||||
mcp_json_path = plugin_dir / ".mcp.json"
|
||||
with open(mcp_json_path, 'w') as f:
|
||||
json.dump(MCP_JSON_TEMPLATE, f, indent=2)
|
||||
print(f" ✓ Created: .mcp.json")
|
||||
|
||||
# Create hooks.json
|
||||
print("\nCreating hooks/hooks.json...")
|
||||
hooks_json_path = plugin_dir / "hooks" / "hooks.json"
|
||||
with open(hooks_json_path, 'w') as f:
|
||||
json.dump(HOOKS_JSON_TEMPLATE, f, indent=2)
|
||||
print(f" ✓ Created: hooks/hooks.json")
|
||||
|
||||
# Create LICENSE
|
||||
print("\nCreating LICENSE...")
|
||||
license_path = plugin_dir / "LICENSE"
|
||||
license_content = LICENSE_TEMPLATE.format(year=datetime.now().year)
|
||||
license_path.write_text(license_content)
|
||||
print(f" ✓ Created: LICENSE")
|
||||
|
||||
# Create CHANGELOG.md
|
||||
print("\nCreating CHANGELOG.md...")
|
||||
changelog_path = plugin_dir / "CHANGELOG.md"
|
||||
changelog_content = CHANGELOG_TEMPLATE.format(
|
||||
date=datetime.now().strftime("%Y-%m-%d"),
|
||||
skill_name=skill_display_name
|
||||
)
|
||||
changelog_path.write_text(changelog_content)
|
||||
print(f" ✓ Created: CHANGELOG.md")
|
||||
|
||||
# Create README.md
|
||||
print("\nCreating README.md...")
|
||||
readme_path = plugin_dir / "README.md"
|
||||
readme_content = README_TEMPLATE.format(
|
||||
plugin_name=plugin_name,
|
||||
description=f"{subsystem.title()} functionality for multiagent framework",
|
||||
skill_display_name=skill_display_name,
|
||||
skill_description=f"Provides {subsystem} capabilities",
|
||||
skill_slug=skill_slug
|
||||
)
|
||||
readme_path.write_text(readme_content)
|
||||
print(f" ✓ Created: README.md")
|
||||
|
||||
# Create skill files
|
||||
skill_dir = plugin_dir / "skills" / skill_slug
|
||||
|
||||
print(f"\nCreating skill documentation for '{skill_display_name}'...")
|
||||
|
||||
# SKILL.md
|
||||
skill_md_path = skill_dir / "SKILL.md"
|
||||
skill_md_content = SKILL_MD_TEMPLATE.format(
|
||||
skill_display_name=skill_display_name,
|
||||
skill_description=f"Provides {subsystem} capabilities for the multiagent framework"
|
||||
)
|
||||
skill_md_path.write_text(skill_md_content)
|
||||
print(f" ✓ Created: skills/{skill_slug}/SKILL.md")
|
||||
|
||||
# reference.md
|
||||
reference_md_path = skill_dir / "reference.md"
|
||||
reference_content = REFERENCE_MD_TEMPLATE.format(
|
||||
skill_display_name=skill_display_name
|
||||
)
|
||||
reference_md_path.write_text(reference_content)
|
||||
print(f" ✓ Created: skills/{skill_slug}/reference.md")
|
||||
|
||||
# examples.md
|
||||
examples_md_path = skill_dir / "examples.md"
|
||||
examples_content = EXAMPLES_MD_TEMPLATE.format(
|
||||
skill_display_name=skill_display_name
|
||||
)
|
||||
examples_md_path.write_text(examples_content)
|
||||
print(f" ✓ Created: skills/{skill_slug}/examples.md")
|
||||
|
||||
# Create .gitkeep files
|
||||
(skill_dir / "scripts" / ".gitkeep").touch()
|
||||
(skill_dir / "templates" / ".gitkeep").touch()
|
||||
(plugin_dir / "commands" / ".gitkeep").touch()
|
||||
(plugin_dir / "agents" / ".gitkeep").touch()
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("SUCCESS: Plugin Structure Created!")
|
||||
print("=" * 70)
|
||||
|
||||
print(f"\nLocation: {plugin_dir}")
|
||||
print(f"\nNext Steps:")
|
||||
print(f" 1. Edit skills/{skill_slug}/SKILL.md with actual skill instructions")
|
||||
print(f" 2. Add commands to commands/ directory")
|
||||
print(f" 3. Add agents to agents/ directory")
|
||||
print(f" 4. Add scripts to skills/{skill_slug}/scripts/")
|
||||
print(f" 5. Add templates to skills/{skill_slug}/templates/")
|
||||
print(f" 6. Update README.md with actual documentation")
|
||||
print(f" 7. Test with: /plugin install {plugin_name}@multiagent-dev")
|
||||
print()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python create-plugin-structure.py <plugin-name> [--skill <skill-name>]")
|
||||
print("\nExamples:")
|
||||
print(" python create-plugin-structure.py multiagent-analytics")
|
||||
print(" python create-plugin-structure.py multiagent-testing --skill test-runner")
|
||||
sys.exit(1)
|
||||
|
||||
plugin_name = sys.argv[1]
|
||||
skill_name = None
|
||||
|
||||
# Parse optional --skill argument
|
||||
if "--skill" in sys.argv:
|
||||
skill_idx = sys.argv.index("--skill")
|
||||
if skill_idx + 1 < len(sys.argv):
|
||||
skill_name = sys.argv[skill_idx + 1]
|
||||
|
||||
success = create_plugin_structure(plugin_name, skill_name)
|
||||
sys.exit(0 if success else 1)
|
||||
168
skills/build-assistant/scripts/create-skill-structures.py
Executable file
168
skills/build-assistant/scripts/create-skill-structures.py
Executable file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Create Skill Directory Structures for Existing Plugins
|
||||
|
||||
Mechanically creates the directory structure for skills in plugins
|
||||
that don't have them yet. Agent will fill in content later.
|
||||
|
||||
Usage:
|
||||
python create-skill-structures.py [--dry-run]
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
MARKETPLACE_DIR = Path.home() / ".claude/marketplaces/multiagent-dev/plugins"
|
||||
|
||||
# Mapping: plugin-name → skill-name
|
||||
PLUGIN_SKILLS = {
|
||||
"multiagent-ai-infrastructure": "ai-infrastructure-assistant",
|
||||
"multiagent-backend": "backend-developer",
|
||||
"multiagent-build": "build-assistant",
|
||||
"multiagent-compliance": "compliance-advisor",
|
||||
"multiagent-core": "core-initializer",
|
||||
"multiagent-cto": "architecture-reviewer",
|
||||
"multiagent-deployment": "deployment-assistant",
|
||||
"multiagent-docs": "documentation-writer",
|
||||
"multiagent-enhancement": "enhancement-manager",
|
||||
"multiagent-frontend": "frontend-developer",
|
||||
"multiagent-github": "github-integration",
|
||||
"multiagent-idea": "idea-tracker",
|
||||
"multiagent-implementation": "implementation-orchestrator",
|
||||
"multiagent-iterate": "iteration-manager",
|
||||
"multiagent-mcp": "mcp-manager",
|
||||
"multiagent-notes": "notes-tracker",
|
||||
"multiagent-observability": "observability-monitor",
|
||||
"multiagent-performance": "performance-optimizer",
|
||||
"multiagent-profile": "developer-profile",
|
||||
"multiagent-refactoring": "refactoring-analyzer",
|
||||
"multiagent-reliability": "reliability-engineer",
|
||||
"multiagent-security": "security-advisor",
|
||||
"multiagent-supervisor": "supervisor-coordinator",
|
||||
"multiagent-validation": "validation-checker",
|
||||
"multiagent-version": "version-manager",
|
||||
}
|
||||
|
||||
|
||||
def create_skill_structure(plugin_name: str, skill_name: str, dry_run: bool = False):
|
||||
"""Create mechanical skill directory structure."""
|
||||
|
||||
plugin_dir = MARKETPLACE_DIR / plugin_name
|
||||
skill_slug = skill_name.lower().replace(" ", "-")
|
||||
skill_dir = plugin_dir / "skills" / skill_slug
|
||||
|
||||
# Check if plugin exists
|
||||
if not plugin_dir.exists():
|
||||
print(f" WARNING: Plugin directory not found: {plugin_dir}")
|
||||
return False
|
||||
|
||||
# Check if skill already exists
|
||||
if (skill_dir / "SKILL.md").exists():
|
||||
print(f" SKIP: Skill already exists: {skill_slug}")
|
||||
return False
|
||||
|
||||
print(f"\n[{plugin_name}]")
|
||||
print(f" Creating skill: {skill_slug}")
|
||||
|
||||
if dry_run:
|
||||
print(f" [DRY RUN] Would create:")
|
||||
print(f" - {skill_dir}/")
|
||||
print(f" - {skill_dir}/scripts/")
|
||||
print(f" - {skill_dir}/templates/")
|
||||
print(f" - {skill_dir}/SKILL.md (placeholder)")
|
||||
print(f" - {skill_dir}/reference.md (placeholder)")
|
||||
print(f" - {skill_dir}/examples.md (placeholder)")
|
||||
return True
|
||||
|
||||
# Create directories
|
||||
skill_dir.mkdir(parents=True, exist_ok=True)
|
||||
(skill_dir / "scripts").mkdir(exist_ok=True)
|
||||
(skill_dir / "templates").mkdir(exist_ok=True)
|
||||
|
||||
print(f" OK: Created directories")
|
||||
|
||||
# Create placeholder files (agent will fill these)
|
||||
skill_md = skill_dir / "SKILL.md"
|
||||
skill_md.write_text(f"""---
|
||||
name: {skill_name.title()}
|
||||
description: TODO - Agent will fill this
|
||||
allowed-tools: Read, Write, Bash
|
||||
---
|
||||
|
||||
# {skill_name.title()}
|
||||
|
||||
TODO: Agent will generate content using skill-builder
|
||||
""")
|
||||
|
||||
reference_md = skill_dir / "reference.md"
|
||||
reference_md.write_text(f"""# {skill_name.title()} - Reference
|
||||
|
||||
TODO: Agent will generate API reference
|
||||
""")
|
||||
|
||||
examples_md = skill_dir / "examples.md"
|
||||
examples_md.write_text(f"""# {skill_name.title()} - Examples
|
||||
|
||||
TODO: Agent will generate examples
|
||||
""")
|
||||
|
||||
# Create .gitkeep in empty dirs
|
||||
(skill_dir / "scripts" / ".gitkeep").touch()
|
||||
(skill_dir / "templates" / ".gitkeep").touch()
|
||||
|
||||
print(f" OK: Created placeholder files")
|
||||
print(f" Location: {skill_dir}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
"""Create skill structures for all plugins."""
|
||||
|
||||
dry_run = "--dry-run" in sys.argv
|
||||
|
||||
print("=" * 70)
|
||||
print("Creating Skill Structures for Existing Plugins")
|
||||
print("=" * 70)
|
||||
|
||||
if dry_run:
|
||||
print("\n⚠️ DRY RUN MODE - No changes will be made\n")
|
||||
|
||||
print(f"\nMarketplace: {MARKETPLACE_DIR}")
|
||||
print(f"Plugins to process: {len(PLUGIN_SKILLS)}\n")
|
||||
|
||||
created = 0
|
||||
skipped = 0
|
||||
errors = 0
|
||||
|
||||
for plugin_name, skill_name in PLUGIN_SKILLS.items():
|
||||
try:
|
||||
result = create_skill_structure(plugin_name, skill_name, dry_run)
|
||||
if result:
|
||||
created += 1
|
||||
else:
|
||||
skipped += 1
|
||||
except Exception as e:
|
||||
print(f" ERROR: {e}")
|
||||
errors += 1
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("Summary")
|
||||
print("=" * 70)
|
||||
print(f"Created: {created}")
|
||||
print(f"Skipped: {skipped}")
|
||||
print(f"Errors: {errors}")
|
||||
print()
|
||||
|
||||
if dry_run:
|
||||
print("Run without --dry-run to create structures")
|
||||
else:
|
||||
print("Next step: Run headless skill generation to fill content")
|
||||
print(" ./scripts/plugins/marketplace/fill-skill-content.sh")
|
||||
|
||||
return 0 if errors == 0 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
44
skills/build-assistant/scripts/find-hardcoded-paths.sh
Executable file
44
skills/build-assistant/scripts/find-hardcoded-paths.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Find hardcoded paths in plugin files
|
||||
# Usage: bash find-hardcoded-paths.sh [directory]
|
||||
|
||||
DIR="${1:-$HOME/.claude/plugins/marketplaces/domain-plugin-builder/plugins/domain-plugin-builder}"
|
||||
|
||||
echo "Scanning for hardcoded paths in: $DIR"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
|
||||
# Find markdown files with hardcoded paths (excluding URLs)
|
||||
echo "## Hardcoded paths in markdown files:"
|
||||
echo ""
|
||||
|
||||
# Look for absolute paths that aren't URLs
|
||||
grep -rn \
|
||||
--include="*.md" \
|
||||
-E '(~/.claude/|/home/[^/]+/|\.claude/plugins/marketplaces/|plugins/[^/]+/)' \
|
||||
"$DIR" \
|
||||
| grep -v 'http://' \
|
||||
| grep -v 'https://' \
|
||||
| grep -v 'bash ~/.claude/plugins/marketplaces/domain-plugin-builder/plugins/domain-plugin-builder/skills/build-assistant/scripts/' \
|
||||
| grep -v '# ' \
|
||||
| grep -v 'CRITICAL: Script Paths Must Be Absolute' \
|
||||
| grep -v 'Example:' \
|
||||
| grep -v '```'
|
||||
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo ""
|
||||
echo "## What should be fixed:"
|
||||
echo ""
|
||||
echo "For documentation references in agents/commands/skills:"
|
||||
echo " ❌ ~/.claude/plugins/.../docs/frameworks/claude/dans-composition-pattern.md"
|
||||
echo " ✅ @dans-composition-pattern.md"
|
||||
echo ""
|
||||
echo "For bash validation scripts (these SHOULD stay absolute):"
|
||||
echo " ✅ bash ~/.claude/plugins/marketplaces/domain-plugin-builder/plugins/domain-plugin-builder/skills/build-assistant/scripts/validate-agent.sh"
|
||||
echo ""
|
||||
echo "For @ references in prompts:"
|
||||
echo " ✅ @agent-color-decision.md"
|
||||
echo " ✅ @docs/frameworks/claude/dans-composition-pattern.md"
|
||||
echo ""
|
||||
66
skills/build-assistant/scripts/fix-argument-hints.sh
Executable file
66
skills/build-assistant/scripts/fix-argument-hints.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
# fix-argument-hints.sh
|
||||
# Fixes argument hint formatting issues found by validate-argument-hints.sh
|
||||
|
||||
PLUGIN_DIR="${1:-.}"
|
||||
FIXES_APPLIED=0
|
||||
|
||||
echo "=== Fixing Argument Hints ==="
|
||||
echo ""
|
||||
|
||||
if [ ! -d "$PLUGIN_DIR" ]; then
|
||||
echo "❌ ERROR: Directory not found: $PLUGIN_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find all command files
|
||||
COMMAND_FILES=$(find "$PLUGIN_DIR" -type f -path "*/commands/*.md" 2>/dev/null)
|
||||
|
||||
if [ -z "$COMMAND_FILES" ]; then
|
||||
echo "⚠️ No command files found in $PLUGIN_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Scanning and fixing command files..."
|
||||
echo ""
|
||||
|
||||
while IFS= read -r file; do
|
||||
# Check if argument-hint is missing
|
||||
if ! grep -q "^argument-hint:" "$file"; then
|
||||
echo "📝 Adding missing argument-hint to: $file"
|
||||
# Add after description line
|
||||
sed -i '/^description:/a argument-hint: none' "$file"
|
||||
((FIXES_APPLIED++))
|
||||
fi
|
||||
|
||||
# Fix improper format (quoted strings without brackets)
|
||||
HINT=$(sed -n '/^---$/,/^---$/p' "$file" | grep "^argument-hint:" | sed 's/argument-hint: *//')
|
||||
|
||||
# Check if it's a quoted string like "Spec directory (e.g., 002-system-context-we)"
|
||||
if echo "$HINT" | grep -qE '^".*\(e\.g\.'; then
|
||||
echo "🔧 Fixing format in: $file"
|
||||
# Extract the main part before (e.g.,
|
||||
MAIN_PART=$(echo "$HINT" | sed 's/".*(\(e\.g\.,.*\))".*/<\1>/' | sed 's/Spec directory/<spec-directory>/')
|
||||
sed -i "s|^argument-hint:.*|argument-hint: <spec-directory>|" "$file"
|
||||
((FIXES_APPLIED++))
|
||||
fi
|
||||
|
||||
# Fix legacy subsystem references
|
||||
if grep -q "^argument-hint:.*subsystem" "$file"; then
|
||||
echo "🔄 Replacing 'subsystem' with 'plugin' in: $file"
|
||||
sed -i 's/argument-hint:.*subsystem/argument-hint: <plugin-name>/' "$file"
|
||||
((FIXES_APPLIED++))
|
||||
fi
|
||||
|
||||
done <<< "$COMMAND_FILES"
|
||||
|
||||
echo ""
|
||||
echo "=== Summary ==="
|
||||
if [ $FIXES_APPLIED -eq 0 ]; then
|
||||
echo "✅ No fixes needed"
|
||||
exit 0
|
||||
else
|
||||
echo "✅ Applied $FIXES_APPLIED fix(es)"
|
||||
exit 0
|
||||
fi
|
||||
70
skills/build-assistant/scripts/fix-hardcoded-doc-refs.sh
Executable file
70
skills/build-assistant/scripts/fix-hardcoded-doc-refs.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Fix hardcoded documentation references to use @ symbol
|
||||
# Usage: bash fix-hardcoded-doc-refs.sh <marketplace-directory>
|
||||
|
||||
MARKETPLACE_DIR="${1:-$(pwd)}"
|
||||
|
||||
echo "Fixing hardcoded documentation references in: $MARKETPLACE_DIR"
|
||||
echo "========================================================"
|
||||
|
||||
# Counter
|
||||
FIXED=0
|
||||
|
||||
# Find all markdown files
|
||||
find "$MARKETPLACE_DIR" -type f -name "*.md" | while read -r FILE; do
|
||||
# Skip CLAUDE.md script path examples (those should stay absolute)
|
||||
if [[ "$FILE" == *"/CLAUDE.md" ]]; then
|
||||
# Only fix @ references in CLAUDE.md, not bash script paths
|
||||
if grep -q "@plugins/domain-plugin-builder/" "$FILE" 2>/dev/null; then
|
||||
echo "Fixing @ references in: $FILE"
|
||||
|
||||
# Fix template references
|
||||
sed -i 's|@plugins/domain-plugin-builder/skills/build-assistant/templates/commands/template-command-patterns.md|@template-command-patterns.md|g' "$FILE"
|
||||
sed -i 's|@plugins/domain-plugin-builder/skills/build-assistant/templates/agents/agent-with-phased-webfetch.md|@agent-with-phased-webfetch.md|g' "$FILE"
|
||||
|
||||
# Fix framework doc references
|
||||
sed -i 's|@plugins/domain-plugin-builder/docs/frameworks/claude/component-decision-framework.md|@component-decision-framework.md|g' "$FILE"
|
||||
sed -i 's|@plugins/domain-plugin-builder/docs/sdks/|@|g' "$FILE"
|
||||
|
||||
((FIXED++))
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
# For all other files, fix @ references
|
||||
if grep -q "@plugins/" "$FILE" 2>/dev/null; then
|
||||
echo "Fixing: $FILE"
|
||||
|
||||
# Template references
|
||||
sed -i 's|@plugins/domain-plugin-builder/skills/build-assistant/templates/commands/template-command-patterns.md|@template-command-patterns.md|g' "$FILE"
|
||||
sed -i 's|@plugins/domain-plugin-builder/skills/build-assistant/templates/agents/agent-with-phased-webfetch.md|@agent-with-phased-webfetch.md|g' "$FILE"
|
||||
|
||||
# Framework docs
|
||||
sed -i 's|@plugins/domain-plugin-builder/docs/frameworks/claude/component-decision-framework.md|@component-decision-framework.md|g' "$FILE"
|
||||
sed -i 's|@plugins/domain-plugin-builder/docs/frameworks/claude/dans-composition-pattern.md|@dans-composition-pattern.md|g' "$FILE"
|
||||
sed -i 's|@plugins/domain-plugin-builder/docs/frameworks/claude/agent-skills-architecture.md|@agent-skills-architecture.md|g' "$FILE"
|
||||
|
||||
# SDK docs (simplified)
|
||||
sed -i 's|@plugins/domain-plugin-builder/docs/sdks/claude-agent-sdk-documentation.md|@claude-agent-sdk-documentation.md|g' "$FILE"
|
||||
sed -i 's|@plugins/domain-plugin-builder/docs/sdks/\([^/]*\)|@\1|g' "$FILE"
|
||||
|
||||
# Plugin-specific docs (keep relative @plugins/PLUGIN_NAME/docs/)
|
||||
# These are OK and should stay as-is for cross-plugin references
|
||||
|
||||
((FIXED++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========================================================"
|
||||
echo "✅ Fixed $FIXED files"
|
||||
echo ""
|
||||
echo "Files with @ references should now use short names:"
|
||||
echo " @template-command-patterns.md"
|
||||
echo " @component-decision-framework.md"
|
||||
echo " @dans-composition-pattern.md"
|
||||
echo ""
|
||||
echo "Cross-plugin references stay as:"
|
||||
echo " @plugins/PLUGIN_NAME/docs/file.md"
|
||||
echo ""
|
||||
105
skills/build-assistant/scripts/fix-hardcoded-paths.sh
Executable file
105
skills/build-assistant/scripts/fix-hardcoded-paths.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: fix-hardcoded-paths.sh
|
||||
# Purpose: Fix hardcoded multiagent-core paths to use simple project-relative paths
|
||||
# Usage: ./fix-hardcoded-paths.sh [--dry-run]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DRY_RUN=false
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
DRY_RUN=true
|
||||
echo "DRY RUN MODE - No files will be changed"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
PLUGINS_DIR="$HOME/.claude/marketplaces/multiagent-dev/plugins"
|
||||
|
||||
echo "=== Fixing Hardcoded Paths in Plugins ==="
|
||||
echo ""
|
||||
|
||||
# Complex path pattern that needs replacement
|
||||
COMPLEX_CONFIG_PATH='$([ -f "$([ -d "$HOME/.claude/marketplaces/multiagent-dev/plugins/*/skills/*/config" ] && echo "$HOME/.claude/marketplaces/multiagent-dev/plugins/*/skills/*/config" || find "$HOME/.claude/marketplaces/multiagent-dev/plugins/multiagent-config" -type d -path "*/skills/*" -name "config" 2>/dev/null | head -1).json" ] && echo "$([ -d "$HOME/.claude/marketplaces/multiagent-dev/plugins/*/skills/*/config" ] && echo "$HOME/.claude/marketplaces/multiagent-dev/plugins/*/skills/*/config" || find "$HOME/.claude/marketplaces/multiagent-dev/plugins/multiagent-config" -type d -path "*/skills/*" -name "config" 2>/dev/null | head -1).json" || find "$HOME/.claude/marketplaces/multiagent-dev/plugins/multiagent-core/skills/*" -name "config.json" -type f 2>/dev/null | head -1)'
|
||||
|
||||
SIMPLE_CONFIG_PATH='.multiagent/config.json'
|
||||
|
||||
# Count before
|
||||
COMPLEX_PATHS_BEFORE=$(grep -r "multiagent-core/skills" "$PLUGINS_DIR"/*/commands/*.md "$PLUGINS_DIR"/*/agents/*.md 2>/dev/null | wc -l)
|
||||
|
||||
echo "Before:"
|
||||
echo " - Complex multiagent-core paths: $COMPLEX_PATHS_BEFORE"
|
||||
echo ""
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "[DRY RUN] Would perform these replacements:"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
FIXED_FILES=0
|
||||
|
||||
# Fix all agent and command files
|
||||
for file in "$PLUGINS_DIR"/*/agents/*.md "$PLUGINS_DIR"/*/commands/*.md; do
|
||||
[ -f "$file" ] || continue
|
||||
|
||||
# Check if file has hardcoded multiagent-core references
|
||||
if grep -q "multiagent-core" "$file" 2>/dev/null; then
|
||||
if $DRY_RUN; then
|
||||
echo "[DRY RUN] Would fix: $file"
|
||||
grep -n "multiagent-core" "$file" | head -3
|
||||
echo ""
|
||||
else
|
||||
# Create backup
|
||||
cp "$file" "$file.backup"
|
||||
|
||||
# Replace all complex config paths with simple .multiagent/config.json
|
||||
sed -i 's|\$HOME/\.\(claude\|multiagent\)/[^"]*config\.json|.multiagent/config.json|g' "$file"
|
||||
sed -i 's|\$(find[^)]*multiagent-core[^)]*)|.multiagent/config.json|g' "$file"
|
||||
sed -i 's|\$(find[^)]*multiagent-config[^)]*)|.multiagent/config.json|g' "$file"
|
||||
|
||||
# Fix script references
|
||||
sed -i 's|\$HOME/\.\(claude\|multiagent\)/[^"]*\.sh|.multiagent/scripts/\$(basename \$0)|g' "$file"
|
||||
sed -i 's|\$(find[^)]*multiagent-core[^)]*\.sh[^)]*)|.multiagent/scripts/\$(basename \$0)|g' "$file"
|
||||
|
||||
# Fix template references
|
||||
sed -i 's|\$HOME/\.\(claude\|multiagent\)/[^"]*templates|.multiagent/templates|g' "$file"
|
||||
sed -i 's|\$(find[^)]*multiagent-core[^)]*templates[^)]*)|.multiagent/templates|g' "$file"
|
||||
|
||||
# Fix specific worktree reference
|
||||
sed -i 's|../multiagent-core-worktrees/|../PROJECT-worktrees/|g' "$file"
|
||||
|
||||
# Fix validation reference
|
||||
sed -i 's|--plugin multiagent-core|--plugin \$(basename \$(git rev-parse --show-toplevel))|g' "$file"
|
||||
|
||||
echo "✓ Fixed: $file"
|
||||
((FIXED_FILES++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "[DRY RUN] Would fix $FIXED_FILES files"
|
||||
else
|
||||
echo "✓ Fixed $FIXED_FILES files"
|
||||
|
||||
# Count after
|
||||
COMPLEX_PATHS_AFTER=$(grep -r "multiagent-core" "$PLUGINS_DIR"/*/commands/*.md "$PLUGINS_DIR"/*/agents/*.md 2>/dev/null | wc -l || echo 0)
|
||||
|
||||
echo ""
|
||||
echo "After:"
|
||||
echo " - Complex multiagent-core paths: $COMPLEX_PATHS_AFTER (was $COMPLEX_PATHS_BEFORE)"
|
||||
echo ""
|
||||
|
||||
if [ "$COMPLEX_PATHS_AFTER" -eq 0 ]; then
|
||||
echo "🎉 All hardcoded paths fixed!"
|
||||
else
|
||||
echo "⚠️ Some references remain - may need manual review"
|
||||
echo ""
|
||||
echo "Remaining issues:"
|
||||
grep -rn "multiagent-core" "$PLUGINS_DIR"/*/commands/*.md "$PLUGINS_DIR"/*/agents/*.md 2>/dev/null | head -5
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Backups saved with .backup extension"
|
||||
echo "To restore: find $PLUGINS_DIR -name '*.backup' -exec bash -c 'mv \"\$0\" \"\${0%.backup}\"' {} \\;"
|
||||
fi
|
||||
79
skills/build-assistant/scripts/fix-script-issues.sh
Executable file
79
skills/build-assistant/scripts/fix-script-issues.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Fix script issues across marketplaces:
|
||||
# 1. Make all .sh scripts executable
|
||||
# 2. Verify script references use absolute paths (they should!)
|
||||
# Usage: bash fix-script-issues.sh <marketplace-directory>
|
||||
|
||||
MARKETPLACE_DIR="${1:-$(pwd)}"
|
||||
|
||||
echo "Fixing script issues in: $MARKETPLACE_DIR"
|
||||
echo "========================================================"
|
||||
echo ""
|
||||
|
||||
# Counter
|
||||
FIXED_PERMS=0
|
||||
WRONG_REFS=0
|
||||
|
||||
echo "## Phase 1: Making all .sh scripts executable"
|
||||
echo ""
|
||||
|
||||
# Find all .sh files and make them executable
|
||||
find "$MARKETPLACE_DIR" -type f -name "*.sh" | while read -r SCRIPT; do
|
||||
if [ ! -x "$SCRIPT" ]; then
|
||||
echo "Making executable: $SCRIPT"
|
||||
chmod +x "$SCRIPT"
|
||||
((FIXED_PERMS++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "✅ Fixed permissions on $FIXED_PERMS scripts"
|
||||
echo ""
|
||||
|
||||
echo "## Phase 2: Checking script references in markdown files"
|
||||
echo ""
|
||||
|
||||
# Find markdown files with script references that DON'T use absolute paths
|
||||
find "$MARKETPLACE_DIR" -type f -name "*.md" -exec grep -l '!{bash .*\.sh' {} \; | while read -r FILE; do
|
||||
# Check for relative script paths (bad)
|
||||
if grep -q '!{bash plugins/.*\.sh' "$FILE" 2>/dev/null; then
|
||||
echo "⚠️ Found RELATIVE script path in: $FILE"
|
||||
grep -n '!{bash plugins/.*\.sh' "$FILE"
|
||||
((WRONG_REFS++))
|
||||
fi
|
||||
|
||||
# Check for $HOME variable usage (bad - should be ~)
|
||||
if grep -q '!{bash \$HOME/.claude/.*\.sh' "$FILE" 2>/dev/null; then
|
||||
echo "⚠️ Found \$HOME instead of ~ in: $FILE"
|
||||
grep -n '!{bash \$HOME/.claude/.*\.sh' "$FILE"
|
||||
((WRONG_REFS++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========================================================"
|
||||
echo "✅ Made $FIXED_PERMS scripts executable"
|
||||
echo ""
|
||||
|
||||
if [ $WRONG_REFS -gt 0 ]; then
|
||||
echo "⚠️ Found $WRONG_REFS files with incorrect script references"
|
||||
echo ""
|
||||
echo "Script references should use ABSOLUTE paths:"
|
||||
echo " ✅ !{bash ~/.claude/plugins/marketplaces/domain-plugin-builder/plugins/domain-plugin-builder/skills/build-assistant/scripts/validate-agent.sh}"
|
||||
echo " ❌ !{bash plugins/domain-plugin-builder/skills/build-assistant/scripts/validate-agent.sh}"
|
||||
echo " ❌ !{bash \$HOME/.claude/plugins/...}"
|
||||
echo ""
|
||||
echo "Why: Scripts must be callable from ANY working directory"
|
||||
else
|
||||
echo "✅ All script references use absolute paths correctly"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "## Summary"
|
||||
echo ""
|
||||
echo "Executable scripts:"
|
||||
find "$MARKETPLACE_DIR" -type f -name "*.sh" -executable | wc -l
|
||||
echo ""
|
||||
echo "Non-executable scripts:"
|
||||
find "$MARKETPLACE_DIR" -type f -name "*.sh" ! -executable | wc -l
|
||||
echo ""
|
||||
99
skills/build-assistant/scripts/fix-tool-formatting.sh
Executable file
99
skills/build-assistant/scripts/fix-tool-formatting.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Fix tool formatting across all plugins
|
||||
# Converts ALL formats to horizontal comma-separated without quotes
|
||||
# Removes incorrect MCP wildcards and brackets
|
||||
|
||||
# Portable: Use argument or current directory (works from any location)
|
||||
MARKETPLACE_ROOT="${1:-$(pwd)}"
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔧 Fixing Tool Formatting"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
fix_all_tool_formats() {
|
||||
local file="$1"
|
||||
|
||||
python3 - "$file" << 'PYTHON'
|
||||
import sys
|
||||
import re
|
||||
|
||||
file_path = sys.argv[1]
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
original = content
|
||||
|
||||
# Fix 1: Convert JSON array format to comma-separated
|
||||
# tools: ["Bash", "Read", "Write"] -> tools: Bash, Read, Write
|
||||
pattern1 = r'(tools|allowed-tools):\s*\[(.*?)\]'
|
||||
def fix_json_array(match):
|
||||
prefix = match.group(1)
|
||||
tools_str = match.group(2)
|
||||
# Extract tool names from quoted strings
|
||||
tools = re.findall(r'"([^"]+)"', tools_str)
|
||||
return f'{prefix}: {", ".join(tools)}'
|
||||
|
||||
content = re.sub(pattern1, fix_json_array, content, flags=re.DOTALL)
|
||||
|
||||
# Fix 2: Convert vertical lists to horizontal
|
||||
# tools:\n - Bash\n - Read -> tools: Bash, Read
|
||||
pattern2 = r'(tools|allowed-tools):\n((?: - [^\n]+\n)+)'
|
||||
def fix_vertical(match):
|
||||
prefix = match.group(1)
|
||||
tools_section = match.group(2)
|
||||
tools = re.findall(r' - ([^\n]+)', tools_section)
|
||||
return f'{prefix}: {", ".join(tools)}\n'
|
||||
|
||||
content = re.sub(pattern2, fix_vertical, content)
|
||||
|
||||
# Fix 3: Remove (*) from all tools EXCEPT Bash with restrictions
|
||||
# Task(*) -> Task, but keep Bash(git add:*)
|
||||
content = re.sub(r'\b(Task|Read|Write|Edit|Grep|Glob|WebFetch|AskUserQuestion|TodoWrite|SlashCommand)\(\*\)', r'\1', content)
|
||||
|
||||
# Fix 4: Remove wildcards from MCP tools
|
||||
# mcp__server__* -> mcp__server
|
||||
# Task(mcp__*) -> mcp__servername (placeholder)
|
||||
content = re.sub(r'mcp__([^_,)\s]+)__\*', r'mcp__\1', content)
|
||||
content = re.sub(r'Task\(mcp__\*\)', r'mcp__servername', content)
|
||||
|
||||
# Fix 5: Remove brackets from mcp tools (except those with specific tool names)
|
||||
# mcp__server(*) -> mcp__server
|
||||
content = re.sub(r'(mcp__[a-z0-9_]+)\(\*\)', r'\1', content)
|
||||
|
||||
if content != original:
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(content)
|
||||
print(f"✅ Fixed: {file_path}")
|
||||
return True
|
||||
return False
|
||||
PYTHON
|
||||
}
|
||||
|
||||
# Process all agent files
|
||||
echo ""
|
||||
echo "📋 Fixing Agent Files..."
|
||||
find "$MARKETPLACE_ROOT/plugins" -name "*.md" -path "*/agents/*" | while read -r file; do
|
||||
fix_all_tool_formats "$file"
|
||||
done
|
||||
|
||||
# Process all command files
|
||||
echo ""
|
||||
echo "📋 Fixing Command Files..."
|
||||
find "$MARKETPLACE_ROOT/plugins" -name "*.md" -path "*/commands/*" | while read -r file; do
|
||||
fix_all_tool_formats "$file"
|
||||
done
|
||||
|
||||
# Process template files
|
||||
echo ""
|
||||
echo "📋 Fixing Template Files..."
|
||||
find "$MARKETPLACE_ROOT/plugins/domain-plugin-builder/skills/build-assistant/templates" -name "*.md" 2>/dev/null | while read -r file; do
|
||||
fix_all_tool_formats "$file"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ Tool Formatting Fixed!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
59
skills/build-assistant/scripts/install-plugin-locally.sh
Executable file
59
skills/build-assistant/scripts/install-plugin-locally.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: install-plugin-locally.sh
|
||||
# Purpose: Install plugin to local Claude marketplace for testing
|
||||
# Usage: ./install-plugin-locally.sh <plugin-directory>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PLUGIN_DIR="${1:?Usage: $0 <plugin-directory>}"
|
||||
MARKETPLACE_DIR="$HOME/.claude/plugins/marketplaces/ai-dev-marketplace"
|
||||
|
||||
echo "[INFO] Installing plugin to local marketplace"
|
||||
|
||||
# Check plugin directory exists
|
||||
if [[ ! -d "$PLUGIN_DIR" ]]; then
|
||||
echo "❌ ERROR: Plugin directory not found: $PLUGIN_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get plugin name
|
||||
PLUGIN_NAME=$(basename "$PLUGIN_DIR")
|
||||
|
||||
# Check if marketplace exists
|
||||
if [[ ! -d "$MARKETPLACE_DIR" ]]; then
|
||||
echo "❌ ERROR: Marketplace not found at $MARKETPLACE_DIR"
|
||||
echo "[INFO] Run: /plugin marketplace add ai-dev-marketplace"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if we're in the development directory
|
||||
DEV_MARKETPLACE="/home/vanman2025/Projects/ai-dev-marketplace"
|
||||
CURRENT_DIR=$(pwd)
|
||||
if [[ "$CURRENT_DIR" == "$DEV_MARKETPLACE" ]]; then
|
||||
echo "[INFO] Running from development directory"
|
||||
else
|
||||
echo "⚠️ WARNING: Not in development directory"
|
||||
echo "[INFO] Expected: $DEV_MARKETPLACE"
|
||||
echo "[INFO] Current: $CURRENT_DIR"
|
||||
fi
|
||||
|
||||
# Copy plugin to marketplace
|
||||
echo "[INFO] Copying plugin to marketplace..."
|
||||
cp -r "$PLUGIN_DIR" "$MARKETPLACE_DIR/plugins/"
|
||||
|
||||
# Update marketplace.json
|
||||
echo "[INFO] Updating marketplace.json..."
|
||||
cp .claude-plugin/marketplace.json "$MARKETPLACE_DIR/.claude-plugin/marketplace.json"
|
||||
|
||||
echo "✅ Plugin installed to local marketplace"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📦 NEXT STEP: Install Plugin"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo " /plugin install $PLUGIN_NAME@ai-dev-marketplace"
|
||||
echo ""
|
||||
echo " Verify: /$PLUGIN_NAME:init (or any command from the plugin)"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
exit 0
|
||||
54
skills/build-assistant/scripts/list-agents.sh
Executable file
54
skills/build-assistant/scripts/list-agents.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
# List all available agents across plugins and global directory
|
||||
# Searches: plugins/*/agents/*.md and ~/.claude/agents/*.md
|
||||
|
||||
# Portable: Use current directory or argument (works from any location)
|
||||
MARKETPLACE_DIR="${1:-$(pwd)}"
|
||||
GLOBAL_AGENTS_DIR="$HOME/.claude/agents"
|
||||
|
||||
echo "=== Available Agents ==="
|
||||
echo ""
|
||||
|
||||
# Track total count
|
||||
TOTAL=0
|
||||
|
||||
# Function to extract agent info from frontmatter
|
||||
extract_agent_info() {
|
||||
local file="$1"
|
||||
local location="$2"
|
||||
|
||||
# Extract name and description from frontmatter
|
||||
local name=$(grep -m1 "^name:" "$file" | cut -d: -f2- | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
|
||||
local desc=$(grep -m1 "^description:" "$file" | cut -d: -f2- | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
|
||||
|
||||
if [ -n "$name" ]; then
|
||||
echo " - $name"
|
||||
[ -n "$desc" ] && echo " $desc"
|
||||
echo " Location: $location"
|
||||
echo ""
|
||||
((TOTAL++))
|
||||
fi
|
||||
}
|
||||
|
||||
# Find all plugin agents
|
||||
echo "Plugin Agents:"
|
||||
while IFS= read -r agent_file; do
|
||||
plugin_name=$(echo "$agent_file" | sed -E 's|.*/plugins/([^/]+)/agents/.*|\1|')
|
||||
extract_agent_info "$agent_file" "$plugin_name plugin"
|
||||
done < <(find "$MARKETPLACE_DIR/plugins" -type f -path "*/agents/*.md" 2>/dev/null | sort)
|
||||
|
||||
# Find global agents
|
||||
if [ -d "$GLOBAL_AGENTS_DIR" ]; then
|
||||
echo "Global Agents:"
|
||||
while IFS= read -r agent_file; do
|
||||
extract_agent_info "$agent_file" "global"
|
||||
done < <(find "$GLOBAL_AGENTS_DIR" -type f -name "*.md" 2>/dev/null | sort)
|
||||
fi
|
||||
|
||||
echo "---"
|
||||
echo "Total: $TOTAL agents available"
|
||||
echo ""
|
||||
echo "Built-in agents (always available):"
|
||||
echo " - general-purpose: Multi-step tasks and complex questions"
|
||||
echo " - Explore: Fast codebase exploration and search"
|
||||
74
skills/build-assistant/scripts/register-commands-in-settings.sh
Executable file
74
skills/build-assistant/scripts/register-commands-in-settings.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Register plugin commands in .claude/settings.local.json
|
||||
# Usage: ./register-commands-in-settings.sh <plugin-name>
|
||||
|
||||
set -e
|
||||
|
||||
PLUGIN_NAME=$1
|
||||
SETTINGS_FILE="$HOME/.claude/settings.local.json"
|
||||
|
||||
if [ -z "$PLUGIN_NAME" ]; then
|
||||
echo "Usage: $0 <plugin-name>"
|
||||
echo "Example: $0 elevenlabs"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "plugins/$PLUGIN_NAME" ]; then
|
||||
echo "ERROR: Plugin directory plugins/$PLUGIN_NAME does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$SETTINGS_FILE" ]; then
|
||||
echo "ERROR: Settings file $SETTINGS_FILE does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[INFO] Registering commands for plugin: $PLUGIN_NAME"
|
||||
|
||||
# Get list of commands
|
||||
COMMANDS=$(ls plugins/$PLUGIN_NAME/commands/*.md 2>/dev/null | sed 's|plugins/||; s|/commands/|:|; s|.md||' || true)
|
||||
|
||||
if [ -z "$COMMANDS" ]; then
|
||||
echo "[WARN] No commands found for plugin $PLUGIN_NAME"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[INFO] Found commands:"
|
||||
echo "$COMMANDS"
|
||||
|
||||
# Check if wildcard already registered
|
||||
if grep -q "\"SlashCommand(/$PLUGIN_NAME:\*)\"" "$SETTINGS_FILE"; then
|
||||
echo "[INFO] Commands already registered for $PLUGIN_NAME"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[INFO] Adding commands to $SETTINGS_FILE"
|
||||
|
||||
# Create temp file with commands to add
|
||||
TEMP_COMMANDS=$(mktemp)
|
||||
echo " \"SlashCommand(/$PLUGIN_NAME:*)\"," > "$TEMP_COMMANDS"
|
||||
while IFS= read -r cmd; do
|
||||
echo " \"SlashCommand(/$cmd)\"," >> "$TEMP_COMMANDS"
|
||||
done <<< "$COMMANDS"
|
||||
|
||||
# Find the line before "Bash" and insert commands there
|
||||
LINE_NUM=$(grep -n '"Bash"' "$SETTINGS_FILE" | head -1 | cut -d: -f1)
|
||||
|
||||
if [ -z "$LINE_NUM" ]; then
|
||||
echo "ERROR: Could not find 'Bash' entry in $SETTINGS_FILE"
|
||||
rm "$TEMP_COMMANDS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Insert before Bash line
|
||||
head -n $((LINE_NUM - 1)) "$SETTINGS_FILE" > "${SETTINGS_FILE}.tmp"
|
||||
cat "$TEMP_COMMANDS" >> "${SETTINGS_FILE}.tmp"
|
||||
tail -n +$LINE_NUM "$SETTINGS_FILE" >> "${SETTINGS_FILE}.tmp"
|
||||
|
||||
# Replace original file
|
||||
mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE"
|
||||
rm "$TEMP_COMMANDS"
|
||||
|
||||
echo "[SUCCESS] Commands registered for $PLUGIN_NAME"
|
||||
echo "[INFO] Added $(echo "$COMMANDS" | wc -l) commands plus wildcard"
|
||||
95
skills/build-assistant/scripts/register-skills-in-settings.sh
Executable file
95
skills/build-assistant/scripts/register-skills-in-settings.sh
Executable file
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Register plugin skills in ~/.claude/settings.json
|
||||
# Usage: ./register-skills-in-settings.sh <plugin-name>
|
||||
|
||||
set -e
|
||||
|
||||
PLUGIN_NAME=$1
|
||||
SETTINGS_FILE="$HOME/.claude/settings.json"
|
||||
|
||||
if [ -z "$PLUGIN_NAME" ]; then
|
||||
echo "Usage: $0 <plugin-name>"
|
||||
echo "Example: $0 domain-plugin-builder"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "plugins/$PLUGIN_NAME" ]; then
|
||||
echo "ERROR: Plugin directory plugins/$PLUGIN_NAME does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$SETTINGS_FILE" ]; then
|
||||
echo "ERROR: Settings file $SETTINGS_FILE does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[INFO] Registering skills for plugin: $PLUGIN_NAME"
|
||||
|
||||
# Get list of skills (directories in skills/)
|
||||
SKILL_DIRS=$(find plugins/$PLUGIN_NAME/skills -maxdepth 1 -mindepth 1 -type d 2>/dev/null || true)
|
||||
|
||||
if [ -z "$SKILL_DIRS" ]; then
|
||||
echo "[WARN] No skills found for plugin $PLUGIN_NAME"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[INFO] Found skills:"
|
||||
for SKILL_DIR in $SKILL_DIRS; do
|
||||
SKILL_NAME=$(basename "$SKILL_DIR")
|
||||
echo " - $SKILL_NAME"
|
||||
done
|
||||
|
||||
# Check each skill and add if not already registered
|
||||
for SKILL_DIR in $SKILL_DIRS; do
|
||||
SKILL_NAME=$(basename "$SKILL_DIR")
|
||||
SKILL_ENTRY="Skill($PLUGIN_NAME:$SKILL_NAME)"
|
||||
|
||||
# Check if this skill is already registered
|
||||
if grep -q "\"$SKILL_ENTRY\"" "$SETTINGS_FILE"; then
|
||||
echo "[INFO] Skill already registered: $SKILL_NAME"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "[INFO] Adding skill to settings.json: $SKILL_NAME"
|
||||
|
||||
# Find the last Skill() entry and add after it
|
||||
# Use Python for JSON manipulation to ensure valid JSON
|
||||
python3 << EOF
|
||||
import json
|
||||
|
||||
with open('$SETTINGS_FILE', 'r') as f:
|
||||
settings = json.load(f)
|
||||
|
||||
# Add skill to permissions.allow
|
||||
if 'permissions' not in settings:
|
||||
settings['permissions'] = {}
|
||||
if 'allow' not in settings['permissions']:
|
||||
settings['permissions']['allow'] = []
|
||||
|
||||
skill_entry = '$SKILL_ENTRY'
|
||||
if skill_entry not in settings['permissions']['allow']:
|
||||
# Find position after last Skill() entry
|
||||
last_skill_index = -1
|
||||
for i, entry in enumerate(settings['permissions']['allow']):
|
||||
if isinstance(entry, str) and entry.startswith('Skill('):
|
||||
last_skill_index = i
|
||||
|
||||
if last_skill_index >= 0:
|
||||
# Insert after last skill
|
||||
settings['permissions']['allow'].insert(last_skill_index + 1, skill_entry)
|
||||
else:
|
||||
# No skills yet, add at end
|
||||
settings['permissions']['allow'].append(skill_entry)
|
||||
|
||||
with open('$SETTINGS_FILE', 'w') as f:
|
||||
json.dump(settings, f, indent=2)
|
||||
|
||||
print(f"[SUCCESS] Added {skill_entry} to settings.json")
|
||||
else:
|
||||
print(f"[INFO] Skill already registered: {skill_entry}")
|
||||
EOF
|
||||
|
||||
done
|
||||
|
||||
echo "[SUCCESS] Skill registration complete for plugin: $PLUGIN_NAME"
|
||||
99
skills/build-assistant/scripts/sync-marketplace.sh
Executable file
99
skills/build-assistant/scripts/sync-marketplace.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: sync-marketplace.sh
|
||||
# Purpose: Sync all plugins to marketplace.json registry
|
||||
# Usage: ./sync-marketplace.sh
|
||||
# This ensures marketplace.json is up-to-date with all plugins
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Find script location and navigate to marketplace root
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
MARKETPLACE_ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)"
|
||||
|
||||
cd "$MARKETPLACE_ROOT"
|
||||
|
||||
MARKETPLACE_FILE=".claude-plugin/marketplace.json"
|
||||
BACKUP_FILE=".claude-plugin/marketplace.json.backup"
|
||||
|
||||
echo "[INFO] Syncing plugins to marketplace.json in: $MARKETPLACE_ROOT"
|
||||
|
||||
# Find all plugins with plugin.json
|
||||
PLUGINS=()
|
||||
PLUGIN_JSON_FILES=$(find plugins -path "*/.claude-plugin/plugin.json" -type f | sort)
|
||||
|
||||
for plugin_json in $PLUGIN_JSON_FILES; do
|
||||
PLUGIN_DIR=$(dirname "$(dirname "$plugin_json")")
|
||||
PLUGIN_NAME=$(basename "$PLUGIN_DIR")
|
||||
|
||||
# Read plugin.json data
|
||||
DESCRIPTION=$(python3 -c "import json; print(json.load(open('$plugin_json'))['description'])" 2>/dev/null || echo "No description")
|
||||
VERSION=$(python3 -c "import json; print(json.load(open('$plugin_json'))['version'])" 2>/dev/null || echo "1.0.0")
|
||||
|
||||
# Extract author if exists
|
||||
AUTHOR_NAME=$(python3 -c "import json; d=json.load(open('$plugin_json')); print(d.get('author', {}).get('name', 'vanman2024'))" 2>/dev/null || echo "vanman2024")
|
||||
AUTHOR_EMAIL=$(python3 -c "import json; d=json.load(open('$plugin_json')); print(d.get('author', {}).get('email', 'noreply@ai-dev-marketplace.dev'))" 2>/dev/null || echo "noreply@ai-dev-marketplace.dev")
|
||||
|
||||
# Extract keywords if exists
|
||||
KEYWORDS=$(python3 -c "import json; d=json.load(open('$plugin_json')); print(','.join(['\"' + k + '\"' for k in d.get('keywords', [])]))" 2>/dev/null || echo "")
|
||||
|
||||
# Build plugin entry
|
||||
PLUGIN_ENTRY=$(cat <<EOF
|
||||
{
|
||||
"name": "$PLUGIN_NAME",
|
||||
"description": "$DESCRIPTION",
|
||||
"version": "$VERSION",
|
||||
"author": {
|
||||
"name": "$AUTHOR_NAME",
|
||||
"email": "$AUTHOR_EMAIL"
|
||||
},
|
||||
"source": "./plugins/$PLUGIN_NAME",
|
||||
"category": "development",
|
||||
"keywords": [$KEYWORDS]
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
PLUGINS+=("$PLUGIN_ENTRY")
|
||||
done
|
||||
|
||||
# Build marketplace.json
|
||||
PLUGIN_COUNT=${#PLUGINS[@]}
|
||||
PLUGINS_JSON=""
|
||||
|
||||
for i in "${!PLUGINS[@]}"; do
|
||||
PLUGINS_JSON+="${PLUGINS[$i]}"
|
||||
if [[ $i -lt $((PLUGIN_COUNT - 1)) ]]; then
|
||||
PLUGINS_JSON+=","
|
||||
fi
|
||||
done
|
||||
|
||||
# Write marketplace.json
|
||||
cat > "$MARKETPLACE_FILE" <<EOF
|
||||
{
|
||||
"name": "ai-dev-marketplace",
|
||||
"version": "1.0.0",
|
||||
"description": "AI Development Marketplace - Master repository for tech-specific plugins (SDKs, frameworks, platforms)",
|
||||
"owner": {
|
||||
"name": "AI Development Team",
|
||||
"email": "noreply@ai-dev-marketplace.dev"
|
||||
},
|
||||
"plugins": [
|
||||
$PLUGINS_JSON
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
# Format JSON
|
||||
python3 -m json.tool "$MARKETPLACE_FILE" > "${MARKETPLACE_FILE}.tmp" && mv "${MARKETPLACE_FILE}.tmp" "$MARKETPLACE_FILE"
|
||||
|
||||
echo "✅ Updated marketplace.json with $PLUGIN_COUNT plugins"
|
||||
|
||||
# Show summary
|
||||
echo ""
|
||||
echo "Registered plugins in marketplace:"
|
||||
for plugin_json in $PLUGIN_JSON_FILES; do
|
||||
PLUGIN_DIR=$(dirname "$(dirname "$plugin_json")")
|
||||
PLUGIN_NAME=$(basename "$PLUGIN_DIR")
|
||||
VERSION=$(python3 -c "import json; print(json.load(open('$plugin_json'))['version'])" 2>/dev/null || echo "1.0.0")
|
||||
echo " - $PLUGIN_NAME (v$VERSION)"
|
||||
done
|
||||
93
skills/build-assistant/scripts/sync-settings-permissions.sh
Executable file
93
skills/build-assistant/scripts/sync-settings-permissions.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: sync-settings-permissions.sh
|
||||
# Purpose: Automatically sync all plugin commands to .claude/settings.local.json
|
||||
# Usage: ./sync-settings-permissions.sh
|
||||
# This ensures all commands are registered and can be invoked
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Find script location and navigate to marketplace root
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
MARKETPLACE_ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)"
|
||||
|
||||
cd "$MARKETPLACE_ROOT"
|
||||
|
||||
SETTINGS_FILE="$HOME/.claude/settings.local.json"
|
||||
BACKUP_FILE="$HOME/.claude/settings.local.json.backup"
|
||||
|
||||
echo "[INFO] Syncing plugin commands to settings.local.json from: $MARKETPLACE_ROOT"
|
||||
|
||||
# Find all plugins
|
||||
PLUGINS=$(find plugins -mindepth 1 -maxdepth 1 -type d | sort)
|
||||
|
||||
# Build command list
|
||||
COMMANDS=()
|
||||
|
||||
for plugin in $PLUGINS; do
|
||||
PLUGIN_NAME=$(basename "$plugin")
|
||||
|
||||
# Check if plugin has commands
|
||||
if [[ -d "$plugin/commands" ]]; then
|
||||
# Add wildcard permission for the plugin
|
||||
COMMANDS+=(" \"SlashCommand(/$PLUGIN_NAME:*)\"")
|
||||
|
||||
# Find all command files
|
||||
COMMAND_FILES=$(find "$plugin/commands" -name "*.md" -type f | sort)
|
||||
|
||||
for cmd_file in $COMMAND_FILES; do
|
||||
CMD_NAME=$(basename "$cmd_file" .md)
|
||||
COMMANDS+=(" \"SlashCommand(/$PLUGIN_NAME:$CMD_NAME)\"")
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
# Add base tools
|
||||
BASE_TOOLS=(
|
||||
"Bash"
|
||||
"Write"
|
||||
"Read"
|
||||
"Edit"
|
||||
"WebFetch"
|
||||
"WebSearch"
|
||||
"AskUserQuestion"
|
||||
"Glob"
|
||||
"Grep"
|
||||
"Task"
|
||||
"Skill"
|
||||
)
|
||||
|
||||
for tool in "${BASE_TOOLS[@]}"; do
|
||||
COMMANDS+=(" \"$tool\"")
|
||||
done
|
||||
|
||||
# Build JSON
|
||||
cat > "$SETTINGS_FILE" <<EOF
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
$(IFS=$',\n'; echo "${COMMANDS[*]}")
|
||||
]
|
||||
},
|
||||
"enableAllProjectMcpServers": true,
|
||||
"enabledMcpjsonServers": [
|
||||
"filesystem",
|
||||
"playwright",
|
||||
"context7",
|
||||
"postman"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "✅ Updated settings.local.json with $(echo "${COMMANDS[@]}" | wc -w) permissions"
|
||||
echo "[INFO] All plugin commands are now registered"
|
||||
|
||||
# Show summary
|
||||
echo ""
|
||||
echo "Registered plugins:"
|
||||
for plugin in $PLUGINS; do
|
||||
PLUGIN_NAME=$(basename "$plugin")
|
||||
CMD_COUNT=$(find "$plugin/commands" -name "*.md" -type f 2>/dev/null | wc -l || echo "0")
|
||||
if [[ $CMD_COUNT -gt 0 ]]; then
|
||||
echo " - $PLUGIN_NAME ($CMD_COUNT commands)"
|
||||
fi
|
||||
done
|
||||
78
skills/build-assistant/scripts/sync-to-local-marketplace.sh
Executable file
78
skills/build-assistant/scripts/sync-to-local-marketplace.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: sync-to-local-marketplace.sh
|
||||
# Purpose: Automatically sync development directory to installed marketplace
|
||||
# Usage: ./sync-to-local-marketplace.sh [plugin-name]
|
||||
# Called by: git pre-commit hook (automatic) or manually
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DEV_DIR="/home/vanman2025/Projects/ai-dev-marketplace"
|
||||
MARKETPLACE_DIR="$HOME/.claude/plugins/marketplaces/ai-dev-marketplace"
|
||||
|
||||
# Check if marketplace exists
|
||||
if [[ ! -d "$MARKETPLACE_DIR" ]]; then
|
||||
echo "[INFO] Marketplace not installed at $MARKETPLACE_DIR"
|
||||
echo "[INFO] Run: /plugin marketplace add vanman2024/ai-dev-marketplace"
|
||||
exit 0 # Not an error - just not installed
|
||||
fi
|
||||
|
||||
# Check if marketplace is a git repo
|
||||
if [[ ! -d "$MARKETPLACE_DIR/.git" ]]; then
|
||||
echo "⚠️ WARNING: Marketplace is not a git repository"
|
||||
echo "[INFO] Expected git clone, found regular directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$DEV_DIR"
|
||||
|
||||
# If plugin name provided, sync only that plugin
|
||||
if [[ -n "${1:-}" ]]; then
|
||||
PLUGIN_NAME="$1"
|
||||
echo "[INFO] Syncing plugin: $PLUGIN_NAME"
|
||||
|
||||
if [[ ! -d "plugins/$PLUGIN_NAME" ]]; then
|
||||
echo "❌ ERROR: Plugin not found: plugins/$PLUGIN_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy plugin directory
|
||||
rsync -av --delete "plugins/$PLUGIN_NAME/" "$MARKETPLACE_DIR/plugins/$PLUGIN_NAME/"
|
||||
echo "✅ Synced plugin: $PLUGIN_NAME"
|
||||
else
|
||||
# Sync entire repository
|
||||
echo "[INFO] Syncing entire marketplace..."
|
||||
|
||||
# Sync all plugins
|
||||
rsync -av --delete plugins/ "$MARKETPLACE_DIR/plugins/"
|
||||
|
||||
# Sync marketplace.json
|
||||
rsync -av .claude-plugin/marketplace.json "$MARKETPLACE_DIR/.claude-plugin/marketplace.json"
|
||||
|
||||
# Sync other important files
|
||||
rsync -av README.md "$MARKETPLACE_DIR/README.md"
|
||||
|
||||
echo "✅ Synced all plugins and marketplace metadata"
|
||||
fi
|
||||
|
||||
# Show what was synced
|
||||
cd "$MARKETPLACE_DIR"
|
||||
git status --short || true
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📦 LOCAL MARKETPLACE SYNCED"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Changes are immediately available to Claude Code!"
|
||||
echo ""
|
||||
echo "To sync these changes to GitHub:"
|
||||
echo " 1. cd $DEV_DIR"
|
||||
echo " 2. git add -A && git commit -m 'feat: ...'"
|
||||
echo " 3. git push origin master"
|
||||
echo ""
|
||||
echo "To pull GitHub changes to local marketplace:"
|
||||
echo " /plugin marketplace update ai-dev-marketplace"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
exit 0
|
||||
163
skills/build-assistant/scripts/test-build-system.sh
Executable file
163
skills/build-assistant/scripts/test-build-system.sh
Executable file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: test-build-system.sh
|
||||
# Purpose: Automated testing for domain-plugin-builder infrastructure
|
||||
# Usage: ./test-build-system.sh [--quick|--full]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PLUGIN_DIR="$HOME/.claude/plugins/marketplaces/domain-plugin-builder/plugins/domain-plugin-builder"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
PASSED=0
|
||||
FAILED=0
|
||||
SKIPPED=0
|
||||
|
||||
echo "================================================"
|
||||
echo "Domain Plugin Builder Testing Suite"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
echo "Testing: $PLUGIN_DIR"
|
||||
echo ""
|
||||
|
||||
# Test functions
|
||||
test_file_exists() {
|
||||
local file=$1
|
||||
if [ -f "$file" ]; then
|
||||
echo -e "${GREEN}✓${NC} $file"
|
||||
PASSED=$((PASSED + 1))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} $file (missing)"
|
||||
FAILED=$((FAILED + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_dir_exists() {
|
||||
local dir=$1
|
||||
if [ -d "$dir" ]; then
|
||||
echo -e "${GREEN}✓${NC} $dir/"
|
||||
PASSED=$((PASSED + 1))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} $dir/ (missing)"
|
||||
FAILED=$((FAILED + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_executable() {
|
||||
local file=$1
|
||||
if [ -x "$file" ]; then
|
||||
echo -e "${GREEN}✓${NC} $file (executable)"
|
||||
PASSED=$((PASSED + 1))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} $file (not executable)"
|
||||
FAILED=$((FAILED + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 1: Core Directory Structure
|
||||
echo "[1/8] Testing Core Directory Structure..."
|
||||
test_dir_exists "$PLUGIN_DIR/commands"
|
||||
test_dir_exists "$PLUGIN_DIR/agents"
|
||||
test_dir_exists "$PLUGIN_DIR/skills"
|
||||
test_dir_exists "$PLUGIN_DIR/docs"
|
||||
test_dir_exists "$PLUGIN_DIR/skills/build-assistant/scripts"
|
||||
test_dir_exists "$PLUGIN_DIR/skills/build-assistant/templates"
|
||||
echo ""
|
||||
|
||||
# Test 2: Commands
|
||||
echo "[2/8] Testing Commands..."
|
||||
test_file_exists "$PLUGIN_DIR/commands/plugin-create.md"
|
||||
test_file_exists "$PLUGIN_DIR/commands/build-plugin.md"
|
||||
test_file_exists "$PLUGIN_DIR/commands/agents-create.md"
|
||||
test_file_exists "$PLUGIN_DIR/commands/slash-commands-create.md"
|
||||
test_file_exists "$PLUGIN_DIR/commands/skills-create.md"
|
||||
test_file_exists "$PLUGIN_DIR/commands/hooks-create.md"
|
||||
test_file_exists "$PLUGIN_DIR/commands/validate.md"
|
||||
echo ""
|
||||
|
||||
# Test 3: Agents
|
||||
echo "[3/8] Testing Agents..."
|
||||
test_file_exists "$PLUGIN_DIR/agents/agents-builder.md"
|
||||
test_file_exists "$PLUGIN_DIR/agents/slash-commands-builder.md"
|
||||
test_file_exists "$PLUGIN_DIR/agents/skills-builder.md"
|
||||
test_file_exists "$PLUGIN_DIR/agents/hooks-builder.md"
|
||||
test_file_exists "$PLUGIN_DIR/agents/plugin-validator.md"
|
||||
echo ""
|
||||
|
||||
# Test 4: Skills
|
||||
echo "[4/8] Testing Skills..."
|
||||
test_dir_exists "$PLUGIN_DIR/skills/build-assistant"
|
||||
test_file_exists "$PLUGIN_DIR/skills/build-assistant/SKILL.md"
|
||||
test_file_exists "$PLUGIN_DIR/skills/build-assistant/reference.md"
|
||||
test_file_exists "$PLUGIN_DIR/skills/build-assistant/examples.md"
|
||||
echo ""
|
||||
|
||||
# Test 5: Templates
|
||||
echo "[5/8] Testing Templates..."
|
||||
test_dir_exists "$PLUGIN_DIR/skills/build-assistant/templates/agents"
|
||||
test_dir_exists "$PLUGIN_DIR/skills/build-assistant/templates/commands"
|
||||
test_dir_exists "$PLUGIN_DIR/skills/build-assistant/templates/skills"
|
||||
test_file_exists "$PLUGIN_DIR/skills/build-assistant/templates/agents/agent-with-phased-webfetch.md"
|
||||
test_file_exists "$PLUGIN_DIR/skills/build-assistant/templates/commands/template-command-patterns.md"
|
||||
test_file_exists "$PLUGIN_DIR/skills/build-assistant/templates/skills/SKILL.md.template"
|
||||
echo ""
|
||||
|
||||
# Test 6: Documentation
|
||||
echo "[6/8] Testing Documentation..."
|
||||
test_file_exists "$PLUGIN_DIR/README.md"
|
||||
test_file_exists "$PLUGIN_DIR/CLAUDE.md"
|
||||
test_dir_exists "$PLUGIN_DIR/docs/frameworks/claude"
|
||||
test_dir_exists "$PLUGIN_DIR/docs/frameworks/claude/agents"
|
||||
test_dir_exists "$PLUGIN_DIR/docs/frameworks/claude/plugins"
|
||||
test_dir_exists "$PLUGIN_DIR/docs/frameworks/claude/reference"
|
||||
test_file_exists "$PLUGIN_DIR/docs/frameworks/claude/agents/agent-color-decision.md"
|
||||
test_file_exists "$PLUGIN_DIR/docs/frameworks/claude/agents/agent-color-standard.md"
|
||||
test_file_exists "$PLUGIN_DIR/docs/frameworks/claude/reference/component-decision-framework.md"
|
||||
test_file_exists "$PLUGIN_DIR/docs/frameworks/claude/reference/dans-composition-pattern.md"
|
||||
echo ""
|
||||
|
||||
# Test 7: Validation Scripts
|
||||
echo "[7/8] Testing Validation Scripts..."
|
||||
test_executable "$PLUGIN_DIR/skills/build-assistant/scripts/validate-agent.sh"
|
||||
test_executable "$PLUGIN_DIR/skills/build-assistant/scripts/validate-command.sh"
|
||||
test_executable "$PLUGIN_DIR/skills/build-assistant/scripts/validate-skill.sh"
|
||||
test_executable "$PLUGIN_DIR/skills/build-assistant/scripts/validate-plugin.sh"
|
||||
test_executable "$PLUGIN_DIR/skills/build-assistant/scripts/validate-all.sh"
|
||||
test_executable "$PLUGIN_DIR/skills/build-assistant/scripts/sync-marketplace.sh"
|
||||
test_executable "$PLUGIN_DIR/skills/build-assistant/scripts/register-commands-in-settings.sh"
|
||||
test_executable "$PLUGIN_DIR/skills/build-assistant/scripts/register-skills-in-settings.sh"
|
||||
echo ""
|
||||
|
||||
# Test 8: Configuration Files
|
||||
echo "[8/8] Testing Configuration Files..."
|
||||
test_file_exists "$PLUGIN_DIR/.claude-plugin/plugin.json"
|
||||
test_file_exists "$HOME/.claude/plugins/marketplaces/domain-plugin-builder/docs/security/SECURITY-RULES.md"
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "================================================"
|
||||
echo "Test Summary"
|
||||
echo "================================================"
|
||||
echo -e "${GREEN}Passed:${NC} $PASSED"
|
||||
echo -e "${RED}Failed:${NC} $FAILED"
|
||||
echo -e "${YELLOW}Skipped:${NC} $SKIPPED"
|
||||
echo ""
|
||||
|
||||
if [ $FAILED -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ All tests passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}✗ Some tests failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
88
skills/build-assistant/scripts/validate-agent-references.sh
Executable file
88
skills/build-assistant/scripts/validate-agent-references.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
# Validate that all agent references in commands exist
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PLUGIN_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 <plugin-path>"
|
||||
echo "Example: $0 plugins/domain-plugin-builder"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PLUGIN_PATH="$1"
|
||||
|
||||
if [ ! -d "$PLUGIN_PATH" ]; then
|
||||
echo "❌ Error: Plugin directory not found: $PLUGIN_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔍 Validating agent references in: $PLUGIN_PATH"
|
||||
echo ""
|
||||
|
||||
# Extract all agent references from commands
|
||||
AGENT_REFS=$(grep -rh 'subagent_type="[^"]*"' "$PLUGIN_PATH/commands/" 2>/dev/null | \
|
||||
grep -o 'subagent_type="[^"]*"' | \
|
||||
sed 's/subagent_type="//; s/"$//' | \
|
||||
sed 's/.*://' | \
|
||||
sort -u)
|
||||
|
||||
if [ -z "$AGENT_REFS" ]; then
|
||||
echo "✅ No agent references found in commands"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TOTAL=0
|
||||
VALID=0
|
||||
INVALID=0
|
||||
MISSING_AGENTS=()
|
||||
|
||||
echo "📋 Checking agent references..."
|
||||
echo ""
|
||||
|
||||
for agent in $AGENT_REFS; do
|
||||
TOTAL=$((TOTAL + 1))
|
||||
AGENT_FILE="$PLUGIN_PATH/agents/${agent}.md"
|
||||
|
||||
if [ -f "$AGENT_FILE" ]; then
|
||||
echo " ✅ $agent"
|
||||
VALID=$((VALID + 1))
|
||||
else
|
||||
echo " ❌ $agent (MISSING: $AGENT_FILE)"
|
||||
INVALID=$((INVALID + 1))
|
||||
MISSING_AGENTS+=("$agent")
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📊 Summary:"
|
||||
echo " Total agent references: $TOTAL"
|
||||
echo " Valid: $VALID"
|
||||
echo " Invalid: $INVALID"
|
||||
echo ""
|
||||
|
||||
if [ $INVALID -gt 0 ]; then
|
||||
echo "❌ VALIDATION FAILED"
|
||||
echo ""
|
||||
echo "Commands reference non-existent agents:"
|
||||
for agent in "${MISSING_AGENTS[@]}"; do
|
||||
echo " - $agent (referenced in commands but agents/$agent.md doesn't exist)"
|
||||
done
|
||||
echo ""
|
||||
echo "🔧 FIX THIS BY:"
|
||||
echo " 1. Check which commands reference these agents:"
|
||||
echo " grep -r 'subagent_type=\".*:$agent\"' $PLUGIN_PATH/commands/"
|
||||
echo ""
|
||||
echo " 2. Update commands to use ACTUAL agent names from agents/ directory:"
|
||||
echo " ls $PLUGIN_PATH/agents/"
|
||||
echo ""
|
||||
echo " 3. Fix the subagent_type in commands to match real agent filenames"
|
||||
echo ""
|
||||
echo "❌ DO NOT create new agents to match wrong command references!"
|
||||
echo "✅ FIX the commands to use correct existing agent names!"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ ALL AGENT REFERENCES VALID"
|
||||
exit 0
|
||||
fi
|
||||
68
skills/build-assistant/scripts/validate-agent.sh
Executable file
68
skills/build-assistant/scripts/validate-agent.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: validate-agent.sh
|
||||
# Purpose: Validate agent file compliance with framework standards
|
||||
# Subsystem: build-system
|
||||
# Called by: /build:agent command after generation
|
||||
# Outputs: Validation report to stdout
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
AGENT_FILE="${1:?Usage: $0 <agent-file>}"
|
||||
|
||||
echo "[INFO] Validating agent file: $AGENT_FILE"
|
||||
|
||||
# Check file exists
|
||||
if [[ ! -f "$AGENT_FILE" ]]; then
|
||||
echo "❌ ERROR: File not found: $AGENT_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check frontmatter exists
|
||||
if ! grep -q "^---$" "$AGENT_FILE"; then
|
||||
echo "❌ ERROR: Missing frontmatter"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check required frontmatter fields
|
||||
REQUIRED_FIELDS=("name:" "description:" "model:")
|
||||
for field in "${REQUIRED_FIELDS[@]}"; do
|
||||
if ! grep -q "^$field" "$AGENT_FILE"; then
|
||||
echo "❌ ERROR: Missing required field: $field"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Warn if tools field is present (agents should inherit tools)
|
||||
if grep -q "^tools:" "$AGENT_FILE"; then
|
||||
echo "⚠️ WARNING: tools field found - agents should inherit tools from parent, not specify them"
|
||||
fi
|
||||
|
||||
# Check for incorrect MCP server naming (common mistake: mcp__supabase instead of mcp__plugin_supabase_supabase)
|
||||
INVALID_MCP_NAMES=("mcp__supabase" "mcp__shadcn" "mcp__nextjs" "mcp__vercel-ai")
|
||||
for invalid_name in "${INVALID_MCP_NAMES[@]}"; do
|
||||
if grep -q "\`$invalid_name\`" "$AGENT_FILE" 2>/dev/null; then
|
||||
echo "❌ ERROR: Found $invalid_name - plugin-specific MCP servers must use full name:"
|
||||
echo " - Use: mcp__plugin_supabase_supabase (not mcp__supabase)"
|
||||
echo " - Use: mcp__plugin_*_shadcn (not mcp__shadcn)"
|
||||
echo " Generic MCP servers are fine: mcp__github, mcp__filesystem, mcp__docker, mcp__fetch, etc."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check Step 0 exists (optional - only for validator agents)
|
||||
if ! grep -q "### Step 0: Load Required Context" "$AGENT_FILE"; then
|
||||
echo "⚠️ WARNING: Missing Step 0: Load Required Context section (only required for validator agents)"
|
||||
fi
|
||||
|
||||
# Check for @ symbol references
|
||||
if ! grep -q 'Read("' "$AGENT_FILE"; then
|
||||
echo "⚠️ WARNING: No Read() calls found - agent may not load context"
|
||||
fi
|
||||
|
||||
# Check Success Criteria exists (optional - only for validator agents)
|
||||
if ! grep -q "## Success Criteria" "$AGENT_FILE"; then
|
||||
echo "⚠️ WARNING: Missing Success Criteria section (only required for validator agents)"
|
||||
fi
|
||||
|
||||
echo "✅ Agent validation passed"
|
||||
exit 0
|
||||
157
skills/build-assistant/scripts/validate-all.sh
Executable file
157
skills/build-assistant/scripts/validate-all.sh
Executable file
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env bash
|
||||
# validate-all.sh - Master validation script for entire plugin
|
||||
# Usage: validate-all.sh <plugin-directory>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PLUGIN_DIR="${1:?Plugin directory required}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Counters
|
||||
TOTAL_COMMANDS=0
|
||||
PASSED_COMMANDS=0
|
||||
TOTAL_AGENTS=0
|
||||
PASSED_AGENTS=0
|
||||
TOTAL_SKILLS=0
|
||||
PASSED_SKILLS=0
|
||||
|
||||
echo "========================================="
|
||||
echo " Plugin Validation: $(basename "$PLUGIN_DIR")"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# Validate plugin structure
|
||||
echo "[1/4] Validating plugin structure..."
|
||||
if bash "$SCRIPT_DIR/validate-plugin.sh" "$PLUGIN_DIR"; then
|
||||
echo -e "${GREEN}✅ Plugin structure valid${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ Plugin structure invalid${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Validate all commands
|
||||
echo "[2/4] Validating commands..."
|
||||
if [ -d "$PLUGIN_DIR/commands" ]; then
|
||||
for cmd in "$PLUGIN_DIR/commands"/*.md; do
|
||||
if [ -f "$cmd" ]; then
|
||||
TOTAL_COMMANDS=$((TOTAL_COMMANDS + 1))
|
||||
CMD_NAME=$(basename "$cmd")
|
||||
|
||||
if bash "$SCRIPT_DIR/validate-command.sh" "$cmd" > /dev/null 2>&1; then
|
||||
PASSED_COMMANDS=$((PASSED_COMMANDS + 1))
|
||||
echo -e " ${GREEN}✅${NC} $CMD_NAME"
|
||||
else
|
||||
echo -e " ${RED}❌${NC} $CMD_NAME"
|
||||
# Show errors for failed commands
|
||||
bash "$SCRIPT_DIR/validate-command.sh" "$cmd" 2>&1 | grep -E "ERROR|WARNING" || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo -e "${YELLOW}⚠ No commands directory found${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Commands: $PASSED_COMMANDS/$TOTAL_COMMANDS passed"
|
||||
echo ""
|
||||
|
||||
# Validate all agents
|
||||
echo "[3/4] Validating agents..."
|
||||
if [ -d "$PLUGIN_DIR/agents" ]; then
|
||||
for agent in "$PLUGIN_DIR/agents"/*.md; do
|
||||
if [ -f "$agent" ]; then
|
||||
TOTAL_AGENTS=$((TOTAL_AGENTS + 1))
|
||||
AGENT_NAME=$(basename "$agent")
|
||||
|
||||
if bash "$SCRIPT_DIR/validate-agent.sh" "$agent" > /dev/null 2>&1; then
|
||||
PASSED_AGENTS=$((PASSED_AGENTS + 1))
|
||||
echo -e " ${GREEN}✅${NC} $AGENT_NAME"
|
||||
else
|
||||
echo -e " ${RED}❌${NC} $AGENT_NAME"
|
||||
# Show errors for failed agents
|
||||
bash "$SCRIPT_DIR/validate-agent.sh" "$agent" 2>&1 | grep -E "ERROR|WARNING" || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo -e "${YELLOW}⚠ No agents directory found${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Agents: $PASSED_AGENTS/$TOTAL_AGENTS passed"
|
||||
echo ""
|
||||
|
||||
# Validate all skills
|
||||
echo "[4/5] Validating skills..."
|
||||
if [ -d "$PLUGIN_DIR/skills" ]; then
|
||||
for skill_dir in "$PLUGIN_DIR/skills"/*/; do
|
||||
if [ -d "$skill_dir" ]; then
|
||||
TOTAL_SKILLS=$((TOTAL_SKILLS + 1))
|
||||
SKILL_NAME=$(basename "$skill_dir")
|
||||
|
||||
if bash "$SCRIPT_DIR/validate-skill.sh" "$skill_dir" > /dev/null 2>&1; then
|
||||
PASSED_SKILLS=$((PASSED_SKILLS + 1))
|
||||
echo -e " ${GREEN}✅${NC} $SKILL_NAME"
|
||||
else
|
||||
echo -e " ${RED}❌${NC} $SKILL_NAME"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $TOTAL_SKILLS -eq 0 ]; then
|
||||
echo -e "${YELLOW}⚠ Skills directory empty${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠ No skills directory found${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [ $TOTAL_SKILLS -gt 0 ]; then
|
||||
echo "Skills: $PASSED_SKILLS/$TOTAL_SKILLS passed"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Validate plugin completeness (templates, examples, scripts)
|
||||
echo "[5/5] Validating plugin completeness..."
|
||||
if bash "$SCRIPT_DIR/validate-plugin-completeness.sh" "$PLUGIN_DIR"; then
|
||||
echo -e "${GREEN}✅ Plugin completeness check passed${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ Plugin completeness check failed${NC}"
|
||||
echo "Some skills may be missing templates, examples, or scripts."
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "========================================="
|
||||
echo " Validation Summary"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "Commands: $PASSED_COMMANDS/$TOTAL_COMMANDS"
|
||||
echo "Agents: $PASSED_AGENTS/$TOTAL_AGENTS"
|
||||
if [ $TOTAL_SKILLS -gt 0 ]; then
|
||||
echo "Skills: $PASSED_SKILLS/$TOTAL_SKILLS"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Calculate total
|
||||
TOTAL=$((TOTAL_COMMANDS + TOTAL_AGENTS + TOTAL_SKILLS))
|
||||
PASSED=$((PASSED_COMMANDS + PASSED_AGENTS + PASSED_SKILLS))
|
||||
|
||||
if [ $PASSED -eq $TOTAL ]; then
|
||||
echo -e "${GREEN}✅ ALL VALIDATIONS PASSED ($PASSED/$TOTAL)${NC}"
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
FAILED=$((TOTAL - PASSED))
|
||||
echo -e "${RED}❌ VALIDATION FAILED: $FAILED failures out of $TOTAL total${NC}"
|
||||
echo ""
|
||||
echo "Fix the failed validations and run again."
|
||||
exit 1
|
||||
fi
|
||||
109
skills/build-assistant/scripts/validate-and-sync-all.sh
Executable file
109
skills/build-assistant/scripts/validate-and-sync-all.sh
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: validate-and-sync-all.sh
|
||||
# Purpose: Complete validation and synchronization of all plugins
|
||||
# Usage: ./validate-and-sync-all.sh
|
||||
# This is the MASTER script that ensures everything is in sync
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
echo "========================================="
|
||||
echo " Plugin System Validation & Sync"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# Step 1: Validate all individual plugins
|
||||
echo "[STEP 1/4] Validating individual plugins..."
|
||||
echo ""
|
||||
|
||||
PLUGINS=$(find plugins -mindepth 1 -maxdepth 1 -type d -name "*" ! -name ".*" | sort)
|
||||
PLUGIN_COUNT=0
|
||||
VALID_COUNT=0
|
||||
INVALID_COUNT=0
|
||||
|
||||
for plugin in $PLUGINS; do
|
||||
((PLUGIN_COUNT++))
|
||||
PLUGIN_NAME=$(basename "$plugin")
|
||||
|
||||
# Check if has .claude-plugin directory (skip if not a valid plugin)
|
||||
if [[ ! -d "$plugin/.claude-plugin" ]]; then
|
||||
echo " ⏭️ Skipping $PLUGIN_NAME (not a Claude Code plugin)"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo " Validating: $PLUGIN_NAME"
|
||||
if bash "$SCRIPT_DIR/validate-plugin.sh" "$plugin" 2>&1 | grep -q "✅"; then
|
||||
((VALID_COUNT++))
|
||||
else
|
||||
((INVALID_COUNT++))
|
||||
echo " ❌ Validation failed for $PLUGIN_NAME"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo " Plugins found: $PLUGIN_COUNT"
|
||||
echo " Valid: $VALID_COUNT"
|
||||
echo " Invalid: $INVALID_COUNT"
|
||||
echo ""
|
||||
|
||||
# Step 2: Sync all commands to settings.local.json
|
||||
echo "[STEP 2/4] Syncing commands to settings.local.json..."
|
||||
echo ""
|
||||
|
||||
bash "$SCRIPT_DIR/sync-settings-permissions.sh"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 3: Sync all plugins to marketplace.json
|
||||
echo "[STEP 3/4] Syncing plugins to marketplace.json..."
|
||||
echo ""
|
||||
|
||||
bash "$SCRIPT_DIR/sync-marketplace.sh"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 4: Final validation
|
||||
echo "[STEP 4/4] Final validation checks..."
|
||||
echo ""
|
||||
|
||||
# Check settings.local.json is valid JSON
|
||||
if python3 -m json.tool "$HOME/.claude/settings.local.json" > /dev/null 2>&1; then
|
||||
echo " ✅ settings.local.json is valid JSON"
|
||||
else
|
||||
echo " ❌ settings.local.json is INVALID JSON"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check marketplace.json is valid JSON
|
||||
if python3 -m json.tool .claude-plugin/marketplace.json > /dev/null 2>&1; then
|
||||
echo " ✅ marketplace.json is valid JSON"
|
||||
else
|
||||
echo " ❌ marketplace.json is INVALID JSON"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Count registrations
|
||||
SETTINGS_PERMISSIONS=$(grep -c "SlashCommand" "$HOME/.claude/settings.local.json" || echo "0")
|
||||
MARKETPLACE_PLUGINS=$(python3 -c "import json; print(len(json.load(open('.claude-plugin/marketplace.json'))['plugins']))" 2>/dev/null || echo "0")
|
||||
|
||||
echo " ✅ $SETTINGS_PERMISSIONS slash commands registered in settings"
|
||||
echo " ✅ $MARKETPLACE_PLUGINS plugins registered in marketplace"
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo " ✅ All validations passed!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "Summary:"
|
||||
echo " - Plugins validated: $VALID_COUNT/$PLUGIN_COUNT"
|
||||
echo " - Commands registered: $SETTINGS_PERMISSIONS"
|
||||
echo " - Marketplace entries: $MARKETPLACE_PLUGINS"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Test a command: /fastmcp:new-server my-test"
|
||||
echo " 2. Commit changes: git add .claude/ .claude-plugin/"
|
||||
echo " 3. Review docs: cat SETTINGS-SYNC-GUIDE.md"
|
||||
echo ""
|
||||
|
||||
exit 0
|
||||
105
skills/build-assistant/scripts/validate-argument-hints.sh
Executable file
105
skills/build-assistant/scripts/validate-argument-hints.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
|
||||
# validate-argument-hints.sh
|
||||
# Validates command argument-hint frontmatter for proper formatting
|
||||
|
||||
PLUGIN_DIR="${1:-.}"
|
||||
ISSUES_FOUND=0
|
||||
|
||||
echo "=== Validating Command Argument Hints ==="
|
||||
echo ""
|
||||
|
||||
# Check if directory exists
|
||||
if [ ! -d "$PLUGIN_DIR" ]; then
|
||||
echo "❌ ERROR: Directory not found: $PLUGIN_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find all command files
|
||||
COMMAND_FILES=$(find "$PLUGIN_DIR" -type f -path "*/commands/*.md" 2>/dev/null)
|
||||
|
||||
if [ -z "$COMMAND_FILES" ]; then
|
||||
echo "⚠️ No command files found in $PLUGIN_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Scanning command files..."
|
||||
echo ""
|
||||
|
||||
while IFS= read -r file; do
|
||||
# Extract argument-hint from frontmatter
|
||||
HINT=$(sed -n '/^---$/,/^---$/p' "$file" | grep "^argument-hint:" | sed 's/argument-hint: *//')
|
||||
|
||||
if [ -z "$HINT" ]; then
|
||||
echo "⚠️ MISSING argument-hint: $file"
|
||||
((ISSUES_FOUND++))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check for old "subsystem" terminology
|
||||
if echo "$HINT" | grep -qi "subsystem"; then
|
||||
echo "❌ LEGACY TERM 'subsystem': $file"
|
||||
echo " Current: $HINT"
|
||||
echo " Should use: <plugin-name>, <spec-directory>, or specific argument"
|
||||
echo ""
|
||||
((ISSUES_FOUND++))
|
||||
fi
|
||||
|
||||
# Check for proper formatting patterns
|
||||
# Valid patterns: <required>, [optional], <arg1> <arg2>, [--flag]
|
||||
|
||||
# Check if using proper brackets
|
||||
if ! echo "$HINT" | grep -qE '(<[^>]+>|\[.*\]|--[a-z-]+)'; then
|
||||
# If no brackets at all and not empty
|
||||
if [ "$HINT" != "none" ] && [ "$HINT" != "" ]; then
|
||||
echo "⚠️ IMPROPER FORMAT (missing brackets): $file"
|
||||
echo " Current: $HINT"
|
||||
echo " Should use: <required-arg> or [optional-arg] or [--flag]"
|
||||
echo ""
|
||||
((ISSUES_FOUND++))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for common bad patterns
|
||||
if echo "$HINT" | grep -qE '\$[0-9]|\$ARGUMENTS|multiagent_core'; then
|
||||
echo "❌ CONTAINS VARIABLES/LEGACY: $file"
|
||||
echo " Current: $HINT"
|
||||
echo " Should be: Plain text description, not variable references"
|
||||
echo ""
|
||||
((ISSUES_FOUND++))
|
||||
fi
|
||||
|
||||
# Check for overly generic hints
|
||||
if [ "$HINT" = "<args>" ] || [ "$HINT" = "[args]" ] || [ "$HINT" = "args" ]; then
|
||||
echo "⚠️ TOO GENERIC: $file"
|
||||
echo " Current: $HINT"
|
||||
echo " Should be: Specific argument names (e.g., <spec-directory> [--type=TYPE])"
|
||||
echo ""
|
||||
((ISSUES_FOUND++))
|
||||
fi
|
||||
|
||||
done <<< "$COMMAND_FILES"
|
||||
|
||||
echo ""
|
||||
echo "=== Summary ==="
|
||||
if [ $ISSUES_FOUND -eq 0 ]; then
|
||||
echo "✅ All argument hints are properly formatted"
|
||||
exit 0
|
||||
else
|
||||
echo "❌ Found $ISSUES_FOUND issue(s) with argument hints"
|
||||
echo ""
|
||||
echo "Valid formats:"
|
||||
echo " <required-arg> - Required argument"
|
||||
echo " [optional-arg] - Optional argument"
|
||||
echo " <arg1> <arg2> - Multiple arguments"
|
||||
echo " [--flag] - Optional flag"
|
||||
echo " <spec> [--type=TYPE] - Mixed required/optional"
|
||||
echo " none - No arguments"
|
||||
echo ""
|
||||
echo "Bad patterns:"
|
||||
echo " ❌ subsystem - Use 'plugin-name' or 'spec-directory'"
|
||||
echo " ❌ \$1, \$ARGUMENTS - No variable references"
|
||||
echo " ❌ args - Too generic, be specific"
|
||||
echo " ❌ No brackets - Must use <> or [] or --"
|
||||
exit 1
|
||||
fi
|
||||
148
skills/build-assistant/scripts/validate-command.sh
Executable file
148
skills/build-assistant/scripts/validate-command.sh
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: validate-command.sh
|
||||
# Purpose: Validate slash command file compliance with framework standards
|
||||
# Subsystem: build-system
|
||||
# Called by: framework-slash-command after generation
|
||||
# Outputs: Validation report to stdout
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
COMMAND_FILE="${1:?Usage: $0 <command-file>}"
|
||||
EXIT_CODE=0
|
||||
|
||||
echo "[INFO] Validating command file: $COMMAND_FILE"
|
||||
echo ""
|
||||
|
||||
# Check file exists
|
||||
if [[ ! -f "$COMMAND_FILE" ]]; then
|
||||
echo "❌ ERROR: File not found: $COMMAND_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check frontmatter exists
|
||||
if ! grep -q "^---$" "$COMMAND_FILE"; then
|
||||
echo "❌ ERROR: Missing frontmatter"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
# Check required frontmatter fields
|
||||
REQUIRED_FIELDS=("allowed-tools:" "description:")
|
||||
for field in "${REQUIRED_FIELDS[@]}"; do
|
||||
if ! grep -q "^$field" "$COMMAND_FILE"; then
|
||||
echo "❌ ERROR: Missing required field: $field"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check file length (target: 120-150, allow 15% overage = 172)
|
||||
LINE_COUNT=$(wc -l < "$COMMAND_FILE")
|
||||
TARGET_MAX=150
|
||||
TOLERANCE_MAX=172 # 150 + 15% overage
|
||||
|
||||
if ((LINE_COUNT > TOLERANCE_MAX)); then
|
||||
echo "❌ ERROR: Command file is $LINE_COUNT lines (max: $TOLERANCE_MAX with 15% tolerance)"
|
||||
EXIT_CODE=1
|
||||
elif ((LINE_COUNT > TARGET_MAX)); then
|
||||
OVERAGE=$((LINE_COUNT - TARGET_MAX))
|
||||
echo "⚠️ WARNING: Command file is $LINE_COUNT lines (target: $TARGET_MAX, +$OVERAGE over)"
|
||||
elif ((LINE_COUNT < 50)); then
|
||||
echo "⚠️ WARNING: Command file is $LINE_COUNT lines (might be too short)"
|
||||
fi
|
||||
|
||||
# Check for $ARGUMENTS usage (not $1, $2, $3)
|
||||
if grep -qE '\$[0-9]' "$COMMAND_FILE"; then
|
||||
echo "❌ ERROR: Found \$1/\$2/\$3 - use \$ARGUMENTS instead"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
if grep -q '\$ARGUMENTS' "$COMMAND_FILE" || grep -q 'DOLLAR_ARGUMENTS' "$COMMAND_FILE"; then
|
||||
echo "✅ Uses \$ARGUMENTS correctly"
|
||||
else
|
||||
echo "⚠️ WARNING: No \$ARGUMENTS found"
|
||||
fi
|
||||
|
||||
# Check for agent invocation patterns
|
||||
USES_NATURAL_LANGUAGE=false
|
||||
USES_EXPLICIT_TASK=false
|
||||
USES_PARALLEL_PATTERN=false
|
||||
|
||||
# Check for natural language agent invocation
|
||||
if grep -qiE "(Invoke the|Launch.*agent|Run.*agent)" "$COMMAND_FILE"; then
|
||||
USES_NATURAL_LANGUAGE=true
|
||||
fi
|
||||
|
||||
# Check for explicit Task tool with subagent_type
|
||||
if grep -q "subagent_type=" "$COMMAND_FILE"; then
|
||||
USES_EXPLICIT_TASK=true
|
||||
fi
|
||||
|
||||
# Check for parallel execution pattern
|
||||
if grep -qiE "(in parallel|IN PARALLEL|simultaneously|all at once)" "$COMMAND_FILE"; then
|
||||
USES_PARALLEL_PATTERN=true
|
||||
fi
|
||||
|
||||
# Validate invocation patterns
|
||||
if $USES_NATURAL_LANGUAGE || $USES_EXPLICIT_TASK; then
|
||||
if $USES_NATURAL_LANGUAGE; then
|
||||
echo "✅ Uses agent invocation (natural language)"
|
||||
fi
|
||||
if $USES_EXPLICIT_TASK; then
|
||||
echo "✅ Uses explicit Task tool with subagent_type"
|
||||
fi
|
||||
|
||||
# If parallel pattern detected, check for proper implementation
|
||||
if $USES_PARALLEL_PATTERN; then
|
||||
echo "✅ Uses parallel execution pattern"
|
||||
|
||||
# Check if it properly explains Task tool usage for parallel
|
||||
if grep -q "Task tool" "$COMMAND_FILE" && grep -q "SAME.*message\|single.*message\|ONE message" "$COMMAND_FILE"; then
|
||||
echo "✅ Properly explains parallel Task tool execution (multiple calls in ONE message)"
|
||||
elif $USES_EXPLICIT_TASK; then
|
||||
echo "✅ Uses explicit Task tool (parallel execution implied)"
|
||||
else
|
||||
echo "⚠️ WARNING: Parallel pattern found but no Task tool explanation"
|
||||
echo " Consider adding explicit Task tool syntax for clarity"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "⚠️ WARNING: No agent invocation found - might be Pattern 1 (simple command)"
|
||||
fi
|
||||
|
||||
# Check for backticks in examples (causes parsing issues)
|
||||
if grep -q '`' "$COMMAND_FILE"; then
|
||||
BACKTICK_COUNT=$(grep -o '`' "$COMMAND_FILE" | wc -l)
|
||||
echo "⚠️ WARNING: Found $BACKTICK_COUNT backticks - may cause parsing issues"
|
||||
fi
|
||||
|
||||
# Check for code blocks (should NOT be in slash commands - use scripts instead)
|
||||
if grep -q "\`\`\`" "$COMMAND_FILE"; then
|
||||
CODE_BLOCK_COUNT=$(grep -c "\`\`\`" "$COMMAND_FILE")
|
||||
echo "❌ ERROR: Found code blocks (triple backticks) - move code to scripts"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
# Check for proper allowed-tools format
|
||||
if grep -q "allowed-tools:.*Task(\*)" "$COMMAND_FILE"; then
|
||||
echo "✅ Proper allowed-tools format found"
|
||||
else
|
||||
echo "⚠️ WARNING: allowed-tools may not be properly formatted"
|
||||
fi
|
||||
|
||||
# Check for bash execution patterns (! prefix)
|
||||
if grep -q '!{' "$COMMAND_FILE"; then
|
||||
echo "✅ Uses bash execution pattern !{command}"
|
||||
fi
|
||||
|
||||
# Check for file loading patterns (@ prefix)
|
||||
if grep -q '@/' "$COMMAND_FILE" || grep -q '@[a-zA-Z]' "$COMMAND_FILE"; then
|
||||
echo "✅ Uses file loading pattern @filename"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo "✅ Command validation passed"
|
||||
else
|
||||
echo "❌ Command validation failed"
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
108
skills/build-assistant/scripts/validate-plugin-completeness.sh
Executable file
108
skills/build-assistant/scripts/validate-plugin-completeness.sh
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/bin/bash
|
||||
# Validate that all plugin skills have sufficient content in templates, scripts, and examples
|
||||
|
||||
set -e
|
||||
|
||||
# Use current directory if no argument provided (portable - works from any location)
|
||||
PLUGIN_DIR="${1:-$(pwd)}"
|
||||
MIN_EXAMPLES=3
|
||||
MIN_SCRIPTS=3
|
||||
MIN_TEMPLATES_PER_DIR=2
|
||||
|
||||
echo "🔍 Validating Plugin Completeness: $(basename $PLUGIN_DIR)"
|
||||
echo "================================================"
|
||||
|
||||
ISSUES_FOUND=0
|
||||
|
||||
# Find all skills
|
||||
for SKILL_DIR in "$PLUGIN_DIR/skills"/*; do
|
||||
if [ ! -d "$SKILL_DIR" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
SKILL_NAME=$(basename "$SKILL_DIR")
|
||||
echo ""
|
||||
echo "📦 Skill: $SKILL_NAME"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Check SKILL.md exists
|
||||
if [ ! -f "$SKILL_DIR/SKILL.md" ]; then
|
||||
echo " ❌ SKILL.md is missing"
|
||||
((ISSUES_FOUND++))
|
||||
else
|
||||
echo " ✅ SKILL.md exists"
|
||||
fi
|
||||
|
||||
# Check scripts directory
|
||||
if [ -d "$SKILL_DIR/scripts" ]; then
|
||||
SCRIPT_COUNT=$(find "$SKILL_DIR/scripts" -type f -name "*.sh" | wc -l)
|
||||
echo " 📜 Scripts: $SCRIPT_COUNT files"
|
||||
if [ $SCRIPT_COUNT -lt $MIN_SCRIPTS ]; then
|
||||
echo " ⚠️ Warning: Less than $MIN_SCRIPTS scripts"
|
||||
((ISSUES_FOUND++))
|
||||
fi
|
||||
else
|
||||
echo " ❌ scripts/ directory missing"
|
||||
((ISSUES_FOUND++))
|
||||
fi
|
||||
|
||||
# Check templates directory
|
||||
if [ -d "$SKILL_DIR/templates" ]; then
|
||||
echo " 📄 Templates:"
|
||||
|
||||
# Find all template subdirectories
|
||||
EMPTY_DIRS=0
|
||||
for TEMPLATE_DIR in "$SKILL_DIR/templates"/*; do
|
||||
if [ -d "$TEMPLATE_DIR" ]; then
|
||||
DIR_NAME=$(basename "$TEMPLATE_DIR")
|
||||
FILE_COUNT=$(find "$TEMPLATE_DIR" -type f | wc -l)
|
||||
|
||||
if [ $FILE_COUNT -eq 0 ]; then
|
||||
echo " ❌ $DIR_NAME/ is EMPTY"
|
||||
((EMPTY_DIRS++))
|
||||
((ISSUES_FOUND++))
|
||||
elif [ $FILE_COUNT -lt $MIN_TEMPLATES_PER_DIR ]; then
|
||||
echo " ⚠️ $DIR_NAME/ has only $FILE_COUNT file(s)"
|
||||
find "$TEMPLATE_DIR" -type f -exec basename {} \; | sed 's/^/ - /'
|
||||
else
|
||||
echo " ✅ $DIR_NAME/ has $FILE_COUNT files"
|
||||
find "$TEMPLATE_DIR" -type f -exec basename {} \; | sed 's/^/ - /'
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $EMPTY_DIRS -gt 0 ]; then
|
||||
echo " ⚠️ Total empty template directories: $EMPTY_DIRS"
|
||||
fi
|
||||
else
|
||||
echo " ❌ templates/ directory missing"
|
||||
((ISSUES_FOUND++))
|
||||
fi
|
||||
|
||||
# Check examples directory
|
||||
if [ -d "$SKILL_DIR/examples" ]; then
|
||||
EXAMPLE_COUNT=$(find "$SKILL_DIR/examples" -type f -name "*.md" | wc -l)
|
||||
echo " 📚 Examples: $EXAMPLE_COUNT files"
|
||||
|
||||
if [ $EXAMPLE_COUNT -lt $MIN_EXAMPLES ]; then
|
||||
echo " ❌ Less than $MIN_EXAMPLES examples"
|
||||
((ISSUES_FOUND++))
|
||||
fi
|
||||
|
||||
# List examples
|
||||
find "$SKILL_DIR/examples" -type f -name "*.md" -exec basename {} \; | sed 's/^/ - /'
|
||||
else
|
||||
echo " ❌ examples/ directory missing"
|
||||
((ISSUES_FOUND++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "================================================"
|
||||
if [ $ISSUES_FOUND -eq 0 ]; then
|
||||
echo "✅ All validations passed! Plugin is complete."
|
||||
exit 0
|
||||
else
|
||||
echo "❌ Found $ISSUES_FOUND issue(s) that need to be addressed."
|
||||
exit 1
|
||||
fi
|
||||
136
skills/build-assistant/scripts/validate-plugin-manifest.sh
Executable file
136
skills/build-assistant/scripts/validate-plugin-manifest.sh
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Validate and fix plugin.json manifest
|
||||
# Usage: ./validate-plugin-manifest.sh <plugin-name> [--fix]
|
||||
|
||||
set -e
|
||||
|
||||
PLUGIN_NAME=$1
|
||||
FIX_MODE=false
|
||||
|
||||
if [ "$2" = "--fix" ]; then
|
||||
FIX_MODE=true
|
||||
fi
|
||||
|
||||
if [ -z "$PLUGIN_NAME" ]; then
|
||||
echo "Usage: $0 <plugin-name> [--fix]"
|
||||
echo "Example: $0 elevenlabs --fix"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MANIFEST_FILE="plugins/$PLUGIN_NAME/.claude-plugin/plugin.json"
|
||||
|
||||
if [ ! -f "$MANIFEST_FILE" ]; then
|
||||
echo "ERROR: Manifest file not found: $MANIFEST_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[INFO] Validating plugin manifest: $MANIFEST_FILE"
|
||||
|
||||
# Test 1: Valid JSON syntax
|
||||
if ! python3 -m json.tool "$MANIFEST_FILE" > /dev/null 2>&1; then
|
||||
echo "❌ FAIL: Invalid JSON syntax"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ PASS: Valid JSON syntax"
|
||||
|
||||
# Test 2: Required fields
|
||||
REQUIRED_FIELDS=("name" "version" "description" "author")
|
||||
MISSING_FIELDS=()
|
||||
|
||||
for field in "${REQUIRED_FIELDS[@]}"; do
|
||||
if ! grep -q "\"$field\"" "$MANIFEST_FILE"; then
|
||||
MISSING_FIELDS+=("$field")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#MISSING_FIELDS[@]} -gt 0 ]; then
|
||||
echo "❌ FAIL: Missing required fields: ${MISSING_FIELDS[*]}"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ PASS: All required fields present"
|
||||
|
||||
# Test 3: Repository field format (should be string, not object)
|
||||
if grep -q '"repository".*{' "$MANIFEST_FILE"; then
|
||||
echo "❌ FAIL: repository field is an object (should be string)"
|
||||
|
||||
if [ "$FIX_MODE" = true ]; then
|
||||
echo "[INFO] Fixing repository field..."
|
||||
|
||||
# Extract URL from repository object
|
||||
REPO_URL=$(grep -A2 '"repository"' "$MANIFEST_FILE" | grep '"url"' | sed 's/.*"url": "\(.*\)".*/\1/')
|
||||
|
||||
if [ -n "$REPO_URL" ]; then
|
||||
# Create temp file with fixed repository
|
||||
python3 << EOF
|
||||
import json
|
||||
with open("$MANIFEST_FILE", "r") as f:
|
||||
data = json.load(f)
|
||||
if isinstance(data.get("repository"), dict):
|
||||
data["repository"] = data["repository"].get("url", "")
|
||||
with open("$MANIFEST_FILE", "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
f.write("\n")
|
||||
EOF
|
||||
echo "✅ FIXED: repository changed to string: $REPO_URL"
|
||||
else
|
||||
echo "❌ Could not extract repository URL"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "[HINT] Run with --fix to automatically correct this"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "✅ PASS: repository field is correct format"
|
||||
fi
|
||||
|
||||
# Test 4: Category field (should NOT exist)
|
||||
if grep -q '"category"' "$MANIFEST_FILE"; then
|
||||
echo "❌ FAIL: 'category' field found (not a valid field)"
|
||||
|
||||
if [ "$FIX_MODE" = true ]; then
|
||||
echo "[INFO] Removing category field..."
|
||||
python3 << EOF
|
||||
import json
|
||||
with open("$MANIFEST_FILE", "r") as f:
|
||||
data = json.load(f)
|
||||
if "category" in data:
|
||||
del data["category"]
|
||||
with open("$MANIFEST_FILE", "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
f.write("\n")
|
||||
EOF
|
||||
echo "✅ FIXED: category field removed"
|
||||
else
|
||||
echo "[HINT] Run with --fix to automatically correct this"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "✅ PASS: No invalid 'category' field"
|
||||
fi
|
||||
|
||||
# Test 5: Author field structure
|
||||
if ! grep -A1 '"author"' "$MANIFEST_FILE" | grep -q '"name"'; then
|
||||
echo "❌ FAIL: author.name field missing"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ PASS: author field properly structured"
|
||||
|
||||
# Final validation
|
||||
if python3 -m json.tool "$MANIFEST_FILE" > /dev/null 2>&1; then
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "✅ MANIFEST VALIDATION PASSED"
|
||||
echo "=========================================="
|
||||
echo "Plugin: $PLUGIN_NAME"
|
||||
echo "File: $MANIFEST_FILE"
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "❌ MANIFEST VALIDATION FAILED"
|
||||
echo "=========================================="
|
||||
exit 1
|
||||
fi
|
||||
137
skills/build-assistant/scripts/validate-plugin.sh
Executable file
137
skills/build-assistant/scripts/validate-plugin.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: validate-plugin.sh
|
||||
# Purpose: Validate plugin directory compliance with Claude Code standards
|
||||
# Subsystem: build-system
|
||||
# Called by: /build:plugin command after generation
|
||||
# Outputs: Validation report to stdout
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PLUGIN_DIR="${1:?Usage: $0 <plugin-directory>}"
|
||||
SETTINGS_FILE="$HOME/.claude/settings.local.json"
|
||||
|
||||
echo "[INFO] Validating plugin directory: $PLUGIN_DIR"
|
||||
|
||||
# Check directory exists
|
||||
if [[ ! -d "$PLUGIN_DIR" ]]; then
|
||||
echo "❌ ERROR: Directory not found: $PLUGIN_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check .claude-plugin directory exists
|
||||
if [[ ! -d "$PLUGIN_DIR/.claude-plugin" ]]; then
|
||||
echo "❌ ERROR: Missing .claude-plugin directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check plugin.json exists
|
||||
if [[ ! -f "$PLUGIN_DIR/.claude-plugin/plugin.json" ]]; then
|
||||
echo "❌ ERROR: Missing plugin.json manifest"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate JSON syntax
|
||||
if ! python3 -m json.tool "$PLUGIN_DIR/.claude-plugin/plugin.json" > /dev/null 2>&1; then
|
||||
echo "❌ ERROR: Invalid JSON in plugin.json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate plugin.json schema (only allowed fields)
|
||||
ALLOWED_FIELDS=("name" "version" "description" "author" "homepage" "repository" "license" "keywords" "category" "tags" "strict" "commands" "agents" "hooks" "mcpServers")
|
||||
INVALID_FIELDS=$(python3 -c "
|
||||
import json, sys
|
||||
with open('$PLUGIN_DIR/.claude-plugin/plugin.json') as f:
|
||||
data = json.load(f)
|
||||
allowed = set($( printf "'%s', " "${ALLOWED_FIELDS[@]}" | sed 's/, $//' ))
|
||||
invalid = [k for k in data.keys() if k not in allowed]
|
||||
if invalid:
|
||||
print(' '.join(invalid))
|
||||
" 2>/dev/null)
|
||||
|
||||
if [[ -n "$INVALID_FIELDS" ]]; then
|
||||
echo "❌ ERROR: Invalid fields in plugin.json: $INVALID_FIELDS"
|
||||
echo "[INFO] Allowed fields: ${ALLOWED_FIELDS[*]}"
|
||||
echo "[INFO] Move custom metadata to keywords array for discoverability"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check required fields in plugin.json
|
||||
REQUIRED_FIELDS=("name" "version" "description")
|
||||
for field in "${REQUIRED_FIELDS[@]}"; do
|
||||
if ! grep -q "\"$field\":" "$PLUGIN_DIR/.claude-plugin/plugin.json"; then
|
||||
echo "❌ ERROR: Missing required field: $field"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Validate author field structure if present
|
||||
if grep -q "\"author\":" "$PLUGIN_DIR/.claude-plugin/plugin.json"; then
|
||||
AUTHOR_VALID=$(python3 -c "
|
||||
import json
|
||||
with open('$PLUGIN_DIR/.claude-plugin/plugin.json') as f:
|
||||
data = json.load(f)
|
||||
author = data.get('author')
|
||||
if isinstance(author, dict):
|
||||
if 'name' in author:
|
||||
print('valid')
|
||||
else:
|
||||
print('missing_name')
|
||||
elif isinstance(author, str):
|
||||
print('string')
|
||||
" 2>/dev/null)
|
||||
|
||||
if [[ "$AUTHOR_VALID" == "string" ]]; then
|
||||
echo "❌ ERROR: author field must be an object with 'name' and 'email' fields, not a string"
|
||||
exit 1
|
||||
elif [[ "$AUTHOR_VALID" == "missing_name" ]]; then
|
||||
echo "❌ ERROR: author object must include 'name' field"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check component directories are at root (not inside .claude-plugin)
|
||||
if [[ -d "$PLUGIN_DIR/.claude-plugin/commands" ]] || \
|
||||
[[ -d "$PLUGIN_DIR/.claude-plugin/skills" ]] || \
|
||||
[[ -d "$PLUGIN_DIR/.claude-plugin/hooks" ]]; then
|
||||
echo "❌ ERROR: Component directories must be at plugin root, not inside .claude-plugin/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# NEW: Check if plugin commands are registered in settings.local.json
|
||||
PLUGIN_NAME=$(basename "$PLUGIN_DIR")
|
||||
|
||||
if [[ -f "$SETTINGS_FILE" ]]; then
|
||||
if [[ -d "$PLUGIN_DIR/commands" ]]; then
|
||||
# Check for wildcard permission
|
||||
if ! grep -q "SlashCommand(/$PLUGIN_NAME:\\*)" "$SETTINGS_FILE"; then
|
||||
echo "⚠️ WARNING: Plugin commands not registered in settings.local.json"
|
||||
echo "[INFO] Run: bash plugins/domain-plugin-builder/skills/build-assistant/scripts/sync-settings-permissions.sh"
|
||||
else
|
||||
echo "✅ Plugin commands registered in settings.local.json"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "⚠️ WARNING: No settings.local.json found"
|
||||
echo "[INFO] Run: bash plugins/domain-plugin-builder/skills/build-assistant/scripts/sync-settings-permissions.sh"
|
||||
fi
|
||||
|
||||
echo "✅ Plugin validation passed"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📦 NEXT STEP: Install Plugin to Test"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "To make the plugin available for use:"
|
||||
echo ""
|
||||
echo " 1. Update marketplace in ~/.claude:"
|
||||
echo " cp -r $PLUGIN_DIR ~/.claude/plugins/marketplaces/ai-dev-marketplace/plugins/"
|
||||
echo " cp .claude-plugin/marketplace.json ~/.claude/plugins/marketplaces/ai-dev-marketplace/.claude-plugin/"
|
||||
echo ""
|
||||
echo " 2. Install the plugin:"
|
||||
echo " /plugin install $PLUGIN_NAME@ai-dev-marketplace"
|
||||
echo ""
|
||||
echo " 3. Verify installation:"
|
||||
echo " /$PLUGIN_NAME:init (or any command from the plugin)"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
exit 0
|
||||
206
skills/build-assistant/scripts/validate-skill.sh
Executable file
206
skills/build-assistant/scripts/validate-skill.sh
Executable file
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env bash
|
||||
# Script: validate-skill.sh
|
||||
# Purpose: Validate Agent Skill directory compliance with Claude Code standards
|
||||
# Subsystem: build-system
|
||||
# Called by: /build:skill command after generation
|
||||
# Outputs: Validation report to stdout
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SKILL_DIR="${1:?Usage: $0 <skill-directory>}"
|
||||
EXIT_CODE=0
|
||||
|
||||
echo "[INFO] Validating skill directory: $SKILL_DIR"
|
||||
echo ""
|
||||
|
||||
# Check directory exists
|
||||
if [[ ! -d "$SKILL_DIR" ]]; then
|
||||
echo "❌ ERROR: Directory not found: $SKILL_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check SKILL.md exists
|
||||
if [[ ! -f "$SKILL_DIR/SKILL.md" ]]; then
|
||||
echo "❌ ERROR: Missing SKILL.md file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check frontmatter exists and starts at line 1
|
||||
FIRST_LINE=$(head -n 1 "$SKILL_DIR/SKILL.md")
|
||||
if [[ "$FIRST_LINE" != "---" ]]; then
|
||||
echo "❌ ERROR: YAML frontmatter MUST start at line 1"
|
||||
echo " Found: $FIRST_LINE"
|
||||
echo " Expected: ---"
|
||||
echo " CRITICAL: Nothing can come before the opening --- (no titles, no comments, no blank lines)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q "^---$" "$SKILL_DIR/SKILL.md"; then
|
||||
echo "❌ ERROR: Missing closing frontmatter delimiter in SKILL.md"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check required frontmatter fields
|
||||
REQUIRED_FIELDS=("name:" "description:")
|
||||
for field in "${REQUIRED_FIELDS[@]}"; do
|
||||
if ! grep -q "^$field" "$SKILL_DIR/SKILL.md"; then
|
||||
echo "❌ ERROR: Missing required field: $field"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check description includes trigger keywords
|
||||
if ! grep -q "Use when" "$SKILL_DIR/SKILL.md"; then
|
||||
echo "⚠️ WARNING: Description should include 'Use when' trigger context"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "[INFO] Checking minimum requirements (scripts, templates, examples)..."
|
||||
echo ""
|
||||
|
||||
# Minimum requirements per skill
|
||||
MIN_SCRIPTS=3
|
||||
MIN_TEMPLATES=4
|
||||
MIN_EXAMPLES=3
|
||||
|
||||
# Count scripts
|
||||
if [[ -d "$SKILL_DIR/scripts" ]]; then
|
||||
SCRIPT_COUNT=$(find "$SKILL_DIR/scripts" -type f -name "*.sh" | wc -l)
|
||||
echo "📂 Scripts found: $SCRIPT_COUNT"
|
||||
|
||||
if ((SCRIPT_COUNT >= MIN_SCRIPTS)); then
|
||||
echo " ✅ Meets minimum requirement (>= $MIN_SCRIPTS scripts)"
|
||||
else
|
||||
echo " ❌ ERROR: Below minimum requirement (need $MIN_SCRIPTS, found $SCRIPT_COUNT)"
|
||||
echo " Each skill should have 3-5 helper scripts (setup, validate, generate, etc.)"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
# List scripts
|
||||
if ((SCRIPT_COUNT > 0)); then
|
||||
echo " Scripts:"
|
||||
find "$SKILL_DIR/scripts" -type f -name "*.sh" -exec basename {} \; | sed 's/^/ - /'
|
||||
fi
|
||||
else
|
||||
echo "📂 Scripts: directory not found"
|
||||
echo " ❌ ERROR: Missing scripts/ directory"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Count templates
|
||||
if [[ -d "$SKILL_DIR/templates" ]]; then
|
||||
TEMPLATE_COUNT=$(find "$SKILL_DIR/templates" -type f | wc -l)
|
||||
echo "📂 Templates found: $TEMPLATE_COUNT"
|
||||
|
||||
if ((TEMPLATE_COUNT >= MIN_TEMPLATES)); then
|
||||
echo " ✅ Meets minimum requirement (>= $MIN_TEMPLATES templates)"
|
||||
else
|
||||
echo " ❌ ERROR: Below minimum requirement (need $MIN_TEMPLATES, found $TEMPLATE_COUNT)"
|
||||
echo " Each skill should have 4-6 templates (basic, advanced, TS, Python, etc.)"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
# Check for TypeScript and Python coverage
|
||||
TS_COUNT=$(find "$SKILL_DIR/templates" -type f -name "*.ts" -o -name "*.tsx" | wc -l)
|
||||
PY_COUNT=$(find "$SKILL_DIR/templates" -type f -name "*.py" | wc -l)
|
||||
|
||||
if ((TS_COUNT > 0)) && ((PY_COUNT > 0)); then
|
||||
echo " ✅ Has both TypeScript ($TS_COUNT) and Python ($PY_COUNT) templates"
|
||||
elif ((TS_COUNT > 0)); then
|
||||
echo " ⚠️ WARNING: Has TypeScript templates but no Python templates"
|
||||
elif ((PY_COUNT > 0)); then
|
||||
echo " ⚠️ WARNING: Has Python templates but no TypeScript templates"
|
||||
fi
|
||||
|
||||
# List templates by type
|
||||
if ((TEMPLATE_COUNT > 0)); then
|
||||
echo " Templates:"
|
||||
find "$SKILL_DIR/templates" -type f | sed "s|$SKILL_DIR/templates/||" | sed 's/^/ - /'
|
||||
fi
|
||||
else
|
||||
echo "📂 Templates: directory not found"
|
||||
echo " ❌ ERROR: Missing templates/ directory"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Count examples
|
||||
if [[ -d "$SKILL_DIR/examples" ]]; then
|
||||
EXAMPLE_COUNT=$(find "$SKILL_DIR/examples" -type f -name "*.md" | wc -l)
|
||||
echo "📂 Examples found: $EXAMPLE_COUNT"
|
||||
|
||||
if ((EXAMPLE_COUNT >= MIN_EXAMPLES)); then
|
||||
echo " ✅ Meets minimum requirement (>= $MIN_EXAMPLES examples)"
|
||||
else
|
||||
echo " ❌ ERROR: Below minimum requirement (need $MIN_EXAMPLES, found $EXAMPLE_COUNT)"
|
||||
echo " Each skill should have 3-5 examples (basic, advanced, patterns, edge-cases, integration)"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
# List examples
|
||||
if ((EXAMPLE_COUNT > 0)); then
|
||||
echo " Examples:"
|
||||
find "$SKILL_DIR/examples" -type f -name "*.md" -exec basename {} \; | sed 's/^/ - /'
|
||||
fi
|
||||
else
|
||||
echo "📂 Examples: directory not found"
|
||||
echo " ❌ ERROR: Missing examples/ directory"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Cross-reference: Check that SKILL.md references match actual files
|
||||
echo "[INFO] Checking SKILL.md references match actual files..."
|
||||
echo ""
|
||||
|
||||
# Count references in SKILL.md
|
||||
SCRIPT_REFS=$(grep -oE 'scripts/[a-zA-Z0-9_-]+\.sh' "$SKILL_DIR/SKILL.md" 2>/dev/null | sort -u | wc -l || echo 0)
|
||||
TEMPLATE_REFS=$(grep -oE 'templates/[a-zA-Z0-9_/.-]+\.(ts|py|tsx|js)' "$SKILL_DIR/SKILL.md" 2>/dev/null | sort -u | wc -l || echo 0)
|
||||
EXAMPLE_REFS=$(grep -oE 'examples/[a-zA-Z0-9_-]+\.md' "$SKILL_DIR/SKILL.md" 2>/dev/null | sort -u | wc -l || echo 0)
|
||||
|
||||
echo "📝 SKILL.md references:"
|
||||
echo " Scripts referenced: $SCRIPT_REFS"
|
||||
echo " Templates referenced: $TEMPLATE_REFS"
|
||||
echo " Examples referenced: $EXAMPLE_REFS"
|
||||
|
||||
echo ""
|
||||
|
||||
# Compare references to actual files
|
||||
if [[ -d "$SKILL_DIR/scripts" ]]; then
|
||||
if ((SCRIPT_REFS == SCRIPT_COUNT)); then
|
||||
echo " ✅ Script references match actual files ($SCRIPT_REFS = $SCRIPT_COUNT)"
|
||||
else
|
||||
echo " ⚠️ WARNING: Script count mismatch (referenced: $SCRIPT_REFS, actual: $SCRIPT_COUNT)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -d "$SKILL_DIR/templates" ]]; then
|
||||
if ((TEMPLATE_REFS == TEMPLATE_COUNT)); then
|
||||
echo " ✅ Template references match actual files ($TEMPLATE_REFS = $TEMPLATE_COUNT)"
|
||||
else
|
||||
echo " ⚠️ WARNING: Template count mismatch (referenced: $TEMPLATE_REFS, actual: $TEMPLATE_COUNT)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -d "$SKILL_DIR/examples" ]]; then
|
||||
if ((EXAMPLE_REFS == EXAMPLE_COUNT)); then
|
||||
echo " ✅ Example references match actual files ($EXAMPLE_REFS = $EXAMPLE_COUNT)"
|
||||
else
|
||||
echo " ⚠️ WARNING: Example count mismatch (referenced: $EXAMPLE_REFS, actual: $EXAMPLE_COUNT)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Final result
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo "✅ Skill validation passed - all minimum requirements met!"
|
||||
else
|
||||
echo "❌ Skill validation failed - does not meet minimum requirements"
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
Reference in New Issue
Block a user