| name | setup |
| description | Set up SDD/TDD development process for the current project. Creates CLAUDE.md, configures settings, and optionally creates spec files. Use when starting a new project or adding process to an existing one. |
| disable-model-invocation | true |
| argument-hint | [new or existing] [--template <path>] |
Project Setup
Set up the Spec-Driven Development and TDD workflow for this project.
FIRST ACTION (under non-interactive stdin) — STE-251 AC-STE-251.1. When process.stdin.isTTY === false (e.g., claude -p), the first tool call this skill emits MUST be AskUserQuestion tool_use OR RequiresInputRefusedError raise (via requireOrRefuse(...) from adapters/_shared/src/requires_input.ts). Write / Edit / NotebookEdit are forbidden before that ask/refusal — they produce silent scaffolding that bypasses the Socratic Loop Contract. Which of the two fires is not a judgement call: it is decided by the single-call arbiter evaluateGateMarkerRefusal({promptBody, isTty, gateSite: "setup-socratic"}) from adapters/_shared/src/gate_marker_refusal.ts, whose SOLE byte-checkable evaluation path is the marker grep at adapters/_shared/src/check_marker_runtime.ts — outcome apply (literal marker present) ⇒ proceed non-interactively on the sanctioned answers named in the step contract below; outcome prompt (tty) ⇒ ask; every other case raises RequiresInputRefusedError, and that refusal MUST be surfaced verbatim so the <dpt:requires-input-refused>v1</dpt:requires-input-refused> marker it carries reaches the stream, because a prose-only refusal reads as vacuous (a non-pass) — byte-indistinguishable from a run that simply did nothing. Read-only orientation (Read / Grep / Glob / Bash-read-only) is allowed. Interactive (tty) sessions are byte-identical to v2.17.0 — non-tty stdin only. See docs/auto-mode-protocol.md § Socratic Loop Contract.
Step contract — verification probes vs. Q&A
Every step below is annotated as one of three kinds. The agent — including non-interactive runs — MUST honour the kind:
verification: — runs unconditionally regardless of prompt mode (e.g., git status baseline check, linear list_teams MCP test call, bun --version). Failure → loud abort with NFR-10 canonical shape per the "fail loud, don't silently skip" principle.
default: <value> — proceeds with the named value when no answer is supplied (autonomous mode or pre-baked answers). Every resolved outcome (user-supplied / pre-baked / default-applied / model-imputed) appends a ## /setup audit row to CLAUDE.md via appendAuditRow from adapters/_shared/src/setup/audit_log.ts — provenance is recorded in the row's source: parameter and the derived imputed: column (STE-232 AC.4). The legacy appendAuditEntry wrapper is retained for callers still passing reason: directly.
requires-input: <reason> — refuses to proceed without an answer. The canonical helper requireOrRefuse(spec, key, sentinel) from adapters/_shared/src/requires_input.ts consolidates the four-outcome decision (user-supplied / pre-baked / default-applied / refused) and throws RequiresInputRefusedError (NFR-10 shape) when no answer is supplied. The auto-approve marker <dpt:auto-approve>v1</dpt:auto-approve> does NOT relax requires-input: — it is informational for these gates. Full cross-skill contract: docs/auto-mode-protocol.md. Sanctioned interview-answer source — the same mechanism /spec-write names, not a second shape. Under non-tty the one legitimate answer to a requires-input: question is an operator-authored block that resolveInterviewAnswer(promptBody, key) from adapters/_shared/src/auto_answers.ts lifts out of the <dpt:answers>v1 … </dpt:answers> region and hands to this same requireOrRefuse(...) call in its preBakedValue slot: a hit resolves as pre-baked, so the interview is ANSWERED rather than bypassed and never reaches the default-applied slot reserved for gates with a documented safe default; a miss returns undefined and leaves the refusal above to fire untouched. The auto-approve marker is a hard precondition (extractAutoAnswers byte-greps for it first), so a block sitting in an unmarked body — or one whose delimiters are absent or out of order — is inert and answers nothing, and the marker on its own answers nothing either.
Verification ≠ Q&A. The "do not prompt" instruction common in autonomous runs governs Q&A only. Verification calls always fire. The auto-approve marker (STE-226) is the byte-checkable pre-authorization signal for default:-annotated gates; it is read but never relaxes requires-input: — see docs/auto-mode-protocol.md § The Rule.
Toolkit marker
The first action that writes to CLAUDE.md prepends the literal HTML comment:
<!-- generated by /dev-process-toolkit:setup -->
This marker lets gate-check probes (setup-audit-section-presence, toolkit-bootstrap-committed) distinguish toolkit-managed CLAUDE.md from hand-written ones without scanning every file's contents.
Process
0. Tracker mode probe (existing projects)
Before any detection or setup, run the Schema L probe (see docs/patterns.md § Tracker Mode Probe). If CLAUDE.md already exists and contains a ## Task Tracking section, this is an existing tracker-mode project — /setup --migrate is the right entry point for changing modes. If CLAUDE.md is absent, empty of ## Task Tracking, or $ARGUMENTS contains new — and $ARGUMENTS does not contain --migrate or --migrate-dry-run — run the normal fresh-setup flow below. When --migrate is present, section 0b overrides this routing regardless of ## Task Tracking presence. When $ARGUMENTS contains --template <path>, dispatch to the setup-template child skill at skills/setup-template/SKILL.md (template-source reuse flow) before the normal fresh-setup steps.
0b. Mode-switch invocation (/setup --migrate / --migrate-dry-run)
When $ARGUMENTS contains --migrate or --migrate-dry-run, skip steps 1–8 and route into tracker-mode switching — handles all transitions between modes. The CLI flag stays --migrate to preserve operator muscle memory. Current mode is detected via Schema L probe: absence of ## Task Tracking = mode: none (canonical form); presence = parse mode: <value>. All modes (including none) are valid starting states.
- Detect current mode via Schema L probe.
- Prompt for target mode; refuse no-op via NFR-10 canonical shape:
Detected current mode: <current>. Supported targets: <others>. Mode switch must change mode.
- Supported transitions:
none → <tracker> / <tracker> → none / <tracker> → <other>. Unsupported = NFR-10 canonical refusal.
- The CLAUDE.md
mode: line + active-FR renames land in one commit; if the switch fails partway, the user reruns /setup --migrate from a clean working tree (no rollback prompt).
- Active-FR rename. On any mode change, re-derive filenames for every active FR under
specs/frs/*.md (not archive/) using the target-mode Provider.filenameFor(spec) and git mv each file to its new name. Archive is frozen by mode transitions. All renames + the CLAUDE.md mode: flip land in a single atomic commit.
- Branch-template re-seed. Call
reseedBranchTemplate(claudeMdPath, { date }) from adapters/_shared/src/setup/migrate_branch_template.ts: a branch_template: value byte-identical to the retired seeded default {type}/{ticket-id}-{slug} is rewritten to {type}/m{N}-{slug} and logged in ## /setup audit; any other value is preserved verbatim, and an absent key stays absent.
Detailed tracker-mode switching procedures live in docs/setup-tracker-mode.md.
1. Detect the project
Check for project files (package.json, pubspec.yaml, pyproject.toml, go.mod, Cargo.toml, build.gradle.kts / settings.gradle.kts for Kotlin/Gradle, etc.) and source directories.
If no project files are found (empty directory or only basic files), this is a new project — go to step 2. If project files exist, skip to step 3.
1b. Doctor Validation
For existing projects (project files found in step 1), validate prerequisites before proceeding:
| Check | How | Remediation |
|---|
| Required tools installed | Run version commands using the per-stack declared invocation prefix (STE-209 AC-STE-209.5): when the rendered CLAUDE.md / examples/<stack>/gate-commands.md declares fvm flutter, the doctor probes fvm flutter --version (not bare flutter --version); same pattern for any wrapper (bun vs node, pnpm vs npm, etc.). Falls back to the bare command when no wrapper is declared. Examples: node -v, fvm flutter --version (when fvm is declared) or flutter --version, python3 --version, ./gradlew --version (Kotlin/Gradle — probe the wrapper, NOT a bare gradle --version). | Install the missing tool or update PATH |
| Gate commands runnable | Run the gating rule from CLAUDE.md (e.g., npm run typecheck && npm run lint && npm run test) | Fix failing commands or update CLAUDE.md gating rule |
| CLAUDE.md present | Check if CLAUDE.md exists in project root | Will be created in step 5 |
| .claude/settings.json present | Check if .claude/settings.json exists | Will be created in step 6 |
Spec anchor IDs (if specs/ exists) | Grep specs/plan/M*.md for every ## M{N} — heading (legacy ## M{N}: too) and specs/requirements.md for every ### FR-{N}: heading; each must carry a matching {#M{N}} or {#FR-{N}} anchor | Add the missing {#M{N}} / {#FR-{N}} anchor. Missing anchors do NOT cause doctor failure — they report under GATE PASSED WITH NOTES |
Report pass/fail for each check with remediation. Missing anchor IDs surface under GATE PASSED WITH NOTES, never as a hard failure.
For new projects, skip this step.
2. Scaffold new projects
Ask the user what stack they want, then scaffold a working, gate-check-ready project. Every generated config must work out of the box — no manual fixup required.
2a. Research current best practices
Before generating any config files, search the web for the current recommended setup for the chosen stack. Look for latest stable versions, current config file formats, recommended tsconfig/pyproject/pubspec settings, and known gotchas. Stale defaults degrade scaffolding.
2b. Scaffold the project
Key requirements for every stack:
- Git repo — Run
git init if not already in a git repository
- ESM/modern module format — Use the current module standard (e.g.,
"type": "module" for Node)
- Placeholder source file — Prevents "no inputs found" errors (e.g.,
src/index.ts, src/__init__.py, main.go)
- Test runner configured to pass with no tests — Critical for gate-check on empty project (e.g., Vitest
passWithNoTests: true)
- Don't use interactive
init commands that generate broken defaults
- .gitignore — Stack-appropriate ignores
- All config files — Must work together without conflicts or manual fixup
Stack-specific guidance:
- TypeScript/Node:
npm init -y, set "type": "module", install typescript + vitest + eslint, create tsconfig.json (strict, ESM, src/dist dirs), vitest.config.ts, eslint config, src/index.ts, tests/ dir
- Bun (TypeScript): see
${CLAUDE_PLUGIN_ROOT}/examples/bun-typescript.md. Bun has no --passWithNoTests flag — see step 2c branch + docs/setup-reference.md § Bun scaffold-verify branch
- Flutter/Dart:
flutter create . (writes test/widget_test.dart automatically), add bloc_test + mocktail. do NOT add an extra placeholder file (e.g. test/empty_test.dart) — flutter test exits 0 against an empty test/ directory on current Flutter SDKs, and test/widget_test.dart from flutter create keeps test/ non-empty in the canonical bootstrap path.
- Python:
uv init or poetry init, add pytest + mypy + ruff, create src/init.py, tests/ dir
- Go:
go mod init <module>, create main.go, install golangci-lint
- Kotlin (Gradle/JVM): scaffold
build.gradle.kts applying the Kotlin-JVM plugin and the detekt Gradle plugin (io.gitlab.arturbosch.detekt), settings.gradle.kts, a placeholder src/main/kotlin source + a src/test/kotlin test — see step 2c branch + docs/setup-reference.md § Kotlin detekt scaffold-verify branch
2c. Verify scaffolding
verification: Run the gate commands to verify they all pass. If anything fails, fix it immediately — the project must be gate-check-ready before proceeding to step 3.
Bun-specific scaffold-verify branch writes a placeholder file before running the gate. See docs/setup-reference.md § Bun scaffold-verify branch for the placeholder location, marker comment, and layout-policy logic.
Kotlin detekt scaffold-verify branch ensures the detekt Gradle plugin is applied in build.gradle.kts before the first gate (analogous to the Bun zero-match branch), so ./gradlew detekt does not fail with "task 'detekt' not found". See docs/setup-reference.md § Kotlin detekt scaffold-verify branch.
3. Read the templates
Load reference material from the plugin directory:
${CLAUDE_PLUGIN_ROOT}/templates/CLAUDE.md.template — Base template
${CLAUDE_PLUGIN_ROOT}/examples/ — Stack-specific gate commands and patterns
${CLAUDE_PLUGIN_ROOT}/docs/adaptation-guide.md — Configuration reference
Match the detected stack to the closest example (e.g., a detected Kotlin/Gradle stack maps to examples/kotlin/). For other stacks, use the adaptation guide's gate command table.
4. Present the plan
Show the user what you'll create and ask for approval:
- CLAUDE.md — With their project's actual commands, patterns, and conventions
- .claude/settings.json — With tool permissions for their stack
- specs/ (optional) — Spec file templates if they want the full SDD workflow
5. Generate CLAUDE.md
Create CLAUDE.md based on the template, filling in: project name and description, detected tech stack, actual directory structure, real gate commands, key patterns (infer from existing code, or leave as TODOs), testing conventions, DO NOT section with sensible defaults.
Important: Generate real content, not template placeholders. If you can detect the test framework, write it. If you can see the directory structure, document it.
6. Configure settings
verification: Write .claude/settings.json with the canonical permissions allow-list for the detected stack. Required, abort on failure.
- Read
${CLAUDE_PLUGIN_ROOT}/templates/permissions.json for the canonical allow-list keyed by stack (bun, node, flutter, python, generic).
- Compose via
canonicalAllowList(template, stack) from adapters/_shared/src/setup/merge_settings.ts. Falls back to generic for unregistered stacks.
- If
.claude/settings.json does NOT exist: write a fresh JSON with {"permissions": {"allow": <canonical>}}.
- If it exists: read + parse it, merge via
mergeAllowList(existing, canonical), write back. Preserves user additions; never strips entries. Malformed pre-existing JSON → abort with NFR-10 canonical shape. Report the merge loudly on BOTH paths (3 and 4). Call mergeAllowListReport(existing, canonical) from the same module instead of the bare merge — it returns the byte-identical settings (the merge is unchanged: a caller-supplied list is merged, never replaced) plus callerCount / addedCount / mergedCount and a pre-rendered row — and MUST emit setup_allowlist_entries_added (literal backticked token) in the Step 11 closing summary, naming how many entries the merge added beyond the caller-supplied list. The list a reviewer reads in the diff is the opening posture, not the effective policy, which is why silence here keeps getting re-filed as a defect. Emit on every path, including the two quiet ones — no caller list ⇒ the added count IS the full scaffolded count; zero added ⇒ a row saying so. A skipped row and a broken one read identically, so never skip.
- On any write failure (sandbox refusal, ENOSPC, etc.), emit NFR-10-shape error and exit non-zero. Never continue with a partial scaffold.
6b. Install commit-msg hook
default: shell — install the POSIX-shell Conventional-Commits hook (zero-dep). $ARGUMENTS contains --commitlint ⇒ install the commitlint-delegating variant. Skip entirely when .git/hooks/ is absent (log setup: skipping commit-msg hook (not a git repo)).
The hook is a local-machine artifact (not tracked in git). commitlint.config.js, when written, IS in the bootstrap set — step 8b appends it when --commitlint was used.
Best-effort under non-interactive runs. Hook install is best-effort — the model-layer block on .git/hooks/ writes is expected under bypassPermissions and is logged in ## /setup audit rather than failing the run. The hook is per-clone, not part of the bootstrap set; a downstream user re-running /setup interactively (or running the manual fallback) closes the gap without re-bootstrapping. Manual fallback (run post-/setup when the audit shows setup: skipping commit-msg hook (model-layer block)): cp plugins/dev-process-toolkit/templates/git-hooks/commit-msg.sh .git/hooks/commit-msg && chmod +x .git/hooks/commit-msg.
Full procedure (idempotency tier, --commitlint extras, manual install command): docs/setup-reference.md § Step 6b — commit-msg hook install.
6c. Git-ignore the toolkit's own state directory
Call writeDptGitignore(projectRoot) from adapters/_shared/src/setup/dpt_gitignore.ts on every run to write the toolkit-owned .dpt/.gitignore, creating .dpt/ when absent, which keeps ledger + scratch state out of commits while leaving the lock namespace tracked. The write is existence-guarded and idempotent — the helper byte-compares the baseline and returns unchanged without touching the file on a match, so re-running is free. Record the returned outcome and path on the ## /setup audit surface via appendAuditRow; the write is best-effort, so a failure logs its audit row and the run continues rather than failing — the same precedent step 6b follows for the commit-msg hook.
Available-migration hint. After the gitignore write, walk the shared migration registry (adapters/_shared/src/migrations/) and run each entry's detector against this tree; when any detector fires, append exactly one ## /setup audit row via appendAuditRow recommending /dev-process-toolkit:gate-check — whose probe #69 (upgrade_staleness) reports the drift and hands Claude the remedy, since the migration runner is model-invocable only and is not on the slash menu — and MUST emit upgrade_available (literal backticked token). The detection walk is best-effort on the same precedent step 6b follows — a detector that throws, or a registry that is absent or unreadable, is swallowed into its audit row and the run continues; the hint never blocks or fails /setup, and /setup never applies a migration itself (the runner owns every write, behind its own approval).
7. Configure MCP servers
verification: When tracker mode != none, write .mcp.json with the resolved adapter's mcpServers entry. Required, abort on failure — the setup-output-completeness probe (gate-check #17) hard-fails when missing in tracker mode.
If .mcp.json already exists, merge — don't overwrite.
Full procedure (rubber-duck offer, chrome-devtools-mcp for web projects): docs/setup-reference.md § Step 7 — Configure MCP servers.
7b. Tracker mode (opt-in)
requires-input: tracker mode is a workspace-wide decision; no safe default exists.
Ask exactly once, near the end of the flow — after CLAUDE.md is drafted but before it's written:
Task Tracking: where do ACs live? 1. none (ACs stay in specs/requirements.md) / 2. linear / 3. jira / 4. custom (copy adapters/_template). Enter 1–4. No default — autonomous runs MUST pre-bake an answer.
Auto Mode + first-run MCP (STE-199 AC.1..3 / AC.7..8 + STE-232 AC.3). Auto Mode does NOT relax requires-input. Step 7b's resolver returns --tracker=<mode>, an interactive answer, or a sentinel; the result is routed through requireOrRefuse(spec, "tracker_mode", SENTINEL) from adapters/_shared/src/requires_input.ts (per the cross-skill contract in docs/auto-mode-protocol.md). On the 'refused' outcome, the helper throws RequiresInputRefusedError carrying the canonical NFR-10 message "/setup step 7b requires an explicit answer for \"tracker_mode\"; tracker mode is a workspace-wide decision; no safe default exists. Auto Mode does not default-apply requires-input steps." + requires_input_refused row. --tracker=<mode> accepts none | linear | jira | <custom>. When the chosen tracker's MCP is not yet registered, take the first-run-deferral branch: write team: <deferred> / project: <deferred> to CLAUDE.md, audit via appendAuditRow(... source: "default-applied", reason: "<MCP> unregistered at /setup time; deferred to first downstream skill" ...) — imputed: true is derived because the operator did not directly confirm the deferral — and surface workspace_binding_deferred; operator runs /setup --resume-tracker-binding after authenticating to fill the binding. Full table: docs/setup-reference.md § Step 7b.
Schema L canonical-key constraint. When emitting the ## Task Tracking section, write only the canonical keys: mode, mcp_server, jira_ac_field, branch_template. Tracker-specific metadata (project IDs, team names) belong in a separate sub-section under ## Task Tracking (e.g., ### Linear) or in the adapter's own config — not as Schema L keys at the top level. The task-tracking-canonical-keys probe (gate-check #21) hard-fails any non-canonical top-level key.
If the user picks 1 (or pre-baked answer is none), do NOT emit a ## Task Tracking section in CLAUDE.md — absence is the canonical form for mode: none. Continue to step 7c.
If the user picks 2–4, run the full numbered flow in docs/setup-reference.md § Step 7b — Tracker mode (covers the bun re-verification — bun is a universal toolkit prerequisite per the README ## Prerequisites section, re-checked here for tracker mode — plus MCP detection, test call, Jira field discovery, Schema L emit, and workspace binding). Background detail in docs/setup-tracker-mode.md.
Jira-specific prerequisite (mode: jira only): the operator must have created the Jira Space (project) in the Jira UI manually before running /setup — the Atlassian MCP exposes no project-creation tool. After the operator supplies the project key, /setup validates visibility via mcp__atlassian__getVisibleJiraProjects and refuses with NFR-10 canonical shape on miss. AC-field discovery returning { ok: false } is also a first-class branch: the operator chooses between creating a custom field (records jira_ac_field: customfield_XXXXX) or accepting the description-body sentinel (records jira_ac_field: description); both choices are recorded under the existing canonical key — no new Schema L key is added.
7c. Branch-naming template
default: {type}/m{N}-{slug} — the single seeded default, and it is seeded only where Schema L exists, i.e. in tracker mode; proceed with it if no answer is supplied. Every resolved branch_template value appends ## /setup audit entry recording step:7c value:"<resolved>" reason:"<user-supplied|default applied>" — provenance per resolution, not per run.
- Empty response ⇒ accept default.
- Non-empty response ⇒ use verbatim (
/implement sanitizes LLM output at render time).
- Write the resolved value as
branch_template: <value> in Schema L. mode: none projects that elected 1 in 7b: skip writing — Schema L is the ## Task Tracking section (step 7b above), and a tracker-less project emits no such section, so there is nowhere for the key to go. That disables ONE of the two mechanisms reading the key, not both (the blunter claim this bullet used to carry was false for the second, and contradicted the seeded-default line above): /implement § 0.b″'s branch proposal is gated on Schema L and is skipped entirely, while the universal pre-commit branch gate (requireCommittableBranch, called before staging by /spec-write, /setup, /spec-archive, /ship-milestone, /deps, /upgrade) has no tracker awareness and never reads Schema L — it keeps running, taking its name from each skill's branchNameFor(...) over canonicalBranchTemplate({ milestone }) in code. {N} still resolves: a minted M_<tail> id parses as an epic token, so M_WV047W ⇒ feat/m_wv047w-<slug>. What /setup WRITES does not change — no key is emitted under mode: none, exactly as before.
- Skip condition: if CLAUDE.md already has
branch_template:, do not re-ask.
See docs/setup-tracker-mode.md § Branch template for the long-form prompt and placeholder substitution rules.
7d. Docs modes
default: all-false — when no answer is supplied (autonomous mode or skipped), the skill always emits the ## Docs section with all three flags false. Every resolved ## Docs flag appends ## /setup audit entry: step:7d (docs.<flag>) value:<bool> reason:"<user-supplied|default applied>" — provenance per resolution.
Ask one at a time, in this order — wait for each answer before asking the next (per docs/patterns.md § Pattern 26):
Generate user-facing docs (narrative + mermaid state/flow diagrams)?
Generate packages-style API reference docs? — body is stack-adaptive. TS-only project: (typedoc <detected|not found>, ts-morph <bundled>, stack: <ts|other>). Other stacks render their toolchain-probe results (Dart: dart-analyzer; Python: griffe).
Is CHANGELOG.md generated by CI (if yes, /ship-milestone will not write it)?
Accept y/n/yes/no case-insensitively; other inputs re-prompt with answer y or n. If both 1 and 2 are declined, refuse with NFR-10 shape and re-ask only those two; if declined again, still emit the section with all-false defaults (read-side readDocsConfig returns all-false for an absent section by design).
Write the section as Schema L (lowercase true/false, no quoting), placed immediately after ## Task Tracking. Re-runs are atomic — splice the full block, write once.
Toolchain snapshot. When packages_mode == true, also write docs/.dpt-docs-toolchain.json recording per-stack preferred signature-extraction strategy. The signature-strategy-honors-setup probe (#24) reads this later for drift detection. docs_default_applied (STE-202 AC-STE-202.6): when the autonomous run applies the all-false default to all three flags (no pre-baked answers for any of user_facing_mode / packages_mode / changelog_ci_owned), surface a docs_default_applied row listing the defaulted flags. Interactive runs and mixed runs do NOT emit the row.
Full prompt list, NFR-10 refusal text, and section format: docs/setup-reference.md § Step 7d — Docs modes. Background detail in docs/setup-docs-mode.md.
7e. Release Files block
default: per-stack template — emit a ## Release Files block in CLAUDE.md from examples/<stack>/release.yml (typescript-node / flutter-dart / python / plugin); unrecognized stack ⇒ commented stub. Skip when CLAUDE.md already carries a ## Release Files heading — user-edited overrides win on regenerate. Append immediately after ## Docs. Drives /ship-milestone's version bump; full schema in docs/ship-milestone-reference.md.
7f. Tracker-config write
requires-input: tracker-config is the canonical mapping from the project's verbatim tracker statuses to the canonical four-role enum; no safe default exists. — runs only in tracker mode (mode: linear / mode: jira / mode: <custom>). Vacuous in mode: none — when mode: was set to none in step 7b, this step short-circuits with no MCP fetch, no proposal, no write. The byte-checkable phrase mode: none appears in this step's prose so the gate-check probe sees the skip-condition.
Composes a specs/tracker-config.yaml file via the four sub-steps below, then routes through runTrackerConfigWrite(...) from adapters/_shared/src/tracker_config_proposal.ts. The helper wraps the readTrackerConfig + writeTrackerConfig loaders in adapters/_shared/src/tracker_config.ts so the file on disk is always schema-valid.
Sub-steps (run in order — the helper enforces this; the prose mirrors it for operator visibility):
a. Query the active adapter for the project's full status list. Invoke the adapter's list_project_statuses capability via MCP; the returned list is the verbatim tracker vocabulary (casing, whitespace, special characters preserved). When the active adapter declares list_project_statuses=false (a custom adapter without the capability), the step short-circuits with outcome skipped_adapter_limit and MUST emit tracker_config_write_skipped_adapter_limit (literal backticked token) in the Step 11 closing summary. When the MCP call throws (server unreachable, timeout, auth failure), the step refuses with NFR-10 canonical shape (Refusing: / Remedy: / Context:) and MUST emit tracker_config_write_mcp_unavailable (literal backticked token); no partial write fires. Jira note: the allowedValues introspection behind this fetch is company-managed-only — for team-managed ("simplified": true) projects the adapter falls back to the transitions-derived path (getTransitionsForJiraIssue → to.name) as primary, per adapters/jira.md § list_project_statuses; both paths return the same ordered verbatim string array.
b. Read specs/tracker-config.yaml if present (re-entry path) else treats baseline as empty. Use readTrackerConfig(specsDir) from the shared loader; absent file ⇒ baseline = "". The baseline ⇄ proposal comparison is logical (whitespace-normalized), so identical content short-circuits the prompt + write with outcome unchanged and MUST emit tracker_config_unchanged (literal backticked token).
c. Compose an LLM-judgment role-to-status mapping inline. This is the running /setup skill's conversation step — NOT a new subagent. For each canonical role in the four-value enum (initial, in_progress, in_review, done), pick the verbatim status from sub-step (a) that best matches the role's semantics. When no plausible match exists for a role (e.g., a tracker workflow without an In Review-equivalent), record null with reasoning that names the gap explicitly; the operator routes through the edit branch in sub-step (e) to disambiguate.
d. Show a unified-diff between baseline and proposal. Render via renderUnifiedDiff(baseline, proposal) from the helper. Empty baseline ⇒ + lines only (first-run shape). Delta between baseline and proposal renders both - and + lines. The diff is shown to the operator before the prompt fires.
e. Prompt approve / edit / cancel via AskUserQuestion. The closed-form prompt offers three choices — approve / edit / cancel. On approve: route to sub-step (f). On cancel: no write fires, baseline untouched, outcome cancelled, MUST emit tracker_config_write_cancelled (literal backticked token). On edit: re-prompt once per canonical role with a per-role pick from the full status list; replace the proposal's role mapping with the operator's picks; then route to sub-step (f). Auto-approve marker: when the prompt body contains <dpt:auto-approve>v1</dpt:auto-approve>, default-apply approve without firing the prompt (the operator pre-authorized the write).
f. On approve, call writeTrackerConfig(specsDir, config) from the shared loader. The writer validates the schema before persisting — invalid configs throw TrackerConfigShapeError and the step fails without writing. Successful write ⇒ outcome succeeded, MUST emit tracker_config_write_succeeded (literal backticked token) in the Step 11 closing summary.
Capability-key emission contract. The helper returns one of five outcomes; each outcome maps to exactly one literal backticked token the closing summary MUST emit so /gate-check's closing_summary_capability_keys probe can grep for it:
- succeeded ⇒ MUST emit
tracker_config_write_succeeded
- cancelled ⇒ MUST emit
tracker_config_write_cancelled
- unchanged ⇒ MUST emit
tracker_config_unchanged
- skipped_adapter_limit ⇒ MUST emit
tracker_config_write_skipped_adapter_limit
- mcp_unavailable ⇒ MUST emit
tracker_config_write_mcp_unavailable
The plain-language prose for each token lives in /spec-write SKILL.md § 7's static map (mirrored row-for-row).
7g. Token Stats (opt-in)
default: false — requires-input: false (safe default). Ask one AskUserQuestion (per the Socratic Loop Contract — never bare prose): "Enable per-skill token-usage stats? (records token usage per skill into a git-ignored ledger + renders ## Token Stats tables at commit boundaries)" [y/n]. The safe default false applies under non-tty stdin with no pre-baked answer — this is not a requires-input gate.
Always emit the ## Token Stats section (enabled: false when declined or unanswered) as Schema L (lowercase true/false). Re-runs are atomic — splice the full block, write once (mirrors step 7d's ## Docs). Over a project that already carries the section, show [current: <bool>]; empty input keeps the current value. Each resolved value appends a ## /setup audit row step:7g (token_stats.enabled) value:<bool> reason:"<user-supplied|default applied>".
8. Create specs (optional)
If the user wants the full SDD workflow (or $ARGUMENTS contains "new"):
- Monolith guard — runs BEFORE the create/seed step below. Run the read-only detector
monolithSplit.detect(projectRoot) — a deterministic bun one-liner importing monolithSplit from adapters/_shared/src/migrations/monolith_split and printing the { applies, evidence } verdict as JSON; it reads only and never mutates. When it APPLIES (live ### FR-<digits>: sections in specs/requirements.md + no per-FR content), /setup SKIPS the specs/frs/ mkdir + .gitkeep seeding entirely — including the specs/frs/archive/ subdir; the specs/plan/{,archive/} scaffolding and the technical/testing template copies are UNAFFECTED (only the specs/frs/ scaffold is withheld).
- When the guard fires, /setup prints a one-line explanation naming the detected monolithic layout and the follow-up — run
/dev-process-toolkit:gate-check, whose probe #69 (upgrade_staleness) surfaces the monolith-split migration on this legacy tree once bootstrap completes (mirrors the step-6c upgrade_available → gate-check hint) — and appends a ## /setup audit row step:8 (monolith_guard) value:frs-scaffold-skipped reason:"monolithic requirements.md layout — split via gate-check probe #69".
- Non-monolithic trees are byte-identical. The guard keys on the live-section pattern
### FR-<digits>: (both limbs of monolithSplit.detect require real FR-<digits> headings), so the requirements template's OWN placeholder ### FR-N: [Feature Name] (FR-N — a literal letter, not digits) does NOT match; a fresh / non-monolithic scaffold never trips the guard and stays byte-identical to today.
- Create
specs/ plus specs/frs/, specs/frs/archive/, specs/plan/, specs/plan/archive/ (per-unit archival; no rolling index file).
- Copy cross-cutting templates from
${CLAUDE_PLUGIN_ROOT}/templates/spec-templates/. Do not create or copy any archive-index.md — archival is git mv + frontmatter flip.
- Never-overwrite rule. An existing
specs/requirements.md is NEVER overwritten by the template copy — pre-existing spec files are the user's; the template copy fills only gaps (absent files), and never clobbers live content already on disk.
Scaffold deliverables (canonical inventory, STE-189). /setup produces six artifact classes — each a /setup deliverable, emitted unconditionally when its step runs: (1) CLAUDE.md (step 5), (2) .claude/settings.json (step 6), (3) .mcp.json (step 7, tracker mode only), (4) specs/{requirements,technical-spec,testing-spec}.md (step 8), (5) specs/plan/M1.md plus .gitkeep stubs in specs/frs/{,archive/} and specs/plan/{,archive/} (step 8), (6) src/.placeholder.test.ts (Bun, step 2c). Class-(5) carve-out: the .gitkeep stubs are the sole exception to the emitted-unconditionally posture — the specs/frs/{,archive/} stubs are skipped when the monolith guard fires (a detected legacy monolithic requirements.md layout); the guard is narrow, so on every non-monolithic tree and for every OTHER artifact class the unconditional posture stays intact. These are emitted regardless of any phrasing in the operator's invocation context — "only /setup completes here", "do not run /spec-write", "minimal mode" all constrain which downstream skills run after /setup, not which artifacts /setup itself produces (smoke #9 / run 2 F2). /spec-write step 1 ("Assess current state") expects these on disk; absence is a recovery-path failure mode, not the canonical contract. The scaffold list is non-negotiable; the operator can populate spec bodies later via /spec-write, but the empty templates ship from /setup.
8a. Audit-section post-condition
verification: runs unconditionally before step 8b's bootstrap commit. Materialises the deterministic post-condition that every well-formed /setup run with at least one populated Schema L surface (branch_template: populated or ## Docs present) ships a ## /setup audit section — provenance is preserved per resolution via the entry's reason: field, not gated on whether a default was applied.
- Read CLAUDE.md once.
- Call
hasDefaultApplicableOutcomes(content) from adapters/_shared/src/setup_audit_section_presence.ts — single source of truth for the audit-required surfaces predicate (branch_template: populated or ## Docs present). Reused by /setup step 8a and /gate-check probe #19.
- Branch:
- false ⇒ no-op, continue to 8b. Vacuous when no Schema L surface is populated (interactive run that wrote neither
branch_template: nor ## Docs).
- true AND
## /setup audit heading present ⇒ no-op. Per-step appends already populated it.
- true AND heading absent ⇒ call
synthesizeAuditSection(claudeMdPath, resolvedSchemaLValues) from adapters/_shared/src/setup/synthesize_audit.ts. Pass the in-scope resolved Schema L values table populated during 7b/7c/7d — every resolution lands here, user-supplied or default-applied. Never re-derive from CLAUDE.md. The helper is idempotent ((step, field) dedup).
On AuditPostconditionUnsatisfiable (the resolved Schema L values table is empty but the file shows audit-required surfaces — defensive invariant; unreachable on the canonical 7b/7c/7d → 8a path), refuse with NFR-10 canonical shape and abort before 8b commits malformed output. Full refusal text + procedure: docs/setup-reference.md § Step 8a.
8b. Bootstrap commit
default: commit — autonomous mode proceeds; interactive mode prompts. The bootstrap is mechanical and the diff is reviewable post-commit.
After all writes settle and step 8a's post-condition has run, /setup produces a single bootstrap commit so the canonical /setup output set lands at HEAD instead of leaking into the first feature PR.
Procedure: pre-flight git status --porcelain check, stage the canonical set (CLAUDE.md, settings.json, .mcp.json when written, specs files, .gitkeep stubs, plus commitlint.config.js when --commitlint was used, plus src/.placeholder.test.ts when the Bun scaffold-verify branch wrote it), diff preview + prompt, commit (subject: chore: bootstrap dev-process-toolkit — no parenthesized version suffix; the historic (v<plugin-version>) form referenced the project's bun init default rather than the toolkit version and was misleading), handle declines/edits/failures.
Bun placeholder rides the bootstrap commit. The scaffolded pre-commit-tdd-orchestrator hook exempts the staged placeholder from the /tdd-evidence check via its verbatim Bun zero-match workaround marker comment (marker grep primary, basename guard secondary; staged deletions are also exempt per the first-real-test-lands lifecycle) — do not leave it staged for a manual operator commit. Mixed commits (placeholder + any tdd-required file) still require evidence; the exemption subtracts only the placeholder.
Toolkit version footer. Append a Toolkit: dev-process-toolkit v<plugin-version> line to the commit body after any existing footers (Refs:, etc.). Read <plugin-version> from plugins/dev-process-toolkit/.claude-plugin/plugin.json version field. The read is fault-tolerant / best-effort: if plugin.json is missing or unparseable, skip the footer entirely (footer absent rather than malformed v<unknown>) and emit a Step 7 advisory under setup_toolkit_version_unreadable. The bootstrap commit is load-bearing; the footer is not.
Full numbered procedure (decline + edit + failure paths): docs/setup-reference.md § Step 8b. Probes: toolkit-bootstrap-committed (#22, hard-fails on uncommitted toolkit-managed CLAUDE.md) + setup-bootstrap-commit-subject (#30, asserts subject + footer shape on the most recent chore: bootstrap dev-process-toolkit* commit; legacy-pre-FR-ship-date commits get a backwards-compat carve-out).
8c. Seed a project check skill (opt-in)
default: skip — offered after CLAUDE.md generation (step 5), before the 8a/8b commit sequence so the scaffold lands in the bootstrap commit. Opt-in and skippable; a no-op on decline (write nothing). Offer to scaffold a stack-aware, project-local check skill ("drive" skill) so /implement can run a real end-to-end verification pass after gate checks.
On accept, call scaffoldCheckSkill({ projectRoot, stack, slug }) from adapters/_shared/src/scaffold_check_skill.ts. It writes a stack-appropriate stub — Flutter → dart/Flutter-MCP drive; web/JS/TS → Chrome-MCP or /dev-process-toolkit:visual-check; Python/API → HTTP e2e; other/unknown → a documented generic TODO — to .claude/skills/<slug>-drive/SKILL.md with disable-model-invocation: true (inert until you fill its TODOs), then writes verify_skill: <slug>-drive into the CLAUDE.md ## Verification block. Collision-safe: an existing skill is never overwritten (de-duplicated to <slug>-drive-2). The same accept path also settles run_cmd: alongside the scaffold, offer a run command seeded from the stack detected in step 1 — a web project's dev server, a Flutter run command, a Python service's entry point — and adopt the operator's confirmed answer into the same ## Verification block, adding no step of its own. Key the quiet branch on detection, never on which stack was matched: when detectRunnability(projectRoot) reports the project is not runnable — no source in its closed set fires — write run_cmd: none, which is an answer and not a skip. Neither path leaves run_cmd absent: an absent key parses to null, no answer at all rather than a declaration that the project cannot be run, and the moment the repo later grows a dev script or a Running heading that silence reds probe #80 where none would already have answered it.
Full procedure: docs/setup-reference.md § Step 8c — Seed a project check skill.
8d. Seed the best-practices manifest (opt-in)
default: skip — optional; a no-op on decline or skip (M124), mirroring the step-7f re-entrant pattern. Read any existing specs/best-practices.yaml via readManifest from adapters/_shared/src/best_practices_manifest.ts as the baseline (absent file ⇒ empty baseline), compose a fresh proposal from the detected project layout, render a baseline → proposal unified diff, then prompt approve / cancel via AskUserQuestion. On approve, write through writeManifest — never hand-author the YAML. On cancel, no write fires — the file stays untouched.
9. Verify
Run gate check commands to verify they all pass. If any fail, fix immediately — don't report a broken setup.
10. Offer to fill specs
If spec files were created, ask the user: "Spec templates are ready. Want me to help you fill them in now? I can walk you through defining requirements, technical decisions, and the implementation plan. (Run /dev-process-toolkit:spec-write)"
After /setup
Subsequent /setup re-runs (e.g., flag changes) produce follow-up commits (chore: re-run /setup ...) — not amendments. The bootstrap commit is canonical even if the user later refines flags.
If you skipped the bootstrap commit (n at step 8b), commit the staged files manually before running /spec-write so the first feature PR's diff is FR-scoped. Universal pre-commit branch gate (STE-228): before step 8b stages the bootstrap commit, call requireCommittableBranch({...}) from adapters/_shared/src/require_committable_branch.ts with the name from branchNameFor() at skills/setup/branch_name_for.ts (literal chore/setup-bootstrap); gate is git-only — runs before CLAUDE.md exists; auto-mode default-apply via <dpt:auto-approve>v1</dpt:auto-approve> (STE-226).
11. Report
Summarize what was created (list files created/modified), then present the SDD workflow.
Your SDD Workflow:
0. /brainstorm → (Optional) Explore approaches before writing specs
1. /spec-write → Fill in specs/*.md (requirements first, then technical, testing, plan)
2. /implement → Builds features with TDD + three-stage self-review (the main entry point)
3. /gate-check → Verify quality gates pass
4. /debug → Investigate failing tests or unclear gate failures
5. /spec-review → Audit implementation against specs
6. /simplify → Clean up changed code
7. /pr → Create pull request
Key principle: Specs are the source of truth. /implement reads specs to understand what to build, writes tests first, self-reviews against acceptance criteria, and reports for human approval before committing.
Workflow paths (Bugfix / Feature / Refactor) and the spec-fill-in checklist live in docs/setup-reference.md § Step 11. Surface them to the user when relevant.
Rules
- Always ask for approval before creating or modifying files
- Never overwrite an existing CLAUDE.md without confirmation — offer to merge instead
- Generate real content based on what you detect, not empty templates
- The scaffolded project MUST pass gate-check before proceeding — fix any issues
- Steps 7b, 7c, 7d, 7e, 7g are five separate prompt sites — ask one question per turn, wait for the answer, then ask the next. Do not bundle them into a single turn even at phase transitions. See
plugins/dev-process-toolkit/docs/patterns.md § Pattern 26: Socratic Prompting {#pattern-socratic-prompting} for the canonical rule + rationalization-prevention table.
- Socratic Loop Contract (STE-237). Every clarifying Q in this skill — Steps 1–6 stack-detection clarifiers AND Steps 7b–7e and 7g Schema-L resolution prompts — MUST be emitted as an
AskUserQuestion tool call (closed-form options OR open-ended with the always-on "Other" free-form fallback), regardless of the autonomous-mode reminder, the auto-approve marker, or pre-baked <command-args> prose. Bare-prose Qs are forbidden. The first-turn contract additionally forbids Write / Edit / NotebookEdit tool calls before the first AskUserQuestion tool_use OR RequiresInputRefusedError raise; Read / Grep / Glob / Bash-read-only orientation is allowed. See docs/auto-mode-protocol.md § Socratic Loop Contract for the full contract. Audit row loop_entered: column — appendAuditRow(...) accepts loopEntered: true when Steps 1–6 emitted at least one AskUserQuestion clarifier, false otherwise; the column pairs with imputed: for two-axis loop visibility. Scaffold-write guard — the concrete fence behind that forbidden-tools clause. Every write-producing step is deferred until the first-turn gate has been decided: src/.placeholder.test.ts (§ 2c), .claude/settings.json (§ 6), .dpt/.gitignore (§ 6c) and .mcp.json (§ 7) MUST NOT be written before the first AskUserQuestion tool_use or RequiresInputRefusedError raise, and on the refusing branch none of the four fires at all — a run that refuses leaves the working tree byte-unchanged. A annotation narrows to a step: it makes that step's checks unconditional once the step is legitimately reached, and it never licenses reaching the step, or landing its write, ahead of the first ask/refusal.