get-shit-done 的 --sdk 安装旗标接线修复:forceSdk 机制与 GSD SDK 部署链路解析
【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done
本文基于 changeset 3033-sdk-flag-wired.md 展开,讲解 get-shit-done(GSD)安装器中一个隐蔽的旗标失效问题:--sdk参数此前只在 bin/install.js 中被解析、却从未真正传入 SDK 部署函数,导致npx get-shit-done-cc@latest --sdk会静默跳过 SDK 部署并给出误导性成功提示。读完本文,你将理解 GSD SDK 的部署不变式(gsd-sdk必须可被 PATH 解析)、forceSdk选项如何打通“解析—部署”链路,以及 #2678 本地安装软跳过契约如何在修复中被完整保留,并能据此在本地排障时准确判断安装器输出的各类诊断信息。
背景:--sdk旗标与 GSD SDK 的部署模型
GSD 的 slash 命令、agent 提示词和 hook 脚本依赖一个外部命令gsd-sdk来执行查询类操作。自fix/2441-sdk-decouple之后,SDK 不再作为独立子包安装:sdk/dist/以预构建形式直接打进get-shit-done-ccnpm tarball,父包声明bin条目"gsd-sdk": "bin/gsd-sdk.js",由 npm 在打包安装时正确设置可执行权限(bin/gsd-sdk.js)。
这个 shim 本身极薄:它以自身__dirname为基准解析出sdk/dist/cli.js,再通过spawnSync用当前 node 解释器代为执行(bin/gsd-sdk.js#L30-L37)。因此安装器必须保证两件事:
sdk/dist/cli.js存在且可执行;gsd-sdk命令在用户 shell 的 PATH 中可被解析(即command -v gsd-sdk能成功),这是工作流依赖的不变式,而不仅仅是“文件存在”。
安装器入口 bin/install.js 在启动阶段解析安装旗标,--sdk与--no-sdk是互斥的一对开关(bin/install.js#L215-L226):
const hasSdk = args.includes('--sdk'); const hasNoSdk = args.includes('--no-sdk'); if (hasSdk && hasNoSdk) { console.error(` ${yellow}Cannot specify both --sdk and --no-sdk${reset}`); process.exit(1); }两者的语义在installSdkIfNeeded的注释中被明确定义:--no-sdk完全跳过检查(向后兼容);--sdk则“强制检查,即使本应被跳过”(bin/install.js#L10218-L10219)。问题在于,#3033 修复之前,这个“强制”语义并没有真正生效。
缺陷:hasSdk被解析却从未接线
缺陷本身很典型:hasSdk变量在模块顶层被正确解析,但安装全部运行时之后的收尾阶段调用 SDK 部署函数时,参数对象里根本没有它。修复前的效果是——用户显式传入--sdk要求部署 SDK,安装器对本地安装(local install)却走了“软跳过”分支,静默返回,同时终端可能仍然显示误导性的 "✓ GSD SDK ready",用户无从察觉gsd-sdk其实并没有落到 PATH 上。
修复点在安装收尾函数finalize中,将解析结果透传给installSdkIfNeeded(bin/install.js#L11122-L11129):
// #3033: pass forceSdk so --sdk overrides the local-install skip. installSdkIfNeeded({ isLocal: !isGlobal, forceSdk: hasSdk, throwOnFailure: true });三个参数的分工:
isLocal: !isGlobal:区分全局安装与本地安装(本地安装不拥有全局 node_modules,不能走全局安装的报错路径);forceSdk: hasSdk:把--sdk的语义真正接入部署逻辑;throwOnFailure: true:让缺失 SDK 产物时抛出可捕获的异常,而不是直接process.exit,以便与安装回滚机制(installer migrations rollback)协作。
修复核心:forceSdk如何改变早退判定
installSdkIfNeeded对本地安装的早期返回条件,在 #3033 中加入了forceSdk否定项(bin/install.js#L10376-L10404):
// #3033: --sdk (opts.forceSdk) overrides the local-install early-return — // the user explicitly requested SDK deployment, so treat the missing-dist // case like a global install (fail fast with an actionable diagnostic) // instead of silently skipping. if (opts.isLocal && !opts.forceSdk && !fs.existsSync(sdkCliPath)) { console.warn(`\n ${yellow}⚠${reset} Skipping SDK check for local install — sdk/dist/cli.js not found at ${sdkCliPath}.`); return; } if (!fs.existsSync(sdkCliPath)) { const ir = buildSdkFailFastReport(sdkDir, sdkCliPath); renderSdkFailFastReport(ir); if (opts.throwOnFailure) { const error = new Error(`GSD SDK prebuilt artifact missing: ${sdkCliPath}`); error.code = 'GSD_SDK_MISSING_DIST'; error.exitCode = 1; throw error; } process.exit(1); }从源码结构看,修复后的行为可以用一张矩阵概括(sdkCliPath即sdk/dist/cli.js):
| 安装模式 | 是否传--sdk | sdk/dist/cli.js | 行为 |
|---|---|---|---|
| 本地安装 | 否 | 缺失 | 软跳过:打印黄色 "Skipping SDK check for local install" 警告后正常返回(保留 #2678 契约) |
| 本地安装 | 是(forceSdk: true) | 缺失 | 绕过软跳过,进入 fail-fast:渲染诊断报告,throwOnFailure为真时抛出GSD_SDK_MISSING_DIST异常,否则process.exit(1) |
| 本地安装 | 是(forceSdk: true) | 存在 | 执行完整 shim 链接路径,将gsd-sdk落到 PATH 并打印 "✓ GSD SDK ready" |
| 全局安装 | 任意 | 缺失 | 与本地+--sdk相同,fail-fast 退出 |
fail-fast 诊断并非一句笼统报错。buildSdkFailFastReport先通过classifySdkInstall判断安装形态,再给出对应的修复命令(bin/install.js#L10252-L10322):
- npx-cache(路径含
_npx段,npx 缓存目录被视为只读):提示升级全局安装或清空 npx 缓存重跑,明确声明“不会在 npx 缓存内尝试嵌套npm install”(该行为针对 #2649 的 Windows EACCES/EPERM 误报); - tarball(路径含
node_modules段):提示发布产物本身缺失sdk/dist/,给出升级命令(对应 #2647); - dev-clone(附近存在
.git目录):提示开发者自行执行cd sdk && npm install && npm run build,安装器自身绝不 shell out 调 npm。
此外,classifySdkInstall还会做一次副作用最小的写探针(创建临时文件再删除)来判断文件系统是否只读,探针失败一律按只读处理(bin/install.js#L10270-L10281)。
部署成功路径:shim 自链接与跨 shell PATH 校验
当sdk/dist/cli.js存在时,installSdkIfNeeded还会完成一系列更严格的工作,这正是--sdk用户期望“完整 shim-link 路径”的具体含义(bin/install.js#L10406-L10522):
- 补齐可执行位:
tsc产物是 0o644、git clone 保留提交时的模式,若 execute bit 缺失则就地chmod修复;chmod 失败不致命,因为 shim 仍可通过node <cliPath>方式调用; - 过滤 npx 临时 PATH 段后再解析:
filterNpxFromPath剔除 npx 注入的~/.npm/_npx/<hash>/node_modules/.bin——那是安装子进程的临时路径,用户在交互 shell 中不可达,在那里发现gsd-sdk不算“on PATH”(#3231,bin/install.js#L10665 一带的filterNpxFromPath/findGsdSdkOnPath); - 自链接 shim:若 PATH 上找不到
gsd-sdk,trySelfLinkGsdSdk会挑选第一个“像用户自有 bin 目录”的 PATH 条目写入 shim,找不到则回退到~/.local/bin(即使它不在 PATH 上,随后打印后续建议)(bin/install.js#L10910); - 跨 shell 复验(#3020):进一步探测用户登录 shell 的 PATH(Windows 下经 PowerShell 读用户级
Path注册表键,Git Bash、PowerShell、cmd.exe 均继承它),要求 shim 在该持久 PATH 中也可达才允许打印 "✓ GSD SDK ready"; - 版本错配报告:若 PATH 上解析到的
gsd-sdk与当前包版本不一致,打印版本错配诊断而非无脑打勾。
只有全部通过,终端才会输出绿色的✓ GSD SDK ready (sdk/dist/cli.js);否则输出带shimLocationLine和具体行动建议的 PATH 诊断(#3011)。
回归测试:四组断言锁住 #3033 契约
配套回归测试 tests/bug-3033-sdk-flag-wired.test.cjs 直接调用导出的installSdkIfNeeded,断言文件系统状态与控制台输出,不依赖源码文本匹配。测试在beforeEach中构造临时sdk/目录、独立的HOME与 PATH,四个用例分别覆盖(tests/bug-3033-sdk-flag-wired.test.cjs#L62-L171):
forceSdk=true+isLocal=true+ dist 存在:断言~/.local/bin/gsd-sdkshim 被真实落盘,输出包含GSD SDK ready,且不再出现 "Skipping SDK check for local install";forceSdk=true+isLocal=true+ dist 缺失:断言绕过软跳过、走 fail-fast,process.exit(1)被调用;throwOnFailure=true:断言缺失 dist 转化为可捕获异常,且error.code === 'GSD_SDK_MISSING_DIST'、error.exitCode === 1;- 默认(无
--sdk)+isLocal=true+ dist 缺失:断言不触发process.exit,验证 #2678 的本地安装软跳过契约未被破坏。
该测试文件声明为 POSIX-only(Windows 上整体跳过),因为它强制把 shebang shim 写入~/.local/bin并断言 0o755 权限模式(tests/bug-3033-sdk-flag-wired.test.cjs#L33-L34)。
实战验证与适用前提
修复后,典型验证流程是:
# 全局安装并显式要求 SDK 部署 npx get-shit-done-cc@latest --sdk # 确认 shim 已落上 PATH command -v gsd-sdk gsd-sdk query --help几点适用前提值得注意:
--sdk的完整语义依赖 tarball 内预构建的sdk/dist/(fix/2441-sdk-decouple引入的形态);从 git clone 运行安装器时若未先构建 SDK,--sdk现在会明确 fail-fast 并提示cd sdk && npm install && npm run build,而不是静默跳过;--no-sdk仍是完全跳过检查的向后兼容开关,与--sdk互斥,二者同传会直接报错退出;- 本地安装(非
--global)不带--sdk时,行为与 #2678 之前一致:sdk/dist/cli.js缺失仅打印黄色警告,安装整体不受影响; - 由于收尾阶段使用
throwOnFailure: true,SDK 部署失败会向上抛异常并触发 installer migrations 的回滚逻辑(见 docs/installer-migrations.md 所述集成方式),避免留下半成品安装。
小结
#3033 的修复面很小——一个布尔参数的接线——但它修正的是“用户显式意图 vs 安装器静默默认”之间的契约缺口。修复后,--sdk真正强制了 GSD SDK 部署不变式:缺失产物时 fail-fast 并给出按安装形态(npx 缓存 / 全局 tarball / git clone)分类的可执行诊断,产物存在时完整走完可执行位修复、npx 临时 PATH 过滤、shim 自链接与跨 shell 复验,最终才允许打出 "✓ GSD SDK ready"。相关证据链可沿 changeset 记录、bin/install.js、bin/gsd-sdk.js 与 tests/bug-3033-sdk-flag-wired.test.cjs 逐层核对。
【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考