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
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
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
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
NEXT=$($GSD_SDK query phase.next-decimal 999 --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
NEXT=$(gsd_run query phase.next-decimal 999 --raw)
```
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:
```bash
SLUG=$($GSD_SDK query generate-slug "$ARGUMENTS" --raw)
PROJECT_CODE=$($GSD_SDK query config-get project_code --raw 2>/dev/null || echo "")
SLUG=$(gsd_run query generate-slug "$ARGUMENTS" --raw)
PROJECT_CODE=$(gsd_run query config-get project_code --raw 2>/dev/null || echo "")
PREFIX=$([ -n "$PROJECT_CODE" ] && echo "${PROJECT_CODE}-" || echo "")
PHASE_DIR=".planning/phases/${PREFIX}${NEXT}-${SLUG}"
mkdir -p "${PHASE_DIR}"
@@ -75,7 +64,7 @@ touch "${PHASE_DIR}/.gitkeep"
## Step 5: Commit
```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

View File

@@ -29,19 +29,8 @@ Exit.
Load phase operation context:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 "0")
_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_run query init.phase-op "0")
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`:**
```bash
RESULT=$($GSD_SDK query phase.add "${description}")
RESULT=$(gsd_run query phase.add "${description}")
```
The CLI handles:

View File

@@ -33,19 +33,8 @@ Exit.
Load phase operation context:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
_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_run query init.phase-op "${PHASE_ARG}")
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:
```bash
$GSD_SDK query state-snapshot
gsd_run query state-snapshot
```
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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.todos)
_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_run query init.todos)
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:
```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`:
@@ -133,7 +122,7 @@ If `.planning/STATE.md` exists:
Commit the todo and any updated state:
```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.

View File

@@ -20,19 +20,8 @@ This prevents the two most common AI development failures: choosing the wrong fr
## 1. Initialize
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.plan-phase "$PHASE")
_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_run query init.plan-phase "$PHASE")
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:
```bash
SELECTOR_MODEL=$($GSD_SDK 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)
DOMAIN_MODEL=$($GSD_SDK 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)
SELECTOR_MODEL=$(gsd_run query resolve-model gsd-framework-selector 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_run query resolve-model gsd-domain-researcher 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:
```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`:**
@@ -66,7 +55,7 @@ Exit workflow.
Extract phase number from $ARGUMENTS. If not provided, detect next unplanned phase.
```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.

View File

@@ -32,19 +32,8 @@ Invoke the source audit command and capture output.
For `audit-uat` source:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 audit-uat 2>/dev/null || echo "{}")
_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_run query audit-uat 2>/dev/null || echo "{}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```
@@ -117,7 +106,7 @@ Agent(
**b. Run tests:**
```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 [ -f "Makefile" ] && grep -q "^test:" Makefile; then
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
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.milestone-op)
_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_run query init.milestone-op)
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`.
Resolve integration checker model:
```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
```bash
# 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
@@ -58,7 +47,7 @@ For each phase directory, read the VERIFICATION.md:
```bash
# 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
# 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
for summary in .planning/phases/*-*/*-SUMMARY.md; do
[ -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
```
@@ -155,7 +144,7 @@ For each REQ-ID, determine status using all three sources:
Skip if `workflow.nyquist_validation` is explicitly `false` (absent = enabled).
```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.

View File

@@ -8,19 +8,8 @@ Cross-phase audit of all UAT and verification files. Finds every outstanding ite
Run the CLI audit:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
AUDIT=$($GSD_SDK query audit-uat --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
AUDIT=$(gsd_run query audit-uat --raw)
```
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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.milestone-op)
_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_run query init.milestone-op)
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:
```bash
ROADMAP=$($GSD_SDK query roadmap.analyze)
ROADMAP=$(gsd_run query roadmap.analyze)
```
Parse the JSON `phases` array.
@@ -153,7 +142,7 @@ Exit cleanly.
**Fetch details for each phase:**
```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.
@@ -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:
```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.
@@ -197,7 +186,7 @@ Proceed to 3b.
**If has_context is false:** Check if discuss is disabled via settings:
```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:
@@ -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:
```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:
@@ -261,7 +250,7 @@ None — discuss phase skipped.
Commit the minimal context:
```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.
@@ -282,7 +271,7 @@ Skill(skill="gsd-discuss-phase", args="${PHASE_NUM}")
After discuss completes (either mode), verify context was written:
```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."
@@ -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:
```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.
# 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
@@ -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:
```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`:**
@@ -379,7 +368,7 @@ Auto-invoke code review and fix chain. Autonomous mode chains both review and fi
**Config gate:**
```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.
@@ -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:
```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.
@@ -503,7 +492,7 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
Check if UI review is enabled:
```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`:**
@@ -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):
```bash
ROADMAP=$($GSD_SDK query roadmap.analyze)
ROADMAP=$(gsd_run query roadmap.analyze)
```
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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.todos)
_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_run query init.todos)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```
@@ -169,7 +158,7 @@ If todo was moved to done/, commit the change:
```bash
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.

View File

@@ -124,19 +124,8 @@ Repeat for all milestones in the cleanup set.
Commit the changes:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 commit "chore: archive phase directories from completed milestones" --files .planning/milestones/ .planning/phases/
_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_run query commit "chore: archive phase directories from completed milestones" --files .planning/milestones/ .planning/phases/
```
</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:
```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}"
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
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:
```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":
@@ -375,7 +364,7 @@ if [ -f "${FIX_REPORT_PATH}" ]; then
echo "REVIEW-FIX.md created at ${FIX_REPORT_PATH}"
if [ "$COMMIT_DOCS" = "true" ]; then
$GSD_SDK query commit \
gsd_run query commit \
"docs(${PADDED_PHASE}): add code review fix report" \
--files "${FIX_REPORT_PATH}"
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:
```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}"
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```
@@ -86,7 +75,7 @@ fi
Check if code review is enabled via config:
```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":
@@ -109,7 +98,7 @@ Determine review depth with priority order:
if [ -n "$DEPTH_OVERRIDE" ]; then
REVIEW_DEPTH="$DEPTH_OVERRIDE"
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}"
fi
```
@@ -335,10 +324,10 @@ Optional structural cross-module pass powered by fallow.
Read fallow config gates:
```bash
FALLOW_ENABLED=$($GSD_SDK 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_PROFILE=$($GSD_SDK 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_ENABLED=$(gsd_run query config-get code_quality.fallow.enabled 2>/dev/null || echo "false")
FALLOW_SCOPE=$(gsd_run query config-get code_quality.fallow.scope 2>/dev/null || echo "phase")
FALLOW_PROFILE=$(gsd_run query config-get code_quality.fallow.profile 2>/dev/null || echo "standard")
FALLOW_MCP=$(gsd_run query config-get code_quality.fallow.mcp 2>/dev/null || echo "false")
```
Defaults are fail-closed and opt-in:
@@ -505,7 +494,7 @@ if [ -f "${REVIEW_PATH}" ]; then
echo "REVIEW.md created at ${REVIEW_PATH}"
if [ "$COMMIT_DOCS" = "true" ]; then
$GSD_SDK query commit \
gsd_run query commit \
"docs(${PADDED_PHASE}): add code review report" \
--files "${REVIEW_PATH}"
fi

View File

