92 lines
4.4 KiB
Markdown
92 lines
4.4 KiB
Markdown
# Script Creation Standards
|
|
|
|
When building scripts for a skill, follow these standards to ensure portability and zero-friction execution. Skills must work across macOS, Linux, and Windows (native, Git Bash, and WSL).
|
|
|
|
## Python Over Bash
|
|
|
|
**Always favor Python for script logic.** Bash is not portable — it fails or behaves inconsistently on Windows (Git Bash is MSYS2-based, not a full Linux shell; WSL bash can conflict with Git Bash on PATH; PowerShell is a different language entirely). Python with `uv run` works identically on all platforms.
|
|
|
|
**Safe bash commands** — these work reliably across all environments and are fine to use directly:
|
|
|
|
- `git`, `gh` — version control and GitHub CLI
|
|
- `uv run` — Python script execution with automatic dependency handling
|
|
- `npm`, `npx`, `pnpm` — Node.js ecosystem
|
|
- `mkdir -p` — directory creation
|
|
|
|
**Everything else should be Python** — piping, `jq`, `grep`, `sed`, `awk`, `find`, `diff`, `wc`, and any non-trivial logic. Even `sed -i` behaves differently on macOS vs Linux. If it's more than a single safe command, write a Python script.
|
|
|
|
## Favor the Standard Library
|
|
|
|
Always prefer Python's standard library over external dependencies. The stdlib is pre-installed everywhere, requires no `uv run`, and has zero supply-chain risk. Common stdlib modules that cover most script needs:
|
|
|
|
- `json` — JSON parsing and output
|
|
- `pathlib` — cross-platform path handling
|
|
- `re` — pattern matching
|
|
- `argparse` — CLI interface
|
|
- `collections` — counters, defaultdicts
|
|
- `difflib` — text comparison
|
|
- `ast` — Python source analysis
|
|
- `csv`, `xml.etree` — data formats
|
|
|
|
Only pull in external dependencies when the stdlib genuinely cannot do the job (e.g., `tiktoken` for accurate token counting, `pyyaml` for YAML parsing, `jsonschema` for schema validation). **External dependencies must be confirmed with the user during the build process** — they add install-time cost, supply-chain surface, and require `uv` to be available.
|
|
|
|
## PEP 723 Inline Metadata (Required)
|
|
|
|
Every Python script MUST include a PEP 723 metadata block. For scripts with external dependencies, use the `uv run` shebang:
|
|
|
|
```python
|
|
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# requires-python = ">=3.10"
|
|
# dependencies = ["pyyaml>=6.0", "jsonschema>=4.0"]
|
|
# ///
|
|
```
|
|
|
|
For scripts using only the standard library, use a plain Python shebang but still include the metadata block:
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
# /// script
|
|
# requires-python = ">=3.10"
|
|
# ///
|
|
```
|
|
|
|
**Key rules:**
|
|
|
|
- The shebang MUST be line 1 — before the metadata block
|
|
- Always include `requires-python`
|
|
- List all external dependencies with version constraints
|
|
- Never use `requirements.txt`, `pip install`, or expect global package installs
|
|
- The shebang is a Unix convenience — cross-platform invocation relies on `uv run ./scripts/foo.py`, not `./scripts/foo.py`
|
|
|
|
## Invocation in SKILL.md
|
|
|
|
How a built skill's SKILL.md should reference its scripts:
|
|
|
|
- **All scripts:** `uv run ./scripts/foo.py {args}` — consistent invocation regardless of whether the script has external dependencies
|
|
|
|
`uv run` reads the PEP 723 metadata, silently caches dependencies in an isolated environment, and runs the script — no user prompt, no global install. Like `npx` for Python.
|
|
|
|
## Graceful Degradation
|
|
|
|
Skills may run in environments where Python or `uv` is unavailable (e.g., claude.ai web). Scripts should be the fast, reliable path — but the skill must still deliver its outcome when execution is not possible.
|
|
|
|
**Pattern:** When a script cannot execute, the LLM performs the equivalent work directly. The script's `--help` documents what it checks, making this fallback natural. Design scripts so their logic is understandable from their help output and the skill's context.
|
|
|
|
In SKILL.md, frame script steps as outcomes, not just commands:
|
|
|
|
- Good: "Validate path conventions (run `./scripts/scan-paths.py --help` for details)"
|
|
- Avoid: "Execute `uv run ./scripts/scan-paths.py`" with no context about what it does
|
|
|
|
## Script Interface Standards
|
|
|
|
- Implement `--help` via `argparse` (single source of truth for the script's API)
|
|
- Accept target path as a positional argument
|
|
- `-o` flag for output file (default to stdout)
|
|
- Diagnostics and progress to stderr
|
|
- Exit codes: 0=pass, 1=fail, 2=error
|
|
- `--verbose` flag for debugging
|
|
- Output valid JSON to stdout
|
|
- No interactive prompts, no network dependencies
|
|
- Tests in `./scripts/tests/`
|