fix(#373): replace unquoted $GSD_SDK with space-safe gsd_run launcher (#379)

* fix(#373): replace unquoted $GSD_SDK with space-safe gsd_run launcher

Workflow bash blocks resolved the runtime as GSD_SDK="node $GSD_TOOLS" and
invoked it unquoted ($GSD_SDK query ...). On install paths containing spaces
(e.g. /Volumes/Mini Me/...) the unquoted expansion word-split into
`node /Volumes/Mini gsd-tools.cjs ...`, failing with "Cannot find module
'/Volumes/Mini'" and getting masked by `2>/dev/null || echo "{}"` into a
silent empty state.

Replace the string variable with a single-line shell launcher that defines a
gsd_run function, invokes the runtime with a fully-quoted path and "$@", and
preserves the local-cjs / installed-gsd-tools-on-PATH fallback (#3668) plus
the loud not-found error and install hint. The launcher uses _GSD_SHIM_NAME
indirection so no workflow emits the /gsd-tools substring that the do.md
dispatcher-parity scanner would misread, and is single-line to stay within
the per-file progressive-disclosure line budgets (#2551).

The canonical launcher lives in
get-shit-done/workflows/_runtime-launcher.snippet.sh, is propagated once per
file by scripts/sync-runtime-launcher.cjs, and is locked by
tests/runtime-launcher-parity.test.cjs (fails CI on drift, on a reappearing
$GSD_SDK token, or on a /gsd-tools substring). Dependent workflow-assertion
tests are updated from $GSD_SDK to gsd_run, and the runtime launcher is
registered in CONTEXT.md.

Fixes #373

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci(#373): add changeset fragment and allow-test-rule for parity guard

The runtime-launcher parity test is a structural drift guard that reads
workflow markdown to assert the canonical launcher is present and the
retired $GSD_SDK / /gsd-tools tokens are absent; annotate it with
allow-test-rule per the no-source-grep lint escape hatch. Add the required
.changeset fragment for this user-facing fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(#373): make parity PATH-fallback assertion cross-platform (Windows)

Subtest (E) compared GSD_TOOLS against the Node-side absolute temp path,
but the value originates from git-bash which reports the POSIX form, so the
prefix comparison failed on windows-latest while the launcher itself worked
(the installed stub was invoked). Assert the resolved binary by normalized
suffix (/bin/gsd-tools, not .cjs) instead of the absolute prefix; the
behavioral stub-invocation assertion is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-05-27 20:21:05 -04:00
committed by GitHub
parent 33ebd1de19
commit 2b9fa009d3
94 changed files with 1410 additions and 1320 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 379
---
**Workflow runtime launcher no longer breaks on install paths containing spaces** — workflow bash blocks resolved the runtime via an unquoted shell string variable (`GSD_SDK="node $GSD_TOOLS"`, invoked as `$GSD_SDK query …`), which word-split on paths like `/Volumes/Mini Me/…` and was silently masked into empty `{}` state. Replaced with a single-line, space-safe `gsd_run` shell launcher (quoted path + `"$@"`), propagated once per workflow file from one canonical snippet and enforced by a parity test. The local-`.cjs` / installed-`gsd-tools`-on-PATH fallback (#3668) and the loud not-found error are preserved. Fixes #373.

View File

@@ -64,6 +64,9 @@ Single dispatch seam (`get-shit-done/bin/lib/command-routing-hub.cjs`) that cent
### Runtime Source Layout Module ### Runtime Source Layout Module
Single-runtime seam layout for this repository after SDK retirement. Runtime execution paths live under `get-shit-done/bin/lib/` and are grouped by seam concern (dispatch, manifest, handlers, runtime, observability, installer). ADR-0174 preserves the seam vocabulary and defines the canonical long-term shape as a seam-aligned TypeScript `src/` tree (`src/dispatch/`, `src/handlers/`, `src/errors/`, `src/manifest/`, `src/config/`, `src/state/`, `src/workstream/`, `src/runtime/`, `src/cli/`, `src/observability/`) compiled to CJS. Single-runtime seam layout for this repository after SDK retirement. Runtime execution paths live under `get-shit-done/bin/lib/` and are grouped by seam concern (dispatch, manifest, handlers, runtime, observability, installer). ADR-0174 preserves the seam vocabulary and defines the canonical long-term shape as a seam-aligned TypeScript `src/` tree (`src/dispatch/`, `src/handlers/`, `src/errors/`, `src/manifest/`, `src/config/`, `src/state/`, `src/workstream/`, `src/runtime/`, `src/cli/`, `src/observability/`) compiled to CJS.
### Runtime Launcher Module
Canonical space-safe shell preamble (`gsd_run`) used by every workflow bash block to invoke the GSD runtime CLI. Resolves `get-shit-done/bin/gsd-tools.cjs` via `node` when present, falls back to a `gsd-tools` binary on PATH, else errors. Single source of truth: `get-shit-done/workflows/_runtime-launcher.snippet.sh`; propagated by `scripts/sync-runtime-launcher.cjs`; enforced by `tests/runtime-launcher-parity.test.cjs`. Replaced the retired unquoted `$GSD_SDK` variable (#373).
### Dispatch Observability Module ### Dispatch Observability Module
Module owning dispatch-event creation, redaction, and logger behavior for the Command Routing Hub. Core files: `get-shit-done/bin/lib/observability/event.cjs`, `get-shit-done/bin/lib/observability/logger.cjs`, `get-shit-done/bin/lib/observability/redaction.cjs`. Contract: silent on success by default, structured JSON to stderr on error, and opt-in audit trail at `.planning/.gsd-trace.jsonl` via `GSD_AUDIT=1` or config (`audit.enabled`). Each dispatch carries a `traceId`; composed dispatches set `parentTraceId` for correlation. Module owning dispatch-event creation, redaction, and logger behavior for the Command Routing Hub. Core files: `get-shit-done/bin/lib/observability/event.cjs`, `get-shit-done/bin/lib/observability/logger.cjs`, `get-shit-done/bin/lib/observability/redaction.cjs`. Contract: silent on success by default, structured JSON to stderr on error, and opt-in audit trail at `.planning/.gsd-trace.jsonl` via `GSD_AUDIT=1` or config (`audit.enabled`). Each dispatch carries a `traceId`; composed dispatches set `parentTraceId` for correlation.

View File

@@ -0,0 +1 @@
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi

View File

@@ -19,19 +19,8 @@ cat .planning/ROADMAP.md
## Step 2: Find next backlog number ## Step 2: Find next backlog number
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" NEXT=$(gsd_run query phase.next-decimal 999 --raw)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
NEXT=$($GSD_SDK query phase.next-decimal 999 --raw)
``` ```
If no 999.x phases exist yet, `phase.next-decimal` returns `999.1`. Sparse numbering If no 999.x phases exist yet, `phase.next-decimal` returns `999.1`. Sparse numbering
@@ -64,8 +53,8 @@ Plans:
Apply the `project_code` prefix (if set in `.planning/config.json`) so the backlog directory name is consistent with all other phase-creation paths: Apply the `project_code` prefix (if set in `.planning/config.json`) so the backlog directory name is consistent with all other phase-creation paths:
```bash ```bash
SLUG=$($GSD_SDK query generate-slug "$ARGUMENTS" --raw) SLUG=$(gsd_run query generate-slug "$ARGUMENTS" --raw)
PROJECT_CODE=$($GSD_SDK query config-get project_code --raw 2>/dev/null || echo "") PROJECT_CODE=$(gsd_run query config-get project_code --raw 2>/dev/null || echo "")
PREFIX=$([ -n "$PROJECT_CODE" ] && echo "${PROJECT_CODE}-" || echo "") PREFIX=$([ -n "$PROJECT_CODE" ] && echo "${PROJECT_CODE}-" || echo "")
PHASE_DIR=".planning/phases/${PREFIX}${NEXT}-${SLUG}" PHASE_DIR=".planning/phases/${PREFIX}${NEXT}-${SLUG}"
mkdir -p "${PHASE_DIR}" mkdir -p "${PHASE_DIR}"
@@ -75,7 +64,7 @@ touch "${PHASE_DIR}/.gitkeep"
## Step 5: Commit ## Step 5: Commit
```bash ```bash
$GSD_SDK query commit "docs: add backlog item ${NEXT} — ${ARGUMENTS}" --files .planning/ROADMAP.md "${PHASE_DIR}/.gitkeep" gsd_run query commit "docs: add backlog item ${NEXT} — ${ARGUMENTS}" --files .planning/ROADMAP.md "${PHASE_DIR}/.gitkeep"
``` ```
## Step 6: Report ## Step 6: Report

View File

@@ -29,19 +29,8 @@ Exit.
Load phase operation context: Load phase operation context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "0")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "0")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -57,7 +46,7 @@ Exit.
**Delegate the phase addition to `gsd-tools.cjs query phase.add`:** **Delegate the phase addition to `gsd-tools.cjs query phase.add`:**
```bash ```bash
RESULT=$($GSD_SDK query phase.add "${description}") RESULT=$(gsd_run query phase.add "${description}")
``` ```
The CLI handles: The CLI handles:

View File

@@ -33,19 +33,8 @@ Exit.
Load phase operation context: Load phase operation context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -312,7 +301,7 @@ Create a test coverage report and present to user:
Record test generation in project state: Record test generation in project state:
```bash ```bash
$GSD_SDK query state-snapshot gsd_run query state-snapshot
``` ```
If there are passing tests to commit: If there are passing tests to commit:

View File

@@ -12,19 +12,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Load todo context: Load todo context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.todos)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.todos)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -98,7 +87,7 @@ Use values from init context: `timestamp` and `date` are already available.
Generate slug for the title: Generate slug for the title:
```bash ```bash
slug=$($GSD_SDK query generate-slug "$title" --raw) slug=$(gsd_run query generate-slug "$title" --raw)
``` ```
Write to `.planning/todos/pending/${date}-${slug}.md`: Write to `.planning/todos/pending/${date}-${slug}.md`:
@@ -133,7 +122,7 @@ If `.planning/STATE.md` exists:
Commit the todo and any updated state: Commit the todo and any updated state:
```bash ```bash
$GSD_SDK query commit "docs: capture todo - [title]" --files .planning/todos/pending/[filename] .planning/STATE.md gsd_run query commit "docs: capture todo - [title]" --files .planning/todos/pending/[filename] .planning/STATE.md
``` ```
Tool respects `commit_docs` config and gitignore automatically. Tool respects `commit_docs` config and gitignore automatically.

View File

@@ -20,19 +20,8 @@ This prevents the two most common AI development failures: choosing the wrong fr
## 1. Initialize ## 1. Initialize
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.plan-phase "$PHASE")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.plan-phase "$PHASE")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -42,15 +31,15 @@ Parse JSON for: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded
Resolve agent models: Resolve agent models:
```bash ```bash
SELECTOR_MODEL=$($GSD_SDK query resolve-model gsd-framework-selector 2>/dev/null | jq -r '.model' 2>/dev/null || true) SELECTOR_MODEL=$(gsd_run query resolve-model gsd-framework-selector 2>/dev/null | jq -r '.model' 2>/dev/null || true)
RESEARCHER_MODEL=$($GSD_SDK query resolve-model gsd-ai-researcher 2>/dev/null | jq -r '.model' 2>/dev/null || true) RESEARCHER_MODEL=$(gsd_run query resolve-model gsd-ai-researcher 2>/dev/null | jq -r '.model' 2>/dev/null || true)
DOMAIN_MODEL=$($GSD_SDK query resolve-model gsd-domain-researcher 2>/dev/null | jq -r '.model' 2>/dev/null || true) DOMAIN_MODEL=$(gsd_run query resolve-model gsd-domain-researcher 2>/dev/null | jq -r '.model' 2>/dev/null || true)
PLANNER_MODEL=$($GSD_SDK query resolve-model gsd-eval-planner 2>/dev/null | jq -r '.model' 2>/dev/null || true) PLANNER_MODEL=$(gsd_run query resolve-model gsd-eval-planner 2>/dev/null | jq -r '.model' 2>/dev/null || true)
``` ```
Check config: Check config:
```bash ```bash
AI_PHASE_ENABLED=$($GSD_SDK query config-get workflow.ai_integration_phase 2>/dev/null || echo "true") AI_PHASE_ENABLED=$(gsd_run query config-get workflow.ai_integration_phase 2>/dev/null || echo "true")
``` ```
**If `AI_PHASE_ENABLED` is `false`:** **If `AI_PHASE_ENABLED` is `false`:**
@@ -66,7 +55,7 @@ Exit workflow.
Extract phase number from $ARGUMENTS. If not provided, detect next unplanned phase. Extract phase number from $ARGUMENTS. If not provided, detect next unplanned phase.
```bash ```bash
PHASE_INFO=$($GSD_SDK query roadmap.get-phase "${PHASE}") PHASE_INFO=$(gsd_run query roadmap.get-phase "${PHASE}")
``` ```
**If `found` is false:** Error with available phases. **If `found` is false:** Error with available phases.

View File

@@ -32,19 +32,8 @@ Invoke the source audit command and capture output.
For `audit-uat` source: For `audit-uat` source:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query audit-uat 2>/dev/null || echo "{}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query audit-uat 2>/dev/null || echo "{}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -117,7 +106,7 @@ Agent(
**b. Run tests:** **b. Run tests:**
```bash ```bash
AUDIT_TEST_CMD=$($GSD_SDK query config-get workflow.test_command --default "" 2>/dev/null || true) AUDIT_TEST_CMD=$(gsd_run query config-get workflow.test_command --default "" 2>/dev/null || true)
if [ -z "$AUDIT_TEST_CMD" ]; then if [ -z "$AUDIT_TEST_CMD" ]; then
if [ -f "Makefile" ] && grep -q "^test:" Makefile; then if [ -f "Makefile" ] && grep -q "^test:" Makefile; then
AUDIT_TEST_CMD="make test" AUDIT_TEST_CMD="make test"

View File

@@ -16,35 +16,24 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
## 0. Initialize Milestone Context ## 0. Initialize Milestone Context
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.milestone-op)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.milestone-op)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_CHECKER=$($GSD_SDK query agent-skills gsd-integration-checker) AGENT_SKILLS_CHECKER=$(gsd_run query agent-skills gsd-integration-checker)
``` ```
Extract from init JSON: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `commit_docs`. Extract from init JSON: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `commit_docs`.
Resolve integration checker model: Resolve integration checker model:
```bash ```bash
integration_checker_model=$($GSD_SDK query resolve-model gsd-integration-checker --raw) integration_checker_model=$(gsd_run query resolve-model gsd-integration-checker --raw)
``` ```
## 1. Determine Milestone Scope ## 1. Determine Milestone Scope
```bash ```bash
# Get phases in milestone (sorted numerically, handles decimals) # Get phases in milestone (sorted numerically, handles decimals)
$GSD_SDK query phases.list gsd_run query phases.list
``` ```
- Parse version from arguments or detect current from ROADMAP.md - Parse version from arguments or detect current from ROADMAP.md
@@ -58,7 +47,7 @@ For each phase directory, read the VERIFICATION.md:
```bash ```bash
# For each phase, use find-phase to resolve the directory (handles archived phases) # For each phase, use find-phase to resolve the directory (handles archived phases)
PHASE_INFO=$($GSD_SDK query find-phase 01 --raw) PHASE_INFO=$(gsd_run query find-phase 01 --raw)
# Extract directory from JSON, then read VERIFICATION.md from that directory # Extract directory from JSON, then read VERIFICATION.md from that directory
# Repeat for each phase number from ROADMAP.md # Repeat for each phase number from ROADMAP.md
``` ```
@@ -127,7 +116,7 @@ For each phase's SUMMARY.md, extract `requirements-completed` from YAML frontmat
```bash ```bash
for summary in .planning/phases/*-*/*-SUMMARY.md; do for summary in .planning/phases/*-*/*-SUMMARY.md; do
[ -e "$summary" ] || continue [ -e "$summary" ] || continue
$GSD_SDK query summary-extract "$summary" --fields requirements_completed --pick requirements_completed gsd_run query summary-extract "$summary" --fields requirements_completed --pick requirements_completed
done done
``` ```
@@ -155,7 +144,7 @@ For each REQ-ID, determine status using all three sources:
Skip if `workflow.nyquist_validation` is explicitly `false` (absent = enabled). Skip if `workflow.nyquist_validation` is explicitly `false` (absent = enabled).
```bash ```bash
NYQUIST_CONFIG=$($GSD_SDK query config-get workflow.nyquist_validation --raw 2>/dev/null) NYQUIST_CONFIG=$(gsd_run query config-get workflow.nyquist_validation --raw 2>/dev/null)
``` ```
If `false`: skip entirely. If `false`: skip entirely.

View File

@@ -8,19 +8,8 @@ Cross-phase audit of all UAT and verification files. Finds every outstanding ite
Run the CLI audit: Run the CLI audit:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" AUDIT=$(gsd_run query audit-uat --raw)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
AUDIT=$($GSD_SDK query audit-uat --raw)
``` ```
Parse JSON for `results` array and `summary` object. Parse JSON for `results` array and `summary` object.

View File

@@ -48,19 +48,8 @@ When `--interactive` is set, discuss runs inline with questions (not auto-answer
Bootstrap via milestone-level init: Bootstrap via milestone-level init:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.milestone-op)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.milestone-op)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -94,7 +83,7 @@ If `INTERACTIVE` is set, display: `Mode: Interactive (discuss inline, plan+execu
Run phase discovery: Run phase discovery:
```bash ```bash
ROADMAP=$($GSD_SDK query roadmap.analyze) ROADMAP=$(gsd_run query roadmap.analyze)
``` ```
Parse the JSON `phases` array. Parse the JSON `phases` array.
@@ -153,7 +142,7 @@ Exit cleanly.
**Fetch details for each phase:** **Fetch details for each phase:**
```bash ```bash
DETAIL=$($GSD_SDK query roadmap.get-phase ${PHASE_NUM}) DETAIL=$(gsd_run query roadmap.get-phase ${PHASE_NUM})
``` ```
Extract `phase_name`, `goal`, `success_criteria` from each. Store for use in execute_phase and transition messages. Extract `phase_name`, `goal`, `success_criteria` from each. Store for use in execute_phase and transition messages.
@@ -181,7 +170,7 @@ Where N = current phase number (from the ROADMAP, e.g., 63), T = total milestone
Check if CONTEXT.md already exists for this phase: Check if CONTEXT.md already exists for this phase:
```bash ```bash
PHASE_STATE=$($GSD_SDK query init.phase-op ${PHASE_NUM}) PHASE_STATE=$(gsd_run query init.phase-op ${PHASE_NUM})
``` ```
Parse `has_context` from JSON. Parse `has_context` from JSON.
@@ -197,7 +186,7 @@ Proceed to 3b.
**If has_context is false:** Check if discuss is disabled via settings: **If has_context is false:** Check if discuss is disabled via settings:
```bash ```bash
SKIP_DISCUSS=$($GSD_SDK query config-get workflow.skip_discuss 2>/dev/null || echo "false") SKIP_DISCUSS=$(gsd_run query config-get workflow.skip_discuss 2>/dev/null || echo "false")
``` ```
**If SKIP_DISCUSS is `true`:** Skip discuss entirely — the ROADMAP phase description is the spec. Display: **If SKIP_DISCUSS is `true`:** Skip discuss entirely — the ROADMAP phase description is the spec. Display:
@@ -209,7 +198,7 @@ Phase ${PHASE_NUM}: Discuss skipped (workflow.skip_discuss=true) — using ROADM
Write a minimal CONTEXT.md so downstream plan-phase has valid input. Get phase details: Write a minimal CONTEXT.md so downstream plan-phase has valid input. Get phase details:
```bash ```bash
DETAIL=$($GSD_SDK query roadmap.get-phase ${PHASE_NUM}) DETAIL=$(gsd_run query roadmap.get-phase ${PHASE_NUM})
``` ```
Extract `goal` and `requirements` from JSON. Write `${phase_dir}/${padded_phase}-CONTEXT.md` with: Extract `goal` and `requirements` from JSON. Write `${phase_dir}/${padded_phase}-CONTEXT.md` with:
@@ -261,7 +250,7 @@ None — discuss phase skipped.
Commit the minimal context: Commit the minimal context:
```bash ```bash
$GSD_SDK query commit "docs(${PADDED_PHASE}): auto-generated context (discuss skipped)" --files "${phase_dir}/${padded_phase}-CONTEXT.md" gsd_run query commit "docs(${PADDED_PHASE}): auto-generated context (discuss skipped)" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
``` ```
Proceed to 3b. Proceed to 3b.
@@ -282,7 +271,7 @@ Skill(skill="gsd-discuss-phase", args="${PHASE_NUM}")
After discuss completes (either mode), verify context was written: After discuss completes (either mode), verify context was written:
```bash ```bash
PHASE_STATE=$($GSD_SDK query init.phase-op ${PHASE_NUM}) PHASE_STATE=$(gsd_run query init.phase-op ${PHASE_NUM})
``` ```
Check `has_context`. If false → go to handle_blocker: "Discuss for phase ${PHASE_NUM} did not produce CONTEXT.md." Check `has_context`. If false → go to handle_blocker: "Discuss for phase ${PHASE_NUM} did not produce CONTEXT.md."
@@ -292,7 +281,7 @@ Check `has_context`. If false → go to handle_blocker: "Discuss for phase ${PHA
Check if this phase has frontend indicators and whether a UI-SPEC already exists: Check if this phase has frontend indicators and whether a UI-SPEC already exists:
```bash ```bash
PHASE_SECTION=$($GSD_SDK query roadmap.get-phase ${PHASE_NUM} 2>/dev/null) PHASE_SECTION=$(gsd_run query roadmap.get-phase ${PHASE_NUM} 2>/dev/null)
# Shell-free word-boundary gate (#3718): Node.js helper — no locale env-var dependency. # Shell-free word-boundary gate (#3718): Node.js helper — no locale env-var dependency.
# Reads via stdin to avoid OS ARG_MAX limits on large phase text. # Reads via stdin to avoid OS ARG_MAX limits on large phase text.
# Path anchored to repo root; falls back to CWD if git is unavailable # Path anchored to repo root; falls back to CWD if git is unavailable
@@ -306,7 +295,7 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
Check if UI phase workflow is enabled: Check if UI phase workflow is enabled:
```bash ```bash
UI_PHASE_CFG=$($GSD_SDK query config-get workflow.ui_phase 2>/dev/null || echo "true") UI_PHASE_CFG=$(gsd_run query config-get workflow.ui_phase 2>/dev/null || echo "true")
``` ```
**If `HAS_UI` is 0 (frontend indicators found) AND `UI_SPEC_FILE` is empty (no UI-SPEC exists) AND `UI_PHASE_CFG` is not `false`:** **If `HAS_UI` is 0 (frontend indicators found) AND `UI_SPEC_FILE` is empty (no UI-SPEC exists) AND `UI_PHASE_CFG` is not `false`:**
@@ -379,7 +368,7 @@ Auto-invoke code review and fix chain. Autonomous mode chains both review and fi
**Config gate:** **Config gate:**
```bash ```bash
CODE_REVIEW_ENABLED=$($GSD_SDK query config-get workflow.code_review 2>/dev/null || echo "true") CODE_REVIEW_ENABLED=$(gsd_run query config-get workflow.code_review 2>/dev/null || echo "true")
``` ```
If `"false"`: display "Code review skipped (workflow.code_review=false)" and proceed to 3d. If `"false"`: display "Code review skipped (workflow.code_review=false)" and proceed to 3d.
@@ -407,7 +396,7 @@ VERIFY_STATUS=$(grep "^status:" "${PHASE_DIR}"/*-VERIFICATION.md 2>/dev/null | h
Where `PHASE_DIR` comes from the `init phase-op` call already made in step 3a. If the variable is not in scope, re-fetch: Where `PHASE_DIR` comes from the `init phase-op` call already made in step 3a. If the variable is not in scope, re-fetch:
```bash ```bash
PHASE_STATE=$($GSD_SDK query init.phase-op ${PHASE_NUM}) PHASE_STATE=$(gsd_run query init.phase-op ${PHASE_NUM})
``` ```
Parse `phase_dir` from the JSON. Parse `phase_dir` from the JSON.
@@ -503,7 +492,7 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
Check if UI review is enabled: Check if UI review is enabled:
```bash ```bash
UI_REVIEW_CFG=$($GSD_SDK query config-get workflow.ui_review 2>/dev/null || echo "true") UI_REVIEW_CFG=$(gsd_run query config-get workflow.ui_review 2>/dev/null || echo "true")
``` ```
**If `UI_SPEC_FILE` is not empty AND `UI_REVIEW_CFG` is not `false`:** **If `UI_SPEC_FILE` is not empty AND `UI_REVIEW_CFG` is not `false`:**
@@ -562,7 +551,7 @@ Proceed directly to lifecycle step (which handles partial completion — skips a
**Otherwise:** After each phase completes, re-read ROADMAP.md to catch phases inserted mid-execution (decimal phases like 5.1): **Otherwise:** After each phase completes, re-read ROADMAP.md to catch phases inserted mid-execution (decimal phases like 5.1):
```bash ```bash
ROADMAP=$($GSD_SDK query roadmap.analyze) ROADMAP=$(gsd_run query roadmap.analyze)
``` ```
Re-filter incomplete phases using the same logic as discover_phases: Re-filter incomplete phases using the same logic as discover_phases:

View File

@@ -12,19 +12,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Load todo context: Load todo context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.todos)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.todos)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -169,7 +158,7 @@ If todo was moved to done/, commit the change:
```bash ```bash
git rm --cached .planning/todos/pending/[filename] 2>/dev/null || true git rm --cached .planning/todos/pending/[filename] 2>/dev/null || true
$GSD_SDK query commit "docs: start work on todo - [title]" --files .planning/todos/completed/[filename] .planning/STATE.md gsd_run query commit "docs: start work on todo - [title]" --files .planning/todos/completed/[filename] .planning/STATE.md
``` ```
Tool respects `commit_docs` config and gitignore automatically. Tool respects `commit_docs` config and gitignore automatically.

View File

@@ -124,19 +124,8 @@ Repeat for all milestones in the cleanup set.
Commit the changes: Commit the changes:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query commit "chore: archive phase directories from completed milestones" --files .planning/milestones/ .planning/phases/
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query commit "chore: archive phase directories from completed milestones" --files .planning/milestones/ .planning/phases/
``` ```
</step> </step>

View File

@@ -17,20 +17,9 @@ Read all files referenced by the invoking prompt's execution_context before star
Parse arguments and load project state: Parse arguments and load project state:
```bash ```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
PHASE_ARG="${1}" PHASE_ARG="${1}"
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs"
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -86,7 +75,7 @@ FIX_REPORT_PATH="${PHASE_DIR}/${PADDED_PHASE}-REVIEW-FIX.md"
Check if code review is enabled via config: Check if code review is enabled via config:
```bash ```bash
CODE_REVIEW_ENABLED=$($GSD_SDK query config-get workflow.code_review 2>/dev/null || echo "true") CODE_REVIEW_ENABLED=$(gsd_run query config-get workflow.code_review 2>/dev/null || echo "true")
``` ```
If CODE_REVIEW_ENABLED is "false": If CODE_REVIEW_ENABLED is "false":
@@ -375,7 +364,7 @@ if [ -f "${FIX_REPORT_PATH}" ]; then
echo "REVIEW-FIX.md created at ${FIX_REPORT_PATH}" echo "REVIEW-FIX.md created at ${FIX_REPORT_PATH}"
if [ "$COMMIT_DOCS" = "true" ]; then if [ "$COMMIT_DOCS" = "true" ]; then
$GSD_SDK query commit \ gsd_run query commit \
"docs(${PADDED_PHASE}): add code review fix report" \ "docs(${PADDED_PHASE}): add code review fix report" \
--files "${FIX_REPORT_PATH}" --files "${FIX_REPORT_PATH}"
fi fi

View File

@@ -17,20 +17,9 @@ Read all files referenced by the invoking prompt's execution_context before star
Parse arguments and load project state: Parse arguments and load project state:
```bash ```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
PHASE_ARG="${1}" PHASE_ARG="${1}"
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs"
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -86,7 +75,7 @@ fi
Check if code review is enabled via config: Check if code review is enabled via config:
```bash ```bash
CODE_REVIEW_ENABLED=$($GSD_SDK query config-get workflow.code_review 2>/dev/null || echo "true") CODE_REVIEW_ENABLED=$(gsd_run query config-get workflow.code_review 2>/dev/null || echo "true")
``` ```
If CODE_REVIEW_ENABLED is "false": If CODE_REVIEW_ENABLED is "false":
@@ -109,7 +98,7 @@ Determine review depth with priority order:
if [ -n "$DEPTH_OVERRIDE" ]; then if [ -n "$DEPTH_OVERRIDE" ]; then
REVIEW_DEPTH="$DEPTH_OVERRIDE" REVIEW_DEPTH="$DEPTH_OVERRIDE"
else else
CONFIG_DEPTH=$($GSD_SDK query config-get workflow.code_review_depth 2>/dev/null || echo "") CONFIG_DEPTH=$(gsd_run query config-get workflow.code_review_depth 2>/dev/null || echo "")
REVIEW_DEPTH="${CONFIG_DEPTH:-standard}" REVIEW_DEPTH="${CONFIG_DEPTH:-standard}"
fi fi
``` ```
@@ -335,10 +324,10 @@ Optional structural cross-module pass powered by fallow.
Read fallow config gates: Read fallow config gates:
```bash ```bash
FALLOW_ENABLED=$($GSD_SDK query config-get code_quality.fallow.enabled 2>/dev/null || echo "false") FALLOW_ENABLED=$(gsd_run query config-get code_quality.fallow.enabled 2>/dev/null || echo "false")
FALLOW_SCOPE=$($GSD_SDK query config-get code_quality.fallow.scope 2>/dev/null || echo "phase") FALLOW_SCOPE=$(gsd_run query config-get code_quality.fallow.scope 2>/dev/null || echo "phase")
FALLOW_PROFILE=$($GSD_SDK query config-get code_quality.fallow.profile 2>/dev/null || echo "standard") FALLOW_PROFILE=$(gsd_run query config-get code_quality.fallow.profile 2>/dev/null || echo "standard")
FALLOW_MCP=$($GSD_SDK query config-get code_quality.fallow.mcp 2>/dev/null || echo "false") FALLOW_MCP=$(gsd_run query config-get code_quality.fallow.mcp 2>/dev/null || echo "false")
``` ```
Defaults are fail-closed and opt-in: Defaults are fail-closed and opt-in:
@@ -505,7 +494,7 @@ if [ -f "${REVIEW_PATH}" ]; then
echo "REVIEW.md created at ${REVIEW_PATH}" echo "REVIEW.md created at ${REVIEW_PATH}"
if [ "$COMMIT_DOCS" = "true" ]; then if [ "$COMMIT_DOCS" = "true" ]; then
$GSD_SDK query commit \ gsd_run query commit \
"docs(${PADDED_PHASE}): add code review report" \ "docs(${PADDED_PHASE}): add code review report" \
--files "${REVIEW_PATH}" --files "${REVIEW_PATH}"
fi fi

View File

@@ -41,19 +41,8 @@ When a milestone completes:
Before proceeding with milestone close, run the comprehensive open artifact audit. Before proceeding with milestone close, run the comprehensive open artifact audit.
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query audit-open
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query audit-open
``` ```
If the output contains open items (any section with count > 0): If the output contains open items (any section with count > 0):
@@ -96,7 +85,7 @@ SECURITY: Audit JSON output is structured data from the `audit-open` query handl
**Use `roadmap analyze` for comprehensive readiness check:** **Use `roadmap analyze` for comprehensive readiness check:**
```bash ```bash
ROADMAP=$($GSD_SDK query roadmap.analyze) ROADMAP=$(gsd_run query roadmap.analyze)
``` ```
This returns all phases with plan/summary counts and disk status. Use this to verify: This returns all phases with plan/summary counts and disk status. Use this to verify:
@@ -211,7 +200,7 @@ Extract one-liners from SUMMARY.md files using summary-extract:
# For each phase in milestone, extract one-liner # For each phase in milestone, extract one-liner
for summary in .planning/phases/*-*/*-SUMMARY.md; do for summary in .planning/phases/*-*/*-SUMMARY.md; do
[ -e "$summary" ] || continue [ -e "$summary" ] || continue
$GSD_SDK query summary-extract "$summary" --fields one_liner --pick one_liner gsd_run query summary-extract "$summary" --fields one_liner --pick one_liner
done done
``` ```
@@ -424,7 +413,7 @@ Update `.planning/ROADMAP.md` — group completed milestone phases:
**Delegate archival to `gsd-tools.cjs query milestone.complete`:** **Delegate archival to `gsd-tools.cjs query milestone.complete`:**
```bash ```bash
ARCHIVE=$($GSD_SDK query milestone.complete "v[X.Y]" --name "[Milestone Name]") ARCHIVE=$(gsd_run query milestone.complete "v[X.Y]" --name "[Milestone Name]")
``` ```
The CLI handles: The CLI handles:
@@ -506,7 +495,7 @@ Append the extracted Backlog content verbatim to the end of the newly written RO
**Safety commit — commit archive files BEFORE deleting any originals:** **Safety commit — commit archive files BEFORE deleting any originals:**
```bash ```bash
$GSD_SDK query commit "chore: archive v[X.Y] milestone files" --files .planning/milestones/v[X.Y]-ROADMAP.md .planning/milestones/v[X.Y]-REQUIREMENTS.md .planning/milestones/v[X.Y]-MILESTONE-AUDIT.md .planning/MILESTONES.md .planning/PROJECT.md .planning/STATE.md .planning/ROADMAP.md gsd_run query commit "chore: archive v[X.Y] milestone files" --files .planning/milestones/v[X.Y]-ROADMAP.md .planning/milestones/v[X.Y]-REQUIREMENTS.md .planning/milestones/v[X.Y]-MILESTONE-AUDIT.md .planning/MILESTONES.md .planning/PROJECT.md .planning/STATE.md .planning/ROADMAP.md
``` ```
This creates a durable checkpoint in git history. If anything fails after this point, the working tree can be reconstructed from git. This creates a durable checkpoint in git history. If anything fails after this point, the working tree can be reconstructed from git.
@@ -575,7 +564,7 @@ If the "## Cross-Milestone Trends" section exists, update the tables with new da
**Commit:** **Commit:**
```bash ```bash
$GSD_SDK query commit "docs: update retrospective for v${VERSION}" --files .planning/RETROSPECTIVE.md gsd_run query commit "docs: update retrospective for v${VERSION}" --files .planning/RETROSPECTIVE.md
``` ```
</step> </step>
@@ -609,7 +598,7 @@ Check branching strategy and offer merge options.
Use `init milestone-op` for context, or load config directly: Use `init milestone-op` for context, or load config directly:
```bash ```bash
INIT=$($GSD_SDK query init.execute-phase "1") INIT=$(gsd_run query init.execute-phase "1")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -617,7 +606,7 @@ Extract `branching_strategy`, `phase_branch_template`, `milestone_branch_templat
Detect base branch: Detect base branch:
```bash ```bash
BASE_BRANCH=$($GSD_SDK query config-get git.base_branch 2>/dev/null || echo "") BASE_BRANCH=$(gsd_run query config-get git.base_branch 2>/dev/null || echo "")
if [ -z "$BASE_BRANCH" ] || [ "$BASE_BRANCH" = "null" ]; then if [ -z "$BASE_BRANCH" ] || [ "$BASE_BRANCH" = "null" ]; then
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||') BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')
BASE_BRANCH="${BASE_BRANCH:-main}" BASE_BRANCH="${BASE_BRANCH:-main}"

