| name | create-script |
| description | Author a workspace script in the Tau monorepo following established conventions for bash and TypeScript scripts (shebang, set -euo pipefail, header comment template, env-var validation, REPO_ROOT pattern, location decision tree, chmod, Nx wiring). Use when creating a new script under scripts/, apps/*/scripts/, packages/*/scripts/, or a skill-bundled script, or when the user asks to add a script, helper, CLI tool, smoke test, release helper, or operator runbook. |
| disable-model-invocation | true |
Create a workspace script
Conventions for bash and TypeScript scripts in the Tau monorepo. Pick the right location, copy the template, fill in the header, chmod +x, done.
Quick decision: where does it live?
Rule of thumb: workspace-wide one-shot ops → scripts/; gated CI checks → scripts/src/ + Nx target; build-output assertions → next to the thing they assert about (apps/<app>/scripts/).
Bash template
Copy this for every new *.sh and fill in the header. The header is non-optional — it is the script's contract.
#!/usr/bin/env bash
set -euo pipefail
: "${FOO:?set FOO}"
BAR="${BAR:-default}"
command -v gh >/dev/null || { echo "ERROR: gh CLI required" >&2; exit 3; }
REPO_ROOT="$(git rev-parse --show-toplevel)"
echo "✓ done"
See examples.md for full copy-pastable templates (workspace bash, per-project bash, TS Nx target).
TypeScript template
Use plain .ts for workspace scripts/src/ (Nx-managed) and .mts for per-project scripts that need an explicit ESM marker. Both run under Node 22+ with native TS support (via nx's node invocation or NX_PREFER_NODE_STRIP_TYPES=true).
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import process from 'node:process';
const repoRoot = resolve(import.meta.dirname, '../..');
const main = (): void => {
};
try {
main();
} catch (error) {
console.error('script failed:', error);
process.exit(1);
}
Node native APIs only for scripts/ unless a dep already lives in scripts/package.json. If you need YAML/zod/etc., add to scripts/package.json first.
Conventions checklist (bash)
Conventions checklist (TypeScript)
Wiring a TS script as an Nx target
For workspace-wide CI gates, add to scripts/project.json:
"targets": {
"validate-<thing>": {
"executor": "nx:run-commands",
"options": { "command": "node scripts/src/validate-<thing>.ts" }
}
}
Then invoke as pnpm nx run scripts:validate-<thing>.
Anti-patterns
| Smell | Why it bites | Fix |
|---|
set -e alone | Doesn't catch unset vars or piped failures | Use set -euo pipefail |
| No header block | Script becomes mystery meat after 3 months | Always include purpose + env vars + usage + exit codes |
Hard-coded absolute paths (/Users/..., /home/...) | Breaks on every other machine | Use REPO_ROOT="$(git rev-parse --show-toplevel)" |
cd $(dirname $0)/.. without quotes | Breaks on paths with spaces | Quote: cd "$(dirname "${BASH_SOURCE[0]}")/.." — or just use REPO_ROOT |
echo "$VAR" for required vars without check | Silent empty-string propagation | Validate with : "${VAR:?msg}" |
echo -e for ANSI/escapes | Not portable to all sh impls | Use printf '%s\n' |
Forgotten chmod +x | Script fails with "permission denied" | Run chmod +x at creation time |
| Emoji in output | Inconsistent terminal rendering, breaks log greps | Stick to → / ✓ ASCII-only markers |
Adding npm deps inline at top of .mts | Lockfile drift, not installed in CI | Add to relevant package.json first |
Additional Resources