Recreate source from .skill zip file
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
+271
@@ -0,0 +1,271 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Workflow Analysis Script for Meta Skill Generator
|
||||
|
||||
Analyzes user requirements and suggests which operations should be:
|
||||
- Go scripts (deterministic, performance-critical)
|
||||
- Python scripts (library-heavy, data science)
|
||||
- Agent workflows (requires reasoning, context-dependent)
|
||||
|
||||
Usage:
|
||||
analyze_workflow.py [--examples file1.txt file2.txt ...] [--interactive]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Tuple
|
||||
|
||||
# Keywords that indicate deterministic operations
|
||||
DETERMINISTIC_KEYWORDS = {
|
||||
'convert', 'transform', 'parse', 'extract', 'validate',
|
||||
'format', 'encode', 'decode', 'compress', 'decompress',
|
||||
'resize', 'crop', 'rotate', 'merge', 'split',
|
||||
'sort', 'filter', 'calculate', 'compute'
|
||||
}
|
||||
|
||||
# Keywords that indicate dynamic/reasoning operations
|
||||
DYNAMIC_KEYWORDS = {
|
||||
'analyze', 'understand', 'interpret', 'decide', 'choose',
|
||||
'suggest', 'recommend', 'summarize', 'explain', 'describe',
|
||||
'evaluate', 'assess', 'determine', 'identify', 'classify'
|
||||
}
|
||||
|
||||
# Keywords that suggest Go (performance-critical)
|
||||
GO_INDICATORS = {
|
||||
'large file', 'batch', 'thousands', 'millions', 'concurrent',
|
||||
'parallel', 'performance', 'fast', 'binary', 'low-level',
|
||||
'file system', 'network', 'stream'
|
||||
}
|
||||
|
||||
# Keywords that suggest Python (library-heavy)
|
||||
PYTHON_INDICATORS = {
|
||||
'pandas', 'numpy', 'scikit', 'machine learning', 'data science',
|
||||
'plot', 'graph', 'visualization', 'api client', 'requests',
|
||||
'beautiful soup', 'selenium', 'opencv'
|
||||
}
|
||||
|
||||
class OperationAnalyzer:
|
||||
def __init__(self):
|
||||
self.operations = []
|
||||
|
||||
def analyze_text(self, text: str) -> List[Dict]:
|
||||
"""Analyze text and identify operations."""
|
||||
text_lower = text.lower()
|
||||
sentences = text.replace('?', '.').replace('!', '.').split('.')
|
||||
|
||||
operations = []
|
||||
for sentence in sentences:
|
||||
sentence = sentence.strip()
|
||||
if not sentence:
|
||||
continue
|
||||
|
||||
op = self._analyze_sentence(sentence)
|
||||
if op:
|
||||
operations.append(op)
|
||||
|
||||
return operations
|
||||
|
||||
def _analyze_sentence(self, sentence: str) -> Dict:
|
||||
"""Analyze a single sentence for operation type."""
|
||||
sentence_lower = sentence.lower()
|
||||
|
||||
# Check for deterministic vs dynamic
|
||||
det_score = sum(1 for kw in DETERMINISTIC_KEYWORDS if kw in sentence_lower)
|
||||
dyn_score = sum(1 for kw in DYNAMIC_KEYWORDS if kw in sentence_lower)
|
||||
|
||||
# Check for Go vs Python indicators
|
||||
go_score = sum(1 for kw in GO_INDICATORS if kw in sentence_lower)
|
||||
py_score = sum(1 for kw in PYTHON_INDICATORS if kw in sentence_lower)
|
||||
|
||||
if det_score == 0 and dyn_score == 0:
|
||||
return None
|
||||
|
||||
# Determine operation type
|
||||
if det_score > dyn_score:
|
||||
if go_score > py_score:
|
||||
op_type = 'go_script'
|
||||
reason = 'Deterministic operation with performance/binary characteristics'
|
||||
elif py_score > 0:
|
||||
op_type = 'python_script'
|
||||
reason = 'Deterministic operation requiring specialized libraries'
|
||||
else:
|
||||
op_type = 'go_script'
|
||||
reason = 'Deterministic operation suitable for compiled binary'
|
||||
else:
|
||||
op_type = 'agent_workflow'
|
||||
reason = 'Requires reasoning, context analysis, or decision-making'
|
||||
|
||||
return {
|
||||
'description': sentence,
|
||||
'type': op_type,
|
||||
'reason': reason,
|
||||
'det_score': det_score,
|
||||
'dyn_score': dyn_score,
|
||||
'go_score': go_score,
|
||||
'py_score': py_score
|
||||
}
|
||||
|
||||
def generate_recommendations(self, operations: List[Dict]) -> str:
|
||||
"""Generate recommendations report."""
|
||||
go_ops = [op for op in operations if op['type'] == 'go_script']
|
||||
py_ops = [op for op in operations if op['type'] == 'python_script']
|
||||
agent_ops = [op for op in operations if op['type'] == 'agent_workflow']
|
||||
|
||||
report = []
|
||||
report.append("=" * 70)
|
||||
report.append("WORKFLOW ANALYSIS REPORT")
|
||||
report.append("=" * 70)
|
||||
report.append("")
|
||||
|
||||
report.append(f"Total operations identified: {len(operations)}")
|
||||
report.append(f" - Go scripts recommended: {len(go_ops)}")
|
||||
report.append(f" - Python scripts recommended: {len(py_ops)}")
|
||||
report.append(f" - Agent workflows recommended: {len(agent_ops)}")
|
||||
report.append("")
|
||||
|
||||
if go_ops:
|
||||
report.append("-" * 70)
|
||||
report.append("GO SCRIPTS (Deterministic, Performance-Critical)")
|
||||
report.append("-" * 70)
|
||||
for i, op in enumerate(go_ops, 1):
|
||||
report.append(f"\n{i}. {op['description']}")
|
||||
report.append(f" Reason: {op['reason']}")
|
||||
report.append(f" Suggested name: {self._suggest_script_name(op['description'])}")
|
||||
report.append("")
|
||||
|
||||
if py_ops:
|
||||
report.append("-" * 70)
|
||||
report.append("PYTHON SCRIPTS (Library-Heavy Operations)")
|
||||
report.append("-" * 70)
|
||||
for i, op in enumerate(py_ops, 1):
|
||||
report.append(f"\n{i}. {op['description']}")
|
||||
report.append(f" Reason: {op['reason']}")
|
||||
report.append(f" Suggested name: {self._suggest_script_name(op['description'])}")
|
||||
report.append("")
|
||||
|
||||
if agent_ops:
|
||||
report.append("-" * 70)
|
||||
report.append("AGENT WORKFLOWS (Reasoning Required)")
|
||||
report.append("-" * 70)
|
||||
for i, op in enumerate(agent_ops, 1):
|
||||
report.append(f"\n{i}. {op['description']}")
|
||||
report.append(f" Reason: {op['reason']}")
|
||||
report.append(f" Implementation: Keep as natural language workflow in SKILL.md")
|
||||
report.append("")
|
||||
|
||||
report.append("=" * 70)
|
||||
report.append("RECOMMENDATIONS")
|
||||
report.append("=" * 70)
|
||||
report.append("")
|
||||
|
||||
if go_ops:
|
||||
report.append("For Go scripts:")
|
||||
report.append(" 1. Use generate_go_script.py to create each script")
|
||||
report.append(" 2. Focus on performance and error handling")
|
||||
report.append(" 3. Support parallel processing where applicable")
|
||||
report.append("")
|
||||
|
||||
if py_ops:
|
||||
report.append("For Python scripts:")
|
||||
report.append(" 1. Create scripts in scripts/ directory")
|
||||
report.append(" 2. Add requirements.txt for dependencies")
|
||||
report.append(" 3. Consider virtual environments")
|
||||
report.append("")
|
||||
|
||||
if agent_ops:
|
||||
report.append("For agent workflows:")
|
||||
report.append(" 1. Document in SKILL.md with clear decision points")
|
||||
report.append(" 2. Provide examples for different scenarios")
|
||||
report.append(" 3. Use references/ for detailed guidance")
|
||||
report.append("")
|
||||
|
||||
return '\n'.join(report)
|
||||
|
||||
def _suggest_script_name(self, description: str) -> str:
|
||||
"""Suggest a script name from description."""
|
||||
# Extract key verbs and nouns
|
||||
words = description.lower().split()
|
||||
important_words = []
|
||||
|
||||
for word in words:
|
||||
cleaned = ''.join(c for c in word if c.isalnum())
|
||||
if cleaned in DETERMINISTIC_KEYWORDS or len(cleaned) > 3:
|
||||
important_words.append(cleaned)
|
||||
if len(important_words) >= 3:
|
||||
break
|
||||
|
||||
return '-'.join(important_words[:3]) if important_words else 'operation'
|
||||
|
||||
def interactive_mode():
|
||||
"""Run in interactive mode to gather requirements."""
|
||||
print("=" * 70)
|
||||
print("WORKFLOW ANALYSIS - INTERACTIVE MODE")
|
||||
print("=" * 70)
|
||||
print("\nDescribe the operations your skill should perform.")
|
||||
print("Enter each operation on a separate line.")
|
||||
print("Press Ctrl+D (Unix) or Ctrl+Z (Windows) when done.\n")
|
||||
|
||||
lines = []
|
||||
try:
|
||||
while True:
|
||||
line = input("> ")
|
||||
if line.strip():
|
||||
lines.append(line)
|
||||
except EOFError:
|
||||
pass
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Analyze workflows to identify deterministic vs dynamic operations',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument('--examples', nargs='+', help='Files containing example requests')
|
||||
parser.add_argument('--interactive', action='store_true', help='Run in interactive mode')
|
||||
parser.add_argument('--output', help='Output file for report (default: stdout)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
analyzer = OperationAnalyzer()
|
||||
|
||||
# Gather input
|
||||
text = ""
|
||||
if args.interactive:
|
||||
text = interactive_mode()
|
||||
elif args.examples:
|
||||
for example_file in args.examples:
|
||||
path = Path(example_file)
|
||||
if path.exists():
|
||||
text += path.read_text() + '\n'
|
||||
else:
|
||||
print(f"Warning: File not found: {example_file}", file=sys.stderr)
|
||||
else:
|
||||
print("Error: Provide --examples or use --interactive mode", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if not text.strip():
|
||||
print("Error: No input provided", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Analyze
|
||||
operations = analyzer.analyze_text(text)
|
||||
|
||||
if not operations:
|
||||
print("No operations detected in input", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Generate report
|
||||
report = analyzer.generate_recommendations(operations)
|
||||
|
||||
# Output
|
||||
if args.output:
|
||||
Path(args.output).write_text(report)
|
||||
print(f"✅ Report written to: {args.output}")
|
||||
else:
|
||||
print(report)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+438
@@ -0,0 +1,438 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Go Script Generator for Claude Code Skills
|
||||
|
||||
Analyzes operation requirements and generates efficient Go scripts for
|
||||
deterministic operations that don't require agent interaction.
|
||||
|
||||
Usage:
|
||||
generate_go_script.py --name <script-name> --description <desc> \
|
||||
--input <input-desc> --output <output-desc> \
|
||||
--logic <logic-desc> --skill-path <path>
|
||||
|
||||
Example:
|
||||
generate_go_script.py \
|
||||
--name pdf-to-images \
|
||||
--description "Convert PDF pages to PNG images" \
|
||||
--input "PDF file path" \
|
||||
--output "Directory of PNG files" \
|
||||
--logic "Extract each page as separate image at 300 DPI" \
|
||||
--skill-path ./my-skill
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
|
||||
# Go script template with best practices
|
||||
GO_SCRIPT_TEMPLATE = '''package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
{extra_imports}
|
||||
)
|
||||
|
||||
// {description}
|
||||
// Generated by meta-skill-generator for Claude Code skills
|
||||
|
||||
var (
|
||||
verbose = flag.Bool("verbose", false, "Enable verbose logging")
|
||||
help = flag.Bool("help", false, "Show this help message")
|
||||
)
|
||||
|
||||
func main() {{
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if *help {{
|
||||
usage()
|
||||
os.Exit(0)
|
||||
}}
|
||||
|
||||
if *verbose {{
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||
}} else {{
|
||||
log.SetFlags(0)
|
||||
}}
|
||||
|
||||
// Validate command line arguments
|
||||
if err := validateArgs(); err != nil {{
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\\n", err)
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}}
|
||||
|
||||
// Execute main logic
|
||||
if err := run(); err != nil {{
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\\n", err)
|
||||
os.Exit(1)
|
||||
}}
|
||||
}}
|
||||
|
||||
func usage() {{
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [options] {usage_args}\\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "\\n{description}\\n\\n")
|
||||
fmt.Fprintf(os.Stderr, "Input: {input_desc}\\n")
|
||||
fmt.Fprintf(os.Stderr, "Output: {output_desc}\\n\\n")
|
||||
fmt.Fprintf(os.Stderr, "Options:\\n")
|
||||
flag.PrintDefaults()
|
||||
}}
|
||||
|
||||
func validateArgs() error {{
|
||||
{validation_logic}
|
||||
return nil
|
||||
}}
|
||||
|
||||
func run() error {{
|
||||
if *verbose {{
|
||||
log.Println("Starting {name}...")
|
||||
}}
|
||||
|
||||
{run_logic}
|
||||
|
||||
if *verbose {{
|
||||
log.Println("Completed successfully")
|
||||
}}
|
||||
return nil
|
||||
}}
|
||||
|
||||
{helper_functions}
|
||||
'''
|
||||
|
||||
def infer_imports(logic_desc):
|
||||
"""Infer required Go imports from operation description."""
|
||||
imports = []
|
||||
logic_lower = logic_desc.lower()
|
||||
|
||||
if any(word in logic_lower for word in ['file', 'directory', 'path', 'copy', 'move']):
|
||||
imports.append('"io"')
|
||||
imports.append('"path/filepath"')
|
||||
|
||||
if any(word in logic_lower for word in ['json', 'parse']):
|
||||
imports.append('"encoding/json"')
|
||||
|
||||
if any(word in logic_lower for word in ['csv', 'comma']):
|
||||
imports.append('"encoding/csv"')
|
||||
|
||||
if any(word in logic_lower for word in ['http', 'api', 'request']):
|
||||
imports.append('"net/http"')
|
||||
|
||||
if any(word in logic_lower for word in ['string', 'text', 'replace']):
|
||||
imports.append('"strings"')
|
||||
|
||||
if any(word in logic_lower for word in ['regex', 'pattern', 'match']):
|
||||
imports.append('"regexp"')
|
||||
|
||||
if any(word in logic_lower for word in ['time', 'date', 'duration']):
|
||||
imports.append('"time"')
|
||||
|
||||
if any(word in logic_lower for word in ['concurrent', 'parallel', 'goroutine']):
|
||||
imports.append('"sync"')
|
||||
|
||||
if any(word in logic_lower for word in ['buffer', 'bytes']):
|
||||
imports.append('"bytes"')
|
||||
|
||||
return imports
|
||||
|
||||
def generate_validation_logic(input_desc):
|
||||
"""Generate input validation logic."""
|
||||
validations = []
|
||||
|
||||
if 'file' in input_desc.lower():
|
||||
validations.append(dedent('''
|
||||
if flag.NArg() < 1 {
|
||||
return fmt.Errorf("input file required")
|
||||
}
|
||||
inputFile := flag.Arg(0)
|
||||
if _, err := os.Stat(inputFile); os.IsNotExist(err) {
|
||||
return fmt.Errorf("input file does not exist: %s", inputFile)
|
||||
}
|
||||
''').strip())
|
||||
|
||||
if 'directory' in input_desc.lower():
|
||||
validations.append(dedent('''
|
||||
if flag.NArg() < 1 {
|
||||
return fmt.Errorf("input directory required")
|
||||
}
|
||||
inputDir := flag.Arg(0)
|
||||
if info, err := os.Stat(inputDir); os.IsNotExist(err) || !info.IsDir() {
|
||||
return fmt.Errorf("input directory does not exist: %s", inputDir)
|
||||
}
|
||||
''').strip())
|
||||
|
||||
return '\n\t'.join(validations) if validations else '// No validation needed'
|
||||
|
||||
def generate_run_logic(logic_desc, input_desc, output_desc):
|
||||
"""Generate main execution logic with placeholder."""
|
||||
logic_lower = logic_desc.lower()
|
||||
|
||||
# Start with input handling
|
||||
logic = []
|
||||
|
||||
if 'file' in input_desc.lower():
|
||||
logic.append('inputFile := flag.Arg(0)')
|
||||
logic.append('')
|
||||
|
||||
if 'directory' in input_desc.lower():
|
||||
logic.append('inputDir := flag.Arg(0)')
|
||||
logic.append('')
|
||||
|
||||
# Add operation-specific logic template
|
||||
logic.append('// TODO: Implement the following logic:')
|
||||
logic.append(f'// {logic_desc}')
|
||||
logic.append('')
|
||||
|
||||
if any(word in logic_lower for word in ['convert', 'transform', 'process']):
|
||||
logic.append(dedent('''
|
||||
// 1. Read input
|
||||
// 2. Process/transform data
|
||||
// 3. Write output
|
||||
''').strip())
|
||||
|
||||
if 'parallel' in logic_lower or 'concurrent' in logic_lower:
|
||||
logic.append(dedent('''
|
||||
// Consider using goroutines for parallel processing:
|
||||
// var wg sync.WaitGroup
|
||||
// for _, item := range items {
|
||||
// wg.Add(1)
|
||||
// go func(item Item) {
|
||||
// defer wg.Done()
|
||||
// // Process item
|
||||
// }(item)
|
||||
// }
|
||||
// wg.Wait()
|
||||
''').strip())
|
||||
|
||||
# Add output handling
|
||||
if 'stdout' in output_desc.lower():
|
||||
logic.append('')
|
||||
logic.append('// Write results to stdout')
|
||||
logic.append('fmt.Println(result)')
|
||||
elif 'file' in output_desc.lower():
|
||||
logic.append('')
|
||||
logic.append('outputFile := "output.txt" // TODO: Make configurable')
|
||||
logic.append('if err := os.WriteFile(outputFile, []byte(result), 0644); err != nil {')
|
||||
logic.append(' return fmt.Errorf("failed to write output: %w", err)')
|
||||
logic.append('}')
|
||||
|
||||
logic.append('')
|
||||
logic.append('return nil')
|
||||
|
||||
return '\n\t'.join(logic)
|
||||
|
||||
def generate_helper_functions(logic_desc):
|
||||
"""Generate helper function templates."""
|
||||
helpers = []
|
||||
logic_lower = logic_desc.lower()
|
||||
|
||||
if 'progress' in logic_lower or 'batch' in logic_lower:
|
||||
helpers.append(dedent('''
|
||||
func showProgress(current, total int) {
|
||||
if total > 0 {
|
||||
percent := float64(current) / float64(total) * 100
|
||||
fmt.Fprintf(os.Stderr, "\\rProgress: %.1f%% (%d/%d)", percent, current, total)
|
||||
if current == total {
|
||||
fmt.Fprintln(os.Stderr)
|
||||
}
|
||||
}
|
||||
}
|
||||
''').strip())
|
||||
|
||||
if 'validate' in logic_lower or 'check' in logic_lower:
|
||||
helpers.append(dedent('''
|
||||
func validateInput(data interface{}) error {
|
||||
// TODO: Implement validation logic
|
||||
return nil
|
||||
}
|
||||
''').strip())
|
||||
|
||||
return '\n\n'.join(helpers) if helpers else '// No helper functions needed'
|
||||
|
||||
def determine_usage_args(input_desc):
|
||||
"""Determine usage string from input description."""
|
||||
if 'file' in input_desc.lower():
|
||||
return '<input-file>'
|
||||
elif 'directory' in input_desc.lower():
|
||||
return '<input-dir>'
|
||||
else:
|
||||
return '<input>'
|
||||
|
||||
def generate_go_script(name, description, input_desc, output_desc, logic_desc, skill_path):
|
||||
"""Generate a complete Go script."""
|
||||
|
||||
# Infer what imports we need
|
||||
extra_imports = '\n\t'.join(infer_imports(logic_desc))
|
||||
|
||||
# Generate different sections
|
||||
validation_logic = generate_validation_logic(input_desc)
|
||||
run_logic = generate_run_logic(logic_desc, input_desc, output_desc)
|
||||
helper_functions = generate_helper_functions(logic_desc)
|
||||
usage_args = determine_usage_args(input_desc)
|
||||
|
||||
# Fill in template
|
||||
script_content = GO_SCRIPT_TEMPLATE.format(
|
||||
description=description,
|
||||
name=name,
|
||||
input_desc=input_desc,
|
||||
output_desc=output_desc,
|
||||
usage_args=usage_args,
|
||||
extra_imports=extra_imports,
|
||||
validation_logic=validation_logic,
|
||||
run_logic=run_logic,
|
||||
helper_functions=helper_functions
|
||||
)
|
||||
|
||||
return script_content
|
||||
|
||||
def create_build_script(skill_path, script_name):
|
||||
"""Create a build script to compile the Go binary."""
|
||||
build_script = f'''#!/bin/bash
|
||||
# Build script for {script_name}
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)"
|
||||
BIN_DIR="$SCRIPT_DIR/bin"
|
||||
|
||||
mkdir -p "$BIN_DIR"
|
||||
|
||||
echo "Building {script_name}..."
|
||||
cd "$SCRIPT_DIR"
|
||||
go build -o "$BIN_DIR/{script_name}" {script_name}.go
|
||||
|
||||
echo "✅ Built: $BIN_DIR/{script_name}"
|
||||
echo "Run with: $BIN_DIR/{script_name} --help"
|
||||
'''
|
||||
|
||||
build_path = skill_path / 'scripts' / f'build_{script_name}.sh'
|
||||
build_path.write_text(build_script)
|
||||
build_path.chmod(0o755)
|
||||
|
||||
return build_path
|
||||
|
||||
def update_skill_md(skill_path, script_name, description, input_desc, output_desc):
|
||||
"""Add usage information to SKILL.md."""
|
||||
skill_md = skill_path / 'SKILL.md'
|
||||
|
||||
if not skill_md.exists():
|
||||
print(f"⚠️ Warning: SKILL.md not found at {skill_md}")
|
||||
return
|
||||
|
||||
content = skill_md.read_text()
|
||||
|
||||
# Add script documentation if not already present
|
||||
script_section = f'''
|
||||
### {script_name}
|
||||
|
||||
{description}
|
||||
|
||||
**Input:** {input_desc}
|
||||
**Output:** {output_desc}
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
scripts/bin/{script_name} [options] <input>
|
||||
scripts/bin/{script_name} --help # For detailed options
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
scripts/bin/{script_name} input.txt
|
||||
```
|
||||
'''
|
||||
|
||||
if script_name not in content:
|
||||
# Try to add after "## Resources" or at the end
|
||||
if '## Resources' in content:
|
||||
content = content.replace('## Resources', script_section + '\n## Resources')
|
||||
else:
|
||||
content += '\n' + script_section
|
||||
|
||||
skill_md.write_text(content)
|
||||
print(f"✅ Updated SKILL.md with {script_name} documentation")
|
||||
else:
|
||||
print(f"ℹ️ {script_name} already documented in SKILL.md")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate Go scripts for Claude Code skills',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=dedent('''
|
||||
Examples:
|
||||
generate_go_script.py \\
|
||||
--name pdf-extract-text \\
|
||||
--description "Extract text from PDF files" \\
|
||||
--input "PDF file path" \\
|
||||
--output "Text content to stdout" \\
|
||||
--logic "Parse PDF and extract all text content" \\
|
||||
--skill-path ./my-pdf-skill
|
||||
|
||||
generate_go_script.py \\
|
||||
--name csv-to-json \\
|
||||
--description "Convert CSV files to JSON" \\
|
||||
--input "CSV file path" \\
|
||||
--output "JSON file" \\
|
||||
--logic "Parse CSV rows and convert to JSON array" \\
|
||||
--skill-path ./data-tools
|
||||
''')
|
||||
)
|
||||
|
||||
parser.add_argument('--name', required=True, help='Name of the script (e.g., pdf-to-images)')
|
||||
parser.add_argument('--description', required=True, help='What the script does')
|
||||
parser.add_argument('--input', required=True, help='Description of input')
|
||||
parser.add_argument('--output', required=True, help='Description of output')
|
||||
parser.add_argument('--logic', required=True, help='Description of the transformation logic')
|
||||
parser.add_argument('--skill-path', required=True, help='Path to the skill directory')
|
||||
parser.add_argument('--no-build', action='store_true', help='Skip creating build script')
|
||||
parser.add_argument('--no-update-md', action='store_true', help='Skip updating SKILL.md')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Validate skill path
|
||||
skill_path = Path(args.skill_path).resolve()
|
||||
if not skill_path.exists():
|
||||
print(f"❌ Error: Skill path does not exist: {skill_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# Create scripts directory if needed
|
||||
scripts_dir = skill_path / 'scripts'
|
||||
scripts_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Generate Go script
|
||||
print(f"🚀 Generating Go script: {args.name}")
|
||||
script_content = generate_go_script(
|
||||
args.name,
|
||||
args.description,
|
||||
args.input,
|
||||
args.output,
|
||||
args.logic,
|
||||
skill_path
|
||||
)
|
||||
|
||||
# Write Go file
|
||||
go_file = scripts_dir / f'{args.name}.go'
|
||||
go_file.write_text(script_content)
|
||||
print(f"✅ Created: {go_file}")
|
||||
|
||||
# Create build script
|
||||
if not args.no_build:
|
||||
build_script = create_build_script(skill_path, args.name)
|
||||
print(f"✅ Created build script: {build_script}")
|
||||
print(f" Run: ./scripts/build_{args.name}.sh")
|
||||
|
||||
# Update SKILL.md
|
||||
if not args.no_update_md:
|
||||
update_skill_md(skill_path, args.name, args.description, args.input, args.output)
|
||||
|
||||
print(f"\n✅ Go script '{args.name}' generated successfully")
|
||||
print("\nNext steps:")
|
||||
print(f"1. Review and customize: {go_file}")
|
||||
print(f"2. Build the binary: ./scripts/build_{args.name}.sh")
|
||||
print(f"3. Test: ./scripts/bin/{args.name} --help")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,280 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
End-to-End Skill Creator with Workflow Analysis
|
||||
|
||||
Guides through the complete process of creating a Claude Code skill with
|
||||
intelligent Go script generation.
|
||||
|
||||
Usage:
|
||||
init_skill_with_analysis.py <skill-name> --path <output-dir>
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def run_command(cmd, cwd=None):
|
||||
"""Run a command and return success status."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
cwd=cwd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
return True, result.stdout
|
||||
except subprocess.CalledProcessError as e:
|
||||
return False, e.stderr
|
||||
|
||||
def gather_examples():
|
||||
"""Gather example use cases from user."""
|
||||
print("\n" + "="*70)
|
||||
print("STEP 1: GATHER EXAMPLE USE CASES")
|
||||
print("="*70)
|
||||
print("\nProvide 3-5 example requests that your skill should handle.")
|
||||
print("Each example should be a concrete user request.")
|
||||
print("Press Enter twice when done.\n")
|
||||
|
||||
examples = []
|
||||
blank_count = 0
|
||||
|
||||
while blank_count < 2:
|
||||
line = input(f"Example {len(examples)+1}: ").strip()
|
||||
if not line:
|
||||
blank_count += 1
|
||||
else:
|
||||
blank_count = 0
|
||||
examples.append(line)
|
||||
|
||||
if not examples:
|
||||
print("Error: No examples provided")
|
||||
sys.exit(1)
|
||||
|
||||
return examples
|
||||
|
||||
def analyze_workflow(examples):
|
||||
"""Analyze examples to identify operation types."""
|
||||
print("\n" + "="*70)
|
||||
print("STEP 2: ANALYZING WORKFLOW")
|
||||
print("="*70)
|
||||
|
||||
# Create temporary file with examples
|
||||
temp_file = Path("/tmp/skill_examples.txt")
|
||||
temp_file.write_text('\n'.join(examples))
|
||||
|
||||
# Run analysis
|
||||
script_path = Path(__file__).parent / "analyze_workflow.py"
|
||||
cmd = [sys.executable, str(script_path), "--examples", str(temp_file)]
|
||||
|
||||
success, output = run_command(cmd)
|
||||
if not success:
|
||||
print(f"Warning: Workflow analysis failed: {output}")
|
||||
return None
|
||||
|
||||
print(output)
|
||||
return output
|
||||
|
||||
def confirm_plan():
|
||||
"""Ask user to confirm the plan."""
|
||||
print("\n" + "="*70)
|
||||
print("Does this analysis look correct?")
|
||||
response = input("Continue with skill creation? (y/n): ").lower()
|
||||
return response == 'y'
|
||||
|
||||
def initialize_skill(skill_name, path):
|
||||
"""Initialize skill directory structure."""
|
||||
print("\n" + "="*70)
|
||||
print("STEP 3: INITIALIZING SKILL")
|
||||
print("="*70)
|
||||
|
||||
# Run init_skill.py
|
||||
init_script = Path("/mnt/skills/examples/skill-creator/scripts/init_skill.py")
|
||||
cmd = [sys.executable, str(init_script), skill_name, "--path", path]
|
||||
|
||||
success, output = run_command(cmd)
|
||||
if not success:
|
||||
print(f"Error: Failed to initialize skill: {output}")
|
||||
sys.exit(1)
|
||||
|
||||
print(output)
|
||||
return Path(path) / skill_name
|
||||
|
||||
def gather_script_specs(skill_path):
|
||||
"""Gather specifications for Go scripts."""
|
||||
print("\n" + "="*70)
|
||||
print("STEP 4: SPECIFY GO SCRIPTS")
|
||||
print("="*70)
|
||||
print("\nBased on the analysis, let's define the Go scripts.")
|
||||
print("Enter details for each deterministic operation.")
|
||||
print()
|
||||
|
||||
scripts = []
|
||||
while True:
|
||||
print(f"\n--- Go Script {len(scripts)+1} ---")
|
||||
print("Press Enter with empty name to finish")
|
||||
|
||||
name = input("Script name (e.g., pdf-to-images): ").strip()
|
||||
if not name:
|
||||
break
|
||||
|
||||
description = input("Description: ").strip()
|
||||
if not description:
|
||||
print("Description required")
|
||||
continue
|
||||
|
||||
input_desc = input("Input (e.g., 'PDF file path'): ").strip()
|
||||
if not input_desc:
|
||||
print("Input description required")
|
||||
continue
|
||||
|
||||
output_desc = input("Output (e.g., 'Directory of PNG files'): ").strip()
|
||||
if not output_desc:
|
||||
print("Output description required")
|
||||
continue
|
||||
|
||||
logic = input("Logic (e.g., 'Extract each page at 300 DPI'): ").strip()
|
||||
if not logic:
|
||||
print("Logic description required")
|
||||
continue
|
||||
|
||||
scripts.append({
|
||||
'name': name,
|
||||
'description': description,
|
||||
'input': input_desc,
|
||||
'output': output_desc,
|
||||
'logic': logic
|
||||
})
|
||||
|
||||
print(f"✅ Script '{name}' configured")
|
||||
|
||||
return scripts
|
||||
|
||||
def generate_go_scripts(skill_path, scripts):
|
||||
"""Generate Go scripts using generate_go_script.py."""
|
||||
if not scripts:
|
||||
print("\nNo Go scripts to generate")
|
||||
return
|
||||
|
||||
print("\n" + "="*70)
|
||||
print("STEP 5: GENERATING GO SCRIPTS")
|
||||
print("="*70)
|
||||
|
||||
generator = Path(__file__).parent / "generate_go_script.py"
|
||||
|
||||
for script in scripts:
|
||||
print(f"\nGenerating {script['name']}...")
|
||||
|
||||
cmd = [
|
||||
sys.executable, str(generator),
|
||||
"--name", script['name'],
|
||||
"--description", script['description'],
|
||||
"--input", script['input'],
|
||||
"--output", script['output'],
|
||||
"--logic", script['logic'],
|
||||
"--skill-path", str(skill_path)
|
||||
]
|
||||
|
||||
success, output = run_command(cmd)
|
||||
if not success:
|
||||
print(f"⚠️ Warning: Failed to generate {script['name']}: {output}")
|
||||
else:
|
||||
print(f"✅ Generated {script['name']}")
|
||||
|
||||
def finalize_skill_md(skill_path, examples):
|
||||
"""Add examples to SKILL.md."""
|
||||
print("\n" + "="*70)
|
||||
print("STEP 6: FINALIZING SKILL.MD")
|
||||
print("="*70)
|
||||
|
||||
skill_md = skill_path / "SKILL.md"
|
||||
content = skill_md.read_text()
|
||||
|
||||
# Add examples section
|
||||
examples_section = "\n## Example Use Cases\n\n"
|
||||
for i, example in enumerate(examples, 1):
|
||||
examples_section += f"{i}. {example}\n"
|
||||
|
||||
# Insert before ## Resources section
|
||||
if "## Resources" in content:
|
||||
content = content.replace("## Resources", examples_section + "\n## Resources")
|
||||
else:
|
||||
content += examples_section
|
||||
|
||||
skill_md.write_text(content)
|
||||
print(f"✅ Updated SKILL.md with {len(examples)} example use cases")
|
||||
|
||||
def print_next_steps(skill_path):
|
||||
"""Print next steps for the user."""
|
||||
print("\n" + "="*70)
|
||||
print("✅ SKILL CREATED SUCCESSFULLY")
|
||||
print("="*70)
|
||||
print(f"\nSkill location: {skill_path}")
|
||||
print("\nNext steps:")
|
||||
print(f"1. Review and customize SKILL.md:")
|
||||
print(f" - Complete the description in frontmatter")
|
||||
print(f" - Add detailed workflow instructions")
|
||||
print(f" - Remove TODO placeholders")
|
||||
print()
|
||||
print(f"2. Build Go scripts:")
|
||||
print(f" cd {skill_path}")
|
||||
for script_file in (skill_path / "scripts").glob("build_*.sh"):
|
||||
print(f" ./{script_file.relative_to(skill_path)}")
|
||||
print()
|
||||
print(f"3. Test the scripts:")
|
||||
for bin_file in (skill_path / "scripts" / "bin").glob("*"):
|
||||
if bin_file.is_file():
|
||||
print(f" ./scripts/bin/{bin_file.name} --help")
|
||||
print()
|
||||
print(f"4. Package the skill:")
|
||||
print(f" python3 /mnt/skills/examples/skill-creator/scripts/package_skill.py {skill_path}")
|
||||
print()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Create a Claude Code skill with workflow analysis and Go script generation',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument('skill_name', help='Name of the skill (e.g., pdf-tools)')
|
||||
parser.add_argument('--path', required=True, help='Directory where skill will be created')
|
||||
parser.add_argument('--skip-analysis', action='store_true',
|
||||
help='Skip workflow analysis (use for simple skills)')
|
||||
parser.add_argument('--skip-go', action='store_true',
|
||||
help='Skip Go script generation (create structure only)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("="*70)
|
||||
print("CLAUDE CODE SKILL CREATOR")
|
||||
print("="*70)
|
||||
print(f"\nCreating skill: {args.skill_name}")
|
||||
print(f"Location: {args.path}")
|
||||
|
||||
# Step 1: Gather examples
|
||||
examples = gather_examples()
|
||||
|
||||
# Step 2: Analyze workflow (optional)
|
||||
if not args.skip_analysis:
|
||||
analysis = analyze_workflow(examples)
|
||||
if analysis and not confirm_plan():
|
||||
print("\nAborted by user")
|
||||
sys.exit(0)
|
||||
|
||||
# Step 3: Initialize skill
|
||||
skill_path = initialize_skill(args.skill_name, args.path)
|
||||
|
||||
# Step 4-5: Generate Go scripts (optional)
|
||||
if not args.skip_go:
|
||||
scripts = gather_script_specs(skill_path)
|
||||
generate_go_scripts(skill_path, scripts)
|
||||
|
||||
# Step 6: Finalize
|
||||
finalize_skill_md(skill_path, examples)
|
||||
|
||||
# Done
|
||||
print_next_steps(skill_path)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test runner for skill scripts
|
||||
|
||||
Validates that all scripts in a skill are properly structured and executable.
|
||||
|
||||
Usage:
|
||||
test_skill_scripts.py <skill-path>
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def test_python_scripts(scripts_dir):
|
||||
"""Test Python scripts for syntax and executability."""
|
||||
results = []
|
||||
|
||||
for script in scripts_dir.glob("*.py"):
|
||||
if script.name.startswith("test_") or script.name == "__init__.py":
|
||||
continue
|
||||
|
||||
# Check if executable
|
||||
is_executable = script.stat().st_mode & 0o111
|
||||
|
||||
# Check syntax
|
||||
try:
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "py_compile", str(script)],
|
||||
capture_output=True,
|
||||
check=True
|
||||
)
|
||||
syntax_ok = True
|
||||
except subprocess.CalledProcessError:
|
||||
syntax_ok = False
|
||||
|
||||
results.append({
|
||||
'name': script.name,
|
||||
'type': 'python',
|
||||
'executable': is_executable,
|
||||
'syntax_ok': syntax_ok
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
def test_go_scripts(scripts_dir):
|
||||
"""Test Go scripts for compilation."""
|
||||
results = []
|
||||
|
||||
for script in scripts_dir.glob("*.go"):
|
||||
# Try to compile
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["go", "build", "-o", "/dev/null", str(script)],
|
||||
capture_output=True,
|
||||
check=True
|
||||
)
|
||||
compiles = True
|
||||
error = None
|
||||
except subprocess.CalledProcessError as e:
|
||||
compiles = False
|
||||
error = e.stderr.decode()
|
||||
except FileNotFoundError:
|
||||
compiles = None # Go not installed
|
||||
error = "Go compiler not found"
|
||||
|
||||
results.append({
|
||||
'name': script.name,
|
||||
'type': 'go',
|
||||
'compiles': compiles,
|
||||
'error': error
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
def test_bash_scripts(scripts_dir):
|
||||
"""Test Bash scripts for syntax."""
|
||||
results = []
|
||||
|
||||
for script in scripts_dir.glob("*.sh"):
|
||||
# Check if executable
|
||||
is_executable = script.stat().st_mode & 0o111
|
||||
|
||||
# Check syntax
|
||||
try:
|
||||
subprocess.run(
|
||||
["bash", "-n", str(script)],
|
||||
capture_output=True,
|
||||
check=True
|
||||
)
|
||||
syntax_ok = True
|
||||
except subprocess.CalledProcessError:
|
||||
syntax_ok = False
|
||||
|
||||
results.append({
|
||||
'name': script.name,
|
||||
'type': 'bash',
|
||||
'executable': is_executable,
|
||||
'syntax_ok': syntax_ok
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
def print_results(results):
|
||||
"""Print test results."""
|
||||
print("\n" + "="*70)
|
||||
print("SCRIPT TEST RESULTS")
|
||||
print("="*70 + "\n")
|
||||
|
||||
all_passed = True
|
||||
|
||||
for result in results:
|
||||
name = result['name']
|
||||
script_type = result['type']
|
||||
|
||||
print(f"{name} ({script_type})")
|
||||
|
||||
if script_type == 'python':
|
||||
if result['syntax_ok']:
|
||||
print(" ✅ Syntax: OK")
|
||||
else:
|
||||
print(" ❌ Syntax: FAILED")
|
||||
all_passed = False
|
||||
|
||||
if result['executable']:
|
||||
print(" ✅ Executable: Yes")
|
||||
else:
|
||||
print(" ⚠️ Executable: No (run: chmod +x)")
|
||||
|
||||
elif script_type == 'go':
|
||||
if result['compiles'] is None:
|
||||
print(" ⚠️ Go compiler not found (skipped)")
|
||||
elif result['compiles']:
|
||||
print(" ✅ Compiles: OK")
|
||||
else:
|
||||
print(" ❌ Compiles: FAILED")
|
||||
if result['error']:
|
||||
print(f" Error: {result['error'][:100]}")
|
||||
all_passed = False
|
||||
|
||||
elif script_type == 'bash':
|
||||
if result['syntax_ok']:
|
||||
print(" ✅ Syntax: OK")
|
||||
else:
|
||||
print(" ❌ Syntax: FAILED")
|
||||
all_passed = False
|
||||
|
||||
if result['executable']:
|
||||
print(" ✅ Executable: Yes")
|
||||
else:
|
||||
print(" ⚠️ Executable: No (run: chmod +x)")
|
||||
|
||||
print()
|
||||
|
||||
return all_passed
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Test all scripts in a skill directory'
|
||||
)
|
||||
parser.add_argument('skill_path', help='Path to skill directory')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
skill_path = Path(args.skill_path).resolve()
|
||||
scripts_dir = skill_path / 'scripts'
|
||||
|
||||
if not scripts_dir.exists():
|
||||
print(f"Error: scripts directory not found: {scripts_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Testing scripts in: {scripts_dir}")
|
||||
|
||||
results = []
|
||||
results.extend(test_python_scripts(scripts_dir))
|
||||
results.extend(test_go_scripts(scripts_dir))
|
||||
results.extend(test_bash_scripts(scripts_dir))
|
||||
|
||||
if not results:
|
||||
print("No scripts found to test")
|
||||
sys.exit(0)
|
||||
|
||||
all_passed = print_results(results)
|
||||
|
||||
if all_passed:
|
||||
print("✅ All tests passed!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("❌ Some tests failed")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user