@chenmk/superflow 0.4.0 → 0.5.1

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.
Files changed (242) hide show
  1. package/INSTALL.en.md +58 -12
  2. package/INSTALL.md +62 -33
  3. package/README.md +181 -54
  4. package/README.zh-CN.md +158 -51
  5. package/assets/brand/managed-review-loop.png +0 -0
  6. package/assets/manifest.json +10 -5
  7. package/assets/scripts/superflow-coding-ready.mjs +135 -0
  8. package/assets/scripts/superflow-dependency-update-hook.sh +81 -8
  9. package/assets/scripts/superflow-document-audit.mjs +240 -0
  10. package/assets/scripts/superflow-environment-preflight.mjs +398 -0
  11. package/assets/scripts/superflow-hook-guard.sh +40 -1
  12. package/assets/scripts/superflow-managed-command-guard.py +309 -0
  13. package/assets/scripts/superflow-managed-executor-preflight.mjs +421 -0
  14. package/assets/scripts/superflow-managed-owner-verification.sh +117 -0
  15. package/assets/scripts/superflow-managed-work-check.mjs +287 -26
  16. package/assets/scripts/superflow-managed-work-guard.sh +6 -92
  17. package/assets/skills/superflow-clarify/SKILL.md +14 -0
  18. package/assets/skills/superflow-design/SKILL.md +38 -13
  19. package/assets/skills/superflow-docs/SKILL.md +93 -2
  20. package/assets/skills/superflow-pipeline/SKILL.md +167 -10
  21. package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +17 -9
  22. package/assets/skills/superflow-pipeline/references/document-readiness.md +157 -0
  23. package/assets/skills/superflow-pipeline/references/document-templates.md +9 -5
  24. package/assets/skills/superflow-pipeline/references/feature-gated-workflow.md +14 -0
  25. package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +20 -11
  26. package/assets/skills/superflow-pipeline/references/managed-work.md +123 -15
  27. package/assets/skills/superflow-pipeline/references/orchestration.md +2 -2
  28. package/assets/skills/superflow-pipeline/references/p0-baseline-template.md +3 -2
  29. package/assets/skills/superflow-pipeline/references/quality-gate.md +7 -5
  30. package/assets/skills/superflow-pipeline/references/reviewer-checklist.md +7 -0
  31. package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +205 -9
  32. package/assets/skills/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
  33. package/assets/skills/superflow-requirement-review/SKILL.md +178 -0
  34. package/assets/skills/superflow-requirement-review/agents/openai.yaml +4 -0
  35. package/assets/skills-en/superflow-clarify/SKILL.md +17 -0
  36. package/assets/skills-en/superflow-design/SKILL.md +31 -12
  37. package/assets/skills-en/superflow-docs/SKILL.md +99 -0
  38. package/assets/skills-en/superflow-pipeline/SKILL.md +163 -9
  39. package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +4 -0
  40. package/assets/skills-en/superflow-pipeline/references/document-readiness.md +125 -0
  41. package/assets/skills-en/superflow-pipeline/references/document-templates.md +10 -0
  42. package/assets/skills-en/superflow-pipeline/references/feature-gated-workflow.md +7 -0
  43. package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +7 -0
  44. package/assets/skills-en/superflow-pipeline/references/managed-work.md +160 -10
  45. package/assets/skills-en/superflow-pipeline/references/quality-gate.md +12 -0
  46. package/assets/skills-en/superflow-pipeline/references/reviewer-checklist.md +7 -0
  47. package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +205 -9
  48. package/assets/skills-en/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
  49. package/assets/skills-en/superflow-requirement-review/SKILL.md +99 -0
  50. package/assets/skills-en/superflow-requirement-review/agents/openai.yaml +4 -0
  51. package/dist/app/cli.js +64 -9
  52. package/dist/app/cli.js.map +1 -1
  53. package/dist/app/commands/change-guard.js +19 -2
  54. package/dist/app/commands/change-guard.js.map +1 -1
  55. package/dist/app/commands/check.js +107 -45
  56. package/dist/app/commands/check.js.map +1 -1
  57. package/dist/app/commands/docs.js +7 -3
  58. package/dist/app/commands/docs.js.map +1 -1
  59. package/dist/app/commands/doctor.js +174 -198
  60. package/dist/app/commands/doctor.js.map +1 -1
  61. package/dist/app/commands/eval.js +64 -0
  62. package/dist/app/commands/eval.js.map +1 -0
  63. package/dist/app/commands/implement.js +7 -3
  64. package/dist/app/commands/implement.js.map +1 -1
  65. package/dist/app/commands/init.js +12 -24
  66. package/dist/app/commands/init.js.map +1 -1
  67. package/dist/app/commands/mcp.js +167 -0
  68. package/dist/app/commands/mcp.js.map +1 -0
  69. package/dist/app/commands/pipeline.js +652 -18
  70. package/dist/app/commands/pipeline.js.map +1 -1
  71. package/dist/app/commands/requirement-review.js +6 -0
  72. package/dist/app/commands/requirement-review.js.map +1 -0
  73. package/dist/app/commands/status.js +66 -3
  74. package/dist/app/commands/status.js.map +1 -1
  75. package/dist/app/commands/uninstall.js +3 -3
  76. package/dist/app/commands/uninstall.js.map +1 -1
  77. package/dist/app/commands/update.js +83 -9
  78. package/dist/app/commands/update.js.map +1 -1
  79. package/dist/domains/agent.js +3 -11
  80. package/dist/domains/agent.js.map +1 -1
  81. package/dist/domains/config/cli-help.js +54 -12
  82. package/dist/domains/config/cli-help.js.map +1 -1
  83. package/dist/domains/config/i18n.js +6 -8
  84. package/dist/domains/config/i18n.js.map +1 -1
  85. package/dist/domains/config/manifest.js +0 -3
  86. package/dist/domains/config/manifest.js.map +1 -1
  87. package/dist/domains/deps.js +68 -7
  88. package/dist/domains/deps.js.map +1 -1
  89. package/dist/domains/managed-work/completion-policy.js +174 -0
  90. package/dist/domains/managed-work/completion-policy.js.map +1 -0
  91. package/dist/domains/managed-work/context-manifest.js +208 -0
  92. package/dist/domains/managed-work/context-manifest.js.map +1 -0
  93. package/dist/domains/managed-work/contract.js +135 -4
  94. package/dist/domains/managed-work/contract.js.map +1 -1
  95. package/dist/domains/managed-work/control-signals.js +34 -0
  96. package/dist/domains/managed-work/control-signals.js.map +1 -0
  97. package/dist/domains/managed-work/control.js +628 -0
  98. package/dist/domains/managed-work/control.js.map +1 -0
  99. package/dist/domains/managed-work/evaluation.js +497 -0
  100. package/dist/domains/managed-work/evaluation.js.map +1 -0
  101. package/dist/domains/managed-work/evidence-path.js +14 -0
  102. package/dist/domains/managed-work/evidence-path.js.map +1 -0
  103. package/dist/domains/managed-work/execution-contract.js +167 -0
  104. package/dist/domains/managed-work/execution-contract.js.map +1 -0
  105. package/dist/domains/managed-work/executor-policy.js +65 -0
  106. package/dist/domains/managed-work/executor-policy.js.map +1 -0
  107. package/dist/domains/managed-work/failure.js +65 -0
  108. package/dist/domains/managed-work/failure.js.map +1 -0
  109. package/dist/domains/managed-work/host-review.js +77 -0
  110. package/dist/domains/managed-work/host-review.js.map +1 -0
  111. package/dist/domains/managed-work/human-messages.js +31 -0
  112. package/dist/domains/managed-work/human-messages.js.map +1 -0
  113. package/dist/domains/managed-work/input.js +29 -3
  114. package/dist/domains/managed-work/input.js.map +1 -1
  115. package/dist/domains/managed-work/journal.js +77 -3
  116. package/dist/domains/managed-work/journal.js.map +1 -1
  117. package/dist/domains/managed-work/lock.js +67 -1
  118. package/dist/domains/managed-work/lock.js.map +1 -1
  119. package/dist/domains/managed-work/notifications.js +0 -15
  120. package/dist/domains/managed-work/notifications.js.map +1 -1
  121. package/dist/domains/managed-work/pair-admission.js +108 -0
  122. package/dist/domains/managed-work/pair-admission.js.map +1 -0
  123. package/dist/domains/managed-work/prompts.js +257 -22
  124. package/dist/domains/managed-work/prompts.js.map +1 -1
  125. package/dist/domains/managed-work/redaction.js +90 -0
  126. package/dist/domains/managed-work/redaction.js.map +1 -0
  127. package/dist/domains/managed-work/review-convergence.js +41 -0
  128. package/dist/domains/managed-work/review-convergence.js.map +1 -0
  129. package/dist/domains/managed-work/review-facts.js +181 -0
  130. package/dist/domains/managed-work/review-facts.js.map +1 -0
  131. package/dist/domains/managed-work/rule-selection.js +84 -0
  132. package/dist/domains/managed-work/rule-selection.js.map +1 -0
  133. package/dist/domains/managed-work/runner.js +1967 -175
  134. package/dist/domains/managed-work/runner.js.map +1 -1
  135. package/dist/domains/managed-work/schemas.js +72 -2
  136. package/dist/domains/managed-work/schemas.js.map +1 -1
  137. package/dist/domains/managed-work/service.js +82 -13
  138. package/dist/domains/managed-work/service.js.map +1 -1
  139. package/dist/domains/managed-work/state.js +195 -5
  140. package/dist/domains/managed-work/state.js.map +1 -1
  141. package/dist/domains/managed-work/storage.js +66 -24
  142. package/dist/domains/managed-work/storage.js.map +1 -1
  143. package/dist/domains/managed-work/types.js +12 -9
  144. package/dist/domains/managed-work/types.js.map +1 -1
  145. package/dist/domains/managed-work/verification-categories.js +39 -0
  146. package/dist/domains/managed-work/verification-categories.js.map +1 -0
  147. package/dist/domains/managed-work/verification-policy.js +116 -0
  148. package/dist/domains/managed-work/verification-policy.js.map +1 -0
  149. package/dist/domains/managed-work/wait.js +21 -3
  150. package/dist/domains/managed-work/wait.js.map +1 -1
  151. package/dist/domains/managed-work/workspace-binding.js +133 -0
  152. package/dist/domains/managed-work/workspace-binding.js.map +1 -0
  153. package/dist/domains/sdd-readiness.js +44 -0
  154. package/dist/domains/sdd-readiness.js.map +1 -0
  155. package/dist/domains/skill/deploy.js +0 -6
  156. package/dist/domains/skill/deploy.js.map +1 -1
  157. package/dist/domains/skill/scripts.js +0 -8
  158. package/dist/domains/skill/scripts.js.map +1 -1
  159. package/dist/domains/state.js +1 -2
  160. package/dist/domains/state.js.map +1 -1
  161. package/dist/mcp/runtime-identity.js +21 -0
  162. package/dist/mcp/runtime-identity.js.map +1 -0
  163. package/dist/mcp/server.js +369 -0
  164. package/dist/mcp/server.js.map +1 -0
  165. package/dist/platform/agent-process.js +499 -38
  166. package/dist/platform/agent-process.js.map +1 -1
  167. package/dist/platform/executable.js +28 -0
  168. package/dist/platform/executable.js.map +1 -0
  169. package/dist/platform/paths.js +1 -17
  170. package/dist/platform/paths.js.map +1 -1
  171. package/dist/platform/process-tree.js +44 -0
  172. package/dist/platform/process-tree.js.map +1 -0
  173. package/dist/platform/process.js +1 -1
  174. package/dist/platform/process.js.map +1 -1
  175. package/package.json +17 -7
  176. package/scripts/check-design-principles.mjs +107 -0
  177. package/scripts/check-managed-work-design.mjs +507 -0
  178. package/scripts/clean-dist.js +9 -0
  179. package/scripts/ensure-bin-modes.js +11 -0
  180. package/scripts/preinstall.js +57 -0
  181. package/dist/cli/index.js +0 -193
  182. package/dist/cli/index.js.map +0 -1
  183. package/dist/commands/archive.js +0 -6
  184. package/dist/commands/archive.js.map +0 -1
  185. package/dist/commands/clarify.js +0 -6
  186. package/dist/commands/clarify.js.map +0 -1
  187. package/dist/commands/design.js +0 -28
  188. package/dist/commands/design.js.map +0 -1
  189. package/dist/commands/docs.js +0 -6
  190. package/dist/commands/docs.js.map +0 -1
  191. package/dist/commands/doctor.js +0 -494
  192. package/dist/commands/doctor.js.map +0 -1
  193. package/dist/commands/implement.js +0 -6
  194. package/dist/commands/implement.js.map +0 -1
  195. package/dist/commands/init.js +0 -520
  196. package/dist/commands/init.js.map +0 -1
  197. package/dist/commands/pipeline.js +0 -6
  198. package/dist/commands/pipeline.js.map +0 -1
  199. package/dist/commands/scan.js +0 -63
  200. package/dist/commands/scan.js.map +0 -1
  201. package/dist/commands/status.js +0 -173
  202. package/dist/commands/status.js.map +0 -1
  203. package/dist/commands/uninstall.js +0 -214
  204. package/dist/commands/uninstall.js.map +0 -1
  205. package/dist/commands/update.js +0 -193
  206. package/dist/commands/update.js.map +0 -1
  207. package/dist/commands/verify.js +0 -6
  208. package/dist/commands/verify.js.map +0 -1
  209. package/dist/core/assets.js +0 -27
  210. package/dist/core/assets.js.map +0 -1
  211. package/dist/core/cli-help.js +0 -98
  212. package/dist/core/cli-help.js.map +0 -1
  213. package/dist/core/context.js +0 -123
  214. package/dist/core/context.js.map +0 -1
  215. package/dist/core/dependencies.js +0 -146
  216. package/dist/core/dependencies.js.map +0 -1
  217. package/dist/core/detect.js +0 -64
  218. package/dist/core/detect.js.map +0 -1
  219. package/dist/core/i18n.js +0 -105
  220. package/dist/core/i18n.js.map +0 -1
  221. package/dist/core/integrity.js +0 -46
  222. package/dist/core/integrity.js.map +0 -1
  223. package/dist/core/manifest.js +0 -21
  224. package/dist/core/manifest.js.map +0 -1
  225. package/dist/core/prompts.js +0 -20
  226. package/dist/core/prompts.js.map +0 -1
  227. package/dist/core/registry.js +0 -134
  228. package/dist/core/registry.js.map +0 -1
  229. package/dist/core/rules.js +0 -17
  230. package/dist/core/rules.js.map +0 -1
  231. package/dist/core/scripts.js +0 -48
  232. package/dist/core/scripts.js.map +0 -1
  233. package/dist/core/skill-check.js +0 -32
  234. package/dist/core/skill-check.js.map +0 -1
  235. package/dist/core/skills.js +0 -62
  236. package/dist/core/skills.js.map +0 -1
  237. package/dist/core/state.js +0 -54
  238. package/dist/core/state.js.map +0 -1
  239. package/dist/utils/path.js +0 -11
  240. package/dist/utils/path.js.map +0 -1
  241. package/dist/utils/shell.js +0 -29
  242. package/dist/utils/shell.js.map +0 -1
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import net from "node:net";
5
+ import path from "node:path";
6
+
7
+ const args = process.argv.slice(2);
8
+ const changeArg = args.find((arg) => !arg.startsWith("--"));
9
+ const jsonOutput = args.includes("--json");
10
+
11
+ if (!changeArg) {
12
+ fail(["用法: superflow-environment-preflight.mjs <change-dir> [--json]"]);
13
+ }
14
+
15
+ const changeDir = fs.realpathSync(path.resolve(changeArg));
16
+ const reportPath = path.join(
17
+ changeDir,
18
+ ".sdd",
19
+ "readiness",
20
+ "environment.json",
21
+ );
22
+ const issues = [];
23
+ const report = readReport(reportPath, issues);
24
+ const handoffHash = readHandoffHash(changeDir, issues);
25
+
26
+ if (report) {
27
+ if (report.schemaVersion !== "superflow.environment-readiness.v2") {
28
+ issues.push(
29
+ "环境报告 schemaVersion 必须为 superflow.environment-readiness.v2",
30
+ );
31
+ }
32
+ if (report.handoffHash !== handoffHash) {
33
+ issues.push("环境报告未绑定当前 handoff hash");
34
+ }
35
+ if (!report.scope) issues.push("环境报告必须声明 local/dev/test 等实际范围");
36
+ if (!['READY', 'LOCAL_FIXTURE_READY'].includes(report.overall)) {
37
+ issues.push(`环境整体状态不是可执行状态: ${report.overall ?? "未填写"}`);
38
+ }
39
+ if ((report.ownerHelpRequired?.length ?? 0) > 0) {
40
+ issues.push("仍有需要用户协助的环境问题,必须集中澄清后再交付开发");
41
+ }
42
+ const contractTargets = validateExecutionContract(
43
+ report.executionContract,
44
+ issues,
45
+ );
46
+ const referencedTargets = new Set();
47
+ if (!Array.isArray(report.checks) || report.checks.length === 0) {
48
+ issues.push("环境报告至少需要一项可执行检查");
49
+ } else {
50
+ for (const check of report.checks) {
51
+ validateContractRef(check, contractTargets, referencedTargets, issues);
52
+ await probe(check, issues);
53
+ }
54
+ for (const target of contractTargets) {
55
+ if (!referencedTargets.has(target)) {
56
+ issues.push(`${target}: 缺少对应的失败安全检查`);
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ const result = {
63
+ schemaVersion: "superflow.environment-preflight.v1",
64
+ ready: issues.length === 0,
65
+ scope: report?.scope ?? null,
66
+ handoffHash,
67
+ checks: (report?.checks ?? []).map((check) => ({
68
+ id: check.id,
69
+ type: check.type,
70
+ status: check.status,
71
+ })),
72
+ issues,
73
+ };
74
+
75
+ if (issues.length > 0) fail(issues, result);
76
+ if (jsonOutput) {
77
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
78
+ } else {
79
+ process.stdout.write(`环境预检通过:${result.checks.length} 项检查\n`);
80
+ }
81
+
82
+ async function probe(check, targetIssues) {
83
+ if (!check?.id || !check?.type) {
84
+ targetIssues.push("环境检查缺少 id 或 type");
85
+ return;
86
+ }
87
+ if (!['READY', 'LOCAL_FIXTURE_READY'].includes(check.status)) {
88
+ targetIssues.push(`${check.id}: 状态不是 READY 或 LOCAL_FIXTURE_READY`);
89
+ return;
90
+ }
91
+ try {
92
+ switch (check.type) {
93
+ case "file": {
94
+ const target = resolveSafeTarget(check.target);
95
+ if (!fs.existsSync(target) || !fs.statSync(target).isFile()) {
96
+ targetIssues.push(`${check.id}: 文件不存在或已失效`);
97
+ }
98
+ break;
99
+ }
100
+ case "directory": {
101
+ const target = resolveSafeTarget(check.target);
102
+ if (!fs.existsSync(target) || !fs.statSync(target).isDirectory()) {
103
+ targetIssues.push(`${check.id}: 目录不存在或已失效`);
104
+ }
105
+ break;
106
+ }
107
+ case "executable":
108
+ if (!findExecutable(check.target)) {
109
+ targetIssues.push(`${check.id}: 可执行程序不可用`);
110
+ }
111
+ break;
112
+ case "tcp":
113
+ await probeTcp(check.host, check.port, check.timeoutMs ?? 1500);
114
+ break;
115
+ case "http":
116
+ await probeHttp(check);
117
+ break;
118
+ default:
119
+ targetIssues.push(`${check.id}: 不支持的环境检查类型 ${check.type}`);
120
+ }
121
+ } catch (error) {
122
+ targetIssues.push(`${check.id}: ${safeError(error)}`);
123
+ }
124
+ }
125
+
126
+ function validateExecutionContract(contract, targetIssues) {
127
+ const targets = new Set();
128
+ if (!contract || typeof contract !== "object") {
129
+ targetIssues.push("缺少结构化环境执行合同 executionContract");
130
+ return targets;
131
+ }
132
+
133
+ const applicationLocations = new Set(["local", "dev", "test"]);
134
+ if (!applicationLocations.has(contract.applicationLocation)) {
135
+ targetIssues.push("executionContract.applicationLocation 必须为 local/dev/test");
136
+ }
137
+
138
+ const dependencyPolicies = new Set([
139
+ "local-isolated",
140
+ "shared-dev",
141
+ "shared-test",
142
+ "mixed",
143
+ ]);
144
+ if (!dependencyPolicies.has(contract.dependencyPolicy)) {
145
+ targetIssues.push(
146
+ "executionContract.dependencyPolicy 必须为 local-isolated/shared-dev/shared-test/mixed",
147
+ );
148
+ }
149
+ if (typeof contract.allowLocalProvisioning !== "boolean") {
150
+ targetIssues.push("executionContract.allowLocalProvisioning 必须为布尔值");
151
+ }
152
+ if (typeof contract.allowRemoteDevDependencies !== "boolean") {
153
+ targetIssues.push("executionContract.allowRemoteDevDependencies 必须为布尔值");
154
+ }
155
+ if (
156
+ contract.dependencyPolicy === "shared-dev" &&
157
+ contract.allowLocalProvisioning === true
158
+ ) {
159
+ targetIssues.push("dependencyPolicy=shared-dev 禁止自建本地依赖");
160
+ }
161
+ if (
162
+ contract.dependencyPolicy === "local-isolated" &&
163
+ contract.allowRemoteDevDependencies === true
164
+ ) {
165
+ targetIssues.push("dependencyPolicy=local-isolated 禁止使用远程开发依赖");
166
+ }
167
+
168
+ validateOverrides(contract, targetIssues);
169
+ validateContractItems(
170
+ contract.services,
171
+ "service",
172
+ contract.dependencyPolicy,
173
+ targets,
174
+ targetIssues,
175
+ );
176
+ validateContractItems(
177
+ contract.dependencies,
178
+ "dependency",
179
+ contract.dependencyPolicy,
180
+ targets,
181
+ targetIssues,
182
+ );
183
+ return targets;
184
+ }
185
+
186
+ function validateOverrides(contract, targetIssues) {
187
+ const allowed = contract.allowedOverrides;
188
+ const forbidden = contract.forbiddenOverrides;
189
+ if (!isStringArray(allowed)) {
190
+ targetIssues.push("executionContract.allowedOverrides 必须为字符串数组");
191
+ }
192
+ if (!isStringArray(forbidden)) {
193
+ targetIssues.push("executionContract.forbiddenOverrides 必须为字符串数组");
194
+ }
195
+ if (!isStringArray(allowed) || !isStringArray(forbidden)) return;
196
+ const forbiddenSet = new Set(forbidden);
197
+ const overlap = [...new Set(allowed)].filter((item) => forbiddenSet.has(item));
198
+ if (overlap.length > 0) {
199
+ targetIssues.push(`允许覆盖项与禁止覆盖项重复: ${overlap.join(", ")}`);
200
+ }
201
+ }
202
+
203
+ function validateContractItems(
204
+ items,
205
+ type,
206
+ dependencyPolicy,
207
+ targets,
208
+ targetIssues,
209
+ ) {
210
+ const label = type === "service" ? "services" : "dependencies";
211
+ if (!Array.isArray(items) || items.length === 0) {
212
+ targetIssues.push(`executionContract.${label} 至少需要一项`);
213
+ return;
214
+ }
215
+ for (const item of items) {
216
+ if (!item?.id || typeof item.id !== "string") {
217
+ targetIssues.push(`${label}: 缺少 id`);
218
+ continue;
219
+ }
220
+ const reference = `${type}:${item.id}`;
221
+ if (targets.has(reference)) {
222
+ targetIssues.push(`${reference}: id 重复`);
223
+ }
224
+ targets.add(reference);
225
+ if (!isConfigSource(item.configSource)) {
226
+ targetIssues.push(`${item.id}: 缺少 configSource`);
227
+ }
228
+ if (
229
+ type === "service" &&
230
+ (!item.startupCommandSource ||
231
+ typeof item.startupCommandSource !== "string")
232
+ ) {
233
+ targetIssues.push(`${item.id}: 缺少 startupCommandSource`);
234
+ }
235
+ if (type === "dependency") {
236
+ validateDependency(item, dependencyPolicy, targetIssues);
237
+ }
238
+ }
239
+ }
240
+
241
+ function validateDependency(dependency, dependencyPolicy, targetIssues) {
242
+ const provisioningValues = new Set([
243
+ "local-isolated",
244
+ "shared-dev",
245
+ "shared-test",
246
+ ]);
247
+ if (!dependency.kind || typeof dependency.kind !== "string") {
248
+ targetIssues.push(`${dependency.id}: 缺少 kind`);
249
+ }
250
+ if (!provisioningValues.has(dependency.provisioning)) {
251
+ targetIssues.push(`${dependency.id}: provisioning 不合法`);
252
+ return;
253
+ }
254
+ if (
255
+ dependencyPolicy !== "mixed" &&
256
+ dependencyPolicy !== dependency.provisioning
257
+ ) {
258
+ targetIssues.push(
259
+ `${dependency.id}: provisioning 与 dependencyPolicy 不一致`,
260
+ );
261
+ }
262
+ }
263
+
264
+ function validateContractRef(
265
+ check,
266
+ contractTargets,
267
+ referencedTargets,
268
+ targetIssues,
269
+ ) {
270
+ if (!check?.id) return;
271
+ if (!check.contractRef || typeof check.contractRef !== "string") {
272
+ targetIssues.push(`${check.id}: 缺少 contractRef`);
273
+ return;
274
+ }
275
+ if (!contractTargets.has(check.contractRef)) {
276
+ targetIssues.push(`${check.id}: contractRef 未指向已声明的服务或依赖`);
277
+ return;
278
+ }
279
+ referencedTargets.add(check.contractRef);
280
+ }
281
+
282
+ function isStringArray(value) {
283
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
284
+ }
285
+
286
+ function isConfigSource(value) {
287
+ const types = new Set([
288
+ "bundled-profile",
289
+ "external-file",
290
+ "environment",
291
+ "cli-override",
292
+ "generated-fixture",
293
+ ]);
294
+ return Boolean(
295
+ value &&
296
+ typeof value === "object" &&
297
+ types.has(value.type) &&
298
+ typeof value.ref === "string" &&
299
+ value.ref.length > 0,
300
+ );
301
+ }
302
+
303
+ function resolveSafeTarget(target) {
304
+ if (typeof target !== "string" || target.length === 0) {
305
+ throw new Error("缺少检查目标");
306
+ }
307
+ return path.isAbsolute(target) ? target : path.resolve(changeDir, target);
308
+ }
309
+
310
+ function findExecutable(name) {
311
+ if (typeof name !== "string" || !/^[A-Za-z0-9._-]+$/.test(name)) return false;
312
+ return (process.env.PATH ?? "")
313
+ .split(path.delimiter)
314
+ .some((directory) => {
315
+ try {
316
+ fs.accessSync(path.join(directory, name), fs.constants.X_OK);
317
+ return true;
318
+ } catch {
319
+ return false;
320
+ }
321
+ });
322
+ }
323
+
324
+ function probeTcp(host, port, timeoutMs) {
325
+ if (typeof host !== "string" || !Number.isInteger(port)) {
326
+ return Promise.reject(new Error("TCP 检查缺少 host/port"));
327
+ }
328
+ return new Promise((resolve, reject) => {
329
+ const socket = net.createConnection({ host, port });
330
+ const timer = setTimeout(() => socket.destroy(new Error("连接超时")), timeoutMs);
331
+ socket.once("connect", () => {
332
+ clearTimeout(timer);
333
+ socket.end();
334
+ resolve();
335
+ });
336
+ socket.once("error", (error) => {
337
+ clearTimeout(timer);
338
+ reject(error);
339
+ });
340
+ });
341
+ }
342
+
343
+ async function probeHttp(check) {
344
+ const url = new URL(check.url);
345
+ if (url.username || url.password) throw new Error("HTTP URL 禁止内嵌凭据");
346
+ const response = await fetch(url, {
347
+ method: "GET",
348
+ signal: AbortSignal.timeout(check.timeoutMs ?? 2000),
349
+ });
350
+ const configured = check.expectedStatus ?? [200, 204];
351
+ const expected = Array.isArray(configured) ? configured : [configured];
352
+ if (!expected.includes(response.status)) {
353
+ throw new Error(`HTTP 状态不符合预期: ${response.status}`);
354
+ }
355
+ }
356
+
357
+ function readReport(file, targetIssues) {
358
+ if (!fs.existsSync(file)) {
359
+ targetIssues.push("缺少环境预检合同 .sdd/readiness/environment.json");
360
+ return null;
361
+ }
362
+ try {
363
+ return JSON.parse(fs.readFileSync(file, "utf-8"));
364
+ } catch {
365
+ targetIssues.push("环境预检合同不是合法 JSON");
366
+ return null;
367
+ }
368
+ }
369
+
370
+ function readHandoffHash(root, targetIssues) {
371
+ const file = path.join(root, ".sdd", "handoff", "sdd-context.sha256");
372
+ const content = fs.existsSync(file) ? fs.readFileSync(file, "utf-8") : "";
373
+ const match = content.match(/[a-f0-9]{64}/i);
374
+ if (!match) {
375
+ targetIssues.push("缺少合法的当前 handoff hash");
376
+ return "";
377
+ }
378
+ return match[0].toLowerCase();
379
+ }
380
+
381
+ function safeError(error) {
382
+ const message = error instanceof Error ? error.message : "检查失败";
383
+ return message.replace(
384
+ /\b(password|secret|token|authorization)=[^\s]+/gi,
385
+ (_match, key) => `${key}=[REDACTED]`,
386
+ );
387
+ }
388
+
389
+ function fail(targetIssues, result = { ready: false, issues: targetIssues }) {
390
+ if (jsonOutput) {
391
+ process.stderr.write(`${JSON.stringify(result, null, 2)}\n`);
392
+ } else {
393
+ process.stderr.write(
394
+ `环境预检失败:\n${targetIssues.map((item) => `- ${item}`).join("\n")}\n`,
395
+ );
396
+ }
397
+ process.exit(1);
398
+ }
@@ -86,7 +86,7 @@ is_root_markdown_or_config() {
86
86
  }
87
87
 
88
88
  case "$REL" in
89
- *.java|*.xml|*.sql|*.yml|*.yaml|*.properties)
89
+ *.java|*.kt|*.kts|*.xml|*.sql|*.yml|*.yaml|*.properties|*.ts|*.tsx|*.js|*.jsx|*.mjs|*.cjs|*.py|*.go|*.rs|*.vue)
90
90
  RUNTIME=1
91
91
  ;;
92
92
  *)
@@ -94,6 +94,42 @@ case "$REL" in
94
94
  ;;
95
95
  esac
96
96
 
97
+ require_coding_ready() {
98
+ local change_dir receipt expected_hash
99
+ change_dir=$(dirname "$(dirname "$STATE_FILE")")
100
+ receipt="$change_dir/.sdd/readiness/coding-ready.json"
101
+ expected_hash=$(awk -F':' '$1=="handoff_hash"{gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2}' "$STATE_FILE" 2>/dev/null)
102
+ if [ ! -f "$receipt" ] || [ -z "$expected_hash" ]; then
103
+ cat <<'BLOCK_MSG' >&2
104
+ [SDD Coding Ready] 当前任务没有可用的 Coding Ready 凭证,禁止修改运行时代码。
105
+ 请先完成多轮文档评审、环境预检和 docs/design/implement 门禁,再执行:
106
+ superflow check <change> --level coding-ready
107
+ BLOCK_MSG
108
+ return 2
109
+ fi
110
+ python3 - "$receipt" "$expected_hash" <<'PY'
111
+ import json, sys
112
+ try:
113
+ with open(sys.argv[1], encoding="utf-8") as handle:
114
+ receipt = json.load(handle)
115
+ except Exception:
116
+ raise SystemExit(2)
117
+ if receipt.get("codingReady") is not True:
118
+ raise SystemExit(2)
119
+ if receipt.get("schemaVersion") != "superflow.coding-ready.v1":
120
+ raise SystemExit(2)
121
+ if receipt.get("handoffHash") != sys.argv[2]:
122
+ raise SystemExit(2)
123
+ PY
124
+ if [ $? -ne 0 ]; then
125
+ cat <<'BLOCK_MSG' >&2
126
+ [SDD Coding Ready] 凭证已过期或与当前 handoff hash 不一致。
127
+ 请重新执行 superflow check <change> --level coding-ready。
128
+ BLOCK_MSG
129
+ return 2
130
+ fi
131
+ }
132
+
97
133
  case "$PHASE" in
98
134
  docs)
99
135
  if is_sdd_doc_path || is_root_markdown_or_config; then
@@ -108,6 +144,9 @@ BLOCK_MSG
108
144
  fi
109
145
  ;;
110
146
  implement|verify)
147
+ if [ "$RUNTIME" -eq 1 ]; then
148
+ require_coding_ready || exit 2
149
+ fi
111
150
  exit 0
112
151
  ;;
113
152
  archive)