npx skills add ...
npx skills add desplega-ai/ai-toolbox --skill script-builder
Generate durable, re-runnable scripts from session intent — validation scripts (PASS/FAIL contract) AND gather/bulk scripts (many API/tool calls in code, one derived summary out). Supports TypeScript, Python, Bash with auto-detection, enforces context-optimal output, and auto-documents scripts in CLAUDE.md. Triggers on "turn this into a script", "I want to test/validate X end-to-end", "wrap this in a re-runnable script" — and, critically, whenever you are about to make (or just made) ~10+ similar tool/API calls or any bulk fan-out over a list: that mechanical middle belongs in a script, not in context.
npx skills add desplega-ai/ai-toolbox --skill script-builder
You are converting session intent into a durable, re-runnable script committed to the target project's scripts/ directory. Two script classes share one principle — only the derived answer re-enters context; raw payloads never do:
check-*, e2e-*, smoke-*, …): a single PASS/FAIL line + /tmp log path on success, full verbose output redirected to a timestamped log file.gather-*, bulk-*): replace N similar tool/API calls with one script that loops, filters, and aggregates in code — stdout is one compact summary block (JSON or table), raw responses go to the /tmp log. Rubric (from production code-mode data): past ~10 items or any fan-out over a list, a script beats individual tool calls by ~100x on context and roughly halves end-to-end cost. Offer the script before the fan-out happens when you can see it coming, not just retrospectively.Both classes get an <important if> block in CLAUDE.md/AGENTS.md so future agents discover the script when the intent recurs — durable scripts are reusable agent memory: import, don't re-derive.
Schema discovery happens in code, too: when scripting against an unfamiliar API, grep/filter its OpenAPI spec or typed client programmatically to find the few relevant endpoints — never paste the full schema into context.
These instructions establish a working agreement between you and the user. The key principles are:
AskUserQuestion is your primary communication tool - Whenever you need to ask the user anything (clarifications, preferences, decisions, confirmations), use the AskUserQuestion tool. Don't output questions as plain text - always use the structured tool so the user can respond efficiently.
Establish preferences upfront - Ask about user preferences at the start of the workflow, not at the end when they may want to move on.
Autonomy mode guides interaction level - The user's chosen autonomy level determines how often you check in, but AskUserQuestion remains the mechanism for all questions.
This skill activates when:
/script-builder command**OPTIONAL SUB-SKILL:** desplega:script-builderplanning, qa, or verifying flow needs a re-runnable validation script that doesn't yet existAdapt your behavior based on the autonomy mode:
| Mode | Behavior |
|---|---|
| Autopilot | Detect mode, draft, syntax-check, document, and (if escalation tier) run + iterate without intermediate confirmations. Pause only at hard blockers or destructive side-effects. |
| Critical (Default) | Confirm at each tier boundary (after draft, before doc edit, before run). Use AskUserQuestion for fix application during iterate loop. |
| Verbose | Confirm before each step. Show every diff. Walk through detection reasoning out loud. |
The autonomy mode is passed by the invoking command. If not specified, default to Critical.
Decide silently — do not ask the user "which mode?". Use the following heuristics:
curl/fetch calls, bun run/python/pytest, database queries, agent-browser actions, repeated grep/log inspection of a single endpoint or table. If ≥2 such actions targeting the same area exist → retrospective mode. Separately, if the history (or the task ahead) shows the same call shape repeated ~10+ times or a fan-out over a list → gather-script mode: propose replacing the repetition with one gather-* script before continuing.Retrospective first action: summarize the observed activity back to the user as plain text (≤6 lines: what was probed, against what, with what success signal), then go to Step 3 with a confirmation prompt instead of Q&A.
Forward first action: skip directly to Step 3's Q&A.
Resolve the scripts directory in this order:
CLAUDE.md for a <!-- script-builder:dir=<path> --> marker. If present, use that path.scripts/ exists at the repo root, use it.scripts/ (Recommended) | custom path | skip dir persistence for this run.Edge case — directory absent: if the resolved path doesn't exist, skip the dedup scan, print a one-line note (No existing scripts directory at <path> — proceeding to intent gathering), and remember to optionally offer to create the directory before Step 5.
When the directory exists:
CLAUDE.md/AGENTS.md for <important if="..."> blocks that point at scripts in this directory and capture the trigger phrases.auth, health, webhook).Reuse <name> as-is | Extend <name> | Generate new anyway.Never silently skip dedup on borderline matches — surface and let the user decide. The Extend branch appends a sub-command/flag to the existing script (do not create a new file); document the new flag in the script's header comment and the existing <important if> block.
Both modes converge on the same internal intent structure: { what, success_signal, failure_signal, inputs, env, side_effects }.
Forward mode — use AskUserQuestion (one or two questions, not five):
| Question | Options |
|---|---|
| "What are we validating, and what's the success signal?" | [Free text — collect what + success signal in one answer] |
| "Any required env vars, inputs, or side-effects to flag (e.g., writes to prod, costs money)?" | [Free text — optional; skip if forward intent already specified them] |
Retrospective mode — present the summary from Step 1 and use AskUserQuestion with: That's the flow | Close but fix X | Start over. On Close but fix X, ask for the correction inline; on Start over, fall through to forward-mode Q&A.
Persist the resolved intent in working memory (do not write to disk yet).
Priority order — first match wins, with override paths:
package.json + tsconfig.json exist.
bun.lock or bunfig.toml is present.tsx appears in package.json devDependencies.tsc build).pyproject.toml or uv.lock or requirements.txt exists.
uv.lock exists or pyproject.toml has [tool.uv] configured (sets {{UV_METADATA}}).{{UV_METADATA}} substituted with empty string).src//top-level and ask AskUserQuestion tiebreaker with the dominant one Recommended.Cargo.toml / go.mod only → fall back to bash (note: TS/Python/Bash only in v1).Task-driven override: if the gathered intent is clearly shell-y ("verify three docker containers respond", "tail this log file for an error"), propose bash even in a TS/Python project.
Confirm via AskUserQuestion with the detected language as the first option (Recommended). Skip in Autopilot.
cc-plugin/base/skills/script-builder/templates/{typescript.ts.tmpl|python.py.tmpl|bash.sh.tmpl}.{{SCRIPT_NAME}} ← proposed file-name stem (see naming table).{{WHAT}} / {{WHEN}} / {{ENV}} / {{EXAMPLE}} ← intent fields.{{UV_METADATA}} ← per Step 4.{{TEST_BODY}} from the intent. Keep it minimal: a single concrete probe + assertion, not a battery. Re-read the templates' README (templates/README.md) for the contract — the body must respect the PASS/FAIL surface. Throw/raise/exit 1 on failure; let the template's outer try/trap convert it into the FAIL line.
Gather-class bodies adapt the same template: the loop/aggregation replaces the probe, raw per-item responses go to the log only, and the final console.log/print emits one compact summary (JSON or aligned table) instead of the PASS line — exit non-zero only on operational failure (auth, network), not on "found problems" (problems ARE the output).custom name as the alternative.Naming conventions (advisory — skill proposes, user overrides):
| Prefix | When | Example |
|---|---|---|
e2e-* | End-to-end flows across multiple components | e2e-auth-flow.ts |
check-* | Idempotent single-probe verifications | check-db-boundary.sh |
smoke-* | Minimal-viability post-deploy checks | smoke-prod-api.ts |
measure-* | Performance / size / token measurements | measure-tool-tokens.ts |
seed-* / generate-* | Data seeding or artifact generation (rare for validation) | seed-api-keys.sh |
gather-* / bulk-* | Bulk data-gathering or bulk mutation replacing N tool calls; stdout = one summary block, not PASS/FAIL | gather-workflow-health.ts |
If the intent doesn't match any prefix cleanly, propose a free-form name like validate-<area>.<ext>.
chmod +x).Run the appropriate checker:
| Language | Checker (in priority order) |
|---|---|
| TypeScript | bunx tsc --noEmit <file> if Bun present, else npx tsc --noEmit <file> |
| Python | python3 -m py_compile <file> (always) + ruff check <file> if ruff is on PATH |
| Bash | shellcheck <file> if available, else bash -n <file> |
On success: log a single line (syntax check OK) and proceed to Step 7.
On failure:
Edit you'd apply).Apply fix | Investigate | Stop.
Auto-edit the target project's CLAUDE.md and/or AGENTS.md so future agents discover this script when the matching intent recurs.
Block template (the literal markdown the skill emits):
Behavior:
CLAUDE.md and AGENTS.md if both exist; edit only what exists. Never create either file from scratch — if neither exists, print a one-line note (No CLAUDE.md or AGENTS.md found — script generated but not documented) and skip this step.Test|Testing|Validation|Scripts (case-insensitive). If found, append the new block within/after that section. Otherwise, append a new ## Scripts for testing & validation section near the end of the file but before any heading matching License|Acknowledg|Maintain|Contributors.scripts/<name> already exists (i.e., a prior /script-builder run for the same script name), update it in place — replace the entire <important if=...>...</important> block, do not append a duplicate.<!-- script-builder:dir=<path> --> near the top of CLAUDE.md (after the title) so subsequent runs are silent. Skip if the marker already exists.scripts/index.md: if the scripts directory holds >10 documented scripts, per-script <important if> blocks become their own context bloat. Generate/maintain a scripts/index.md hub instead (one line + link per script, mirroring the runbooks/ convention from desplega:engineering-standards), collapse the CLAUDE.md/AGENTS.md blocks into ONE pointer block referencing the hub, and add new scripts to the hub only.git diff CLAUDE.md AGENTS.md and print a 5-line summary of what changed. Never auto-stage — the user commits.After the doc edit, decide whether to proceed to the run-and-iterate tier.
| Autonomy | Behavior |
|---|---|
| Autopilot | Auto-escalate to Step 9 unless the intent flagged side-effects (writes to prod, sends real money, mutates a shared resource). On flagged side-effects, fall through to Critical behavior. |
| Critical (Default) | Use AskUserQuestion: `Run it now to confirm it works |
| Verbose | Same as Critical, plus offer the proposed run command for review before executing. |
If the user picks "I'll run it myself later" or "Just generate, don't run" → skip directly to Step 10.
A bounded-by-the-human loop. Never auto-apply a fix outside Autopilot.
--verbose, no --json). Capture exit code and tail -n 40 of the /tmp log.ECONNREFUSED, Traceback, non-zero exit, command not found, etc.).Edit you would apply to the script. Show old → new.Apply fix | Investigate differently | Stop.
No hardcoded retry cap — the human is the implicit bound.
Side-effect flag: if the intent declared side-effects (Step 3) and Autopilot is active, surface a one-line WARNING: this script <does X> against <target> before the first run and require explicit confirmation via AskUserQuestion even in Autopilot mode.
How the skill exits depends on how it was invoked:
Invoked as a sub-skill (from planning, qa, verifying):
Return control to the parent skill with a structured summary: { script_path, status: "pass"|"fail"|"unrun", log_path?, doc_files_edited: [...] }.
Invoked directly (/script-builder):
Use AskUserQuestion: Run it with /qa | Run /verify-plan | Commit the script and doc changes | Done.
desplega:qa with the script path as the source.desplega:verifying if a plan path is in current context.feat(scripts): add scripts/<name> for <area> validation) and stage only the generated script + the CLAUDE.md/AGENTS.md edits. Do not auto-commit; show the proposed git add and git commit commands and require user confirmation.Abort path (Stop selected at any earlier gate): leave the generated script in place, leave any CLAUDE.md/AGENTS.md edit in place if Step 7 ran, and print a one-liner: Aborted. Script: <path>. Doc edits: <files or "none">. Re-run /script-builder or git checkout to discard. Do not git restore on the user's behalf.
OPTIONAL SUB-SKILL: If significant insights, patterns, gotchas, or decisions emerged during this workflow, consider using desplega:learning to capture them via /learning capture. Focus on learnings that would help someone else in a future session.