@peterxiaoyang/superspec 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/i18n.js CHANGED
@@ -1,68 +1,69 @@
1
1
  const COMMAND_ZH = {
2
- init: { label_zh: "初始化", hint_zh: "初始化 SuperSpec 工作流所需的目录和受管内容。" },
3
- status: { label_zh: "状态检查", hint_zh: "查看当前变更的守卫状态与摘要。" },
4
- recompute: { label_zh: "状态重算", hint_zh: "重新计算守卫状态指纹与阶段摘要。" },
2
+ init: { label_zh: "初始化", hint_zh: "初始化 SuperSpec 工作流所需的目录和文件。" },
3
+ status: { label_zh: "状态检查", hint_zh: "查看当前变更的检查状态和摘要。" },
4
+ recompute: { label_zh: "重新计算状态", hint_zh: "重新计算检查状态指纹和阶段摘要。" },
5
5
  "check-init": { label_zh: "初始化前置检查", hint_zh: "检查项目是否具备运行 SuperSpec 的基础内容。" },
6
6
  "check-artifact": { label_zh: "工件前置检查", hint_zh: "检查某个 OpenSpec 工件是否允许开始编写。" },
7
- "check-enter": { label_zh: "前置门禁检查", hint_zh: "检查某个流程关卡是否允许进入。" },
8
- "check-apply-ready": { label_zh: "进入实现前检查", hint_zh: "检查提案阶段是否已经完整收口,可以进入实现阶段。" },
7
+ "check-enter": { label_zh: "进入阶段前检查", hint_zh: "检查是否可以进入指定流程阶段。" },
8
+ "check-apply-ready": { label_zh: "进入实现前检查", hint_zh: "检查提案阶段是否已经处理完成,可以进入实现阶段。" },
9
9
  "check-task-reopen": { label_zh: "任务重开检查", hint_zh: "检查被审查打回的任务是否满足重开条件。" },
10
10
  "check-task-edit": { label_zh: "任务编辑前检查", hint_zh: "检查任务在开始实现前是否满足测试与范围前置条件。" },
11
11
  "check-task-complete": { label_zh: "任务完成检查", hint_zh: "检查任务在勾完成前是否满足 GREEN 或替代验证要求。" },
12
- "check-review-ready": { label_zh: "进入审查前检查", hint_zh: "检查实现阶段是否完整收口,可以进入审查阶段。" },
12
+ "check-review-ready": { label_zh: "进入审查前检查", hint_zh: "检查实现阶段是否已经处理完成,可以进入审查阶段。" },
13
13
  "check-review-complete": { label_zh: "审查完成检查", hint_zh: "检查审查阶段证据是否齐备并允许通过。" },
14
14
  "check-verify-ready": { label_zh: "验证完成检查", hint_zh: "检查最终验证证据是否完整。" },
15
15
  "check-archive-ready": { label_zh: "归档前检查", hint_zh: "检查是否满足 archive 前的确认与保全要求。" },
16
16
  "check-archived": { label_zh: "归档结果检查", hint_zh: "检查 change 是否已经被正确归档。" },
17
17
  };
18
18
  const GATE_ZH = {
19
- status: { label_zh: "状态", hint_zh: "当前变更的总览状态,不对应单个关卡。" },
19
+ status: { label_zh: "状态", hint_zh: "当前变更的总览状态,不对应单个阶段。" },
20
20
  init: { label_zh: "初始化", hint_zh: "项目或 change 级别的初始化检查。" },
21
- recompute: { label_zh: "状态重算", hint_zh: "重新计算 state 与守卫指纹的辅助关卡。" },
22
- project_init: { label_zh: "项目初始化", hint_zh: "为当前仓库安装或修复 SuperSpec 项目级受管内容。" },
23
- project_update: { label_zh: "项目级更新", hint_zh: "更新当前仓库中的 SuperSpec 项目级受管内容。" },
24
- project_uninstall: { label_zh: "项目级卸载", hint_zh: "卸载当前仓库中的 SuperSpec 项目级受管内容。" },
25
- user_install: { label_zh: "用户级安装", hint_zh: "在用户级 Codex 目录安装 SuperSpec 受管内容。" },
26
- user_update: { label_zh: "用户级更新", hint_zh: "更新用户级 Codex 目录中的 SuperSpec 受管内容。" },
27
- user_uninstall: { label_zh: "用户级卸载", hint_zh: "卸载用户级 Codex 目录中的 SuperSpec 受管内容。" },
28
- guard_error: { label_zh: "命令执行异常", hint_zh: "当前 guard init 命令在执行过程中返回了错误。" },
21
+ recompute: { label_zh: "重新计算状态", hint_zh: "重新计算 state 与检查指纹的辅助步骤。" },
22
+ openspec_preflight: { label_zh: "OpenSpec 前置检查", hint_zh: "确认 OpenSpec CLI 已满足 SuperSpec 运行要求。" },
23
+ project_init: { label_zh: "项目初始化", hint_zh: "为当前仓库安装或修复 SuperSpec 项目文件。" },
24
+ project_update: { label_zh: "项目级更新", hint_zh: "更新当前仓库中的 SuperSpec 项目文件。" },
25
+ project_uninstall: { label_zh: "项目级卸载", hint_zh: "卸载当前仓库中的 SuperSpec 项目文件。" },
26
+ user_install: { label_zh: "用户级安装", hint_zh: "在用户级 Codex 目录安装 SuperSpec 文件。" },
27
+ user_update: { label_zh: "用户级更新", hint_zh: "更新用户级 Codex 目录中的 SuperSpec 文件。" },
28
+ user_uninstall: { label_zh: "用户级卸载", hint_zh: "卸载用户级 Codex 目录中的 SuperSpec 文件。" },
29
+ guard_error: { label_zh: "命令执行异常", hint_zh: "当前检查或初始化命令在执行过程中返回了错误。" },
29
30
  preset_upgrade: { label_zh: "预设升级确认", hint_zh: "需要先确认是否接受更高强度的流程预设。" },
30
31
  branch_handling: { label_zh: "分支处理确认", hint_zh: "需要先确认当前分支与工作区的处理方式。" },
31
32
  apply_isolation: { label_zh: "实现隔离确认", hint_zh: "需要先确认实现阶段允许写入的隔离范围。" },
32
33
  scope_expansion: { label_zh: "范围扩张确认", hint_zh: "需要先确认本次变更是否允许扩大范围。" },
33
- verify_failure_handling: { label_zh: "失败验证处置确认", hint_zh: "需要先确认失败验证是继续修复还是接受偏差。" },
34
- explore_complete: { label_zh: "探索完成", hint_zh: "探索记录与探索期审查披露闭环已完成。" },
35
- proposal_reviewed: { label_zh: "提案审查完成", hint_zh: "提案已经完成对抗审查,并完成必要披露或用户裁决。" },
36
- design_complete: { label_zh: "设计完成", hint_zh: "设计与规格的审查闭环已完成。" },
37
- invariants_reviewed: { label_zh: "业务不变量审查完成", hint_zh: "业务不变量文档已完成审查与披露闭环。" },
38
- test_contract_drafted: { label_zh: "测试契约起草完成", hint_zh: "测试契约已完成审查与披露闭环。" },
39
- tasks_complete: { label_zh: "任务映射完成", hint_zh: "任务映射、测试引用与上游约束已经收口。" },
34
+ verify_failure_handling: { label_zh: "失败验证处理确认", hint_zh: "需要先确认失败验证是继续修复还是接受偏差。" },
35
+ explore_complete: { label_zh: "探索完成", hint_zh: "探索记录、审查问题说明和必要的用户确认都已完成。" },
36
+ proposal_reviewed: { label_zh: "提案审查完成", hint_zh: "提案已经完成严格审查,并完成必要的问题说明或用户确认。" },
37
+ design_complete: { label_zh: "设计完成", hint_zh: "设计与规格审查已经完成。" },
38
+ invariants_reviewed: { label_zh: "业务不变量审查完成", hint_zh: "业务不变量文档已经完成审查和必要的问题说明。" },
39
+ test_contract_drafted: { label_zh: "测试契约起草完成", hint_zh: "测试契约已经完成审查和必要的问题说明。" },
40
+ tasks_complete: { label_zh: "任务映射完成", hint_zh: "任务映射、测试引用和上游约束已经处理完成。" },
40
41
  propose_complete: { label_zh: "提案阶段完成", hint_zh: "从探索到任务映射的提案链路已经全部通过。" },
41
42
  task_reopen: { label_zh: "任务重开", hint_zh: "审查打回后的任务重开流程。" },
42
43
  task_edit: { label_zh: "任务编辑", hint_zh: "实现前检查 RED 或现状锁定测试等前置条件。" },
43
44
  task_complete: { label_zh: "任务完成", hint_zh: "任务勾完成前的证据检查。" },
44
45
  review_ready: { label_zh: "进入审查前准备完成", hint_zh: "可以开始审查阶段。" },
45
- review_complete: { label_zh: "审查完成", hint_zh: "审查与最终验证证据已经收口。" },
46
- verify_complete: { label_zh: "验证完成", hint_zh: "最终验证闭环已完成。" },
46
+ review_complete: { label_zh: "审查完成", hint_zh: "审查和最终验证证据都已经处理完成。" },
47
+ verify_complete: { label_zh: "验证完成", hint_zh: "最终验证已完成。" },
47
48
  archive_ready: { label_zh: "归档准备完成", hint_zh: "满足 archive 之前的所有保全与确认要求。" },
48
49
  archived: { label_zh: "已归档", hint_zh: "change 已完成 archive。" },
49
50
  };
