speccore 8.3.333 → 8.3.341

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 (309) hide show
  1. package/.agents/skills/spec-ask/SKILL.md +4 -0
  2. package/.agents/skills/spec-unitize/SKILL.md +9 -1
  3. package/.agents/skills/speccore-router/SKILL.md +12 -11
  4. package/README.en.md +3 -11
  5. package/README.md +3 -13
  6. package/dist/cli.js +64 -206
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/analyze.d.ts.map +1 -1
  9. package/dist/commands/analyze.js +530 -481
  10. package/dist/commands/analyze.js.map +1 -1
  11. package/dist/commands/ask.d.ts.map +1 -1
  12. package/dist/commands/ask.js +6 -0
  13. package/dist/commands/ask.js.map +1 -1
  14. package/dist/commands/completion.d.ts.map +1 -1
  15. package/dist/commands/completion.js +9 -7
  16. package/dist/commands/completion.js.map +1 -1
  17. package/dist/commands/doc2spec.d.ts +44 -1
  18. package/dist/commands/doc2spec.d.ts.map +1 -1
  19. package/dist/commands/doc2spec.js +257 -5
  20. package/dist/commands/doc2spec.js.map +1 -1
  21. package/dist/commands/doctor.js +3 -3
  22. package/dist/commands/doctor.js.map +1 -1
  23. package/dist/commands/done.js +1 -5
  24. package/dist/commands/done.js.map +1 -1
  25. package/dist/commands/execute.js +118 -66
  26. package/dist/commands/execute.js.map +1 -1
  27. package/dist/commands/help.js +6 -54
  28. package/dist/commands/help.js.map +1 -1
  29. package/dist/commands/init.d.ts.map +1 -1
  30. package/dist/commands/init.js +71 -47
  31. package/dist/commands/init.js.map +1 -1
  32. package/dist/commands/iteration/create.d.ts.map +1 -1
  33. package/dist/commands/iteration/create.js +72 -10
  34. package/dist/commands/iteration/create.js.map +1 -1
  35. package/dist/commands/iteration/taskify.d.ts.map +1 -1
  36. package/dist/commands/iteration/taskify.js +133 -35
  37. package/dist/commands/iteration/taskify.js.map +1 -1
  38. package/dist/commands/pr.d.ts.map +1 -1
  39. package/dist/commands/pr.js +24 -2
  40. package/dist/commands/pr.js.map +1 -1
  41. package/dist/commands/prompts.d.ts +6 -0
  42. package/dist/commands/prompts.d.ts.map +1 -1
  43. package/dist/commands/prompts.js +19 -10
  44. package/dist/commands/prompts.js.map +1 -1
  45. package/dist/commands/search.js +1 -1
  46. package/dist/commands/search.js.map +1 -1
  47. package/dist/commands/status-panel.d.ts.map +1 -1
  48. package/dist/commands/status-panel.js +23 -34
  49. package/dist/commands/status-panel.js.map +1 -1
  50. package/dist/commands/unitize.d.ts +3 -0
  51. package/dist/commands/unitize.d.ts.map +1 -1
  52. package/dist/commands/unitize.js +32 -9
  53. package/dist/commands/unitize.js.map +1 -1
  54. package/dist/commands/update-env-configs.d.ts.map +1 -1
  55. package/dist/commands/update-env-configs.js +2 -1
  56. package/dist/commands/update-env-configs.js.map +1 -1
  57. package/dist/commands/update.d.ts +4 -0
  58. package/dist/commands/update.d.ts.map +1 -1
  59. package/dist/commands/update.js +41 -1
  60. package/dist/commands/update.js.map +1 -1
  61. package/dist/commands/welcome.d.ts.map +1 -1
  62. package/dist/commands/welcome.js +41 -0
  63. package/dist/commands/welcome.js.map +1 -1
  64. package/dist/core/analyze-engine.js +3 -3
  65. package/dist/core/analyze-engine.js.map +1 -1
  66. package/dist/core/arbitration/l3-human-arbiter.js +1 -1
  67. package/dist/core/arbitration/l3-human-arbiter.js.map +1 -1
  68. package/dist/core/arbitration/verdict-generator.js +1 -1
  69. package/dist/core/arbitration/verdict-generator.js.map +1 -1
  70. package/dist/core/ask-context.d.ts.map +1 -1
  71. package/dist/core/ask-context.js +14 -3
  72. package/dist/core/ask-context.js.map +1 -1
  73. package/dist/core/ask-engine.d.ts.map +1 -1
  74. package/dist/core/ask-engine.js +55 -49
  75. package/dist/core/ask-engine.js.map +1 -1
  76. package/dist/core/code-graph/reporter.d.ts.map +1 -1
  77. package/dist/core/code-graph/reporter.js +3 -12
  78. package/dist/core/code-graph/reporter.js.map +1 -1
  79. package/dist/core/code-graph/requirement-linker.js +1 -1
  80. package/dist/core/code-graph/requirement-linker.js.map +1 -1
  81. package/dist/core/code-scanner.js +1 -1
  82. package/dist/core/code-scanner.js.map +1 -1
  83. package/dist/core/deploy/engine.d.ts.map +1 -1
  84. package/dist/core/deploy/engine.js +58 -44
  85. package/dist/core/deploy/engine.js.map +1 -1
  86. package/dist/core/environment-config.d.ts +13 -0
  87. package/dist/core/environment-config.d.ts.map +1 -1
  88. package/dist/core/environment-config.js +28 -1
  89. package/dist/core/environment-config.js.map +1 -1
  90. package/dist/core/error-diagnosis.js +2 -2
  91. package/dist/core/error-diagnosis.js.map +1 -1
  92. package/dist/core/git-integration.d.ts +5 -7
  93. package/dist/core/git-integration.d.ts.map +1 -1
  94. package/dist/core/git-integration.js +137 -12
  95. package/dist/core/git-integration.js.map +1 -1
  96. package/dist/core/global-knowledge.d.ts.map +1 -1
  97. package/dist/core/global-knowledge.js +25 -11
  98. package/dist/core/global-knowledge.js.map +1 -1
  99. package/dist/core/handoff-generator.d.ts.map +1 -1
  100. package/dist/core/handoff-generator.js +3 -1
  101. package/dist/core/handoff-generator.js.map +1 -1
  102. package/dist/core/index-guard.d.ts.map +1 -1
  103. package/dist/core/index-guard.js +4 -26
  104. package/dist/core/index-guard.js.map +1 -1
  105. package/dist/core/intent-recognition.d.ts +1 -1
  106. package/dist/core/intent-recognition.d.ts.map +1 -1
  107. package/dist/core/intent-recognition.js +68 -68
  108. package/dist/core/intent-recognition.js.map +1 -1
  109. package/dist/core/knowledge-graph.d.ts.map +1 -1
  110. package/dist/core/knowledge-graph.js +6 -4
  111. package/dist/core/knowledge-graph.js.map +1 -1
  112. package/dist/core/name-validator.d.ts.map +1 -1
  113. package/dist/core/name-validator.js +41 -20
  114. package/dist/core/name-validator.js.map +1 -1
  115. package/dist/core/pinyin-initials.d.ts +10 -0
  116. package/dist/core/pinyin-initials.d.ts.map +1 -0
  117. package/dist/core/pinyin-initials.js +44 -0
  118. package/dist/core/pinyin-initials.js.map +1 -0
  119. package/dist/core/prompt-builder.js +1 -1
  120. package/dist/core/prompt-builder.js.map +1 -1
  121. package/dist/core/prompt-plugins/analyze-graph-plugin.js +1 -1
  122. package/dist/core/prompt-plugins/analyze-graph-plugin.js.map +1 -1
  123. package/dist/core/reindex-engine.js +4 -4
  124. package/dist/core/reindex-engine.js.map +1 -1
  125. package/dist/core/skeleton-residue.d.ts +13 -0
  126. package/dist/core/skeleton-residue.d.ts.map +1 -1
  127. package/dist/core/skeleton-residue.js +83 -14
  128. package/dist/core/skeleton-residue.js.map +1 -1
  129. package/dist/core/spec-merger.d.ts.map +1 -1
  130. package/dist/core/spec-merger.js +3 -2
  131. package/dist/core/spec-merger.js.map +1 -1
  132. package/dist/core/state.d.ts +10 -0
  133. package/dist/core/state.d.ts.map +1 -1
  134. package/dist/core/state.js +20 -1
  135. package/dist/core/state.js.map +1 -1
  136. package/dist/core/structured-extractor.d.ts +6 -0
  137. package/dist/core/structured-extractor.d.ts.map +1 -1
  138. package/dist/core/structured-extractor.js +6 -0
  139. package/dist/core/structured-extractor.js.map +1 -1
  140. package/dist/core/task-paths.d.ts +12 -0
  141. package/dist/core/task-paths.d.ts.map +1 -1
  142. package/dist/core/task-paths.js +33 -1
  143. package/dist/core/task-paths.js.map +1 -1
  144. package/dist/core/unified-config.d.ts +7 -1
  145. package/dist/core/unified-config.d.ts.map +1 -1
  146. package/dist/core/unified-config.js +70 -21
  147. package/dist/core/unified-config.js.map +1 -1
  148. package/dist/core/unit-staffing.d.ts +21 -4
  149. package/dist/core/unit-staffing.d.ts.map +1 -1
  150. package/dist/core/unit-staffing.js +59 -12
  151. package/dist/core/unit-staffing.js.map +1 -1
  152. package/dist/core/usage-analytics.js +1 -1
  153. package/dist/core/usage-analytics.js.map +1 -1
  154. package/dist/core/validator.d.ts.map +1 -1
  155. package/dist/core/validator.js +45 -5
  156. package/dist/core/validator.js.map +1 -1
  157. package/dist/core/verify-report-aggregator.d.ts.map +1 -1
  158. package/dist/core/verify-report-aggregator.js +19 -2
  159. package/dist/core/verify-report-aggregator.js.map +1 -1
  160. package/dist/utils/task-utils.d.ts +4 -1
  161. package/dist/utils/task-utils.d.ts.map +1 -1
  162. package/dist/utils/task-utils.js +30 -4
  163. package/dist/utils/task-utils.js.map +1 -1
  164. package/package.json +4 -3
  165. package/templates/deploy-examples.yaml +33 -5
  166. package/templates/html/speccore-ask-explain.html +1 -1
  167. package/templates/html/speccore-ask-guide.html +1 -1
  168. package/templates/html/speccore-ask-match.html +1 -1
  169. package/templates/html/speccore-ask-pipeline.html +1 -1
  170. package/templates/html/speccore-ask-result.html +1 -1
  171. package/templates/html/speccore-help.html +2 -2
  172. package/templates/speccore-yml-example.yml +174 -54
  173. package/templates/verify-spec-example.yaml +66 -1
  174. package/dist/commands/audit.d.ts +0 -12
  175. package/dist/commands/audit.d.ts.map +0 -1
  176. package/dist/commands/audit.js +0 -540
  177. package/dist/commands/audit.js.map +0 -1
  178. package/dist/commands/backup.d.ts +0 -18
  179. package/dist/commands/backup.d.ts.map +0 -1
  180. package/dist/commands/backup.js +0 -102
  181. package/dist/commands/backup.js.map +0 -1
  182. package/dist/commands/baseline.d.ts +0 -13
  183. package/dist/commands/baseline.d.ts.map +0 -1
  184. package/dist/commands/baseline.js +0 -227
  185. package/dist/commands/baseline.js.map +0 -1
  186. package/dist/commands/chain.d.ts +0 -55
  187. package/dist/commands/chain.d.ts.map +0 -1
  188. package/dist/commands/chain.js +0 -501
  189. package/dist/commands/chain.js.map +0 -1
  190. package/dist/commands/clarify.d.ts +0 -17
  191. package/dist/commands/clarify.d.ts.map +0 -1
  192. package/dist/commands/clarify.js +0 -351
  193. package/dist/commands/clarify.js.map +0 -1
  194. package/dist/commands/code-index.d.ts +0 -21
  195. package/dist/commands/code-index.d.ts.map +0 -1
  196. package/dist/commands/code-index.js +0 -193
  197. package/dist/commands/code-index.js.map +0 -1
  198. package/dist/commands/context.d.ts +0 -10
  199. package/dist/commands/context.d.ts.map +0 -1
  200. package/dist/commands/context.js +0 -213
  201. package/dist/commands/context.js.map +0 -1
  202. package/dist/commands/current.d.ts +0 -10
  203. package/dist/commands/current.d.ts.map +0 -1
  204. package/dist/commands/current.js +0 -53
  205. package/dist/commands/current.js.map +0 -1
  206. package/dist/commands/dev.d.ts +0 -18
  207. package/dist/commands/dev.d.ts.map +0 -1
  208. package/dist/commands/dev.js +0 -854
  209. package/dist/commands/dev.js.map +0 -1
  210. package/dist/commands/diff.d.ts +0 -10
  211. package/dist/commands/diff.d.ts.map +0 -1
  212. package/dist/commands/diff.js +0 -160
  213. package/dist/commands/diff.js.map +0 -1
  214. package/dist/commands/global-status.d.ts +0 -9
  215. package/dist/commands/global-status.d.ts.map +0 -1
  216. package/dist/commands/global-status.js +0 -135
  217. package/dist/commands/global-status.js.map +0 -1
  218. package/dist/commands/graph.d.ts +0 -25
  219. package/dist/commands/graph.d.ts.map +0 -1
  220. package/dist/commands/graph.js +0 -523
  221. package/dist/commands/graph.js.map +0 -1
  222. package/dist/commands/handover.d.ts +0 -11
  223. package/dist/commands/handover.d.ts.map +0 -1
  224. package/dist/commands/handover.js +0 -170
  225. package/dist/commands/handover.js.map +0 -1
  226. package/dist/commands/health.d.ts +0 -7
  227. package/dist/commands/health.d.ts.map +0 -1
  228. package/dist/commands/health.js +0 -80
  229. package/dist/commands/health.js.map +0 -1
  230. package/dist/commands/history.d.ts +0 -9
  231. package/dist/commands/history.d.ts.map +0 -1
  232. package/dist/commands/history.js +0 -117
  233. package/dist/commands/history.js.map +0 -1
  234. package/dist/commands/impact.d.ts +0 -12
  235. package/dist/commands/impact.d.ts.map +0 -1
  236. package/dist/commands/impact.js +0 -238
  237. package/dist/commands/impact.js.map +0 -1
  238. package/dist/commands/import.d.ts +0 -19
  239. package/dist/commands/import.d.ts.map +0 -1
  240. package/dist/commands/import.js +0 -673
  241. package/dist/commands/import.js.map +0 -1
  242. package/dist/commands/knowledge.d.ts +0 -6
  243. package/dist/commands/knowledge.d.ts.map +0 -1
  244. package/dist/commands/knowledge.js +0 -94
  245. package/dist/commands/knowledge.js.map +0 -1
  246. package/dist/commands/lifecycle.d.ts +0 -9
  247. package/dist/commands/lifecycle.d.ts.map +0 -1
  248. package/dist/commands/lifecycle.js +0 -288
  249. package/dist/commands/lifecycle.js.map +0 -1
  250. package/dist/commands/merge-check.d.ts +0 -10
  251. package/dist/commands/merge-check.d.ts.map +0 -1
  252. package/dist/commands/merge-check.js +0 -183
  253. package/dist/commands/merge-check.js.map +0 -1
  254. package/dist/commands/notify.d.ts +0 -13
  255. package/dist/commands/notify.d.ts.map +0 -1
  256. package/dist/commands/notify.js +0 -63
  257. package/dist/commands/notify.js.map +0 -1
  258. package/dist/commands/rag-index.d.ts +0 -19
  259. package/dist/commands/rag-index.d.ts.map +0 -1
  260. package/dist/commands/rag-index.js +0 -286
  261. package/dist/commands/rag-index.js.map +0 -1
  262. package/dist/commands/recommend.d.ts +0 -13
  263. package/dist/commands/recommend.d.ts.map +0 -1
  264. package/dist/commands/recommend.js +0 -37
  265. package/dist/commands/recommend.js.map +0 -1
  266. package/dist/commands/refresh.d.ts +0 -21
  267. package/dist/commands/refresh.d.ts.map +0 -1
  268. package/dist/commands/refresh.js +0 -211
  269. package/dist/commands/refresh.js.map +0 -1
  270. package/dist/commands/rollback.d.ts +0 -8
  271. package/dist/commands/rollback.d.ts.map +0 -1
  272. package/dist/commands/rollback.js +0 -113
  273. package/dist/commands/rollback.js.map +0 -1
  274. package/dist/commands/status.d.ts +0 -7
  275. package/dist/commands/status.d.ts.map +0 -1
  276. package/dist/commands/status.js +0 -262
  277. package/dist/commands/status.js.map +0 -1
  278. package/dist/commands/trace.d.ts +0 -12
  279. package/dist/commands/trace.d.ts.map +0 -1
  280. package/dist/commands/trace.js +0 -163
  281. package/dist/commands/trace.js.map +0 -1
  282. package/dist/commands/tracker.d.ts +0 -2
  283. package/dist/commands/tracker.d.ts.map +0 -1
  284. package/dist/commands/tracker.js +0 -28
  285. package/dist/commands/tracker.js.map +0 -1
  286. package/dist/commands/verdict.d.ts +0 -16
  287. package/dist/commands/verdict.d.ts.map +0 -1
  288. package/dist/commands/verdict.js +0 -189
  289. package/dist/commands/verdict.js.map +0 -1
  290. package/dist/commands/workspace.d.ts +0 -20
  291. package/dist/commands/workspace.d.ts.map +0 -1
  292. package/dist/commands/workspace.js +0 -142
  293. package/dist/commands/workspace.js.map +0 -1
  294. package/dist/core/constraint-set.d.ts +0 -67
  295. package/dist/core/constraint-set.d.ts.map +0 -1
  296. package/dist/core/constraint-set.js +0 -151
  297. package/dist/core/constraint-set.js.map +0 -1
  298. package/dist/core/smart-recommend.d.ts +0 -16
  299. package/dist/core/smart-recommend.d.ts.map +0 -1
  300. package/dist/core/smart-recommend.js +0 -149
  301. package/dist/core/smart-recommend.js.map +0 -1
  302. package/dist/core/task-type-registry.d.ts +0 -110
  303. package/dist/core/task-type-registry.d.ts.map +0 -1
  304. package/dist/core/task-type-registry.js +0 -334
  305. package/dist/core/task-type-registry.js.map +0 -1
  306. package/dist/utils/mermaid-render.d.ts +0 -39
  307. package/dist/utils/mermaid-render.d.ts.map +0 -1
  308. package/dist/utils/mermaid-render.js +0 -284
  309. package/dist/utils/mermaid-render.js.map +0 -1
