2026-05-21 10:29:13 +00:00

439 lines
13 KiB
Python
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()