50
51
  const REASON_ZH = {
51
- missing_review_digest: { label_zh: "缺少审查披露记录", hint_zh: "当前轮次的审查问题还没有被主线程整理成披露记录。" },
52
- needs_user_decision_pending: { label_zh: "等待用户裁决", hint_zh: "当前问题已经进入用户检查点,必须先由用户在 A/B/C/D 中拍板。" },
53
- finding_unresolved: { label_zh: "历史问题未收口", hint_zh: "旧问题还没有终态处置,不能靠新一轮无问题审查抹掉历史问题。" },
54
- finding_undisclosed: { label_zh: "本轮问题未披露", hint_zh: "审查角色提出的问题没有被写进本轮披露记录并展示给用户。" },
55
- user_decision_unbound: { label_zh: "用户裁决未绑定", hint_zh: "关键问题的终态处置没有正确绑定到用户裁决或有效授权。" },
56
- standing_authorization_unbound: { label_zh: "常设授权不适用", hint_zh: "当前问题不能被现有常设授权合法覆盖。" },
57
- review_round_stale: { label_zh: "审查轮次已过期", hint_zh: "审查轮次钉住的工件版本已经不是当前版本,需要重审。" },
58
- review_digest_stale: { label_zh: "披露记录已过期", hint_zh: "披露记录钉住的工件集合已过期,需要按当前版本重做披露。" },
59
- review_round_discontinuous: { label_zh: "审查轮次断档", hint_zh: "review_round_id 必须连续编号,不能跳过中间轮次。" },
60
- digest_chain_broken: { label_zh: "披露链断裂", hint_zh: "后一轮披露记录没有正确引用前一轮披露记录。" },
61
- finding_identity_mismatch: { label_zh: "问题身份不一致", hint_zh: "主线程在披露记录里改写了审查方原始问题的分类或身份字段。" },
62
- finding_summary_not_verbatim: { label_zh: "问题原文未逐字保留", hint_zh: "用户可见披露没有逐字保留审查方的原始摘要。" },
52
+ missing_review_digest: { label_zh: "缺少审查问题记录", hint_zh: "当前轮次的审查问题还没有整理成给用户看的记录。" },
53
+ needs_user_decision_pending: { label_zh: "等待用户确认", hint_zh: "当前问题需要用户确认,请先选择 A/B/C/D 中的一项。" },
54
+ finding_unresolved: { label_zh: "历史问题未处理完", hint_zh: "旧问题还没有最终处理结果,不能靠新一轮无问题审查把它忽略掉。" },
55
+ finding_undisclosed: { label_zh: "本轮问题未说明", hint_zh: "审查角色提出的问题还没有写进本轮审查问题记录并展示给用户。" },
56
+ user_decision_unbound: { label_zh: "用户确认未绑定", hint_zh: "关键问题的最终处理结果没有正确关联到用户确认或有效授权。" },
57
+ standing_authorization_unbound: { label_zh: "长期授权不适用", hint_zh: "当前问题不能被现有长期授权覆盖。" },
58
+ review_round_stale: { label_zh: "审查轮次已过期", hint_zh: "审查轮次记录的工件版本已经不是当前版本,需要重新审查。" },
59
+ review_digest_stale: { label_zh: "审查问题记录已过期", hint_zh: "审查问题记录对应的工件集合已过期,需要按当前版本重新整理。" },
60
+ review_round_discontinuous: { label_zh: "审查轮次断档", hint_zh: "审查轮次编号必须连续,不能跳过中间轮次。" },
61
+ digest_chain_broken: { label_zh: "审查问题记录链不完整", hint_zh: "后一轮审查问题记录没有正确引用前一轮记录。" },
62
+ finding_identity_mismatch: { label_zh: "问题身份不一致", hint_zh: "主流程在审查问题记录里改写了审查方原始问题的分类或身份字段。" },
63
+ finding_summary_not_verbatim: { label_zh: "问题原文未逐字保留", hint_zh: "展示给用户的问题说明没有逐字保留审查方的原始摘要。" },
63
64
  accepted_deviation_unacknowledged: { label_zh: "已接受偏差未被重审确认", hint_zh: "新一轮无问题审查没有明确确认已接受的偏差。" },
64
- ledger_injection_missing: { label_zh: "缺少问题台账注入", hint_zh: "增量重审输入中缺少工具生成的问题台账。" },
65
- round_budget_exhausted: { label_zh: "审查轮次预算耗尽", hint_zh: "同一流程关卡超过预算轮次仍未收敛,需要升级给用户。" },
65
+ ledger_injection_missing: { label_zh: "缺少问题清单", hint_zh: "增量重审输入中缺少工具生成的问题清单。" },
66
+ round_budget_exhausted: { label_zh: "审查轮次已达上限", hint_zh: "同一流程阶段超过预算轮次仍未处理完成,需要升级给用户。" },
66
67
  missing_characterization: { label_zh: "缺少现状锁定测试", hint_zh: "行为保持重构在动代码前,必须先用 GREEN 测试锁住当前行为。" },
67
68
  missing_red_evidence: { label_zh: "缺少 RED 证据", hint_zh: "新增行为或修 bug 的任务在实现前需要先有失败测试证据。" },
68
69
  missing_green_evidence: { label_zh: "缺少 GREEN 证据", hint_zh: "任务完成前需要通过成功测试或等价验证。" },
@@ -70,29 +71,34 @@ const REASON_ZH = {
70
71
  invalid_no_tdd_reason: { label_zh: "免 TDD 理由无效", hint_zh: "task 上声明的 no_tdd_reason 不在允许枚举里。" },
71
72
  missing_discovery: { label_zh: "缺少探索记录", hint_zh: "探索阶段缺少 discovery.md,或者该文件内容为空。" },
72
73
  missing_proposal: { label_zh: "缺少提案工件", hint_zh: "提案工件还没有完成。" },
73
- missing_proposal_review: { label_zh: "缺少提案审查", hint_zh: "提案审查关卡还没有收集到对抗审查证据。" },
74
- explore_complete_failed: { label_zh: "探索阶段未通过", hint_zh: "下游关卡依赖的探索完成检查还没有通过。" },
75
- proposal_reviewed_failed: { label_zh: "提案审查阶段未通过", hint_zh: "下游关卡依赖的提案审查完成还没有通过。" },
74
+ missing_proposal_review: { label_zh: "缺少提案审查", hint_zh: "提案审查阶段还没有收集到严格审查证据。" },
75
+ explore_complete_failed: { label_zh: "探索阶段未通过", hint_zh: "后续阶段依赖的探索完成检查还没有通过。" },
76
+ proposal_reviewed_failed: { label_zh: "提案审查阶段未通过", hint_zh: "后续阶段依赖的提案审查完成还没有通过。" },
76
77
  validate_failed: { label_zh: "OpenSpec 校验失败", hint_zh: "openspec validate 还没有通过。" },
77
- review_not_ready: { label_zh: "尚未达到审查条件", hint_zh: "进入审查阶段之前的前置关卡还没有全部通过。" },
78
+ review_not_ready: { label_zh: "尚未达到审查条件", hint_zh: "进入审查阶段之前的检查还没有全部通过。" },
78
79
  apply_isolation_unconfirmed: { label_zh: "实现隔离尚未确认", hint_zh: "实现阶段缺少用户对写入隔离范围的确认。" },
79
80
  scope_expansion_unconfirmed: { label_zh: "范围扩张尚未确认", hint_zh: "tasks 结构变化扩大了范围,但还没有新的用户确认。" },
80
81
  state_concurrent_update: { label_zh: "状态写入发生并发变更", hint_zh: "决策和写入之间输入发生变化,需要等变更稳定后重新运行。" },
81
82
  project_init_failed: { label_zh: "项目初始化失败", hint_zh: "项目级 SuperSpec 初始化没有成功完成。" },
82
- project_update_failed: { label_zh: "项目级更新失败", hint_zh: "项目级受管内容更新失败。" },
83
- project_uninstall_failed: { label_zh: "项目级卸载失败", hint_zh: "项目级受管内容卸载失败。" },
83
+ openspec_cli_too_old: { label_zh: "OpenSpec 版本过低", hint_zh: "当前 openspec CLI 低于 SuperSpec 要求的最低版本。" },
84
+ openspec_cli_unavailable: { label_zh: "OpenSpec CLI 不可用", hint_zh: "PATH 中没有可执行的 openspec CLI。" },
85
+ openspec_native_surface_missing: { label_zh: "OpenSpec 原生能力缺失", hint_zh: "当前 openspec CLI 不是受支持版本,或缺少 SuperSpec 依赖的子命令。" },
86
+ openspec_auto_install_unavailable: { label_zh: "OpenSpec 无法自动安装", hint_zh: "当前环境没有可用包管理器完成 OpenSpec 自动安装或升级。" },
87
+ openspec_auto_install_failed: { label_zh: "OpenSpec 自动安装失败", hint_zh: "OpenSpec 自动安装或升级命令没有成功完成。" },
88
+ project_update_failed: { label_zh: "项目级更新失败", hint_zh: "项目级 SuperSpec 文件更新失败。" },
89
+ project_uninstall_failed: { label_zh: "项目级卸载失败", hint_zh: "项目级 SuperSpec 文件卸载失败。" },
84
90
  user_install_failed: { label_zh: "用户级安装失败", hint_zh: "用户级 SuperSpec 安装失败。" },
85
91
  user_update_failed: { label_zh: "用户级更新失败", hint_zh: "用户级 SuperSpec 更新失败。" },
86
92
  user_uninstall_failed: { label_zh: "用户级卸载失败", hint_zh: "用户级 SuperSpec 卸载失败。" },
87
- guard_error: { label_zh: "Guard 执行失败", hint_zh: "guard 命令执行过程中发生了可报告错误。" },
88
- guard_internal_error: { label_zh: "Guard 内部错误", hint_zh: "guard 内部抛出了异常,需要查看错误信息排查。" },
93
+ guard_error: { label_zh: "检查执行失败", hint_zh: "guard 命令执行过程中发生了可报告错误。" },
94
+ guard_internal_error: { label_zh: "检查器内部错误", hint_zh: "guard 内部抛出了异常,需要查看错误信息排查。" },
89
95
  };
90
96
  const WORKFLOW_TERMS_ZH = {
91
- "check-enter": { term: "check-enter", label_zh: "前置门禁检查", hint_zh: "进入某个 gate 前先跑的守门检查命令。" },
92
- acceptance: { term: "acceptance", label_zh: "验收口径", hint_zh: "这个 change 交付后必须满足的结果和边界。" },
97
+ "check-enter": { term: "check-enter", label_zh: "进入阶段前检查", hint_zh: "进入某个流程阶段前先运行的检查命令。" },
98
+ acceptance: { term: "acceptance", label_zh: "验收标准", hint_zh: "这个 change 交付后必须满足的结果和边界。" },
93
99
  characterization: { term: "characterization", label_zh: "现状锁定测试", hint_zh: "先把当前真实行为测出来并锁住,重构后保持一致。" },
94
- main_review_digest: { term: "main_review_digest", label_zh: "主线程审查披露记录", hint_zh: "主线程把本轮审查问题整理给用户看的记录。" },
95
- user_review_decision: { term: "user_review_decision", label_zh: "用户裁决记录", hint_zh: "用户对 A/B/C/D 选项做出的正式决定记录。" },
100
+ main_review_digest: { term: "main_review_digest", label_zh: "审查问题记录", hint_zh: "主流程把本轮审查问题整理给用户看的记录。" },
101
+ user_review_decision: { term: "user_review_decision", label_zh: "用户确认记录", hint_zh: "用户对 A/B/C/D 选项做出的正式确认记录。" },
96
102
  };
97
103
  const TRUST_WARNING_ZH = {
98
104
  "v1 evidence is audit-only/self-reported unless explicitly backed by OpenSpec facts": "v1 证据默认只作审计参考或自报信息;除非另有 OpenSpec 事实支撑,否则不能单独当作强证明。",
@@ -103,9 +109,9 @@ const TRUST_WARNING_ZH = {
103
109
  const ACTION_ZH_EXACT = {
104
110
  "continue with superspec-explore/propose/apply guard checks": "继续执行探索、提案和实现阶段的前置检查。",
105
111
  "create/select a change during superspec-explore, then run change-scoped guard checks": "先在探索阶段创建或选择一个变更,然后运行该变更范围内的前置检查。",
106
- "review *.new files for user-modified surfaces, then rerun superspec guard check-init": "先检查因用户改动而生成的 *.new 文件,然后重新运行初始化前置检查。",
112
+ "review *.new files for user-modified surfaces, then rerun superspec guard check-init": "先检查因用户改动而生成的 *.new 文件,然后重新运行初始化前置检查;Windows PowerShell 中使用 superspec.cmd guard check-init。",
107
113
  "review *.new files for user-modified user-level surfaces": "先检查用户级内容对应的 *.new 文件。",
108
- "user-level SuperSpec Codex surfaces installed; use superspec init --scope project inside a repo when project-local surfaces are needed": "用户级 SuperSpec 配套内容已安装;如果仓库里还需要项目级内容,请在仓库内运行 superspec init --scope project。",
114
+ "user-level SuperSpec Codex surfaces installed; use superspec init --scope project inside a repo when project-local surfaces are needed": "用户级 SuperSpec 配套内容已安装;如果仓库里还需要项目级内容,请在仓库内运行 superspec init --scope project;Windows PowerShell 中使用 superspec.cmd init --scope project。",
109
115
  "finish remaining unchecked tasks and mark them complete only after check-task-complete passes": "先完成剩余未勾选任务,并且仅在任务完成检查通过后再标记完成。",
110
116
  "record final_test pass evidence and reference it from verification_review": "记录最终测试通过证据,并在验证审查证据中引用它。",
111
117
  };
@@ -120,7 +126,7 @@ const ACTION_STATUS_ZH = {
120
126
  };
121
127
  const ROLE_ZH = {
122
128
  architect: "架构审查",
123
- critic: "对抗审查",
129
+ critic: "严格审查",
124
130
  "test-engineer": "测试审查",
125
131
  "code-reviewer": "代码审查",
126
132
  verifier: "验证审查",
@@ -129,9 +135,9 @@ const EVIDENCE_KIND_ZH = {
129
135
  source_guidance: "审查指导证据",
130
136
  verification_review: "验证审查证据",
131
137
  final_test: "最终测试通过证据",
132
- main_adjudication: "主线程裁决记录",
133
- main_review_digest: "主线程审查披露记录",
134
- user_review_decision: "用户裁决记录",
138
+ main_adjudication: "主流程最终判断记录",
139
+ main_review_digest: "审查问题记录",
140
+ user_review_decision: "用户确认记录",
135
141
  human_confirmation: "人工确认证据",
136
142
  test_run: "测试运行证据",
137
143
  alternative_verification: "替代验证证据",
@@ -147,8 +153,8 @@ const FIELD_ZH = {
147
153
  blocking_source_evidence_refs: "阻塞来源证据引用",
148
154
  required_claim_ids: "必需结论项",
149
155
  required_load_refs: "必需加载引用",
150
- claim_adjudications: "结论项裁决",
151
- finding_adjudications: "问题项裁决",
156
+ claim_adjudications: "结论项判断",
157
+ finding_adjudications: "问题处理判断",
152
158
  request_changes_route: "打回路由",
153
159
  reopen_task_ids: "需重开任务列表",
154
160
  output_ref: "输出引用",
@@ -169,9 +175,9 @@ const NOUN_ZH = {
169
175
  source_guidance: "审查指导证据",
170
176
  verification_review: "验证审查证据",
171
177
  final_test: "最终测试通过证据",
172
- main_adjudication: "主线程裁决记录",
173
- main_review_digest: "主线程审查披露记录",
174
- review_digest: "审查披露记录",
178
+ main_adjudication: "主流程最终判断记录",
179
+ main_review_digest: "审查问题记录",
180
+ review_digest: "审查问题记录",
175
181
  review_round: "审查轮次",
176
182
  final_verification_review: "最终验证审查",
177
183
  business_invariants: "业务不变量",
@@ -193,9 +199,9 @@ const NOUN_ZH = {
193
199
  review_evidence: "审查证据",
194
200
  task_evidence: "任务证据",
195
201
  native_subagent_evidence: "本地子代理证据",
196
- claim_adjudication: "结论项裁决",
197
- claim_adjudications: "结论项裁决",
198
- finding_adjudication: "问题项裁决",
202
+ claim_adjudication: "结论项判断",
203
+ claim_adjudications: "结论项判断",
204
+ finding_adjudication: "问题处理判断",
199
205
  test_contract_review: "测试契约审查",
200
206
  invariant_review: "不变量审查",
201
207
  missing_roles: "缺失角色",
@@ -203,7 +209,7 @@ const NOUN_ZH = {
203
209
  reviewed_files: "审查文件列表",
204
210
  required_loads: "必需加载项",
205
211
  claims: "结论项",
206
- blocking_findings: "阻塞问题项",
212
+ blocking_findings: "阻塞问题",
207
213
  human_confirmation: "人工确认",
208
214
  scope_drift: "范围漂移",
209
215
  openspec_cli: "OpenSpec CLI",
@@ -229,11 +235,11 @@ const TOKEN_ZH = {
229
235
  unknown: "未知",
230
236
  unexpected: "非预期",
231
237
  ambiguous: "不唯一",
232
- unresolved: "未收口",
238
+ unresolved: "未处理完",
233
239
  stale: "已过期",
234
240
  failed: "失败",
235
241
  incomplete: "不完整",
236
- blocked: "被阻塞",
242
+ blocked: "未通过",
237
243
  unavailable: "不可用",
238
244
  mismatch: "不匹配",
239
245
  duplicate: "重复",
@@ -274,15 +280,15 @@ const TOKEN_ZH = {
274
280
  native: "本地",
275
281
  subagent: "子代理",
276
282
  evidence: "证据",
277
- adjudication: "裁决",
283
+ adjudication: "最终判断",
278
284
  claim: "结论项",
279
285
  claims: "结论项",
280
- finding: "问题项",
281
- findings: "问题项",
286
+ finding: "问题",
287
+ findings: "问题",
282
288
  complete: "完成",
283
289
  propose: "提案",
284
290
  apply: "实现",
285
- gate: "关卡",
291
+ gate: "阶段",
286
292
  rereview: "重新审查",
287
293
  rollback: "回滚",
288
294
  target: "目标",
@@ -309,7 +315,7 @@ export function command_zh(command) {
309
315
  return COMMAND_ZH[command] ?? { label_zh: noun_phrase_zh(command) ?? "内部命令", hint_zh: "这是内部命令标识;界面层应优先展示对应的中文说明。" };
310
316
  }
311
317
  export function gate_zh(gate) {
312
- return GATE_ZH[gate] ?? { label_zh: noun_phrase_zh(gate) ?? "内部流程关卡", hint_zh: "这是内部流程关卡标识;界面层应优先展示对应的中文说明。" };
318
+ return GATE_ZH[gate] ?? { label_zh: noun_phrase_zh(gate) ?? "内部流程阶段", hint_zh: "这是内部流程阶段标识;界面层应优先展示对应的中文说明。" };
313
319
  }
314
320
  export function reason_zh(code) {
315
321
  return REASON_ZH[code] ?? auto_reason_zh(code);
@@ -318,10 +324,10 @@ export function decision_zh(decision) {
318
324
  if (decision === "allow")
319
325
  return "通过";
320
326
  if (decision === "block")
321
- return "阻塞";
327
+ return "未通过";
322
328
  if (decision === "status")
323
329
  return "状态";
324
- return "内部决策";
330
+ return "内部结果";
325
331
  }
326
332
  function hasHan(text) {
327
333
  return /[\u3400-\u9fff]/u.test(text);
@@ -371,7 +377,7 @@ function auto_reason_zh(code) {
371
377
  [/^unknown_(.+)$/u, (subject) => render(subject, "未知", "无法被当前流程识别。")],
372
378
  [/^unexpected_(.+)$/u, (subject) => render(subject, "出现非预期的", "超出了当前流程允许范围。")],
373
379
  [/^ambiguous_(.+)$/u, (subject) => render(subject, "", "不唯一,需要先消歧。")],
374
- [/^unresolved_(.+)$/u, (subject) => render(subject, "", "尚未收口,需要先处理。")],
380
+ [/^unresolved_(.+)$/u, (subject) => render(subject, "", "尚未处理完成,需要先处理。")],
375
381
  [/^stale_(.+)$/u, (subject) => render(subject, "", "已经过期,需要基于最新内容重做。")],
376
382
  [/^(.+)_failed$/u, (subject) => render(subject, "", "未通过当前检查。")],
377
383
  [/^(.+)_invalid$/u, (subject) => render(subject, "", "不符合预期格式或约束。")],
@@ -382,7 +388,7 @@ function auto_reason_zh(code) {
382
388
  [/^(.+)_unloaded$/u, (subject) => render(subject, "", "尚未被正确加载。")],
383
389
  [/^(.+)_mismatch$/u, (subject) => render(subject, "", "与预期不一致。")],
384
390
  [/^(.+)_duplicate$/u, (subject) => render(subject, "", "出现重复。")],
385
- [/^(.+)_blocked$/u, (subject) => render(subject, "", "当前被阻塞。")],
391
+ [/^(.+)_blocked$/u, (subject) => render(subject, "", "当前未通过。")],
386
392
  [/^(.+)_required$/u, (subject) => render(subject, "需要", "是当前流程的必需项。")],
387
393
  [/^(.+)_not_honored$/u, (subject) => render(subject, "", "尚未被满足。")],
388
394
  [/^(.+)_not_ready$/u, (subject) => render(subject, "", "尚未就绪。")],
@@ -397,12 +403,25 @@ function auto_reason_zh(code) {
397
403
  }
398
404
  const label = noun_phrase_zh(code);
399
405
  if (label)
400
- return { label_zh: label, hint_zh: "当前流程触发了这条阻塞原因,请结合上下文继续排查。" };
401
- return fallbackZh("阻塞原因");
406
+ return { label_zh: label, hint_zh: "当前流程触发了这条未通过原因,请结合上下文继续排查。" };
407
+ return fallbackZh("未通过原因");
402
408
  }
403
409
  function translate_message_tokens_zh(raw) {
404
410
  let out = raw;
405
411
  const phraseReplacements = [
412
+ [/main_review_digest/giu, "审查问题记录"],
413
+ [/user_review_decision/giu, "用户确认记录"],
414
+ [/review_standing_authorization/giu, "长期授权记录"],
415
+ [/review_round_id/giu, "审查轮次编号"],
416
+ [/finding_uid/giu, "问题唯一标识"],
417
+ [/decision_scope_key/giu, "确认范围标识"],
418
+ [/confirmed_refs/giu, "已确认内容引用"],
419
+ [/source_review_evidence_refs/giu, "来源审查证据引用"],
420
+ [/previous_digest_refs/giu, "上一轮审查问题记录引用"],
421
+ [/finding_dispositions/giu, "问题处理结果"],
422
+ [/needs_user_decision/giu, "等待用户确认"],
423
+ [/ledger_injection_missing/giu, "缺少问题清单"],
424
+ [/round_budget_exhausted/giu, "审查轮次已达上限"],
406
425
  [/source_guidance evidence/giu, "审查指导证据"],
407
426
  [/verification_review evidence/giu, "验证审查证据"],
408
427
  [/final_test pass evidence/giu, "最终测试通过证据"],
@@ -501,7 +520,8 @@ export function translate_action_zh(action) {
501
520
  if (exact)
502
521
  return exact;
503
522
  const patterns = [
504
- [/^managed SuperSpec surfaces removed; run superspec init --scope (.+) to reinstall$/u, (scope) => `受管 SuperSpec 内容已移除;如需重新安装,请运行 superspec init --scope ${scope}。`],
523
+ [/^managed SuperSpec surfaces removed; run superspec init --scope (.+) to reinstall$/u, (scope) => `SuperSpec 管理的文件已移除;如需重新安装,请运行 superspec init --scope ${scope};Windows PowerShell 中使用 superspec.cmd init --scope ${scope}。`],
524
+ [/^install or upgrade @fission-ai\/openspec >= ([0-9.]+), then rerun `superspec init --scope (.+)`$/u, (version, scope) => `先安装或升级 @fission-ai/openspec 到 ${version} 或更高版本,然后重新运行 superspec init --scope ${scope};Windows PowerShell 中使用 superspec.cmd init --scope ${scope}。`],
505
525
  [/^fix ([A-Za-z0-9_.-]+) reasons, then rerun(?: (.+))?$/u, (problem, cmd) => {
506
526
  const label = display_term_zh(problem);
507
527
  return cmd ? `先处理${label}对应问题,然后重新运行相关命令。` : `先处理${label}对应问题,然后重新运行相关检查。`;
@@ -510,42 +530,42 @@ export function translate_action_zh(action) {
510
530
  [/^collect ([A-Za-z0-9_.-]+) source_guidance from missing roles: (.+)$/u, (gate, roles) => `补齐${display_term_zh(gate)}所缺的审查指导证据(角色:${render_roles_zh(roles)})。`],
511
531
  [/^collect verification_review evidence from missing roles: (.+)$/u, (roles) => `补齐验证审查证据(角色:${render_roles_zh(roles)})。`],
512
532
  [/^repair source_guidance evidence fields so every review lane has base\/head refs, reviewed_files, and rollback_targets$/u, () => "修复审查指导证据字段,确保每条审查线都包含基线/当前提交引用、审查文件列表和回滚目标。"],
513
- [/^write exactly one live main_adjudication referencing every live source_guidance and final verification evidence$/u, () => "补写且仅保留一条有效的主线程裁决记录,并引用全部有效的审查指导证据和最终验证证据。"],
514
- [/^repair main_adjudication so it references all source_guidance\/final verification evidence and explicitly covers required loads, claims, and blocking findings$/u, () => "修复主线程裁决记录,确保它引用全部审查指导证据与最终验证证据,并明确覆盖必需加载项、结论项和阻塞问题项。"],
533
+ [/^write exactly one live main_adjudication referencing every live source_guidance and final verification evidence$/u, () => "补写且仅保留一条有效的主流程最终判断记录,并引用全部有效的审查指导证据和最终验证证据。"],
534
+ [/^repair main_adjudication so it references all source_guidance\/final verification evidence and explicitly covers required loads, claims, and blocking findings$/u, () => "修复主流程最终判断记录,确保它引用全部审查指导证据与最终验证证据,并明确覆盖必需加载项、结论项和阻塞问题。"],
515
535
  [/^repair verification_review references so openspec_validate_ref, matrices, scope drift report, and test evidence refs are readable$/u, () => "修复验证审查证据引用,确保 OpenSpec 校验引用、各类矩阵、范围漂移报告和测试证据引用都可读取。"],
516
536
  [/^resolve scope drift before review close: narrow the change or record accepted\/none with evidence$/u, () => "先处理范围漂移,再结束审查:要么收窄本次变更范围,要么补充证据后明确记录为已接受或无漂移。"],
517
537
  [/^record ([A-Za-z0-9_.-]+) pass evidence and reference it from ([A-Za-z0-9_.-]+)$/u, (kind, target) => `记录${display_term_zh(kind)},并在${display_term_zh(target)}中引用它。`],
518
538
  [/^AskUserQuestion for apply isolation\/execution mode and record gate="apply_isolation" human_confirmation$/u, () => "请先让用户确认实现隔离范围或执行方式,并记录对应的人工确认证据。"],
519
- [/^run the proposal critic review \(round-tagged, findings\[\]\) and record a main_review_digest disclosing every finding$/u, () => "先完成提案的对抗审查,并记录一条向用户完整披露所有问题的主线程审查披露记录。"],
539
+ [/^run the proposal critic review \(round-tagged, findings\[\]\) and record a main_review_digest disclosing every finding$/u, () => "先完成提案的严格审查,并记录一条向用户说明所有问题的审查问题记录。"],
520
540
  [/^pass (.+) before entering (.+)$/u, (name, stage) => `进入${display_term_zh(stage)}之前先通过${display_term_zh(name)}。`],
521
541
  [/^pass (.+) first$/u, (name) => `先通过${display_term_zh(name)}。`],
522
542
  [/^pass (.+)$/u, (name) => `先通过${display_term_zh(name)}。`],
523
543
  [/^rerun (.+)$/u, (cmd) => `重新运行${display_term_zh(cmd)}。`],
524
544
  [/^run (.+)$/u, (cmd) => `运行${display_term_zh(cmd)}。`],
525
545
  [/^fix (.+), then rerun (.+)$/u, (_problem, cmd) => `先修复相关问题,然后重新运行 ${cmd}。`],
526
- [/^fix (.+)$/u, (problem) => hasHan(problem) ? `修复 ${problem}。` : "先修复当前阻塞项。"],
546
+ [/^fix (.+)$/u, (problem) => hasHan(problem) ? `修复 ${problem}。` : "先修复当前未通过项。"],
527
547
  [/^inspect (.+), then rerun (.+)$/u, (target, cmd) => `先检查 ${target},然后重新运行 ${cmd}。`],
528
548
  [/^keep (.+) checked, fix (.+), then rerun (.+)$/u, (target, _problem, cmd) => `保持 ${target} 处于已勾选状态,修复当前问题,然后重新运行 ${cmd}。`],
529
549
  [/^continue with (.+)$/u, (what) => `继续执行 ${what}。`],
530
550
  [/^record (.+) human_confirmation evidence, then rerun (.+)$/u, (kind, cmd) => `记录${display_term_zh(kind)}对应的人工确认证据,然后重新运行${display_term_zh(cmd)}。`],
531
- [/^stop review completion and hand off to apply task_reopen for (.+)$/u, (taskIds) => `停止当前审查收口,转回实现阶段处理任务重开:${taskIds}。`],
532
- [/^stop review completion and hand off to apply task_reopen$/u, () => "停止当前审查收口,转回实现阶段处理任务重开。"],
533
- [/^stop review completion and hand off to propose\/change update$/u, () => "停止当前审查收口,转回提案阶段处理变更回改。"],
534
- [/^do not add allow-path verification_review\/final_test evidence to this request_changes round$/u, () => "本轮打回处理中不要补写仅用于放行路径的验证审查证据或最终测试通过证据。"],
551
+ [/^stop review completion and hand off to apply task_reopen for (.+)$/u, (taskIds) => `停止当前审查完成流程,转回实现阶段处理任务重开:${taskIds}。`],
552
+ [/^stop review completion and hand off to apply task_reopen$/u, () => "停止当前审查完成流程,转回实现阶段处理任务重开。"],
553
+ [/^stop review completion and hand off to propose\/change update$/u, () => "停止当前审查完成流程,转回提案阶段处理变更回改。"],
554
+ [/^do not add allow-path verification_review\/final_test evidence to this request_changes round$/u, () => "本轮打回处理中不要补写仅用于通过审查的验证审查证据或最终测试通过证据。"],
535
555
  [/^repair request_changes_route and hand off the review round before retrying review_complete$/u, () => "先修复打回路由并完成当前审查轮次交接,再重新尝试审查完成检查。"],
536
- [/^AskUserQuestion for the failed-verification disposition \(fix or accept deviation\) and record gate="verify_failure_handling" human_confirmation referencing the failed evidence ids$/u, () => "把失败验证的处置交给用户拍板:选择继续修复还是接受偏差,并补记引用失败证据的人工确认。"],
556
+ [/^AskUserQuestion for the failed-verification disposition \(fix or accept deviation\) and record gate="verify_failure_handling" human_confirmation referencing the failed evidence ids$/u, () => "请用户确认失败验证的处理方式:继续修复,或接受这次偏差;并补记引用失败证据的人工确认。"],
537
557
  ];
538
558
  for (const [pattern, render] of patterns) {
539
559
  const match = action.match(pattern);
540
560
  if (match)
541
561
  return render(...match.slice(1));
542
562
  }
543
- return "先处理当前阻塞项,再重新运行相关检查。";
563
+ return "先处理当前未通过项,再重新运行相关检查。";
544
564
  }
545
565
  export function reason_message_zh(code, rawMessage, refs = []) {
546
566
  const zh = reason_zh(code);
547
567
  if (hasHan(rawMessage))
548
- return `${rawMessage}${refs_suffix_zh(refs)}`;
568
+ return `${translate_message_tokens_zh(rawMessage)}${refs_suffix_zh(refs)}`;
549
569
  if (Object.prototype.hasOwnProperty.call(REASON_ZH, code) && zh.hint_zh)
550
570
  return `${zh.label_zh}:${zh.hint_zh}${refs_suffix_zh(refs)}`;
551
571
  const translated = translate_message_tokens_zh(rawMessage);
@@ -581,7 +601,7 @@ export function action_label_zh(action) {
581
601
  if (match)
582
602
  return render(...match.slice(1));
583
603
  }
584
- return "执行受管文件操作";
604
+ return "执行由 SuperSpec 管理的文件操作";
585
605
  }
586
606
  export function action_detail_zh(detail) {
587
607
  const patterns = [
@@ -589,9 +609,9 @@ export function action_detail_zh(detail) {
589
609
  [/^pre-existing file with different content kept; rerun with --force to overwrite \(backs up \*\.bak\)$/u, () => "已有不同内容文件已保留;如需覆盖,请使用 --force,覆盖前会备份为 *.bak。"],
590
610
  [/^preexisting file is never touched$/u, () => "既有文件不会被改动。"],
591
611
  [/^user-modified file kept; new version written to (.+)$/u, (target) => `已保留用户修改版本;新版本写入 ${target}。`],
592
- [/^managed file no longer shipped$/u, () => "该受管文件在当前版本中已不再分发。"],
612
+ [/^managed file no longer shipped$/u, () => "该文件在当前版本中已不再分发。"],
593
613
  [/^no longer shipped but user-modified; kept$/u, () => "该文件在当前版本中已不再分发,但因存在用户修改而被保留。"],
594
- [/^preexisting\/unmanaged file is never touched$/u, () => "既有或非受管文件不会被改动。"],
614
+ [/^preexisting\/unmanaged file is never touched$/u, () => "既有文件或非 SuperSpec 管理的文件不会被改动。"],
595
615
  [/^already absent$/u, () => "该文件原本就不存在。"],
596
616
  [/^user-modified since install; kept$/u, () => "该文件在安装后已被用户修改,因此会被保留。"],
597
617
  ];
@@ -1,4 +1,4 @@
1
- import { runCommand } from "./core.ts";
1
+ import { runCommand, type OpenspecCliProbe } from "./core.ts";
2
2
  import { type InstallScope } from "./install_engine.ts";
3
3
  type InitArgs = {
4
4
  path: string;
@@ -16,10 +16,12 @@ export declare function maybe_install_missing_openspec(opts: {
16
16
  commandExistsFn?: (cmd: string, meta?: {
17
17
  cwd?: string;
18
18
  }) => boolean;
19
- confirm?: (question: string) => Promise<boolean>;
19
+ probeOpenspecFn?: (meta?: {
20
+ cwd?: string;
21
+ }) => OpenspecCliProbe;
20
22
  run?: typeof runCommand;
21
23
  writeStderr?: (text: string) => void;
22
- }): Promise<"not-needed" | "installed" | "skipped" | "failed">;
24
+ }): "not-needed" | "installed" | "skipped" | "failed";
23
25
  export declare function main_init(argv?: string[]): number;
24
26
  export declare function main_init_async(argv?: string[]): Promise<number>;
25
27
  export {};
@@ -1,5 +1,5 @@
1
- import { block, commandExists, GuardError, printDecision, reason, runCommand } from "./core.js";
2
- import { project_init, recommended_openspec_install_plan } from "./project_init.js";
1
+ import { block, commandExists, GuardError, printDecision, reason, runCommand, openspec_cli_probe, REQUIRED_OPENSPEC_MIN_VERSION, } from "./core.js";
2
+ import { forced_openspec_install_plan, project_init, recommended_openspec_install_plan } from "./project_init.js";
3
3
  import { install_workflow, uninstall_workflow, update_workflow } from "./install_engine.js";
4
4
  import { homedir } from "node:os";
5
5
  import { resolve } from "node:path";
@@ -9,7 +9,7 @@ function usage() {
9
9
  return "usage: superspec init [-h] [--scope {project,user}] [--path PATH] [--codex-home PATH] [--create] [--update] [--uninstall] [--dry-run] [--force]\n";
10
10
  }
11
11
  function help() {
12
- return `${usage()}\n可选参数:\n -h, --help 显示帮助并退出\n --scope {project,user} 安装到当前项目的 .codex 目录,或安装到用户级 Codex 目录(默认:project)\n --project 等价于 --scope project\n --user 等价于 --scope user\n --global 兼容别名,等价于 --user\n --path PATH --scope project 时使用的项目根目录(默认:当前目录)\n --codex-home PATH --scope user 时使用的 Codex 用户目录(默认:$CODEX_HOME 或 ~/.codex)\n --create 兼容参数;init 默认就会创建缺失内容\n --update 按 manifest 更新受管 SuperSpec 内容;用户改动文件保留,新的版本写入 *.new\n --uninstall 按 manifest 卸载受管 SuperSpec 内容;.superspec 数据与既有/用户改动文件会保留\n --dry-run 配合 --uninstall 时只预览将删除的文件,不实际修改\n --force 安装时覆盖已有且内容不同的文件,并保留 *.bak 备份\n`;
12
+ return `${usage()}\n可选参数:\n -h, --help 显示帮助并退出\n --scope {project,user} 安装到当前项目的 .codex 目录,或安装到用户级 Codex 目录(默认:project)\n --project 等价于 --scope project\n --user 等价于 --scope user\n --global 兼容别名,等价于 --user\n --path PATH --scope project 时使用的项目根目录(默认:当前目录)\n --codex-home PATH --scope user 时使用的 Codex 用户目录(默认:$CODEX_HOME 或 ~/.codex)\n --create 兼容参数;init 默认就会创建缺失内容\n --update 按 manifest 更新 SuperSpec 管理的文件;用户改动文件保留,新的版本写入 *.new\n --uninstall 按 manifest 卸载 SuperSpec 管理的文件;.superspec 数据与既有/用户改动文件会保留\n --dry-run 配合 --uninstall 时只预览将删除的文件,不实际修改\n --force 安装时覆盖已有且内容不同的文件,并保留 *.bak 备份\n`;
13
13
  }
14
14
  function parse_init_argv(argv) {
15
15
  const getValue = (flag) => {
@@ -56,6 +56,12 @@ function commandFailure(proc) {
56
56
  return `安装命令执行失败(退出状态码 ${proc.status})。`;
57
57
  return "安装命令执行失败,请查看终端日志后重试。";
58
58
  }
59
+ function shouldRetryOpenSpecInstallWithForce(proc) {
60
+ const output = `${proc.error?.message ?? ""}\n${proc.stderr}\n${proc.stdout}`;
61
+ const binConflict = /\bEEXIST\b|already exists|file exists|Refusing to delete|will not overwrite|would overwrite/iu.test(output);
62
+ const openspecBin = /\bopenspec(?:\.(?:cmd|ps1))?\b/iu.test(output);
63
+ return binConflict && openspecBin;
64
+ }
59
65
  function engineDecision(gate, projectRoot, result, nextActions) {
60
66
  if (result.problems.length > 0) {
61
67
  const decision = block("project", gate, result.problems.map((item) => reason(`${gate}_failed`, item)));
@@ -92,56 +98,60 @@ async function promptInstallScope() {
92
98
  function canPrompt() {
93
99
  return Boolean(process.stdin.isTTY && process.stderr.isTTY);
94
100
  }
95
- async function promptYesNo(question, defaultYes = true) {
96
- const rl = createInterface({ input: process.stdin, output: process.stderr });
97
- const suffix = defaultYes ? "[Y/n]" : "[y/N]";
98
- try {
99
- for (;;) {
100
- const answer = (await rl.question(`${question} ${suffix} `)).trim().toLowerCase();
101
- if (answer === "")
102
- return defaultYes;
103
- if (["y", "yes", "是", "好", "确认"].includes(answer))
104
- return true;
105
- if (["n", "no", "否", "不", "取消"].includes(answer))
106
- return false;
107
- process.stderr.write("请输入 y / n,或输入 是 / 否。\n");
108
- }
109
- }
110
- finally {
111
- rl.close();
112
- }
113
- }
114
- export async function maybe_install_missing_openspec(opts) {
101
+ export function maybe_install_missing_openspec(opts) {
115
102
  const commandExistsFn = opts.commandExistsFn ?? ((cmd, meta) => commandExists(cmd, { cwd: meta?.cwd }));
116
- if (opts.scope !== "project" || opts.mode !== "install")
103
+ if (opts.mode !== "install")
117
104
  return "not-needed";
118
- if (commandExistsFn("openspec", { cwd: opts.cwd }))
105
+ const run = opts.run ?? runCommand;
106
+ const probeOpenspecFn = opts.probeOpenspecFn ?? ((meta) => openspec_cli_probe({ cwd: meta?.cwd, commandExistsFn, run }));
107
+ const before = probeOpenspecFn({ cwd: opts.cwd });
108
+ if (before.ok)
119
109
  return "not-needed";
120
- if (!(opts.interactive ?? canPrompt()))
121
- return "skipped";
122
- const plan = recommended_openspec_install_plan({ cwd: opts.cwd, commandExistsFn });
123
- if (plan === null)
124
- return "skipped";
125
- const confirm = opts.confirm ?? ((question) => promptYesNo(question));
126
- const accepted = await confirm(`未检测到 openspec CLI。是否现在尝试自动安装?\n将执行:${plan.rendered}`);
127
- if (!accepted)
128
- return "skipped";
129
110
  const writeStderr = opts.writeStderr ?? ((text) => {
130
111
  process.stderr.write(text);
131
112
  });
113
+ const plan = recommended_openspec_install_plan({ cwd: opts.cwd, commandExistsFn });
114
+ if (plan === null) {
115
+ writeStderr(`${before.message}。SuperSpec 需要 @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION},但未找到 npm、pnpm、yarn 或 bun,无法自动安装。\n`);
116
+ return "skipped";
117
+ }
118
+ writeStderr(`${before.message}。SuperSpec 需要 @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION},将自动安装或升级。\n`);
132
119
  writeStderr(`正在安装 OpenSpec CLI:${plan.rendered}\n`);
133
- const proc = (opts.run ?? runCommand)(plan.cmd, plan.args, { cwd: opts.cwd, timeout: 300_000 });
120
+ let proc = run(plan.cmd, plan.args, { cwd: opts.cwd, timeout: 300_000 });
121
+ if ((proc.error || proc.status !== 0) && shouldRetryOpenSpecInstallWithForce(proc)) {
122
+ const forcedPlan = forced_openspec_install_plan(plan);
123
+ writeStderr(`OpenSpec CLI 安装遇到全局 bin 冲突,正在覆盖重试:${forcedPlan.rendered}\n`);
124
+ proc = run(forcedPlan.cmd, forcedPlan.args, { cwd: opts.cwd, timeout: 300_000 });
125
+ }
134
126
  if (proc.error || proc.status !== 0) {
135
127
  writeStderr(`自动安装 OpenSpec CLI 失败:${commandFailure(proc)}\n`);
136
128
  return "failed";
137
129
  }
138
- if (!commandExistsFn("openspec", { cwd: opts.cwd })) {
139
- writeStderr("安装命令已完成,但当前 PATH 里仍未检测到 `openspec`。请重新打开终端或确认全局 bin 已在 PATH 中,然后重新运行 superspec init。\n");
130
+ const after = probeOpenspecFn({ cwd: opts.cwd });
131
+ if (!after.ok) {
132
+ writeStderr(`安装命令已完成,但当前 PATH 里的 openspec 仍不可用:${after.message}。请重新打开终端或确认全局 bin 已在 PATH 中,然后重新运行 superspec init;Windows PowerShell 请运行 superspec.cmd init。\n`);
140
133
  return "failed";
141
134
  }
142
- writeStderr("OpenSpec CLI 安装完成,继续执行 superspec init。\n");
135
+ writeStderr("OpenSpec CLI 安装或升级完成,继续执行 superspec init。\n");
143
136
  return "installed";
144
137
  }
138
+ function openspecPreflightBlocked(args, scope, installResult) {
139
+ const targetRoot = scope === "user" ? args.codexHome : args.path;
140
+ const code = installResult === "skipped" ? "openspec_auto_install_unavailable" : "openspec_auto_install_failed";
141
+ const message = installResult === "skipped"
142
+ ? `OpenSpec CLI 不满足 SuperSpec 要求,且未找到可用包管理器自动安装 @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION}。`
143
+ : `OpenSpec CLI 自动安装或升级 @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION} 未完成。`;
144
+ const decision = block("project", "openspec_preflight", [reason(code, message)], {
145
+ next_actions: [`install or upgrade @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION}, then rerun \`superspec init --scope ${scope}\``],
146
+ });
147
+ printDecision({
148
+ ...decision,
149
+ project_root: args.path,
150
+ install_scope: scope,
151
+ install_root: targetRoot,
152
+ }, { command: "init" });
153
+ return 1;
154
+ }
145
155
  function run_init(args, scope) {
146
156
  const targetRoot = scope === "user" ? args.codexHome : args.path;
147
157
  const gatePrefix = scope === "user" ? "user" : "project";
@@ -176,7 +186,11 @@ export function main_init(argv = process.argv.slice(2)) {
176
186
  return 0;
177
187
  }
178
188
  const args = parse_init_argv(argv);
179
- return run_init(args, args.scope ?? "project");
189
+ const scope = args.scope ?? "project";
190
+ const openspecInstall = maybe_install_missing_openspec({ cwd: args.path, scope, mode: args.mode });
191
+ if (openspecInstall === "failed" || openspecInstall === "skipped")
192
+ return openspecPreflightBlocked(args, scope, openspecInstall);
193
+ return run_init(args, scope);
180
194
  }
181
195
  catch (err) {
182
196
  const change = "project";
@@ -193,7 +207,9 @@ export async function main_init_async(argv = process.argv.slice(2)) {
193
207
  }
194
208
  const args = parse_init_argv(argv);
195
209
  const scope = args.scope ?? (canPrompt() ? await promptInstallScope() : "project");
196
- await maybe_install_missing_openspec({ cwd: args.path, scope, mode: args.mode });
210
+ const openspecInstall = maybe_install_missing_openspec({ cwd: args.path, scope, mode: args.mode });
211
+ if (openspecInstall === "failed" || openspecInstall === "skipped")
212
+ return openspecPreflightBlocked(args, scope, openspecInstall);
197
213
  return run_init(args, scope);
198
214
  }
199
215
  catch (err) {