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:
- https://docs.astral.sh/uv/guides/scripts/ - Official UV scripts guide with current syntax
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
# /// scriptcomment block dependenciesfield present (even if empty)- Comment block closed with
# /// - No syntax errors in TOML metadata
Best practices:
- Shebang uses
-Sflag for executables - Dependencies pinned appropriately (exact versions for reproducibility, ranges for flexibility)
- Script made executable with
chmod +xif 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:
- https://docs.astral.sh/uv/guides/scripts/ - Current syntax and features
- https://peps.python.org/pep-0723/ - PEP 723 specification for inline metadata
Related patterns:
- Fetch box-factory:hooks-design skill for hook lifecycle and execution patterns
- UV project documentation for multi-file Python projects