@@ -41,19 +41,8 @@ When a milestone completes:
Before proceeding with milestone close, run the comprehensive open artifact audit.
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 audit-open
_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_run query audit-open
```
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:**
```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:
@@ -211,7 +200,7 @@ Extract one-liners from SUMMARY.md files using summary-extract:
# For each phase in milestone, extract one-liner
for summary in .planning/phases/*-*/*-SUMMARY.md; do
[ -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
```
@@ -424,7 +413,7 @@ Update `.planning/ROADMAP.md` — group completed milestone phases:
**Delegate archival to `gsd-tools.cjs query milestone.complete`:**
```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:
@@ -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:**
```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.
@@ -575,7 +564,7 @@ If the "## Cross-Milestone Trends" section exists, update the tables with new da
**Commit:**
```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>
@@ -609,7 +598,7 @@ Check branching strategy and offer merge options.
Use `init milestone-op` for context, or load config directly:
```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
```
@@ -617,7 +606,7 @@ Extract `branching_strategy`, `phase_branch_template`, `milestone_branch_templat
Detect base branch:
```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
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')
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
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 state.load)
_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_run query state.load)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```
Extract `commit_docs` from init JSON. Resolve debugger model:
```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:
```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

View File

@@ -58,19 +58,8 @@ gaps = [
**Read worktree config:**
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
USE_WORKTREES=$($GSD_SDK query config-get workflow.use_worktrees 2>/dev/null || echo "true")
_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
USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees 2>/dev/null || echo "true")
```
**Report diagnosis plan to user:**
@@ -99,7 +88,7 @@ This runs in parallel - all gaps investigated simultaneously.
**Load agent skills:**
```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)
```
@@ -191,7 +180,7 @@ Update status in frontmatter to "diagnosed".
Commit the updated UAT.md:
```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>

View File

@@ -64,21 +64,10 @@ plain-text numbered list and ask the user to type their choice number.
Phase number from argument (required).
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
_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_run query init.phase-op "${PHASE}")
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`,
@@ -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.
```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[]`.
@@ -564,7 +553,7 @@ Write file.
Commit phase context and discussion log:
```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)"
@@ -574,7 +563,7 @@ Confirm: "Committed: docs(${padded_phase}): capture phase context (assumptions m
Update STATE.md with session info:
```bash
$GSD_SDK query state.record-session \
gsd_run query state.record-session \
--stopped-at "Phase ${PHASE} context gathered (assumptions mode)" \
--resume-file "${phase_dir}/${padded_phase}-CONTEXT.md"
```
@@ -582,7 +571,7 @@ $GSD_SDK query state.record-session \
Commit STATE.md:
```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>
@@ -635,17 +624,17 @@ Check for auto-advance trigger:
2. Sync chain flag:
```bash
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
```
3. Read consolidated auto-mode (`active` = chain flag OR user preference):
```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:**
```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:**

View File

@@ -109,9 +109,9 @@ Phase: "API documentation" → Structure/navigation, Code examples depth,
Phase number from argument (required).
```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
INIT=$($GSD_SDK query init.phase-op "${PHASE}"); [[ "$INIT" == @file:* ]] && INIT=$(cat "${INIT#@file:}")
AGENT_SKILLS_ADVISOR=$($GSD_SDK query agent-skills gsd-advisor-researcher)
_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_run query init.phase-op "${PHASE}"); [[ "$INIT" == @file:* ]] && INIT=$(cat "${INIT#@file:}")
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`.
@@ -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.
```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`).
@@ -447,7 +447,7 @@ rm -f "${phase_dir}/${padded_phase}-DISCUSS-CHECKPOINT.json"
Commit phase context and discussion log:
```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"
@@ -457,11 +457,11 @@ Confirm: "Committed: docs(${padded_phase}): capture phase context"
Update STATE.md with session info:
```bash
$GSD_SDK query state.record-session \
gsd_run query state.record-session \
--stopped-at "Phase ${PHASE} context gathered" \
--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>

View File

@@ -37,7 +37,8 @@ Map to calibration tier:
Resolve advisor model:
```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

View File

@@ -40,7 +40,8 @@ that the next pass treats as gaps, consuming unbounded time and resources.
Check the pass cap from config:
```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

View File

@@ -25,20 +25,21 @@
interrupted `--auto` chain. This does NOT touch `workflow.auto_advance`
(the user's persistent settings preference):
```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
$GSD_SDK query config-set workflow._auto_chain_active false || true
gsd_run query config-set workflow._auto_chain_active false || true
fi
```
3. Read consolidated auto-mode (`active` = chain flag OR user preference):
```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:**
Persist chain flag to config (handles direct usage without new-project):
```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

View File

@@ -26,20 +26,8 @@ Wait for response before continuing.
**Check if project exists.**
```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_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)
_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_run query state.load 2>/dev/null)
```
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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 docs-init)
_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_run query docs-init)
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:
@@ -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):
```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
# Append any conditional docs that were generated:
# --files ... docs/API.md docs/DEPLOYMENT.md CONTRIBUTING.md

View File

@@ -34,19 +34,8 @@ Exit.
Load phase operation context:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 "${target}")
_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_run query init.phase-op "${target}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```
@@ -62,7 +51,7 @@ Exit.
Read the current phase section from ROADMAP.md:
```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:
@@ -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:
```bash
ANALYZE=$($GSD_SDK query roadmap analyze)
ANALYZE=$(gsd_run query roadmap analyze)
```
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:
```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.
@@ -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:
```bash
$GSD_SDK query state.add-roadmap-evolution \
gsd_run query state.add-roadmap-evolution \
--phase {target} \
--action edited \
--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
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
_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_run query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `commit_docs`.
```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:

View File

