* 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:
5
.changeset/jolly-cranes-chatter.md
Normal file
5
.changeset/jolly-cranes-chatter.md
Normal 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.
|
||||
@@ -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.
|
||||
|
||||
|
||||
1
get-shit-done/workflows/_runtime-launcher.snippet.sh
Normal file
1
get-shit-done/workflows/_runtime-launcher.snippet.sh
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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:**
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):**
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
|
||||
@@ -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:**
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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} \
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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")
|
||||
```
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
402
scripts/sync-runtime-launcher.cjs
Normal file
402
scripts/sync-runtime-launcher.cjs
Normal 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();
|
||||
@@ -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)',
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"'),
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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)',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
385
tests/runtime-launcher-parity.test.cjs
Normal file
385
tests/runtime-launcher-parity.test.cjs
Normal 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'),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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',
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user