Skip to content
Back to skills

2615 Script Patterns Catalog Ac036d19

ASecurity

Standard Python patterns for skill scripts, derived from successful implementations across the ecosystem. These patterns ensure consistency, reliability, and agentic capability. ---

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
businesspythongo

Works with

  • cli

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 2615-script-patterns-catalog_ac036d19 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2615 Script Patterns Catalog Ac036d19?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2615 Script Patterns Catalog Ac036d19
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2615-script-patterns-catalog-ac036d19/badge)](https://www.skillsdirectory.com/skills/tools-only-2615-script-patterns-catalog-ac036d19)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Script Patterns Catalog

Standard Python patterns for skill scripts, derived from successful implementations across the ecosystem. These patterns ensure consistency, reliability, and agentic capability.

---

## Core Patterns

### Pattern 1: Result Dataclass

Standard result object for all script operations.

```python
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Optional, Any

@dataclass
class Result:
    """
    Standard result object for script operations.

    Attributes:
        success: Whether the operation succeeded
        message: Human-readable summary
        data: Any output data from the operation
        errors: List of error messages
        warnings: List of warning messages
    """
    success: bool
    message: str
    data: dict = field(default_factory=dict)
    errors: List[str] = field(default_factory=list)
    warnings: List[str] = field(default_factory=list)

    def __bool__(self) -> bool:
        """Allow Result to be used in boolean context."""
        return self.success

    def to_dict(self) -> dict:
        """Serialize to dictionary for JSON output."""
        return {
            "success": self.success,
            "message": self.message,
            "data": self.data,
            "errors": self.errors,
            "warnings": self.warnings,
            "timestamp": datetime.now().isoformat()
        }

# Usage
def process_file(path: Path) -> Result:
    errors = []

    if not path.exists():
        return Result(
            success=False,
            message=f"File not found: {path}",
            errors=[f"File not found: {path}"]
        )

    # Process...
    return Result(
        success=True,
        message="Processing complete",
        data={"processed_lines": 42}
    )
```

**When to Use:** Every script that returns a status.

---

### Pattern 2: ValidationResult Class

Specialized result for validation scripts with check tracking.

```python
from dataclasses import dataclass, field
from typing import List, Tuple

@dataclass
class ValidationResult:
    """
    Result object for validation scripts with detailed check tracking.
    """
    passed: List[str] = field(default_factory=list)
    warnings: List[str] = field(default_factory=list)
    errors: List[str] = field(default_factory=list)

    def check(self, name: str, condition: bool, message: str, warning_only: bool = False):
        """Record a validation check result."""
        if condition:
            self.passed.append(f"[PASS] {name}: {message}")
        elif warning_only:
            self.warnings.append(f"[WARN] {name}: {message}")
        else:
            self.errors.append(f"[FAIL] {name}: {message}")

    @property
    def is_valid(self) -> bool:
        """True if no errors (warnings are OK)."""
        return len(self.errors) == 0

    @property
    def summary(self) -> str:
        """Human-readable summary."""
        total = len(self.passed) + len(self.warnings) + len(self.errors)
        return f"{len(self.passed)}/{total} checks passed, {len(self.warnings)} warnings, {len(self.errors)} errors"

    def format_report(self, title: str = "Validation Report") -> str:
        """Generate formatted report."""
        lines = [
            "=" * 60,
            title.center(60),
            "=" * 60,
            "",
            f"Summary: {self.summary}",
            ""
        ]

        if self.errors:
            lines.append("ERRORS:")
            lines.extend(f"  {e}" for e in self.errors)
            lines.append("")

        if self.warnings:
            lines.append("WARNINGS:")
            lines.extend(f"  {w}" for w in self.warnings)
            lines.append("")

        if self.passed:
            lines.append("PASSED:")
            lines.extend(f"  {p}" for p in self.passed)

        lines.append("=" * 60)
        return "\n".join(lines)

# Usage
def validate_skill(path: Path) -> ValidationResult:
    result = ValidationResult()

    result.check(
        "file_exists",
        path.exists(),
        f"SKILL.md exists at {path}"
    )

    content = path.read_text() if path.exists() else ""

    result.check(
        "has_frontmatter",
        content.startswith("---"),
        "File has YAML frontmatter"
    )

    result.check(
        "has_triggers",
        "## Triggers" in content or "trigger" in content.lower(),
        "File has trigger phrases",
        warning_only=True  # Warning, not error
    )

    return result
```

**When to Use:** Validation scripts that check multiple criteria.

---

### Pattern 3: Argparse with Subcommands

Modular CLI interface for multi-operation scripts.

```python
import argparse
import sys

def cmd_init(args):
    """Handle 'init' subcommand."""
    print(f"Initializing: {args.name}")
    return 0

def cmd_run(args):
    """Handle 'run' subcommand."""
    print(f"Running with verbose={args.verbose}")
    return 0

def cmd_status(args):
    """Handle 'status' subcommand."""
    print("Status: OK")
    return 0

def main():
    parser = argparse.ArgumentParser(
        description="Multi-command script example",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  %(prog)s init "My Project"
  %(prog)s run --verbose
  %(prog)s status
        """
    )

    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # Subcommand: init
    init_parser = subparsers.add_parser("init", help="Initialize a new project")
    init_parser.add_argument("name", help="Project name")
    init_parser.add_argument("--description", "-d", help="Project description")

    # Subcommand: run
    run_parser = subparsers.add_parser("run", help="Run the process")
    run_parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
    run_parser.add_argument("--dry-run", action="store_true", help="Don't make changes")

    # Subcommand: status
    status_parser = subparsers.add_parser("status", help="Show current status")

    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        sys.exit(1)

    # Dispatch to handler
    handlers = {
        "init": cmd_init,
        "run": cmd_run,
        "status": cmd_status,
    }

    exit_code = handlers[args.command](args)
    sys.exit(exit_code)

if __name__ == "__main__":
    main()
```

**When to Use:** Scripts with multiple distinct operations (state management, trackers).

---

### Pattern 4: Simple Argparse

For single-purpose scripts.

```python
import argparse
import sys
from pathlib import Path

def main():
    parser = argparse.ArgumentParser(
        description="Validate a skill directory"
    )

    # Positional argument
    parser.add_argument(
        "path",
        type=Path,
        help="Path to skill directory"
    )

    # Optional flags
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Show detailed output"
    )

    parser.add_argument(
        "--json",
        action="store_true",
        help="Output as JSON"
    )

    parser.add_argument(
        "--strict",
        action="store_true",
        help="Treat warnings as errors"
    )

    args = parser.parse_args()

    # Validate input
    if not args.path.exists():
        print(f"Error: Path not found: {args.path}", file=sys.stderr)
        sys.exit(2)

    # Process
    result = validate(args.path, verbose=args.verbose)

    # Output
    if args.json:
        print(json.dumps(result.to_dict()))
    else:
        print(result.format_report())

    sys.exit(0 if result.is_valid else 1)

if __name__ == "__main__":
    main()
```

**When to Use:** Single-purpose scripts (validation, generation, transformation).

---

### Pattern 5: JSON State Persistence

For tracking state across sessions.

```python
from pathlib import Path
from datetime import datetime
import json
import os

def get_state_dir(skill_name: str) -> Path:
    """Get XDG-compliant state directory."""
    xdg_cache = os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache")
    return Path(xdg_cache) / skill_name

def get_state_path(skill_name: str, project_name: str = "default") -> Path:
    """Get state file path for a project."""
    safe_name = project_name.lower().replace(" ", "-").replace("/", "-")
    return get_state_dir(skill_name) / f"{safe_name}.json"

def load_state(path: Path) -> dict:
    """Load state with graceful fallback on corruption."""
    if not path.exists():
        return {
            "version": "1.0",
            "created_at": datetime.now().isoformat(),
            "data": {}
        }

    try:
        return json.loads(path.read_text())
    except json.JSONDecodeError:
        # Backup corrupted file
        backup = path.with_suffix(".json.bak")
        path.rename(backup)
        return {
            "version": "1.0",
            "created_at": datetime.now().isoformat(),
            "data": {},
            "recovered_from": str(backup)
        }

def save_state(path: Path, state: dict) -> None:
    """Save state atomically."""
    path.parent.mkdir(parents=True, exist_ok=True)
    state["updated_at"] = datetime.now().isoformat()

    # Write to temp file first (atomic save)
    temp_path = path.with_suffix(".json.tmp")
    temp_path.write_text(json.dumps(state, indent=2))
    temp_path.rename(path)

# Usage
def main():
    state_path = get_state_path("my-skill", "my-project")
    state = load_state(state_path)

    # Modify state
    state["data"]["last_run"] = datetime.now().isoformat()
    state["data"]["runs"] = state["data"].get("runs", 0) + 1

    save_state(state_path, state)
    print(f"State saved. Total runs: {state['data']['runs']}")
```

**When to Use:** Progress tracking, multi-session workflows, caching.

---

### Pattern 6: Graceful Dependency Fallback

Handle optional dependencies gracefully.

```python
# YAML parsing with fallback
def parse_yaml(text: str) -> dict:
    """Parse YAML with graceful fallback if PyYAML not installed."""
    try:
        import yaml
        return yaml.safe_load(text)
    except ImportError:
        # Fallback: basic key-value parsing for simple YAML
        result = {}
        current_key = None
        for line in text.split('\n'):
            stripped = line.strip()
            if not stripped or stripped.startswith('#'):
                continue
            if ':' in line and not line.startswith(' '):
                key, value = line.split(':', 1)
                key = key.strip()
                value = value.strip()
                if value:
                    # Remove quotes if present
                    if (value.startswith('"') and value.endswith('"')) or \
                       (value.startswith("'") and value.endswith("'")):
                        value = value[1:-1]
                    result[key] = value
                else:
                    result[key] = {}
                    current_key = key
        return result

# Rich output with fallback
def print_styled(message: str, style: str = "normal"):
    """Print with optional rich styling."""
    try:
        from rich.console import Console
        console = Console()
        styles = {
            "success": "[green]",
            "error": "[red]",
            "warning": "[yellow]",
            "normal": ""
        }
        prefix = styles.get(style, "")
        console.print(f"{prefix}{message}")
    except ImportError:
        # Fallback: plain output with emoji
        prefixes = {
            "success": "[OK] ",
            "error": "[ERROR] ",
            "warning": "[WARN] ",
            "normal": ""
        }
        print(f"{prefixes.get(style, '')}{message}")
```

**When to Use:** When using non-stdlib libraries, always provide fallback.

---

### Pattern 7: Exit Code Conventions

Consistent exit codes for script chaining.

```python
import sys
from enum import IntEnum

class ExitCode(IntEnum):
    """Standard exit codes for skill scripts."""
    SUCCESS = 0
    GENERAL_ERROR = 1
    INVALID_ARGUMENTS = 2
    FILE_NOT_FOUND = 3
    PERMISSION_DENIED = 4
    VALIDATION_FAILED = 10
    VERIFICATION_FAILED = 11
    DEPENDENCY_ERROR = 20
    NETWORK_ERROR = 21
    TIMEOUT = 30

def main():
    try:
        result = process()

        if not result.success:
            if "validation" in result.message.lower():
                sys.exit(ExitCode.VALIDATION_FAILED)
            sys.exit(ExitCode.GENERAL_ERROR)

        sys.exit(ExitCode.SUCCESS)

    except FileNotFoundError:
        sys.exit(ExitCode.FILE_NOT_FOUND)
    except PermissionError:
        sys.exit(ExitCode.PERMISSION_DENIED)
    except TimeoutError:
        sys.exit(ExitCode.TIMEOUT)
    except Exception as e:
        print(f"Unexpected error: {e}", file=sys.stderr)
        sys.exit(ExitCode.GENERAL_ERROR)
```

**When to Use:** All scripts should use explicit exit codes.

---

### Pattern 8: Progress Visualization

ASCII-based progress display.

```python
def render_progress_bar(current: int, total: int, width: int = 40) -> str:
    """Render ASCII progress bar."""
    if total == 0:
        return f"[{'?' * width}] 0%"

    ratio = current / total
    filled = int(ratio * width)
    empty = width - filled

    bar = "=" * filled + "-" * empty
    percent = ratio * 100

    return f"[{bar}] {percent:.1f}% ({current}/{total})"

def render_status_icon(status: str) -> str:
    """Render status as ASCII icon."""
    icons = {
        "pending": "[ ]",
        "in_progress": "[>]",
        "completed": "[x]",
        "verified": "[V]",
        "failed": "[!]",
        "skipped": "[-]",
    }
    return icons.get(status, "[?]")

def render_tree(items: list, indent: int = 0) -> str:
    """Render hierarchical tree structure."""
    lines = []
    for i, item in enumerate(items):
        is_last = i == len(items) - 1
        prefix = "  " * indent + ("'" if is_last else "|") + "-- "
        status = render_status_icon(item.get("status", "pending"))
        lines.append(f"{prefix}{status} {item['name']}")

        if item.get("children"):
            child_lines = render_tree(item["children"], indent + 1)
            lines.append(child_lines)

    return "\n".join(lines)

# Usage
print(render_progress_bar(7, 10))
# Output: [============================------------] 70.0% (7/10)

print(render_tree([
    {"name": "Phase 1", "status": "completed", "children": [
        {"name": "Step 1.1", "status": "completed"},
        {"name": "Step 1.2", "status": "completed"},
    ]},
    {"name": "Phase 2", "status": "in_progress", "children": [
        {"name": "Step 2.1", "status": "in_progress"},
        {"name": "Step 2.2", "status": "pending"},
    ]},
]))
```

**When to Use:** Progress tracking, status dashboards, visual feedback.

---

### Pattern 9: Self-Verification

Scripts that verify their own outputs.

```python
def execute_with_verification(input_data, verify_func):
    """
    Execute an operation and verify the result.

    Args:
        input_data: Input to process
        verify_func: Function that returns (is_valid, reason)

    Returns:
        Result with success based on verification
    """
    try:
        # Execute
        output = process(input_data)

        # Verify
        is_valid, reason = verify_func(output)

        if not is_valid:
            return Result(
                success=False,
                message=f"Verification failed: {reason}",
                data={"output": output, "verification": reason},
                errors=[reason]
            )

        return Result(
            success=True,
            message="Operation completed and verified",
            data={"output": output, "verified": True}
        )

    except Exception as e:
        return Result(
            success=False,
            message=f"Execution failed: {e}",
            errors=[str(e)]
        )

# Example verification functions
def verify_json_output(output):
    """Verify output is valid JSON."""
    try:
        json.loads(output)
        return True, "Valid JSON"
    except json.JSONDecodeError as e:
        return False, f"Invalid JSON: {e}"

def verify_file_exists(path):
    """Verify file was created."""
    if Path(path).exists():
        return True, f"File exists: {path}"
    return False, f"File not created: {path}"

def verify_non_empty(output):
    """Verify output is non-empty."""
    if output and len(str(output).strip()) > 0:
        return True, "Output is non-empty"
    return False, "Output is empty"
```

**When to Use:** Any script that produces output that can be verified.

---

## Category Templates

### Validation Script Template

```python
#!/usr/bin/env python3
"""
validate_<target>.py - Validates <target> against <standard>

Usage:
    python validate_<target>.py <path>
    python validate_<target>.py <path> --strict
"""

import argparse
import sys
from pathlib import Path

# ... include ValidationResult class from Pattern 2 ...

def validate(path: Path, strict: bool = False) -> ValidationResult:
    """Validate the target."""
    result = ValidationResult()

    # Add your checks here
    result.check("example", True, "Example check passed")

    return result

def main():
    parser = argparse.ArgumentParser(description="Validate <target>")
    parser.add_argument("path", type=Path, help="Path to validate")
    parser.add_argument("--strict", action="store_true", help="Treat warnings as errors")
    parser.add_argument("--json", action="store_true", help="Output as JSON")

    args = parser.parse_args()

    result = validate(args.path, args.strict)

    if args.json:
        import json
        print(json.dumps({"valid": result.is_valid, "errors": result.errors, "warnings": result.warnings}))
    else:
        print(result.format_report())

    sys.exit(0 if result.is_valid else 1)

if __name__ == "__main__":
    main()
```

### State Management Script Template

```python
#!/usr/bin/env python3
"""
<name>_tracker.py - Track progress for <purpose>

Usage:
    python <name>_tracker.py init "Project Name"
    python <name>_tracker.py add-item "Item description"
    python <name>_tracker.py update <id> --status completed
    python <name>_tracker.py status
"""

import argparse
import json
import sys
from datetime import datetime
from pathlib import Path

# ... include state management functions from Pattern 5 ...

def cmd_init(args):
    """Initialize a new project."""
    # Implementation
    pass

def cmd_add(args):
    """Add an item."""
    # Implementation
    pass

def cmd_update(args):
    """Update an item."""
    # Implementation
    pass

def cmd_status(args):
    """Show current status."""
    # Implementation
    pass

def main():
    parser = argparse.ArgumentParser(description="Track <purpose>")
    subparsers = parser.add_subparsers(dest="command")

    # Add subparsers...

    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        sys.exit(1)

    handlers = {"init": cmd_init, "add": cmd_add, "update": cmd_update, "status": cmd_status}
    sys.exit(handlers[args.command](args))

if __name__ == "__main__":
    main()
```

### Generation Script Template

```python
#!/usr/bin/env python3
"""
generate_<artifact>.py - Generate <artifact> from <input>

Usage:
    python generate_<artifact>.py <input> --output <output>
"""

import argparse
import sys
from pathlib import Path
from string import Template

TEMPLATE = """
# Generated ${name}
# Created: ${timestamp}

${content}
"""

def generate(input_path: Path, output_path: Path) -> bool:
    """Generate artifact from input."""
    from datetime import datetime

    # Read input
    data = input_path.read_text()

    # Transform using template
    template = Template(TEMPLATE)
    result = template.substitute(
        name=input_path.stem,
        timestamp=datetime.now().isoformat(),
        content=data
    )

    # Write output
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(result)

    # Verify
    if not output_path.exists():
        return False

    return True

def main():
    parser = argparse.ArgumentParser(description="Generate <artifact>")
    parser.add_argument("input", type=Path, help="Input file")
    parser.add_argument("--output", "-o", type=Path, required=True, help="Output file")

    args = parser.parse_args()

    if not args.input.exists():
        print(f"Error: Input not found: {args.input}", file=sys.stderr)
        sys.exit(2)

    success = generate(args.input, args.output)

    if success:
        print(f"Generated: {args.output}")
        sys.exit(0)
    else:
        print("Generation failed", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()
```

---

## Quick Reference

| Pattern | Use Case | Key Imports |
|---------|----------|-------------|
| Result dataclass | Return values | `dataclasses`, `typing` |
| ValidationResult | Multi-check validation | `dataclasses`, `typing` |
| Argparse subcommands | Multi-operation scripts | `argparse` |
| Simple argparse | Single-purpose scripts | `argparse` |
| JSON state | State persistence | `json`, `pathlib` |
| Graceful fallback | Optional dependencies | try/except ImportError |
| Exit codes | Script chaining | `sys`, `enum` |
| Progress bars | Visual feedback | (stdlib only) |
| Self-verification | Autonomous operation | (pattern, not import) |

---

## Related References

- [Script Integration Framework](script-integration-framework.md) - When to use scripts
- [Script Template](../assets/templates/script-template.py) - Full starter template

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.