View File

@@ -16,30 +16,19 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
## 0. Initialize Context ## 0. Initialize Context
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query state.load)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query state.load)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
Extract `commit_docs` from init JSON. Resolve debugger model: Extract `commit_docs` from init JSON. Resolve debugger model:
```bash ```bash
debugger_model=$($GSD_SDK query resolve-model gsd-debugger 2>/dev/null | jq -r '.model' 2>/dev/null || true) debugger_model=$(gsd_run query resolve-model gsd-debugger 2>/dev/null | jq -r '.model' 2>/dev/null || true)
``` ```
Read TDD mode from config: Read TDD mode from config:
```bash ```bash
TDD_MODE=$($GSD_SDK query config-get workflow.tdd_mode 2>/dev/null | jq -r 'if type == "boolean" then tostring else . end' 2>/dev/null || echo "false") TDD_MODE=$(gsd_run query config-get workflow.tdd_mode 2>/dev/null | jq -r 'if type == "boolean" then tostring else . end' 2>/dev/null || echo "false")
``` ```
## 1a. LIST subcommand ## 1a. LIST subcommand

View File

@@ -58,19 +58,8 @@ gaps = [
**Read worktree config:** **Read worktree config:**
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees 2>/dev/null || echo "true")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
USE_WORKTREES=$($GSD_SDK query config-get workflow.use_worktrees 2>/dev/null || echo "true")
``` ```
**Report diagnosis plan to user:** **Report diagnosis plan to user:**
@@ -99,7 +88,7 @@ This runs in parallel - all gaps investigated simultaneously.
**Load agent skills:** **Load agent skills:**
```bash ```bash
AGENT_SKILLS_DEBUGGER=$($GSD_SDK query agent-skills gsd-debugger) AGENT_SKILLS_DEBUGGER=$(gsd_run query agent-skills gsd-debugger)
EXPECTED_BASE=$(git rev-parse HEAD) EXPECTED_BASE=$(git rev-parse HEAD)
``` ```
@@ -191,7 +180,7 @@ Update status in frontmatter to "diagnosed".
Commit the updated UAT.md: Commit the updated UAT.md:
```bash ```bash
$GSD_SDK query commit "docs({phase_num}): add root causes from diagnosis" --files ".planning/phases/XX-name/{phase_num}-UAT.md" gsd_run query commit "docs({phase_num}): add root causes from diagnosis" --files ".planning/phases/XX-name/{phase_num}-UAT.md"
``` ```
</step> </step>

View File

@@ -64,21 +64,10 @@ plain-text numbered list and ask the user to type their choice number.
Phase number from argument (required). Phase number from argument (required).
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${PHASE}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_ANALYZER=$($GSD_SDK query agent-skills gsd-assumptions-analyzer) AGENT_SKILLS_ANALYZER=$(gsd_run query agent-skills gsd-assumptions-analyzer)
``` ```
Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`,
@@ -183,7 +172,7 @@ Structure the extracted information for use in assumption generation.
Check if any pending todos are relevant to this phase's scope. Check if any pending todos are relevant to this phase's scope.
```bash ```bash
TODO_MATCHES=$($GSD_SDK query todo.match-phase "${PHASE_NUMBER}") TODO_MATCHES=$(gsd_run query todo.match-phase "${PHASE_NUMBER}")
``` ```
Parse JSON for: `todo_count`, `matches[]`. Parse JSON for: `todo_count`, `matches[]`.
@@ -564,7 +553,7 @@ Write file.
Commit phase context and discussion log: Commit phase context and discussion log:
```bash ```bash
$GSD_SDK query commit "docs(${padded_phase}): capture phase context (assumptions mode)" --files "${phase_dir}/${padded_phase}-CONTEXT.md" "${phase_dir}/${padded_phase}-DISCUSSION-LOG.md" gsd_run query commit "docs(${padded_phase}): capture phase context (assumptions mode)" --files "${phase_dir}/${padded_phase}-CONTEXT.md" "${phase_dir}/${padded_phase}-DISCUSSION-LOG.md"
``` ```
Confirm: "Committed: docs(${padded_phase}): capture phase context (assumptions mode)" Confirm: "Committed: docs(${padded_phase}): capture phase context (assumptions mode)"
@@ -574,7 +563,7 @@ Confirm: "Committed: docs(${padded_phase}): capture phase context (assumptions m
Update STATE.md with session info: Update STATE.md with session info:
```bash ```bash
$GSD_SDK query state.record-session \ gsd_run query state.record-session \
--stopped-at "Phase ${PHASE} context gathered (assumptions mode)" \ --stopped-at "Phase ${PHASE} context gathered (assumptions mode)" \
--resume-file "${phase_dir}/${padded_phase}-CONTEXT.md" --resume-file "${phase_dir}/${padded_phase}-CONTEXT.md"
``` ```
@@ -582,7 +571,7 @@ $GSD_SDK query state.record-session \
Commit STATE.md: Commit STATE.md:
```bash ```bash
$GSD_SDK query commit "docs(state): record phase ${PHASE} context session" --files .planning/STATE.md gsd_run query commit "docs(state): record phase ${PHASE} context session" --files .planning/STATE.md
``` ```
</step> </step>
@@ -635,17 +624,17 @@ Check for auto-advance trigger:
2. Sync chain flag: 2. Sync chain flag:
```bash ```bash
if [[ ! "$ARGUMENTS" =~ --auto ]]; then if [[ ! "$ARGUMENTS" =~ --auto ]]; then
$GSD_SDK query config-set workflow._auto_chain_active false || true gsd_run query config-set workflow._auto_chain_active false || true
fi fi
``` ```
3. Read consolidated auto-mode (`active` = chain flag OR user preference): 3. Read consolidated auto-mode (`active` = chain flag OR user preference):
```bash ```bash
AUTO_MODE=$($GSD_SDK query check auto-mode --pick active 2>/dev/null || echo "false") AUTO_MODE=$(gsd_run query check auto-mode --pick active 2>/dev/null || echo "false")
``` ```
**If `--auto` flag present AND `AUTO_MODE` is not true:** **If `--auto` flag present AND `AUTO_MODE` is not true:**
```bash ```bash
$GSD_SDK query config-set workflow._auto_chain_active true gsd_run query config-set workflow._auto_chain_active true
``` ```
**If `--auto` flag present OR `AUTO_MODE` is true:** **If `--auto` flag present OR `AUTO_MODE` is true:**

View File

@@ -109,9 +109,9 @@ Phase: "API documentation" → Structure/navigation, Code examples depth,
Phase number from argument (required). Phase number from argument (required).
```bash ```bash
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs"; if [ -f "$GSD_TOOLS" ]; then GSD_SDK="node $GSD_TOOLS"; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; GSD_SDK="$GSD_TOOLS"; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2; exit 1; fi _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
INIT=$($GSD_SDK query init.phase-op "${PHASE}"); [[ "$INIT" == @file:* ]] && INIT=$(cat "${INIT#@file:}") INIT=$(gsd_run query init.phase-op "${PHASE}"); [[ "$INIT" == @file:* ]] && INIT=$(cat "${INIT#@file:}")
AGENT_SKILLS_ADVISOR=$($GSD_SDK query agent-skills gsd-advisor-researcher) AGENT_SKILLS_ADVISOR=$(gsd_run query agent-skills gsd-advisor-researcher)
``` ```
Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `has_verification`, `plan_count`, `roadmap_exists`, `planning_exists`, `response_language`. Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `has_verification`, `plan_count`, `roadmap_exists`, `planning_exists`, `response_language`.
@@ -265,7 +265,7 @@ Build internal `<prior_decisions>` with sections for Project-Level (from PROJECT
Check pending todos for matches with this phase's scope. Check pending todos for matches with this phase's scope.
```bash ```bash
TODO_MATCHES=$($GSD_SDK query todo.match-phase "${PHASE_NUMBER}") TODO_MATCHES=$(gsd_run query todo.match-phase "${PHASE_NUMBER}")
``` ```
Parse JSON for: `todo_count`, `matches[]` (each with `file`, `title`, `area`, `score`, `reasons`). Parse JSON for: `todo_count`, `matches[]` (each with `file`, `title`, `area`, `score`, `reasons`).
@@ -447,7 +447,7 @@ rm -f "${phase_dir}/${padded_phase}-DISCUSS-CHECKPOINT.json"
Commit phase context and discussion log: Commit phase context and discussion log:
```bash ```bash
$GSD_SDK query commit "docs(${padded_phase}): capture phase context" --files "${phase_dir}/${padded_phase}-CONTEXT.md" "${phase_dir}/${padded_phase}-DISCUSSION-LOG.md" gsd_run query commit "docs(${padded_phase}): capture phase context" --files "${phase_dir}/${padded_phase}-CONTEXT.md" "${phase_dir}/${padded_phase}-DISCUSSION-LOG.md"
``` ```
Confirm: "Committed: docs(${padded_phase}): capture phase context" Confirm: "Committed: docs(${padded_phase}): capture phase context"
@@ -457,11 +457,11 @@ Confirm: "Committed: docs(${padded_phase}): capture phase context"
Update STATE.md with session info: Update STATE.md with session info:
```bash ```bash
$GSD_SDK query state.record-session \ gsd_run query state.record-session \
--stopped-at "Phase ${PHASE} context gathered" \ --stopped-at "Phase ${PHASE} context gathered" \
--resume-file "${phase_dir}/${padded_phase}-CONTEXT.md" --resume-file "${phase_dir}/${padded_phase}-CONTEXT.md"
$GSD_SDK query commit "docs(state): record phase ${PHASE} context session" --files .planning/STATE.md gsd_run query commit "docs(state): record phase ${PHASE} context session" --files .planning/STATE.md
``` ```
</step> </step>

View File

@@ -37,7 +37,8 @@ Map to calibration tier:
Resolve advisor model: Resolve advisor model:
```bash ```bash
ADVISOR_MODEL=$($GSD_SDK query resolve-model gsd-advisor-researcher --raw) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
ADVISOR_MODEL=$(gsd_run query resolve-model gsd-advisor-researcher --raw)
``` ```
## Non-technical owner detection ## Non-technical owner detection

View File

@@ -40,7 +40,8 @@ that the next pass treats as gaps, consuming unbounded time and resources.
Check the pass cap from config: Check the pass cap from config:
```bash ```bash
MAX_PASSES=$($GSD_SDK query config-get workflow.max_discuss_passes 2>/dev/null || echo "3") _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
MAX_PASSES=$(gsd_run query config-get workflow.max_discuss_passes 2>/dev/null || echo "3")
``` ```
If you have already written and committed CONTEXT.md, the discuss step is If you have already written and committed CONTEXT.md, the discuss step is

View File

@@ -25,20 +25,21 @@
interrupted `--auto` chain. This does NOT touch `workflow.auto_advance` interrupted `--auto` chain. This does NOT touch `workflow.auto_advance`
(the user's persistent settings preference): (the user's persistent settings preference):
```bash ```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then
$GSD_SDK query config-set workflow._auto_chain_active false || true gsd_run query config-set workflow._auto_chain_active false || true
fi fi
``` ```
3. Read consolidated auto-mode (`active` = chain flag OR user preference): 3. Read consolidated auto-mode (`active` = chain flag OR user preference):
```bash ```bash
AUTO_MODE=$($GSD_SDK query check auto-mode --pick active 2>/dev/null || echo "false") AUTO_MODE=$(gsd_run query check auto-mode --pick active 2>/dev/null || echo "false")
``` ```
4. **If `--auto` or `--chain` flag present AND `AUTO_MODE` is not true:** 4. **If `--auto` or `--chain` flag present AND `AUTO_MODE` is not true:**
Persist chain flag to config (handles direct usage without new-project): Persist chain flag to config (handles direct usage without new-project):
```bash ```bash
$GSD_SDK query config-set workflow._auto_chain_active true gsd_run query config-set workflow._auto_chain_active true
``` ```
5. **If `--auto` flag present OR `--chain` flag present OR `AUTO_MODE` is 5. **If `--auto` flag present OR `--chain` flag present OR `AUTO_MODE` is

View File

@@ -26,20 +26,8 @@ Wait for response before continuing.
**Check if project exists.** **Check if project exists.**
```bash ```bash
# SDK resolution: prefer local node shim, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
_GSD_SHIM_NAME="gsd-tools.cjs" INIT=$(gsd_run query state.load 2>/dev/null)
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query state.load 2>/dev/null)
``` ```
Track whether `.planning/` exists — some routes require it, others don't. Track whether `.planning/` exists — some routes require it, others don't.

View File

@@ -14,21 +14,10 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
Load docs-update context: Load docs-update context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query docs-init)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query docs-init)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS=$($GSD_SDK query agent-skills gsd-doc-writer) AGENT_SKILLS=$(gsd_run query agent-skills gsd-doc-writer)
``` ```
Extract from init JSON: Extract from init JSON:
@@ -1072,7 +1061,7 @@ Only run this step if `commit_docs` is `true` from the init JSON. If `commit_doc
Assemble the list of files that were actually generated (do not include files that failed or were skipped): Assemble the list of files that were actually generated (do not include files that failed or were skipped):
```bash ```bash
$GSD_SDK query commit "docs: generate project documentation" \ gsd_run query commit "docs: generate project documentation" \
--files README.md docs/ARCHITECTURE.md docs/CONFIGURATION.md docs/GETTING-STARTED.md docs/DEVELOPMENT.md docs/TESTING.md --files README.md docs/ARCHITECTURE.md docs/CONFIGURATION.md docs/GETTING-STARTED.md docs/DEVELOPMENT.md docs/TESTING.md
# Append any conditional docs that were generated: # Append any conditional docs that were generated:
# --files ... docs/API.md docs/DEPLOYMENT.md CONTRIBUTING.md # --files ... docs/API.md docs/DEPLOYMENT.md CONTRIBUTING.md

View File

