* chore(#191): migrate gsd-sdk query call sites to gsd-tools query Retiring the gsd-sdk shim. gsd-tools.cjs already accepts `query` as a meta-prefix (gsd-tools query <command>), so this is a behavior-preserving 1:1 swap across the runtime reference prompts, the graphify hook's commit-detection gate, and two bin/lib comment/message references. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#191): remove vestigial gsd-sdk shim code from installer + projection The gsd-sdk shim was already not wired up (no gsd-sdk bin in package.json; buildWindowsShimTriple had zero call sites). Remove the dead code: - shell-command-projection.cjs: buildWindowsShimTriple + formatSdkPathDiagnostic (+ their now-unused PACKAGE_NAME import) and exports - install.js: the re-export wrappers + imports, the #3406 stale-standalone-sdk detection (detectStaleStandaloneSdk/formatStaleStandaloneSdkWarning + its global-install call site), and the exports Preserved (retained, not gsd-sdk): buildCodexHookWindowsShimIR (#3426) — only its comments referenced the gsd-sdk pattern; reworded. Also kept the homePathCoveredByRc 'reopen your shell' branch in maybeSuggestPathExport — its logic is bin-dir-agnostic, only the message mentioned gsd-sdk; reworded to use the actual bin dir. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#191): update tests for retired gsd-sdk shim - bug-3441/bug-3442: drop the formatSdkPathDiagnostic / buildWindowsShimTriple assertions (functions removed); retained PATH-action + drift-guard tests stay - bug-505: remove the 'still exported' assertions for detectStaleStandaloneSdk / formatStaleStandaloneSdkWarning / the shim contract surface (#505 kept them; #191 removes them) - graphify-auto-update: migrate the hook-dispatch inputs gsd-sdk query commit -> gsd-tools query commit to match the migrated commit hook Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#191): point active docs at gsd-tools query (gsd-sdk shim retired) Update the user/agent-facing docs (AGENTS, COMMANDS, CONFIGURATION, USER-GUIDE, ship-pr-body-sections) that presented gsd-sdk query as a current command to gsd-tools query. Historical docs (ADRs, PRDs, release notes) left untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#191): correct state.load vs state.json description for gsd-tools query Adversarial-review (codex) finding: the migrated USER-GUIDE line claimed both 'gsd-tools query state.json' and 'state.load' resolve to the frontmatter-rebuild handler. Verified they don't — state.load returns the CJS load shape (config + state_raw + flags), state.json returns the frontmatter shape. Both are available via gsd-tools query; corrected the text to say so. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#191): add changeset for gsd-sdk shim retirement Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/plucky-pumas-tumble.md
Normal file
5
.changeset/plucky-pumas-tumble.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Removed
|
||||
pr: 522
|
||||
---
|
||||
Retire the gsd-sdk shim/command. GSD now invokes gsd-tools query <command> (behaviorally identical via gsd-tools' query meta-prefix) everywhere; the vestigial gsd-sdk shim builder, installer wiring, and the #3406 stale-standalone-sdk shadow warning are removed.
|
||||
149
bin/install.js
149
bin/install.js
@@ -6,8 +6,6 @@ const os = require('os');
|
||||
const readline = require('readline');
|
||||
const crypto = require('crypto');
|
||||
const {
|
||||
buildWindowsShimTriple: buildWindowsShimTripleFromProjection,
|
||||
formatSdkPathDiagnostic: formatSdkPathDiagnosticFromProjection,
|
||||
isManagedHookBasename,
|
||||
isManagedHookCommand,
|
||||
projectLocalHookPrefix,
|
||||
@@ -1070,8 +1068,8 @@ function reconcileCodexHooksJsonSessionStart(targetDir, opts = {}) {
|
||||
* (a Windows PE binary) via execvp(), which fails with ENOEXEC on Windows PE
|
||||
* binaries that the MSYS layer doesn't know how to fork-exec natively.
|
||||
*
|
||||
* Fix: write a .cmd shim (same IR pattern as buildWindowsShimTriple for
|
||||
* gsd-sdk.cmd) whose content is `@ECHO OFF / @SETLOCAL / @"node.exe" "script.js" %*`.
|
||||
* Fix: write a .cmd shim (using the same CRLF .cmd shim pattern) whose
|
||||
* content is `@ECHO OFF / @SETLOCAL / @"node.exe" "script.js" %*`.
|
||||
* cmd.exe executes
|
||||
* .cmd natively via CreateProcess — no POSIX exec layer, no MSYS shebang
|
||||
* walk, no PE binary fork-exec failure.
|
||||
@@ -1117,9 +1115,8 @@ function buildCodexHookWindowsShimIR(scriptAbsPath, absoluteRunnerToken) {
|
||||
eol: { cmd: '\r\n' }, // CRLF — canonical for cmd.exe .cmd files
|
||||
passthroughArgs: true, // the shim forwards all args via %*
|
||||
render: {
|
||||
// Mirror buildWindowsShimTriple's CRLF line endings for strict
|
||||
// cmd.exe compatibility (LF-only .cmd files work in modern Windows but
|
||||
// CRLF is canonical and what the existing gsd-sdk.cmd triple emits).
|
||||
// Use CRLF line endings for strict cmd.exe compatibility (LF-only
|
||||
// .cmd files work in modern Windows but CRLF is the canonical format).
|
||||
cmd: () => `@ECHO OFF\r\n@SETLOCAL\r\n@${runnerQuoted} ${scriptQuoted} %*\r\n`,
|
||||
},
|
||||
};
|
||||
@@ -8275,45 +8272,6 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
||||
? targetDir.replace(os.homedir(), '~')
|
||||
: targetDir.replace(process.cwd(), '.');
|
||||
|
||||
// #3406: warn if a stale standalone `@opengsd/gsd-sdk` is globally installed
|
||||
// and shadows the `gsd-sdk` shim this installer wires up. Only meaningful
|
||||
// for global installs (the shim collision lives in the global node_modules
|
||||
// bin dir). Guarded by GSD_SKIP_STALE_SDK_CHECK so CI/tests can silence it.
|
||||
// #3406 CR: opt-out only on explicit "1" / "true" / "yes" rather than any
|
||||
// non-empty value. Without this guard `GSD_SKIP_STALE_SDK_CHECK=0` and
|
||||
// `GSD_SKIP_STALE_SDK_CHECK=false` would silently disable the check.
|
||||
const skipRaw = process.env.GSD_SKIP_STALE_SDK_CHECK;
|
||||
const skipStaleCheck = skipRaw === '1' || skipRaw === 'true' || skipRaw === 'yes';
|
||||
if (isGlobal && !skipStaleCheck) {
|
||||
try {
|
||||
const { execFileSync } = require('child_process');
|
||||
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const staleInfo = detectStaleStandaloneSdk(() => {
|
||||
try {
|
||||
return execFileSync(
|
||||
npmCmd,
|
||||
['ls', '-g', '@opengsd/gsd-sdk', '--json', '--depth=0'],
|
||||
{ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 10_000 }
|
||||
);
|
||||
} catch (e) {
|
||||
// `npm ls -g <missing>` exits 1 with the JSON still on stdout when
|
||||
// the package is absent. execFileSync throws on non-zero exit but
|
||||
// attaches stdout to the error. Recover the JSON in that case so
|
||||
// the detector classifies "absent" correctly.
|
||||
if (e && typeof e.stdout !== 'undefined') {
|
||||
return Buffer.isBuffer(e.stdout) ? e.stdout.toString('utf-8') : String(e.stdout);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
if (staleInfo.stale) {
|
||||
console.warn(`\n${yellow}${formatStaleStandaloneSdkWarning(staleInfo)}${reset}\n`);
|
||||
}
|
||||
} catch {
|
||||
// Detection is best-effort; never block install on its failure.
|
||||
}
|
||||
}
|
||||
|
||||
// Path prefix for file references in markdown content (e.g. gsd-tools.cjs).
|
||||
// Replaces $HOME/.claude/ or ~/.claude/ so the result is <pathPrefix>get-shit-done/bin/...
|
||||
// For global installs: use $HOME/ so paths expand correctly inside double-quoted
|
||||
@@ -10673,8 +10631,13 @@ function maybeSuggestPathExport(globalBin, homeDir) {
|
||||
});
|
||||
if (onPath) return;
|
||||
|
||||
// Already added to PATH via an rc file, but the current shell predates that
|
||||
// edit — tell the user to reopen rather than (wrongly) suggesting they add it
|
||||
// again. Applies to whatever bin dir we install into (retained shim-agnostic).
|
||||
if (homePathCoveredByRc(globalBin, homeDir)) {
|
||||
console.log(` ${yellow}⚠${reset} ${bold}gsd-sdk${reset}'s directory is already on your PATH via an rc file entry — try reopening your shell (or ${cyan}source ~/.zshrc${reset}).`);
|
||||
console.log('');
|
||||
console.log(` ${yellow}⚠${reset} ${bold}${globalBin}${reset}'s directory is already on your PATH via an rc file entry — try reopening your shell (or ${cyan}source ~/.zshrc${reset}).`);
|
||||
console.log('');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10692,94 +10655,6 @@ function maybeSuggestPathExport(globalBin, homeDir) {
|
||||
console.log('');
|
||||
}
|
||||
|
||||
/**
|
||||
* #3406 helper: detect a stale globally-installed `@opengsd/gsd-sdk` package
|
||||
* shadowing the `gsd-sdk` shim that `@opengsd/gsd-core` installs.
|
||||
*
|
||||
* Background: `@opengsd/gsd-sdk@0.1.0` was published once and never updated
|
||||
* (the SDK now ships embedded in `@opengsd/gsd-core`). When a user has the
|
||||
* 0.1.0 standalone package installed globally, its `gsd-sdk` bin shadows
|
||||
* the one `@opengsd/gsd-core` provides — and the 0.1.0 binary only knows
|
||||
* `run | auto | init` (no `query`), so every `gsd-sdk query <command>`
|
||||
* call from skills/hooks fails until the user runs
|
||||
* `npm uninstall -g @opengsd/gsd-sdk`.
|
||||
*
|
||||
* Pure function: takes an injected `runNpmLs` executor that returns
|
||||
* `npm ls -g @opengsd/gsd-sdk --json --depth=0` stdout. Returns:
|
||||
* `{ stale: true, version }` when the package is present.
|
||||
* `{ stale: false }` for every other input — including:
|
||||
* - executor throws (npm missing / EACCES / network),
|
||||
* - executor returns null/undefined/non-string,
|
||||
* - stdout is not parseable JSON,
|
||||
* - the JSON has no `.dependencies['@opengsd/gsd-sdk']` field.
|
||||
*
|
||||
* Fail-closed conservative: we'd rather miss a detection than fire a
|
||||
* false-positive warning that confuses users who have a fine install.
|
||||
*/
|
||||
function detectStaleStandaloneSdk(runNpmLs) {
|
||||
if (typeof runNpmLs !== 'function') return { stale: false };
|
||||
let out;
|
||||
try {
|
||||
out = runNpmLs();
|
||||
} catch {
|
||||
return { stale: false };
|
||||
}
|
||||
if (typeof out !== 'string' || out.length === 0) return { stale: false };
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(out);
|
||||
} catch {
|
||||
return { stale: false };
|
||||
}
|
||||
const deps = parsed && typeof parsed === 'object' ? parsed.dependencies : null;
|
||||
if (!deps || typeof deps !== 'object') return { stale: false };
|
||||
const entry = deps['@opengsd/gsd-sdk'];
|
||||
if (!entry || typeof entry !== 'object') return { stale: false };
|
||||
const version = typeof entry.version === 'string' ? entry.version : '(unknown)';
|
||||
// #3406 CR: scope stale detection to the known-bad version (0.1.0). Any
|
||||
// newer @opengsd/gsd-sdk version is an intentional install (or a future
|
||||
// republish) and should not be flagged as a shim shadow. Without this
|
||||
// narrowing, a maintainer's local-link or a legitimate future publish
|
||||
// would trigger a misleading "stale shadow" warning on every install.
|
||||
if (version !== '0.1.0') return { stale: false };
|
||||
return { stale: true, version };
|
||||
}
|
||||
|
||||
/**
|
||||
* #3406 helper: format the install-time warning emitted when
|
||||
* `detectStaleStandaloneSdk` reports a stale shadow. Separated from the
|
||||
* detection so the message contract is testable independently of npm.
|
||||
*/
|
||||
function formatStaleStandaloneSdkWarning(info) {
|
||||
const version = info && info.version ? info.version : '(unknown)';
|
||||
return [
|
||||
'⚠ A stale globally-installed @opengsd/gsd-sdk@' + version + ' is shadowing the',
|
||||
' `gsd-sdk` shim that ' + pkg.name + ' provides. The standalone package',
|
||||
' only knows `run | auto | init` — every `gsd-sdk query <cmd>` call from',
|
||||
' skills and hooks will fail until you remove it.',
|
||||
'',
|
||||
' Remediation:',
|
||||
' npm uninstall -g @opengsd/gsd-sdk',
|
||||
' npx -y ' + pkg.name + '@latest --<runtime> --global',
|
||||
'',
|
||||
' Tracking: #3406 — https://github.com/open-gsd/gsd-core/issues/3406',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
// Projection-contract surfaces (drift guards: tests/bug-3441, tests/bug-3442).
|
||||
// These have no production caller after the #505 dead-SDK-verification removal,
|
||||
// but they are the install.js side of the shell-command projection contract:
|
||||
// the drift-guard tests assert install.js delegates to
|
||||
// shell-command-projection.cjs rather than hand-rolling the projection. Kept
|
||||
// exported so that contract stays verifiable.
|
||||
function buildWindowsShimTriple(shimSrc) {
|
||||
return buildWindowsShimTripleFromProjection(shimSrc);
|
||||
}
|
||||
|
||||
function formatSdkPathDiagnostic({ shimDir, platform, runDir }) {
|
||||
return formatSdkPathDiagnosticFromProjection({ shimDir, platform, runDir });
|
||||
}
|
||||
|
||||
/**
|
||||
* Install GSD for all selected runtimes
|
||||
*/
|
||||
@@ -10931,10 +10806,6 @@ module.exports = {
|
||||
install,
|
||||
installAllRuntimes,
|
||||
uninstall,
|
||||
detectStaleStandaloneSdk,
|
||||
formatStaleStandaloneSdkWarning,
|
||||
buildWindowsShimTriple,
|
||||
formatSdkPathDiagnostic,
|
||||
convertClaudeCommandToCodexSkill,
|
||||
convertClaudeToOpencodeFrontmatter,
|
||||
convertClaudeToKiloFrontmatter,
|
||||
|
||||
@@ -674,11 +674,11 @@ Twelve additional agents ship under `agents/gsd-*.md` and are used by specialty
|
||||
| **Tools** | Read, Write, Bash, Glob, Grep |
|
||||
| **Model (balanced)** | Sonnet |
|
||||
| **Color** | Cyan |
|
||||
| **Produces** | `.planning/intel/*.json` (and companion Markdown) consumed by `gsd-sdk query intel` |
|
||||
| **Produces** | `.planning/intel/*.json` (and companion Markdown) consumed by `gsd-tools query intel` |
|
||||
|
||||
**Key behaviors:**
|
||||
- Writes current state only — no temporal language, every claim references an actual file path
|
||||
- Uses Glob / Read / Grep for cross-platform correctness; Bash is reserved for `gsd-sdk query intel` CLI calls
|
||||
- Uses Glob / Read / Grep for cross-platform correctness; Bash is reserved for `gsd-tools query intel` CLI calls
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -932,7 +932,7 @@ Interactive configuration of workflow toggles and model profile. Questions are g
|
||||
- **Model & Pipeline** — Model Profile, Auto-Advance, Branching
|
||||
- **Misc** — Context Warnings, Research Qs
|
||||
|
||||
All answers are merged via `gsd-sdk query config-set` into the resolved project config path (`.planning/config.json` for a standard install, or `.planning/workstreams/<active>/config.json` when a workstream is active), preserving unrelated keys. After confirmation, the user may save the full settings object to `~/.gsd/defaults.json` so future `/gsd-new-project` runs start from the same baseline.
|
||||
All answers are merged via `gsd-tools query config-set` into the resolved project config path (`.planning/config.json` for a standard install, or `.planning/workstreams/<active>/config.json` when a workstream is active), preserving unrelated keys. After confirmation, the user may save the full settings object to `~/.gsd/defaults.json` so future `/gsd-new-project` runs start from the same baseline.
|
||||
|
||||
```bash
|
||||
/gsd-settings # Interactive config
|
||||
@@ -960,7 +960,7 @@ Configure GSD settings interactively — workflow toggles, advanced knobs, integ
|
||||
| Git Customization | `git.base_branch`, `git.phase_branch_template`, `git.milestone_branch_template` |
|
||||
| Runtime / Output | `response_language`, `context_window`, `search_gitignored`, `graphify.build_timeout` |
|
||||
|
||||
All answers merge via `gsd-sdk query config-set`, preserving unrelated keys. API keys are masked (`****<last-4>`) in all output.
|
||||
All answers merge via `gsd-tools query config-set`, preserving unrelated keys. API keys are masked (`****<last-4>`) in all output.
|
||||
|
||||
```bash
|
||||
/gsd-config # Common-case interactive config
|
||||
|
||||
@@ -344,7 +344,7 @@ Example:
|
||||
|
||||
### Project-Root Resolution in Multi-Repo Workspaces
|
||||
|
||||
When `sub_repos` is set and `gsd-tools.cjs` or `gsd-sdk query` is invoked from inside a listed child repo, both CLIs walk up to the parent workspace that owns `.planning/` before dispatching handlers. Resolution order (checked at each ancestor up to 10 levels, never above `$HOME`):
|
||||
When `sub_repos` is set and `gsd-tools.cjs` or `gsd-tools query` is invoked from inside a listed child repo, both CLIs walk up to the parent workspace that owns `.planning/` before dispatching handlers. Resolution order (checked at each ancestor up to 10 levels, never above `$HOME`):
|
||||
|
||||
1. If the starting directory already has its own `.planning/`, it is the project root (no walk-up).
|
||||
2. Parent has `.planning/config.json` listing the starting directory's top-level segment in `sub_repos` (or the legacy `planning.sub_repos` shape).
|
||||
@@ -424,7 +424,7 @@ Any GSD agent type can receive skills. Common types:
|
||||
|
||||
### How It Works
|
||||
|
||||
At spawn time, workflows call `gsd-sdk query agent-skills <type>` (or legacy `node gsd-tools.cjs agent-skills <type>`) to load configured skills. If skills exist for the agent type, they are injected as an `<agent_skills>` block in the Task() prompt:
|
||||
At spawn time, workflows call `gsd-tools query agent-skills <type>` (or legacy `node gsd-tools.cjs agent-skills <type>`) to load configured skills. If skills exist for the agent type, they are injected as an `<agent_skills>` block in the Task() prompt:
|
||||
|
||||
```xml
|
||||
<agent_skills>
|
||||
@@ -441,7 +441,7 @@ If no skills are configured, the block is omitted (zero overhead).
|
||||
Set skills via the CLI:
|
||||
|
||||
```bash
|
||||
gsd-sdk query config-set agent_skills.gsd-executor '["skills/my-skill"]'
|
||||
gsd-tools query config-set agent_skills.gsd-executor '["skills/my-skill"]'
|
||||
```
|
||||
|
||||
---
|
||||
@@ -512,10 +512,10 @@ architecture questions.
|
||||
|
||||
```bash
|
||||
# Enable a feature
|
||||
gsd-sdk query config-set features.global_learnings true
|
||||
gsd-tools query config-set features.global_learnings true
|
||||
|
||||
# Disable a feature
|
||||
gsd-sdk query config-set features.thinking_partner false
|
||||
gsd-tools query config-set features.thinking_partner false
|
||||
```
|
||||
|
||||
The `features.*` namespace is a dynamic key pattern — new feature flags can be added without modifying `VALID_CONFIG_KEYS`. Any key matching `features.<name>` is accepted by the config system.
|
||||
|
||||
@@ -1095,11 +1095,11 @@ Each workspace gets:
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Programmatic CLI (`gsd-sdk query` vs `gsd-tools.cjs`)
|
||||
### Programmatic CLI (`gsd-tools query` vs `gsd-tools.cjs`)
|
||||
|
||||
For automation and copy-paste from docs, prefer **`gsd-sdk query`** with a registered subcommand (see [CLI-TOOLS.md — SDK and programmatic access](CLI-TOOLS.md#sdk-and-programmatic-access) and [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md)). The legacy `node $HOME/.claude/get-shit-done/bin/gsd-tools.cjs` CLI remains supported for dual-mode operation.
|
||||
For automation and copy-paste from docs, prefer **`gsd-tools query`** with a registered subcommand (see [CLI-TOOLS.md — SDK and programmatic access](CLI-TOOLS.md#sdk-and-programmatic-access) and [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md)). The legacy `node $HOME/.claude/get-shit-done/bin/gsd-tools.cjs` CLI remains supported for dual-mode operation.
|
||||
|
||||
**CLI-only (not in the query registry):** **graphify**, **from-gsd2** / **gsd2-import** — call `gsd-tools.cjs` (see [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md)). **Two different `state` JSON shapes in the legacy CLI:** `state json` (frontmatter rebuild) vs `state load` (`config` + `state_raw` + flags). **`gsd-sdk query` today:** both `state.json` and `state.load` resolve to the frontmatter-rebuild handler — use `node …/gsd-tools.cjs state load` when you need the CJS `state load` shape. See [CLI-TOOLS.md](CLI-TOOLS.md#sdk-and-programmatic-access) and QUERY-HANDLERS.
|
||||
**CLI-only (not in the query registry):** **graphify**, **from-gsd2** / **gsd2-import** — call `gsd-tools.cjs` (see [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md)). **Two distinct `state` JSON shapes, both available via `gsd-tools query`:** `state.json` (frontmatter rebuild) vs `state.load` (`config` + `state_raw` + flags) — they resolve to different handlers, so pick the one whose shape you need. The legacy `gsd-tools.cjs state json` / `state load` forms produce the same two shapes. See [CLI-TOOLS.md](CLI-TOOLS.md#sdk-and-programmatic-access) and QUERY-HANDLERS.
|
||||
|
||||
### STATE.md Out of Sync
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@ Selected sections are written with `"enabled": true`. Seeded but unselected sect
|
||||
|
||||
## Configure Sections Manually
|
||||
|
||||
Set `ship.pr_body_sections` with `gsd-sdk query config-set`:
|
||||
Set `ship.pr_body_sections` with `gsd-tools query config-set`:
|
||||
|
||||
```bash
|
||||
gsd-sdk query config-set ship.pr_body_sections '[{"heading":"Risks & Dependencies","enabled":true,"source":"PLAN.md ## Risks || PLAN.md ## Dependencies","fallback":"- No known high-risk rollout dependencies."}]'
|
||||
gsd-tools query config-set ship.pr_body_sections '[{"heading":"Risks & Dependencies","enabled":true,"source":"PLAN.md ## Risks || PLAN.md ## Dependencies","fallback":"- No known high-risk rollout dependencies."}]'
|
||||
```
|
||||
|
||||
You can also edit `.planning/config.json` directly:
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* Parse code-review flags from an argv array.
|
||||
*
|
||||
* The first positional argument (phase number) is ignored by this function —
|
||||
* phase validation is handled by `gsd-sdk query init.phase-op`.
|
||||
* phase validation is handled by `gsd-tools query init.phase-op`.
|
||||
*
|
||||
* @param {string[]} argv - Array of argument strings, e.g. ['2', '--fix', '--all']
|
||||
* @returns {CodeReviewFlags}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { PACKAGE_NAME } = require('./package-identity.cjs');
|
||||
// Use non-destructured access so test-time mock.method(childProcess, 'spawnSync')
|
||||
// can intercept calls from this seam — destructured imports capture references
|
||||
// at load time and become un-mockable.
|
||||
@@ -311,56 +310,6 @@ function projectPersistentPathExportActions({ targetDir, platform = process.plat
|
||||
return { shellActions: projected.shellActions };
|
||||
}
|
||||
|
||||
function buildWindowsShimTriple(shimSrc) {
|
||||
const shimAbs = path.resolve(shimSrc);
|
||||
const shimQuoted = JSON.stringify(shimAbs);
|
||||
const invocation = {
|
||||
interpreter: 'node',
|
||||
target: shimAbs,
|
||||
};
|
||||
const renderCmd = () =>
|
||||
'@ECHO OFF\r\n@SETLOCAL\r\n@node ' + shimQuoted + ' %*\r\n';
|
||||
const renderPs1 = () =>
|
||||
'#!/usr/bin/env pwsh\n& node ' + shimQuoted + ' $args\nexit $LASTEXITCODE\n';
|
||||
const renderSh = () =>
|
||||
'#!/usr/bin/env sh\nexec node ' + shimQuoted + ' "$@"\n';
|
||||
return {
|
||||
invocation,
|
||||
eol: { cmd: '\r\n', ps1: '\n', sh: '\n' },
|
||||
fileNames: { cmd: 'gsd-sdk.cmd', ps1: 'gsd-sdk.ps1', sh: 'gsd-sdk' },
|
||||
render: { cmd: renderCmd, ps1: renderPs1, sh: renderSh },
|
||||
};
|
||||
}
|
||||
|
||||
function formatSdkPathDiagnostic({ shimDir, platform, runDir }) {
|
||||
const isWin32 = platform === 'win32';
|
||||
const isNpx = typeof runDir === 'string' &&
|
||||
(runDir.includes('/_npx/') || runDir.includes('\\_npx\\'));
|
||||
const shimLocationLine = shimDir ? `Shim written to: ${shimDir}` : '';
|
||||
const actionLines = [];
|
||||
let shellActions = [];
|
||||
if (shimDir) {
|
||||
const projected = projectPathActionProjection({
|
||||
mode: 'repair',
|
||||
targetDir: shimDir,
|
||||
platform,
|
||||
});
|
||||
shellActions = projected.shellActions;
|
||||
actionLines.push('Add that directory to your PATH and restart your shell.');
|
||||
actionLines.push(...projected.actionLines);
|
||||
} else {
|
||||
actionLines.push('Could not locate a writable PATH directory to install the shim.');
|
||||
actionLines.push('Install globally to materialize the bin symlink:');
|
||||
actionLines.push(`npm install -g ${PACKAGE_NAME}`);
|
||||
}
|
||||
const npxNoteLines = isNpx
|
||||
? [
|
||||
"Note: you're running via npx. For a persistent shim,",
|
||||
`install globally instead: npm install -g ${PACKAGE_NAME}`,
|
||||
]
|
||||
: [];
|
||||
return { shimLocationLine, actionLines, shellActions, npxNoteLines, isNpx, isWin32 };
|
||||
}
|
||||
|
||||
// ─── Subprocess dispatch ──────────────────────────────────────────────────────
|
||||
|
||||
@@ -540,8 +489,6 @@ module.exports = {
|
||||
projectPathActionProjection,
|
||||
renderShellActionLines,
|
||||
projectPersistentPathExportActions,
|
||||
buildWindowsShimTriple,
|
||||
formatSdkPathDiagnostic,
|
||||
execGit,
|
||||
execNpm,
|
||||
execTool,
|
||||
|
||||
@@ -31,7 +31,7 @@ function routeStateCommand({ state, args, cwd, raw, error }) {
|
||||
subcommands: ['load', 'complete-phase', ...STATE_SUBCOMMANDS.filter((s) => s !== 'load')],
|
||||
defaultSubcommand: 'load',
|
||||
unsupported: {
|
||||
'add-roadmap-evolution': 'state add-roadmap-evolution is SDK-only. Use: gsd-sdk query state.add-roadmap-evolution ...',
|
||||
'add-roadmap-evolution': 'state add-roadmap-evolution is SDK-only. Use: gsd-tools query state.add-roadmap-evolution ...',
|
||||
},
|
||||
error,
|
||||
cwd,
|
||||
|
||||
@@ -5,7 +5,7 @@ Smart discuss is the autonomous-optimized variant of `gsd-discuss-phase`. It pro
|
||||
**Inputs:** `PHASE_NUM` from execute_phase. Run init to get phase paths:
|
||||
|
||||
```bash
|
||||
PHASE_STATE=$(gsd-sdk query init.phase-op ${PHASE_NUM})
|
||||
PHASE_STATE=$(gsd-tools query init.phase-op ${PHASE_NUM})
|
||||
```
|
||||
|
||||
Parse from JSON: `phase_dir`, `phase_slug`, `padded_phase`, `phase_name`.
|
||||
@@ -94,7 +94,7 @@ Read the 3-5 most relevant files to understand existing patterns.
|
||||
**Get phase details:**
|
||||
|
||||
```bash
|
||||
DETAIL=$(gsd-sdk query roadmap.get-phase ${PHASE_NUM})
|
||||
DETAIL=$(gsd-tools query roadmap.get-phase ${PHASE_NUM})
|
||||
```
|
||||
|
||||
Extract `goal`, `requirements`, `success_criteria` from the JSON response.
|
||||
@@ -266,7 +266,7 @@ Write the file.
|
||||
**Commit:**
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "docs(${PADDED_PHASE}): smart discuss context" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
|
||||
gsd-tools query commit "docs(${PADDED_PHASE}): smart discuss context" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
|
||||
```
|
||||
|
||||
Display confirmation:
|
||||
|
||||
@@ -6,7 +6,7 @@ Calculate the next decimal phase number for urgent insertions.
|
||||
|
||||
```bash
|
||||
# Get next decimal phase after phase 6
|
||||
gsd-sdk query phase.next-decimal 6
|
||||
gsd-tools query phase.next-decimal 6
|
||||
```
|
||||
|
||||
Output:
|
||||
@@ -32,13 +32,13 @@ With existing decimals:
|
||||
## Extract Values
|
||||
|
||||
```bash
|
||||
DECIMAL_PHASE=$(gsd-sdk query phase.next-decimal "${AFTER_PHASE}" --pick next)
|
||||
BASE_PHASE=$(gsd-sdk query phase.next-decimal "${AFTER_PHASE}" --pick base_phase)
|
||||
DECIMAL_PHASE=$(gsd-tools query phase.next-decimal "${AFTER_PHASE}" --pick next)
|
||||
BASE_PHASE=$(gsd-tools query phase.next-decimal "${AFTER_PHASE}" --pick base_phase)
|
||||
```
|
||||
|
||||
Or with --raw flag:
|
||||
```bash
|
||||
DECIMAL_PHASE=$(gsd-sdk query phase.next-decimal "${AFTER_PHASE}" --raw)
|
||||
DECIMAL_PHASE=$(gsd-tools query phase.next-decimal "${AFTER_PHASE}" --raw)
|
||||
# Returns just: 06.1
|
||||
```
|
||||
|
||||
@@ -56,7 +56,7 @@ DECIMAL_PHASE=$(gsd-sdk query phase.next-decimal "${AFTER_PHASE}" --raw)
|
||||
Decimal phase directories use the full decimal number:
|
||||
|
||||
```bash
|
||||
SLUG=$(gsd-sdk query generate-slug "$DESCRIPTION" --raw)
|
||||
SLUG=$(gsd-tools query generate-slug "$DESCRIPTION" --raw)
|
||||
PHASE_DIR=".planning/phases/${DECIMAL_PHASE}-${SLUG}"
|
||||
mkdir -p "$PHASE_DIR"
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Phases:
|
||||
What to commit:
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "docs: initialize [project-name] ([N] phases)" --files .planning/
|
||||
gsd-tools query commit "docs: initialize [project-name] ([N] phases)" --files .planning/
|
||||
```
|
||||
|
||||
</format>
|
||||
@@ -136,7 +136,7 @@ SUMMARY: .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md
|
||||
What to commit:
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-PLAN.md .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md
|
||||
gsd-tools query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-PLAN.md .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md
|
||||
```
|
||||
|
||||
**Note:** Code files NOT included - already committed per-task.
|
||||
@@ -156,7 +156,7 @@ Current: [task name]
|
||||
What to commit:
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "wip: [phase-name] paused at task [X]/[Y]" --files .planning/
|
||||
gsd-tools query commit "wip: [phase-name] paused at task [X]/[Y]" --files .planning/
|
||||
```
|
||||
|
||||
</format>
|
||||
@@ -287,7 +287,7 @@ Set `commit_docs: false` so planning docs stay local and are not committed to an
|
||||
Instead of the standard `commit` command, use `commit-to-subrepo` when `sub_repos` is configured:
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit-to-subrepo "feat(02-01): add user API" \
|
||||
gsd-tools query commit-to-subrepo "feat(02-01): add user API" \
|
||||
--files backend/src/api/users.ts backend/src/types/user.ts frontend/src/components/UserForm.tsx
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Git Planning Commit
|
||||
|
||||
Commit planning artifacts via `gsd-sdk query commit`, which checks `commit_docs` config and gitignore status (same behavior as legacy `gsd-tools.cjs commit`).
|
||||
Commit planning artifacts via `gsd-tools query commit`, which checks `commit_docs` config and gitignore status (same behavior as legacy `gsd-tools.cjs commit`).
|
||||
|
||||
## Commit via CLI
|
||||
|
||||
@@ -9,7 +9,7 @@ Pass the message first, then file paths via `--files`. Both `commit` and `commit
|
||||
Always use this for `.planning/` files — it handles `commit_docs` and gitignore checks automatically:
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "docs({scope}): {description}" --files .planning/STATE.md .planning/ROADMAP.md
|
||||
gsd-tools query commit "docs({scope}): {description}" --files .planning/STATE.md .planning/ROADMAP.md
|
||||
```
|
||||
|
||||
The CLI will return `skipped` (with reason) if `commit_docs` is `false` or `.planning/` is gitignored. No manual conditional checks needed.
|
||||
@@ -19,7 +19,7 @@ The CLI will return `skipped` (with reason) if `commit_docs` is `false` or `.pla
|
||||
To fold `.planning/` file changes into the previous commit:
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "" --files .planning/codebase/*.md --amend
|
||||
gsd-tools query commit "" --files .planning/codebase/*.md --amend
|
||||
```
|
||||
|
||||
## Commit Message Patterns
|
||||
|
||||
@@ -14,7 +14,7 @@ From `$ARGUMENTS`:
|
||||
The `find-phase` command handles normalization and validation in one step:
|
||||
|
||||
```bash
|
||||
PHASE_INFO=$(gsd-sdk query find-phase "${PHASE}")
|
||||
PHASE_INFO=$(gsd-tools query find-phase "${PHASE}")
|
||||
```
|
||||
|
||||
Returns JSON with:
|
||||
@@ -45,7 +45,7 @@ fi
|
||||
Use `roadmap get-phase` to validate phase exists:
|
||||
|
||||
```bash
|
||||
PHASE_CHECK=$(gsd-sdk query roadmap.get-phase "${PHASE}" --pick found)
|
||||
PHASE_CHECK=$(gsd-tools query roadmap.get-phase "${PHASE}" --pick found)
|
||||
if [ "$PHASE_CHECK" = "false" ]; then
|
||||
echo "ERROR: Phase ${PHASE} not found in roadmap"
|
||||
exit 1
|
||||
@@ -57,5 +57,5 @@ fi
|
||||
Use `find-phase` for directory lookup:
|
||||
|
||||
```bash
|
||||
PHASE_DIR=$(gsd-sdk query find-phase "${PHASE}" --raw)
|
||||
PHASE_DIR=$(gsd-tools query find-phase "${PHASE}" --raw)
|
||||
```
|
||||
|
||||
@@ -55,7 +55,7 @@ Group by plan, dimension, severity.
|
||||
### Step 6: Commit
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "fix($PHASE): revise plans based on checker feedback" --files .planning/phases/$PHASE-*/$PHASE-*-PLAN.md
|
||||
gsd-tools query commit "fix($PHASE): revise plans based on checker feedback" --files .planning/phases/$PHASE-*/$PHASE-*-PLAN.md
|
||||
```
|
||||
|
||||
### Step 7: Return Revision Summary
|
||||
|
||||
@@ -55,19 +55,19 @@ Configuration options for `.planning/` directory behavior.
|
||||
- User must add `.planning/` to `.gitignore`
|
||||
- Useful for: OSS contributions, client projects, keeping planning private
|
||||
|
||||
**Using `gsd-sdk query` (preferred):**
|
||||
**Using `gsd-tools query` (preferred):**
|
||||
|
||||
```bash
|
||||
# Commit with automatic commit_docs + gitignore checks:
|
||||
gsd-sdk query commit "docs: update state" --files .planning/STATE.md
|
||||
gsd-tools query commit "docs: update state" --files .planning/STATE.md
|
||||
|
||||
# Load config via state load (returns JSON):
|
||||
INIT=$(gsd-sdk query state.load)
|
||||
INIT=$(gsd-tools query state.load)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# commit_docs is available in the JSON output
|
||||
|
||||
# Or use init commands which include commit_docs:
|
||||
INIT=$(gsd-sdk query init.execute-phase "1")
|
||||
INIT=$(gsd-tools query init.execute-phase "1")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# commit_docs is included in all init command outputs
|
||||
```
|
||||
@@ -77,7 +77,7 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
**Commit via CLI (handles checks automatically):**
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "docs: update state" --files .planning/STATE.md
|
||||
gsd-tools query commit "docs: update state" --files .planning/STATE.md
|
||||
```
|
||||
|
||||
The CLI checks `commit_docs` config and gitignore status internally — no manual conditionals needed.
|
||||
@@ -165,14 +165,14 @@ To use uncommitted mode:
|
||||
|
||||
Use `init execute-phase` which returns all config as JSON:
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.execute-phase "1")
|
||||
INIT=$(gsd-tools query init.execute-phase "1")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# JSON output includes: branching_strategy, phase_branch_template, milestone_branch_template
|
||||
```
|
||||
|
||||
Or use `state load` for the config values:
|
||||
```bash
|
||||
INIT=$(gsd-sdk query state.load)
|
||||
INIT=$(gsd-tools query state.load)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# Parse branching_strategy, phase_branch_template, milestone_branch_template from JSON
|
||||
```
|
||||
|
||||
@@ -34,7 +34,7 @@ Reference: `references/questioning.md` for the full anti-pattern list.
|
||||
|
||||
## State Management Anti-Patterns
|
||||
|
||||
15. **No direct Write/Edit to STATE.md or ROADMAP.md for mutations.** Always use `gsd-sdk query` for registered state/roadmap handlers (e.g. `state.update`, `state.advance-plan`, `roadmap.update-plan-progress`), or legacy `node …/gsd-tools.cjs` for CLI-only commands. Direct Write tool usage bypasses safe update logic and is unsafe in multi-session environments. Exception: first-time creation of STATE.md from template is allowed.
|
||||
15. **No direct Write/Edit to STATE.md or ROADMAP.md for mutations.** Always use `gsd-tools query` for registered state/roadmap handlers (e.g. `state.update`, `state.advance-plan`, `roadmap.update-plan-progress`), or legacy `node …/gsd-tools.cjs` for CLI-only commands. Direct Write tool usage bypasses safe update logic and is unsafe in multi-session environments. Exception: first-time creation of STATE.md from template is allowed.
|
||||
|
||||
## Behavioral Rules
|
||||
|
||||
@@ -53,7 +53,7 @@ Reference: `references/questioning.md` for the full anti-pattern list.
|
||||
## GSD-Specific Rules
|
||||
|
||||
24. **Do not** check for `mode === 'auto'` or `mode === 'autonomous'` -- GSD uses `yolo` config flag. Check `yolo: true` for autonomous mode, absence or `false` for interactive mode.
|
||||
25. **Prefer `gsd-sdk query`** for orchestration when a handler exists; when shelling out to the legacy CLI, use **`gsd-tools.cjs`** (not `gsd-tools.js` or any other filename) — GSD ships the programmatic API as CommonJS for Node.js CLI compatibility.
|
||||
25. **Prefer `gsd-tools query`** for orchestration when a handler exists; when shelling out to the legacy CLI, use **`gsd-tools.cjs`** (not `gsd-tools.js` or any other filename) — GSD ships the programmatic API as CommonJS for Node.js CLI compatibility.
|
||||
26. **Plan files MUST follow `{padded_phase}-{NN}-PLAN.md` pattern** (e.g., `01-01-PLAN.md`). Never use `PLAN-01.md`, `plan-01.md`, or any other variation -- gsd-tools detection depends on this exact pattern.
|
||||
27. **Do not start executing the next plan before writing the SUMMARY.md for the current plan** -- downstream plans may reference it via `@` includes.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ The user-flow form mirrors what a real user does: open, fill, click, see. No HTT
|
||||
## When this framing applies
|
||||
|
||||
The framing fires when:
|
||||
- The phase under verification has `**Mode:** mvp` in ROADMAP.md (parsed via `gsd-sdk query roadmap.get-phase --pick mode`).
|
||||
- The phase under verification has `**Mode:** mvp` in ROADMAP.md (parsed via `gsd-tools query roadmap.get-phase --pick mode`).
|
||||
- AND the phase has a user-story-formatted goal (set by `/gsd mvp-phase` per Phase 2): "As a [user role], I want to [capability], so that [outcome]."
|
||||
|
||||
If the phase has `mode: mvp` but the goal is NOT in user-story format, the verifier surfaces this as a discrepancy and asks the user to run `/gsd mvp-phase` to reformat the goal — same pattern as the planner agent under MVP_MODE (per `references/planner-mvp-mode.md`).
|
||||
|
||||
@@ -93,19 +93,19 @@ This ensures workstream scope chains automatically through the workflow:
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
# All gsd-sdk query commands accept --ws
|
||||
gsd-sdk query state.json --ws feature-a
|
||||
gsd-sdk query find-phase 3 --ws feature-b
|
||||
# All gsd-tools query commands accept --ws
|
||||
gsd-tools query state.json --ws feature-a
|
||||
gsd-tools query find-phase 3 --ws feature-b
|
||||
|
||||
# Session-local switching without --ws on every command
|
||||
GSD_SESSION_KEY=my-terminal-a gsd-sdk query workstream.set feature-a
|
||||
GSD_SESSION_KEY=my-terminal-a gsd-sdk query state.json
|
||||
GSD_SESSION_KEY=my-terminal-b gsd-sdk query workstream.set feature-b
|
||||
GSD_SESSION_KEY=my-terminal-b gsd-sdk query state.json
|
||||
GSD_SESSION_KEY=my-terminal-a gsd-tools query workstream.set feature-a
|
||||
GSD_SESSION_KEY=my-terminal-a gsd-tools query state.json
|
||||
GSD_SESSION_KEY=my-terminal-b gsd-tools query workstream.set feature-b
|
||||
GSD_SESSION_KEY=my-terminal-b gsd-tools query state.json
|
||||
|
||||
# Workstream CRUD
|
||||
gsd-sdk query workstream.create <name>
|
||||
gsd-sdk query workstream.list
|
||||
gsd-sdk query workstream.status <name>
|
||||
gsd-sdk query workstream.complete <name>
|
||||
gsd-tools query workstream.create <name>
|
||||
gsd-tools query workstream.list
|
||||
gsd-tools query workstream.status <name>
|
||||
gsd-tools query workstream.complete <name>
|
||||
```
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# Gates (in fast-fail order — each shaves work off the common non-dispatch path):
|
||||
# 1. Stdin payload present and tool_name == "Bash"
|
||||
# 2. tool_input.command matches a HEAD-advancing git op (shell-direct or
|
||||
# the exact `gsd-sdk query commit` command shape; the SDK command invokes
|
||||
# the exact `gsd-tools query commit` command shape; the SDK command invokes
|
||||
# git internally, so the literal "git commit" substring never appears —
|
||||
# see #3653)
|
||||
# 3. $CI is unset/empty
|
||||
@@ -49,10 +49,10 @@ COMMAND=$(printf '%s\n' "$TOOL_INFO" | sed -n '2p')
|
||||
|
||||
[ "$TOOL_NAME" = "Bash" ] || exit 0
|
||||
|
||||
# Gate 2 — HEAD-advancing git op (shell-direct or exact `gsd-sdk query commit`)
|
||||
# Gate 2 — HEAD-advancing git op (shell-direct or exact `gsd-tools query commit`)
|
||||
case "$COMMAND" in
|
||||
*"git commit"*|*"git merge"*|*"git pull"*|*"git rebase --continue"*|*"git cherry-pick"*) ;;
|
||||
*"gsd-sdk query commit"|*"gsd-sdk query commit "*) ;;
|
||||
*"gsd-tools query commit"|*"gsd-tools query commit "*) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -32,23 +32,8 @@ describe('bug #3441: PATH guidance is projected from typed shell action IR', ()
|
||||
assert.equal(typeof projection.projectPathActionProjection, 'function');
|
||||
});
|
||||
|
||||
test('formatSdkPathDiagnostic exposes structured shellActions alongside rendered actionLines', () => {
|
||||
const ir = install.formatSdkPathDiagnostic({
|
||||
shimDir: 'C:\\Users\\me\\AppData\\Roaming\\npm',
|
||||
platform: 'win32',
|
||||
runDir: 'C:\\some\\path',
|
||||
});
|
||||
|
||||
assert.ok(Array.isArray(ir.shellActions), 'shellActions must be an array');
|
||||
assert.ok(ir.shellActions.length >= 3, `expected 3+ shell actions, got ${ir.shellActions.length}`);
|
||||
assert.equal(ir.shellActions[0].label, 'PowerShell');
|
||||
assert.equal(typeof ir.shellActions[0].command, 'string');
|
||||
assert.equal(
|
||||
ir.actionLines.some((line) => line.startsWith('PowerShell:')),
|
||||
true,
|
||||
`rendered action lines should include shell labels: ${JSON.stringify(ir.actionLines)}`,
|
||||
);
|
||||
});
|
||||
// (formatSdkPathDiagnostic removed with the gsd-sdk shim, #191 — the PATH
|
||||
// action projection it wrapped is still covered by the tests below.)
|
||||
|
||||
test('persistent PATH export guidance is projected via the same seam', () => {
|
||||
const posix = projection.projectPathActionProjection({
|
||||
|
||||
@@ -10,8 +10,6 @@ const path = require('node:path');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const INSTALL = require(path.join(ROOT, 'bin', 'install.js'));
|
||||
const PROJECTION = require(path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'shell-command-projection.cjs'));
|
||||
const DRIFT_LINT = path.join(ROOT, 'scripts', 'lint-shell-command-projection-drift.cjs');
|
||||
|
||||
function runLint(targetFile) {
|
||||
@@ -21,19 +19,8 @@ function runLint(targetFile) {
|
||||
});
|
||||
}
|
||||
|
||||
describe('bug #3442: shim/wrapper projection seam', () => {
|
||||
test('buildWindowsShimTriple matches shared projection output', () => {
|
||||
const shimSrc = path.join(ROOT, 'bin', 'gsd-sdk.js');
|
||||
const fromInstall = INSTALL.buildWindowsShimTriple(shimSrc);
|
||||
const fromProjection = PROJECTION.buildWindowsShimTriple(shimSrc);
|
||||
assert.deepEqual(fromInstall.invocation, fromProjection.invocation);
|
||||
assert.deepEqual(fromInstall.eol, fromProjection.eol);
|
||||
assert.deepEqual(fromInstall.fileNames, fromProjection.fileNames);
|
||||
assert.equal(fromInstall.render.cmd(), fromProjection.render.cmd());
|
||||
assert.equal(fromInstall.render.ps1(), fromProjection.render.ps1());
|
||||
assert.equal(fromInstall.render.sh(), fromProjection.render.sh());
|
||||
});
|
||||
});
|
||||
// (The buildWindowsShimTriple parity test was removed with the gsd-sdk shim,
|
||||
// #191. The serialized-command drift guard below is retained and unaffected.)
|
||||
|
||||
describe('bug #3442: shim/wrapper serialized-command drift guard', () => {
|
||||
test('drift guard passes for current install.js', () => {
|
||||
|
||||
@@ -54,33 +54,10 @@ describe('bug #505: dead SDK verification subsystem removed from bin/install.js'
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Live symbols — MUST still be exported as functions
|
||||
// The stale-standalone-SDK helpers (detectStaleStandaloneSdk,
|
||||
// formatStaleStandaloneSdkWarning) and the gsd-sdk shim contract surface
|
||||
// (buildWindowsShimTriple, formatSdkPathDiagnostic) that #505 kept were
|
||||
// removed when the gsd-sdk shim itself was retired (#191). Their absence is
|
||||
// covered by the dead-symbol assertions above.
|
||||
// ----------------------------------------------------------------
|
||||
test('live function detectStaleStandaloneSdk is still exported', () => {
|
||||
assert.equal(
|
||||
typeof inst.detectStaleStandaloneSdk,
|
||||
'function',
|
||||
'detectStaleStandaloneSdk handles real stale-SDK condition (#3406) and must not be removed',
|
||||
);
|
||||
});
|
||||
|
||||
test('live function formatStaleStandaloneSdkWarning is still exported', () => {
|
||||
assert.equal(
|
||||
typeof inst.formatStaleStandaloneSdkWarning,
|
||||
'function',
|
||||
'formatStaleStandaloneSdkWarning handles real stale-SDK condition (#3406) and must not be removed',
|
||||
);
|
||||
});
|
||||
|
||||
// buildWindowsShimTriple / formatSdkPathDiagnostic have no production caller
|
||||
// after this removal, but they are the install.js side of a projection-contract
|
||||
// drift guard (tests/bug-3441, tests/bug-3442) that assert install.js delegates
|
||||
// to shell-command-projection.cjs. They are deliberately kept exported.
|
||||
test('contract surface buildWindowsShimTriple is still exported', () => {
|
||||
assert.equal(typeof inst.buildWindowsShimTriple, 'function');
|
||||
});
|
||||
|
||||
test('contract surface formatSdkPathDiagnostic is still exported', () => {
|
||||
assert.equal(typeof inst.formatSdkPathDiagnostic, 'function');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -571,11 +571,11 @@ describe('auto-update', () => {
|
||||
'git pull --ff-only',
|
||||
'git rebase --continue',
|
||||
'git cherry-pick abc123',
|
||||
// #3653 — `gsd-sdk query commit` invokes git via spawnSync('git', [...]),
|
||||
// #3653 — `gsd-tools query commit` invokes git via spawnSync('git', [...]),
|
||||
// so the substring "git commit" never appears in tool_input.command.
|
||||
// The hook must match the user-facing SDK invocation directly.
|
||||
'gsd-sdk query commit "docs: probe" --files .planning/STATE.md',
|
||||
'npx gsd-sdk query commit "docs: probe" --files .planning/STATE.md',
|
||||
'gsd-tools query commit "docs: probe" --files .planning/STATE.md',
|
||||
'npx gsd-tools query commit "docs: probe" --files .planning/STATE.md',
|
||||
]) {
|
||||
test(`dispatches on: ${cmd}`, (t) => {
|
||||
const tmpDir = createTempGitRepo({
|
||||
@@ -610,7 +610,7 @@ describe('auto-update', () => {
|
||||
{
|
||||
tool_name: 'Bash',
|
||||
tool_input: {
|
||||
command: 'gsd-sdk query commit-to-subrepo "msg" --files packages/foo',
|
||||
command: 'gsd-tools query commit-to-subrepo "msg" --files packages/foo',
|
||||
},
|
||||
},
|
||||
{ pathPrepend: mockBin },
|
||||
@@ -623,13 +623,13 @@ describe('auto-update', () => {
|
||||
});
|
||||
|
||||
// #3653 — only the SDK `commit` verb invokes git internally. Other
|
||||
// `gsd-sdk query` verbs (phase.complete, roadmap.update-plan-progress,
|
||||
// `gsd-tools query` verbs (phase.complete, roadmap.update-plan-progress,
|
||||
// state.begin-phase) mutate .md files but do NOT advance HEAD; matching
|
||||
// them would cause a spurious rebuild per state mutation.
|
||||
for (const cmd of [
|
||||
'gsd-sdk query phase.complete 109',
|
||||
'gsd-sdk query roadmap.update-plan-progress 109 W001',
|
||||
'gsd-sdk query state.begin-phase 110',
|
||||
'gsd-tools query phase.complete 109',
|
||||
'gsd-tools query roadmap.update-plan-progress 109 W001',
|
||||
'gsd-tools query state.begin-phase 110',
|
||||
]) {
|
||||
test(`does NOT dispatch on non-HEAD-advancing SDK verb: ${cmd}`, (t) => {
|
||||
const tmpDir = createTempGitRepo({
|
||||
|
||||
Reference in New Issue
Block a user