script-builder
You are converting testing/validation intent into a durable, re-runnable script committed to the target project's
directory. The output is
context-optimal: a single PASS/FAIL line +
log path on success, full verbose output redirected to a timestamped log file, and an
block in
/
so future agents discover the script when the relevant testing intent recurs.
Working Agreement
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.
When to Use
This skill activates when:
- User invokes command
- Another skill references
**OPTIONAL SUB-SKILL:** desplega:script-builder
- A , , or flow needs a re-runnable validation script that doesn't yet exist
- The user expresses intent to "turn X into a script", "wrap this validation into something reusable", or "I want to test X end-to-end" with no existing script
Autonomy Mode
Adapt 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.
Process Steps
Step 1: Detect Mode (Retrospective vs Forward-Declared)
Decide silently — do not ask the user "which mode?". Use the following heuristics:
- Scan recent session tool-use history (the last ~20 tool calls in the current conversation) for test/validation-shaped activity: / calls, //, database queries, browser actions, repeated /log inspection of a single endpoint or table. If ≥2 such actions targeting the same area exist → retrospective mode.
- Parse the user's invocation message for cues:
- Narrative cues → retrospective: "we just figured out", "turn this into a script", "wrap that in", "that thing we just did".
- Intent cues → forward-declared: "I want to test", "validate that", "smoke check", "check before deploy".
- Fallback: when ambiguous, default to forward-declared.
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.
Step 2: Scan Existing Scripts for Overlap
Resolve the scripts directory in this order:
- Check for a
<!-- script-builder:dir=<path> -->
marker. If present, use that path.
- If exists at the repo root, use it.
- Use AskUserQuestion with options:
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:
- List files in the scripts directory (top-level only for v1).
- For each file, read the top of the file (header comment) and capture the file-name tokens.
- Read / for blocks that point at scripts in this directory and capture the trigger phrases.
- Fuzzy-match the current intent string against (file-name tokens ∪ header keywords ∪ trigger phrases). A "plausible match" is shared keyword + same area noun (e.g., , , ).
- If ≥1 plausible match → use AskUserQuestion with options:
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
block.
Step 3: Gather Intent
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).
Step 4: Detect Language
Priority order — first match wins, with override paths:
- TypeScript — + exist.
- Bun if or is present.
- else tsx if appears in devDependencies.
- else compiled Node (note in script header: requires build).
- Python — or or exists.
- uv if exists or has configured (sets ).
- else vanilla python3 ( substituted with empty string).
- Both TS and Python detected — count source files in /top-level and ask AskUserQuestion tiebreaker with the dominant one Recommended.
- / only → fall back to bash (note: TS/Python/Bash only in v1).
- Nothing matched → bash.
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.
Step 5: Draft the Script
- Pick the template:
cc-plugin/base/skills/script-builder/templates/{typescript.ts.tmpl|python.py.tmpl|bash.sh.tmpl}
.
- Resolve substitution markers from gathered intent:
- ← proposed file-name stem (see naming table).
- / / / ← intent fields.
- ← per Step 4.
- Generate from the intent. Keep it minimal: a single concrete probe + assertion, not a battery. Re-read the templates' README () for the contract — the body must respect the PASS/FAIL surface. Throw/raise/ on failure; let the template's outer try/trap convert it into the FAIL line.
- Propose a file name matching the intent shape — see the prefix table below. Use AskUserQuestion with the proposed name first (Recommended) and as the alternative.
Naming conventions (advisory — skill proposes, user overrides):
| Prefix | When | Example |
|---|
| End-to-end flows across multiple components | |
| Idempotent single-probe verifications | |
| Minimal-viability post-deploy checks | |
| Performance / size / token measurements | |
| / | Data seeding or artifact generation (rare for validation) | |
If the intent doesn't match any prefix cleanly, propose a free-form name like
.
- Write the file to the resolved scripts directory. Make it executable ().
Step 6: Syntax/Type-Check
Run the appropriate checker:
| Language | Checker (in priority order) |
|---|
| TypeScript | if Bun present, else |
| Python | python3 -m py_compile <file>
(always) + if is on PATH |
| Bash | if available, else |
On success: log a single line (
) and proceed to Step 7.
On failure:
- Print the error (≤10 lines, not the full output).
- Propose a concrete one-edit fix (the exact you'd apply).
- Use AskUserQuestion with
Apply fix | Investigate | Stop
.
- Apply fix → edit the script and re-run the checker (loop, no hardcoded cap).
- Investigate → drop into discussion with the user; do not auto-apply anything.
- Stop → leave script in place at its path; print the path; do not delete.
Step 7: Document the Script
Auto-edit the target project's
and/or
so future agents discover this script when the matching intent recurs.
Block template (the literal markdown the skill emits):
markdown
<important if="[TRIGGER: e.g., you are testing the auth flow]">
## [Area] validation
Run `scripts/<name>` to [one-liner]. Requires [env/deps]. Example: `<cmd>`. Full log at `/tmp/<name>-*.log`.
Generated/maintained via `/script-builder`.
</important>
Behavior:
- Target file selection: edit and 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.
- Placement heuristic: search the file for the first heading matching
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
.
- Idempotency: if a block referencing already exists (i.e., a prior run for the same script name), update it in place — replace the entire
<important if=...>...</important>
block, do not append a duplicate.
- Scripts-dir marker: if Step 2 resolved the scripts directory by user choice (not from an existing marker), insert
<!-- script-builder:dir=<path> -->
near the top of (after the title) so subsequent runs are silent. Skip if the marker already exists.
- Show the diff: run the equivalent of
git diff CLAUDE.md AGENTS.md
and print a 5-line summary of what changed. Never auto-stage — the user commits.
Step 8: Offer Escalation
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.
Step 9: Iterate on Failures (Escalated Tier Only)
A bounded-by-the-human loop. Never auto-apply a fix outside Autopilot.
- Run the script with the proposed example invocation (no , no ). Capture exit code and of the log.
- If exit code 0 → report PASS (echo the script's PASS line) and proceed to Step 10.
- If exit code non-zero:
- Summarize the failure in 1–3 lines: error class (timeout / 4xx / 5xx / assertion / dependency-missing / etc.) + likely cause (grep the log for known signatures: , , , , etc.).
- Propose a concrete diff — the exact you would apply to the script. Show old → new.
- Use AskUserQuestion:
Apply fix | Investigate differently | Stop
.
- Apply fix → edit the script, log the change, loop back to (1).
- Investigate differently → drop into discussion; do not auto-apply.
- Stop → leave the script in place, print its path; do not revert.
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.
Step 10: Handoff
How the skill exits depends on how it was invoked:
Invoked as a sub-skill (from
,
,
):
Return control to the parent skill with a structured summary:
{ script_path, status: "pass"|"fail"|"unrun", log_path?, doc_files_edited: [...] }
.
Invoked directly (
):
Use
AskUserQuestion:
Run it with /qa | Run /verify-plan | Commit the script and doc changes | Done
.
- Run with /qa → invoke with the script path as the source.
- Run /verify-plan → invoke if a plan path is in current context.
- Commit → propose a commit message (
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 and commands and require user confirmation.
- Done → print the script path + log path (if escalation ran) and exit.
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
on the user's behalf.
Learning Capture
OPTIONAL SUB-SKILL: If significant insights, patterns, gotchas, or decisions emerged during this workflow, consider using
to capture them via
. Focus on learnings that would help someone else in a future session.