@@ -34,19 +34,8 @@ Exit.
Load phase operation context: Load phase operation context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${target}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${target}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -62,7 +51,7 @@ Exit.
Read the current phase section from ROADMAP.md: Read the current phase section from ROADMAP.md:
```bash ```bash
PHASE_DATA=$($GSD_SDK query roadmap get-phase "${target}") PHASE_DATA=$(gsd_run query roadmap get-phase "${target}")
``` ```
Parse the JSON result. If `found` is false: Parse the JSON result. If `found` is false:
@@ -90,7 +79,7 @@ Also parse the full section text to extract additional fields not in the SDK res
Determine the phase status from disk. Compare against STATE.md current phase: Determine the phase status from disk. Compare against STATE.md current phase:
```bash ```bash
ANALYZE=$($GSD_SDK query roadmap analyze) ANALYZE=$(gsd_run query roadmap analyze)
``` ```
Find the phase entry in the `phases` array. Extract `disk_status`. Find the phase entry in the `phases` array. Extract `disk_status`.
@@ -192,7 +181,7 @@ Wait for user input. Use the clarified intent to rewrite all fields:
If `depends_on` is being updated (or preserved as non-empty), validate that every referenced phase number exists in ROADMAP.md: If `depends_on` is being updated (or preserved as non-empty), validate that every referenced phase number exists in ROADMAP.md:
```bash ```bash
ALL_PHASES=$($GSD_SDK query roadmap analyze) ALL_PHASES=$(gsd_run query roadmap analyze)
``` ```
Parse the `phases` array to get all valid phase numbers. Parse the `phases` array to get all valid phase numbers.
@@ -251,7 +240,7 @@ Read the full ROADMAP.md content, locate the phase section by its header (`## Ph
After writing ROADMAP.md, update STATE.md Roadmap Evolution: After writing ROADMAP.md, update STATE.md Roadmap Evolution:
```bash ```bash
$GSD_SDK query state.add-roadmap-evolution \ gsd_run query state.add-roadmap-evolution \
--phase {target} \ --phase {target} \
--action edited \ --action edited \
--note "edited fields: {changed_field_list}" --note "edited fields: {changed_field_list}"

View File

@@ -13,26 +13,15 @@ Use after /gsd:execute-phase to verify that the evaluation strategy from AI-SPEC
## 0. Initialize ## 0. Initialize
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `commit_docs`. Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `commit_docs`.
```bash ```bash
AUDITOR_MODEL=$($GSD_SDK query resolve-model gsd-eval-auditor 2>/dev/null | jq -r '.model' 2>/dev/null || true) AUDITOR_MODEL=$(gsd_run query resolve-model gsd-eval-auditor 2>/dev/null | jq -r '.model' 2>/dev/null || true)
``` ```
Display banner: Display banner:

View File

@@ -66,21 +66,10 @@ If `--wave` is absent, preserve the current behavior of executing all incomplete
Load all context in one call: Load all context in one call:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.execute-phase "${PHASE_ARG}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.execute-phase "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS=$($GSD_SDK query agent-skills gsd-executor) AGENT_SKILLS=$(gsd_run query agent-skills gsd-executor)
``` ```
Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`, `phase_req_ids`, `response_language`. Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`, `phase_req_ids`, `response_language`.
@@ -92,17 +81,17 @@ Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelizat
Read runtime/worktree config and fail closed before any executor dispatch: Read runtime/worktree config and fail closed before any executor dispatch:
```bash ```bash
RUNTIME=$($GSD_SDK query config-get runtime --default claude 2>/dev/null || echo "claude") RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo "claude")
USE_WORKTREES=$($GSD_SDK query config-get workflow.use_worktrees 2>/dev/null || echo "true") USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees 2>/dev/null || echo "true")
EXECUTOR_STALL_INTERVAL_MINUTES=$($GSD_SDK query config-get executor.stall_detect_interval_minutes 2>/dev/null || echo "5") EXECUTOR_STALL_INTERVAL_MINUTES=$(gsd_run query config-get executor.stall_detect_interval_minutes 2>/dev/null || echo "5")
EXECUTOR_STALL_THRESHOLD_MINUTES=$($GSD_SDK query config-get executor.stall_threshold_minutes 2>/dev/null || echo "10") EXECUTOR_STALL_THRESHOLD_MINUTES=$(gsd_run query config-get executor.stall_threshold_minutes 2>/dev/null || echo "10")
if [ "$RUNTIME" = "codex" ] && [ "$USE_WORKTREES" != "false" ]; then if [ "$RUNTIME" = "codex" ] && [ "$USE_WORKTREES" != "false" ]; then
echo "FATAL: Codex execute-phase worktree isolation is unsupported. Set workflow.use_worktrees=false or use a runtime with Agent isolation=\"worktree\" support." >&2 echo "FATAL: Codex execute-phase worktree isolation is unsupported. Set workflow.use_worktrees=false or use a runtime with Agent isolation=\"worktree\" support." >&2
exit 1 exit 1
fi fi
# Sweep orphaned locked worktrees from prior crashed sessions before spawning executors (#3707). # Sweep orphaned locked worktrees from prior crashed sessions before spawning executors (#3707).
[ "$USE_WORKTREES" != "false" ] && $GSD_SDK query worktree.reap-orphans 2>/dev/null || true [ "$USE_WORKTREES" != "false" ] && gsd_run query worktree.reap-orphans 2>/dev/null || true
``` ```
Codex maps subagents to `spawn_agent`, which has no direct Codex mapping for Claude Code's `isolation="worktree"` parameter. Failing closed prevents main-checkout edits while the workflow believes agents are isolated. Codex maps subagents to `spawn_agent`, which has no direct Codex mapping for Claude Code's `isolation="worktree"` parameter. Failing closed prevents main-checkout edits while the workflow believes agents are isolated.
@@ -125,7 +114,7 @@ When `USE_WORKTREES` (project-level) is `false`, all executor agents run without
Read context window size for adaptive prompt enrichment: Read context window size for adaptive prompt enrichment:
```bash ```bash
CONTEXT_WINDOW=$($GSD_SDK query config-get context_window 2>/dev/null || echo "200000") CONTEXT_WINDOW=$(gsd_run query config-get context_window 2>/dev/null || echo "200000")
``` ```
When `CONTEXT_WINDOW >= 500000` (1M-class models), subagent prompts include richer context: When `CONTEXT_WINDOW >= 500000` (1M-class models), subagent prompts include richer context:
@@ -157,7 +146,7 @@ inline path for each plan.
```bash ```bash
# REQUIRED: prevents stale auto-chain from previous --auto runs # REQUIRED: prevents stale auto-chain from previous --auto runs
if [[ ! "$ARGUMENTS" =~ --auto ]]; then if [[ ! "$ARGUMENTS" =~ --auto ]]; then
$GSD_SDK query config-set workflow._auto_chain_active false || true gsd_run query config-set workflow._auto_chain_active false || true
fi fi
``` ```
@@ -165,8 +154,8 @@ Resolve `MVP_MODE` once via the centralized `phase.mvp-mode` query verb (precede
```bash ```bash
MVP_FLAG_ARG="" MVP_FLAG_ARG=""
if [[ "$ARGUMENTS" =~ (^|[[:space:]])--mvp([[:space:]]|$) ]]; then MVP_FLAG_ARG="--cli-flag"; fi if [[ "$ARGUMENTS" =~ (^|[[:space:]])--mvp([[:space:]]|$) ]]; then MVP_FLAG_ARG="--cli-flag"; fi
MVP_MODE=$($GSD_SDK query phase.mvp-mode "${PHASE_NUMBER}" $MVP_FLAG_ARG --pick active) MVP_MODE=$(gsd_run query phase.mvp-mode "${PHASE_NUMBER}" $MVP_FLAG_ARG --pick active)
TDD_MODE=$($GSD_SDK query config-get workflow.tdd_mode 2>/dev/null || echo "false") TDD_MODE=$(gsd_run query config-get workflow.tdd_mode 2>/dev/null || echo "false")
``` ```
<step name="safe_resume_gate"> <step name="safe_resume_gate">
@@ -188,11 +177,11 @@ Offer these recovery options:
**MVP+TDD gate.** Task-scoped enforcement runs inside plan execution (immediately before each implementation step), where `TASK_FILE`, `PLAN_ID`, and `TASK_ID` are defined. Keep the same predicate and RED-commit contract: **MVP+TDD gate.** Task-scoped enforcement runs inside plan execution (immediately before each implementation step), where `TASK_FILE`, `PLAN_ID`, and `TASK_ID` are defined. Keep the same predicate and RED-commit contract:
```bash ```bash
if [ "$MVP_MODE" = "true" ] && [ "$TDD_MODE" = "true" ]; then if [ "$MVP_MODE" = "true" ] && [ "$TDD_MODE" = "true" ]; then
IS_BEHAVIOR_ADDING=$($GSD_SDK query task.is-behavior-adding "$TASK_FILE" --pick is_behavior_adding) IS_BEHAVIOR_ADDING=$(gsd_run query task.is-behavior-adding "$TASK_FILE" --pick is_behavior_adding)
if [ "$IS_BEHAVIOR_ADDING" = "true" ]; then if [ "$IS_BEHAVIOR_ADDING" = "true" ]; then
RED_COMMIT=$(git log --oneline --grep="^test(${PHASE_NUMBER}-${PLAN_ID}):" -- "**/*.test.*" "**/*.spec.*" "tests/" | head -1) RED_COMMIT=$(git log --oneline --grep="^test(${PHASE_NUMBER}-${PLAN_ID}):" -- "**/*.test.*" "**/*.spec.*" "tests/" | head -1)
if [ -z "$RED_COMMIT" ]; then if [ -z "$RED_COMMIT" ]; then
$GSD_SDK query state.update last_gate_trip "${PLAN_ID}/${TASK_ID}" || true gsd_run query state.update last_gate_trip "${PLAN_ID}/${TASK_ID}" || true
echo "MVP+TDD GATE TRIPPED: missing RED commit for ${PLAN_ID}/${TASK_ID}" echo "MVP+TDD GATE TRIPPED: missing RED commit for ${PLAN_ID}/${TASK_ID}"
exit 1 exit 1
fi fi
@@ -318,7 +307,7 @@ Report: "Found {plan_count} plans in {phase_dir} ({incomplete_count} incomplete)
**Update STATE.md for phase start:** **Update STATE.md for phase start:**
```bash ```bash
$GSD_SDK query state.begin-phase --phase "${PHASE_NUMBER}" --name "${PHASE_NAME}" --plans "${PLAN_COUNT}" gsd_run query state.begin-phase --phase "${PHASE_NUMBER}" --name "${PHASE_NAME}" --plans "${PLAN_COUNT}"
``` ```
This updates Status, Last Activity, Current focus, Current Position, and plan counts in STATE.md so frontmatter and body text reflect the active phase immediately. This updates Status, Last Activity, Current focus, Current Position, and plan counts in STATE.md so frontmatter and body text reflect the active phase immediately.
</step> </step>
@@ -327,7 +316,7 @@ This updates Status, Last Activity, Current focus, Current Position, and plan co
Load plan inventory with wave grouping in one call: Load plan inventory with wave grouping in one call:
```bash ```bash
PLAN_INDEX=$($GSD_SDK query phase-plan-index "${PHASE_NUMBER}") PLAN_INDEX=$(gsd_run query phase-plan-index "${PHASE_NUMBER}")
``` ```
Parse JSON for: `phase`, `plans[]` (each with `id`, `wave`, `autonomous`, `objective`, `files_modified`, `task_count`, `has_summary`), `waves` (map of wave number → plan IDs), `incomplete`, `has_checkpoints`. Parse JSON for: `phase`, `plans[]` (each with `id`, `wave`, `autonomous`, `objective`, `files_modified`, `task_count`, `has_summary`), `waves` (map of wave number → plan IDs), `incomplete`, `has_checkpoints`.
@@ -369,9 +358,9 @@ executor skips them.
`workflow.cross_ai_execution` is `true`. Plans matching both conditions are marked for cross-AI. `workflow.cross_ai_execution` is `true`. Plans matching both conditions are marked for cross-AI.
```bash ```bash
CROSS_AI_ENABLED=$($GSD_SDK query config-get workflow.cross_ai_execution 2>/dev/null || echo "false") CROSS_AI_ENABLED=$(gsd_run query config-get workflow.cross_ai_execution 2>/dev/null || echo "false")
CROSS_AI_CMD=$($GSD_SDK query config-get workflow.cross_ai_command 2>/dev/null || echo "") CROSS_AI_CMD=$(gsd_run query config-get workflow.cross_ai_command 2>/dev/null || echo "")
CROSS_AI_TIMEOUT=$($GSD_SDK query config-get workflow.cross_ai_timeout 2>/dev/null || echo "300") CROSS_AI_TIMEOUT=$(gsd_run query config-get workflow.cross_ai_timeout 2>/dev/null || echo "300")
``` ```
**If no plans are marked for cross-AI:** Skip to execute_waves. **If no plans are marked for cross-AI:** Skip to execute_waves.
@@ -731,7 +720,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
5. **Post-wave hook validation (parallel mode only):** Hooks run on every executor commit by default (#2924); this post-wave run only fires when `workflow.worktree_skip_hooks=true` opted out of per-commit hooks: 5. **Post-wave hook validation (parallel mode only):** Hooks run on every executor commit by default (#2924); this post-wave run only fires when `workflow.worktree_skip_hooks=true` opted out of per-commit hooks:
```bash ```bash
SKIP_HOOKS=$($GSD_SDK query config-get workflow.worktree_skip_hooks 2>/dev/null || echo "false") SKIP_HOOKS=$(gsd_run query config-get workflow.worktree_skip_hooks 2>/dev/null || echo "false")
if [ "$SKIP_HOOKS" = "true" ]; then if [ "$SKIP_HOOKS" = "true" ]; then
# Stash uncommitted changes under a named ref so we always pop (bare `git stash` strands them on hook/script failure). #3542: `refs/stash` is shared across worktrees, so this helper runs ONLY in the orchestrator's main checkout after all wave worktrees have been merged + removed; executors are forbidden from running any `git stash` subcommand (see `<destructive_git_prohibition>` in `agents/gsd-executor.md`). # Stash uncommitted changes under a named ref so we always pop (bare `git stash` strands them on hook/script failure). #3542: `refs/stash` is shared across worktrees, so this helper runs ONLY in the orchestrator's main checkout after all wave worktrees have been merged + removed; executors are forbidden from running any `git stash` subcommand (see `<destructive_git_prohibition>` in `agents/gsd-executor.md`).
STASHED=false STASHED=false
@@ -774,7 +763,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
[ -z "${EXPECTED_BRANCH:-}" ] || [ "$ORCH_BRANCH" = "$EXPECTED_BRANCH" ] || { echo "FATAL: orchestrator on '$ORCH_BRANCH' but expected '$EXPECTED_BRANCH' before worktree cleanup — refusing to merge (#3174-class drift)" >&2; exit 1; } [ -z "${EXPECTED_BRANCH:-}" ] || [ "$ORCH_BRANCH" = "$EXPECTED_BRANCH" ] || { echo "FATAL: orchestrator on '$ORCH_BRANCH' but expected '$EXPECTED_BRANCH' before worktree cleanup — refusing to merge (#3174-class drift)" >&2; exit 1; }
# Fail closed: SDK refusal (safety guard #3174/#3384) must surface — do not swallow exit 1. # Fail closed: SDK refusal (safety guard #3174/#3384) must surface — do not swallow exit 1.
$GSD_SDK query worktree.cleanup-wave --manifest "$WAVE_WORKTREE_MANIFEST" || exit 1 gsd_run query worktree.cleanup-wave --manifest "$WAVE_WORKTREE_MANIFEST" || exit 1
``` ```
**Cleanup-tail snippet (use after any wave whose merges did not flow through the templated path above):** **Cleanup-tail snippet (use after any wave whose merges did not flow through the templated path above):**
@@ -847,12 +836,12 @@ increases monotonically across waves. `{status}` is `complete` (success),
if [ "${TEST_EXIT}" -eq 0 ]; then if [ "${TEST_EXIT}" -eq 0 ]; then
# Update ROADMAP plan progress for each completed plan in this wave # Update ROADMAP plan progress for each completed plan in this wave
for plan_id in {completed_plan_ids}; do for plan_id in {completed_plan_ids}; do
$GSD_SDK query roadmap.update-plan-progress "${PHASE_NUMBER}" "${plan_id}" "complete" gsd_run query roadmap.update-plan-progress "${PHASE_NUMBER}" "${plan_id}" "complete"
done done
# Only commit tracking files if they actually changed # Only commit tracking files if they actually changed
if ! git diff --quiet .planning/ROADMAP.md .planning/STATE.md 2>/dev/null; then if ! git diff --quiet .planning/ROADMAP.md .planning/STATE.md 2>/dev/null; then
$GSD_SDK query commit "docs(phase-${PHASE_NUMBER}): update tracking after wave ${N}" --files .planning/ROADMAP.md .planning/STATE.md gsd_run query commit "docs(phase-${PHASE_NUMBER}): update tracking after wave ${N}" --files .planning/ROADMAP.md .planning/STATE.md
fi fi
elif [ "${TEST_EXIT}" -eq 124 ]; then elif [ "${TEST_EXIT}" -eq 124 ]; then
echo "⚠ Skipping tracking update — test suite timed out. Plans remain in-progress. Run tests manually to confirm." echo "⚠ Skipping tracking update — test suite timed out. Plans remain in-progress. Run tests manually to confirm."
@@ -928,7 +917,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
7. **Handle failures:** 7. **Handle failures:**
**Step 7.0 — classify before branching (#3095):** **Step 7.0 — classify before branching (#3095):**
```bash ```bash
CLASS_JSON=$($GSD_SDK query agent.classify-failure -- "$AGENT_RETURN_BODY") CLASS_JSON=$(gsd_run query agent.classify-failure -- "$AGENT_RETURN_BODY")
CLASS=$(echo "$CLASS_JSON" | jq -r '.class') CLASS=$(echo "$CLASS_JSON" | jq -r '.class')
SENTINEL=$(echo "$CLASS_JSON" | jq -r '.sentinel // empty') SENTINEL=$(echo "$CLASS_JSON" | jq -r '.sentinel // empty')
RETRY_AFTER=$(echo "$CLASS_JSON" | jq -r '.retryAfterSeconds // empty') RETRY_AFTER=$(echo "$CLASS_JSON" | jq -r '.retryAfterSeconds // empty')
@@ -967,7 +956,7 @@ Plans with `autonomous: false` require user interaction.
**Auto-mode checkpoint handling:** **Auto-mode checkpoint handling:**
Read auto-advance config (chain flag OR user preference — same boolean as `check.auto-mode`): Read auto-advance config (chain flag OR user preference — same boolean as `check.auto-mode`):
```bash ```bash
AUTO_MODE=$($GSD_SDK query check auto-mode --pick active 2>/dev/null || echo "false") AUTO_MODE=$(gsd_run query check auto-mode --pick active 2>/dev/null || echo "false")
``` ```
When executor returns a checkpoint AND `AUTO_MODE` is `true`: When executor returns a checkpoint AND `AUTO_MODE` is `true`:
@@ -1028,7 +1017,7 @@ After all waves:
**Security gate check:** **Security gate check:**
```bash ```bash
SECURITY_CFG=$($GSD_SDK query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true") SECURITY_CFG=$(gsd_run query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true")
SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1) SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
``` ```
@@ -1052,7 +1041,7 @@ If `SECURITY_CFG` is `true` AND SECURITY.md exists: check frontmatter `threats_o
**Optional step — TDD collaborative review.** **Optional step — TDD collaborative review.**
```bash ```bash
TDD_MODE=$($GSD_SDK query config-get workflow.tdd_mode 2>/dev/null || echo "false") TDD_MODE=$(gsd_run query config-get workflow.tdd_mode 2>/dev/null || echo "false")
``` ```
**Skip if `TDD_MODE` is `false`.** **Skip if `TDD_MODE` is `false`.**
@@ -1105,7 +1094,7 @@ The verifier agent (step `verify_phase_goal`) still checks TDD discipline in bot
If `WAVE_FILTER` was used, re-run plan discovery after execution: If `WAVE_FILTER` was used, re-run plan discovery after execution:
```bash ```bash
POST_PLAN_INDEX=$($GSD_SDK query phase-plan-index "${PHASE_NUMBER}") POST_PLAN_INDEX=$(gsd_run query phase-plan-index "${PHASE_NUMBER}")
``` ```
Apply the same "incomplete" filtering rules as earlier: Apply the same "incomplete" filtering rules as earlier:
@@ -1137,7 +1126,7 @@ Selected wave finished successfully. This phase still has incomplete plans, so p
**Config gate:** **Config gate:**
```bash ```bash
CODE_REVIEW_ENABLED=$($GSD_SDK query config-get workflow.code_review 2>/dev/null || echo "true") CODE_REVIEW_ENABLED=$(gsd_run query config-get workflow.code_review 2>/dev/null || echo "true")
``` ```
If `CODE_REVIEW_ENABLED` is `"false"`: display "Code review skipped (workflow.code_review=false)" and proceed to next step. If `CODE_REVIEW_ENABLED` is `"false"`: display "Code review skipped (workflow.code_review=false)" and proceed to next step.
@@ -1180,7 +1169,7 @@ fi
**2. Find parent UAT file:** **2. Find parent UAT file:**
```bash ```bash
PARENT_INFO=$($GSD_SDK query find-phase "${PARENT_PHASE}" --raw) PARENT_INFO=$(gsd_run query find-phase "${PARENT_PHASE}" --raw)
# Extract directory from PARENT_INFO JSON, then find UAT file in that directory # Extract directory from PARENT_INFO JSON, then find UAT file in that directory
``` ```
@@ -1211,7 +1200,7 @@ mv .planning/debug/{slug}.md .planning/debug/resolved/
**6. Commit updated artifacts:** **6. Commit updated artifacts:**
```bash ```bash
$GSD_SDK query commit "docs(phase-${PARENT_PHASE}): resolve UAT gaps and debug sessions after ${PHASE_NUMBER} gap closure" --files .planning/phases/*${PARENT_PHASE}*/*-UAT.md .planning/debug/resolved/*.md gsd_run query commit "docs(phase-${PARENT_PHASE}): resolve UAT gaps and debug sessions after ${PHASE_NUMBER} gap closure" --files .planning/phases/*${PARENT_PHASE}*/*-UAT.md .planning/debug/resolved/*.md
``` ```
</step> </step>
@@ -1239,7 +1228,7 @@ Collect all unique test file paths into `REGRESSION_FILES`.
```bash ```bash
# Resolve test command: project config > Makefile > language sniff # Resolve test command: project config > Makefile > language sniff
REG_TEST_CMD=$($GSD_SDK query config-get workflow.test_command --default "" 2>/dev/null || true) REG_TEST_CMD=$(gsd_run query config-get workflow.test_command --default "" 2>/dev/null || true)
if [ -z "$REG_TEST_CMD" ]; then if [ -z "$REG_TEST_CMD" ]; then
if [ -f "Makefile" ] && grep -q "^test:" Makefile; then if [ -f "Makefile" ] && grep -q "^test:" Makefile; then
REG_TEST_CMD="make test" REG_TEST_CMD="make test"
@@ -1295,7 +1284,7 @@ build/types pass because TypeScript types come from config, not the live databas
**Run after execution completes but BEFORE verification marks success.** **Run after execution completes but BEFORE verification marks success.**
```bash ```bash
SCHEMA_DRIFT=$($GSD_SDK query verify.schema-drift "${PHASE_NUMBER}" 2>/dev/null) SCHEMA_DRIFT=$(gsd_run query verify.schema-drift "${PHASE_NUMBER}" 2>/dev/null)
``` ```
Parse JSON result for: `drift_detected`, `blocking`, `schema_files`, `orms`, `unpushed_orms`, `message`. Parse JSON result for: `drift_detected`, `blocking`, `schema_files`, `orms`, `unpushed_orms`, `message`.
@@ -1369,7 +1358,7 @@ spawn template, and the two `workflow.drift_*` config keys.
Verify phase achieved its GOAL, not just completed tasks. Verify phase achieved its GOAL, not just completed tasks.
```bash ```bash
VERIFIER_SKILLS=$($GSD_SDK query agent-skills gsd-verifier) VERIFIER_SKILLS=$(gsd_run query agent-skills gsd-verifier)
``` ```
``` ```
@@ -1454,7 +1443,7 @@ blocked: 0
Commit the file: Commit the file:
```bash ```bash
$GSD_SDK query commit "test({phase_num}): persist human verification items as UAT" --files "{phase_dir}/{phase_num}-HUMAN-UAT.md" gsd_run query commit "test({phase_num}): persist human verification items as UAT" --files "{phase_dir}/{phase_num}-HUMAN-UAT.md"
``` ```
**Step B: Present to user:** **Step B: Present to user:**
@@ -1503,7 +1492,7 @@ Gap closure cycle: `/gsd:plan-phase {X} --gaps ${GSD_WS}` reads VERIFICATION.md
**Mark phase complete and update all tracking files:** **Mark phase complete and update all tracking files:**
```bash ```bash
COMPLETION=$($GSD_SDK query phase.complete "${PHASE_NUMBER}") COMPLETION=$(gsd_run query phase.complete "${PHASE_NUMBER}")
``` ```
The CLI handles: The CLI handles:
@@ -1526,7 +1515,7 @@ These items are tracked and will appear in `/gsd:progress` and `/gsd:audit-uat`.
``` ```
```bash ```bash
$GSD_SDK query commit "docs(phase-{X}): complete phase execution" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md {phase_dir}/*-VERIFICATION.md gsd_run query commit "docs(phase-{X}): complete phase execution" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md {phase_dir}/*-VERIFICATION.md
``` ```
</step> </step>
@@ -1538,7 +1527,7 @@ entries from the completed phase to the global learnings store at `~/.gsd/knowle
**Check config gate:** **Check config gate:**
```bash ```bash
GL_ENABLED=$($GSD_SDK query config-get features.global_learnings --raw 2>/dev/null || echo "false") GL_ENABLED=$(gsd_run query config-get features.global_learnings --raw 2>/dev/null || echo "false")
``` ```
**If `GL_ENABLED` is not `true`:** Skip this step entirely (feature disabled by default). **If `GL_ENABLED` is not `true`:** Skip this step entirely (feature disabled by default).
@@ -1548,7 +1537,7 @@ GL_ENABLED=$($GSD_SDK query config-get features.global_learnings --raw 2>/dev/nu
1. Check if LEARNINGS.md exists in the phase directory (use the `phase_dir` value from init context) 1. Check if LEARNINGS.md exists in the phase directory (use the `phase_dir` value from init context)
2. If found, copy to global store: 2. If found, copy to global store:
```bash ```bash
$GSD_SDK query learnings.copy 2>/dev/null || echo "⚠ Learnings copy failed — continuing" gsd_run query learnings.copy 2>/dev/null || echo "⚠ Learnings copy failed — continuing"
``` ```
Copy failure must NOT block phase completion. Copy failure must NOT block phase completion.
</step> </step>
@@ -1576,7 +1565,7 @@ for TODO_FILE in "$PENDING_DIR"/*.md; do
done done
if [ ${#CLOSED[@]} -gt 0 ]; then if [ ${#CLOSED[@]} -gt 0 ]; then
$GSD_SDK query commit "docs(phase-${PHASE_NUMBER}): auto-close ${#CLOSED[@]} todo(s) resolved by this phase" --files .planning/todos/completed/ .planning/STATE.md|| true gsd_run query commit "docs(phase-${PHASE_NUMBER}): auto-close ${#CLOSED[@]} todo(s) resolved by this phase" --files .planning/todos/completed/ .planning/STATE.md|| true
echo "◆ Closed ${#CLOSED[@]} todo(s) resolved by Phase ${PHASE_NUMBER}:" echo "◆ Closed ${#CLOSED[@]} todo(s) resolved by Phase ${PHASE_NUMBER}:"
for f in "${CLOSED[@]}"; do echo " ✓ $f"; done for f in "${CLOSED[@]}"; do echo " ✓ $f"; done
fi fi
@@ -1601,7 +1590,7 @@ PROJECT.md falls behind silently over multiple phases.
5. Commit the change: 5. Commit the change:
```bash ```bash
$GSD_SDK query commit "docs(phase-{X}): evolve PROJECT.md after phase completion" --files .planning/PROJECT.md gsd_run query commit "docs(phase-{X}): evolve PROJECT.md after phase completion" --files .planning/PROJECT.md
``` ```
**Skip this step if** `.planning/PROJECT.md` does not exist. **Skip this step if** `.planning/PROJECT.md` does not exist.
@@ -1639,7 +1628,7 @@ STOP. Do not proceed to auto-advance or transition.
1. Parse `--auto` flag from $ARGUMENTS 1. Parse `--auto` flag from $ARGUMENTS
2. Read consolidated auto-mode (`active` = chain flag OR user preference; chain flag already synced in init step): 2. Read consolidated auto-mode (`active` = chain flag OR user preference; chain flag already synced in init step):
```bash ```bash
AUTO_MODE=$($GSD_SDK query check auto-mode --pick active 2>/dev/null || echo "false") AUTO_MODE=$(gsd_run query check auto-mode --pick active 2>/dev/null || echo "false")
``` ```
**If `--auto` flag present OR `AUTO_MODE` is true (AND verification passed with no gaps):** **If `--auto` flag present OR `AUTO_MODE` is true (AND verification passed with no gaps):**

View File

@@ -45,7 +45,8 @@ First load the mapper agent's skill bundle (the executor's `AGENT_SKILLS`
from step `init_context` is for `gsd-executor`, not the mapper): from step `init_context` is for `gsd-executor`, not the mapper):
```bash ```bash
AGENT_SKILLS_MAPPER=$($GSD_SDK query agent-skills gsd-codebase-mapper) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
AGENT_SKILLS_MAPPER=$(gsd_run query agent-skills gsd-codebase-mapper)
``` ```
Then spawn `gsd-codebase-mapper` agents with the `--paths` hint: Then spawn `gsd-codebase-mapper` agents with the `--paths` hint:

View File

@@ -8,8 +8,9 @@ detect.
**Step A — Build gate:** **Step A — Build gate:**
```bash ```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
# Resolve build command: project config > Xcode > Makefile > language sniff # Resolve build command: project config > Xcode > Makefile > language sniff
BUILD_CMD=$($GSD_SDK query config-get workflow.build_command --default "" 2>/dev/null || true) BUILD_CMD=$(gsd_run query config-get workflow.build_command --default "" 2>/dev/null || true)
if [ -z "$BUILD_CMD" ]; then if [ -z "$BUILD_CMD" ]; then
XCODEPROJ=$(find . -maxdepth 2 -name "*.xcodeproj" -not -path "*/node_modules/*" 2>/dev/null | head -1) XCODEPROJ=$(find . -maxdepth 2 -name "*.xcodeproj" -not -path "*/node_modules/*" 2>/dev/null | head -1)
if [ -n "$XCODEPROJ" ]; then if [ -n "$XCODEPROJ" ]; then
@@ -63,7 +64,7 @@ fi
```bash ```bash
# Resolve test command: project config > Xcode > Makefile > language sniff # Resolve test command: project config > Xcode > Makefile > language sniff
TEST_CMD=$($GSD_SDK query config-get workflow.test_command --default "" 2>/dev/null || true) TEST_CMD=$(gsd_run query config-get workflow.test_command --default "" 2>/dev/null || true)
if [ -z "$TEST_CMD" ]; then if [ -z "$TEST_CMD" ]; then
XCODEPROJ=$(find . -maxdepth 2 -name "*.xcodeproj" -not -path "*/node_modules/*" 2>/dev/null | head -1) XCODEPROJ=$(find . -maxdepth 2 -name "*.xcodeproj" -not -path "*/node_modules/*" 2>/dev/null | head -1)
if [ -n "$XCODEPROJ" ]; then if [ -n "$XCODEPROJ" ]; then

View File

@@ -30,19 +30,8 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
Load execution context (paths only to minimize orchestrator context): Load execution context (paths only to minimize orchestrator context):
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.execute-phase "${PHASE}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.execute-phase "${PHASE}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -85,7 +74,7 @@ PLAN_START_EPOCH=$(date +%s)
```bash ```bash
# Count tasks — match <task tag at any indentation level # Count tasks — match <task tag at any indentation level
TASK_COUNT=$(grep -cE '^\s*<task[[:space:]>]' .planning/phases/XX-name/{phase}-{plan}-PLAN.md 2>/dev/null || echo "0") TASK_COUNT=$(grep -cE '^\s*<task[[:space:]>]' .planning/phases/XX-name/{phase}-{plan}-PLAN.md 2>/dev/null || echo "0")
INLINE_THRESHOLD=$($GSD_SDK query config-get workflow.inline_plan_threshold 2>/dev/null || echo "2") INLINE_THRESHOLD=$(gsd_run query config-get workflow.inline_plan_threshold 2>/dev/null || echo "2")
grep -n "type=\"checkpoint" .planning/phases/XX-name/{phase}-{plan}-PLAN.md grep -n "type=\"checkpoint" .planning/phases/XX-name/{phase}-{plan}-PLAN.md
``` ```
@@ -169,7 +158,7 @@ This IS the execution instructions. Follow exactly. If plan references CONTEXT.m
<step name="previous_phase_check"> <step name="previous_phase_check">
```bash ```bash
$GSD_SDK query phases.list --type summaries --raw gsd_run query phases.list --type summaries --raw
# Extract the second-to-last summary from the JSON result # Extract the second-to-last summary from the JSON result
``` ```
@@ -323,7 +312,7 @@ If verification fails:
**Check if node repair is enabled** (default: on): **Check if node repair is enabled** (default: on):
```bash ```bash
NODE_REPAIR=$($GSD_SDK query config-get workflow.node_repair 2>/dev/null || echo "true") NODE_REPAIR=$(gsd_run query config-get workflow.node_repair 2>/dev/null || echo "true")
``` ```
If `NODE_REPAIR` is `true`: invoke `@./.claude/get-shit-done/workflows/node-repair.md` with: If `NODE_REPAIR` is `true`: invoke `@./.claude/get-shit-done/workflows/node-repair.md` with:
@@ -396,13 +385,13 @@ IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false")
# Skip in parallel mode — orchestrator handles STATE.md centrally # Skip in parallel mode — orchestrator handles STATE.md centrally
if [ "$IS_WORKTREE" != "true" ]; then if [ "$IS_WORKTREE" != "true" ]; then
# Advance plan counter (handles last-plan edge case) # Advance plan counter (handles last-plan edge case)
$GSD_SDK query state.advance-plan gsd_run query state.advance-plan
# Recalculate progress bar from disk state # Recalculate progress bar from disk state
$GSD_SDK query state.update-progress gsd_run query state.update-progress
# Record execution metrics # Record execution metrics
$GSD_SDK query state.record-metric \ gsd_run query state.record-metric \
--phase "${PHASE}" --plan "${PLAN}" --duration "${DURATION}" \ --phase "${PHASE}" --plan "${PLAN}" --duration "${DURATION}" \
--tasks "${TASK_COUNT}" --files "${FILE_COUNT}" --tasks "${TASK_COUNT}" --files "${FILE_COUNT}"
fi fi
@@ -415,11 +404,11 @@ From SUMMARY: Extract decisions and add to STATE.md:
```bash ```bash
# Add each decision from SUMMARY key-decisions # Add each decision from SUMMARY key-decisions
# Prefer file inputs for shell-safe text (preserves `$`, `*`, etc. exactly) # Prefer file inputs for shell-safe text (preserves `$`, `*`, etc. exactly)
$GSD_SDK query state.add-decision \ gsd_run query state.add-decision \
--phase "${PHASE}" --summary-file "${DECISION_TEXT_FILE}" --rationale-file "${RATIONALE_FILE}" --phase "${PHASE}" --summary-file "${DECISION_TEXT_FILE}" --rationale-file "${RATIONALE_FILE}"
# Add blockers if any found # Add blockers if any found
$GSD_SDK query state.add-blocker --text-file "${BLOCKER_TEXT_FILE}" gsd_run query state.add-blocker --text-file "${BLOCKER_TEXT_FILE}"
``` ```
</step> </step>
@@ -427,7 +416,7 @@ $GSD_SDK query state.add-blocker --text-file "${BLOCKER_TEXT_FILE}"
Update session info using gsd-tools.cjs query (or legacy gsd-tools): Update session info using gsd-tools.cjs query (or legacy gsd-tools):
```bash ```bash
$GSD_SDK query state.record-session \ gsd_run query state.record-session \
--stopped-at "Completed ${PHASE}-${PLAN}-PLAN.md" \ --stopped-at "Completed ${PHASE}-${PLAN}-PLAN.md" \
--resume-file "None" --resume-file "None"
``` ```
@@ -453,7 +442,7 @@ IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false")
if [ "$IS_WORKTREE" != "true" ]; then if [ "$IS_WORKTREE" != "true" ]; then
# use_worktrees: false → this handler is the sole post-plan sync point (#2661) # use_worktrees: false → this handler is the sole post-plan sync point (#2661)
$GSD_SDK query roadmap.update-plan-progress "${PHASE}" gsd_run query roadmap.update-plan-progress "${PHASE}"
fi fi
``` ```
Counts PLAN vs SUMMARY files on disk. Updates progress table row with correct count and status (`In Progress` or `Complete` with date). Counts PLAN vs SUMMARY files on disk. Updates progress table row with correct count and status (`In Progress` or `Complete` with date).
@@ -463,7 +452,7 @@ Counts PLAN vs SUMMARY files on disk. Updates progress table row with correct co
Mark completed requirements from the PLAN.md frontmatter `requirements:` field: Mark completed requirements from the PLAN.md frontmatter `requirements:` field:
```bash ```bash
$GSD_SDK query requirements.mark-complete ${REQ_IDS} gsd_run query requirements.mark-complete ${REQ_IDS}
``` ```
Extract requirement IDs from the plan's frontmatter (e.g., `requirements: [AUTH-01, AUTH-02]`). If no requirements field, skip. Extract requirement IDs from the plan's frontmatter (e.g., `requirements: [AUTH-01, AUTH-02]`). If no requirements field, skip.
@@ -483,9 +472,9 @@ IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false")
# In parallel mode: exclude STATE.md and ROADMAP.md (orchestrator commits these) # In parallel mode: exclude STATE.md and ROADMAP.md (orchestrator commits these)
if [ "$IS_WORKTREE" = "true" ]; then if [ "$IS_WORKTREE" = "true" ]; then
$GSD_SDK query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/REQUIREMENTS.md gsd_run query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/REQUIREMENTS.md
else else
$GSD_SDK query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md .planning/REQUIREMENTS.md gsd_run query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md .planning/REQUIREMENTS.md
fi fi
``` ```
</step> </step>
@@ -501,7 +490,7 @@ git diff --name-only ${FIRST_TASK}^..HEAD 2>/dev/null || true
Update only structural changes: new src/ dir → STRUCTURE.md | deps → STACK.md | file pattern → CONVENTIONS.md | API client → INTEGRATIONS.md | config → STACK.md | renamed → update paths. Skip code-only/bugfix/content changes. Update only structural changes: new src/ dir → STRUCTURE.md | deps → STACK.md | file pattern → CONVENTIONS.md | API client → INTEGRATIONS.md | config → STACK.md | renamed → update paths. Skip code-only/bugfix/content changes.
```bash ```bash
$GSD_SDK query commit "" --files .planning/codebase/*.md --amend gsd_run query commit "" --files .planning/codebase/*.md --amend
``` ```
</step> </step>

View File

@@ -115,19 +115,8 @@ For each selected output, write the file:
Commit if `commit_docs` is enabled: Commit if `commit_docs` is enabled:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query commit "docs: capture exploration — {topic_slug}" --files {file_list}
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query commit "docs: capture exploration — {topic_slug}" --files {file_list}
``` ```
## Step 6: Close ## Step 6: Close

View File

@@ -16,19 +16,8 @@ Analyze completed phase artifacts (PLAN.md, SUMMARY.md, VERIFICATION.md, UAT.md,
Parse arguments and load project state: Parse arguments and load project state:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -201,7 +190,7 @@ The body follows this structure:
Update STATE.md to reflect the learning extraction: Update STATE.md to reflect the learning extraction:
```bash ```bash
$GSD_SDK query state.update "Last Activity" "$(date +%Y-%m-%d)" gsd_run query state.update "Last Activity" "$(date +%Y-%m-%d)"
``` ```
</step> </step>

View File

@@ -272,19 +272,8 @@ gh issue create \
## Step 8: Update STATE.md ## Step 8: Update STATE.md
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query state.record-session "" \
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query state.record-session "" \
"Forensic investigation complete" \ "Forensic investigation complete" \
".planning/forensics/report-{timestamp}.md" ".planning/forensics/report-{timestamp}.md"
``` ```

View File

@@ -21,21 +21,10 @@ Read from project config (`config.json`):
## Step 1: Guard Checks ## Step 1: Guard Checks
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" GRADUATION_ENABLED=$(gsd_run query config-get features.graduation 2>/dev/null || echo "true")
if [ -f "$GSD_TOOLS" ]; then GRADUATION_WINDOW=$(gsd_run query config-get features.graduation_window 2>/dev/null || echo "5")
GSD_SDK="node $GSD_TOOLS" GRADUATION_THRESHOLD=$(gsd_run query config-get features.graduation_threshold 2>/dev/null || echo "3")
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
GRADUATION_ENABLED=$($GSD_SDK query config-get features.graduation 2>/dev/null || echo "true")
GRADUATION_WINDOW=$($GSD_SDK query config-get features.graduation_window 2>/dev/null || echo "5")
GRADUATION_THRESHOLD=$($GSD_SDK query config-get features.graduation_threshold 2>/dev/null || echo "3")
``` ```
**Skip silently (print nothing) if:** **Skip silently (print nothing) if:**

View File

@@ -49,19 +49,8 @@ available — replace the prompt with a plain-text two-question sequence
plain text from the user's response. plain text from the user's response.
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query validate.context \
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query validate.context \
--tokens-used "$TOKENS_USED" \ --tokens-used "$TOKENS_USED" \
--context-window "$CONTEXT_WINDOW" --context-window "$CONTEXT_WINDOW"
``` ```
@@ -76,7 +65,7 @@ health output, the two modes are independent diagnostics.
**Run health validation:** **Run health validation:**
```bash ```bash
$GSD_SDK query validate.health $REPAIR_FLAG $BACKFILL_FLAG gsd_run query validate.health $REPAIR_FLAG $BACKFILL_FLAG
``` ```
Parse JSON output: Parse JSON output:
@@ -163,7 +152,7 @@ If yes, re-run with --repair flag and display results.
Re-run health check without --repair to confirm issues are resolved: Re-run health check without --repair to confirm issues are resolved:
```bash ```bash
$GSD_SDK query validate.health gsd_run query validate.health
``` ```
Report final status. Report final status.

View File

@@ -176,19 +176,8 @@ Apply GSD naming convention for the output filename:
Determine the target directory by querying `init.phase-op` for the phase number extracted in `plan_read_input`. This ensures the `project_code` prefix from `.planning/config.json` is applied: Determine the target directory by querying `init.phase-op` for the phase number extracted in `plan_read_input`. This ensures the `project_code` prefix from `.planning/config.json` is applied:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "{NN}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "{NN}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
expected_phase_dir=$(echo "$INIT" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).expected_phase_dir)") expected_phase_dir=$(echo "$INIT" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).expected_phase_dir)")
``` ```
@@ -237,7 +226,7 @@ Update `.planning/STATE.md` if appropriate (e.g., increment total plan count).
Commit the imported plan and updated files: Commit the imported plan and updated files:
```bash ```bash
$GSD_SDK query commit "docs({phase}): import plan from {basename FILEPATH}" --files .planning/phases/{phase}/{plan}-PLAN.md .planning/ROADMAP.md gsd_run query commit "docs({phase}): import plan from {basename FILEPATH}" --files .planning/phases/{phase}/{plan}-PLAN.md .planning/ROADMAP.md
``` ```
Display completion: Display completion:

View File

@@ -34,19 +34,8 @@ Validate first argument is an integer.
Load phase operation context: Load phase operation context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${after_phase}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${after_phase}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -61,7 +50,7 @@ Exit.
**Delegate the phase insertion to `gsd-tools.cjs query phase.insert`:** **Delegate the phase insertion to `gsd-tools.cjs query phase.insert`:**
```bash ```bash
RESULT=$($GSD_SDK query phase.insert "${after_phase}" "${description}") RESULT=$(gsd_run query phase.insert "${after_phase}" "${description}")
``` ```
The CLI handles: The CLI handles:
@@ -83,7 +72,7 @@ blocks direct STATE.md writes):
`{decimal_phase}`: `{decimal_phase}`:
```bash ```bash
$GSD_SDK query state.patch '{"Current Phase":"{decimal_phase}","Next recommended run":"/gsd:plan-phase {decimal_phase}"}' gsd_run query state.patch '{"Current Phase":"{decimal_phase}","Next recommended run":"/gsd:plan-phase {decimal_phase}"}'
``` ```
(Adjust field names to whatever pointers STATE.md exposes — the handler (Adjust field names to whatever pointers STATE.md exposes — the handler
@@ -94,7 +83,7 @@ blocks direct STATE.md writes):
and dedupes identical entries: and dedupes identical entries:
```bash ```bash
$GSD_SDK query state.add-roadmap-evolution \ gsd_run query state.add-roadmap-evolution \
--phase {decimal_phase} \ --phase {decimal_phase} \
--action inserted \ --action inserted \
--after {after_phase} \ --after {after_phase} \

View File

@@ -11,19 +11,8 @@ Read all files referenced by the invoking prompt's execution_context before star
## 1. Setup ## 1. Setup
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.list-workspaces)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.list-workspaces)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```

View File

@@ -19,19 +19,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Bootstrap via manager init: Bootstrap via manager init:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.manager)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.manager)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -72,7 +61,7 @@ Proceed to dashboard step.
**Every time this step is reached**, re-read state from disk to pick up changes from background agents: **Every time this step is reached**, re-read state from disk to pick up changes from background agents:
```bash ```bash
INIT=$($GSD_SDK query init.manager) INIT=$(gsd_run query init.manager)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```

View File

@@ -69,21 +69,10 @@ documents refreshed.
Load codebase mapping context: Load codebase mapping context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.map-codebase)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.map-codebase)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_MAPPER=$($GSD_SDK query agent-skills gsd-codebase-mapper) AGENT_SKILLS_MAPPER=$(gsd_run query agent-skills gsd-codebase-mapper)
``` ```
Extract from init JSON: `mapper_model`, `commit_docs`, `codebase_dir`, `existing_maps`, `has_maps`, `codebase_dir_exists`, `subagent_timeout`, `date`. Extract from init JSON: `mapper_model`, `commit_docs`, `codebase_dir`, `existing_maps`, `has_maps`, `codebase_dir_exists`, `subagent_timeout`, `date`.
@@ -390,7 +379,7 @@ Continue to commit_codebase_map.
Commit the codebase map: Commit the codebase map:
```bash ```bash
$GSD_SDK query commit "docs: map existing codebase" --files .planning/codebase/*.md gsd_run query commit "docs: map existing codebase" --files .planning/codebase/*.md
``` ```
Continue to offer_next. Continue to offer_next.

View File

@@ -53,19 +53,8 @@ Read all files that exist. Missing files are fine — the summary adapts to what
Find all phase directories: Find all phase directories:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query init.progress
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query init.progress
``` ```
This returns phase metadata. For each phase in the milestone scope: This returns phase metadata. For each phase in the milestone scope:
@@ -201,7 +190,7 @@ mkdir -p .planning/reports
Write the summary, then commit: Write the summary, then commit:
```bash ```bash
$GSD_SDK query commit "docs(v${VERSION}): generate milestone summary for onboarding" --files \ gsd_run query commit "docs(v${VERSION}): generate milestone summary for onboarding" --files \
".planning/reports/MILESTONE_SUMMARY-v${VERSION}.md" ".planning/reports/MILESTONE_SUMMARY-v${VERSION}.md"
``` ```
@@ -229,7 +218,7 @@ If the user is done:
## Step 9: Update STATE.md ## Step 9: Update STATE.md
```bash ```bash
$GSD_SDK query state.record-session "" \ gsd_run query state.record-session "" \
"Milestone v${VERSION} summary generated" \ "Milestone v${VERSION} summary generated" \
".planning/reports/MILESTONE_SUMMARY-v${VERSION}.md" ".planning/reports/MILESTONE_SUMMARY-v${VERSION}.md"
``` ```

View File

@@ -34,26 +34,15 @@ Normalize per `@~/.claude/get-shit-done/references/phase-argument-parsing.md` (z
## 2. Validate phase exists and check status ## 2. Validate phase exists and check status
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" PHASE_INFO=$(gsd_run query roadmap.get-phase "${PHASE}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
PHASE_INFO=$($GSD_SDK query roadmap.get-phase "${PHASE}")
PHASE_FOUND=$(echo "$PHASE_INFO" | jq -r '.found') PHASE_FOUND=$(echo "$PHASE_INFO" | jq -r '.found')
PHASE_NAME=$(echo "$PHASE_INFO" | jq -r '.phase_name') PHASE_NAME=$(echo "$PHASE_INFO" | jq -r '.phase_name')
PHASE_GOAL=$(echo "$PHASE_INFO" | jq -r '.goal') PHASE_GOAL=$(echo "$PHASE_INFO" | jq -r '.goal')
PHASE_MODE=$(echo "$PHASE_INFO" | jq -r '.mode // ""') PHASE_MODE=$(echo "$PHASE_INFO" | jq -r '.mode // ""')
PHASE_COMPLETE=$(echo "$PHASE_INFO" | jq -r '.roadmap_complete // false') PHASE_COMPLETE=$(echo "$PHASE_INFO" | jq -r '.roadmap_complete // false')
ANALYZE=$($GSD_SDK query roadmap.analyze) ANALYZE=$(gsd_run query roadmap.analyze)
if [[ "$ANALYZE" == @file:* ]]; then ANALYZE=$(cat "${ANALYZE#@file:}"); fi if [[ "$ANALYZE" == @file:* ]]; then ANALYZE=$(cat "${ANALYZE#@file:}"); fi
DISK_STATUS=$(echo "$ANALYZE" | jq -r --arg p "$PHASE" '.phases[] | select((.phase_number|tostring)==$p) | .disk_status' | head -1) DISK_STATUS=$(echo "$ANALYZE" | jq -r --arg p "$PHASE" '.phases[] | select((.phase_number|tostring)==$p) | .disk_status' | head -1)
if [[ "$DISK_STATUS" == "complete" || "$PHASE_COMPLETE" == "true" ]]; then if [[ "$DISK_STATUS" == "complete" || "$PHASE_COMPLETE" == "true" ]]; then
@@ -110,7 +99,7 @@ If any of the three answers is empty or whitespace-only, error and re-prompt tha
**Validate via the centralized User Story validator.** The verb owns the canonical regex `/^As a .+, I want to .+, so that .+\.$/` and surfaces per-error guidance: **Validate via the centralized User Story validator.** The verb owns the canonical regex `/^As a .+, I want to .+, so that .+\.$/` and surfaces per-error guidance:
```bash ```bash
USER_STORY_RESULT=$($GSD_SDK query user-story.validate --story "$USER_STORY") USER_STORY_RESULT=$(gsd_run query user-story.validate --story "$USER_STORY")
if [ "$(echo "$USER_STORY_RESULT" | jq -r '.valid')" != "true" ]; then if [ "$(echo "$USER_STORY_RESULT" | jq -r '.valid')" != "true" ]; then
echo "$USER_STORY_RESULT" | jq -r '.errors[]' >&2 echo "$USER_STORY_RESULT" | jq -r '.errors[]' >&2
# Re-prompt the offending field(s) per surfaced errors, then re-run validation. # Re-prompt the offending field(s) per surfaced errors, then re-run validation.
@@ -195,8 +184,8 @@ On Apply, write the updated `ROADMAP.md` atomically (read-edit-write).
## 6. Verify the write ## 6. Verify the write
```bash ```bash
NEW_MODE=$($GSD_SDK query roadmap.get-phase "${PHASE}" --pick mode) NEW_MODE=$(gsd_run query roadmap.get-phase "${PHASE}" --pick mode)
NEW_GOAL=$($GSD_SDK query roadmap.get-phase "${PHASE}" --pick goal) NEW_GOAL=$(gsd_run query roadmap.get-phase "${PHASE}" --pick goal)
``` ```
Assert: Assert:

View File

@@ -181,19 +181,8 @@ blockers, todos) is preserved across the switch — symmetric with
`milestone.complete`. `milestone.complete`.
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query state.milestone-switch --milestone "v[X.Y]" --name "[Name]"
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query state.milestone-switch --milestone "v[X.Y]" --name "[Name]"
``` ```
The resulting Current Position section looks like: The resulting Current Position section looks like:
@@ -220,21 +209,21 @@ Delete MILESTONE-CONTEXT.md if exists (consumed).
Clear leftover phase directories from the previous milestone: Clear leftover phase directories from the previous milestone:
```bash ```bash
$GSD_SDK query phases.clear --confirm gsd_run query phases.clear --confirm
``` ```
```bash ```bash
$GSD_SDK query commit "docs: start milestone v[X.Y] [Name]" --files .planning/PROJECT.md .planning/STATE.md gsd_run query commit "docs: start milestone v[X.Y] [Name]" --files .planning/PROJECT.md .planning/STATE.md
``` ```
## 7. Load Context and Resolve Models ## 7. Load Context and Resolve Models
```bash ```bash
INIT=$($GSD_SDK query init.new-milestone) INIT=$(gsd_run query init.new-milestone)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_RESEARCHER=$($GSD_SDK query agent-skills gsd-project-researcher) AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-project-researcher)
AGENT_SKILLS_SYNTHESIZER=$($GSD_SDK query agent-skills gsd-research-synthesizer) AGENT_SKILLS_SYNTHESIZER=$(gsd_run query agent-skills gsd-research-synthesizer)
AGENT_SKILLS_ROADMAPPER=$($GSD_SDK query agent-skills gsd-roadmapper) AGENT_SKILLS_ROADMAPPER=$(gsd_run query agent-skills gsd-roadmapper)
``` ```
Extract from init JSON: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `research_enabled`, `current_milestone`, `project_exists`, `roadmap_exists`, `latest_completed_milestone`, `phase_dir_count`, `phase_archive_path`, `agents_installed`, `missing_agents`. Extract from init JSON: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `research_enabled`, `current_milestone`, `project_exists`, `roadmap_exists`, `latest_completed_milestone`, `phase_dir_count`, `phase_archive_path`, `agents_installed`, `missing_agents`.
@@ -456,7 +445,7 @@ If "adjust": Return to scoping.
**Commit requirements:** **Commit requirements:**
```bash ```bash
$GSD_SDK query commit "docs: define milestone v[X.Y] requirements" --files .planning/REQUIREMENTS.md gsd_run query commit "docs: define milestone v[X.Y] requirements" --files .planning/REQUIREMENTS.md
``` ```
## 10. Create Roadmap ## 10. Create Roadmap
@@ -542,7 +531,7 @@ Success criteria:
**Commit roadmap** (after approval): **Commit roadmap** (after approval):
```bash ```bash
$GSD_SDK query commit "docs: create milestone v[X.Y] roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md gsd_run query commit "docs: create milestone v[X.Y] roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md
``` ```
## 10.5. Link Pending Todos to Roadmap Phases ## 10.5. Link Pending Todos to Roadmap Phases
@@ -585,7 +574,7 @@ files: [existing]
**If any todos were linked:** **If any todos were linked:**
```bash ```bash
$GSD_SDK query commit "docs: tag [count] pending todos with resolves_phase after milestone v[X.Y] roadmap" --files .planning/todos/pending/*.md gsd_run query commit "docs: tag [count] pending todos with resolves_phase after milestone v[X.Y] roadmap" --files .planning/todos/pending/*.md
``` ```
Print a summary: Print a summary:

View File

@@ -57,23 +57,12 @@ The document should describe what you want to build.
**MANDATORY FIRST STEP — Execute these checks before ANY user interaction:** **MANDATORY FIRST STEP — Execute these checks before ANY user interaction:**
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.new-project)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.new-project)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_RESEARCHER=$($GSD_SDK query agent-skills gsd-project-researcher) AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-project-researcher)
AGENT_SKILLS_SYNTHESIZER=$($GSD_SDK query agent-skills gsd-research-synthesizer) AGENT_SKILLS_SYNTHESIZER=$(gsd_run query agent-skills gsd-research-synthesizer)
AGENT_SKILLS_ROADMAPPER=$($GSD_SDK query agent-skills gsd-roadmapper) AGENT_SKILLS_ROADMAPPER=$(gsd_run query agent-skills gsd-roadmapper)
``` ```
Parse JSON for: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `project_exists`, `has_codebase_map`, `planning_exists`, `has_existing_code`, `has_package_file`, `is_brownfield`, `needs_codebase_map`, `has_git`, `git_worktree_root`, `in_nested_subdir`, `project_path`, `agents_installed`, `missing_agents`, `agent_runtime`, `agents_dir`, `required_agents`, `required_agents_installed`, `missing_required_agents`, `agent_skill_payloads_available`, `agent_skill_payload_agents`. Parse JSON for: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `project_exists`, `has_codebase_map`, `planning_exists`, `has_existing_code`, `has_package_file`, `is_brownfield`, `needs_codebase_map`, `has_git`, `git_worktree_root`, `in_nested_subdir`, `project_path`, `agents_installed`, `missing_agents`, `agent_runtime`, `agents_dir`, `required_agents`, `required_agents_installed`, `missing_required_agents`, `agent_skill_payloads_available`, `agent_skill_payload_agents`.
@@ -275,7 +264,7 @@ Create `.planning/config.json` with all settings (CLI fills in remaining default
```bash ```bash
mkdir -p .planning mkdir -p .planning
$GSD_SDK query config-new-project '{"mode":"yolo","granularity":"[selected]","parallelization":true|false,"commit_docs":true|false,"model_profile":"quality|balanced|budget|inherit","workflow":{"research":true|false,"plan_check":true|false,"verifier":true|false,"nyquist_validation":true|false,"auto_advance":true},"ship":{"pr_body_sections":[{"heading":"User Stories & Acceptance Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## User Stories || REQUIREMENTS.md ## Acceptance Criteria","fallback":"- Acceptance criteria are covered by the linked requirements and verification evidence."},{"heading":"Risks & Dependencies","enabled":true|false,"source":"PLAN.md ## Risks || PLAN.md ## Dependencies","fallback":"- No known high-risk rollout dependencies."},{"heading":"Success Metrics & Release Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## Definition of Done || VERIFICATION.md ## Release Criteria","fallback":"- Release when automated verification and required manual checks pass."},{"heading":"Stakeholder Review & Approval","enabled":true|false,"template":"- Product owner approval pending for {phase_name}."}]}}' gsd_run query config-new-project '{"mode":"yolo","granularity":"[selected]","parallelization":true|false,"commit_docs":true|false,"model_profile":"quality|balanced|budget|inherit","workflow":{"research":true|false,"plan_check":true|false,"verifier":true|false,"nyquist_validation":true|false,"auto_advance":true},"ship":{"pr_body_sections":[{"heading":"User Stories & Acceptance Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## User Stories || REQUIREMENTS.md ## Acceptance Criteria","fallback":"- Acceptance criteria are covered by the linked requirements and verification evidence."},{"heading":"Risks & Dependencies","enabled":true|false,"source":"PLAN.md ## Risks || PLAN.md ## Dependencies","fallback":"- No known high-risk rollout dependencies."},{"heading":"Success Metrics & Release Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## Definition of Done || VERIFICATION.md ## Release Criteria","fallback":"- Release when automated verification and required manual checks pass."},{"heading":"Stakeholder Review & Approval","enabled":true|false,"template":"- Product owner approval pending for {phase_name}."}]}}'
``` ```
**If commit_docs = No:** Add `.planning/` to `.gitignore`. **If commit_docs = No:** Add `.planning/` to `.gitignore`.
@@ -284,13 +273,13 @@ $GSD_SDK query config-new-project '{"mode":"yolo","granularity":"[selected]","pa
```bash ```bash
mkdir -p .planning mkdir -p .planning
$GSD_SDK query commit "chore: add project config" --files .planning/config.json gsd_run query commit "chore: add project config" --files .planning/config.json
``` ```
**Persist auto-advance chain flag to config (survives context compaction):** **Persist auto-advance chain flag to config (survives context compaction):**
```bash ```bash
$GSD_SDK query config-set workflow._auto_chain_active true gsd_run query config-set workflow._auto_chain_active true
``` ```
Proceed to Step 4 (skip Steps 3 and 5). Proceed to Step 4 (skip Steps 3 and 5).
@@ -495,7 +484,7 @@ Do not compress. Capture everything gathered.
```bash ```bash
mkdir -p .planning mkdir -p .planning
$GSD_SDK query commit "docs: initialize project" --files .planning/PROJECT.md gsd_run query commit "docs: initialize project" --files .planning/PROJECT.md
``` ```
## 5. Workflow Preferences ## 5. Workflow Preferences
@@ -756,7 +745,7 @@ Create `.planning/config.json` with all settings (CLI fills in remaining default
```bash ```bash
mkdir -p .planning mkdir -p .planning
$GSD_SDK query config-new-project '{"mode":"[yolo|interactive]","granularity":"[selected]","parallelization":true|false,"commit_docs":true|false,"model_profile":"quality|balanced|budget|inherit","workflow":{"research":true|false,"plan_check":true|false,"verifier":true|false,"nyquist_validation":[false if granularity=coarse, true otherwise]},"ship":{"pr_body_sections":[{"heading":"User Stories & Acceptance Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## User Stories || REQUIREMENTS.md ## Acceptance Criteria","fallback":"- Acceptance criteria are covered by the linked requirements and verification evidence."},{"heading":"Risks & Dependencies","enabled":true|false,"source":"PLAN.md ## Risks || PLAN.md ## Dependencies","fallback":"- No known high-risk rollout dependencies."},{"heading":"Success Metrics & Release Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## Definition of Done || VERIFICATION.md ## Release Criteria","fallback":"- Release when automated verification and required manual checks pass."},{"heading":"Stakeholder Review & Approval","enabled":true|false,"template":"- Product owner approval pending for {phase_name}."}]}}' gsd_run query config-new-project '{"mode":"[yolo|interactive]","granularity":"[selected]","parallelization":true|false,"commit_docs":true|false,"model_profile":"quality|balanced|budget|inherit","workflow":{"research":true|false,"plan_check":true|false,"verifier":true|false,"nyquist_validation":[false if granularity=coarse, true otherwise]},"ship":{"pr_body_sections":[{"heading":"User Stories & Acceptance Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## User Stories || REQUIREMENTS.md ## Acceptance Criteria","fallback":"- Acceptance criteria are covered by the linked requirements and verification evidence."},{"heading":"Risks & Dependencies","enabled":true|false,"source":"PLAN.md ## Risks || PLAN.md ## Dependencies","fallback":"- No known high-risk rollout dependencies."},{"heading":"Success Metrics & Release Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## Definition of Done || VERIFICATION.md ## Release Criteria","fallback":"- Release when automated verification and required manual checks pass."},{"heading":"Stakeholder Review & Approval","enabled":true|false,"template":"- Product owner approval pending for {phase_name}."}]}}'
``` ```
**Note:** Run `/gsd:settings` anytime to update model profile, workflow agents, branching strategy, and other preferences. **Note:** Run `/gsd:settings` anytime to update model profile, workflow agents, branching strategy, and other preferences.
@@ -773,7 +762,7 @@ $GSD_SDK query config-new-project '{"mode":"[yolo|interactive]","granularity":"[
**Commit config.json:** **Commit config.json:**
```bash ```bash
$GSD_SDK query commit "chore: add project config" --files .planning/config.json gsd_run query commit "chore: add project config" --files .planning/config.json
``` ```
## 5.1. Sub-Repo Detection ## 5.1. Sub-Repo Detection
@@ -1221,7 +1210,7 @@ If "adjust": Return to scoping.
**Commit requirements:** **Commit requirements:**
```bash ```bash
$GSD_SDK query commit "docs: define v1 requirements" --files .planning/REQUIREMENTS.md gsd_run query commit "docs: define v1 requirements" --files .planning/REQUIREMENTS.md
``` ```
## 7.5. Project Structure Mode ## 7.5. Project Structure Mode
@@ -1397,7 +1386,7 @@ Use AskUserQuestion:
**Generate or refresh project instruction file before final commit:** **Generate or refresh project instruction file before final commit:**
```bash ```bash
$GSD_SDK query generate-claude-md --output "$INSTRUCTION_FILE" gsd_run query generate-claude-md --output "$INSTRUCTION_FILE"
``` ```
This ensures new projects get the default GSD workflow-enforcement guidance and current project context in `$INSTRUCTION_FILE`. This ensures new projects get the default GSD workflow-enforcement guidance and current project context in `$INSTRUCTION_FILE`.
@@ -1405,7 +1394,7 @@ This ensures new projects get the default GSD workflow-enforcement guidance and
**Commit roadmap (after approval or auto mode):** **Commit roadmap (after approval or auto mode):**
```bash ```bash
$GSD_SDK query commit "docs: create roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md "$INSTRUCTION_FILE" gsd_run query commit "docs: create roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md "$INSTRUCTION_FILE"
``` ```
## 9. Done ## 9. Done
@@ -1446,7 +1435,7 @@ Exit skill and invoke SlashCommand("/gsd:discuss-phase 1 --auto")
Check if Phase 1 has UI indicators (look for `**UI hint**: yes` in Phase 1 detail section of ROADMAP.md): Check if Phase 1 has UI indicators (look for `**UI hint**: yes` in Phase 1 detail section of ROADMAP.md):
```bash ```bash
PHASE1_SECTION=$($GSD_SDK query roadmap.get-phase 1 2>/dev/null) PHASE1_SECTION=$(gsd_run query roadmap.get-phase 1 2>/dev/null)
PHASE1_HAS_UI=$(echo "$PHASE1_SECTION" | grep -qi "UI hint.*yes" && echo "true" || echo "false") PHASE1_HAS_UI=$(echo "$PHASE1_SECTION" | grep -qi "UI hint.*yes" && echo "true" || echo "false")
``` ```

View File

@@ -13,19 +13,8 @@ Read all files referenced by the invoking prompt's execution_context before star
**MANDATORY FIRST STEP — Execute init command:** **MANDATORY FIRST STEP — Execute init command:**
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.new-workspace)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.new-workspace)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```

View File

@@ -13,20 +13,9 @@ Read all files referenced by the invoking prompt's execution_context before star
Read project state to determine current position: Read project state to determine current position:
```bash ```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
# Get state snapshot # Get state snapshot
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) gsd_run query state.json 2>/dev/null || echo "{}"
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs"
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query state.json 2>/dev/null || echo "{}"
``` ```
Also read: Also read:
@@ -139,7 +128,7 @@ Choice [S]:
``` ```
2. Commit the deferral record: 2. Commit the deferral record:
```bash ```bash
$GSD_SDK query commit "docs: defer incomplete Phase {src} items to backlog" gsd_run query commit "docs: defer incomplete Phase {src} items to backlog"
``` ```
3. Continue routing to `determine_next_action` immediately — no second prompt. 3. Continue routing to `determine_next_action` immediately — no second prompt.

View File

@@ -66,19 +66,8 @@ Report any summaries with placeholder content as incomplete items.
**Write structured handoff to `.planning/HANDOFF.json`:** **Write structured handoff to `.planning/HANDOFF.json`:**
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" timestamp=$(gsd_run query current-timestamp full --raw)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
timestamp=$($GSD_SDK query current-timestamp full --raw)
``` ```
```json ```json
@@ -213,13 +202,13 @@ Be specific enough for a fresh Claude to understand immediately.
Use `current-timestamp` for last_updated field. You can use init todos (which provides timestamps) or call directly: Use `current-timestamp` for last_updated field. You can use init todos (which provides timestamps) or call directly:
```bash ```bash
timestamp=$($GSD_SDK query current-timestamp full --raw) timestamp=$(gsd_run query current-timestamp full --raw)
``` ```
</step> </step>
<step name="commit"> <step name="commit">
```bash ```bash
$GSD_SDK query commit "wip: [context-name] paused at [X]/[Y]" --files [handoff-path] .planning/HANDOFF.json gsd_run query commit "wip: [context-name] paused at [X]/[Y]" --files [handoff-path] .planning/HANDOFF.json
``` ```
</step> </step>

View File

@@ -64,20 +64,9 @@ Gap: Flow "View dashboard" broken at data fetch
Find highest existing phase: Find highest existing phase:
```bash ```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
# Get sorted phase list, extract last one # Get sorted phase list, extract last one
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) HIGHEST=$(gsd_run query phases.list --pick directories[-1])
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs"
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
HIGHEST=$($GSD_SDK query phases.list --pick directories[-1])
``` ```
New phases continue from there: New phases continue from there:
@@ -154,7 +143,7 @@ grep -c "Pending" .planning/REQUIREMENTS.md
For each new phase (N, N+1, …), resolve the directory name via `init.phase-op` so the `project_code` prefix is honoured: For each new phase (N, N+1, …), resolve the directory name via `init.phase-op` so the `project_code` prefix is honoured:
```bash ```bash
INIT=$($GSD_SDK query init.phase-op "{NN}") INIT=$(gsd_run query init.phase-op "{NN}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
expected_phase_dir=$(echo "$INIT" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).expected_phase_dir)") expected_phase_dir=$(echo "$INIT" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).expected_phase_dir)")
mkdir -p "${expected_phase_dir}" mkdir -p "${expected_phase_dir}"
@@ -165,7 +154,7 @@ Repeat for each gap-closure phase number. This produces `{CODE}-{NN}-{slug}/` wh
## 9. Commit Roadmap and Requirements Update ## 9. Commit Roadmap and Requirements Update
```bash ```bash
$GSD_SDK query commit "docs(roadmap): add gap closure phases {N}-{M}" --files .planning/ROADMAP.md .planning/REQUIREMENTS.md gsd_run query commit "docs(roadmap): add gap closure phases {N}-{M}" --files .planning/ROADMAP.md .planning/REQUIREMENTS.md
``` ```
## 10. Offer Next Steps ## 10. Offer Next Steps

View File

@@ -31,26 +31,15 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
Load all context in one call (paths only to minimize orchestrator context): Load all context in one call (paths only to minimize orchestrator context):
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.plan-phase "$PHASE")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.plan-phase "$PHASE")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_RESEARCHER=$($GSD_SDK query agent-skills gsd-phase-researcher) AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-phase-researcher)
AGENT_SKILLS_PLANNER=$($GSD_SDK query agent-skills gsd-planner) AGENT_SKILLS_PLANNER=$(gsd_run query agent-skills gsd-planner)
AGENT_SKILLS_CHECKER=$($GSD_SDK query agent-skills gsd-plan-checker) AGENT_SKILLS_CHECKER=$(gsd_run query agent-skills gsd-plan-checker)
CONTEXT_WINDOW=$($GSD_SDK query config-get context_window 2>/dev/null || echo "200000") CONTEXT_WINDOW=$(gsd_run query config-get context_window 2>/dev/null || echo "200000")
TDD_MODE=$($GSD_SDK query config-get workflow.tdd_mode 2>/dev/null || echo "false") TDD_MODE=$(gsd_run query config-get workflow.tdd_mode 2>/dev/null || echo "false")
MVP_MODE_CFG=$($GSD_SDK query config-get workflow.mvp_mode 2>/dev/null || echo "false") MVP_MODE_CFG=$(gsd_run query config-get workflow.mvp_mode 2>/dev/null || echo "false")
``` ```
When `TDD_MODE` is `true`, the planner agent is instructed to apply `type: tdd` to eligible tasks using heuristics from `references/tdd.md`. The planner's `<required_reading>` is extended to include `@~/.claude/get-shit-done/references/tdd.md` so gate enforcement rules are available during planning. When `TDD_MODE` is `true`, the planner agent is instructed to apply `type: tdd` to eligible tasks using heuristics from `references/tdd.md`. The planner's `<required_reading>` is extended to include `@~/.claude/get-shit-done/references/tdd.md` so gate enforcement rules are available during planning.
@@ -148,7 +137,7 @@ The verb returns `true|false`. Full result also exposes `source` (`cli_flag` | `
```bash ```bash
WALKING_SKELETON=false WALKING_SKELETON=false
if [ "$MVP_MODE" = "true" ] && [ "$padded_phase" = "01" ]; then if [ "$MVP_MODE" = "true" ] && [ "$padded_phase" = "01" ]; then
PRIOR_SUMMARIES=$($GSD_SDK query phases.list --pick summaries_total 2>/dev/null || echo "0") PRIOR_SUMMARIES=$(gsd_run query phases.list --pick summaries_total 2>/dev/null || echo "0")
if [ "$PRIOR_SUMMARIES" = "0" ]; then WALKING_SKELETON=true; fi if [ "$PRIOR_SUMMARIES" = "0" ]; then WALKING_SKELETON=true; fi
fi fi
``` ```
@@ -177,7 +166,7 @@ Set `phase_dir="${expected_phase_dir}"` after creation.
Set `CHUNKED_MODE` from flag or config: Set `CHUNKED_MODE` from flag or config:
```bash ```bash
CHUNKED_CFG=$($GSD_SDK query config-get workflow.plan_chunked 2>/dev/null || echo "false") CHUNKED_CFG=$(gsd_run query config-get workflow.plan_chunked 2>/dev/null || echo "false")
CHUNKED_MODE=false CHUNKED_MODE=false
if [[ "$ARGUMENTS" =~ --chunked ]] || [[ "$CHUNKED_CFG" == "true" ]]; then if [[ "$ARGUMENTS" =~ --chunked ]] || [[ "$CHUNKED_CFG" == "true" ]]; then
CHUNKED_MODE=true CHUNKED_MODE=true
@@ -205,14 +194,14 @@ Exit workflow.
## 3. Validate Phase ## 3. Validate Phase
```bash ```bash
PHASE_INFO=$($GSD_SDK query roadmap.get-phase "${PHASE}") PHASE_INFO=$(gsd_run query roadmap.get-phase "${PHASE}")
``` ```
**If `found` is false:** Error with available phases. **If `found` is true:** Extract `phase_number`, `phase_name`, `goal` from JSON. **If `found` is false:** Error with available phases. **If `found` is true:** Extract `phase_number`, `phase_name`, `goal` from JSON.
Now that `PHASE` is finalized, resolve MVP mode: Now that `PHASE` is finalized, resolve MVP mode:
```bash ```bash
MVP_MODE=$($GSD_SDK query phase.mvp-mode "${PHASE}" $MVP_FLAG_ARG --pick active) MVP_MODE=$(gsd_run query phase.mvp-mode "${PHASE}" $MVP_FLAG_ARG --pick active)
``` ```
## 3.5. Handle PRD Express Path ## 3.5. Handle PRD Express Path
@@ -312,7 +301,7 @@ Use full relative paths. Group by topic area.]
5. Commit: 5. Commit:
```bash ```bash
$GSD_SDK query commit "docs(${padded_phase}): generate context from PRD" --files "${phase_dir}/${padded_phase}-CONTEXT.md" gsd_run query commit "docs(${padded_phase}): generate context from PRD" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
``` ```
6. Set `context_content` to the generated CONTEXT.md content and continue to step 5 (Handle Research). 6. Set `context_content` to the generated CONTEXT.md content and continue to step 5 (Handle Research).
@@ -346,7 +335,7 @@ If `context_path` is not null, display: `Using phase context from: ${context_pat
Read discuss mode for context gate label: Read discuss mode for context gate label:
```bash ```bash
DISCUSS_MODE=$($GSD_SDK query config-get workflow.discuss_mode 2>/dev/null || echo "discuss") DISCUSS_MODE=$(gsd_run query config-get workflow.discuss_mode 2>/dev/null || echo "discuss")
``` ```
If `TEXT_MODE` is true, present as a plain-text numbered list: If `TEXT_MODE` is true, present as a plain-text numbered list:
@@ -390,7 +379,7 @@ If "Run discuss-phase first":
```bash ```bash
AI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-AI-SPEC.md 2>/dev/null | head -1) AI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-AI-SPEC.md 2>/dev/null | head -1)
AI_PHASE_CFG=$($GSD_SDK query config-get workflow.ai_integration_phase 2>/dev/null || echo "true") AI_PHASE_CFG=$(gsd_run query config-get workflow.ai_integration_phase 2>/dev/null || echo "true")
``` ```
**Skip if `AI_PHASE_CFG` is `false`.** **Skip if `AI_PHASE_CFG` is `false`.**
@@ -497,7 +486,7 @@ Display banner:
### Spawn gsd-phase-researcher ### Spawn gsd-phase-researcher
```bash ```bash
PHASE_DESC=$($GSD_SDK query roadmap.get-phase "${PHASE}" --pick section) PHASE_DESC=$(gsd_run query roadmap.get-phase "${PHASE}" --pick section)
``` ```
Research prompt: Research prompt:
@@ -598,9 +587,9 @@ test -f "${PHASE_DIR}/${PADDED_PHASE}-VALIDATION.md" && echo "VALIDATION_CREATED
> Skip if `workflow.security_enforcement` is explicitly `false`. Absent = enabled. > Skip if `workflow.security_enforcement` is explicitly `false`. Absent = enabled.
```bash ```bash
SECURITY_CFG=$($GSD_SDK query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true") SECURITY_CFG=$(gsd_run query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true")
SECURITY_ASVS=$($GSD_SDK query config-get workflow.security_asvs_level --raw 2>/dev/null || echo "1") SECURITY_ASVS=$(gsd_run query config-get workflow.security_asvs_level --raw 2>/dev/null || echo "1")
SECURITY_BLOCK=$($GSD_SDK query config-get workflow.security_block_on --raw 2>/dev/null || echo "high") SECURITY_BLOCK=$(gsd_run query config-get workflow.security_block_on --raw 2>/dev/null || echo "high")
``` ```
**If `SECURITY_CFG` is `false`:** Skip to step 5.6. **If `SECURITY_CFG` is `false`:** Skip to step 5.6.
@@ -624,8 +613,8 @@ Continue to step 5.6. Security config is passed to the planner in step 8.
> Skip if `workflow.ui_phase` is explicitly `false` AND `workflow.ui_safety_gate` is explicitly `false` in `.planning/config.json`. If keys are absent, treat as enabled. > Skip if `workflow.ui_phase` is explicitly `false` AND `workflow.ui_safety_gate` is explicitly `false` in `.planning/config.json`. If keys are absent, treat as enabled.
```bash ```bash
UI_PHASE_CFG=$($GSD_SDK query config-get workflow.ui_phase 2>/dev/null || echo "true") UI_PHASE_CFG=$(gsd_run query config-get workflow.ui_phase 2>/dev/null || echo "true")
UI_GATE_CFG=$($GSD_SDK query config-get workflow.ui_safety_gate 2>/dev/null || echo "true") UI_GATE_CFG=$(gsd_run query config-get workflow.ui_safety_gate 2>/dev/null || echo "true")
``` ```
**If both are `false`:** Skip to step 6. **If both are `false`:** Skip to step 6.
@@ -633,7 +622,7 @@ UI_GATE_CFG=$($GSD_SDK query config-get workflow.ui_safety_gate 2>/dev/null || e
Check if phase has frontend indicators: Check if phase has frontend indicators:
```bash ```bash
PHASE_SECTION=$($GSD_SDK query roadmap.get-phase "${PHASE}" 2>/dev/null) PHASE_SECTION=$(gsd_run query roadmap.get-phase "${PHASE}" 2>/dev/null)
# Shell-free word-boundary gate (#3718): Node.js helper — no locale env-var dependency. # Shell-free word-boundary gate (#3718): Node.js helper — no locale env-var dependency.
# Reads via stdin to avoid OS ARG_MAX limits on large phase text. # Reads via stdin to avoid OS ARG_MAX limits on large phase text.
# Path anchored to repo root; falls back to CWD if git is unavailable # Path anchored to repo root; falls back to CWD if git is unavailable
@@ -658,7 +647,7 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
Read ephemeral chain flag (same field as `check.auto-mode` → `auto_chain_active`): Read ephemeral chain flag (same field as `check.auto-mode` → `auto_chain_active`):
```bash ```bash
AUTO_CHAIN=$($GSD_SDK query check auto-mode --pick auto_chain_active 2>/dev/null || echo "false") AUTO_CHAIN=$(gsd_run query check auto-mode --pick auto_chain_active 2>/dev/null || echo "false")
``` ```
**If `AUTO_CHAIN` is `true` (running inside a `--chain` or `--auto` pipeline):** **If `AUTO_CHAIN` is `true` (running inside a `--chain` or `--auto` pipeline):**
@@ -698,7 +687,7 @@ Also available:
Check if any files in the phase scope match schema patterns: Check if any files in the phase scope match schema patterns:
```bash ```bash
PHASE_SECTION=$($GSD_SDK query roadmap.get-phase "${PHASE}" --pick section 2>/dev/null) PHASE_SECTION=$(gsd_run query roadmap.get-phase "${PHASE}" --pick section 2>/dev/null)
``` ```
Scan `PHASE_SECTION`, `CONTEXT.md` (if loaded), and `RESEARCH.md` (if exists) for file paths matching these ORM patterns: Scan `PHASE_SECTION`, `CONTEXT.md` (if loaded), and `RESEARCH.md` (if exists) for file paths matching these ORM patterns:
@@ -813,7 +802,7 @@ Proceed to Step 7.8 (or Step 8 if pattern mapper is disabled) only if user selec
Check config: Check config:
```bash ```bash
PATTERN_MAPPER_CFG=$($GSD_SDK query config-get workflow.pattern_mapper 2>/dev/null || echo "true") PATTERN_MAPPER_CFG=$(gsd_run query config-get workflow.pattern_mapper 2>/dev/null || echo "true")
``` ```
**If `PATTERN_MAPPER_CFG` is `false`:** Skip to step 8. **If `PATTERN_MAPPER_CFG` is `false`:** Skip to step 8.
@@ -1106,7 +1095,7 @@ For each plan entry extracted from `PLAN-OUTLINE.md`:
5. **Commit per-plan:** 5. **Commit per-plan:**
```bash ```bash
$GSD_SDK query commit "docs(${PADDED_PHASE}): plan ${plan_id} (chunked)" --files "${PHASE_DIR}/${plan_id}-PLAN.md" gsd_run query commit "docs(${PADDED_PHASE}): plan ${plan_id} (chunked)" --files "${PHASE_DIR}/${plan_id}-PLAN.md"
``` ```
After all N plans are written and committed, treat this as `## PLANNING COMPLETE` and continue After all N plans are written and committed, treat this as `## PLANNING COMPLETE` and continue
@@ -1398,8 +1387,8 @@ Skipping bounce step.
**Read pass count:** **Read pass count:**
```bash ```bash
BOUNCE_PASSES=$($GSD_SDK query config-get workflow.plan_bounce_passes 2>/dev/null || echo "2") BOUNCE_PASSES=$(gsd_run query config-get workflow.plan_bounce_passes 2>/dev/null || echo "2")
BOUNCE_SCRIPT=$($GSD_SDK query config-get workflow.plan_bounce_script 2>/dev/null | jq -r '.' 2>/dev/null || true) BOUNCE_SCRIPT=$(gsd_run query config-get workflow.plan_bounce_script 2>/dev/null | jq -r '.' 2>/dev/null || true)
``` ```
Display banner: Display banner:
@@ -1444,7 +1433,7 @@ After the script returns, check that the bounced file still has valid YAML front
6. **Commit surviving bounced plans:** If at least one plan survived both the frontmatter validation and the checker re-run, commit the changes: 6. **Commit surviving bounced plans:** If at least one plan survived both the frontmatter validation and the checker re-run, commit the changes:
```bash ```bash
$GSD_SDK query commit "refactor(${padded_phase}): bounce plans through external refinement" --files "${PHASE_DIR}/*-PLAN.md" gsd_run query commit "refactor(${padded_phase}): bounce plans through external refinement" --files "${PHASE_DIR}/*-PLAN.md"
``` ```
Display summary: Display summary:
@@ -1518,9 +1507,9 @@ silently dropped on the way into the plans.
(nothing to translate) or if its `<decisions>` block is empty. (nothing to translate) or if its `<decisions>` block is empty.
```bash ```bash
GATE_CFG=$($GSD_SDK query config-get workflow.context_coverage_gate 2>/dev/null || echo "true") GATE_CFG=$(gsd_run query config-get workflow.context_coverage_gate 2>/dev/null || echo "true")
if [ "$GATE_CFG" != "false" ]; then if [ "$GATE_CFG" != "false" ]; then
GATE_RESULT=$($GSD_SDK query check.decision-coverage-plan "${PHASE_DIR}" "${CONTEXT_PATH}") GATE_RESULT=$(gsd_run query check.decision-coverage-plan "${PHASE_DIR}" "${CONTEXT_PATH}")
# BLOCKING: refuse to mark phase planned when a trackable decision is uncovered. # BLOCKING: refuse to mark phase planned when a trackable decision is uncovered.
# `passed: true` covers both real-pass and skipped cases (gate disabled / no CONTEXT.md / # `passed: true` covers both real-pass and skipped cases (gate disabled / no CONTEXT.md /
# no trackable decisions). Verify-phase counterpart deliberately omits this exit-1 — that # no trackable decisions). Verify-phase counterpart deliberately omits this exit-1 — that
@@ -1576,7 +1565,7 @@ after thousands of dollars of execution.
After plans pass all gates, record that planning is complete so STATE.md reflects the new phase status: After plans pass all gates, record that planning is complete so STATE.md reflects the new phase status:
```bash ```bash
$GSD_SDK query state.planned-phase --phase "${PHASE_NUMBER}" --name "${PHASE_NAME}" --plans "${PLAN_COUNT}" gsd_run query state.planned-phase --phase "${PHASE_NUMBER}" --name "${PHASE_NAME}" --plans "${PLAN_COUNT}"
``` ```
This updates STATUS to "Ready to execute", sets the correct plan count, and timestamps Last Activity. This updates STATUS to "Ready to execute", sets the correct plan count, and timestamps Last Activity.
@@ -1590,7 +1579,7 @@ After plans are finalized, annotate the ROADMAP.md plan list for this phase with
This step is derived entirely from existing PLAN frontmatter — no extra LLM pass is required. This step is derived entirely from existing PLAN frontmatter — no extra LLM pass is required.
```bash ```bash
$GSD_SDK query roadmap.annotate-dependencies "${PHASE_NUMBER}" gsd_run query roadmap.annotate-dependencies "${PHASE_NUMBER}"
``` ```
This operation is idempotent: if wave headers or cross-cutting constraints already exist in the ROADMAP phase section, the command returns without modifying the file. Skip this step if `plan_count` is 0. This operation is idempotent: if wave headers or cross-cutting constraints already exist in the ROADMAP phase section, the command returns without modifying the file. Skip this step if `plan_count` is 0.
@@ -1600,7 +1589,7 @@ This operation is idempotent: if wave headers or cross-cutting constraints alrea
If `commit_docs` is true (from the init JSON parsed in step 1), commit the generated plan artifacts (including any ROADMAP.md annotations from step 13c): If `commit_docs` is true (from the init JSON parsed in step 1), commit the generated plan artifacts (including any ROADMAP.md annotations from step 13c):
```bash ```bash
$GSD_SDK query commit "docs(${PADDED_PHASE}): create phase plan" --files "${PHASE_DIR}"/*-PLAN.md .planning/STATE.md .planning/ROADMAP.md gsd_run query commit "docs(${PADDED_PHASE}): create phase plan" --files "${PHASE_DIR}"/*-PLAN.md .planning/STATE.md .planning/ROADMAP.md
``` ```
This commits all PLAN.md files for the phase plus the updated STATE.md and ROADMAP.md to version-control the planning artifacts. Skip this step if `commit_docs` is false. This commits all PLAN.md files for the phase plus the updated STATE.md and ROADMAP.md to version-control the planning artifacts. Skip this step if `commit_docs` is false.
@@ -1617,7 +1606,7 @@ one place before execution begins.
**Skip if:** `workflow.post_planning_gaps` is `false`. Default is `true`. **Skip if:** `workflow.post_planning_gaps` is `false`. Default is `true`.
```bash ```bash
POST_PLANNING_GAPS=$($GSD_SDK query config-get workflow.post_planning_gaps --default true 2>/dev/null || echo true) POST_PLANNING_GAPS=$(gsd_run query config-get workflow.post_planning_gaps --default true 2>/dev/null || echo true)
if [ "$POST_PLANNING_GAPS" = "true" ]; then if [ "$POST_PLANNING_GAPS" = "true" ]; then
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" gap-analysis --phase-dir "${PHASE_DIR}" node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" gap-analysis --phase-dir "${PHASE_DIR}"
fi fi
@@ -1665,7 +1654,7 @@ Check for auto-advance trigger using values already loaded in step 1:
3. **Sync chain flag with intent** — if user invoked manually (no `--auto` and no `--chain`), clear the ephemeral chain flag from any previous interrupted `--auto` chain. This does NOT touch `workflow.auto_advance` (the user's persistent settings preference): 3. **Sync chain flag with intent** — if user invoked manually (no `--auto` and no `--chain`), clear the ephemeral chain flag from any previous interrupted `--auto` chain. This does NOT touch `workflow.auto_advance` (the user's persistent settings preference):
```bash ```bash
if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then
$GSD_SDK query config-set workflow._auto_chain_active false || true gsd_run query config-set workflow._auto_chain_active false || true
fi fi
``` ```
@@ -1676,7 +1665,7 @@ Set local variables from INIT (parsed once in step 1):
**If `--auto` or `--chain` flag present AND `AUTO_CHAIN` is not true:** Persist chain flag to config (handles direct invocation without prior discuss-phase): **If `--auto` or `--chain` flag present AND `AUTO_CHAIN` is not true:** Persist chain flag to config (handles direct invocation without prior discuss-phase):
```bash ```bash
if ([[ "$ARGUMENTS" =~ --auto ]] || [[ "$ARGUMENTS" =~ --chain ]]) && [[ "$AUTO_CHAIN" != "true" ]]; then if ([[ "$ARGUMENTS" =~ --auto ]] || [[ "$ARGUMENTS" =~ --chain ]]) && [[ "$AUTO_CHAIN" != "true" ]]; then
$GSD_SDK query config-set workflow._auto_chain_active true gsd_run query config-set workflow._auto_chain_active true
fi fi
``` ```

View File

@@ -43,19 +43,8 @@ echo "$ARGUMENTS" | grep -qE '\-\-ws\s+\S+' && GSD_WS=$(echo "$ARGUMENTS" | grep
## 1.5. Config Gate (feature disabled by default) ## 1.5. Config Gate (feature disabled by default)
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" CONVERGENCE_ENABLED=$(gsd_run query config-get workflow.plan_review_convergence 2>/dev/null || echo "false")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
CONVERGENCE_ENABLED=$($GSD_SDK query config-get workflow.plan_review_convergence 2>/dev/null || echo "false")
``` ```
**If `CONVERGENCE_ENABLED` is not `"true"`:** Display and exit: **If `CONVERGENCE_ENABLED` is not `"true"`:** Display and exit:

View File

@@ -135,19 +135,8 @@ Store relevant file paths as `$BREADCRUMBS`.
<step name="commit-seed"> <step name="commit-seed">
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query commit "docs: plant seed — {$IDEA}" --files .planning/seeds/SEED-{PADDED}-{slug}.md
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query commit "docs: plant seed — {$IDEA}" --files .planning/seeds/SEED-{PADDED}-{slug}.md
``` ```
</step> </step>
@@ -218,7 +207,7 @@ Update the seed file's frontmatter and sections with the gathered values:
Commit the update: Commit the update:
```bash ```bash
$GSD_SDK query commit "docs: enrich seed ${SEED_ID} — trigger + why + scope" --files "$SEED_FILE" gsd_run query commit "docs: enrich seed ${SEED_ID} — trigger + why + scope" --files "$SEED_FILE"
``` ```
Confirm: Confirm:

View File

@@ -130,19 +130,8 @@ Display: "◆ Scanning sessions..."
Run session scan: Run session scan:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" SCAN_RESULT=$(gsd_run query scan-sessions --json 2>/dev/null)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
SCAN_RESULT=$($GSD_SDK query scan-sessions --json 2>/dev/null)
``` ```
Parse the JSON output to get session count and project count. Parse the JSON output to get session count and project count.
@@ -162,7 +151,7 @@ Display: "◆ Sampling messages..."
Run profile sampling: Run profile sampling:
```bash ```bash
SAMPLE_RESULT=$($GSD_SDK query profile-sample --json 2>/dev/null) SAMPLE_RESULT=$(gsd_run query profile-sample --json 2>/dev/null)
``` ```
Parse the JSON output to get the temp directory path and message count. Parse the JSON output to get the temp directory path and message count.
@@ -213,7 +202,7 @@ Display: "Using questionnaire to build your profile."
**Get questions:** **Get questions:**
```bash ```bash
QUESTIONS=$($GSD_SDK query profile-questionnaire --json 2>/dev/null) QUESTIONS=$(gsd_run query profile-questionnaire --json 2>/dev/null)
``` ```
Parse the questions JSON. It contains 8 questions, one per dimension. Parse the questions JSON. It contains 8 questions, one per dimension.
@@ -236,7 +225,7 @@ Write the answers JSON to `$ANSWERS_PATH`.
**Convert answers to analysis:** **Convert answers to analysis:**
```bash ```bash
ANALYSIS_RESULT=$($GSD_SDK query profile-questionnaire --answers "$ANSWERS_PATH" --json 2>/dev/null) ANALYSIS_RESULT=$(gsd_run query profile-questionnaire --answers "$ANSWERS_PATH" --json 2>/dev/null)
``` ```
Parse the analysis JSON from the result. Parse the analysis JSON from the result.
@@ -283,7 +272,7 @@ Write updated analysis JSON back to `$ANALYSIS_PATH`.
Display: "◆ Writing profile..." Display: "◆ Writing profile..."
```bash ```bash
$GSD_SDK query write-profile --input "$ANALYSIS_PATH" --json gsd_run query write-profile --input "$ANALYSIS_PATH" --json
``` ```
Display: "✓ Profile written to $HOME/.claude/get-shit-done/USER-PROFILE.md" Display: "✓ Profile written to $HOME/.claude/get-shit-done/USER-PROFILE.md"
@@ -362,7 +351,7 @@ Generate selected artifacts sequentially (file I/O is fast, no benefit from para
**For /gsd-dev-preferences (if selected):** **For /gsd-dev-preferences (if selected):**
```bash ```bash
$GSD_SDK query generate-dev-preferences --analysis "$ANALYSIS_PATH" --json gsd_run query generate-dev-preferences --analysis "$ANALYSIS_PATH" --json
``` ```
Display: "✓ Generated /gsd-dev-preferences at $HOME/.claude/skills/gsd-dev-preferences/SKILL.md" Display: "✓ Generated /gsd-dev-preferences at $HOME/.claude/skills/gsd-dev-preferences/SKILL.md"
@@ -370,7 +359,7 @@ Display: "✓ Generated /gsd-dev-preferences at $HOME/.claude/skills/gsd-dev-pre
**For CLAUDE.md profile section (if selected):** **For CLAUDE.md profile section (if selected):**
```bash ```bash
$GSD_SDK query generate-claude-profile --analysis "$ANALYSIS_PATH" --json gsd_run query generate-claude-profile --analysis "$ANALYSIS_PATH" --json
``` ```
Display: "✓ Added profile section to CLAUDE.md" Display: "✓ Added profile section to CLAUDE.md"
@@ -378,7 +367,7 @@ Display: "✓ Added profile section to CLAUDE.md"
**For Global CLAUDE.md (if selected):** **For Global CLAUDE.md (if selected):**
```bash ```bash
$GSD_SDK query generate-claude-profile --analysis "$ANALYSIS_PATH" --global --json gsd_run query generate-claude-profile --analysis "$ANALYSIS_PATH" --global --json
``` ```
Display: "✓ Added profile section to $HOME/.claude/CLAUDE.md" Display: "✓ Added profile section to $HOME/.claude/CLAUDE.md"

View File

@@ -12,26 +12,15 @@ Read all files referenced by the invoking prompt's execution_context before star
**Load progress context (paths only):** **Load progress context (paths only):**
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.progress)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.progress)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
Extract from init JSON: `project_exists`, `roadmap_exists`, `state_exists`, `phases`, `current_phase`, `next_phase`, `milestone_version`, `completed_count`, `phase_count`, `paused_at`, `state_path`, `roadmap_path`, `project_path`, `config_path`. Extract from init JSON: `project_exists`, `roadmap_exists`, `state_exists`, `phases`, `current_phase`, `next_phase`, `milestone_version`, `completed_count`, `phase_count`, `paused_at`, `state_path`, `roadmap_path`, `project_path`, `config_path`.
```bash ```bash
DISCUSS_MODE=$($GSD_SDK query config-get workflow.discuss_mode 2>/dev/null || echo "discuss") DISCUSS_MODE=$(gsd_run query config-get workflow.discuss_mode 2>/dev/null || echo "discuss")
``` ```
If `project_exists` is false (no `.planning/` directory): If `project_exists` is false (no `.planning/` directory):
@@ -67,7 +56,7 @@ This minimizes orchestrator context usage.
**Get comprehensive roadmap analysis (replaces manual parsing):** **Get comprehensive roadmap analysis (replaces manual parsing):**
```bash ```bash
ROADMAP=$($GSD_SDK query roadmap.analyze) ROADMAP=$(gsd_run query roadmap.analyze)
``` ```
This returns structured JSON with: This returns structured JSON with:
@@ -86,7 +75,7 @@ Use this instead of manually reading/parsing ROADMAP.md.
- Find the 2-3 most recent SUMMARY.md files - Find the 2-3 most recent SUMMARY.md files
- Use `summary-extract` for efficient parsing: - Use `summary-extract` for efficient parsing:
```bash ```bash
$GSD_SDK query summary-extract <path> --fields one_liner gsd_run query summary-extract <path> --fields one_liner
``` ```
- This shows "what we've been working on" - This shows "what we've been working on"
</step> </step>
@@ -110,7 +99,7 @@ Use this instead of manually reading/parsing ROADMAP.md.
```bash ```bash
# Get formatted progress bar # Get formatted progress bar
PROGRESS_BAR=$($GSD_SDK query progress.bar --raw) PROGRESS_BAR=$(gsd_run query progress.bar --raw)
``` ```
Present: Present:
@@ -158,7 +147,7 @@ CONTEXT: [✓ if has_context | - if not]
Resolve `MVP_MODE` per phase via the centralized resolver. progress has no `--mvp` CLI flag (mode is inherited from the planned phase), so we omit `--cli-flag`: Resolve `MVP_MODE` per phase via the centralized resolver. progress has no `--mvp` CLI flag (mode is inherited from the planned phase), so we omit `--cli-flag`:
```bash ```bash
MVP_MODE=$($GSD_SDK query phase.mvp-mode "${PHASE_NUMBER}" --pick active) MVP_MODE=$(gsd_run query phase.mvp-mode "${PHASE_NUMBER}" --pick active)
``` ```
When `MVP_MODE=true`, the per-phase progress block adds a **user-flow status** sub-block sourced from the phase's PLAN.md task names. Each task whose name reads like a user-visible capability (e.g., "Register flow", "Login flow", "Password reset") is rendered as a status line: When `MVP_MODE=true`, the per-phase progress block adds a **user-flow status** sub-block sourced from the phase's PLAN.md task names. Each task whose name reads like a user-visible capability (e.g., "Register flow", "Login flow", "Password reset") is rendered as a status line:
@@ -210,7 +199,7 @@ Track:
Scan ALL phases in the current milestone for outstanding verification debt using the CLI (which respects milestone boundaries via `getMilestonePhaseFilter`): Scan ALL phases in the current milestone for outstanding verification debt using the CLI (which respects milestone boundaries via `getMilestonePhaseFilter`):
```bash ```bash
DEBT=$($GSD_SDK query audit-uat --raw 2>/dev/null) DEBT=$(gsd_run query audit-uat --raw 2>/dev/null)
``` ```
Parse JSON for `summary.total_items` and `summary.total_files`. Parse JSON for `summary.total_items` and `summary.total_files`.
@@ -273,7 +262,7 @@ Check if `{phase_num}-CONTEXT.md` exists in phase directory.
Check if current phase has UI indicators: Check if current phase has UI indicators:
```bash ```bash
PHASE_SECTION=$($GSD_SDK query roadmap.get-phase "${CURRENT_PHASE}" 2>/dev/null) PHASE_SECTION=$(gsd_run query roadmap.get-phase "${CURRENT_PHASE}" 2>/dev/null)
PHASE_HAS_UI=$(echo "$PHASE_SECTION" | grep -qi "UI hint.*yes" && echo "true" || echo "false") PHASE_HAS_UI=$(echo "$PHASE_SECTION" | grep -qi "UI hint.*yes" && echo "true" || echo "false")
``` ```
@@ -419,7 +408,7 @@ Read ROADMAP.md to get the next phase's name and goal.
Check if next phase has UI indicators: Check if next phase has UI indicators:
```bash ```bash
NEXT_PHASE_SECTION=$($GSD_SDK query roadmap.get-phase "$((Z+1))" 2>/dev/null) NEXT_PHASE_SECTION=$(gsd_run query roadmap.get-phase "$((Z+1))" 2>/dev/null)
NEXT_HAS_UI=$(echo "$NEXT_PHASE_SECTION" | grep -qi "UI hint.*yes" && echo "true" || echo "false") NEXT_HAS_UI=$(echo "$NEXT_PHASE_SECTION" | grep -qi "UI hint.*yes" && echo "true" || echo "false")
``` ```

View File

@@ -125,37 +125,26 @@ If `$VALIDATE_MODE` only:
**Step 2: Initialize** **Step 2: Initialize**
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.quick "$DESCRIPTION")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.quick "$DESCRIPTION")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_PLANNER=$($GSD_SDK query agent-skills gsd-planner) AGENT_SKILLS_PLANNER=$(gsd_run query agent-skills gsd-planner)
AGENT_SKILLS_EXECUTOR=$($GSD_SDK query agent-skills gsd-executor) AGENT_SKILLS_EXECUTOR=$(gsd_run query agent-skills gsd-executor)
AGENT_SKILLS_CHECKER=$($GSD_SDK query agent-skills gsd-plan-checker) AGENT_SKILLS_CHECKER=$(gsd_run query agent-skills gsd-plan-checker)
AGENT_SKILLS_VERIFIER=$($GSD_SDK query agent-skills gsd-verifier) AGENT_SKILLS_VERIFIER=$(gsd_run query agent-skills gsd-verifier)
``` ```
Parse JSON for: `planner_model`, `executor_model`, `checker_model`, `verifier_model`, `commit_docs`, `branch_name`, `quick_id`, `slug`, `date`, `timestamp`, `quick_dir`, `task_dir`, `roadmap_exists`, `planning_exists`. Parse JSON for: `planner_model`, `executor_model`, `checker_model`, `verifier_model`, `commit_docs`, `branch_name`, `quick_id`, `slug`, `date`, `timestamp`, `quick_dir`, `task_dir`, `roadmap_exists`, `planning_exists`.
```bash ```bash
USE_WORKTREES=$($GSD_SDK query config-get workflow.use_worktrees 2>/dev/null || echo "true") USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees 2>/dev/null || echo "true")
``` ```
If `USE_WORKTREES` is not `"false"`, run a startup orphan sweep before spawning any executors. This reaps locked worktrees whose lock-owner process is dead, whose branch is merged into the default branch, and whose lock file mtime is older than 5 minutes. Running it at startup prevents accumulation of orphaned worktrees from prior sessions that exited without cleanup (#3707). If `USE_WORKTREES` is not `"false"`, run a startup orphan sweep before spawning any executors. This reaps locked worktrees whose lock-owner process is dead, whose branch is merged into the default branch, and whose lock file mtime is older than 5 minutes. Running it at startup prevents accumulation of orphaned worktrees from prior sessions that exited without cleanup (#3707).
```bash ```bash
if [ "$USE_WORKTREES" != "false" ]; then if [ "$USE_WORKTREES" != "false" ]; then
$GSD_SDK query worktree.reap-orphans 2>/dev/null || true gsd_run query worktree.reap-orphans 2>/dev/null || true
fi fi
``` ```
@@ -641,7 +630,7 @@ Skip this step entirely if `USE_WORKTREES === "false"` (non-worktree mode: PLAN.
```bash ```bash
if [ "${USE_WORKTREES}" != "false" ]; then if [ "${USE_WORKTREES}" != "false" ]; then
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true") COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
if [ "$COMMIT_DOCS" != "false" ]; then if [ "$COMMIT_DOCS" != "false" ]; then
git add "${QUICK_DIR}/${quick_id}-PLAN.md" git add "${QUICK_DIR}/${quick_id}-PLAN.md"
# No-op skip if nothing actually staged (idempotent re-runs). # No-op skip if nothing actually staged (idempotent re-runs).
@@ -650,7 +639,7 @@ if [ "${USE_WORKTREES}" != "false" ]; then
else else
# Run hooks normally (#2924). If a project opts out via # Run hooks normally (#2924). If a project opts out via
# workflow.worktree_skip_hooks=true, honor that opt-in only. # workflow.worktree_skip_hooks=true, honor that opt-in only.
SKIP_HOOKS=$($GSD_SDK query config-get workflow.worktree_skip_hooks 2>/dev/null || echo "false") SKIP_HOOKS=$(gsd_run query config-get workflow.worktree_skip_hooks 2>/dev/null || echo "false")
if [ "$SKIP_HOOKS" = "true" ]; then if [ "$SKIP_HOOKS" = "true" ]; then
git commit --no-verify -m "docs(${quick_id}): pre-dispatch plan for ${DESCRIPTION}" -- "${QUICK_DIR}/${quick_id}-PLAN.md" \ git commit --no-verify -m "docs(${quick_id}): pre-dispatch plan for ${DESCRIPTION}" -- "${QUICK_DIR}/${quick_id}-PLAN.md" \
|| { echo "ERROR: pre-dispatch PLAN.md commit failed (--no-verify path). Aborting before executor dispatch." >&2; exit 1; } || { echo "ERROR: pre-dispatch PLAN.md commit failed (--no-verify path). Aborting before executor dispatch." >&2; exit 1; }
@@ -790,7 +779,7 @@ After executor returns:
# base, deletion diffs, merge result, and worktree removal before branch # base, deletion diffs, merge result, and worktree removal before branch
# deletion. If it blocks, resolve the reported manifest entry and rerun. # deletion. If it blocks, resolve the reported manifest entry and rerun.
# Fail closed: SDK refusal (safety guard #3174/#3384) must surface — do not swallow exit 1. # Fail closed: SDK refusal (safety guard #3174/#3384) must surface — do not swallow exit 1.
$GSD_SDK query worktree.cleanup-wave --manifest "$QUICK_WORKTREE_MANIFEST" || exit 1 gsd_run query worktree.cleanup-wave --manifest "$QUICK_WORKTREE_MANIFEST" || exit 1
``` ```
If `workflow.use_worktrees` is `false`, skip this step. If `workflow.use_worktrees` is `false`, skip this step.
2. Verify summary exists at `${QUICK_DIR}/${quick_id}-SUMMARY.md` 2. Verify summary exists at `${QUICK_DIR}/${quick_id}-SUMMARY.md`
@@ -811,7 +800,7 @@ Skip this step entirely if `$FULL_MODE` is false.
**Config gate:** **Config gate:**
```bash ```bash
CODE_REVIEW_ENABLED=$($GSD_SDK query config-get workflow.code_review 2>/dev/null || echo "true") CODE_REVIEW_ENABLED=$(gsd_run query config-get workflow.code_review 2>/dev/null || echo "true")
``` ```
If `"false"`, skip with message "Code review skipped (workflow.code_review=false)". If `"false"`, skip with message "Code review skipped (workflow.code_review=false)".
@@ -977,14 +966,14 @@ Build file list:
# Explicitly stage all artifacts before commit — PLAN.md may be untracked # Explicitly stage all artifacts before commit — PLAN.md may be untracked
# if the executor ran without worktree isolation and committed docs early # if the executor ran without worktree isolation and committed docs early
# Filter .planning/ files from staging if commit_docs is disabled (#1783) # Filter .planning/ files from staging if commit_docs is disabled (#1783)
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true") COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
if [ "$COMMIT_DOCS" = "false" ]; then if [ "$COMMIT_DOCS" = "false" ]; then
file_list_filtered=$(echo "${file_list}" | tr ' ' '\n' | grep -v '^\.planning/' | tr '\n' ' ') file_list_filtered=$(echo "${file_list}" | tr ' ' '\n' | grep -v '^\.planning/' | tr '\n' ' ')
git add ${file_list_filtered} 2>/dev/null git add ${file_list_filtered} 2>/dev/null
else else
git add ${file_list} 2>/dev/null git add ${file_list} 2>/dev/null
fi fi
$GSD_SDK query commit "docs(quick-${quick_id}): ${DESCRIPTION}" --files ${file_list} gsd_run query commit "docs(quick-${quick_id}): ${DESCRIPTION}" --files ${file_list}
``` ```
Get final commit hash: Get final commit hash:

View File

@@ -29,19 +29,8 @@ Exit.
Load phase operation context: Load phase operation context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${target}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${target}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -92,13 +81,13 @@ Wait for confirmation.
**Delegate the entire removal operation to `gsd-tools.cjs query phase.remove`:** **Delegate the entire removal operation to `gsd-tools.cjs query phase.remove`:**
```bash ```bash
RESULT=$($GSD_SDK query phase.remove "${target}") RESULT=$(gsd_run query phase.remove "${target}")
``` ```
If the phase has executed plans (SUMMARY.md files), the CLI will error. Use `--force` only if the user confirms: If the phase has executed plans (SUMMARY.md files), the CLI will error. Use `--force` only if the user confirms:
```bash ```bash
RESULT=$($GSD_SDK query phase.remove "${target}" --force) RESULT=$(gsd_run query phase.remove "${target}" --force)
``` ```
The CLI handles: The CLI handles:
@@ -115,7 +104,7 @@ Extract from result: `removed`, `directory_deleted`, `renamed_directories`, `ren
Stage and commit the removal: Stage and commit the removal:
```bash ```bash
$GSD_SDK query commit "chore: remove phase {target} ({original-phase-name})" --files .planning/ gsd_run query commit "chore: remove phase {target} ({original-phase-name})" --files .planning/
``` ```
The commit message preserves the historical record of what was removed. The commit message preserves the historical record of what was removed.

View File

@@ -13,19 +13,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Extract workspace name from $ARGUMENTS. Extract workspace name from $ARGUMENTS.
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.remove-workspace "$WORKSPACE_NAME")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.remove-workspace "$WORKSPACE_NAME")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```

View File

@@ -20,19 +20,8 @@ Instantly restore full project context so "Where were we?" has an immediate, com
Load all context in one call: Load all context in one call:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.resume)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.resume)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```

View File

@@ -14,6 +14,7 @@ A plan that survives review from 2-3 independent AI systems is more robust.
Check which AI CLIs are available on the system: Check which AI CLIs are available on the system:
```bash ```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
# Check each CLI # Check each CLI
command -v gemini >/dev/null 2>&1 && echo "gemini:available" || echo "gemini:missing" command -v gemini >/dev/null 2>&1 && echo "gemini:available" || echo "gemini:missing"
command -v claude >/dev/null 2>&1 && echo "claude:available" || echo "claude:missing" command -v claude >/dev/null 2>&1 && echo "claude:available" || echo "claude:missing"
@@ -24,27 +25,15 @@ command -v qwen >/dev/null 2>&1 && echo "qwen:available" || echo "qwen:missing"
command -v cursor >/dev/null 2>&1 && echo "cursor:available" || echo "cursor:missing" command -v cursor >/dev/null 2>&1 && echo "cursor:available" || echo "cursor:missing"
# Check local model servers (OpenAI-compatible HTTP API — no CLI binary required) # Check local model servers (OpenAI-compatible HTTP API — no CLI binary required)
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) OLLAMA_HOST=$(gsd_run query config-get review.ollama_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs"
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
OLLAMA_HOST=$($GSD_SDK query config-get review.ollama_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
if [ -z "$OLLAMA_HOST" ] || [ "$OLLAMA_HOST" = "null" ]; then OLLAMA_HOST="http://localhost:11434"; fi if [ -z "$OLLAMA_HOST" ] || [ "$OLLAMA_HOST" = "null" ]; then OLLAMA_HOST="http://localhost:11434"; fi
curl -s --max-time 2 "${OLLAMA_HOST}/v1/models" >/dev/null 2>&1 && echo "ollama:available" || echo "ollama:missing" curl -s --max-time 2 "${OLLAMA_HOST}/v1/models" >/dev/null 2>&1 && echo "ollama:available" || echo "ollama:missing"
LM_STUDIO_HOST=$($GSD_SDK query config-get review.lm_studio_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "") LM_STUDIO_HOST=$(gsd_run query config-get review.lm_studio_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
if [ -z "$LM_STUDIO_HOST" ] || [ "$LM_STUDIO_HOST" = "null" ]; then LM_STUDIO_HOST="http://localhost:1234"; fi if [ -z "$LM_STUDIO_HOST" ] || [ "$LM_STUDIO_HOST" = "null" ]; then LM_STUDIO_HOST="http://localhost:1234"; fi
curl -s --max-time 2 "${LM_STUDIO_HOST}/v1/models" >/dev/null 2>&1 && echo "lm_studio:available" || echo "lm_studio:missing" curl -s --max-time 2 "${LM_STUDIO_HOST}/v1/models" >/dev/null 2>&1 && echo "lm_studio:available" || echo "lm_studio:missing"
LLAMA_CPP_HOST=$($GSD_SDK query config-get review.llama_cpp_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "") LLAMA_CPP_HOST=$(gsd_run query config-get review.llama_cpp_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
if [ -z "$LLAMA_CPP_HOST" ] || [ "$LLAMA_CPP_HOST" = "null" ]; then LLAMA_CPP_HOST="http://localhost:8080"; fi if [ -z "$LLAMA_CPP_HOST" ] || [ "$LLAMA_CPP_HOST" = "null" ]; then LLAMA_CPP_HOST="http://localhost:8080"; fi
curl -s --max-time 2 "${LLAMA_CPP_HOST}/v1/models" >/dev/null 2>&1 && echo "llama_cpp:available" || echo "llama_cpp:missing" curl -s --max-time 2 "${LLAMA_CPP_HOST}/v1/models" >/dev/null 2>&1 && echo "llama_cpp:available" || echo "llama_cpp:missing"
``` ```
@@ -120,7 +109,7 @@ Rules:
Collect phase artifacts for the review prompt: Collect phase artifacts for the review prompt:
```bash ```bash
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}") INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -225,10 +214,10 @@ Read model preferences from planning config. Null/missing values fall back to CL
```bash ```bash
# JSON scalars from gsd-tools.cjs query; use jq -r to strip JSON string quotes (install jq if missing) # JSON scalars from gsd-tools.cjs query; use jq -r to strip JSON string quotes (install jq if missing)
GEMINI_MODEL=$($GSD_SDK query config-get review.models.gemini 2>/dev/null | jq -r '.' 2>/dev/null || true) GEMINI_MODEL=$(gsd_run query config-get review.models.gemini 2>/dev/null | jq -r '.' 2>/dev/null || true)
CLAUDE_MODEL=$($GSD_SDK query config-get review.models.claude 2>/dev/null | jq -r '.' 2>/dev/null || true) CLAUDE_MODEL=$(gsd_run query config-get review.models.claude 2>/dev/null | jq -r '.' 2>/dev/null || true)
CODEX_MODEL=$($GSD_SDK query config-get review.models.codex 2>/dev/null | jq -r '.' 2>/dev/null || true) CODEX_MODEL=$(gsd_run query config-get review.models.codex 2>/dev/null | jq -r '.' 2>/dev/null || true)
OPENCODE_MODEL=$($GSD_SDK query config-get review.models.opencode 2>/dev/null | jq -r '.' 2>/dev/null || true) OPENCODE_MODEL=$(gsd_run query config-get review.models.opencode 2>/dev/null | jq -r '.' 2>/dev/null || true)
``` ```
For each selected CLI, invoke in sequence (not parallel — avoid rate limits): For each selected CLI, invoke in sequence (not parallel — avoid rate limits):
@@ -325,7 +314,7 @@ prepare_trimmed_prompt_for_reviewer() {
REQUIREMENTS_ARG="" REQUIREMENTS_ARG=""
[ -f "/tmp/gsd-review-{phase}-requirements.md" ] && REQUIREMENTS_ARG="--requirements-file /tmp/gsd-review-{phase}-requirements.md" [ -f "/tmp/gsd-review-{phase}-requirements.md" ] && REQUIREMENTS_ARG="--requirements-file /tmp/gsd-review-{phase}-requirements.md"
$GSD_SDK query prompt-budget \ gsd_run query prompt-budget \
--budget "$REVIEWER_BUDGET" \ --budget "$REVIEWER_BUDGET" \
--instructions-file "/tmp/gsd-review-{phase}-instructions.md" \ --instructions-file "/tmp/gsd-review-{phase}-instructions.md" \
--roadmap-file "/tmp/gsd-review-{phase}-roadmap.md" \ --roadmap-file "/tmp/gsd-review-{phase}-roadmap.md" \
@@ -336,9 +325,9 @@ prepare_trimmed_prompt_for_reviewer() {
} }
# Resolve prompt budget for Ollama: per-reviewer override > global default > null # Resolve prompt budget for Ollama: per-reviewer override > global default > null
OLLAMA_REVIEWER_BUDGET=$($GSD_SDK query config-get review.max_prompt_tokens_per_reviewer.ollama 2>/dev/null | jq -r '.' 2>/dev/null || echo "null") OLLAMA_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens_per_reviewer.ollama 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
if [ -z "$OLLAMA_REVIEWER_BUDGET" ] || [ "$OLLAMA_REVIEWER_BUDGET" = "null" ]; then if [ -z "$OLLAMA_REVIEWER_BUDGET" ] || [ "$OLLAMA_REVIEWER_BUDGET" = "null" ]; then
OLLAMA_REVIEWER_BUDGET=$($GSD_SDK query config-get review.max_prompt_tokens 2>/dev/null | jq -r '.' 2>/dev/null || echo "null") OLLAMA_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
fi fi
# Apply budget trim for Ollama if a budget is configured # Apply budget trim for Ollama if a budget is configured
@@ -362,9 +351,9 @@ if [ -n "$OLLAMA_REVIEWER_BUDGET" ] && [ "$OLLAMA_REVIEWER_BUDGET" != "null" ] &
fi fi
if [ "$OLLAMA_SKIP" != "1" ]; then if [ "$OLLAMA_SKIP" != "1" ]; then
OLLAMA_HOST=$($GSD_SDK query config-get review.ollama_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "") OLLAMA_HOST=$(gsd_run query config-get review.ollama_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
if [ -z "$OLLAMA_HOST" ] || [ "$OLLAMA_HOST" = "null" ]; then OLLAMA_HOST="http://localhost:11434"; fi if [ -z "$OLLAMA_HOST" ] || [ "$OLLAMA_HOST" = "null" ]; then OLLAMA_HOST="http://localhost:11434"; fi
OLLAMA_MODEL=$($GSD_SDK query config-get review.models.ollama 2>/dev/null | jq -r '.' 2>/dev/null || echo "") OLLAMA_MODEL=$(gsd_run query config-get review.models.ollama 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
if [ -z "$OLLAMA_MODEL" ] || [ "$OLLAMA_MODEL" = "null" ]; then if [ -z "$OLLAMA_MODEL" ] || [ "$OLLAMA_MODEL" = "null" ]; then
OLLAMA_MODEL=$(curl -s --max-time 2 "${OLLAMA_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "llama3"' 2>/dev/null || echo "llama3") OLLAMA_MODEL=$(curl -s --max-time 2 "${OLLAMA_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "llama3"' 2>/dev/null || echo "llama3")
fi fi
@@ -384,9 +373,9 @@ fi
**LM Studio (local, OpenAI-compatible):** **LM Studio (local, OpenAI-compatible):**
```bash ```bash
# Resolve prompt budget for LM Studio: per-reviewer override > global default > null # Resolve prompt budget for LM Studio: per-reviewer override > global default > null
LM_STUDIO_REVIEWER_BUDGET=$($GSD_SDK query config-get review.max_prompt_tokens_per_reviewer.lm_studio 2>/dev/null | jq -r '.' 2>/dev/null || echo "null") LM_STUDIO_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens_per_reviewer.lm_studio 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
if [ -z "$LM_STUDIO_REVIEWER_BUDGET" ] || [ "$LM_STUDIO_REVIEWER_BUDGET" = "null" ]; then if [ -z "$LM_STUDIO_REVIEWER_BUDGET" ] || [ "$LM_STUDIO_REVIEWER_BUDGET" = "null" ]; then
LM_STUDIO_REVIEWER_BUDGET=$($GSD_SDK query config-get review.max_prompt_tokens 2>/dev/null | jq -r '.' 2>/dev/null || echo "null") LM_STUDIO_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
fi fi
# Apply budget trim for LM Studio if a budget is configured # Apply budget trim for LM Studio if a budget is configured
@@ -410,9 +399,9 @@ if [ -n "$LM_STUDIO_REVIEWER_BUDGET" ] && [ "$LM_STUDIO_REVIEWER_BUDGET" != "nul
fi fi
if [ "$LM_STUDIO_SKIP" != "1" ]; then if [ "$LM_STUDIO_SKIP" != "1" ]; then
LM_STUDIO_HOST=$($GSD_SDK query config-get review.lm_studio_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "") LM_STUDIO_HOST=$(gsd_run query config-get review.lm_studio_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
if [ -z "$LM_STUDIO_HOST" ] || [ "$LM_STUDIO_HOST" = "null" ]; then LM_STUDIO_HOST="http://localhost:1234"; fi if [ -z "$LM_STUDIO_HOST" ] || [ "$LM_STUDIO_HOST" = "null" ]; then LM_STUDIO_HOST="http://localhost:1234"; fi
LM_STUDIO_MODEL=$($GSD_SDK query config-get review.models.lm_studio 2>/dev/null | jq -r '.' 2>/dev/null || echo "") LM_STUDIO_MODEL=$(gsd_run query config-get review.models.lm_studio 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
if [ -z "$LM_STUDIO_MODEL" ] || [ "$LM_STUDIO_MODEL" = "null" ]; then if [ -z "$LM_STUDIO_MODEL" ] || [ "$LM_STUDIO_MODEL" = "null" ]; then
LM_STUDIO_MODEL=$(curl -s --max-time 2 "${LM_STUDIO_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "local-model"' 2>/dev/null || echo "local-model") LM_STUDIO_MODEL=$(curl -s --max-time 2 "${LM_STUDIO_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "local-model"' 2>/dev/null || echo "local-model")
fi fi
@@ -437,9 +426,9 @@ fi
**llama.cpp (local, OpenAI-compatible):** **llama.cpp (local, OpenAI-compatible):**
```bash ```bash
# Resolve prompt budget for llama.cpp: per-reviewer override > global default > null # Resolve prompt budget for llama.cpp: per-reviewer override > global default > null
LLAMA_CPP_REVIEWER_BUDGET=$($GSD_SDK query config-get review.max_prompt_tokens_per_reviewer.llama_cpp 2>/dev/null | jq -r '.' 2>/dev/null || echo "null") LLAMA_CPP_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens_per_reviewer.llama_cpp 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
if [ -z "$LLAMA_CPP_REVIEWER_BUDGET" ] || [ "$LLAMA_CPP_REVIEWER_BUDGET" = "null" ]; then if [ -z "$LLAMA_CPP_REVIEWER_BUDGET" ] || [ "$LLAMA_CPP_REVIEWER_BUDGET" = "null" ]; then
LLAMA_CPP_REVIEWER_BUDGET=$($GSD_SDK query config-get review.max_prompt_tokens 2>/dev/null | jq -r '.' 2>/dev/null || echo "null") LLAMA_CPP_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
fi fi
# Apply budget trim for llama.cpp if a budget is configured # Apply budget trim for llama.cpp if a budget is configured
@@ -463,9 +452,9 @@ if [ -n "$LLAMA_CPP_REVIEWER_BUDGET" ] && [ "$LLAMA_CPP_REVIEWER_BUDGET" != "nul
fi fi
if [ "$LLAMA_CPP_SKIP" != "1" ]; then if [ "$LLAMA_CPP_SKIP" != "1" ]; then
LLAMA_CPP_HOST=$($GSD_SDK query config-get review.llama_cpp_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "") LLAMA_CPP_HOST=$(gsd_run query config-get review.llama_cpp_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
if [ -z "$LLAMA_CPP_HOST" ] || [ "$LLAMA_CPP_HOST" = "null" ]; then LLAMA_CPP_HOST="http://localhost:8080"; fi if [ -z "$LLAMA_CPP_HOST" ] || [ "$LLAMA_CPP_HOST" = "null" ]; then LLAMA_CPP_HOST="http://localhost:8080"; fi
LLAMA_CPP_MODEL=$($GSD_SDK query config-get review.models.llama_cpp 2>/dev/null | jq -r '.' 2>/dev/null || echo "") LLAMA_CPP_MODEL=$(gsd_run query config-get review.models.llama_cpp 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
if [ -z "$LLAMA_CPP_MODEL" ] || [ "$LLAMA_CPP_MODEL" = "null" ]; then if [ -z "$LLAMA_CPP_MODEL" ] || [ "$LLAMA_CPP_MODEL" = "null" ]; then
LLAMA_CPP_MODEL=$(curl -s --max-time 2 "${LLAMA_CPP_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "local-model"' 2>/dev/null || echo "local-model") LLAMA_CPP_MODEL=$(curl -s --max-time 2 "${LLAMA_CPP_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "local-model"' 2>/dev/null || echo "local-model")
fi fi
@@ -597,7 +586,7 @@ trimmed_reviewers: # only present if at least one reviewer was trimmed
Commit: Commit:
```bash ```bash
$GSD_SDK query commit "docs: cross-AI review for phase {N}" --files {phase_dir}/{padded_phase}-REVIEWS.md gsd_run query commit "docs: cross-AI review for phase {N}" --files {phase_dir}/{padded_phase}-REVIEWS.md
``` ```
</step> </step>

View File

@@ -39,19 +39,8 @@ Exit.
## Step 2: Check for existing documents ## Step 2: Check for existing documents
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.map-codebase 2>/dev/null || echo "{}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.map-codebase 2>/dev/null || echo "{}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```

View File

@@ -16,28 +16,17 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
## 0. Initialize ## 0. Initialize
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_AUDITOR=$($GSD_SDK query agent-skills gsd-security-auditor) AGENT_SKILLS_AUDITOR=$(gsd_run query agent-skills gsd-security-auditor)
``` ```
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`. Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
```bash ```bash
AUDITOR_MODEL=$($GSD_SDK query resolve-model gsd-security-auditor --raw) AUDITOR_MODEL=$(gsd_run query resolve-model gsd-security-auditor --raw)
SECURITY_CFG=$($GSD_SDK query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true") SECURITY_CFG=$(gsd_run query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true")
``` ```
If `SECURITY_CFG` is `false`: exit with "Security enforcement disabled. Enable via /gsd:settings." If `SECURITY_CFG` is `false`: exit with "Security enforcement disabled. Enable via /gsd:settings."
@@ -158,7 +147,7 @@ Do NOT emit next-phase routing. Stop here.
## 7. Commit ## 7. Commit
```bash ```bash
$GSD_SDK query commit "docs(phase-${PHASE}): add/update security threat verification" gsd_run query commit "docs(phase-${PHASE}): add/update security threat verification"
``` ```
## 8. Results + Routing ## 8. Results + Routing

View File

@@ -20,19 +20,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Ensure config exists and resolve the workstream-aware config path (mirrors `settings.md`): Ensure config exists and resolve the workstream-aware config path (mirrors `settings.md`):
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query config-ensure-section
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query config-ensure-section
if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then
if [[ -f .planning/active-workstream ]]; then if [[ -f .planning/active-workstream ]]; then
WS=$(tr -d '\n\r' < .planning/active-workstream) WS=$(tr -d '\n\r' < .planning/active-workstream)
@@ -476,12 +465,12 @@ AskUserQuestion([
For each tier where the user chose "Enter model ID": For each tier where the user chose "Enter model ID":
```bash ```bash
$GSD_SDK query config-set model_profile_overrides.<runtime>.<tier> "<model-id>" gsd_run query config-set model_profile_overrides.<runtime>.<tier> "<model-id>"
``` ```
For each tier where the user chose "Clear override", remove the key by setting it to null: For each tier where the user chose "Clear override", remove the key by setting it to null:
```bash ```bash
$GSD_SDK query config-set model_profile_overrides.<runtime>.<tier> null gsd_run query config-set model_profile_overrides.<runtime>.<tier> null
``` ```
"Keep current" selections are skipped entirely. Never write a key the user did not explicitly "Keep current" selections are skipped entirely. Never write a key the user did not explicitly
@@ -499,14 +488,14 @@ keys and sibling sub-objects.
```bash ```bash
# Example — only write keys the user changed. "Keep current" selections are skipped. # Example — only write keys the user changed. "Keep current" selections are skipped.
$GSD_SDK query config-set workflow.plan_bounce_passes 5 gsd_run query config-set workflow.plan_bounce_passes 5
$GSD_SDK query config-set workflow.subagent_timeout 900 gsd_run query config-set workflow.subagent_timeout 900
$GSD_SDK query config-set git.base_branch main gsd_run query config-set git.base_branch main
$GSD_SDK query config-set context_window 1000000 gsd_run query config-set context_window 1000000
# Runtime model tier examples: # Runtime model tier examples:
$GSD_SDK query config-set runtime gemini gsd_run query config-set runtime gemini
$GSD_SDK query config-set model_profile_overrides.gemini.opus gemini-3-ultra gsd_run query config-set model_profile_overrides.gemini.opus gemini-3-ultra
$GSD_SDK query config-set model_profile_overrides.gemini.haiku null gsd_run query config-set model_profile_overrides.gemini.haiku null
``` ```
Conceptual shape after merge (unchanged top-level keys like `model_profile`, Conceptual shape after merge (unchanged top-level keys like `model_profile`,

View File

@@ -42,19 +42,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Ensure config exists and resolve the active config path (flat vs workstream, #2282): Ensure config exists and resolve the active config path (flat vs workstream, #2282):
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query config-ensure-section
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query config-ensure-section
if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then
if [[ -f .planning/active-workstream ]]; then if [[ -f .planning/active-workstream ]]; then
WS=$(tr -d '\n\r' < .planning/active-workstream) WS=$(tr -d '\n\r' < .planning/active-workstream)
@@ -77,10 +66,10 @@ integration field, compute one of:
- `<value>` — non-secret routing/skill string, shown as-is - `<value>` — non-secret routing/skill string, shown as-is
```bash ```bash
BRAVE=$($GSD_SDK query config-get brave_search --default null) BRAVE=$(gsd_run query config-get brave_search --default null)
FIRECRAWL=$($GSD_SDK query config-get firecrawl --default null) FIRECRAWL=$(gsd_run query config-get firecrawl --default null)
EXA=$($GSD_SDK query config-get exa_search --default null) EXA=$(gsd_run query config-get exa_search --default null)
SEARCH_GITIGNORED=$($GSD_SDK query config-get search_gitignored --default false) SEARCH_GITIGNORED=$(gsd_run query config-get search_gitignored --default false)
``` ```
For each secret key (`brave_search`, `firecrawl`, `exa_search`) the displayed For each secret key (`brave_search`, `firecrawl`, `exa_search`) the displayed
@@ -141,16 +130,16 @@ key value. **The answer must not be echoed back** in subsequent question
descriptions or confirmation text. Write the value via: descriptions or confirmation text. Write the value via:
```bash ```bash
$GSD_SDK query config-set brave_search "<value>" # masked in output gsd_run query config-set brave_search "<value>" # masked in output
$GSD_SDK query config-set firecrawl "<value>" # masked in output gsd_run query config-set firecrawl "<value>" # masked in output
$GSD_SDK query config-set exa_search "<value>" # masked in output gsd_run query config-set exa_search "<value>" # masked in output
$GSD_SDK query config-set search_gitignored true|false gsd_run query config-set search_gitignored true|false
``` ```
For "Clear", write `null`: For "Clear", write `null`:
```bash ```bash
$GSD_SDK query config-set brave_search null gsd_run query config-set brave_search null
``` ```
</step> </step>
@@ -197,7 +186,7 @@ Leave / Replace / Clear, followed by a text-input prompt for the new command
string. Write via: string. Write via:
```bash ```bash
$GSD_SDK query config-set review.models.<cli> "<command string>" gsd_run query config-set review.models.<cli> "<command string>"
``` ```
After each update, return to the "Review model CLI mapping — what next?" question. After each update, return to the "Review model CLI mapping — what next?" question.
@@ -261,7 +250,7 @@ For a selected slug, prompt for the comma-separated skill list (text input).
Show the current value if any, offer Leave / Replace / Clear. Write via: Show the current value if any, offer Leave / Replace / Clear. Write via:
```bash ```bash
$GSD_SDK query config-set agent_skills.<slug> "<skill-a,skill-b,skill-c>" gsd_run query config-set agent_skills.<slug> "<skill-a,skill-b,skill-c>"
``` ```
After each update, return to the "Agent skills mapping — what next?" question. After each update, return to the "Agent skills mapping — what next?" question.

View File

@@ -12,20 +12,9 @@ Read all files referenced by the invoking prompt's execution_context before star
Ensure config exists and load current state: Ensure config exists and load current state:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query config-ensure-section
if [ -f "$GSD_TOOLS" ]; then INIT=$(gsd_run query state.load)
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query config-ensure-section
INIT=$($GSD_SDK query state.load)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
# `state.load` returns STATE frontmatter JSON from the SDK — it does not include `config_path`. Orchestrators may set `GSD_CONFIG_PATH` from init phase-op JSON; otherwise resolve the same path gsd-tools uses for flat vs active workstream (#2282). # `state.load` returns STATE frontmatter JSON from the SDK — it does not include `config_path`. Orchestrators may set `GSD_CONFIG_PATH` from init phase-op JSON; otherwise resolve the same path gsd-tools uses for flat vs active workstream (#2282).
if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then

View File

@@ -12,19 +12,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Parse arguments and load project state: Parse arguments and load project state:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -32,14 +21,14 @@ Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`,
Also load config for branching strategy: Also load config for branching strategy:
```bash ```bash
CONFIG=$($GSD_SDK query state.load) CONFIG=$(gsd_run query state.load)
``` ```
Extract: `branching_strategy`, `branch_name`. Extract: `branching_strategy`, `branch_name`.
Detect base branch for PRs and merges: Detect base branch for PRs and merges:
```bash ```bash
BASE_BRANCH=$($GSD_SDK query config-get git.base_branch 2>/dev/null || echo "") BASE_BRANCH=$(gsd_run query config-get git.base_branch 2>/dev/null || echo "")
if [ -z "$BASE_BRANCH" ] || [ "$BASE_BRANCH" = "null" ]; then if [ -z "$BASE_BRANCH" ] || [ "$BASE_BRANCH" = "null" ]; then
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||') BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')
BASE_BRANCH="${BASE_BRANCH:-main}" BASE_BRANCH="${BASE_BRANCH:-main}"
@@ -158,7 +147,7 @@ For each SUMMARY.md in the phase directory:
Read append-only project-specific PRD/PR body sections from config: Read append-only project-specific PRD/PR body sections from config:
```bash ```bash
CUSTOM_PR_SECTIONS=$($GSD_SDK query config-get ship.pr_body_sections --default '[]' 2>/dev/null || echo '[]') CUSTOM_PR_SECTIONS=$(gsd_run query config-get ship.pr_body_sections --default '[]' 2>/dev/null || echo '[]')
``` ```
`ship.pr_body_sections` is an onboarding-time extension point for teams that need extra PRD-style sections such as `User Stories & Acceptance Criteria`, `Risks & Dependencies`, `Success Metrics`, `Release Criteria`, or `Stakeholder Review & Approval`. `ship.pr_body_sections` is an onboarding-time extension point for teams that need extra PRD-style sections such as `User Stories & Acceptance Criteria`, `Risks & Dependencies`, `Success Metrics`, `Release Criteria`, or `Stakeholder Review & Approval`.
@@ -230,7 +219,7 @@ Report: "PR #{number} created: {url}"
Before prompting the user, check if an external review command is configured: Before prompting the user, check if an external review command is configured:
```bash ```bash
REVIEW_CMD=$($GSD_SDK query config-get workflow.code_review_command 2>/dev/null | jq -r '.' 2>/dev/null || echo "") REVIEW_CMD=$(gsd_run query config-get workflow.code_review_command 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
``` ```
If `REVIEW_CMD` is non-empty and not `"null"`, run the external review: If `REVIEW_CMD` is non-empty and not `"null"`, run the external review:
@@ -243,7 +232,7 @@ If `REVIEW_CMD` is non-empty and not `"null"`, run the external review:
2. **Load phase context from STATE.md:** 2. **Load phase context from STATE.md:**
```bash ```bash
STATE_STATUS=$($GSD_SDK query state.load 2>/dev/null | head -20) STATE_STATUS=$(gsd_run query state.load 2>/dev/null | head -20)
``` ```
3. **Build review prompt and pipe to command via stdin:** 3. **Build review prompt and pipe to command via stdin:**
@@ -316,13 +305,13 @@ Report the PR URL and suggest: "Review the diff at {url}/files"
Update STATE.md to reflect the shipping action: Update STATE.md to reflect the shipping action:
```bash ```bash
$GSD_SDK query state.update "Last Activity" "$(date +%Y-%m-%d)" gsd_run query state.update "Last Activity" "$(date +%Y-%m-%d)"
$GSD_SDK query state.update "Status" "Phase ${PHASE_NUMBER} shipped — PR #${PR_NUMBER}" gsd_run query state.update "Status" "Phase ${PHASE_NUMBER} shipped — PR #${PR_NUMBER}"
``` ```
If `commit_docs` is true: If `commit_docs` is true:
```bash ```bash
$GSD_SDK query commit "docs(${padded_phase}): ship phase ${PHASE_NUMBER} — PR #${PR_NUMBER}" --files .planning/STATE.md gsd_run query commit "docs(${padded_phase}): ship phase ${PHASE_NUMBER} — PR #${PR_NUMBER}" --files .planning/STATE.md
``` ```
</step> </step>

View File

@@ -37,19 +37,8 @@ Exit.
Check `commit_docs` config: Check `commit_docs` config:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true")
``` ```
</step> </step>
@@ -244,7 +233,7 @@ If this routing line already exists (append mode), leave it as-is.
Commit all artifacts (if `COMMIT_DOCS` is true): Commit all artifacts (if `COMMIT_DOCS` is true):
```bash ```bash
$GSD_SDK query commit "docs(sketch-wrap-up): package [N] sketch findings into project skill" --files .planning/sketches/WRAP-UP-SUMMARY.md gsd_run query commit "docs(sketch-wrap-up): package [N] sketch findings into project skill" --files .planning/sketches/WRAP-UP-SUMMARY.md
``` ```
</step> </step>

View File

@@ -99,19 +99,8 @@ ls -d .planning/sketches/[0-9][0-9][0-9]-* 2>/dev/null | sort | tail -1
Check `commit_docs` config: Check `commit_docs` config:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true")
``` ```
</step> </step>
@@ -308,7 +297,7 @@ Iterate until satisfied.
**h.** Commit (if `COMMIT_DOCS` is true): **h.** Commit (if `COMMIT_DOCS` is true):
```bash ```bash
$GSD_SDK query commit "docs(sketch-NNN): [winning direction] — [key visual insight]" --files .planning/sketches/NNN-descriptive-name/ .planning/sketches/MANIFEST.md gsd_run query commit "docs(sketch-NNN): [winning direction] — [key visual insight]" --files .planning/sketches/NNN-descriptive-name/ .planning/sketches/MANIFEST.md
``` ```
**i.** Report: **i.** Report:

View File

@@ -37,19 +37,8 @@ Exit.
Check `commit_docs` config: Check `commit_docs` config:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true")
``` ```
</step> </step>
@@ -258,7 +247,7 @@ Patterns and stack choices established across spike sessions. New spikes follow
Commit all artifacts (if `COMMIT_DOCS` is true): Commit all artifacts (if `COMMIT_DOCS` is true):
```bash ```bash
$GSD_SDK query commit "docs(spike-wrap-up): package [N] spike findings into project skill" --files .planning/spikes/WRAP-UP-SUMMARY.md .planning/spikes/CONVENTIONS.md gsd_run query commit "docs(spike-wrap-up): package [N] spike findings into project skill" --files .planning/spikes/WRAP-UP-SUMMARY.md .planning/spikes/CONVENTIONS.md
``` ```
</step> </step>

View File

@@ -96,19 +96,8 @@ ls -d .planning/spikes/[0-9][0-9][0-9]-* 2>/dev/null | sort | tail -1
Check `commit_docs` config: Check `commit_docs` config:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true")
``` ```
</step> </step>
@@ -346,7 +335,7 @@ tags: [tag1, tag2]
**i.** Commit (if `COMMIT_DOCS` is true): **i.** Commit (if `COMMIT_DOCS` is true):
```bash ```bash
$GSD_SDK query commit "docs(spike-NNN): [VERDICT] — [key finding]" --files .planning/spikes/NNN-descriptive-name/ .planning/spikes/MANIFEST.md gsd_run query commit "docs(spike-NNN): [VERDICT] — [key finding]" --files .planning/spikes/NNN-descriptive-name/ .planning/spikes/MANIFEST.md
``` ```
**j.** Report: **j.** Report:
@@ -400,7 +389,7 @@ Only include patterns that repeated across 2+ spikes or were explicitly chosen b
Commit (if `COMMIT_DOCS` is true): Commit (if `COMMIT_DOCS` is true):
```bash ```bash
$GSD_SDK query commit "docs(spikes): update conventions" --files .planning/spikes/CONVENTIONS.md gsd_run query commit "docs(spikes): update conventions" --files .planning/spikes/CONVENTIONS.md
``` ```
</step> </step>

View File

@@ -12,19 +12,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Gather project statistics: Gather project statistics:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" STATS=$(gsd_run query stats.json)
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
STATS=$($GSD_SDK query stats.json)
if [[ "$STATS" == @file:* ]]; then STATS=$(cat "${STATS#@file:}"); fi if [[ "$STATS" == @file:* ]]; then STATS=$(cat "${STATS#@file:}"); fi
``` ```
@@ -67,7 +56,7 @@ If no `.planning/` directory exists, inform the user to run `/gsd:new-project` f
**MVP phase summary.** Read all phases via `gsd-tools.cjs query roadmap.analyze` (Phase 1's `cmdRoadmapAnalyze` surfaces a `mode` field per phase). Count phases by mode: **MVP phase summary.** Read all phases via `gsd-tools.cjs query roadmap.analyze` (Phase 1's `cmdRoadmapAnalyze` surfaces a `mode` field per phase). Count phases by mode:
```bash ```bash
ANALYZE=$($GSD_SDK query roadmap.analyze) ANALYZE=$(gsd_run query roadmap.analyze)
if [[ "$ANALYZE" == @file:* ]]; then ANALYZE=$(cat "${ANALYZE#@file:}"); fi if [[ "$ANALYZE" == @file:* ]]; then ANALYZE=$(cat "${ANALYZE#@file:}"); fi
MVP_COUNT=$(echo "$ANALYZE" | jq '[.phases[] | select(.mode == "mvp")] | length') MVP_COUNT=$(echo "$ANALYZE" | jq '[.phases[] | select(.mode == "mvp")] | length')
TOTAL_COUNT=$(echo "$ANALYZE" | jq '.phases | length') TOTAL_COUNT=$(echo "$ANALYZE" | jq '.phases | length')

View File

@@ -28,19 +28,8 @@ ls .planning/threads/*.md 2>/dev/null
For each thread file found: For each thread file found:
- Read frontmatter `status` field via: - Read frontmatter `status` field via:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" gsd_run query frontmatter.get .planning/threads/{file} status
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
$GSD_SDK query frontmatter.get .planning/threads/{file} status
``` ```
- If frontmatter `status` field is missing, fall back to reading markdown heading `## Status: OPEN` (or IN PROGRESS / RESOLVED) from the file body - If frontmatter `status` field is missing, fall back to reading markdown heading `## Status: OPEN` (or IN PROGRESS / RESOLVED) from the file body
- Read frontmatter `updated` field for the last-updated date - Read frontmatter `updated` field for the last-updated date
@@ -79,13 +68,13 @@ When SUBCMD=close and SLUG is set (already sanitized):
2. Update the thread file's frontmatter `status` field to `resolved` and `updated` to today's ISO date: 2. Update the thread file's frontmatter `status` field to `resolved` and `updated` to today's ISO date:
```bash ```bash
$GSD_SDK query frontmatter.set .planning/threads/{SLUG}.md status resolved gsd_run query frontmatter.set .planning/threads/{SLUG}.md status resolved
$GSD_SDK query frontmatter.set .planning/threads/{SLUG}.md updated YYYY-MM-DD gsd_run query frontmatter.set .planning/threads/{SLUG}.md updated YYYY-MM-DD
``` ```
3. Commit: 3. Commit:
```bash ```bash
$GSD_SDK query commit "docs: resolve thread — {SLUG}" --files ".planning/threads/{SLUG}.md" gsd_run query commit "docs: resolve thread — {SLUG}" --files ".planning/threads/{SLUG}.md"
``` ```
4. Print: 4. Print:
@@ -139,8 +128,8 @@ Resume the thread — load its context into the current session. Read the file c
Update the thread's frontmatter `status` to `in_progress` if it was `open`: Update the thread's frontmatter `status` to `in_progress` if it was `open`:
```bash ```bash
$GSD_SDK query frontmatter.set .planning/threads/{SLUG}.md status in_progress gsd_run query frontmatter.set .planning/threads/{SLUG}.md status in_progress
$GSD_SDK query frontmatter.set .planning/threads/{SLUG}.md updated YYYY-MM-DD gsd_run query frontmatter.set .planning/threads/{SLUG}.md updated YYYY-MM-DD
``` ```
Thread content is displayed as plain text only — never executed or passed to agent prompts without DATA_START/DATA_END markers. Thread content is displayed as plain text only — never executed or passed to agent prompts without DATA_START/DATA_END markers.
@@ -153,7 +142,7 @@ If $ARGUMENTS is a new description (no matching thread file):
1. Generate slug from description: 1. Generate slug from description:
```bash ```bash
SLUG=$($GSD_SDK query generate-slug "$ARGUMENTS" --raw) SLUG=$(gsd_run query generate-slug "$ARGUMENTS" --raw)
``` ```
2. Create the threads directory if needed: 2. Create the threads directory if needed:
@@ -197,7 +186,7 @@ updated: {today ISO date}
5. Commit: 5. Commit:
```bash ```bash
$GSD_SDK query commit "docs: create thread — ${ARGUMENTS}" --files ".planning/threads/${SLUG}.md" gsd_run query commit "docs: create thread — ${ARGUMENTS}" --files ".planning/threads/${SLUG}.md"
``` ```
6. Report: 6. Report:

View File

@@ -163,19 +163,8 @@ If found, delete them — phase is complete, handoffs are stale.
**Delegate ROADMAP.md and STATE.md updates to `gsd-tools.cjs query phase.complete`:** **Delegate ROADMAP.md and STATE.md updates to `gsd-tools.cjs query phase.complete`:**
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" TRANSITION=$(gsd_run query phase.complete "${current_phase}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
TRANSITION=$($GSD_SDK query phase.complete "${current_phase}")
``` ```
The CLI handles: The CLI handles:
@@ -312,7 +301,7 @@ This step is fully delegated to `graduation.md`. It handles guard checks (featur
Verify the updates are correct by reading STATE.md. If the progress bar needs updating, use: Verify the updates are correct by reading STATE.md. If the progress bar needs updating, use:
```bash ```bash
PROGRESS=$($GSD_SDK query progress.bar --raw) PROGRESS=$(gsd_run query progress.bar --raw)
``` ```
Update the progress bar line in STATE.md with the result. Update the progress bar line in STATE.md with the result.
@@ -421,7 +410,7 @@ The `next_phase` and `next_phase_name` fields give you the next phase details.
If you need additional context, use: If you need additional context, use:
```bash ```bash
ROADMAP=$($GSD_SDK query roadmap.analyze) ROADMAP=$(gsd_run query roadmap.analyze)
``` ```
This returns all phases with goals, disk status, and completion info. This returns all phases with goals, disk status, and completion info.
@@ -440,7 +429,7 @@ In flat mode, go directly to **Route B**.
```bash ```bash
# Only check if we're in workstream mode # Only check if we're in workstream mode
if [ -n "$GSD_WORKSTREAM" ]; then if [ -n "$GSD_WORKSTREAM" ]; then
WS_LIST=$($GSD_SDK query workstream.list --raw) WS_LIST=$(gsd_run query workstream.list --raw)
fi fi
``` ```
@@ -560,7 +549,7 @@ to the next milestone — other workstreams are still working.
**Clear auto-advance chain flag** — workstream boundary is the natural stopping point: **Clear auto-advance chain flag** — workstream boundary is the natural stopping point:
```bash ```bash
$GSD_SDK query config-set workflow._auto_chain_active false gsd_run query config-set workflow._auto_chain_active false
``` ```
<if mode="yolo"> <if mode="yolo">
@@ -614,7 +603,7 @@ Do NOT auto-invoke any further slash commands.
**Clear auto-advance chain flag** — milestone boundary is the natural stopping point: **Clear auto-advance chain flag** — milestone boundary is the natural stopping point:
```bash ```bash
$GSD_SDK query config-set workflow._auto_chain_active false gsd_run query config-set workflow._auto_chain_active false
``` ```
<if mode="yolo"> <if mode="yolo">

View File

@@ -19,22 +19,11 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
## 1. Initialize ## 1. Initialize
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.plan-phase "$PHASE")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.plan-phase "$PHASE")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_UI=$($GSD_SDK query agent-skills gsd-ui-researcher) AGENT_SKILLS_UI=$(gsd_run query agent-skills gsd-ui-researcher)
AGENT_SKILLS_UI_CHECKER=$($GSD_SDK query agent-skills gsd-ui-checker) AGENT_SKILLS_UI_CHECKER=$(gsd_run query agent-skills gsd-ui-checker)
``` ```
Parse JSON for: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_context`, `has_research`, `commit_docs`. Parse JSON for: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_context`, `has_research`, `commit_docs`.
@@ -49,14 +38,14 @@ SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/nul
Resolve UI agent models: Resolve UI agent models:
```bash ```bash
UI_RESEARCHER_MODEL=$($GSD_SDK query resolve-model gsd-ui-researcher --raw) UI_RESEARCHER_MODEL=$(gsd_run query resolve-model gsd-ui-researcher --raw)
UI_CHECKER_MODEL=$($GSD_SDK query resolve-model gsd-ui-checker --raw) UI_CHECKER_MODEL=$(gsd_run query resolve-model gsd-ui-checker --raw)
``` ```
Check config: Check config:
```bash ```bash
UI_ENABLED=$($GSD_SDK query config-get workflow.ui_phase 2>/dev/null || echo "true") UI_ENABLED=$(gsd_run query config-get workflow.ui_phase 2>/dev/null || echo "true")
``` ```
**If `UI_ENABLED` is `false`:** **If `UI_ENABLED` is `false`:**
@@ -72,7 +61,7 @@ Exit workflow.
Extract phase number from $ARGUMENTS. If not provided, detect next unplanned phase. Extract phase number from $ARGUMENTS. If not provided, detect next unplanned phase.
```bash ```bash
PHASE_INFO=$($GSD_SDK query roadmap.get-phase "${PHASE}") PHASE_INFO=$(gsd_run query roadmap.get-phase "${PHASE}")
``` ```
**If `found` is false:** Error with available phases. **If `found` is false:** Error with available phases.
@@ -310,13 +299,13 @@ Dimensions: 6/6 passed
## 11. Commit (if configured) ## 11. Commit (if configured)
```bash ```bash
$GSD_SDK query commit "docs(${padded_phase}): UI design contract" --files "${PHASE_DIR}/${PADDED_PHASE}-UI-SPEC.md" gsd_run query commit "docs(${padded_phase}): UI design contract" --files "${PHASE_DIR}/${PADDED_PHASE}-UI-SPEC.md"
``` ```
## 12. Update State ## 12. Update State
```bash ```bash
$GSD_SDK query state.record-session \ gsd_run query state.record-session \
--stopped-at "Phase ${PHASE} UI-SPEC approved" \ --stopped-at "Phase ${PHASE} UI-SPEC approved" \
--resume-file "${PHASE_DIR}/${PADDED_PHASE}-UI-SPEC.md" --resume-file "${PHASE_DIR}/${PADDED_PHASE}-UI-SPEC.md"
``` ```

View File

@@ -16,27 +16,16 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
## 0. Initialize ## 0. Initialize
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_UI_REVIEWER=$($GSD_SDK query agent-skills gsd-ui-auditor) AGENT_SKILLS_UI_REVIEWER=$(gsd_run query agent-skills gsd-ui-auditor)
``` ```
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `commit_docs`. Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `commit_docs`.
```bash ```bash
UI_AUDITOR_MODEL=$($GSD_SDK query resolve-model gsd-ui-auditor --raw) UI_AUDITOR_MODEL=$(gsd_run query resolve-model gsd-ui-auditor --raw)
``` ```
Display banner: Display banner:
@@ -188,7 +177,7 @@ tools is detected at runtime.
## 5. Commit (if configured) ## 5. Commit (if configured)
```bash ```bash
$GSD_SDK query commit "docs(${padded_phase}): UI audit review" --files "${PHASE_DIR}/${PADDED_PHASE}-UI-REVIEW.md" gsd_run query commit "docs(${padded_phase}): UI audit review" --files "${PHASE_DIR}/${PADDED_PHASE}-UI-REVIEW.md"
``` ```
</process> </process>

View File

@@ -66,19 +66,8 @@ unplanned phase from the roadmap (same logic as /gsd:plan-phase).
Load GSD phase context: Load GSD phase context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.plan-phase "$PHASE")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.plan-phase "$PHASE")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```

View File

@@ -483,7 +483,6 @@ inspect).
Otherwise run `detect-custom-files`: Otherwise run `detect-custom-files`:
```bash ```bash
GSD_TOOLS="$RUNTIME_DIR/get-shit-done/bin/gsd-tools.cjs"
CUSTOM_JSON='' CUSTOM_JSON=''
if [ -f "$GSD_TOOLS" ] && [ -n "$RUNTIME_DIR" ]; then if [ -f "$GSD_TOOLS" ] && [ -n "$RUNTIME_DIR" ]; then
CUSTOM_JSON=$(node "$GSD_TOOLS" detect-custom-files --config-dir "$RUNTIME_DIR" 2>/dev/null) CUSTOM_JSON=$(node "$GSD_TOOLS" detect-custom-files --config-dir "$RUNTIME_DIR" 2>/dev/null)

View File

@@ -16,28 +16,17 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
## 0. Initialize ## 0. Initialize
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_AUDITOR=$($GSD_SDK query agent-skills gsd-nyquist-auditor) AGENT_SKILLS_AUDITOR=$(gsd_run query agent-skills gsd-nyquist-auditor)
``` ```
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`. Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
```bash ```bash
AUDITOR_MODEL=$($GSD_SDK query resolve-model gsd-nyquist-auditor --raw) AUDITOR_MODEL=$(gsd_run query resolve-model gsd-nyquist-auditor --raw)
NYQUIST_CFG=$($GSD_SDK query config-get workflow.nyquist_validation --raw) NYQUIST_CFG=$(gsd_run query config-get workflow.nyquist_validation --raw)
``` ```
If `NYQUIST_CFG` is `false`: exit with "Nyquist validation is disabled. Enable via /gsd:settings." If `NYQUIST_CFG` is `false`: exit with "Nyquist validation is disabled. Enable via /gsd:settings."
@@ -151,7 +140,7 @@ Handle return:
git add {test_files} git add {test_files}
git commit -m "test(phase-${PHASE}): add Nyquist validation tests" git commit -m "test(phase-${PHASE}): add Nyquist validation tests"
$GSD_SDK query commit "docs(phase-${PHASE}): add/update validation strategy" gsd_run query commit "docs(phase-${PHASE}): add/update validation strategy"
``` ```
## 8. Results + Routing ## 8. Results + Routing

View File

@@ -29,19 +29,8 @@ Then verify each level against the actual codebase.
Load phase operation context: Load phase operation context:
```bash ```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) _GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs" INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
``` ```
@@ -49,14 +38,14 @@ Extract from init JSON: `phase_dir`, `phase_number`, `phase_name`, `has_plans`,
Then load phase details and list plans/summaries: Then load phase details and list plans/summaries:
```bash ```bash
$GSD_SDK query roadmap.get-phase "${phase_number}" gsd_run query roadmap.get-phase "${phase_number}"
grep -E "^| ${phase_number}" .planning/REQUIREMENTS.md 2>/dev/null || true grep -E "^| ${phase_number}" .planning/REQUIREMENTS.md 2>/dev/null || true
ls "$phase_dir"/*-SUMMARY.md "$phase_dir"/*-PLAN.md 2>/dev/null || true ls "$phase_dir"/*-SUMMARY.md "$phase_dir"/*-PLAN.md 2>/dev/null || true
``` ```
Load full milestone phases for deferred-item filtering (Step 9b): Load full milestone phases for deferred-item filtering (Step 9b):
```bash ```bash
$GSD_SDK query roadmap.analyze gsd_run query roadmap.analyze
``` ```
Extract **phase goal** from ROADMAP.md (the outcome to verify, not tasks), **requirements** from REQUIREMENTS.md if it exists, and **all milestone phases** from roadmap analyze (for cross-referencing gaps against later phases). Extract **phase goal** from ROADMAP.md (the outcome to verify, not tasks), **requirements** from REQUIREMENTS.md if it exists, and **all milestone phases** from roadmap analyze (for cross-referencing gaps against later phases).
@@ -69,7 +58,7 @@ Use `gsd-tools.cjs query` verify handlers (or legacy gsd-tools) to extract must_
```bash ```bash
for plan in "$PHASE_DIR"/*-PLAN.md; do for plan in "$PHASE_DIR"/*-PLAN.md; do
MUST_HAVES=$($GSD_SDK query frontmatter.get "$plan" --field must_haves) MUST_HAVES=$(gsd_run query frontmatter.get "$plan" --field must_haves)
echo "=== $plan ===" && echo "$MUST_HAVES" echo "=== $plan ===" && echo "$MUST_HAVES"
done done
``` ```
@@ -83,7 +72,7 @@ Aggregate all must_haves across plans for phase-level verification.
If no must_haves in frontmatter (MUST_HAVES returns error or empty), check for Success Criteria: If no must_haves in frontmatter (MUST_HAVES returns error or empty), check for Success Criteria:
```bash ```bash
PHASE_DATA=$($GSD_SDK query roadmap.get-phase "${phase_number}" --raw) PHASE_DATA=$(gsd_run query roadmap.get-phase "${phase_number}" --raw)
``` ```
Parse the `success_criteria` array from the JSON output. If non-empty: Parse the `success_criteria` array from the JSON output. If non-empty:
@@ -119,7 +108,7 @@ Use `gsd-tools.cjs query verify.artifacts` (or legacy gsd-tools) for artifact ve
```bash ```bash
for plan in "$PHASE_DIR"/*-PLAN.md; do for plan in "$PHASE_DIR"/*-PLAN.md; do
ARTIFACT_RESULT=$($GSD_SDK query verify.artifacts "$plan") ARTIFACT_RESULT=$(gsd_run query verify.artifacts "$plan")
echo "=== $plan ===" && echo "$ARTIFACT_RESULT" echo "=== $plan ===" && echo "$ARTIFACT_RESULT"
done done
``` ```
@@ -162,7 +151,7 @@ Use `gsd-tools.cjs query verify.key-links` (or legacy gsd-tools) for key link ve
```bash ```bash
for plan in "$PHASE_DIR"/*-PLAN.md; do for plan in "$PHASE_DIR"/*-PLAN.md; do
LINKS_RESULT=$($GSD_SDK query verify.key-links "$plan") LINKS_RESULT=$(gsd_run query verify.key-links "$plan")
echo "=== $plan ===" && echo "$LINKS_RESULT" echo "=== $plan ===" && echo "$LINKS_RESULT"
done done
``` ```
@@ -217,7 +206,7 @@ phase.
no `<decisions>` block. no `<decisions>` block.
```bash ```bash
GATE_CFG=$($GSD_SDK query config-get workflow.context_coverage_gate 2>/dev/null || echo "true") GATE_CFG=$(gsd_run query config-get workflow.context_coverage_gate 2>/dev/null || echo "true")
if [ "$GATE_CFG" != "false" ]; then if [ "$GATE_CFG" != "false" ]; then
# Discover the phase CONTEXT.md via glob expansion rather than `ls | head` # Discover the phase CONTEXT.md via glob expansion rather than `ls | head`
# (review F17 / ShellCheck SC2012). Globs preserve filenames containing # (review F17 / ShellCheck SC2012). Globs preserve filenames containing
@@ -226,7 +215,7 @@ if [ "$GATE_CFG" != "false" ]; then
for f in "${PHASE_DIR}"/*-CONTEXT.md; do for f in "${PHASE_DIR}"/*-CONTEXT.md; do
[ -e "$f" ] && CONTEXT_PATH="$f" && break [ -e "$f" ] && CONTEXT_PATH="$f" && break
done done
DECISION_RESULT=$($GSD_SDK query check.decision-coverage-verify "${PHASE_DIR}" "${CONTEXT_PATH}") DECISION_RESULT=$(gsd_run query check.decision-coverage-verify "${PHASE_DIR}" "${CONTEXT_PATH}")
fi fi
``` ```
@@ -261,7 +250,7 @@ inspecting static artifacts.
```bash ```bash
# Resolve test command: project config > Makefile > language sniff # Resolve test command: project config > Makefile > language sniff
TEST_CMD=$($GSD_SDK query config-get workflow.test_command --default "" 2>/dev/null || true) TEST_CMD=$(gsd_run query config-get workflow.test_command --default "" 2>/dev/null || true)
if [ -z "$TEST_CMD" ]; then if [ -z "$TEST_CMD" ]; then
if [ -f "Makefile" ] && grep -q "^test:" Makefile; then if [ -f "Makefile" ] && grep -q "^test:" Makefile; then
TEST_CMD="make test" TEST_CMD="make test"

View File

@@ -30,26 +30,15 @@ No Pass/Fail buttons. No severity questions. Just: "Here's what should happen. D
If $ARGUMENTS contains a phase number, load context: If $ARGUMENTS contains a phase number, load context:
```bash ```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
GSD_WS="" GSD_WS=""
echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[^[:space:]]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[^[:space:]]+') echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[^[:space:]]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[^[:space:]]+')
PHASE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[^[:space:]]+//g' | xargs) PHASE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[^[:space:]]+//g' | xargs)
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668) INIT=$(gsd_run query init.verify-work "${PHASE_ARG}" ${GSD_WS})
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs"
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-tools >/dev/null 2>&1; then
GSD_TOOLS="$(command -v gsd-tools)"
GSD_SDK="$GSD_TOOLS"
else
echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH." >&2
echo "Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.verify-work "${PHASE_ARG}" ${GSD_WS})
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_PLANNER=$($GSD_SDK query agent-skills gsd-planner) AGENT_SKILLS_PLANNER=$(gsd_run query agent-skills gsd-planner)
AGENT_SKILLS_CHECKER=$($GSD_SDK query agent-skills gsd-plan-checker) AGENT_SKILLS_CHECKER=$(gsd_run query agent-skills gsd-plan-checker)
``` ```
Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`. Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`.
@@ -58,7 +47,7 @@ Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`,
# MVP mode detection via the centralized phase.mvp-mode resolver. # MVP mode detection via the centralized phase.mvp-mode resolver.
# verify-work has no --mvp CLI flag (mode is inherited from the planned phase), # verify-work has no --mvp CLI flag (mode is inherited from the planned phase),
# so we omit --cli-flag — the verb falls through roadmap → config → false. # so we omit --cli-flag — the verb falls through roadmap → config → false.
MVP_MODE=$($GSD_SDK query phase.mvp-mode "${phase_number}" ${GSD_WS} --pick active) MVP_MODE=$(gsd_run query phase.mvp-mode "${phase_number}" ${GSD_WS} --pick active)
``` ```
</step> </step>
@@ -116,7 +105,7 @@ Before running manual UAT, check whether this phase has a UI component and wheth
`mcp__playwright__*` or `mcp__puppeteer__*` tools are available in the current session. `mcp__playwright__*` or `mcp__puppeteer__*` tools are available in the current session.
```bash ```bash
UI_PHASE_FLAG=$($GSD_SDK query config-get workflow.ui_phase --raw 2>/dev/null || echo "true") UI_PHASE_FLAG=$(gsd_run query config-get workflow.ui_phase --raw 2>/dev/null || echo "true")
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1) UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
``` ```
@@ -170,8 +159,8 @@ When `MVP_MODE=false` (mode is null, absent, or the phase has no `**Mode:**` lin
**User-story format guard.** When `MVP_MODE=true`, also verify the phase's goal is in User Story format via the centralized validator: **User-story format guard.** When `MVP_MODE=true`, also verify the phase's goal is in User Story format via the centralized validator:
```bash ```bash
PHASE_GOAL=$($GSD_SDK query roadmap.get-phase "${phase_number}" ${GSD_WS} --pick goal) PHASE_GOAL=$(gsd_run query roadmap.get-phase "${phase_number}" ${GSD_WS} --pick goal)
USER_STORY_VALID=$($GSD_SDK query user-story.validate --story "$PHASE_GOAL" --pick valid) USER_STORY_VALID=$(gsd_run query user-story.validate --story "$PHASE_GOAL" --pick valid)
if [ "$USER_STORY_VALID" != "true" ]; then if [ "$USER_STORY_VALID" != "true" ]; then
echo "Phase ${phase_number} has '**Mode:** mvp' in ROADMAP.md but the **Goal:** is not in user-story format." echo "Phase ${phase_number} has '**Mode:** mvp' in ROADMAP.md but the **Goal:** is not in user-story format."
echo "Run /gsd mvp-phase ${phase_number} to set a user-story goal before verifying." echo "Run /gsd mvp-phase ${phase_number} to set a user-story goal before verifying."
@@ -279,7 +268,7 @@ Proceed to `present_test`.
Render the checkpoint from the structured UAT file instead of composing it freehand: Render the checkpoint from the structured UAT file instead of composing it freehand:
```bash ```bash
CHECKPOINT=$($GSD_SDK query uat.render-checkpoint --file "$uat_path" --raw) CHECKPOINT=$(gsd_run query uat.render-checkpoint --file "$uat_path" --raw)
if [[ "$CHECKPOINT" == @file:* ]]; then CHECKPOINT=$(cat "${CHECKPOINT#@file:}"); fi if [[ "$CHECKPOINT" == @file:* ]]; then CHECKPOINT=$(cat "${CHECKPOINT#@file:}"); fi
``` ```
@@ -437,7 +426,7 @@ Clear Current Test section:
Commit the UAT file: Commit the UAT file:
```bash ```bash
$GSD_SDK query commit "test({phase_num}): complete UAT - {passed} passed, {issues} issues" --files ".planning/phases/XX-name/{phase_num}-UAT.md" gsd_run query commit "test({phase_num}): complete UAT - {passed} passed, {issues} issues" --files ".planning/phases/XX-name/{phase_num}-UAT.md"
``` ```
Present summary: Present summary:
@@ -461,7 +450,7 @@ Present summary:
**If issues == 0:** **If issues == 0:**
```bash ```bash
SECURITY_CFG=$($GSD_SDK query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true") SECURITY_CFG=$(gsd_run query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true")
SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1) SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
``` ```
@@ -510,7 +499,7 @@ Run phase artifact scan to surface any open items before marking phase verified:
`audit-open` is CJS-only until registered on `gsd-tools.cjs query`: `audit-open` is CJS-only until registered on `gsd-tools.cjs query`:
```bash ```bash
$GSD_SDK query audit-open --json gsd_run query audit-open --json
``` ```
Parse the JSON output. For the CURRENT PHASE ONLY, surface: Parse the JSON output. For the CURRENT PHASE ONLY, surface:

View File

@@ -54,6 +54,7 @@
"fallow": "^2.70.0" "fallow": "^2.70.0"
}, },
"scripts": { "scripts": {
"sync:launcher": "node scripts/sync-runtime-launcher.cjs",
"check:env": "bash scripts/check-env.sh", "check:env": "bash scripts/check-env.sh",
"check:alias-drift": "node scripts/check-alias-drift.cjs", "check:alias-drift": "node scripts/check-alias-drift.cjs",
"check:integrity": "./scripts/check-npm-integrity.sh", "check:integrity": "./scripts/check-npm-integrity.sh",

View File

@@ -0,0 +1,402 @@
'use strict';
/**
* sync-runtime-launcher.cjs
*
* Idempotent transform: for every get-shit-done/workflows/*.md (and subdirs),
* rewrite all bash/sh/shell fenced blocks to:
* 1. Strip ALL old resolver forms from every bash block (GSD_TOOLS=,
* GSD_SDK=, the if/elif/else/fi resolver, _GSD_SHIM_NAME=, and any
* previously-inserted gsd_run preamble).
* 2. Replace $GSD_SDK tokens with gsd_run (idempotent).
* 3. Insert the canonical preamble at the TOP of ONLY the FIRST bash block
* (document order) that contains a gsd_run call. All other bash blocks
* keep their gsd_run calls with NO preamble. (Define once per file,
* use across blocks — original footprint.)
*
* Run: node scripts/sync-runtime-launcher.cjs
*/
const fs = require('node:fs');
const path = require('node:path');
const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows');
const SNIPPET_FILE = path.join(WORKFLOWS_DIR, '_runtime-launcher.snippet.sh');
// Read canonical preamble (full content of snippet file)
function loadPreamble() {
const raw = fs.readFileSync(SNIPPET_FILE, 'utf8');
const lines = raw.split('\n');
// Strip trailing empty line (trailing newline produces an empty last element)
const content = lines[lines.length - 1] === '' ? lines.slice(0, -1) : lines;
if (content.length < 1) {
throw new Error(`_runtime-launcher.snippet.sh is empty`);
}
return content;
}
/**
* Collect all .md files recursively.
*/
function collectFiles(dir) {
const results = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
results.push(...collectFiles(full));
} else if (entry.isFile() && entry.name.endsWith('.md')) {
results.push(full);
}
}
return results;
}
/**
* Given the lines of a bash block (without fence markers), strip all resolver
* boilerplate (old and new preamble forms) and replace $GSD_SDK tokens.
* Does NOT insert preamble — that is done by the file-level transform.
*
* Returns the stripped lines array.
*/
function stripAndReplace(lines, preamble) {
let result = lines.slice();
// Step 1: Remove ALL resolver boilerplate lines (including existing preamble).
result = removeResolverLines(result, preamble);
// Step 2: Replace $GSD_SDK tokens with gsd_run
result = result.map((line) => replaceGsdSdk(line));
return result;
}
/**
* Remove resolver boilerplate lines from a block.
*
* Patterns to remove:
* A) Multi-line if block:
* GSD_TOOLS="...gsd-tools.cjs" (or ...${_GSD_SHIM_NAME})
* if [ -f "$GSD_TOOLS" ]; then
* ...
* fi
*
* B) One-liner form:
* GSD_TOOLS=...; if [ -f "$GSD_TOOLS" ]; then GSD_SDK=...; elif ...; else ...; fi
*
* C) _GSD_SHIM_NAME= line
*
* D) Bare GSD_SDK= line
*
* E) The canonical preamble comment line (so we strip old inserted preambles too)
*
* F) The gsd_run() function definitions that are part of the preamble
* (so previously-inserted preambles are stripped and will be re-inserted
* exactly once at the right location)
*/
function removeResolverLines(lines, preamble) {
const result = [];
let i = 0;
// Build a set of preamble lines for fast lookup (to strip previously-inserted preambles)
const preambleLineSet = new Set(preamble);
while (i < lines.length) {
const line = lines[i];
const trimmed = line.trim();
// B) One-liner form: GSD_TOOLS=... ; if ... GSD_SDK=... fi
if (
/^GSD_TOOLS=.*gsd-tools\.cjs.*;\s*if\s+\[/.test(trimmed) ||
/^GSD_TOOLS=.*\/\$\{_GSD_SHIM_NAME\}.*;\s*if\s+\[/.test(trimmed)
) {
// Skip the whole one-liner, and any adjacent SDK comment above it
const lastIdx = result.length - 1;
if (lastIdx >= 0 && isSdkComment(result[lastIdx])) {
result.pop();
}
i++;
continue;
}
// C) _GSD_SHIM_NAME= line
if (/^_GSD_SHIM_NAME=/.test(trimmed)) {
// Check if there's a canonical preamble comment just before it
const lastIdx = result.length - 1;
if (lastIdx >= 0 && isCanonicalPreambleComment(result[lastIdx])) {
result.pop();
} else if (lastIdx >= 0 && isSdkComment(result[lastIdx])) {
result.pop();
}
i++;
continue;
}
// A) Multi-line form: starts with GSD_TOOLS=...gsd-tools.cjs or GSD_TOOLS=...${_GSD_SHIM_NAME}
if (
/^GSD_TOOLS=.*gsd-tools\.cjs"$/.test(trimmed) ||
/^GSD_TOOLS=.*\/\$\{_GSD_SHIM_NAME\}"$/.test(trimmed)
) {
// Check if there's a canonical preamble comment just before it (preamble already installed)
// or an old SDK comment — either way, remove the whole block
const lastIdx = result.length - 1;
if (lastIdx >= 0 && (isCanonicalPreambleComment(result[lastIdx]) || isSdkComment(result[lastIdx]))) {
result.pop();
}
// Consume the if block that follows (if it exists)
const ahead = lines[i + 1] ? lines[i + 1].trim() : '';
if (/^if\s+\[\s+-f\s+"\$GSD_TOOLS"\s*\]/.test(ahead)) {
// Consume until matching `fi`
i += 2; // skip GSD_TOOLS= and `if [...]`
let depth = 1;
while (i < lines.length && depth > 0) {
const t = lines[i].trim();
if (/^if\s+/.test(t)) depth++;
if (/^fi(\s|$)/.test(t)) {
depth--;
if (depth === 0) {
i++;
break;
}
}
i++;
}
// After fi, skip a blank line if present
if (i < lines.length && lines[i].trim() === '') {
i++;
}
continue;
} else {
// Just skip the GSD_TOOLS= line
i++;
continue;
}
}
// D) Bare GSD_SDK= line (not inside an if block, defensive)
if (/^GSD_SDK=/.test(trimmed) && !/gsd_run/.test(trimmed)) {
i++;
continue;
}
// E+F) Strip lines that are part of the canonical preamble (to remove previously-inserted preambles).
// We match the canonical preamble comment, _GSD_SHIM_NAME (handled above at C),
// and gsd_run() function definition lines.
if (isCanonicalPreambleComment(trimmed.startsWith('#') ? line : '')) {
// Start of a previously-inserted canonical preamble — skip preamble lines
// by consuming lines that match the preamble array in order.
// Check how many consecutive lines match the preamble.
let matchLen = 0;
for (let p = 0; p < preamble.length; p++) {
if (lines[i + p] === preamble[p]) {
matchLen++;
} else {
break;
}
}
if (matchLen === preamble.length) {
// Exact preamble match — skip all preamble lines
i += matchLen;
// Skip trailing blank line if present
if (i < lines.length && lines[i].trim() === '') {
i++;
}
continue;
}
// Partial match or just the comment — still remove the comment line
i++;
continue;
}
result.push(line);
i++;
}
return result;
}
/**
* Returns true if the line looks like an old SDK resolver comment (to be removed).
* Must NOT match the new canonical preamble comment.
*/
function isSdkComment(line) {
// The new canonical preamble comment starts with "# Runtime launcher:" — preserve it.
if (isCanonicalPreambleComment(line)) return false;
return /^\s*#\s*SDK resolution/.test(line) || /^\s*#.*prefer local.*gsd-tools/.test(line);
}
/**
* Returns true if the line is the new canonical preamble comment.
* This identifies the start of the new multi-line canonical preamble.
*/
function isCanonicalPreambleComment(line) {
return /^\s*#\s*Runtime launcher:.*prefer local gsd-tools\.cjs.*installed gsd-tools on PATH/.test(line);
}
/**
* Replace all $GSD_SDK tokens with gsd_run in a line.
* Handles: $GSD_SDK, ${GSD_SDK} forms.
*/
function replaceGsdSdk(line) {
return line.replace(/\$\{?GSD_SDK\}?(?=\s|$|;|"|'|\))/g, 'gsd_run');
}
/**
* Ensure the canonical preamble appears at the top of the block.
* Rules:
* - Skip leading blank lines
* - Check if preamble lines 0..N-1 already match at the scan position
* - If not, insert preamble at the top (after leading blanks)
* - Idempotent: if preamble already present, do nothing
*/
function insertPreamble(lines, preamble) {
// Find insertion point: skip blanks only
let scanIdx = 0;
while (scanIdx < lines.length && lines[scanIdx].trim() === '') scanIdx++;
// Check if preamble already present at scanIdx
let alreadyPresent = preamble.length > 0;
for (let p = 0; p < preamble.length; p++) {
if (lines[scanIdx + p] !== preamble[p]) {
alreadyPresent = false;
break;
}
}
if (alreadyPresent) return lines; // idempotent
// Insert preamble at scanIdx (after leading blanks)
const insertAt = scanIdx;
const before = lines.slice(0, insertAt);
const after = lines.slice(insertAt);
return [...before, ...preamble, ...after];
}
/**
* Transform a single markdown file's content.
* Returns new content string, or null if no changes needed.
*
* Strategy:
* 1. Parse all shell blocks, strip resolver boilerplate from each.
* 2. Find the FIRST block (document order) that has a gsd_run call.
* 3. Insert preamble into that block only.
* 4. Reconstruct the file.
*
* Handles both column-0 fences (```bash) and indented fences ( ```bash).
*/
function transformFile(content, preamble) {
const allLines = content.split('\n');
// --- Pass 1: identify all shell blocks and their positions ---
// Each entry: { openIdx, closeIdx, blockLines, isShell }
// We'll reconstruct by replacing block lines in-place.
const shellBlockRanges = []; // { openLineIdx, contentStart, contentEnd, closingLineIdx }
let i = 0;
while (i < allLines.length) {
const line = allLines[i];
const fenceOpen = line.match(/^(\s*)```(\w+)?\s*$/);
if (!fenceOpen) { i++; continue; }
const indent = fenceOpen[1];
const lang = (fenceOpen[2] || '').toLowerCase();
const isShellBlock = ['bash', 'sh', 'shell', 'zsh', ''].includes(lang);
const closingPattern = new RegExp('^' + escapeRegExp(indent) + '```\\s*$');
const openLineIdx = i;
i++;
const contentStart = i;
while (i < allLines.length && !closingPattern.test(allLines[i])) {
i++;
}
const contentEnd = i; // exclusive
const closingLineIdx = i < allLines.length ? i : -1;
if (i < allLines.length) i++;
if (isShellBlock) {
shellBlockRanges.push({ openLineIdx, contentStart, contentEnd, closingLineIdx });
}
}
if (shellBlockRanges.length === 0) return null;
// --- Pass 2: transform each block ---
// Build a new lines array by splicing in transformed block contents.
// Process ranges in reverse order so indices stay valid.
const outputLines = allLines.slice();
let changed = false;
let firstGsdRunBlockIdx = -1; // index into shellBlockRanges of the first gsd_run block (after strip)
// First: strip + replace in all blocks, find first gsd_run block
const strippedBlocks = shellBlockRanges.map((range) => {
const blockLines = outputLines.slice(range.contentStart, range.contentEnd);
const stripped = stripAndReplace(blockLines, preamble);
return stripped;
});
// Find the first block that has a gsd_run call (after stripping)
for (let bi = 0; bi < strippedBlocks.length; bi++) {
if (strippedBlocks[bi].some((l) => /\bgsd_run\b/.test(l))) {
firstGsdRunBlockIdx = bi;
break;
}
}
// Insert preamble into the first gsd_run block only
const finalBlocks = strippedBlocks.map((stripped, bi) => {
if (bi === firstGsdRunBlockIdx) {
return insertPreamble(stripped, preamble);
}
return stripped;
});
// --- Pass 3: splice back into output (reverse order to preserve indices) ---
for (let bi = shellBlockRanges.length - 1; bi >= 0; bi--) {
const range = shellBlockRanges[bi];
const originalBlock = allLines.slice(range.contentStart, range.contentEnd);
const newBlock = finalBlocks[bi];
if (originalBlock.join('\n') !== newBlock.join('\n')) {
changed = true;
}
// Replace contentStart..contentEnd with newBlock
outputLines.splice(range.contentStart, range.contentEnd - range.contentStart, ...newBlock);
}
if (!changed) return null;
return outputLines.join('\n');
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Main
function main() {
const preamble = loadPreamble();
const files = collectFiles(WORKFLOWS_DIR);
let transformedCount = 0;
let unchangedCount = 0;
for (const f of files) {
const content = fs.readFileSync(f, 'utf8');
const result = transformFile(content, preamble);
if (result !== null) {
fs.writeFileSync(f, result, 'utf8');
transformedCount++;
console.log(`transformed: ${path.relative(WORKFLOWS_DIR, f)}`);
} else {
unchangedCount++;
}
}
console.log(`\nDone. ${transformedCount} files transformed, ${unchangedCount} unchanged.`);
}
main();

View File

@@ -38,7 +38,7 @@ function parseExecutePhaseContract(filePath) {
delegatesToCleanupWave: lines.some(l => l.includes('worktree.cleanup-wave')), delegatesToCleanupWave: lines.some(l => l.includes('worktree.cleanup-wave')),
// Does the cleanup-wave invocation use || exit 1 (fail-closed)? // Does the cleanup-wave invocation use || exit 1 (fail-closed)?
cleanupWaveFailClosed: lines.some( cleanupWaveFailClosed: lines.some(
l => /\$GSD_SDK query worktree\.cleanup-wave.*\|\| exit 1/.test(l), l => /gsd_run query worktree\.cleanup-wave.*\|\| exit 1/.test(l),
), ),
// Does the workflow export/reference WAVE_WORKTREE_MANIFEST for the SDK? // Does the workflow export/reference WAVE_WORKTREE_MANIFEST for the SDK?
passesWaveManifest: lines.some(l => l.includes('WAVE_WORKTREE_MANIFEST')), passesWaveManifest: lines.some(l => l.includes('WAVE_WORKTREE_MANIFEST')),
@@ -54,7 +54,7 @@ describe('execute-phase.md — post-merge deletion audit (#2384)', () => {
// The workflow delegates to the SDK rather than duplicating the check inline. // The workflow delegates to the SDK rather than duplicating the check inline.
assert.ok( assert.ok(
contract.delegatesToCleanupWave, contract.delegatesToCleanupWave,
'execute-phase.md must delegate to $GSD_SDK query worktree.cleanup-wave (#2384/#3797)', 'execute-phase.md must delegate to gsd_run query worktree.cleanup-wave (#2384/#3797)',
); );
}); });

View File

@@ -90,18 +90,18 @@ describe('bug #2661: execute-plan.md update_roadmap gating', () => {
test('update_roadmap step exists and invokes roadmap.update-plan-progress', () => { test('update_roadmap step exists and invokes roadmap.update-plan-progress', () => {
assert.ok(stepMatch, 'update_roadmap step must exist'); assert.ok(stepMatch, 'update_roadmap step must exist');
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form // After #3797 architectural fix, callsites use gsd_run
assert.ok( assert.ok(
/(?:\$GSD_SDK|gsd-sdk) query roadmap\.update-plan-progress/.test(step), /gsd_run query roadmap\.update-plan-progress/.test(step),
'update_roadmap must still invoke roadmap.update-plan-progress' 'update_roadmap must still invoke roadmap.update-plan-progress'
); );
}); });
test('use_worktrees: false mode — sync call is gated to fire (the #2661 reproducer)', () => { test('use_worktrees: false mode — sync call is gated to fire (the #2661 reproducer)', () => {
// The non-worktree branch must contain the sync call. // The non-worktree branch must contain the sync call.
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form // After #3797 architectural fix, callsites use gsd_run
assert.ok( assert.ok(
/IS_WORKTREE.*!=.*"true"[\s\S]*?(?:\$GSD_SDK|gsd-sdk) query roadmap\.update-plan-progress/.test(step), /IS_WORKTREE.*!=.*"true"[\s\S]*?gsd_run query roadmap\.update-plan-progress/.test(step),
'sync call must execute on the IS_WORKTREE != "true" branch (use_worktrees: false)' 'sync call must execute on the IS_WORKTREE != "true" branch (use_worktrees: false)'
); );
}); });
@@ -119,9 +119,9 @@ describe('bug #2661: execute-plan.md update_roadmap gating', () => {
'bash block must include the IS_WORKTREE worktree-detection check' 'bash block must include the IS_WORKTREE worktree-detection check'
); );
// Sync call must appear after the guard check, not before. // Sync call must appear after the guard check, not before.
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form // After #3797 architectural fix, callsites use gsd_run
const guardIdx = bash.search(/if \[ "\$IS_WORKTREE" != "true" \]/); const guardIdx = bash.search(/if \[ "\$IS_WORKTREE" != "true" \]/);
const callIdx = bash.search(/(?:\$GSD_SDK|gsd-sdk) query roadmap\.update-plan-progress/); const callIdx = bash.search(/gsd_run query roadmap\.update-plan-progress/);
assert.ok(guardIdx >= 0, 'guard must be present'); assert.ok(guardIdx >= 0, 'guard must be present');
assert.ok(callIdx > guardIdx, assert.ok(callIdx > guardIdx,
'sync call must appear inside the use_worktrees: false guard, not before/outside it'); 'sync call must appear inside the use_worktrees: false guard, not before/outside it');

View File

@@ -41,7 +41,7 @@ function parseWorkflowContract(filePath) {
delegatesToCleanupWave: lines.some(l => l.includes('worktree.cleanup-wave')), delegatesToCleanupWave: lines.some(l => l.includes('worktree.cleanup-wave')),
// Does the cleanup-wave invocation use || exit 1 (fail-closed)? // Does the cleanup-wave invocation use || exit 1 (fail-closed)?
cleanupWaveFailClosed: lines.some( cleanupWaveFailClosed: lines.some(
l => /\$GSD_SDK query worktree\.cleanup-wave.*\|\| exit 1/.test(l), l => /gsd_run query worktree\.cleanup-wave.*\|\| exit 1/.test(l),
), ),
// Does the workflow still contain the broken ls-files --exclude-standard rescue form? // Does the workflow still contain the broken ls-files --exclude-standard rescue form?
hasBrokenLsFilesForm: lines.some( hasBrokenLsFilesForm: lines.some(

View File

@@ -26,8 +26,8 @@ function parseWorkflowSteps(content) {
const body = match[2]; const body = match[2];
return { return {
name: match[1], name: match[1],
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form // After #3797 architectural fix, callsites use gsd_run
readsRuntimeConfig: body.includes('RUNTIME=$($GSD_SDK query config-get runtime --default claude') || body.includes('RUNTIME=$(gsd-sdk query config-get runtime --default claude'), readsRuntimeConfig: body.includes('RUNTIME=$(gsd_run query config-get runtime --default claude'),
codexWorktreeGuard: body.includes('Codex execute-phase worktree isolation is unsupported'), codexWorktreeGuard: body.includes('Codex execute-phase worktree isolation is unsupported'),
worktreeDispatchGuidance: body.includes('isolation="worktree"'), worktreeDispatchGuidance: body.includes('isolation="worktree"'),
}; };

View File

@@ -30,20 +30,20 @@ describe('bug #3381: verify-work forwards workstream context', () => {
/PHASE_ARG=\$\(echo "\$ARGUMENTS" \| sed -E 's\/--ws\[\[:space:\]\]\+\[\^\[:space:\]\]\+\/\/g' \| xargs\)/, /PHASE_ARG=\$\(echo "\$ARGUMENTS" \| sed -E 's\/--ws\[\[:space:\]\]\+\[\^\[:space:\]\]\+\/\/g' \| xargs\)/,
'verify-work must derive PHASE_ARG after removing --ws', 'verify-work must derive PHASE_ARG after removing --ws',
); );
// After #3797 architectural fix, callsites use $GSD_SDK — accept either bare or $GSD_SDK form // After #3797 architectural fix, callsites use gsd_run
assert.match( assert.match(
workflow, workflow,
/(?:\$GSD_SDK|gsd-sdk) query init\.verify-work "\$\{PHASE_ARG\}" \$\{GSD_WS\}/, /gsd_run query init\.verify-work "\$\{PHASE_ARG\}" \$\{GSD_WS\}/,
'init.verify-work must receive GSD_WS so phase_dir resolves in workstreams', 'init.verify-work must receive GSD_WS so phase_dir resolves in workstreams',
); );
assert.match( assert.match(
workflow, workflow,
/(?:\$GSD_SDK|gsd-sdk) query phase\.mvp-mode "\$\{phase_number\}" \$\{GSD_WS\} --pick active/, /gsd_run query phase\.mvp-mode "\$\{phase_number\}" \$\{GSD_WS\} --pick active/,
'phase.mvp-mode must receive GSD_WS so roadmap mode is workstream-scoped', 'phase.mvp-mode must receive GSD_WS so roadmap mode is workstream-scoped',
); );
assert.match( assert.match(
workflow, workflow,
/(?:\$GSD_SDK|gsd-sdk) query roadmap\.get-phase "\$\{phase_number\}" \$\{GSD_WS\} --pick goal/, /gsd_run query roadmap\.get-phase "\$\{phase_number\}" \$\{GSD_WS\} --pick goal/,
'roadmap.get-phase must receive GSD_WS so goals are workstream-scoped', 'roadmap.get-phase must receive GSD_WS so goals are workstream-scoped',
); );
}); });

View File

@@ -31,12 +31,12 @@ describe('bug #3521 — quick.md post-merge cleanup CWD safety (via SDK delegati
test('quick.md cleanup delegates CWD-safe worktree cleanup to SDK (worktree.cleanup-wave)', () => { test('quick.md cleanup delegates CWD-safe worktree cleanup to SDK (worktree.cleanup-wave)', () => {
const content = readQuickMd(); const content = readQuickMd();
// After #3797: quick.md delegates to $GSD_SDK query worktree.cleanup-wave // After #3797: quick.md delegates to gsd_run query worktree.cleanup-wave
// which handles CWD pinning, STATE.md backup, deletion guards, and branch // which handles CWD pinning, STATE.md backup, deletion guards, and branch
// cleanup internally. The manual shell loop has been removed. // cleanup internally. The manual shell loop has been removed.
assert.ok( assert.ok(
content.includes('worktree.cleanup-wave'), content.includes('worktree.cleanup-wave'),
'quick.md must delegate cleanup to $GSD_SDK query worktree.cleanup-wave (#3797)', 'quick.md must delegate cleanup to gsd_run query worktree.cleanup-wave (#3797)',
); );
}); });
@@ -44,11 +44,11 @@ describe('bug #3521 — quick.md post-merge cleanup CWD safety (via SDK delegati
const content = readQuickMd(); const content = readQuickMd();
// The || exit 1 enforces fail-closed: SDK safety refusals (e.g. branch // The || exit 1 enforces fail-closed: SDK safety refusals (e.g. branch
// drift detection from #3174) surface immediately rather than being swallowed. // drift detection from #3174) surface immediately rather than being swallowed.
// This is the equivalent of the pre-#3797 `gsd-sdk query ... || exit 1` in the // This is the equivalent of the pre-#3797 `gsd_run query ... || exit 1` in the
// `if command -v gsd-sdk` branch. // `if command -v gsd-sdk` branch.
assert.match( assert.match(
content, content,
/\$GSD_SDK query worktree\.cleanup-wave.*\|\| exit 1/, /gsd_run query worktree\.cleanup-wave.*\|\| exit 1/,
'quick.md cleanup-wave must use || exit 1 — fail-closed for safety refusals (#3521/#3797)', 'quick.md cleanup-wave must use || exit 1 — fail-closed for safety refusals (#3521/#3797)',
); );
}); });

View File

@@ -16,14 +16,63 @@ const { execFileSync } = require('node:child_process');
const WORKFLOW_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'next.md'); const WORKFLOW_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'next.md');
/**
* Extract the canonical runtime resolver snippet from next.md.
*
* Supports two forms:
* - One-line form (canonical): the entire launcher is a single line starting with
* `_GSD_SHIM_NAME="gsd-tools.cjs";` — return that line directly.
* - Multi-line form (legacy): starts with a `# Runtime launcher:` comment or a
* `_GSD_SHIM_NAME=` line followed by separate GSD_TOOLS= and if/elif/else/fi
* lines — scan to the closing `fi`.
*/
function extractResolverSnippet() { function extractResolverSnippet() {
const content = fs.readFileSync(WORKFLOW_PATH, 'utf8'); const content = fs.readFileSync(WORKFLOW_PATH, 'utf8');
const lines = content.split(/\r?\n/); const lines = content.split(/\r?\n/);
const start = lines.findIndex((line) => line.includes('SDK resolution: prefer local gsd-tools.cjs'));
assert.notEqual(start, -1, 'next.md must contain the SDK resolution snippet');
const end = lines.findIndex((line, index) => index > start && line === 'fi'); // Find the canonical preamble — prefer _GSD_SHIM_NAME= line (handles both forms)
assert.notEqual(end, -1, 'SDK resolution snippet must end with fi'); let start = lines.findIndex((line) => /^_GSD_SHIM_NAME=/.test(line.trim()));
if (start === -1) {
// Fallback: canonical preamble comment (multi-line legacy form)
start = lines.findIndex((line) =>
/^\s*#\s*Runtime launcher:.*prefer local gsd-tools\.cjs.*installed gsd-tools on PATH/.test(line)
);
}
if (start === -1) {
// Last fallback: GSD_TOOLS= with RUNTIME_DIR
start = lines.findIndex((line) => line.includes('GSD_TOOLS="${RUNTIME_DIR:-'));
}
assert.notEqual(
start,
-1,
'next.md must contain the canonical runtime preamble ' +
'(_GSD_SHIM_NAME= line, # Runtime launcher: comment, or GSD_TOOLS= line with RUNTIME_DIR)'
);
// One-line form: the entire launcher (including `if` and `fi`) is on a single line.
// Detect by checking whether the start line contains a semicolon-separated `if` and `fi`.
const startLine = lines[start].trim();
if (/^_GSD_SHIM_NAME=.*;\s*if\s+\[.*\bfi$/.test(startLine)) {
// Single-line canonical launcher — return it as-is
return startLine;
}
// Multi-line form: scan forward from start to the closing `fi`, tracking if-depth
let depth = 0;
let end = -1;
for (let i = start; i < lines.length; i++) {
const t = lines[i].trim();
if (/^if\s+/.test(t)) depth++;
if (/^fi(\s|$)/.test(t)) {
depth--;
if (depth === 0) {
end = i;
break;
}
}
}
assert.notEqual(end, -1, 'runtime preamble must end with a closing `fi`');
return lines.slice(start, end + 1).join('\n'); return lines.slice(start, end + 1).join('\n');
} }
@@ -36,7 +85,7 @@ function runResolver({ cwd, runtimeDir, pathDir }) {
'set -e', 'set -e',
extractResolverSnippet(), extractResolverSnippet(),
'printf "GSD_TOOLS=%s\\n" "$GSD_TOOLS"', 'printf "GSD_TOOLS=%s\\n" "$GSD_TOOLS"',
'$GSD_SDK query state.json', 'gsd_run query state.json',
].join('\n'); ].join('\n');
return execFileSync('bash', ['-c', script], { return execFileSync('bash', ['-c', script], {
@@ -57,18 +106,28 @@ function writeExecutable(file, content) {
describe('bug-3668: workflow SDK resolver supports installed user projects', () => { describe('bug-3668: workflow SDK resolver supports installed user projects', () => {
test('falls back to installed gsd-tools when project-local runtime copy is absent', () => { test('falls back to installed gsd-tools when project-local runtime copy is absent', () => {
// Bug #3668: when a user project has no local get-shit-done/bin/gsd-tools.cjs,
// the elif branch must resolve to the gsd-tools binary on PATH.
// RUNTIME_DIR points to a dir that has no gsd-tools.cjs.
const tmp = makeTempDir(); const tmp = makeTempDir();
const project = path.join(tmp, 'user-project'); const project = path.join(tmp, 'user-project');
const bin = path.join(tmp, 'bin'); const runtimeNoLocal = path.join(tmp, 'runtime-no-local');
const pathBin = path.join(tmp, 'bin');
fs.mkdirSync(project, { recursive: true }); fs.mkdirSync(project, { recursive: true });
writeExecutable(path.join(bin, 'gsd-tools'), '#!/bin/sh\nprintf "installed:%s %s\\n" "$1" "$2"\n'); fs.mkdirSync(runtimeNoLocal, { recursive: true });
const output = runResolver({ cwd: project, pathDir: bin }); // Place gsd-tools stub on PATH (installed binary)
writeExecutable(
path.join(pathBin, 'gsd-tools'),
'#!/bin/sh\nprintf "installed:%s %s\\n" "$1" "$2"\n',
);
// Normalize separators so the assertion works on Windows (Git bash emits POSIX paths) // NO get-shit-done/bin/gsd-tools.cjs in runtimeNoLocal
const norm = output.replace(/\\/g, '/'); const output = runResolver({ cwd: project, runtimeDir: runtimeNoLocal, pathDir: pathBin });
// subtest 1: the resolved bin is the PATH-fallback gsd-tools (suffix /bin/gsd-tools, no .cjs)
assert.match(norm, /GSD_TOOLS=\S*\/bin\/gsd-tools(?:\s|$)/m); // GSD_TOOLS must have been reassigned to the PATH binary (not the missing .cjs)
assert.match(output, /GSD_TOOLS=.+gsd-tools(?:\s|$)/m);
// The PATH stub must have been invoked
assert.match(output, /installed:query state\.json/); assert.match(output, /installed:query state\.json/);
}); });
@@ -78,7 +137,7 @@ describe('bug-3668: workflow SDK resolver supports installed user projects', ()
const runtime = path.join(tmp, 'runtime'); const runtime = path.join(tmp, 'runtime');
const pathBin = path.join(tmp, 'bin'); const pathBin = path.join(tmp, 'bin');
fs.mkdirSync(project, { recursive: true }); fs.mkdirSync(project, { recursive: true });
writeExecutable(path.join(pathBin, 'gsd-tools'), '#!/bin/sh\nprintf "installed:%s %s\\n" "$1" "$2"\n'); writeExecutable(path.join(pathBin, 'gsd-tools'), '#!/bin/sh\nprintf "path-installed:%s %s\\n" "$1" "$2"\n');
writeExecutable( writeExecutable(
path.join(runtime, 'get-shit-done', 'bin', 'gsd-tools.cjs'), path.join(runtime, 'get-shit-done', 'bin', 'gsd-tools.cjs'),
'#!/usr/bin/env node\nconsole.log(`runtime:${process.argv[2]} ${process.argv[3]}`);\n', '#!/usr/bin/env node\nconsole.log(`runtime:${process.argv[2]} ${process.argv[3]}`);\n',
@@ -88,9 +147,10 @@ describe('bug-3668: workflow SDK resolver supports installed user projects', ()
// Normalize separators so the assertion works on Windows (Git bash emits POSIX paths) // Normalize separators so the assertion works on Windows (Git bash emits POSIX paths)
const norm = output.replace(/\\/g, '/'); const norm = output.replace(/\\/g, '/');
// subtest 2: the resolved bin is the RUNTIME_DIR local runtime (suffix /get-shit-done/bin/gsd-tools.cjs) // The resolved bin is the RUNTIME_DIR local runtime (suffix /get-shit-done/bin/gsd-tools.cjs)
assert.match(norm, /GSD_TOOLS=\S*\/get-shit-done\/bin\/gsd-tools\.cjs(?:\s|$)/m); // Use .+ instead of \S* to handle paths with spaces (e.g. /Volumes/Mini Me/...)
assert.match(norm, /GSD_TOOLS=.+\/get-shit-done\/bin\/gsd-tools\.cjs(?:\s|$)/m);
assert.match(output, /runtime:query state\.json/); assert.match(output, /runtime:query state\.json/);
assert.doesNotMatch(output, /installed:query state\.json/); assert.doesNotMatch(output, /path-installed:query state\.json/);
}); });
}); });

View File

@@ -44,8 +44,8 @@ test('new-milestone.md: todo linking is best-effort and leaves unmatched todos u
}); });
test('new-milestone.md: step 10.5 commits tagged todos', () => { test('new-milestone.md: step 10.5 commits tagged todos', () => {
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form // After #3797 architectural fix, callsites use gsd_run
assert.ok(NEW_MILESTONE.includes('$GSD_SDK query commit') || NEW_MILESTONE.includes('gsd-sdk query commit'), 'should commit tagged todos'); assert.ok(NEW_MILESTONE.includes('gsd_run query commit'), 'should commit tagged todos');
assert.ok(NEW_MILESTONE.includes('resolves_phase after milestone'), 'commit message should mention resolves_phase'); assert.ok(NEW_MILESTONE.includes('resolves_phase after milestone'), 'commit message should mention resolves_phase');
}); });

View File

@@ -197,11 +197,11 @@ describe('gsd-health --context flag is wired into command + workflow', () => {
const stepMatch = raw.match(/<step name="context_check">([\s\S]*?)<\/step>/); const stepMatch = raw.match(/<step name="context_check">([\s\S]*?)<\/step>/);
assert.ok(stepMatch, 'context_check step must be a closed <step>...</step> block'); assert.ok(stepMatch, 'context_check step must be a closed <step>...</step> block');
const stepBody = stepMatch[1]; const stepBody = stepMatch[1];
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form // After #3797 architectural fix, callsites use gsd_run
assert.match( assert.match(
stepBody, stepBody,
/(?:\$GSD_SDK|gsd-sdk)\s+query\s+validate\.context/, /gsd_run\s+query\s+validate\.context/,
'context_check must call `gsd-sdk query validate.context`', 'context_check must call `gsd_run query validate.context`',
); );
assert.match(stepBody, /--tokens-used/, 'context_check must pass --tokens-used'); assert.match(stepBody, /--tokens-used/, 'context_check must pass --tokens-used');
assert.match(stepBody, /--context-window/, 'context_check must pass --context-window'); assert.match(stepBody, /--context-window/, 'context_check must pass --context-window');

View File

@@ -0,0 +1,385 @@
'use strict';
/**
* Parity test for bug #373: space-safe gsd_run launcher
*
* Asserts:
* (A) No retired GSD_SDK token remains in any workflow .md file.
* (B) Each workflow .md that uses gsd_run contains EXACTLY ONE canonical preamble
* (byte-equal to _runtime-launcher.snippet.sh), and it appears before the first
* gsd_run call. NOT every bash block — exactly one per file (define once, use
* across blocks — original footprint).
* (C) Space-safe behavioral: a RUNTIME_DIR path with spaces in it resolves
* and calls gsd-tools.cjs correctly (no word-split, no {}).
* (D) Loud guard behavioral: missing gsd-tools.cjs exits non-zero and emits
* "not found" to stderr.
* (E) PATH fallback behavioral: when no local gsd-tools.cjs, the elif branch
* resolves to the gsd-tools binary on PATH (#3668).
* (F) Regression locks: the snippet file contains no /gsd-tools substring; and
* no line in workflows/do.md matches /\/gsd[:-][a-z]/ (dispatcher-parity
* scanner must not read the preamble as a slash-command stub).
*/
// allow-test-rule: structural parity/drift guard — asserts literal presence/absence of the canonical gsd_run launcher and the retired $GSD_SDK / `/gsd-tools` tokens across workflow markdown; there is no typed IR for "this source file does not contain substring X".
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const { execFileSync } = require('node:child_process');
const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows');
const SNIPPET_FILE = path.join(WORKFLOWS_DIR, '_runtime-launcher.snippet.sh');
/**
* Read the canonical preamble from the snippet file (all lines, no trailing newline).
*/
function expectedPreamble() {
const raw = fs.readFileSync(SNIPPET_FILE, 'utf8');
const lines = raw.split('\n');
// Strip trailing empty element produced by a trailing newline.
const content = lines[lines.length - 1] === '' ? lines.slice(0, -1) : lines;
assert.ok(content.length >= 1, `_runtime-launcher.snippet.sh must not be empty`);
return content; // array of strings
}
/**
* Extract all bash/sh/shell fenced blocks from markdown content.
* Returns array of { index, lines } where index is 0-based block count,
* and lines is the array of content lines (without the fence markers).
*
* Handles both column-0 fences (```bash) and indented fences ( ```bash).
*/
function extractShellBlocks(content) {
const allLines = content.split('\n');
const blocks = [];
let inBlock = false;
let blockLang = null;
let blockLines = [];
let blockIndex = 0;
let blockIndent = '';
let closingPattern = null;
for (let i = 0; i < allLines.length; i++) {
const line = allLines[i];
if (!inBlock) {
const fenceOpen = line.match(/^(\s*)```(\w+)?\s*$/);
if (fenceOpen) {
inBlock = true;
blockIndent = fenceOpen[1];
blockLang = (fenceOpen[2] || '').toLowerCase();
blockLines = [];
// Closing pattern: same indent prefix + ```
closingPattern = new RegExp('^' + blockIndent.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '```\\s*$');
continue;
}
} else {
if (closingPattern.test(line)) {
if (['bash', 'sh', 'shell', 'zsh', ''].includes(blockLang)) {
blocks.push({ index: blockIndex, lang: blockLang, lines: blockLines });
blockIndex++;
}
inBlock = false;
blockLang = null;
blockLines = [];
blockIndent = '';
closingPattern = null;
continue;
}
blockLines.push(line);
}
}
return blocks;
}
/**
* Collect all workflow .md files recursively under WORKFLOWS_DIR.
* Excludes _runtime-launcher.snippet.sh (not a markdown file).
*/
function collectWorkflowFiles() {
const results = [];
function walk(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
walk(full);
} else if (entry.isFile() && entry.name.endsWith('.md')) {
results.push(full);
}
}
}
walk(WORKFLOWS_DIR);
return results;
}
describe('runtime-launcher-parity (#373)', () => {
// ─── (A) No retired GSD_SDK token ────────────────────────────────────────
test('(A) no GSD_SDK token in any workflow .md file', () => {
const files = collectWorkflowFiles();
assert.ok(files.length > 0, 'expected at least one workflow .md file');
const offending = [];
for (const f of files) {
const content = fs.readFileSync(f, 'utf8');
if (content.includes('GSD_SDK')) {
offending.push(path.relative(WORKFLOWS_DIR, f));
}
}
assert.deepStrictEqual(
offending,
[],
'Found GSD_SDK (retired token) in workflow files — run `node scripts/sync-runtime-launcher.cjs` to fix:\n' +
offending.join('\n'),
);
});
// ─── (B) Exactly ONE canonical preamble per using file ───────────────────
test('(B) each workflow .md using gsd_run contains exactly ONE canonical preamble, before the first gsd_run call', () => {
const preamble = expectedPreamble();
const preambleStr = preamble.join('\n');
const files = collectWorkflowFiles();
assert.ok(files.length > 0, 'expected at least one workflow .md file');
const violations = [];
for (const f of files) {
const rel = path.relative(WORKFLOWS_DIR, f);
const content = fs.readFileSync(f, 'utf8');
const blocks = extractShellBlocks(content);
// Collect all block lines in document order for flat analysis
const allBlockLines = [];
for (const blk of blocks) {
allBlockLines.push(...blk.lines);
}
// Does this file use gsd_run at all?
const fileHasGsdRun = allBlockLines.some((l) => /\bgsd_run\b/.test(l));
if (!fileHasGsdRun) continue;
// Count preamble occurrences across all shell content of this file
// Flatten all block lines with a separator so multi-block boundary doesn't create false match
const allContent = allBlockLines.join('\n');
let preambleCount = 0;
let searchPos = 0;
while (true) {
const idx = allContent.indexOf(preambleStr, searchPos);
if (idx === -1) break;
preambleCount++;
searchPos = idx + preambleStr.length;
}
if (preambleCount !== 1) {
violations.push(
`${rel}: expected exactly 1 canonical preamble occurrence in bash blocks, found ${preambleCount}. ` +
`Run \`node scripts/sync-runtime-launcher.cjs\` to fix.`,
);
continue;
}
// Verify preamble appears BEFORE the first gsd_run call (in document order)
// Find the line index of the preamble start vs the first gsd_run call in the flat content
const preamblePos = allContent.indexOf(preambleStr);
const firstGsdRunPos = allContent.search(/\bgsd_run\b/);
// The first gsd_run WITHIN the preamble itself (the function definition) is fine.
// We need to verify that no gsd_run CALL (i.e. gsd_run used as a command, not in a
// function definition body) appears before the preamble starts.
// Simple check: preamble starts at or before the first gsd_run occurrence
if (preamblePos > firstGsdRunPos) {
violations.push(
`${rel}: preamble appears AFTER the first gsd_run reference — it must precede all gsd_run calls.`,
);
}
}
assert.deepStrictEqual(
violations,
[],
'Files with gsd_run calls have wrong preamble count or ordering:\n' +
violations.join('\n---\n'),
);
});
// ─── (C) Space-safe behavioral test ──────────────────────────────────────
test('(C) gsd_run works with a RUNTIME_DIR path containing spaces', () => {
// Create temp dir whose path contains a space
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd 373 '));
try {
const binDir = path.join(base, 'get-shit-done', 'bin');
fs.mkdirSync(binDir, { recursive: true });
// Stub gsd-tools.cjs that prints its argv
const stub = path.join(binDir, 'gsd-tools.cjs');
fs.writeFileSync(stub, '#!/usr/bin/env node\nconsole.log("STUB:" + process.argv.slice(2).join(","));\n');
fs.chmodSync(stub, 0o755);
// Build a shell script: set RUNTIME_DIR, source preamble, run gsd_run
const snippet = fs.readFileSync(SNIPPET_FILE, 'utf8');
const scriptContent =
`export RUNTIME_DIR=${JSON.stringify(base)}\n` +
snippet +
`\ngsd_run query state.json\n`;
const scriptPath = path.join(base, 'test-space.sh');
fs.writeFileSync(scriptPath, scriptContent);
const stdout = execFileSync('bash', [scriptPath], { encoding: 'utf8' });
assert.ok(
stdout.includes('STUB:query,state.json'),
`Expected stdout to contain "STUB:query,state.json" but got: ${stdout.trim()}`,
);
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});
// ─── (D) Loud guard: missing runtime is fatal ─────────────────────────────
test('(D) missing gsd-tools.cjs and no PATH gsd-tools causes loud non-zero exit with "not found" on stderr', () => {
// Create temp dir with a space in the name, but NO gsd-tools.cjs.
// We ensure gsd-tools is not on PATH by prepending a dir that has no
// gsd-tools binary (system binaries remain on PATH so bash/node work).
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd 373 notools '));
// Place a no-op dir first in PATH; no gsd-tools stub there.
const noToolsBin = path.join(base, 'nobin');
fs.mkdirSync(noToolsBin, { recursive: true });
try {
const snippet = fs.readFileSync(SNIPPET_FILE, 'utf8');
// The script must also unset any GSD_TOOLS env var that might leak in
const scriptContent =
`unset GSD_TOOLS\n` +
`export RUNTIME_DIR=${JSON.stringify(base)}\n` +
snippet +
`\ngsd_run query state.json\n`;
const scriptPath = path.join(base, 'test-guard.sh');
fs.writeFileSync(scriptPath, scriptContent);
// Build a PATH that has noToolsBin first (no gsd-tools stub there) but retains
// system paths needed for bash. Exclude any PATH entry that contains a gsd-tools binary.
const systemPaths = (process.env.PATH || '/usr/bin:/bin')
.split(path.delimiter)
.filter((p) => {
try { fs.accessSync(path.join(p, 'gsd-tools'), fs.constants.X_OK); return false; }
catch { return true; }
});
const isolatedPath = [noToolsBin, ...systemPaths].join(path.delimiter);
let threw = false;
let stderrOutput = '';
try {
execFileSync('bash', [scriptPath], {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, PATH: isolatedPath },
});
} catch (err) {
threw = true;
stderrOutput = err.stderr || '';
}
assert.ok(threw, 'Expected the script to exit non-zero when gsd-tools.cjs is missing and gsd-tools is not on PATH');
assert.ok(
stderrOutput.includes('not found') || stderrOutput.includes('ERROR'),
`Expected stderr to contain "not found" or "ERROR", got: ${stderrOutput.trim()}`,
);
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});
// ─── (E) PATH fallback behavioral (#3668) ────────────────────────────────
test('(E) PATH fallback: uses installed gsd-tools when no local gsd-tools.cjs present', () => {
// Create a temp dir with NO local get-shit-done/bin/gsd-tools.cjs.
// Place an executable gsd-tools stub on a dedicated PATH dir.
// RUNTIME_DIR points somewhere that has no gsd-tools.cjs.
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd 373 pathfb '));
try {
const pathBinDir = path.join(base, 'bin');
fs.mkdirSync(pathBinDir, { recursive: true });
// Stub installed gsd-tools binary that prints a marker
const stubPath = path.join(pathBinDir, 'gsd-tools');
fs.writeFileSync(stubPath, '#!/bin/sh\necho "installed:$*"\n');
fs.chmodSync(stubPath, 0o755);
// RUNTIME_DIR points to base — no get-shit-done/bin/gsd-tools.cjs there
const snippet = fs.readFileSync(SNIPPET_FILE, 'utf8');
const scriptContent =
`export RUNTIME_DIR=${JSON.stringify(base)}\n` +
snippet +
`\nprintf "GSD_TOOLS=%s\\n" "$GSD_TOOLS"\n` +
`gsd_run query state.json\n`;
const scriptPath = path.join(base, 'test-pathfb.sh');
fs.writeFileSync(scriptPath, scriptContent);
const stdout = execFileSync('bash', [scriptPath], {
encoding: 'utf8',
env: { ...process.env, PATH: `${pathBinDir}${path.delimiter}${process.env.PATH || ''}` },
});
// The PATH fallback must have resolved GSD_TOOLS to the stub binary.
// Normalize backslashes → forward slashes so the assertion works on Windows
// (git-bash emits POSIX paths while Node's os.tmpdir() returns the Windows form).
// Assert by suffix (/bin/gsd-tools, no .cjs extension) rather than absolute prefix
// because the prefix differs between Windows and POSIX.
// Use .+ (not \S*) to tolerate paths that contain spaces.
const normStdout = stdout.replace(/\\/g, '/');
assert.match(
normStdout,
/GSD_TOOLS=.+\/bin\/gsd-tools(?:\s|$)/m,
`Expected GSD_TOOLS to resolve to the installed PATH stub (suffix /bin/gsd-tools), got: ${stdout.trim()}`,
);
assert.doesNotMatch(
normStdout,
/GSD_TOOLS=.+\.cjs/m,
`Expected GSD_TOOLS NOT to point to a .cjs file in PATH fallback, got: ${stdout.trim()}`,
);
// The stub must have been invoked with the query arguments
assert.ok(
stdout.includes('installed:query state.json'),
`Expected stdout to contain "installed:query state.json" (PATH stub output), got: ${stdout.trim()}`,
);
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});
// ─── (F) Regression locks: no /gsd-tools substring; no do.md dispatcher false-positive ──
test('(F) snippet has no /gsd-tools substring; do.md has no /gsd[:-][a-z] matches', () => {
// (F1) The snippet must not contain the literal substring /gsd-tools.
// The _GSD_SHIM_NAME indirection ensures bin/${_GSD_SHIM_NAME} instead of
// bin/gsd-tools.cjs — so the do.md dispatcher regex /\/gsd[:-]([a-z]...)/ never
// misreads a preamble line as a slash-command stub.
const snippetContent = fs.readFileSync(SNIPPET_FILE, 'utf8');
assert.ok(
!snippetContent.includes('/gsd-tools'),
`_runtime-launcher.snippet.sh must not contain the literal "/gsd-tools" substring. ` +
`Use bin/\${_GSD_SHIM_NAME} indirection to keep the /gsd[:-] scanner from ` +
`misreading it as a slash-command stub. Found in snippet:\n` +
snippetContent.split('\n').filter((l) => l.includes('/gsd-tools')).join('\n'),
);
// (F2) workflows/do.md must not contain the literal substring /gsd-tools
// (the specific path that leaks when _GSD_SHIM_NAME indirection is bypassed).
// The bug-2954 dispatcher scanner /\/gsd[:-]([a-z]...)/ would misread
// /gsd-tools as a slash-command stub named "tools" — which is not shipped.
// Note: /gsd:command references (with colon) in the dispatch table are
// legitimate and are NOT checked here.
const doMdPath = path.join(WORKFLOWS_DIR, 'do.md');
const doMdContent = fs.readFileSync(doMdPath, 'utf8');
const offendingLines = doMdContent
.split('\n')
.filter((l) => /\/gsd-tools/.test(l));
assert.deepStrictEqual(
offendingLines,
[],
`workflows/do.md contains the literal "/gsd-tools" substring which the dispatcher-parity ` +
`scanner (bug-2954) misreads as a slash-command stub. Use \${_GSD_SHIM_NAME} indirection. ` +
`Offending lines:\n` +
offendingLines.join('\n'),
);
});
});

View File

@@ -111,10 +111,10 @@ describe('ultraplan-phase workflow initialization', () => {
const content = fs.readFileSync(WF_PATH, 'utf-8'); const content = fs.readFileSync(WF_PATH, 'utf-8');
test('loads GSD phase context via gsd-sdk query init.plan-phase', () => { test('loads GSD phase context via gsd-sdk query init.plan-phase', () => {
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form // After #3797 architectural fix, callsites use gsd_run
assert.ok( assert.ok(
content.includes('$GSD_SDK query init.plan-phase') || content.includes('gsd-sdk query init.plan-phase'), content.includes('gsd_run query init.plan-phase'),
'workflow must load phase context via gsd-sdk query init.plan-phase', 'workflow must load phase context via gsd_run query init.plan-phase',
); );
}); });

View File

@@ -500,7 +500,7 @@ describe('worktree cleanup after executor completes (#1496)', () => {
// deletion internally). The manual shell cleanup loop has been removed. // deletion internally). The manual shell cleanup loop has been removed.
assert.ok( assert.ok(
content.includes('worktree.cleanup-wave'), content.includes('worktree.cleanup-wave'),
'quick cleanup must delegate to $GSD_SDK query worktree.cleanup-wave (#3797)', 'quick cleanup must delegate to gsd_run query worktree.cleanup-wave (#3797)',
); );
}); });
@@ -510,7 +510,7 @@ describe('worktree cleanup after executor completes (#1496)', () => {
// A soft || { warn } fallback would silently swallow blocked cleanups. // A soft || { warn } fallback would silently swallow blocked cleanups.
assert.match( assert.match(
content, content,
/\$GSD_SDK query worktree\.cleanup-wave.*\|\| exit 1/, /gsd_run query worktree\.cleanup-wave.*\|\| exit 1/,
'quick.md cleanup-wave must use || exit 1 — SDK safety refusals must surface (#3797)', 'quick.md cleanup-wave must use || exit 1 — SDK safety refusals must surface (#3797)',
); );
}); });
@@ -535,8 +535,8 @@ describe('worktree merge: orchestrator file protection (#1756)', () => {
// worktree.cleanup-wave handles STATE.md/ROADMAP.md backup + restore internally. // worktree.cleanup-wave handles STATE.md/ROADMAP.md backup + restore internally.
assert.match( assert.match(
content, content,
/\$GSD_SDK query worktree\.cleanup-wave --manifest "\$WAVE_WORKTREE_MANIFEST" \|\| exit 1/, /gsd_run query worktree\.cleanup-wave --manifest "\$WAVE_WORKTREE_MANIFEST" \|\| exit 1/,
'execute-phase.md must delegate to $GSD_SDK query worktree.cleanup-wave with || exit 1 (#3797)', 'execute-phase.md must delegate to gsd_run query worktree.cleanup-wave with || exit 1 (#3797)',
); );
}); });
@@ -570,8 +570,8 @@ describe('worktree merge: orchestrator file protection (#1756)', () => {
// backup code — that is handled internally by worktree.cleanup-wave. // backup code — that is handled internally by worktree.cleanup-wave.
assert.match( assert.match(
content, content,
/\$GSD_SDK query worktree\.cleanup-wave --manifest "\$QUICK_WORKTREE_MANIFEST" \|\| exit 1/, /gsd_run query worktree\.cleanup-wave --manifest "\$QUICK_WORKTREE_MANIFEST" \|\| exit 1/,
'quick.md must delegate to $GSD_SDK query worktree.cleanup-wave with || exit 1 (#3797)', 'quick.md must delegate to gsd_run query worktree.cleanup-wave with || exit 1 (#3797)',
); );
}); });
}); });
@@ -677,9 +677,9 @@ describe('bug #3384: worktree cleanup workflow contracts', () => {
assert.match(content, /append its returned `\{agent_id, worktree_path, branch, expected_base\}`/); assert.match(content, /append its returned `\{agent_id, worktree_path, branch, expected_base\}`/);
// After #3797 architectural fix: quick.md delegates entirely to the SDK's cleanup-wave // After #3797 architectural fix: quick.md delegates entirely to the SDK's cleanup-wave
// command (which handles manifest parsing internally). The shell fallback with manual // command (which handles manifest parsing internally). The shell fallback with manual
// QUICK_WORKTREE_MANIFEST node-e code is removed — the SDK call with || exit 1 is the // QUICK_WORKTREE_MANIFEST node-e code is removed — the gsd_run call with || exit 1 is the
// only cleanup path, enforcing safety-refusal semantics (#3174/#3384). // only cleanup path, enforcing safety-refusal semantics (#3174/#3384).
assert.match(content, /\$GSD_SDK query worktree\.cleanup-wave --manifest "\$QUICK_WORKTREE_MANIFEST" \|\| exit 1/); assert.match(content, /gsd_run query worktree\.cleanup-wave --manifest "\$QUICK_WORKTREE_MANIFEST" \|\| exit 1/);
assert.doesNotMatch(content, /done < <\(node -e 'const fs=require\("fs"\);const p=process\.env\.QUICK_WORKTREE_MANIFEST/); assert.doesNotMatch(content, /done < <\(node -e 'const fs=require\("fs"\);const p=process\.env\.QUICK_WORKTREE_MANIFEST/);
assert.doesNotMatch(content, /done < <\(git worktree list --porcelain \| grep "\^worktree " \| grep "\\\.claude\/worktrees\/agent-"/); assert.doesNotMatch(content, /done < <\(git worktree list --porcelain \| grep "\^worktree " \| grep "\\\.claude\/worktrees\/agent-"/);
}); });
@@ -696,8 +696,8 @@ test('#3425: helper cleanup path pins orchestrator CWD to primary worktree and c
assert.match(content, /cd "\$PRIMARY_WT" \|\| \{ echo "FATAL: cannot cd to primary worktree \$PRIMARY_WT" >&2; exit 1; \}/); assert.match(content, /cd "\$PRIMARY_WT" \|\| \{ echo "FATAL: cannot cd to primary worktree \$PRIMARY_WT" >&2; exit 1; \}/);
assert.match(content, /ORCH_BRANCH=\$\(git rev-parse --abbrev-ref HEAD\)/); assert.match(content, /ORCH_BRANCH=\$\(git rev-parse --abbrev-ref HEAD\)/);
assert.match(content, /FATAL: orchestrator on '\$ORCH_BRANCH' but expected '\$EXPECTED_BRANCH' before worktree cleanup — refusing to merge \(#3174-class drift\)/); assert.match(content, /FATAL: orchestrator on '\$ORCH_BRANCH' but expected '\$EXPECTED_BRANCH' before worktree cleanup — refusing to merge \(#3174-class drift\)/);
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form // After #3797 architectural fix, callsites use gsd_run
assert.match(content, /(?:\$GSD_SDK|gsd-sdk) query worktree\.cleanup-wave --manifest "\$WAVE_WORKTREE_MANIFEST"/); assert.match(content, /gsd_run query worktree\.cleanup-wave --manifest "\$WAVE_WORKTREE_MANIFEST"/);
}); });
test('#3425: cleanup-tail snippet carries the same primary-worktree pin before removal', () => { test('#3425: cleanup-tail snippet carries the same primary-worktree pin before removal', () => {