@@ -272,10 +272,10 @@ async function executeCommand(options) {
272
272
  tasks = tasks.filter(t => t.status === options.status);
273
273
  if (options.assignee)
274
274
  tasks = tasks.filter(t => t.assignee === options.assignee);
275
- // v8.3.288+: 按所属功能模块筛选(模糊包含,如「支付」命中「支付模块」)
275
+ // v8.3.288+ / v8.3.339+: 按所属功能模块筛选(逗号分隔多值,模糊匹配单元名/模块目录名)
276
276
  if (options.module) {
277
277
  const before = tasks.length;
278
- tasks = tasks.filter(t => (t.feature || '').includes(options.module));
278
+ tasks = (0, state_1.filterTasksByModule)(tasks, options.module);
279
279
  logger_1.logger.info(`🎯 模块筛选「${options.module}」: ${before} → ${tasks.length} 个任务`);
280
280
  }
281
281
  // v8.3.145+: 使用项目实际端名过滤,不再硬编码 backend/frontend
@@ -593,6 +593,12 @@ async function executeWithProgress(tasks, iteration, base, skip, options) {
593
593
  logger_1.logger.info(` 🌿 ${task.id}: ${branchName}`);
594
594
  }
595
595
  await generateTaskSkeleton(task, iteration);
596
+ // v8.3.337+: 直接执行路径同样跑完成收尾(标记 completed + 骨架体检兑底)
597
+ try {
598
+ const tDir = await resolveTaskDir(await (0, context_1.getIterationDir)(iteration), task.id);
599
+ await finalizeTaskCompletion(iteration, task.id, tDir);
600
+ }
601
+ catch { /* 收尾失败不阻断 */ }
596
602
  completed.push(`${task.id} - ${task.name || ''}`);
597
603
  logger_1.logger.info(` ✅ ${task.id} completed`);
598
604
  logger_1.logger.info('');
@@ -832,6 +838,12 @@ async function processBatch(tasks, state, iteration) {
832
838
  }
833
839
  logger_1.logger.info(` 🔄 执行中...`);
834
840
  await generateTaskSkeleton(task, iteration);
841
+ // v8.3.337+: 批次路径同样跑完成收尾(标记 completed + 骨架体检兑底)
842
+ try {
843
+ const tDir = await resolveTaskDir(await (0, context_1.getIterationDir)(iteration), task.id);
844
+ await finalizeTaskCompletion(iteration, task.id, tDir);
845
+ }
846
+ catch { /* 收尾失败不阻断 */ }
835
847
  completed.push(task.id);
836
848
  // 写入任务摘要到 execution-state(文件即记忆)
837
849
  // v8.3.166+: 包含 agent 角色,供跨 Agent 状态共享
@@ -1945,7 +1957,7 @@ async function executionVerifyLoop(tasks, iteration, options) {
1945
1957
  logger_1.logger.info(` 👤 L3 架构契约: ${arbResult.report.L3.passed}/${arbResult.report.L3.total} 通过, ${arbResult.report.L3.pending} 待裁决`);
1946
1958
  if (arbResult.report.L3.pending > 0) {
1947
1959
  logger_1.logger.warn(` ⚠️ ${arbResult.report.L3.pending} 项需人工裁决`);
1948
- logger_1.logger.info(` 执行: speccore verdict --list --task ${task.id}`);
1960
+ logger_1.logger.info(` 裁决入口: 任务目录 ARBITRATION_REPORT.md`);
1949
1961
  allPassed = false;
1950
1962
  }
1951
1963
  }
@@ -2171,10 +2183,10 @@ async function listPendingTasks(iteration, options) {
2171
2183
  }
2172
2184
  // 过滤已完成的任务
2173
2185
  let pending = tasks.filter(t => t.status !== 'completed' && t.status !== 'archived');
2174
- // v8.3.288+: 按所属功能模块筛选(--module,匹配 .meta/feature)
2186
+ // v8.3.288+ / v8.3.339+: 按所属功能模块筛选(--module 逗号分隔多值,模糊匹配单元名/模块目录名)
2175
2187
  if (options.module) {
2176
2188
  const before = pending.length;
2177
- pending = pending.filter(t => (t.feature || '').includes(options.module));
2189
+ pending = (0, state_1.filterTasksByModule)(pending, options.module);
2178
2190
  logger_1.logger.info(`🎯 模块筛选「${options.module}」: ${before} → ${pending.length} 个任务`);
2179
2191
  }
2180
2192
  if (pending.length === 0) {
@@ -2215,8 +2227,22 @@ async function runPromptMode(iteration, options) {
2215
2227
  const taskDir = (await (0, fs_extra_1.pathExists)(tasksDir30)) ? tasksDir30 : iterDir;
2216
2228
  let availableTasks = [];
2217
2229
  try {
2218
- const entries = await (0, fs_extra_1.readdir)(taskDir, { withFileTypes: true });
2219
- availableTasks = entries.filter((e) => e.isDirectory() && e.name.startsWith('Task-')).map((e) => e.name);
2230
+ // v8.3.337+: 兼容 030-tasks 新旧布局(新布局模块目录=功能单元名,位于类型子目录层)
2231
+ const { isTaskModuleDirName } = await Promise.resolve().then(() => __importStar(require('../core/task-paths')));
2232
+ const collect = async (dir, depth) => {
2233
+ const entries = await (0, fs_extra_1.readdir)(dir, { withFileTypes: true });
2234
+ for (const e of entries) {
2235
+ if (!e.isDirectory())
2236
+ continue;
2237
+ if (e.name.startsWith('Task-') || (depth <= 1 && isTaskModuleDirName(e.name))) {
2238
+ availableTasks.push(e.name);
2239
+ }
2240
+ else if (!e.name.startsWith('.')) {
2241
+ await collect((0, path_1.join)(dir, e.name), depth + 1);
2242
+ }
2243
+ }
2244
+ };
2245
+ await collect(taskDir, 0);
2220
2246
  }
2221
2247
  catch { }
2222
2248
  (0, prompt_builder_1.outputNeedsInfo)({
@@ -2543,6 +2569,14 @@ async function runPromptMode(iteration, options) {
2543
2569
  process.stdout.write(`✅ 代码已由子 Agent 直接写入 ${dispatchResult.filesChanged.length} 个文件:\n`);
2544
2570
  for (const f of dispatchResult.filesChanged)
2545
2571
  process.stdout.write(` 📄 ${f}\n`);
2572
+ // v8.3.337+: SDK 直连路径同样跑完成收尾(标记 completed + 骨架体检兑底),
2573
+ // 修复子 Agent 只写代码不覆盖端级骨架时文档保持空模板的问题
2574
+ try {
2575
+ await finalizeTaskCompletion(iteration, task, taskDir);
2576
+ }
2577
+ catch (e) {
2578
+ logger_1.logger.debug(`[execute] SDK 直连完成收尾失败(不阻断): ${e.message}`);
2579
+ }
2546
2580
  process.exitCode = 0;
2547
2581
  return;
2548
2582
  }
@@ -2609,6 +2643,80 @@ async function runPromptMode(iteration, options) {
2609
2643
  process.exitCode = 10;
2610
2644
  }
2611
2645
  // ═══════════════════════════════════════════════════════════
2646
+ // 任务完成收尾(v8.3.337+)——标记 completed + 联动 TASK.md + 完成骨架体检 + 组级聚合
2647
+ // 从 runApplyMode 尾部抽出为公共函数:SDK 直连(filesChanged)、--all、批次等
2648
+ // 所有执行路径共用,确保任何路径任务完成后端级骨架都有兜底体检。
2649
+ // ═══════════════════════════════════════════════════════════
2650
+ async function finalizeTaskCompletion(iteration, task, taskDir) {
2651
+ // 更新 PROJECT_GRAPH
2652
+ await updateProjectGraphStatus(iteration, task);
2653
+ // ── 标记任务状态为 completed ──
2654
+ try {
2655
+ const metaDir = (0, path_1.join)(taskDir, '.meta');
2656
+ await (0, fs_extra_1.ensureDir)(metaDir);
2657
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(metaDir, 'status'), 'completed');
2658
+ }
2659
+ catch { }
2660
+ // v8.3.273+: 联动 TASK.md——状态行改「已完成」+ 工作清单全勾选(修复状态与 checklist 脱节)
2661
+ try {
2662
+ const taskMdPath = (0, path_1.join)(taskDir, 'TASK.md');
2663
+ if (await (0, fs_extra_1.pathExists)(taskMdPath)) {
2664
+ let md = await (0, fs_extra_1.readFile)(taskMdPath, 'utf-8');
2665
+ md = md.replace(/^(- \*\*状态\*\*: ).*$/m, '$1已完成');
2666
+ md = md.replace(/^- \[ \]/gm, '- [x]');
2667
+ await (0, fs_extra_1.writeFile)(taskMdPath, md);
2668
+ }
2669
+ }
2670
+ catch { /* TASK.md 联动失败不阻断 */ }
2671
+ // v8.3.313+: 完成骨架体检——端级文档仍含骨架占位时,先尝试 CLI 自动降级填充
2672
+ // (REQ/TECH 从模块级 00-specs 真实规格派生端级快照),填不了的继续挂「骨架待补」待办(TASK.md + .issues.md 双账本)
2673
+ try {
2674
+ const residues = await (0, skeleton_residue_1.collectSkeletonResidues)(taskDir);
2675
+ if (residues.length > 0) {
2676
+ const filled = await (0, skeleton_residue_1.autoFillSkeletonFallback)(taskDir, residues);
2677
+ const remain = residues.filter(r => !filled.has(r.rel));
2678
+ if (filled.size > 0) {
2679
+ logger_1.logger.info(` 📄 完成骨架体检:${filled.size} 份端级骨架已由 CLI 自动降级填充(模块级规格快照,可在此基础上补充端级细节)`);
2680
+ }
2681
+ if (remain.length > 0) {
2682
+ logger_1.logger.warn(` ⚠️ 完成骨架体检:${remain.length} 份端级文档仍含骨架占位(无模块级规格可降级),已挂「骨架待补」待办(状态保持 completed,建议尽快补写规格)`);
2683
+ }
2684
+ await (0, skeleton_residue_1.appendSkeletonTodos)(taskDir, remain);
2685
+ }
2686
+ }
2687
+ catch { /* 完成骨架体检失败不阻断 */ }
2688
+ // v8.3.273+: 组级状态聚合——所属 Task 组全部子任务完成后,组级 .meta/status 同步 completed
2689
+ // v8.3.337+: 组目录定位改用 isTaskModuleDirName(兼容新命名链 admin-web-01-xxx 与旧 Task-NNN)
2690
+ try {
2691
+ const parts = taskDir.split('/');
2692
+ const taskIdx = parts.findIndex(p => (0, task_paths_1.isTaskModuleDirName)(p));
2693
+ if (taskIdx >= 0) {
2694
+ const groupDir = parts.slice(0, taskIdx + 1).join('/');
2695
+ const statuses = [];
2696
+ const collect = async (dir) => {
2697
+ for (const e of await (0, fs_extra_1.readdir)(dir, { withFileTypes: true })) {
2698
+ if (!e.isDirectory())
2699
+ continue;
2700
+ const stPath = (0, path_1.join)(dir, e.name, '.meta', 'status');
2701
+ if (await (0, fs_extra_1.pathExists)(stPath)) {
2702
+ statuses.push((await (0, fs_extra_1.readFile)(stPath, 'utf-8')).trim());
2703
+ }
2704
+ else {
2705
+ await collect((0, path_1.join)(dir, e.name));
2706
+ }
2707
+ }
2708
+ };
2709
+ await collect(groupDir);
2710
+ const subtaskStatuses = statuses.filter((_, i) => i !== 0); // 排除组级自身
2711
+ if (subtaskStatuses.length > 0 && subtaskStatuses.every(s => s === 'completed')) {
2712
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(groupDir, '.meta', 'status'), 'completed');
2713
+ logger_1.logger.info(` 📊 组级状态已同步: ${(0, path_1.basename)(groupDir)} → completed(${subtaskStatuses.length} 个子任务全部完成)`);
2714
+ }
2715
+ }
2716
+ }
2717
+ catch { /* 组级聚合失败不阻断 */ }
2718
+ }
2719
+ // ═══════════════════════════════════════════════════════════
2612
2720
  // Response 模式 — 接收 AI 返回内容,写入文件
2613
2721
  // ═══════════════════════════════════════════════════════════
2614
2722
  async function runApplyMode(iteration, options) {
@@ -2880,65 +2988,9 @@ async function runApplyMode(iteration, options) {
2880
2988
  // 写入 .issues.md 失败不阻断
2881
2989
  }
2882
2990
  }
2883
- // 更新 PROJECT_GRAPH
2884
- await updateProjectGraphStatus(iteration, task);
2885
- // ── 标记任务状态为 completed ──
2991
+ // v8.3.337+: 任务完成收尾(标记 completed + TASK.md 联动 + 骨架体检 + 组级聚合)抽为公共函数
2886
2992
  const taskDirForStatus = await resolveTaskDir(iterDir, task);
2887
- try {
2888
- const metaDir = (0, path_1.join)(taskDirForStatus, '.meta');
2889
- await (0, fs_extra_1.ensureDir)(metaDir);
2890
- await (0, fs_extra_1.writeFile)((0, path_1.join)(metaDir, 'status'), 'completed');
2891
- }
2892
- catch { }
2893
- // v8.3.273+: 联动 TASK.md——状态行改「已完成」+ 工作清单全勾选(修复状态与 checklist 脱节)
2894
- try {
2895
- const taskMdPath = (0, path_1.join)(taskDirForStatus, 'TASK.md');
2896
- if (await (0, fs_extra_1.pathExists)(taskMdPath)) {
2897
- let md = await (0, fs_extra_1.readFile)(taskMdPath, 'utf-8');
2898
- md = md.replace(/^(- \*\*状态\*\*: ).*$/m, '$1已完成');
2899
- md = md.replace(/^- \[ \]/gm, '- [x]');
2900
- await (0, fs_extra_1.writeFile)(taskMdPath, md);
2901
- }
2902
- }
2903
- catch { /* TASK.md 联动失败不阻断 */ }
2904
- // v8.3.313+: 完成骨架体检——端级文档仍含骨架占位时 completed 照常,但挂「骨架待补」待办(TASK.md + .issues.md 双账本)
2905
- try {
2906
- const residues = await (0, skeleton_residue_1.collectSkeletonResidues)(taskDirForStatus);
2907
- if (residues.length > 0) {
2908
- logger_1.logger.warn(` ⚠️ 完成骨架体检:${residues.length} 份端级文档仍含骨架占位,已挂「骨架待补」待办(状态保持 completed,建议尽快补写规格)`);
2909
- await (0, skeleton_residue_1.appendSkeletonTodos)(taskDirForStatus, residues);
2910
- }
2911
- }
2912
- catch { /* 完成骨架体检失败不阻断 */ }
2913
- // v8.3.273+: 组级状态聚合——所属 Task 组全部子任务完成后,组级 .meta/status 同步 completed
2914
- try {
2915
- const parts = taskDirForStatus.split('/');
2916
- const taskIdx = parts.findIndex(p => /^Task-\d+/.test(p));
2917
- if (taskIdx >= 0) {
2918
- const groupDir = parts.slice(0, taskIdx + 1).join('/');
2919
- const statuses = [];
2920
- const collect = async (dir) => {
2921
- for (const e of await (0, fs_extra_1.readdir)(dir, { withFileTypes: true })) {
2922
- if (!e.isDirectory())
2923
- continue;
2924
- const stPath = (0, path_1.join)(dir, e.name, '.meta', 'status');
2925
- if (await (0, fs_extra_1.pathExists)(stPath)) {
2926
- statuses.push((await (0, fs_extra_1.readFile)(stPath, 'utf-8')).trim());
2927
- }
2928
- else {
2929
- await collect((0, path_1.join)(dir, e.name));
2930
- }
2931
- }
2932
- };
2933
- await collect(groupDir);
2934
- const subtaskStatuses = statuses.filter((_, i) => i !== 0); // 排除组级自身
2935
- if (subtaskStatuses.length > 0 && subtaskStatuses.every(s => s === 'completed')) {
2936
- await (0, fs_extra_1.writeFile)((0, path_1.join)(groupDir, '.meta', 'status'), 'completed');
2937
- logger_1.logger.info(` 📊 组级状态已同步: ${(0, path_1.basename)(groupDir)} → completed(${subtaskStatuses.length} 个子任务全部完成)`);
2938
- }
2939
- }
2940
- }
2941
- catch { /* 组级聚合失败不阻断 */ }
2993
+ await finalizeTaskCompletion(iteration, task, taskDirForStatus);
2942
2994
  logger_1.logger.success(`\n📁 完成: ${writtenCount} 个文件已写入`);
2943
2995
  logger_1.logger.info(` 📂 位置: ${iterDir}/`);
2944
2996
  // v6.69.1: 记录任务执行快照,支持下次增量检测
@@ -2984,7 +3036,7 @@ function outputPostSummary(iteration, task, fileCount, filePaths) {
2984
3036
  logger_1.logger.info('');
2985
3037
  logger_1.logger.warn('🔄 检测到接口/实体变更,建议刷新全局层:');
2986
3038
  logger_1.logger.info(` → speccore global analyze --with-code # 刷新全局 FUNCTION_MAP.md / API_CONTRACT.yaml`);
2987
- logger_1.logger.info(` → speccore refresh # 刷新检索索引`);
3039
+ logger_1.logger.info(` → speccore reindex # 重建检索索引`);
2988
3040
  }
2989
3041
  logger_1.logger.info('━'.repeat(40));
2990
3042
  }