@@ -66,21 +66,10 @@ If `--wave` is absent, preserve the current behavior of executing all incomplete
Load all context in one call:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.execute-phase "${PHASE_ARG}")
_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_run query init.execute-phase "${PHASE_ARG}")
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`.
@@ -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:
```bash
RUNTIME=$($GSD_SDK 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")
EXECUTOR_STALL_INTERVAL_MINUTES=$($GSD_SDK 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")
RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo "claude")
USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees 2>/dev/null || echo "true")
EXECUTOR_STALL_INTERVAL_MINUTES=$(gsd_run query config-get executor.stall_detect_interval_minutes 2>/dev/null || echo "5")
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
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
fi
# 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.
@@ -125,7 +114,7 @@ When `USE_WORKTREES` (project-level) is `false`, all executor agents run without
Read context window size for adaptive prompt enrichment:
```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:
@@ -157,7 +146,7 @@ inline path for each plan.
```bash
# REQUIRED: prevents stale auto-chain from previous --auto runs
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
```
@@ -165,8 +154,8 @@ Resolve `MVP_MODE` once via the centralized `phase.mvp-mode` query verb (precede
```bash
MVP_FLAG_ARG=""
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)
TDD_MODE=$($GSD_SDK query config-get workflow.tdd_mode 2>/dev/null || echo "false")
MVP_MODE=$(gsd_run query phase.mvp-mode "${PHASE_NUMBER}" $MVP_FLAG_ARG --pick active)
TDD_MODE=$(gsd_run query config-get workflow.tdd_mode 2>/dev/null || echo "false")
```
<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:
```bash
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
RED_COMMIT=$(git log --oneline --grep="^test(${PHASE_NUMBER}-${PLAN_ID}):" -- "**/*.test.*" "**/*.spec.*" "tests/" | head -1)
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}"
exit 1
fi
@@ -318,7 +307,7 @@ Report: "Found {plan_count} plans in {phase_dir} ({incomplete_count} incomplete)
**Update STATE.md for phase start:**
```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.
</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:
```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`.
@@ -369,9 +358,9 @@ executor skips them.
`workflow.cross_ai_execution` is `true`. Plans matching both conditions are marked for cross-AI.
```bash
CROSS_AI_ENABLED=$($GSD_SDK 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_TIMEOUT=$($GSD_SDK query config-get workflow.cross_ai_timeout 2>/dev/null || echo "300")
CROSS_AI_ENABLED=$(gsd_run query config-get workflow.cross_ai_execution 2>/dev/null || echo "false")
CROSS_AI_CMD=$(gsd_run query config-get workflow.cross_ai_command 2>/dev/null || echo "")
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.
@@ -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:
```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
# 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
@@ -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; }
# 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):**
@@ -847,12 +836,12 @@ increases monotonically across waves. `{status}` is `complete` (success),
if [ "${TEST_EXIT}" -eq 0 ]; then
# Update ROADMAP plan progress for each completed plan in this wave
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
# Only commit tracking files if they actually changed
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
elif [ "${TEST_EXIT}" -eq 124 ]; then
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:**
**Step 7.0 — classify before branching (#3095):**
```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')
SENTINEL=$(echo "$CLASS_JSON" | jq -r '.sentinel // 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:**
Read auto-advance config (chain flag OR user preference — same boolean as `check.auto-mode`):
```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`:
@@ -1028,7 +1017,7 @@ After all waves:
**Security gate check:**
```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)
```
@@ -1052,7 +1041,7 @@ If `SECURITY_CFG` is `true` AND SECURITY.md exists: check frontmatter `threats_o
**Optional step — TDD collaborative review.**
```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`.**
@@ -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:
```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:
@@ -1137,7 +1126,7 @@ Selected wave finished successfully. This phase still has incomplete plans, so p
**Config gate:**
```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.
@@ -1180,7 +1169,7 @@ fi
**2. Find parent UAT file:**
```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
```
@@ -1211,7 +1200,7 @@ mv .planning/debug/{slug}.md .planning/debug/resolved/
**6. Commit updated artifacts:**
```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>
@@ -1239,7 +1228,7 @@ Collect all unique test file paths into `REGRESSION_FILES`.
```bash
# 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 [ -f "Makefile" ] && grep -q "^test:" Makefile; then
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.**
```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`.
@@ -1369,7 +1358,7 @@ spawn template, and the two `workflow.drift_*` config keys.
Verify phase achieved its GOAL, not just completed tasks.
```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:
```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:**
@@ -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:**
```bash
COMPLETION=$($GSD_SDK query phase.complete "${PHASE_NUMBER}")
COMPLETION=$(gsd_run query phase.complete "${PHASE_NUMBER}")
```
The CLI handles:
@@ -1526,7 +1515,7 @@ These items are tracked and will appear in `/gsd:progress` and `/gsd:audit-uat`.
```
```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>
@@ -1538,7 +1527,7 @@ entries from the completed phase to the global learnings store at `~/.gsd/knowle
**Check config gate:**
```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).
@@ -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)
2. If found, copy to global store:
```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.
</step>
@@ -1576,7 +1565,7 @@ for TODO_FILE in "$PENDING_DIR"/*.md; do
done
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}:"
for f in "${CLOSED[@]}"; do echo " ✓ $f"; done
fi
@@ -1601,7 +1590,7 @@ PROJECT.md falls behind silently over multiple phases.
5. Commit the change:
```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.
@@ -1639,7 +1628,7 @@ STOP. Do not proceed to auto-advance or transition.
1. Parse `--auto` flag from $ARGUMENTS
2. Read consolidated auto-mode (`active` = chain flag OR user preference; chain flag already synced in init step):
```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):**

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):
```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:

View File

@@ -8,8 +8,9 @@ detect.
**Step A — Build gate:**
```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
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
XCODEPROJ=$(find . -maxdepth 2 -name "*.xcodeproj" -not -path "*/node_modules/*" 2>/dev/null | head -1)
if [ -n "$XCODEPROJ" ]; then
@@ -63,7 +64,7 @@ fi
```bash
# 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
XCODEPROJ=$(find . -maxdepth 2 -name "*.xcodeproj" -not -path "*/node_modules/*" 2>/dev/null | head -1)
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):
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.execute-phase "${PHASE}")
_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_run query init.execute-phase "${PHASE}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```
@@ -85,7 +74,7 @@ PLAN_START_EPOCH=$(date +%s)
```bash
# 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")
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
```
@@ -169,7 +158,7 @@ This IS the execution instructions. Follow exactly. If plan references CONTEXT.m
<step name="previous_phase_check">
```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
```
@@ -323,7 +312,7 @@ If verification fails:
**Check if node repair is enabled** (default: on):
```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:
@@ -396,13 +385,13 @@ IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false")
# Skip in parallel mode — orchestrator handles STATE.md centrally
if [ "$IS_WORKTREE" != "true" ]; then
# 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
$GSD_SDK query state.update-progress
gsd_run query state.update-progress
# Record execution metrics
$GSD_SDK query state.record-metric \
gsd_run query state.record-metric \
--phase "${PHASE}" --plan "${PLAN}" --duration "${DURATION}" \
--tasks "${TASK_COUNT}" --files "${FILE_COUNT}"
fi
@@ -415,11 +404,11 @@ From SUMMARY: Extract decisions and add to STATE.md:
```bash
# Add each decision from SUMMARY key-decisions
# 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}"
# 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>
@@ -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):
```bash
$GSD_SDK query state.record-session \
gsd_run query state.record-session \
--stopped-at "Completed ${PHASE}-${PLAN}-PLAN.md" \
--resume-file "None"
```
@@ -453,7 +442,7 @@ IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false")
if [ "$IS_WORKTREE" != "true" ]; then
# 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
```
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:
```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.
@@ -483,9 +472,9 @@ IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false")
# In parallel mode: exclude STATE.md and ROADMAP.md (orchestrator commits these)
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
$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
```
</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.
```bash
$GSD_SDK query commit "" --files .planning/codebase/*.md --amend
gsd_run query commit "" --files .planning/codebase/*.md --amend
```
</step>

View File

@@ -115,19 +115,8 @@ For each selected output, write the file:
Commit if `commit_docs` is enabled:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 commit "docs: capture exploration — {topic_slug}" --files {file_list}
_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_run query commit "docs: capture exploration — {topic_slug}" --files {file_list}
```
## 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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
_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_run query init.phase-op "${PHASE_ARG}")
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:
```bash
$GSD_SDK query state.update "Last Activity" "$(date +%Y-%m-%d)"
gsd_run query state.update "Last Activity" "$(date +%Y-%m-%d)"
```
</step>

View File

@@ -272,19 +272,8 @@ gh issue create \
## Step 8: Update STATE.md
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.record-session "" \
_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_run query state.record-session "" \
"Forensic investigation complete" \
".planning/forensics/report-{timestamp}.md"
```

View File

@@ -21,21 +21,10 @@ Read from project config (`config.json`):
## Step 1: Guard Checks
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
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")
_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
GRADUATION_ENABLED=$(gsd_run query config-get features.graduation 2>/dev/null || echo "true")
GRADUATION_WINDOW=$(gsd_run query config-get features.graduation_window 2>/dev/null || echo "5")
GRADUATION_THRESHOLD=$(gsd_run query config-get features.graduation_threshold 2>/dev/null || echo "3")
```
**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.
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 validate.context \
_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_run query validate.context \
--tokens-used "$TOKENS_USED" \
--context-window "$CONTEXT_WINDOW"
```
@@ -76,7 +65,7 @@ health output, the two modes are independent diagnostics.
**Run health validation:**
```bash
$GSD_SDK query validate.health $REPAIR_FLAG $BACKFILL_FLAG
gsd_run query validate.health $REPAIR_FLAG $BACKFILL_FLAG
```
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:
```bash
$GSD_SDK query validate.health
gsd_run query validate.health
```
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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 "{NN}")
_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_run query init.phase-op "{NN}")
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)")
```
@@ -237,7 +226,7 @@ Update `.planning/STATE.md` if appropriate (e.g., increment total plan count).
Commit the imported plan and updated files:
```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:

View File

@@ -34,19 +34,8 @@ Validate first argument is an integer.
Load phase operation context:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 "${after_phase}")
_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_run query init.phase-op "${after_phase}")
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`:**
```bash
RESULT=$($GSD_SDK query phase.insert "${after_phase}" "${description}")
RESULT=$(gsd_run query phase.insert "${after_phase}" "${description}")
```
The CLI handles:
@@ -83,7 +72,7 @@ blocks direct STATE.md writes):
`{decimal_phase}`:
```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
@@ -94,7 +83,7 @@ blocks direct STATE.md writes):
and dedupes identical entries:
```bash
$GSD_SDK query state.add-roadmap-evolution \
gsd_run query state.add-roadmap-evolution \
--phase {decimal_phase} \
--action inserted \
--after {after_phase} \

View File

@@ -11,19 +11,8 @@ Read all files referenced by the invoking prompt's execution_context before star
## 1. Setup
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.list-workspaces)
_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_run query init.list-workspaces)
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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.manager)
_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_run query init.manager)
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:
```bash
INIT=$($GSD_SDK query init.manager)
INIT=$(gsd_run query init.manager)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```

View File

@@ -69,21 +69,10 @@ documents refreshed.
Load codebase mapping context:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.map-codebase)
_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_run query init.map-codebase)
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`.
@@ -390,7 +379,7 @@ Continue to commit_codebase_map.
Commit the codebase map:
```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.

View File

@@ -53,19 +53,8 @@ Read all files that exist. Missing files are fine — the summary adapts to what
Find all phase directories:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 init.progress
_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_run query init.progress
```
This returns phase metadata. For each phase in the milestone scope:
@@ -201,7 +190,7 @@ mkdir -p .planning/reports
Write the summary, then commit:
```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"
```
@@ -229,7 +218,7 @@ If the user is done:
## Step 9: Update STATE.md
```bash
$GSD_SDK query state.record-session "" \
gsd_run query state.record-session "" \
"Milestone v${VERSION} summary generated" \
".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
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
PHASE_INFO=$($GSD_SDK query roadmap.get-phase "${PHASE}")
_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_INFO=$(gsd_run query roadmap.get-phase "${PHASE}")
PHASE_FOUND=$(echo "$PHASE_INFO" | jq -r '.found')
PHASE_NAME=$(echo "$PHASE_INFO" | jq -r '.phase_name')
PHASE_GOAL=$(echo "$PHASE_INFO" | jq -r '.goal')
PHASE_MODE=$(echo "$PHASE_INFO" | jq -r '.mode // ""')
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
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
@@ -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:
```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
echo "$USER_STORY_RESULT" | jq -r '.errors[]' >&2
# 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
```bash
NEW_MODE=$($GSD_SDK query roadmap.get-phase "${PHASE}" --pick mode)
NEW_GOAL=$($GSD_SDK query roadmap.get-phase "${PHASE}" --pick goal)
NEW_MODE=$(gsd_run query roadmap.get-phase "${PHASE}" --pick mode)
NEW_GOAL=$(gsd_run query roadmap.get-phase "${PHASE}" --pick goal)
```
Assert:

View File

@@ -181,19 +181,8 @@ blockers, todos) is preserved across the switch — symmetric with
`milestone.complete`.
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.milestone-switch --milestone "v[X.Y]" --name "[Name]"
_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_run query state.milestone-switch --milestone "v[X.Y]" --name "[Name]"
```
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:
```bash
$GSD_SDK query phases.clear --confirm
gsd_run query phases.clear --confirm
```
```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
```bash
INIT=$($GSD_SDK query init.new-milestone)
INIT=$(gsd_run query init.new-milestone)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_RESEARCHER=$($GSD_SDK query agent-skills gsd-project-researcher)
AGENT_SKILLS_SYNTHESIZER=$($GSD_SDK query agent-skills gsd-research-synthesizer)
AGENT_SKILLS_ROADMAPPER=$($GSD_SDK query agent-skills gsd-roadmapper)
AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-project-researcher)
AGENT_SKILLS_SYNTHESIZER=$(gsd_run query agent-skills gsd-research-synthesizer)
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`.
@@ -456,7 +445,7 @@ If "adjust": Return to scoping.
**Commit requirements:**
```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
@@ -542,7 +531,7 @@ Success criteria:
**Commit roadmap** (after approval):
```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
@@ -585,7 +574,7 @@ files: [existing]
**If any todos were linked:**
```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:

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:**
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.new-project)
_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_run query init.new-project)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_RESEARCHER=$($GSD_SDK query agent-skills gsd-project-researcher)
AGENT_SKILLS_SYNTHESIZER=$($GSD_SDK query agent-skills gsd-research-synthesizer)
AGENT_SKILLS_ROADMAPPER=$($GSD_SDK query agent-skills gsd-roadmapper)
AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-project-researcher)
AGENT_SKILLS_SYNTHESIZER=$(gsd_run query agent-skills gsd-research-synthesizer)
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`.
@@ -275,7 +264,7 @@ Create `.planning/config.json` with all settings (CLI fills in remaining default
```bash
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`.
@@ -284,13 +273,13 @@ $GSD_SDK query config-new-project '{"mode":"yolo","granularity":"[selected]","pa
```bash
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):**
```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).
@@ -495,7 +484,7 @@ Do not compress. Capture everything gathered.
```bash
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
@@ -756,7 +745,7 @@ Create `.planning/config.json` with all settings (CLI fills in remaining default
```bash
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.
@@ -773,7 +762,7 @@ $GSD_SDK query config-new-project '{"mode":"[yolo|interactive]","granularity":"[
**Commit config.json:**
```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
@@ -1221,7 +1210,7 @@ If "adjust": Return to scoping.
**Commit requirements:**
```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
@@ -1397,7 +1386,7 @@ Use AskUserQuestion:
**Generate or refresh project instruction file before final commit:**
```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`.
@@ -1405,7 +1394,7 @@ This ensures new projects get the default GSD workflow-enforcement guidance and
**Commit roadmap (after approval or auto mode):**
```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
@@ -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):
```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")
```

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:**
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.new-workspace)
_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_run query init.new-workspace)
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:
```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
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 "{}"
gsd_run query state.json 2>/dev/null || echo "{}"
```
Also read:
@@ -139,7 +128,7 @@ Choice [S]:
```
2. Commit the deferral record:
```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.

View File

@@ -66,19 +66,8 @@ Report any summaries with placeholder content as incomplete items.
**Write structured handoff to `.planning/HANDOFF.json`:**
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
timestamp=$($GSD_SDK query current-timestamp full --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
timestamp=$(gsd_run query current-timestamp full --raw)
```
```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:
```bash
timestamp=$($GSD_SDK query current-timestamp full --raw)
timestamp=$(gsd_run query current-timestamp full --raw)
```
</step>
<step name="commit">
```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>

View File

@@ -64,20 +64,9 @@ Gap: Flow "View dashboard" broken at data fetch
Find highest existing phase:
```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
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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])
HIGHEST=$(gsd_run query phases.list --pick directories[-1])
```
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:
```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
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}"
@@ -165,7 +154,7 @@ Repeat for each gap-closure phase number. This produces `{CODE}-{NN}-{slug}/` wh
## 9. Commit Roadmap and Requirements Update
```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

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):
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.plan-phase "$PHASE")
_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_run query init.plan-phase "$PHASE")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_RESEARCHER=$($GSD_SDK query agent-skills gsd-phase-researcher)
AGENT_SKILLS_PLANNER=$($GSD_SDK query agent-skills gsd-planner)
AGENT_SKILLS_CHECKER=$($GSD_SDK query agent-skills gsd-plan-checker)
CONTEXT_WINDOW=$($GSD_SDK 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")
MVP_MODE_CFG=$($GSD_SDK query config-get workflow.mvp_mode 2>/dev/null || echo "false")
AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-phase-researcher)
AGENT_SKILLS_PLANNER=$(gsd_run query agent-skills gsd-planner)
AGENT_SKILLS_CHECKER=$(gsd_run query agent-skills gsd-plan-checker)
CONTEXT_WINDOW=$(gsd_run query config-get context_window 2>/dev/null || echo "200000")
TDD_MODE=$(gsd_run query config-get workflow.tdd_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.
@@ -148,7 +137,7 @@ The verb returns `true|false`. Full result also exposes `source` (`cli_flag` | `
```bash
WALKING_SKELETON=false
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
fi
```
@@ -177,7 +166,7 @@ Set `phase_dir="${expected_phase_dir}"` after creation.
Set `CHUNKED_MODE` from flag or config:
```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
if [[ "$ARGUMENTS" =~ --chunked ]] || [[ "$CHUNKED_CFG" == "true" ]]; then
CHUNKED_MODE=true
@@ -205,14 +194,14 @@ Exit workflow.
## 3. Validate Phase
```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.
Now that `PHASE` is finalized, resolve MVP mode:
```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
@@ -312,7 +301,7 @@ Use full relative paths. Group by topic area.]
5. Commit:
```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).
@@ -346,7 +335,7 @@ If `context_path` is not null, display: `Using phase context from: ${context_pat
Read discuss mode for context gate label:
```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:
@@ -390,7 +379,7 @@ If "Run discuss-phase first":
```bash
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`.**
@@ -497,7 +486,7 @@ Display banner:
### Spawn gsd-phase-researcher
```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:
@@ -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.
```bash
SECURITY_CFG=$($GSD_SDK 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_BLOCK=$($GSD_SDK query config-get workflow.security_block_on --raw 2>/dev/null || echo "high")
SECURITY_CFG=$(gsd_run query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true")
SECURITY_ASVS=$(gsd_run query config-get workflow.security_asvs_level --raw 2>/dev/null || echo "1")
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.
@@ -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.
```bash
UI_PHASE_CFG=$($GSD_SDK 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_PHASE_CFG=$(gsd_run query config-get workflow.ui_phase 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.
@@ -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:
```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.
# 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
@@ -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`):
```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):**
@@ -698,7 +687,7 @@ Also available:
Check if any files in the phase scope match schema patterns:
```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:
@@ -813,7 +802,7 @@ Proceed to Step 7.8 (or Step 8 if pattern mapper is disabled) only if user selec
Check config:
```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.
@@ -1106,7 +1095,7 @@ For each plan entry extracted from `PLAN-OUTLINE.md`:
5. **Commit per-plan:**
```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
@@ -1398,8 +1387,8 @@ Skipping bounce step.
**Read pass count:**
```bash
BOUNCE_PASSES=$($GSD_SDK 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_PASSES=$(gsd_run query config-get workflow.plan_bounce_passes 2>/dev/null || echo "2")
BOUNCE_SCRIPT=$(gsd_run query config-get workflow.plan_bounce_script 2>/dev/null | jq -r '.' 2>/dev/null || true)
```
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:
```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:
@@ -1518,9 +1507,9 @@ silently dropped on the way into the plans.
(nothing to translate) or if its `<decisions>` block is empty.
```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
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.
# `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
@@ -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:
```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.
@@ -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.
```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.
@@ -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):
```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.
@@ -1617,7 +1606,7 @@ one place before execution begins.
**Skip if:** `workflow.post_planning_gaps` is `false`. Default is `true`.
```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
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" gap-analysis --phase-dir "${PHASE_DIR}"
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):
```bash
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
```
@@ -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):
```bash
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
```

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)
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
CONVERGENCE_ENABLED=$($GSD_SDK query config-get workflow.plan_review_convergence 2>/dev/null || echo "false")
_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
CONVERGENCE_ENABLED=$(gsd_run query config-get workflow.plan_review_convergence 2>/dev/null || echo "false")
```
**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">
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 commit "docs: plant seed — {$IDEA}" --files .planning/seeds/SEED-{PADDED}-{slug}.md
_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_run query commit "docs: plant seed — {$IDEA}" --files .planning/seeds/SEED-{PADDED}-{slug}.md
```
</step>
@@ -218,7 +207,7 @@ Update the seed file's frontmatter and sections with the gathered values:
Commit the update:
```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:

View File

@@ -130,19 +130,8 @@ Display: "◆ Scanning sessions..."
Run session scan:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
SCAN_RESULT=$($GSD_SDK query scan-sessions --json 2>/dev/null)
_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
SCAN_RESULT=$(gsd_run query scan-sessions --json 2>/dev/null)
```
Parse the JSON output to get session count and project count.
@@ -162,7 +151,7 @@ Display: "◆ Sampling messages..."
Run profile sampling:
```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.
@@ -213,7 +202,7 @@ Display: "Using questionnaire to build your profile."
**Get questions:**
```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.
@@ -236,7 +225,7 @@ Write the answers JSON to `$ANSWERS_PATH`.
**Convert answers to analysis:**
```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.
@@ -283,7 +272,7 @@ Write updated analysis JSON back to `$ANALYSIS_PATH`.
Display: "◆ Writing profile..."
```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"
@@ -362,7 +351,7 @@ Generate selected artifacts sequentially (file I/O is fast, no benefit from para
**For /gsd-dev-preferences (if selected):**
```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"
@@ -370,7 +359,7 @@ Display: "✓ Generated /gsd-dev-preferences at $HOME/.claude/skills/gsd-dev-pre
**For CLAUDE.md profile section (if selected):**
```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"
@@ -378,7 +367,7 @@ Display: "✓ Added profile section to CLAUDE.md"
**For Global CLAUDE.md (if selected):**
```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"

View File

@@ -12,26 +12,15 @@ Read all files referenced by the invoking prompt's execution_context before star
**Load progress context (paths only):**
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.progress)
_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_run query init.progress)
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`.
```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):
@@ -67,7 +56,7 @@ This minimizes orchestrator context usage.
**Get comprehensive roadmap analysis (replaces manual parsing):**
```bash
ROADMAP=$($GSD_SDK query roadmap.analyze)
ROADMAP=$(gsd_run query roadmap.analyze)
```
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
- Use `summary-extract` for efficient parsing:
```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"
</step>
@@ -110,7 +99,7 @@ Use this instead of manually reading/parsing ROADMAP.md.
```bash
# Get formatted progress bar
PROGRESS_BAR=$($GSD_SDK query progress.bar --raw)
PROGRESS_BAR=$(gsd_run query progress.bar --raw)
```
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`:
```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:
@@ -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`):
```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`.
@@ -273,7 +262,7 @@ Check if `{phase_num}-CONTEXT.md` exists in phase directory.
Check if current phase has UI indicators:
```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")
```
@@ -419,7 +408,7 @@ Read ROADMAP.md to get the next phase's name and goal.
Check if next phase has UI indicators:
```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")
```

View File

@@ -125,37 +125,26 @@ If `$VALIDATE_MODE` only:
**Step 2: Initialize**
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 @opengsd/get-shit-done-redux@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.quick "$DESCRIPTION")
_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_run query init.quick "$DESCRIPTION")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_PLANNER=$($GSD_SDK query agent-skills gsd-planner)
AGENT_SKILLS_EXECUTOR=$($GSD_SDK query agent-skills gsd-executor)
AGENT_SKILLS_CHECKER=$($GSD_SDK query agent-skills gsd-plan-checker)
AGENT_SKILLS_VERIFIER=$($GSD_SDK query agent-skills gsd-verifier)
AGENT_SKILLS_PLANNER=$(gsd_run query agent-skills gsd-planner)
AGENT_SKILLS_EXECUTOR=$(gsd_run query agent-skills gsd-executor)
AGENT_SKILLS_CHECKER=$(gsd_run query agent-skills gsd-plan-checker)
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`.
```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).
```bash
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
```
@@ -641,7 +630,7 @@ Skip this step entirely if `USE_WORKTREES === "false"` (non-worktree mode: PLAN.
```bash
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
git add "${QUICK_DIR}/${quick_id}-PLAN.md"
# No-op skip if nothing actually staged (idempotent re-runs).
@@ -650,7 +639,7 @@ if [ "${USE_WORKTREES}" != "false" ]; then
else
# Run hooks normally (#2924). If a project opts out via
# 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
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; }
@@ -790,7 +779,7 @@ After executor returns:
# base, deletion diffs, merge result, and worktree removal before branch
# 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.
$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.
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:**
```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)".
@@ -977,14 +966,14 @@ Build file list:
# Explicitly stage all artifacts before commit — PLAN.md may be untracked
# if the executor ran without worktree isolation and committed docs early
# 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
file_list_filtered=$(echo "${file_list}" | tr ' ' '\n' | grep -v '^\.planning/' | tr '\n' ' ')
git add ${file_list_filtered} 2>/dev/null
else
git add ${file_list} 2>/dev/null
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:

View File

@@ -29,19 +29,8 @@ Exit.
Load phase operation context:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 "${target}")
_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_run query init.phase-op "${target}")
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`:**
```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:
```bash
RESULT=$($GSD_SDK query phase.remove "${target}" --force)
RESULT=$(gsd_run query phase.remove "${target}" --force)
```
The CLI handles:
@@ -115,7 +104,7 @@ Extract from result: `removed`, `directory_deleted`, `renamed_directories`, `ren
Stage and commit the removal:
```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.

View File

@@ -13,19 +13,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Extract workspace name from $ARGUMENTS.
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.remove-workspace "$WORKSPACE_NAME")
_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_run query init.remove-workspace "$WORKSPACE_NAME")
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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.resume)
_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_run query init.resume)
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:
```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
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"
@@ -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"
# 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)
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 "")
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
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
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
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:
```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
```
@@ -225,10 +214,10 @@ Read model preferences from planning config. Null/missing values fall back to CL
```bash
# 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)
CLAUDE_MODEL=$($GSD_SDK 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)
OPENCODE_MODEL=$($GSD_SDK query config-get review.models.opencode 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_run query config-get review.models.claude 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_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):
@@ -325,7 +314,7 @@ prepare_trimmed_prompt_for_reviewer() {
REQUIREMENTS_ARG=""
[ -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" \
--instructions-file "/tmp/gsd-review-{phase}-instructions.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
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
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
# Apply budget trim for Ollama if a budget is configured
@@ -362,9 +351,9 @@ if [ -n "$OLLAMA_REVIEWER_BUDGET" ] && [ "$OLLAMA_REVIEWER_BUDGET" != "null" ] &
fi
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
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
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
@@ -384,9 +373,9 @@ fi
**LM Studio (local, OpenAI-compatible):**
```bash
# 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
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
# 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
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
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
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
@@ -437,9 +426,9 @@ fi
**llama.cpp (local, OpenAI-compatible):**
```bash
# 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
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
# 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
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
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
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
@@ -597,7 +586,7 @@ trimmed_reviewers: # only present if at least one reviewer was trimmed
Commit:
```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>

View File

@@ -39,19 +39,8 @@ Exit.
## Step 2: Check for existing documents
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.map-codebase 2>/dev/null || echo "{}")
_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_run query init.map-codebase 2>/dev/null || echo "{}")
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
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
_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_run query init.phase-op "${PHASE_ARG}")
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`.
```bash
AUDITOR_MODEL=$($GSD_SDK query resolve-model gsd-security-auditor --raw)
SECURITY_CFG=$($GSD_SDK query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true")
AUDITOR_MODEL=$(gsd_run query resolve-model gsd-security-auditor --raw)
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."
@@ -158,7 +147,7 @@ Do NOT emit next-phase routing. Stop here.
## 7. Commit
```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

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`):
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 config-ensure-section
_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_run query config-ensure-section
if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then
if [[ -f .planning/active-workstream ]]; then
WS=$(tr -d '\n\r' < .planning/active-workstream)
@@ -476,12 +465,12 @@ AskUserQuestion([
For each tier where the user chose "Enter model ID":
```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:
```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
@@ -499,14 +488,14 @@ keys and sibling sub-objects.
```bash
# Example — only write keys the user changed. "Keep current" selections are skipped.
$GSD_SDK query config-set workflow.plan_bounce_passes 5
$GSD_SDK query config-set workflow.subagent_timeout 900
$GSD_SDK query config-set git.base_branch main
$GSD_SDK query config-set context_window 1000000
gsd_run query config-set workflow.plan_bounce_passes 5
gsd_run query config-set workflow.subagent_timeout 900
gsd_run query config-set git.base_branch main
gsd_run query config-set context_window 1000000
# Runtime model tier examples:
$GSD_SDK query config-set runtime gemini
$GSD_SDK 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 runtime gemini
gsd_run query config-set model_profile_overrides.gemini.opus gemini-3-ultra
gsd_run query config-set model_profile_overrides.gemini.haiku null
```
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):
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 config-ensure-section
_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_run query config-ensure-section
if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then
if [[ -f .planning/active-workstream ]]; then
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
```bash
BRAVE=$($GSD_SDK query config-get brave_search --default null)
FIRECRAWL=$($GSD_SDK query config-get firecrawl --default null)
EXA=$($GSD_SDK query config-get exa_search --default null)
SEARCH_GITIGNORED=$($GSD_SDK query config-get search_gitignored --default false)
BRAVE=$(gsd_run query config-get brave_search --default null)
FIRECRAWL=$(gsd_run query config-get firecrawl --default null)
EXA=$(gsd_run query config-get exa_search --default null)
SEARCH_GITIGNORED=$(gsd_run query config-get search_gitignored --default false)
```
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:
```bash
$GSD_SDK query config-set brave_search "<value>" # masked in output
$GSD_SDK query config-set firecrawl "<value>" # masked in output
$GSD_SDK query config-set exa_search "<value>" # masked in output
$GSD_SDK query config-set search_gitignored true|false
gsd_run query config-set brave_search "<value>" # masked in output
gsd_run query config-set firecrawl "<value>" # masked in output
gsd_run query config-set exa_search "<value>" # masked in output
gsd_run query config-set search_gitignored true|false
```
For "Clear", write `null`:
```bash
$GSD_SDK query config-set brave_search null
gsd_run query config-set brave_search null
```
</step>
@@ -197,7 +186,7 @@ Leave / Replace / Clear, followed by a text-input prompt for the new command
string. Write via:
```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.
@@ -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:
```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.

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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 config-ensure-section
INIT=$($GSD_SDK query state.load)
_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_run query config-ensure-section
INIT=$(gsd_run query state.load)
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).
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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
_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_run query init.phase-op "${PHASE_ARG}")
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:
```bash
CONFIG=$($GSD_SDK query state.load)
CONFIG=$(gsd_run query state.load)
```
Extract: `branching_strategy`, `branch_name`.
Detect base branch for PRs and merges:
```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
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')
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:
```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`.
@@ -230,7 +219,7 @@ Report: "PR #{number} created: {url}"
Before prompting the user, check if an external review command is configured:
```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:
@@ -243,7 +232,7 @@ If `REVIEW_CMD` is non-empty and not `"null"`, run the external review:
2. **Load phase context from STATE.md:**
```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:**
@@ -316,13 +305,13 @@ Report the PR URL and suggest: "Review the diff at {url}/files"
Update STATE.md to reflect the shipping action:
```bash
$GSD_SDK 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 "Last Activity" "$(date +%Y-%m-%d)"
gsd_run query state.update "Status" "Phase ${PHASE_NUMBER} shipped — PR #${PR_NUMBER}"
```
If `commit_docs` is true:
```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>

View File

@@ -37,19 +37,8 @@ Exit.
Check `commit_docs` config:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true")
_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
COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
```
</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):
```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>

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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true")
_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
COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
```
</step>
@@ -308,7 +297,7 @@ Iterate until satisfied.
**h.** Commit (if `COMMIT_DOCS` is true):
```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:

View File

@@ -37,19 +37,8 @@ Exit.
Check `commit_docs` config:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true")
_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
COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
```
</step>
@@ -258,7 +247,7 @@ Patterns and stack choices established across spike sessions. New spikes follow
Commit all artifacts (if `COMMIT_DOCS` is true):
```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>

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:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
COMMIT_DOCS=$($GSD_SDK query config-get commit_docs 2>/dev/null || echo "true")
_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
COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
```
</step>
@@ -346,7 +335,7 @@ tags: [tag1, tag2]
**i.** Commit (if `COMMIT_DOCS` is true):
```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:
@@ -400,7 +389,7 @@ Only include patterns that repeated across 2+ spikes or were explicitly chosen b
Commit (if `COMMIT_DOCS` is true):
```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>

View File

@@ -12,19 +12,8 @@ Read all files referenced by the invoking prompt's execution_context before star
Gather project statistics:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
STATS=$($GSD_SDK query stats.json)
_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
STATS=$(gsd_run query stats.json)
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:
```bash
ANALYZE=$($GSD_SDK query roadmap.analyze)
ANALYZE=$(gsd_run query roadmap.analyze)
if [[ "$ANALYZE" == @file:* ]]; then ANALYZE=$(cat "${ANALYZE#@file:}"); fi
MVP_COUNT=$(echo "$ANALYZE" | jq '[.phases[] | select(.mode == "mvp")] | 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:
- Read frontmatter `status` field via:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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 frontmatter.get .planning/threads/{file} status
_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_run 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
- 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:
```bash
$GSD_SDK 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 status resolved
gsd_run query frontmatter.set .planning/threads/{SLUG}.md updated YYYY-MM-DD
```
3. Commit:
```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:
@@ -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`:
```bash
$GSD_SDK 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 status in_progress
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.
@@ -153,7 +142,7 @@ If $ARGUMENTS is a new description (no matching thread file):
1. Generate slug from description:
```bash
SLUG=$($GSD_SDK query generate-slug "$ARGUMENTS" --raw)
SLUG=$(gsd_run query generate-slug "$ARGUMENTS" --raw)
```
2. Create the threads directory if needed:
@@ -197,7 +186,7 @@ updated: {today ISO date}
5. Commit:
```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:

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`:**
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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
TRANSITION=$($GSD_SDK query phase.complete "${current_phase}")
_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
TRANSITION=$(gsd_run query phase.complete "${current_phase}")
```
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:
```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.
@@ -421,7 +410,7 @@ The `next_phase` and `next_phase_name` fields give you the next phase details.
If you need additional context, use:
```bash
ROADMAP=$($GSD_SDK query roadmap.analyze)
ROADMAP=$(gsd_run query roadmap.analyze)
```
This returns all phases with goals, disk status, and completion info.
@@ -440,7 +429,7 @@ In flat mode, go directly to **Route B**.
```bash
# Only check if we're in workstream mode
if [ -n "$GSD_WORKSTREAM" ]; then
WS_LIST=$($GSD_SDK query workstream.list --raw)
WS_LIST=$(gsd_run query workstream.list --raw)
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:
```bash
$GSD_SDK query config-set workflow._auto_chain_active false
gsd_run query config-set workflow._auto_chain_active false
```
<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:
```bash
$GSD_SDK query config-set workflow._auto_chain_active false
gsd_run query config-set workflow._auto_chain_active false
```
<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
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.plan-phase "$PHASE")
_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_run query init.plan-phase "$PHASE")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_UI=$($GSD_SDK query agent-skills gsd-ui-researcher)
AGENT_SKILLS_UI_CHECKER=$($GSD_SDK query agent-skills gsd-ui-checker)
AGENT_SKILLS_UI=$(gsd_run query agent-skills gsd-ui-researcher)
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`.
@@ -49,14 +38,14 @@ SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/nul
Resolve UI agent models:
```bash
UI_RESEARCHER_MODEL=$($GSD_SDK query resolve-model gsd-ui-researcher --raw)
UI_CHECKER_MODEL=$($GSD_SDK query resolve-model gsd-ui-checker --raw)
UI_RESEARCHER_MODEL=$(gsd_run query resolve-model gsd-ui-researcher --raw)
UI_CHECKER_MODEL=$(gsd_run query resolve-model gsd-ui-checker --raw)
```
Check config:
```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`:**
@@ -72,7 +61,7 @@ Exit workflow.
Extract phase number from $ARGUMENTS. If not provided, detect next unplanned phase.
```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.
@@ -310,13 +299,13 @@ Dimensions: 6/6 passed
## 11. Commit (if configured)
```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
```bash
$GSD_SDK query state.record-session \
gsd_run query state.record-session \
--stopped-at "Phase ${PHASE} UI-SPEC approved" \
--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
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
_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_run query init.phase-op "${PHASE_ARG}")
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`.
```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:
@@ -188,7 +177,7 @@ tools is detected at runtime.
## 5. Commit (if configured)
```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>

View File

@@ -66,19 +66,8 @@ unplanned phase from the roadmap (same logic as /gsd:plan-phase).
Load GSD phase context:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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.plan-phase "$PHASE")
_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_run query init.plan-phase "$PHASE")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```

View File

@@ -483,7 +483,6 @@ inspect).
Otherwise run `detect-custom-files`:
```bash
GSD_TOOLS="$RUNTIME_DIR/get-shit-done/bin/gsd-tools.cjs"
CUSTOM_JSON=''
if [ -f "$GSD_TOOLS" ] && [ -n "$RUNTIME_DIR" ]; then
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
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
_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_run query init.phase-op "${PHASE_ARG}")
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`.
```bash
AUDITOR_MODEL=$($GSD_SDK query resolve-model gsd-nyquist-auditor --raw)
NYQUIST_CFG=$($GSD_SDK query config-get workflow.nyquist_validation --raw)
AUDITOR_MODEL=$(gsd_run query resolve-model gsd-nyquist-auditor --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."
@@ -151,7 +140,7 @@ Handle return:
git add {test_files}
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

View File

@@ -29,19 +29,8 @@ Then verify each level against the actual codebase.
Load phase operation context:
```bash
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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}")
_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_run query init.phase-op "${PHASE_ARG}")
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:
```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
ls "$phase_dir"/*-SUMMARY.md "$phase_dir"/*-PLAN.md 2>/dev/null || true
```
Load full milestone phases for deferred-item filtering (Step 9b):
```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).
@@ -69,7 +58,7 @@ Use `gsd-tools.cjs query` verify handlers (or legacy gsd-tools) to extract must_
```bash
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"
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:
```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:
@@ -119,7 +108,7 @@ Use `gsd-tools.cjs query verify.artifacts` (or legacy gsd-tools) for artifact ve
```bash
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"
done
```
@@ -162,7 +151,7 @@ Use `gsd-tools.cjs query verify.key-links` (or legacy gsd-tools) for key link ve
```bash
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"
done
```
@@ -217,7 +206,7 @@ phase.
no `<decisions>` block.
```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
# Discover the phase CONTEXT.md via glob expansion rather than `ls | head`
# (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
[ -e "$f" ] && CONTEXT_PATH="$f" && break
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
```
@@ -261,7 +250,7 @@ inspecting static artifacts.
```bash
# 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 [ -f "Makefile" ] && grep -q "^test:" Makefile; then
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:
```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=""
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)
# SDK resolution: prefer local gsd-tools.cjs, fall back to installed gsd-tools (#3668)
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})
INIT=$(gsd_run query init.verify-work "${PHASE_ARG}" ${GSD_WS})
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_PLANNER=$($GSD_SDK query agent-skills gsd-planner)
AGENT_SKILLS_CHECKER=$($GSD_SDK query agent-skills gsd-plan-checker)
AGENT_SKILLS_PLANNER=$(gsd_run query agent-skills gsd-planner)
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`.
@@ -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.
# 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.
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>
@@ -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.
```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)
```
@@ -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:
```bash
PHASE_GOAL=$($GSD_SDK query roadmap.get-phase "${phase_number}" ${GSD_WS} --pick goal)
USER_STORY_VALID=$($GSD_SDK query user-story.validate --story "$PHASE_GOAL" --pick valid)
PHASE_GOAL=$(gsd_run query roadmap.get-phase "${phase_number}" ${GSD_WS} --pick goal)
USER_STORY_VALID=$(gsd_run query user-story.validate --story "$PHASE_GOAL" --pick valid)
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 "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:
```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
```
@@ -437,7 +426,7 @@ Clear Current Test section:
Commit the UAT file:
```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:
@@ -461,7 +450,7 @@ Present summary:
**If issues == 0:**
```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)
```
@@ -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`:
```bash
$GSD_SDK query audit-open --json
gsd_run query audit-open --json
```
Parse the JSON output. For the CURRENT PHASE ONLY, surface:

View File

@@ -54,6 +54,7 @@
"fallow": "^2.70.0"
},
"scripts": {
"sync:launcher": "node scripts/sync-runtime-launcher.cjs",
"check:env": "bash scripts/check-env.sh",
"check:alias-drift": "node scripts/check-alias-drift.cjs",
"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')),
// Does the cleanup-wave invocation use || exit 1 (fail-closed)?
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?
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.
assert.ok(
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', () => {
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(
/(?:\$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'
);
});
test('use_worktrees: false mode — sync call is gated to fire (the #2661 reproducer)', () => {
// 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(
/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)'
);
});
@@ -119,9 +119,9 @@ describe('bug #2661: execute-plan.md update_roadmap gating', () => {
'bash block must include the IS_WORKTREE worktree-detection check'
);
// 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 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(callIdx > guardIdx,
'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')),
// Does the cleanup-wave invocation use || exit 1 (fail-closed)?
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?
hasBrokenLsFilesForm: lines.some(

View File

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

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)', () => {
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
// cleanup internally. The manual shell loop has been removed.
assert.ok(
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();
// The || exit 1 enforces fail-closed: SDK safety refusals (e.g. branch
// 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.
assert.match(
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)',
);
});

View File

@@ -16,14 +16,63 @@ const { execFileSync } = require('node:child_process');
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() {
const content = fs.readFileSync(WORKFLOW_PATH, 'utf8');
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');
assert.notEqual(end, -1, 'SDK resolution snippet must end with fi');
// Find the canonical preamble — prefer _GSD_SHIM_NAME= line (handles both forms)
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');
}
@@ -36,7 +85,7 @@ function runResolver({ cwd, runtimeDir, pathDir }) {
'set -e',
extractResolverSnippet(),
'printf "GSD_TOOLS=%s\\n" "$GSD_TOOLS"',
'$GSD_SDK query state.json',
'gsd_run query state.json',
].join('\n');
return execFileSync('bash', ['-c', script], {
@@ -57,18 +106,28 @@ function writeExecutable(file, content) {
describe('bug-3668: workflow SDK resolver supports installed user projects', () => {
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 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 });
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)
const norm = output.replace(/\\/g, '/');
// 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);
// NO get-shit-done/bin/gsd-tools.cjs in runtimeNoLocal
const output = runResolver({ cwd: project, runtimeDir: runtimeNoLocal, pathDir: pathBin });
// 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/);
});
@@ -78,7 +137,7 @@ describe('bug-3668: workflow SDK resolver supports installed user projects', ()
const runtime = path.join(tmp, 'runtime');
const pathBin = path.join(tmp, 'bin');
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(
path.join(runtime, 'get-shit-done', 'bin', 'gsd-tools.cjs'),
'#!/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)
const norm = output.replace(/\\/g, '/');
// subtest 2: 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);
// The resolved bin is the RUNTIME_DIR local runtime (suffix /get-shit-done/bin/gsd-tools.cjs)
// 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.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', () => {
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form
assert.ok(NEW_MILESTONE.includes('$GSD_SDK query commit') || NEW_MILESTONE.includes('gsd-sdk query commit'), 'should commit tagged todos');
// After #3797 architectural fix, callsites use gsd_run
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');
});

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>/);
assert.ok(stepMatch, 'context_check step must be a closed <step>...</step> block');
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(
stepBody,
/(?:\$GSD_SDK|gsd-sdk)\s+query\s+validate\.context/,
'context_check must call `gsd-sdk query validate.context`',
/gsd_run\s+query\s+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, /--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');
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(
content.includes('$GSD_SDK query init.plan-phase') || content.includes('gsd-sdk query init.plan-phase'),
'workflow must load phase context via gsd-sdk query init.plan-phase',
content.includes('gsd_run 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.
assert.ok(
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.
assert.match(
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)',
);
});
@@ -535,8 +535,8 @@ describe('worktree merge: orchestrator file protection (#1756)', () => {
// worktree.cleanup-wave handles STATE.md/ROADMAP.md backup + restore internally.
assert.match(
content,
/\$GSD_SDK 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)',
/gsd_run query worktree\.cleanup-wave --manifest "\$WAVE_WORKTREE_MANIFEST" \|\| exit 1/,
'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.
assert.match(
content,
/\$GSD_SDK 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)',
/gsd_run query worktree\.cleanup-wave --manifest "\$QUICK_WORKTREE_MANIFEST" \|\| exit 1/,
'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\}`/);
// 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
// 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).
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 < <\(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, /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\)/);
// After #3797 architectural fix, callsites use $GSD_SDK — accept either form
assert.match(content, /(?:\$GSD_SDK|gsd-sdk) query worktree\.cleanup-wave --manifest "\$WAVE_WORKTREE_MANIFEST"/);
// After #3797 architectural fix, callsites use gsd_run
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', () => {