Give a skill files and a check script it can run
Bundle references and a POSIX shell script beside SKILL.md; the script must fail loudly, and a bundled file reaches context only when read.
The review guard’s checklist is forty items; inlining it in SKILL.md would load all
forty every time the skill fires. Bundle it as a file the body names instead. A bundled
file is a file on disk — it reaches the context only when the model reads it with its
own tool. The bundled script in this example needs a POSIX shell, so on Windows run it
under Git Bash.
The layout
review-guard/
├── SKILL.md
├── scripts/
│ └── run-checks.sh
└── references/
└── checklist.mdSKILL.md names the files by path relative to the skill directory, because Pi tells the
model where the skill lives so it can resolve them:
---
name: review-guard
description: Reviews changed code against this repository's conventions and runs its own checks. Use when reviewing a diff.
---
# Review guard
1. Run `scripts/run-checks.sh` and read its output before reviewing anything.
2. Read `references/checklist.md`.The three directory names are a convention, not an enforced layout. What matters is when each kind loads: a reference is read after the skill decides the task is relevant, a script runs when told to, and an asset is used at the point of output.
The check script
#!/usr/bin/env bash
# scripts/run-checks.sh - run the target repository's own verification.
set -euo pipefail
root="${PI_REVIEW_GUARD_ROOT:-$(git rev-parse --show-toplevel)}"
cd "$root"
echo "== repository: $root =="
echo "== typecheck =="
pnpm typecheckThe script never derives the repository from its own path: dirname "$0" is the
skill’s scripts/ directory, and how many parents separate that from the repository
depends on where the skill was installed. It asks Git for the top level of the working
directory Pi was launched in, which is inherited by the bash tool, not the skill’s.
set -euo pipefail is the difference between a script that fails and one that exits
zero while skipping everything.
Expected outcome
The backing test runs the script under a POSIX shell (skipped when none is available)
with a stand-in pnpm on PATH, and drives a session for the context claims:
- a project skill at
repo/.pi/skills/review-guard/scripts/runspnpmin the repository root — three levels up, so a parent count would have picked wrong; - a shared or packaged skill outside the repository resolves the same target;
PI_REVIEW_GUARD_ROOToverrides discovery; frontmatter naming that variable has no effect, because nothing in Pi writes frontmatter into the environment;- a bundled
guard.confbeside the script is found relative to the script; - the same failing line exits non-zero with
set -euo pipefailand zero without it, and without it the script still printed its final line; - the
SKILL.mdpath is in the prompt, but a marker inreferences/checklist.mdis absent until the model reads it with its ownreadtool call.
Mechanism and limitations
Documented: docs/skills.md says to use relative paths from the skill directory,
that Pi tells the model where the skill lives, and to keep environment setup inside the
skill. Observed: skill-files.test.ts executes the script (POSIX shell required)
and inspects the prompt and tool result of a scripted session. Proposed: the size
rule of thumb — body for steps, references for detail needed after deciding, inline
anything needed on every run.
git rev-parse discovery is a convention, not a boundary: a model told to run a script
has a path into your machine, and the working directory is whatever Pi was launched in.
Frontmatter does not become environment variables. A bundled file is not magically in
context. On Windows this example needs Git Bash; the tests skip without a POSIX shell.
Understand this example
Copy this prompt into your AI tool. No code runs here.
Apply this example
Copy this prompt into your AI tool. No code runs here.