chore(#191): retire the gsd-sdk shim — route everything at gsd-tools (#522)

* 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:
Tom Boucher
2026-05-30 19:19:14 -04:00
committed by GitHub
parent 2bc295b32d
commit 0fbe1d899e
25 changed files with 91 additions and 319 deletions

View File

@@ -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,