From c7d25b183a18739d6e63929967478260701148cd Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Sun, 5 Apr 2026 11:30:38 -0400 Subject: [PATCH] fix(commands): replace undefined $GSD_TOOLS with resolved path (#1766) (#1769) workstreams.md referenced $GSD_TOOLS (6 occurrences) which is never defined anywhere in the system. All other 60+ command files use the standard $HOME/.claude/get-shit-done/bin/gsd-tools.cjs path. The undefined variable resolves to empty string, causing all workstream commands to fail with module not found. Fixes #1766 Co-authored-by: Claude Opus 4.6 --- commands/gsd/workstreams.md | 12 +++---- tests/gsd-tools-path-refs.test.cjs | 57 ++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 tests/gsd-tools-path-refs.test.cjs diff --git a/commands/gsd/workstreams.md b/commands/gsd/workstreams.md index 49ccdcb1..c65164fa 100644 --- a/commands/gsd/workstreams.md +++ b/commands/gsd/workstreams.md @@ -34,30 +34,30 @@ If no subcommand given, default to `list`. ## Step 2: Execute Operation ### list -Run: `node "$GSD_TOOLS" workstream list --raw --cwd "$CWD"` +Run: `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" workstream list --raw --cwd "$CWD"` Display the workstreams in a table format showing name, status, current phase, and progress. ### create -Run: `node "$GSD_TOOLS" workstream create --raw --cwd "$CWD"` +Run: `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" workstream create --raw --cwd "$CWD"` After creation, display the new workstream path and suggest next steps: - `/gsd-new-milestone --ws ` to set up the milestone ### status -Run: `node "$GSD_TOOLS" workstream status --raw --cwd "$CWD"` +Run: `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" workstream status --raw --cwd "$CWD"` Display detailed phase breakdown and state information. ### switch -Run: `node "$GSD_TOOLS" workstream set --raw --cwd "$CWD"` +Run: `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" workstream set --raw --cwd "$CWD"` Also set `GSD_WORKSTREAM` for the current session when the runtime supports it. If the runtime exposes a session identifier, GSD also stores the active workstream session-locally so concurrent sessions do not overwrite each other. ### progress -Run: `node "$GSD_TOOLS" workstream progress --raw --cwd "$CWD"` +Run: `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" workstream progress --raw --cwd "$CWD"` Display a progress overview across all workstreams. ### complete -Run: `node "$GSD_TOOLS" workstream complete --raw --cwd "$CWD"` +Run: `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" workstream complete --raw --cwd "$CWD"` Archive the workstream to milestones/. ### resume diff --git a/tests/gsd-tools-path-refs.test.cjs b/tests/gsd-tools-path-refs.test.cjs new file mode 100644 index 00000000..6738977c --- /dev/null +++ b/tests/gsd-tools-path-refs.test.cjs @@ -0,0 +1,57 @@ +/** + * Regression guard for #1766: $GSD_TOOLS env var undefined + * + * All command files must use the resolved path to gsd-tools.cjs + * ($HOME/.claude/get-shit-done/bin/gsd-tools.cjs), not the undefined + * $GSD_TOOLS variable. This test catches any command file that + * references the undefined variable. + */ + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd'); + +describe('command files: gsd-tools path references (#1766)', () => { + test('no command file references undefined $GSD_TOOLS variable', () => { + const files = fs.readdirSync(COMMANDS_DIR).filter(f => f.endsWith('.md')); + const violations = []; + + for (const file of files) { + const content = fs.readFileSync(path.join(COMMANDS_DIR, file), 'utf-8'); + // Match $GSD_TOOLS or "$GSD_TOOLS" or ${GSD_TOOLS} used as a path + // (not as a documentation reference) + const lines = content.split('\n'); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (/\$GSD_TOOLS\b/.test(line) && /node\s/.test(line)) { + violations.push(`${file}:${i + 1}: ${line.trim()}`); + } + } + } + + assert.strictEqual(violations.length, 0, + 'Command files must not reference undefined $GSD_TOOLS. ' + + 'Use $HOME/.claude/get-shit-done/bin/gsd-tools.cjs instead.\n' + + 'Violations:\n' + violations.join('\n')); + }); + + test('workstreams.md uses standard gsd-tools.cjs path', () => { + const content = fs.readFileSync( + path.join(COMMANDS_DIR, 'workstreams.md'), 'utf-8' + ); + const nodeLines = content.split('\n').filter(l => /node\s/.test(l)); + + assert.ok(nodeLines.length > 0, + 'workstreams.md should contain node invocations'); + + for (const line of nodeLines) { + assert.ok( + line.includes('gsd-tools.cjs'), + 'Each node invocation must reference gsd-tools.cjs, got: ' + line.trim() + ); + } + }); +});