
get-shit-done 安装器迁移修复 #3541 深度解析非交互升级中 prompt-user 阻塞的分类解决机制【免费下载链接】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 3541-installer-migration-prompt-user-resolution.md 展开讲解 get-shit-doneGSD安装器迁移层如何处理非交互non-TTY升级场景下prompt-user动作的阻塞问题通过“按路径分类给出安全默认值stale SDK 构建产物默认remove、用户可见 skill 默认keep 结构化日志 按原因分组的错误信息 GSD_INSTALLER_MIGRATION_RESOLVE环境变量兜底”的组合方案使/gsd:update在 CI、脚本或 Claude Code 代执行等无终端场景下不再卡死。读完本文你能理解 GSD 首次基线扫描为何会产生prompt-user阻塞、自动解决器的分类规则如何落地到具体计划动作以及运维人员面对残余阻塞时应如何处置。问题背景首次基线扫描会主动“拦路”GSD 的安装/升级行为由一个显式的安装器迁移层Installer Migration Module承载其设计目标在 docs/adr/0008-installer-migration-module.md 中定调模糊或未知文件一律默认保留破坏性变更必须持有 manifest 所有权证据或明确的用户选择。完整的模块契约动作类型、执行流、安全策略、运行时配置契约注册表定义在 docs/installer-migrations.md。其中prompt-user动作的文档定义是“在非交互模式下停止破坏性迁移在交互模式下询问用户提示必须给出 preserve、back up、remove、move 等具体选项默认是 preserve”用于“分类存在歧义、猜测可能丢数据”的场景。这正是 #3541 事故的起点。首次安装基线迁移记录2026-05-11-first-time-baseline-scan实现于 000-first-time-baseline.cjs负责在破坏性迁移运行前对既有安装面做分类。它的判定链是见 L149-L219已知用户拥有的路径如get-shit-done/USER-PROFILE.md、skills/gsd-dev-preferences/SKILL.md→baseline-preserve-usermanifest 可证明的受管文件或已知生成 agent →record-baseline看起来像 GSD 产物、但没有 manifest 证明的“stale-gsd-looking”文件 →prompt-userchoices: [keep, remove]reason 为 “GSD-looking file is not proven manifest-managed and needs explicit user choice”其余未知文件 →baseline-preserve-user。判定“看起来像 GSD 产物”的规则在 isStaleGsdLookingPath文件名以gsd-或gsd_开头或位于skills/gsd-*/agents/gsd-*二级目录下。事故形态非 TTY 下/gsd:update不可恢复prompt-user的设计前提是“交互模式下可以问用户”。但 GSD 的更新入口/gsd:update通常由 Claude Code 代为执行——这类运行没有 stdin TTY无法交互式回答任何问题。回归测试 bug-3541-installer-migration-prompt-user-resolution.test.cjs 的头部注释精确还原了事故首次基线安装器迁移的prompt-user动作直接硬抛异常且无解决路径。当残留的gsd-*文件被分类为stale-gsd-looking时/gsd-update变得不可恢复。一个典型的触发场景测试 A 复刻的 1.41.2 → 1.42.2 升级是旧版本把get-shit-done/sdk/{dist,src}/gsd-*下的 SDK 构建产物写进了安装目录而新版本 manifest 不再把这些路径分类为受管文件。基线扫描发现它们“长得像 GSD 产物但没有 manifest 证明”于是对每个文件生成一个prompt-user阻塞项——非 TTY 运行时整个安装流程停在assertInstallerMigrationsUnblocked抛出的异常上用户没有任何文档化的出路。修复方案 A按分类给出安全默认值并记录日志#3541 的核心修复是新增非 TTY 解决器实现于 installer-migration-report.cjs 的 resolveInstallerMigrationPromptsForNonTty。它遍历result.blocked中的prompt-user动作按以下优先级决定每个动作的归宿运维覆盖operator-override若环境变量GSD_INSTALLER_MIGRATION_RESOLVE的值经 normalizeResolutionChoice 规范化去空白、转小写后是keep或remove之一且该动作自身的choices数组允许此选项actionSupportsChoice则直接采用来源标记为GSD_INSTALLER_MIGRATION_RESOLVE分类默认non-tty-default否则调用 classifyPromptUserAction 按路径形状分类。#3541 引入的两个安全类别是类别路径匹配规则默认选择理由源码注释stale-sdk-build-artifact/^get-shit-done\/sdk\/(dist|src)\//remove陈旧 SDK 构建产物每次安装都会重新生成删除无损新安装会提供替代物user-facing-skill/^skills\/gsd-[^/]\/SKILL\.md$/keep面向用户的 skill 锚点会作为命令暴露给用户属于用户拥有的内容必须保留后续 #3610/#3628 扩展bundled-gsd-hook显式白名单BUNDLED_GSD_HOOK_FILESL111-L125中的 13 个hooks/gsd-*文件remove分发自带的 bundled hook安装器即将写入全新副本白名单之外同形状的文件用户自写 hook、旧版本退役 hook仍走阻塞流程分类返回null的动作无法安全默认进入unresolved保持阻塞状态。物化为具体计划动作每个被解决的动作由 materializeResolution 转换——keep变为baseline-preserve-user幂等文件本来就在磁盘上remove变为backup-and-remove安全删除迁移日志gsd-migration-journal/runId-backups/下保留回滚副本。转换后的动作会原地替换result.plan.actions中的原prompt-user项找不到才追加保证下游applyInstallerMigrationPlan永远不会遇到不支持的动作类型result.blocked与result.plan.blocked两个面同步过滤为“仍无法解决”的集合。结构化日志每个被默认解决的动作追加一条 resolution 记录字段为{ relPath, category, choice, reason, resolvedActionType, source }其中source区分operator-override与non-tty-default便于事后审计“这个决定是环境变量的决定还是分类器做出的”。修复方案 B残余阻塞的可操作性错误信息无法安全默认的动作依然阻塞但错误信息从“N 行路径刷屏”升级为按原因分组的可操作报告由 buildBlockedErrorMessage 生成格式如下installer migration blocked pending user choice: 2 files need a decision choices: [keep, remove] - 2 files: GSD-looking file is not proven manifest-managed and needs explicit user choice e.g. get-shit-done/sdk/dist/gsd-a.js, get-shit-done/sdk/dist/gsd-b.js resolve non-interactively by setting GSD_INSTALLER_MIGRATION_RESOLVEchoice (or run the installer in a TTY to be prompted per file).关键设计点同一reason的路径归并为一行摘要 数量groupBlockedByReason最多展示 3 条样例路径避免 SDK 构建产物泄漏时打出千行报错明确列出文档化的选项集合动作自带choices时取并集否则回退为keep/remove见 describeChoicesForActions点名非交互解决面GSD_INSTALLER_MIGRATION_RESOLVE并提示另一条出路在 TTY 中运行以获得逐文件提示抛出的 Error 对象附带机器可读字段blocked、blockedByReason、resolutionEnvVarassertInstallerMigrationsUnblocked调用方无需重新解析文本即可渲染自己的报告。安装主流程中的调用链在 bin/install.js 中迁移运行被串在“回滚快照已建立之后、包实体化之前”这一安全窗口L8197-L8266以baselineScan: true调用runInstallerMigrations({ configDir, runtime, scope, migrations })生成计划若result.blocked非空调用resolveInstallerMigrationPromptsForNonTty(result, { isTty: false })对每条 resolution 打印一行↪ installer-migration auto-resolved: relPath → choice (category..., source...)让每次自动裁决都在安装输出中可见呼应 ADR 的“破坏性动作运行前必须可见”目标若全部阻塞被解决plan.blocked.length 0立即用applyInstallerMigrationPlan应用已解除阻塞的计划再reportInstallerMigrationResult汇报最后assertInstallerMigrationsUnblocked作为守门员还有残余阻塞就抛出上面分组的错误安装在新包文件写入之前失败。从当前源码结构还可以推断出一次后续演进install.js 的 #3610 注释 说明解决器后来改为无论 TTY 与否都运行分类器分支此前按!isTTY门控导致交互式npx安装被 12 个 bundled hook 阻塞项硬中止而GSD_INSTALLER_MIGRATION_RESOLVE环境变量覆盖分支仍只在非 TTY 模式生效。#3541 确立的“分类默认 环境变量兜底”双轨结构保持不变。行为验证三条回归测试tests/bug-3541-installer-migration-prompt-user-resolution.test.cjs 通过公开入口runInstallerMigrations 解决器做了行为级验证测试 AL73-L133在临时配置目录写入一个空 manifest、一个get-shit-done/sdk/dist/gsd-old-bundle.js和一个skills/gsd-roadmap/SKILL.md两者都被基线迁移分类为prompt-user阻塞验证事故前置条件随后非 TTY 解决器必须输出 2 条 resolution——SDK 产物choice: remove/category: stale-sdk-build-artifactskillchoice: keep/category: user-facing-skill且assertInstallerMigrationsUnblocked不再抛出测试 BL135-L200构造两条同 reason 的阻塞动作断言错误信息同时包含keep/remove选项、GSD_INSTALLER_MIGRATION_RESOLVE提示、按 reason 归并的2 files计数摘要且抛出的错误携带blockedByReason映射两条路径归入同一个 key测试 CL202-L238注入GSD_INSTALLER_MIGRATION_RESOLVEkeep环境变量验证无法分类的skills/gsd-custom/SKILL.toml也能被解决resolution 的source为环境变量名、category为operator-override物化后的动作类型为baseline-preserve-user且blocked/plan.blocked均清零。实践指引遇到迁移阻塞时怎么办结合 changeset 与源码运维者在 GSD 非交互升级/gsd:update、CI 脚本等中可遵循如下处置路径观察自动解决日志安装输出中每行↪ installer-migration auto-resolved: path → choice (category..., source...)都代表一个被分类器或环境变量解决的阻塞项可核对source是non-tty-default还是GSD_INSTALLER_MIGRATION_RESOLVE被自动移除的文件有回滚副本分类为remove的prompt-user动作物化为backup-and-remove删除前在迁移日志gsd-migration-journal/runId-backups/下保留副本可事后检查面对残余阻塞错误信息已按 reason 分组并给出选项集合。若你确认某类文件可保留或可删除重新运行时设置GSD_INSTALLER_MIGRATION_RESOLVEkeep或GSD_INSTALLER_MIGRATION_RESOLVEremove仅接受这两个值大小写不敏感即可非交互解决注意该变量会作用于该次运行中所有支持该选项的阻塞动作请谨慎评估范围在 TTY 中运行安装器是错误信息给出的另一条出路可获得逐文件提示。相关资料原始 changeset本文主体.changeset/3541-installer-migration-prompt-user-resolution.md对应 PR #3547随 RELEASE-v1.42.3.md 发布该 release note 条目即“Prompt-user migration actions resolve in non-TTY runs”。迁移层完整契约与动作类型定义docs/installer-migrations.md其中prompt-user、preserve-user、首次基线迁移、安全策略各节是理解本修复的上下文。模块决策记录docs/adr/0008-installer-migration-module.md。核心实现get-shit-done/bin/lib/installer-migration-report.cjs解决器、分类器、错误构造、get-shit-done/bin/lib/installer-migrations/000-first-time-baseline.cjs首次基线扫描与prompt-user产生点、bin/install.js安装主流程接线。回归测试tests/bug-3541-installer-migration-prompt-user-resolution.test.cjs。需要说明的适用前提本修复针对的是 GSD 自身安装器/gsd:update/bin/install.js路径下的迁移阻塞依赖安装器迁移框架的prompt-user动作与blocked结果面GSD_INSTALLER_MIGRATION_RESOLVE是非交互解决面不是通用配置项且在当前代码中其覆盖分支仅在非 TTY 模式下生效。【免费下载链接】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),仅供参考