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.
Creates self-contained Python scripts with inline dependencies using PEP 723 metadata for UV-managed projects. Use when creating executable hooks, standalone utilities, or one-off automation tasks that need isolated dependency management without separate environment setup.
/plugin marketplace add racurry/neat-little-package/plugin install box-factory@neat-little-packageThis skill inherits all available tools. When active, it can use any tool Claude has access to.
This skill documents UV-specific patterns for single-file Python scripts with inline dependency metadata. For general Python knowledge, Claude relies on base training.
Single-file Python scripts with embedded dependencies - UV scripts use PEP 723 inline metadata to declare dependencies directly in the file, enabling self-contained execution without separate requirements.txt or environment setup.
Key principles:
| If you need to... | Go to... |
|---|---|
| Create a UV script from scratch | Inline Metadata Format |
| Make a script executable | Shebang Pattern |
| Create a Claude Code hook script | Common Pattern |
| Understand when to use UV scripts | When to Use |
| Troubleshoot UV script failures | Critical Gotchas |
| Validate before completion | Quality Checklist |
Fetch when you need current UV script syntax:
Deep dive: Official Documentation - Current UV syntax. Traverse when: creating first UV script, syntax errors, need current PEP 723 specification. Skip when: familiar with inline metadata format.
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"]
# ///
Run with uv run:
uv run script.py
UV automatically:
Important behavior: When inline metadata exists, project dependencies are ignored (no need for --no-project).
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.
Use UV scripts for:
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:
Deep dive: When to Use UV Scripts - Decision framework for UV scripts vs projects. Traverse when: choosing between script and project structure, unsure if UV script fits use case. Skip when: clear hook or utility script use case.
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 = []
# ///
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.
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
Deep dive: Critical Gotchas - Common failures with solutions. Traverse when: UV script fails, dependency errors, shebang not working, lockfile issues. Skip when: script runs successfully, familiar with UV patterns.
Before finalizing a UV script:
Format (official requirements):
# /// script comment blockdependencies field present (even if empty)# ///Best practices:
-S flag for executableschmod +x if intended for direct executionTemplate 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"
}
]
}
]
}
Use underscores, not hyphens, in Python script names.
✅ format_code.py (importable, testable)
✅ lint_on_write.py (importable, testable)
❌ format-code.py (cannot import - hyphen is minus operator)
❌ lint-on-write.py (cannot import - hyphen is minus operator)
Why this matters: Hyphenated Python files require importlib.util workarounds for testing. Using underscores allows normal import statements and pytest discovery.
Note: Shell scripts (.sh) can use hyphens freely - this only affects Python files.
Official UV documentation:
Related patterns: