Files
get-shit-done/bin/gsd-sdk.js
Tom Boucher 6a293cfc2a fix(#2775): verify gsd-sdk on PATH before reporting SDK ready (#2777)
* fix(#2775): verify gsd-sdk on PATH before reporting SDK ready

`npx get-shit-done-cc@latest` printed `✓ GSD SDK ready` even though
`gsd-sdk` was not callable. Root cause: npx only links the package's
primary bin (`get-shit-done-cc`); secondary bins like `gsd-sdk` are not
materialized into a PATH directory. The installer asserted the weaker
invariant "sdk/dist/cli.js exists on disk" and treated it as proof of
the stronger invariant "command -v gsd-sdk resolves" — they aren't the
same.

Fix tightens the gate in installSdkIfNeeded:

  1. After confirming the dist is present, walk PATH for an executable
     `gsd-sdk` shim (isGsdSdkOnPath, no spawn).
  2. If absent, attempt to materialize the shim via symlink at
     `~/.local/bin/gsd-sdk` (or the first HOME-rooted PATH dir we can
     write to), falling back to a copy on filesystems that reject
     symlinks (trySelfLinkGsdSdk).
  3. Re-probe PATH after linking. Only print `✓ GSD SDK ready` when the
     probe succeeds; otherwise emit a clear ⚠ + remediation.

Also strips the misleading "or `npx get-shit-done-cc`" clause from the
shim header (it never linked the secondary bin).

Closes #2775

* test(#2775): use centralized helpers from helpers.cjs per CONTRIBUTING

* fix(#2775): wrapper script in symlink fallback to preserve __dirname resolution

CodeRabbit follow-up on PR #2777. The previous symlink-fallback in
trySelfLinkGsdSdk used fs.copyFileSync(shimSrc, target), but
bin/gsd-sdk.js resolves the CLI via path.resolve(__dirname, '..',
'sdk', 'dist', 'cli.js'). After a copy, __dirname becomes the link
directory (e.g. ~/.local/bin), so the resolved CLI path was broken
(~/.local/sdk/dist/cli.js) — and isGsdSdkOnPath() only checked file
existence + execute bit, so the success line still printed over a
broken install.

Replace the copy with a tiny wrapper script that require()s the real
shim by absolute path. This preserves __dirname inside bin/gsd-sdk.js
because the require runs against shimSrc's own location.

Also fixes the PATH restoration nit in the regression test (was
coercing undefined to the string "undefined" if PATH was unset).

Adds a behavioral fallback test that mocks fs.symlinkSync to throw,
exercises the fallback path, and asserts the resulting target is a
require()-wrapper (not a verbatim copy) and is executable.

* fix(#2775): PATH-backed dir ordering + tighten captureConsole + drop tautological assertion (CodeRabbit follow-up)
2026-04-27 12:31:21 -04:00

38 lines
1.5 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* bin/gsd-sdk.js — back-compat shim for external callers of `gsd-sdk`.
*
* When the parent package is installed globally (`npm install -g get-shit-done-cc`)
* npm creates a `gsd-sdk` symlink in the global bin directory pointing at this
* file. npm correctly chmods bin entries from a tarball, so the execute-bit
* problem that afflicted the sub-install approach (issue #2453) cannot occur here.
*
* NOTE (#2775): `npx get-shit-done-cc` does NOT link this shim — npx only
* exposes the package's primary bin (`get-shit-done-cc`). For npx-based usage,
* the installer (`bin/install.js#installSdkIfNeeded`) self-symlinks `gsd-sdk`
* into `~/.local/bin` when needed and verifies PATH callability before
* reporting `✓ GSD SDK ready`.
*
* This shim resolves sdk/dist/cli.js relative to its own location and delegates
* to it via `node`, so `gsd-sdk <args>` behaves identically to
* `node <packageDir>/sdk/dist/cli.js <args>`.
*
* Call sites (slash commands, agent prompts, hook scripts) continue to work without
* changes because `gsd-sdk` still resolves on PATH — it just comes from this shim
* in the parent package rather than from a separately installed @gsd-build/sdk.
*/
'use strict';
const path = require('path');
const { spawnSync } = require('child_process');
const cliPath = path.resolve(__dirname, '..', 'sdk', 'dist', 'cli.js');
const result = spawnSync(process.execPath, [cliPath, ...process.argv.slice(2)], {
stdio: 'inherit',
env: process.env,
});
process.exit(result.status ?? 1);