Files
2025-11-30 08:48:43 +08:00

6.0 KiB

name, description
name description
box-factory-uv-scripts UV-specific patterns for single-file Python scripts using inline metadata (PEP 723). Use when creating Python hooks, standalone utilities, or executable scripts in UV-managed projects.

UV Scripts Skill

This skill documents UV-specific patterns for single-file Python scripts with inline dependency metadata. For general Python knowledge, Claude relies on base training.

Required Reading Before Creating UV Scripts

Fetch this documentation with WebFetch:

Core Understanding

What UV scripts solve: Traditional Python scripts require separate environment setup or requirements.txt files. UV's inline metadata format (PEP 723) embeds dependencies directly in the script, enabling automatic environment creation on-demand.

Key insight: UV scripts are ideal for Claude Code hooks and standalone utilities because they're self-contained and executable without external configuration.

Inline Metadata Format (Official Specification)

Dependencies declared in TOML comment block at top of file:

# /// script
# dependencies = [
#   "requests<3",
#   "rich",
# ]
# ///

import requests
import rich

# Your script logic here

Critical requirement: The dependencies field MUST be provided even if empty:

# /// script
# dependencies = []
# ///

Optional Python version requirement:

# /// script
# requires-python = ">=3.12"
# dependencies = ["requests<3"]
# ///

Execution Pattern (Official Specification)

Run with uv run:

uv run script.py

UV automatically:

  • Parses inline metadata
  • Creates isolated environment
  • Installs dependencies
  • Executes script

Important behavior: When inline metadata exists, project dependencies are ignored (no need for --no-project).

Shebang Pattern for Executables (Best Practice)

For standalone executable scripts (common for hooks):

#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["rich"]
# ///

import rich

if __name__ == "__main__":
    rich.print("[green]Hello from UV script![/green]")

Make executable:

chmod +x script.py
./script.py  # Runs directly without `uv run` prefix

Why this works: Shebang enables PATH-based execution and simplifies hook scripts.

When to Use UV Scripts (Best Practices)

Use UV scripts for:

  • Claude Code hooks (deterministic execution, self-contained)
  • Standalone utilities (formatting, linting, code generation)
  • Shareable scripts (no separate environment setup needed)
  • One-off automation tasks

Example use case (hook script):

#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["ruff"]
# ///

import subprocess
import sys

result = subprocess.run(["ruff", "check", "."], capture_output=True)
sys.exit(result.returncode)

Don't use UV scripts for:

  • Large applications (use proper UV projects instead)
  • Scripts with many files (multi-file projects need project structure)
  • Development requiring lockfile management (scripts don't auto-lock)

Critical Gotchas (Best Practices)

Gotcha #1: Empty Dependencies Must Be Explicit

Problem: Omitting dependencies field causes UV to fail.

# /// script
# requires-python = ">=3.11"
# ///
# ERROR: Missing required 'dependencies' field

Solution: Always include dependencies, even if empty:

# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///

Gotcha #2: Script Locking Requires Explicit Command

Problem: Unlike UV projects, scripts don't auto-generate lockfiles.

Solution: Explicitly lock if reproducibility needed:

uv lock --script script.py

This creates script.lock alongside script.py.

Gotcha #3: Shebang Requires -S Flag

Problem: Standard shebang won't work with multi-word commands.

#!/usr/bin/env uv run --script
# ERROR: env can't handle multiple arguments

Solution: Use -S flag:

#!/usr/bin/env -S uv run --script
# SUCCESS: Splits arguments correctly

Quality Checklist

Before finalizing a UV script:

Format (official requirements):

  • Script starts with # /// script comment block
  • dependencies field present (even if empty)
  • Comment block closed with # ///
  • No syntax errors in TOML metadata

Best practices:

  • Shebang uses -S flag for executables
  • Dependencies pinned appropriately (exact versions for reproducibility, ranges for flexibility)
  • Script made executable with chmod +x if intended for direct execution
  • Hook scripts return proper exit codes (0 = success, non-zero = failure)

Common Pattern: Claude Code Hook Script

Template for hook scripts:

#!/usr/bin/env -S uv run --script
# /// script
# dependencies = [
#   "tool-name>=1.0.0",
# ]
# ///

import subprocess
import sys
import os

def main():
    """Hook logic here."""
    # Get file paths from environment
    file_paths = os.environ.get("CLAUDE_FILE_PATHS", "").split()

    if not file_paths:
        sys.exit(0)  # Nothing to process

    # Run tool
    result = subprocess.run(
        ["tool-name", *file_paths],
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        print(result.stderr, file=sys.stderr)
        sys.exit(2)  # Block operation

    sys.exit(0)  # Success

if __name__ == "__main__":
    main()

Hook registration in hooks.json:

{
  "PostToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "command",
          "command": "./hooks/format-code.py"
        }
      ]
    }
  ]
}

Documentation References

Official UV documentation:

Related patterns:

  • Fetch box-factory:hooks-design skill for hook lifecycle and execution patterns
  • UV project documentation for multi-file Python projects