@entelligentsia/forgecli 1.0.14 → 1.0.21

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 (363) hide show
  1. package/CHANGELOG.md +185 -0
  2. package/README.md +7 -1
  3. package/dist/CHANGELOG-forge-plugin.md +61 -0
  4. package/dist/bin/config.js +4 -4
  5. package/dist/bin/config.js.map +1 -1
  6. package/dist/bin/update-cli.d.ts +1 -1
  7. package/dist/bin/update-cli.js +1 -1
  8. package/dist/bin/update-cli.js.map +1 -1
  9. package/dist/extensions/forgecli/ask-user-tool.js +1 -1
  10. package/dist/extensions/forgecli/ask-user-tool.js.map +1 -1
  11. package/dist/extensions/forgecli/commands/add-pipeline.d.ts +19 -0
  12. package/dist/extensions/forgecli/commands/add-pipeline.js +143 -0
  13. package/dist/extensions/forgecli/commands/add-pipeline.js.map +1 -0
  14. package/dist/extensions/forgecli/commands/add-task.d.ts +20 -0
  15. package/dist/extensions/forgecli/commands/add-task.js +154 -0
  16. package/dist/extensions/forgecli/commands/add-task.js.map +1 -0
  17. package/dist/extensions/forgecli/commands/approve.d.ts +22 -0
  18. package/dist/extensions/forgecli/commands/approve.js +152 -0
  19. package/dist/extensions/forgecli/commands/approve.js.map +1 -0
  20. package/dist/extensions/forgecli/commands/collate.d.ts +22 -0
  21. package/dist/extensions/forgecli/commands/collate.js +134 -0
  22. package/dist/extensions/forgecli/commands/collate.js.map +1 -0
  23. package/dist/extensions/forgecli/commands/commit.d.ts +22 -0
  24. package/dist/extensions/forgecli/commands/commit.js +152 -0
  25. package/dist/extensions/forgecli/commands/commit.js.map +1 -0
  26. package/dist/extensions/forgecli/commands/config-command.d.ts +8 -0
  27. package/dist/extensions/forgecli/commands/config-command.js +67 -0
  28. package/dist/extensions/forgecli/commands/config-command.js.map +1 -0
  29. package/dist/extensions/forgecli/commands/enhance.d.ts +45 -0
  30. package/dist/extensions/forgecli/commands/enhance.js +219 -0
  31. package/dist/extensions/forgecli/commands/enhance.js.map +1 -0
  32. package/dist/extensions/forgecli/commands/implement.d.ts +22 -0
  33. package/dist/extensions/forgecli/commands/implement.js +170 -0
  34. package/dist/extensions/forgecli/commands/implement.js.map +1 -0
  35. package/dist/extensions/forgecli/commands/plan.d.ts +22 -0
  36. package/dist/extensions/forgecli/commands/plan.js +167 -0
  37. package/dist/extensions/forgecli/commands/plan.js.map +1 -0
  38. package/dist/extensions/forgecli/commands/quiz-agent.d.ts +17 -0
  39. package/dist/extensions/forgecli/commands/quiz-agent.js +98 -0
  40. package/dist/extensions/forgecli/commands/quiz-agent.js.map +1 -0
  41. package/dist/extensions/forgecli/commands/read-command.d.ts +2 -0
  42. package/dist/extensions/forgecli/commands/read-command.js +100 -0
  43. package/dist/extensions/forgecli/commands/read-command.js.map +1 -0
  44. package/dist/extensions/forgecli/commands/regenerate.d.ts +40 -0
  45. package/dist/extensions/forgecli/commands/regenerate.js +426 -0
  46. package/dist/extensions/forgecli/commands/regenerate.js.map +1 -0
  47. package/dist/extensions/forgecli/commands/remove-command.d.ts +17 -0
  48. package/dist/extensions/forgecli/commands/remove-command.js +124 -0
  49. package/dist/extensions/forgecli/commands/remove-command.js.map +1 -0
  50. package/dist/extensions/forgecli/commands/report-bug.d.ts +25 -0
  51. package/dist/extensions/forgecli/commands/report-bug.js +159 -0
  52. package/dist/extensions/forgecli/commands/report-bug.js.map +1 -0
  53. package/dist/extensions/forgecli/commands/retrospective.d.ts +20 -0
  54. package/dist/extensions/forgecli/commands/retrospective.js +126 -0
  55. package/dist/extensions/forgecli/commands/retrospective.js.map +1 -0
  56. package/dist/extensions/forgecli/commands/review-code.d.ts +35 -0
  57. package/dist/extensions/forgecli/commands/review-code.js +196 -0
  58. package/dist/extensions/forgecli/commands/review-code.js.map +1 -0
  59. package/dist/extensions/forgecli/commands/review-plan.d.ts +35 -0
  60. package/dist/extensions/forgecli/commands/review-plan.js +200 -0
  61. package/dist/extensions/forgecli/commands/review-plan.js.map +1 -0
  62. package/dist/extensions/forgecli/commands/sprint-intake.d.ts +10 -0
  63. package/dist/extensions/forgecli/commands/sprint-intake.js +91 -0
  64. package/dist/extensions/forgecli/commands/sprint-intake.js.map +1 -0
  65. package/dist/extensions/forgecli/commands/sprint-plan.d.ts +14 -0
  66. package/dist/extensions/forgecli/commands/sprint-plan.js +122 -0
  67. package/dist/extensions/forgecli/commands/sprint-plan.js.map +1 -0
  68. package/dist/extensions/forgecli/commands/status-command.d.ts +19 -0
  69. package/dist/extensions/forgecli/commands/status-command.js +140 -0
  70. package/dist/extensions/forgecli/commands/status-command.js.map +1 -0
  71. package/dist/extensions/forgecli/commands/store-query.d.ts +22 -0
  72. package/dist/extensions/forgecli/commands/store-query.js +107 -0
  73. package/dist/extensions/forgecli/commands/store-query.js.map +1 -0
  74. package/dist/extensions/forgecli/commands/store-repair.d.ts +17 -0
  75. package/dist/extensions/forgecli/commands/store-repair.js +123 -0
  76. package/dist/extensions/forgecli/commands/store-repair.js.map +1 -0
  77. package/dist/extensions/forgecli/commands/test-orchestrate.d.ts +2 -0
  78. package/dist/extensions/forgecli/commands/test-orchestrate.js +182 -0
  79. package/dist/extensions/forgecli/commands/test-orchestrate.js.map +1 -0
  80. package/dist/extensions/forgecli/commands/transcripts-command.d.ts +87 -0
  81. package/dist/extensions/forgecli/commands/transcripts-command.js +418 -0
  82. package/dist/extensions/forgecli/commands/transcripts-command.js.map +1 -0
  83. package/dist/extensions/forgecli/commands/validate.d.ts +22 -0
  84. package/dist/extensions/forgecli/commands/validate.js +152 -0
  85. package/dist/extensions/forgecli/commands/validate.js.map +1 -0
  86. package/dist/extensions/forgecli/config/config-layer.d.ts +53 -0
  87. package/dist/extensions/forgecli/config/config-layer.js +72 -0
  88. package/dist/extensions/forgecli/config/config-layer.js.map +1 -0
  89. package/dist/extensions/forgecli/config/config-writer.d.ts +16 -0
  90. package/dist/extensions/forgecli/config/config-writer.js +69 -0
  91. package/dist/extensions/forgecli/config/config-writer.js.map +1 -0
  92. package/dist/extensions/forgecli/config/model-registry.d.ts +61 -0
  93. package/dist/extensions/forgecli/config/model-registry.js +127 -0
  94. package/dist/extensions/forgecli/config/model-registry.js.map +1 -0
  95. package/dist/extensions/forgecli/config/model-resolver.d.ts +32 -0
  96. package/dist/extensions/forgecli/config/model-resolver.js +65 -0
  97. package/dist/extensions/forgecli/config/model-resolver.js.map +1 -0
  98. package/dist/extensions/forgecli/config/model-validator.d.ts +29 -0
  99. package/dist/extensions/forgecli/config/model-validator.js +107 -0
  100. package/dist/extensions/forgecli/config/model-validator.js.map +1 -0
  101. package/dist/extensions/forgecli/config-layer.d.ts +0 -16
  102. package/dist/extensions/forgecli/config-layer.js +0 -5
  103. package/dist/extensions/forgecli/config-layer.js.map +1 -1
  104. package/dist/extensions/forgecli/config-tui/component.js +1 -1
  105. package/dist/extensions/forgecli/config-tui/component.js.map +1 -1
  106. package/dist/extensions/forgecli/config-tui/handler.js +1 -1
  107. package/dist/extensions/forgecli/config-tui/handler.js.map +1 -1
  108. package/dist/extensions/forgecli/config-tui/screens/override-editor.js +1 -1
  109. package/dist/extensions/forgecli/config-tui/screens/override-editor.js.map +1 -1
  110. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js +1 -1
  111. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js.map +1 -1
  112. package/dist/extensions/forgecli/config-tui/screens/show-resolved.js +1 -1
  113. package/dist/extensions/forgecli/config-tui/screens/show-resolved.js.map +1 -1
  114. package/dist/extensions/forgecli/config-tui/state/buffer.d.ts +2 -2
  115. package/dist/extensions/forgecli/config-tui/state/model.d.ts +1 -1
  116. package/dist/extensions/forgecli/config-tui/state/reducer.js.map +1 -1
  117. package/dist/extensions/forgecli/config-tui/state/selectors.d.ts +2 -2
  118. package/dist/extensions/forgecli/config-tui/state/selectors.js +1 -1
  119. package/dist/extensions/forgecli/config-tui/state/selectors.js.map +1 -1
  120. package/dist/extensions/forgecli/context-governor-compaction.d.ts +94 -0
  121. package/dist/extensions/forgecli/context-governor-compaction.js +327 -0
  122. package/dist/extensions/forgecli/context-governor-compaction.js.map +1 -0
  123. package/dist/extensions/forgecli/context-governor.d.ts +169 -0
  124. package/dist/extensions/forgecli/context-governor.js +592 -0
  125. package/dist/extensions/forgecli/context-governor.js.map +1 -0
  126. package/dist/extensions/forgecli/dashboard/component.d.ts +17 -5
  127. package/dist/extensions/forgecli/dashboard/component.js +160 -115
  128. package/dist/extensions/forgecli/dashboard/component.js.map +1 -1
  129. package/dist/extensions/forgecli/dashboard/register.js +7 -21
  130. package/dist/extensions/forgecli/dashboard/register.js.map +1 -1
  131. package/dist/extensions/forgecli/dashboard/theme.d.ts +27 -0
  132. package/dist/extensions/forgecli/dashboard/theme.js +91 -0
  133. package/dist/extensions/forgecli/dashboard/theme.js.map +1 -0
  134. package/dist/extensions/forgecli/fix-bug.js +59 -5
  135. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  136. package/dist/extensions/forgecli/forge-artifact-tool.js +3 -2
  137. package/dist/extensions/forgecli/forge-artifact-tool.js.map +1 -1
  138. package/dist/extensions/forgecli/forge-cli-schema.json +0 -4
  139. package/dist/extensions/forgecli/forge-commands.js +1 -1
  140. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  141. package/dist/extensions/forgecli/forge-init/forge-init.d.ts +26 -0
  142. package/dist/extensions/forgecli/forge-init/forge-init.js +514 -0
  143. package/dist/extensions/forgecli/forge-init/forge-init.js.map +1 -0
  144. package/dist/extensions/forgecli/forge-init/init-context.d.ts +99 -0
  145. package/dist/extensions/forgecli/forge-init/init-context.js +178 -0
  146. package/dist/extensions/forgecli/forge-init/init-context.js.map +1 -0
  147. package/dist/extensions/forgecli/forge-init/init-progress.d.ts +39 -0
  148. package/dist/extensions/forgecli/forge-init/init-progress.js +117 -0
  149. package/dist/extensions/forgecli/forge-init/init-progress.js.map +1 -0
  150. package/dist/extensions/forgecli/forge-init/phase4-register.js +1 -1
  151. package/dist/extensions/forgecli/forge-init/phase4-register.js.map +1 -1
  152. package/dist/extensions/forgecli/forge-init/run-phases.js +2 -2
  153. package/dist/extensions/forgecli/forge-init/run-phases.js.map +1 -1
  154. package/dist/extensions/forgecli/forge-subagent.d.ts +42 -1
  155. package/dist/extensions/forgecli/forge-subagent.js +59 -18
  156. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  157. package/dist/extensions/forgecli/forge-tools.d.ts +0 -25
  158. package/dist/extensions/forgecli/forge-tools.js +8 -37
  159. package/dist/extensions/forgecli/forge-tools.js.map +1 -1
  160. package/dist/extensions/forgecli/governor-config.d.ts +19 -0
  161. package/dist/extensions/forgecli/governor-config.js +58 -0
  162. package/dist/extensions/forgecli/governor-config.js.map +1 -0
  163. package/dist/extensions/forgecli/health-check.js +1 -1
  164. package/dist/extensions/forgecli/health-check.js.map +1 -1
  165. package/dist/extensions/forgecli/hook-dispatcher.d.ts +3 -1
  166. package/dist/extensions/forgecli/hook-dispatcher.js +39 -5
  167. package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
  168. package/dist/extensions/forgecli/hooks/post-init-hook.js +11 -6
  169. package/dist/extensions/forgecli/hooks/post-init-hook.js.map +1 -1
  170. package/dist/extensions/forgecli/hooks/post-sprint-hook.js +11 -6
  171. package/dist/extensions/forgecli/hooks/post-sprint-hook.js.map +1 -1
  172. package/dist/extensions/forgecli/hooks/write-guard.js +1 -1
  173. package/dist/extensions/forgecli/hooks/write-guard.js.map +1 -1
  174. package/dist/extensions/forgecli/index.js +70 -36
  175. package/dist/extensions/forgecli/index.js.map +1 -1
  176. package/dist/extensions/forgecli/kickoff.d.ts +9 -0
  177. package/dist/extensions/forgecli/kickoff.js +15 -0
  178. package/dist/extensions/forgecli/kickoff.js.map +1 -1
  179. package/dist/extensions/forgecli/lib/forge-config.d.ts +1 -1
  180. package/dist/extensions/forgecli/lib/forge-config.js +1 -1
  181. package/dist/extensions/forgecli/lib/forge-config.js.map +1 -1
  182. package/dist/extensions/forgecli/lib/forge-root.d.ts +10 -0
  183. package/dist/extensions/forgecli/lib/forge-root.js +62 -0
  184. package/dist/extensions/forgecli/lib/forge-root.js.map +1 -0
  185. package/dist/extensions/forgecli/lib/halt-advisor.d.ts +19 -14
  186. package/dist/extensions/forgecli/lib/halt-advisor.js +36 -13
  187. package/dist/extensions/forgecli/lib/halt-advisor.js.map +1 -1
  188. package/dist/extensions/forgecli/lib/run-cjs.d.ts +26 -0
  189. package/dist/extensions/forgecli/lib/run-cjs.js +42 -0
  190. package/dist/extensions/forgecli/lib/run-cjs.js.map +1 -0
  191. package/dist/extensions/forgecli/orchestrator-status-bar.d.ts +3 -2
  192. package/dist/extensions/forgecli/orchestrator-status-bar.js +90 -60
  193. package/dist/extensions/forgecli/orchestrator-status-bar.js.map +1 -1
  194. package/dist/extensions/forgecli/orchestrator-tree.d.ts +4 -0
  195. package/dist/extensions/forgecli/orchestrator-tree.js +21 -3
  196. package/dist/extensions/forgecli/orchestrator-tree.js.map +1 -1
  197. package/dist/extensions/forgecli/orchestrators/calibrate.d.ts +64 -0
  198. package/dist/extensions/forgecli/orchestrators/calibrate.js +481 -0
  199. package/dist/extensions/forgecli/orchestrators/calibrate.js.map +1 -0
  200. package/dist/extensions/forgecli/orchestrators/fix-bug.d.ts +93 -0
  201. package/dist/extensions/forgecli/orchestrators/fix-bug.js +1705 -0
  202. package/dist/extensions/forgecli/orchestrators/fix-bug.js.map +1 -0
  203. package/dist/extensions/forgecli/orchestrators/halt-advisor.d.ts +59 -0
  204. package/dist/extensions/forgecli/orchestrators/halt-advisor.js +113 -0
  205. package/dist/extensions/forgecli/orchestrators/halt-advisor.js.map +1 -0
  206. package/dist/extensions/forgecli/orchestrators/materialize.d.ts +16 -0
  207. package/dist/extensions/forgecli/orchestrators/materialize.js +195 -0
  208. package/dist/extensions/forgecli/orchestrators/materialize.js.map +1 -0
  209. package/dist/extensions/forgecli/orchestrators/migrate.d.ts +22 -0
  210. package/dist/extensions/forgecli/orchestrators/migrate.js +260 -0
  211. package/dist/extensions/forgecli/orchestrators/migrate.js.map +1 -0
  212. package/dist/extensions/forgecli/orchestrators/orchestrator-preflight.d.ts +46 -0
  213. package/dist/extensions/forgecli/orchestrators/orchestrator-preflight.js +64 -0
  214. package/dist/extensions/forgecli/orchestrators/orchestrator-preflight.js.map +1 -0
  215. package/dist/extensions/forgecli/orchestrators/run-sprint.d.ts +27 -0
  216. package/dist/extensions/forgecli/orchestrators/run-sprint.js +734 -0
  217. package/dist/extensions/forgecli/orchestrators/run-sprint.js.map +1 -0
  218. package/dist/extensions/forgecli/orchestrators/run-task.d.ts +215 -0
  219. package/dist/extensions/forgecli/orchestrators/run-task.js +1491 -0
  220. package/dist/extensions/forgecli/orchestrators/run-task.js.map +1 -0
  221. package/dist/extensions/forgecli/paths/paths.d.ts +8 -0
  222. package/dist/extensions/forgecli/paths/paths.js +17 -0
  223. package/dist/extensions/forgecli/paths/paths.js.map +1 -1
  224. package/dist/extensions/forgecli/phase-vocab.d.ts +31 -0
  225. package/dist/extensions/forgecli/phase-vocab.js +82 -0
  226. package/dist/extensions/forgecli/phase-vocab.js.map +1 -0
  227. package/dist/extensions/forgecli/run-sprint.d.ts +3 -1
  228. package/dist/extensions/forgecli/run-sprint.js +1 -0
  229. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  230. package/dist/extensions/forgecli/run-task.d.ts +34 -1
  231. package/dist/extensions/forgecli/run-task.js +144 -6
  232. package/dist/extensions/forgecli/run-task.js.map +1 -1
  233. package/dist/extensions/forgecli/session-registry.d.ts +2 -2
  234. package/dist/extensions/forgecli/session-registry.js +6 -2
  235. package/dist/extensions/forgecli/session-registry.js.map +1 -1
  236. package/dist/extensions/forgecli/skill-curation/friction-emit.d.ts +99 -0
  237. package/dist/extensions/forgecli/skill-curation/friction-emit.js +245 -0
  238. package/dist/extensions/forgecli/skill-curation/friction-emit.js.map +1 -0
  239. package/dist/extensions/forgecli/skill-curation/skill-curation-flag.d.ts +21 -0
  240. package/dist/extensions/forgecli/skill-curation/skill-curation-flag.js +71 -0
  241. package/dist/extensions/forgecli/skill-curation/skill-curation-flag.js.map +1 -0
  242. package/dist/extensions/forgecli/skill-curation/skill-curator-subagent.d.ts +102 -0
  243. package/dist/extensions/forgecli/skill-curation/skill-curator-subagent.js +339 -0
  244. package/dist/extensions/forgecli/skill-curation/skill-curator-subagent.js.map +1 -0
  245. package/dist/extensions/forgecli/skill-curation/skill-retriever.d.ts +84 -0
  246. package/dist/extensions/forgecli/skill-curation/skill-retriever.js +246 -0
  247. package/dist/extensions/forgecli/skill-curation/skill-retriever.js.map +1 -0
  248. package/dist/extensions/forgecli/skill-curation/skill-usage-tracker.d.ts +91 -0
  249. package/dist/extensions/forgecli/skill-curation/skill-usage-tracker.js +224 -0
  250. package/dist/extensions/forgecli/skill-curation/skill-usage-tracker.js.map +1 -0
  251. package/dist/extensions/forgecli/store/store-error-remediation.d.ts +65 -0
  252. package/dist/extensions/forgecli/store/store-error-remediation.js +307 -0
  253. package/dist/extensions/forgecli/store/store-error-remediation.js.map +1 -0
  254. package/dist/extensions/forgecli/store/store-resolver.d.ts +56 -0
  255. package/dist/extensions/forgecli/store/store-resolver.js +263 -0
  256. package/dist/extensions/forgecli/store/store-resolver.js.map +1 -0
  257. package/dist/extensions/forgecli/store/store-validator.d.ts +16 -0
  258. package/dist/extensions/forgecli/store/store-validator.js +32 -0
  259. package/dist/extensions/forgecli/store/store-validator.js.map +1 -0
  260. package/dist/extensions/forgecli/store/transition-guard.d.ts +20 -0
  261. package/dist/extensions/forgecli/store/transition-guard.js +89 -0
  262. package/dist/extensions/forgecli/store/transition-guard.js.map +1 -0
  263. package/dist/extensions/forgecli/subagent/orchestrator-transcript.js +5 -0
  264. package/dist/extensions/forgecli/subagent/orchestrator-transcript.js.map +1 -1
  265. package/dist/extensions/forgecli/thread-switcher.d.ts +4 -1
  266. package/dist/extensions/forgecli/thread-switcher.js +36 -21
  267. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  268. package/dist/extensions/forgecli/transcript-archive-types.d.ts +171 -0
  269. package/dist/extensions/forgecli/transcript-archive-types.js +130 -0
  270. package/dist/extensions/forgecli/transcript-archive-types.js.map +1 -0
  271. package/dist/extensions/forgecli/transcript-archive.d.ts +127 -0
  272. package/dist/extensions/forgecli/transcript-archive.js +656 -0
  273. package/dist/extensions/forgecli/transcript-archive.js.map +1 -0
  274. package/dist/extensions/forgecli/transcript-replay.d.ts +28 -0
  275. package/dist/extensions/forgecli/transcript-replay.js +153 -0
  276. package/dist/extensions/forgecli/transcript-replay.js.map +1 -0
  277. package/dist/extensions/forgecli/transcripts-tui/component.d.ts +36 -0
  278. package/dist/extensions/forgecli/transcripts-tui/component.js +112 -0
  279. package/dist/extensions/forgecli/transcripts-tui/component.js.map +1 -0
  280. package/dist/extensions/forgecli/transcripts-tui/index.d.ts +4 -0
  281. package/dist/extensions/forgecli/transcripts-tui/index.js +5 -0
  282. package/dist/extensions/forgecli/transcripts-tui/index.js.map +1 -0
  283. package/dist/extensions/forgecli/transcripts-tui/screens/browse.d.ts +21 -0
  284. package/dist/extensions/forgecli/transcripts-tui/screens/browse.js +172 -0
  285. package/dist/extensions/forgecli/transcripts-tui/screens/browse.js.map +1 -0
  286. package/dist/extensions/forgecli/transcripts-tui/screens/types.d.ts +22 -0
  287. package/dist/extensions/forgecli/transcripts-tui/screens/types.js +4 -0
  288. package/dist/extensions/forgecli/transcripts-tui/screens/types.js.map +1 -0
  289. package/dist/extensions/forgecli/transcripts-tui/state/index.d.ts +4 -0
  290. package/dist/extensions/forgecli/transcripts-tui/state/index.js +5 -0
  291. package/dist/extensions/forgecli/transcripts-tui/state/index.js.map +1 -0
  292. package/dist/extensions/forgecli/transcripts-tui/state/init.d.ts +8 -0
  293. package/dist/extensions/forgecli/transcripts-tui/state/init.js +18 -0
  294. package/dist/extensions/forgecli/transcripts-tui/state/init.js.map +1 -0
  295. package/dist/extensions/forgecli/transcripts-tui/state/model.d.ts +56 -0
  296. package/dist/extensions/forgecli/transcripts-tui/state/model.js +6 -0
  297. package/dist/extensions/forgecli/transcripts-tui/state/model.js.map +1 -0
  298. package/dist/extensions/forgecli/transcripts-tui/state/reducer.d.ts +2 -0
  299. package/dist/extensions/forgecli/transcripts-tui/state/reducer.js +51 -0
  300. package/dist/extensions/forgecli/transcripts-tui/state/reducer.js.map +1 -0
  301. package/dist/extensions/forgecli/transcripts-tui/state/selectors.d.ts +10 -0
  302. package/dist/extensions/forgecli/transcripts-tui/state/selectors.js +62 -0
  303. package/dist/extensions/forgecli/transcripts-tui/state/selectors.js.map +1 -0
  304. package/dist/extensions/forgecli/transcripts-tui/theme.d.ts +20 -0
  305. package/dist/extensions/forgecli/transcripts-tui/theme.js +47 -0
  306. package/dist/extensions/forgecli/transcripts-tui/theme.js.map +1 -0
  307. package/dist/extensions/forgecli/tui/banner.d.ts +10 -0
  308. package/dist/extensions/forgecli/tui/banner.js +36 -0
  309. package/dist/extensions/forgecli/tui/banner.js.map +1 -0
  310. package/dist/extensions/forgecli/tui/forge-header.d.ts +12 -0
  311. package/dist/extensions/forgecli/tui/forge-header.js +114 -0
  312. package/dist/extensions/forgecli/tui/forge-header.js.map +1 -0
  313. package/dist/extensions/forgecli/tui/input-router.d.ts +33 -0
  314. package/dist/extensions/forgecli/tui/input-router.js +136 -0
  315. package/dist/extensions/forgecli/tui/input-router.js.map +1 -0
  316. package/dist/extensions/forgecli/tui/orchestrator-status-bar.d.ts +26 -0
  317. package/dist/extensions/forgecli/tui/orchestrator-status-bar.js +213 -0
  318. package/dist/extensions/forgecli/tui/orchestrator-status-bar.js.map +1 -0
  319. package/dist/extensions/forgecli/tui/thread-switcher.d.ts +18 -0
  320. package/dist/extensions/forgecli/tui/thread-switcher.js +194 -0
  321. package/dist/extensions/forgecli/tui/thread-switcher.js.map +1 -0
  322. package/dist/extensions/forgecli/update/forge-update-command.d.ts +100 -0
  323. package/dist/extensions/forgecli/update/forge-update-command.js +435 -0
  324. package/dist/extensions/forgecli/update/forge-update-command.js.map +1 -0
  325. package/dist/extensions/forgecli/update/migration-engine.d.ts +117 -0
  326. package/dist/extensions/forgecli/update/migration-engine.js +563 -0
  327. package/dist/extensions/forgecli/update/migration-engine.js.map +1 -0
  328. package/dist/extensions/forgecli/update/update-check.d.ts +37 -0
  329. package/dist/extensions/forgecli/update/update-check.js +185 -0
  330. package/dist/extensions/forgecli/update/update-check.js.map +1 -0
  331. package/dist/extensions/forgecli/update/update-tools.d.ts +23 -0
  332. package/dist/extensions/forgecli/update/update-tools.js +135 -0
  333. package/dist/extensions/forgecli/update/update-tools.js.map +1 -0
  334. package/dist/extensions/forgecli/update/whats-new-widget.d.ts +26 -0
  335. package/dist/extensions/forgecli/update/whats-new-widget.js +376 -0
  336. package/dist/extensions/forgecli/update/whats-new-widget.js.map +1 -0
  337. package/dist/extensions/forgecli/update/whats-new.d.ts +120 -0
  338. package/dist/extensions/forgecli/update/whats-new.js +470 -0
  339. package/dist/extensions/forgecli/update/whats-new.js.map +1 -0
  340. package/dist/extensions/forgecli/viewport/events.d.ts +113 -0
  341. package/dist/extensions/forgecli/viewport/events.js +290 -0
  342. package/dist/extensions/forgecli/viewport/events.js.map +1 -0
  343. package/dist/extensions/forgecli/viewport/renderer.d.ts +102 -0
  344. package/dist/extensions/forgecli/viewport/renderer.js +277 -0
  345. package/dist/extensions/forgecli/viewport/renderer.js.map +1 -0
  346. package/dist/extensions/forgecli/viewport/theme.d.ts +11 -0
  347. package/dist/extensions/forgecli/viewport/theme.js +131 -0
  348. package/dist/extensions/forgecli/viewport/theme.js.map +1 -0
  349. package/dist/extensions/forgecli/wf-engine/engine.js +1 -1
  350. package/dist/extensions/forgecli/wf-engine/engine.js.map +1 -1
  351. package/dist/forge-payload/.base-pack/workflows/implement_plan.md +9 -0
  352. package/dist/forge-payload/.base-pack/workflows/plan_task.md +7 -0
  353. package/dist/forge-payload/.base-pack/workflows/review_code.md +4 -3
  354. package/dist/forge-payload/.base-pack/workflows/review_plan.md +4 -3
  355. package/dist/forge-payload/.base-pack/workflows/validate_task.md +4 -3
  356. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  357. package/dist/forge-payload/.schemas/migrations.json +132 -27
  358. package/dist/forge-payload/meta/workflows/meta-review-implementation.md +4 -3
  359. package/dist/forge-payload/meta/workflows/meta-review-plan.md +4 -3
  360. package/dist/forge-payload/meta/workflows/meta-validate.md +4 -3
  361. package/dist/forge-payload/tools/collate.cjs +32 -0
  362. package/dist/forge-payload/tools/postflight-gate.cjs +56 -10
  363. package/package.json +5 -3
@@ -0,0 +1,734 @@
1
+ // run-sprint.ts — /forge:run-sprint native Orchestrator handler (FORGE-S21-T03, Plan 12).
2
+ //
3
+ // Sprint-level orchestrator that iterates over a sprint's task list,
4
+ // delegating per-task execution to runTaskPipeline (extracted from run-task.ts).
5
+ //
6
+ // The sprint handler does NOT contain its own phase loop; per-phase concerns
7
+ // are ALL delegated to runTaskPipeline. The sprint handler owns sprint
8
+ // coordination only: resolve sprint, confirm gates, iterate tasks, persist
9
+ // sprint state, dispatch architect ceremony, and emit sprint-scoped events.
10
+ //
11
+ // Plan 12 truth table (§3):
12
+ // Clean-complete → architect ceremony (mode=complete) → sprint-complete event
13
+ // User-paused → architect ceremony (mode=partial) if ≥1 task done → sprint-complete event (verdict=partial)
14
+ // Halted-on-failure → NO ceremony → sprint-halted event
15
+ //
16
+ // Iron Laws enforced here:
17
+ // IL1 — code only under forge-cli/src/extensions/forgecli/
18
+ // IL6 — no shell-string interpolation; all external calls via spawnSync argv arrays
19
+ // IL7 — every failure path emits ctx.ui.notify and returns; no silent continuation
20
+ // IL10 — ALL LLM dispatch goes through runForgeSubagent (NO sendKickoff)
21
+ //
22
+ // sendKickoff is NEVER called from this file.
23
+ // Audit-grep: grep -n "sendKickoff(" run-sprint.ts must return empty.
24
+ //
25
+ // N-H-D — Ceremony vs per-task model routing:
26
+ // The sprint ceremony phase (architect summary / sprint-complete event) uses
27
+ // loadLayeredConfig + lookupPersonaModel directly (~lines 216, 227) without
28
+ // calling validateModelConfig. This is intentional: the ceremony is a single
29
+ // LLM call with a known model and the overhead of full preflight validation
30
+ // is not warranted here.
31
+ // Per-task dispatch is entirely delegated to runTaskPipeline, which calls
32
+ // runOrchestratorPreflight (persona/model config validation) at entry before
33
+ // any LLM dispatch. The two paths are deliberately separate.
34
+ // Reference: orchestrator-preflight.ts (N-H-D, FORGE-S25-T17).
35
+ import { spawnSync } from "node:child_process";
36
+ import * as fs from "node:fs";
37
+ import * as path from "node:path";
38
+ import { fileURLToPath } from "node:url";
39
+ import { assertAudience } from "../audience-gate.js";
40
+ import { loadLayeredConfig } from "../config/config-layer.js";
41
+ import { loadForgePersona, runForgeSubagent } from "../forge-subagent.js";
42
+ import { getSubagentTools } from "../forge-tools.js";
43
+ import { emitSyntheticEvent } from "../hook-dispatcher.js";
44
+ import { readPersonaDir as readPersonaDirSprint, readPipelineNames as readPipelineNamesSprint, } from "../lib/catalog-helpers.js";
45
+ import { discoverForgeConfigCached } from "../lib/forge-config.js";
46
+ import { archiveRun, sweepProjectTranscripts } from "../transcript-archive.js";
47
+ import { checkMaterialization } from "../lib/manifest-checker.js";
48
+ import { readJsonState, sprintStateFilePath, writeJsonState } from "../lib/state-helpers.js";
49
+ import { lookupPersonaModel } from "../config/model-resolver.js";
50
+ import { validateModelConfig } from "../config/model-validator.js";
51
+ import { loadWorkflow } from "../parsers/workflow-loader.js";
52
+ import { getSessionRegistry } from "../session-registry.js";
53
+ import { getOrchestratorTree } from "../orchestrator-tree.js";
54
+ import { resolveToCanonicalId, resolveToolDir } from "../store/store-resolver.js";
55
+ import { attachViewportObserver } from "../viewport/events.js";
56
+ import { emitEvent, formatLocalTime, isNonInteractive, isoCompact, isStateStale, readState as readTaskState, runTaskPipeline, validateId, } from "./run-task.js";
57
+ // FORGE-S25-T16 (N-H-B): sprint state helpers delegate to lib/state-helpers.ts.
58
+ function getSprintStatePath(cwd, sprintId) {
59
+ if (!validateId(sprintId)) {
60
+ throw new Error(`Invalid sprintId for state file path: ${sprintId}`);
61
+ }
62
+ return sprintStateFilePath(cwd, sprintId);
63
+ }
64
+ function readSprintState(cwd, sprintId) {
65
+ return readJsonState(getSprintStatePath(cwd, sprintId));
66
+ }
67
+ function writeSprintState(cwd, state) {
68
+ writeJsonState(getSprintStatePath(cwd, state.sprintId), state);
69
+ }
70
+ function deleteSprintState(cwd, sprintId) {
71
+ const fp = getSprintStatePath(cwd, sprintId);
72
+ try {
73
+ if (fs.existsSync(fp))
74
+ fs.unlinkSync(fp);
75
+ }
76
+ catch {
77
+ // non-fatal
78
+ }
79
+ }
80
+ function isSprintStateStale(state) {
81
+ const savedAt = new Date(state.savedAt).getTime();
82
+ const ageMs = Date.now() - savedAt;
83
+ const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
84
+ return ageMs > sevenDaysMs;
85
+ }
86
+ function readSprintRecord(sprintId, storeCli, cwd) {
87
+ const result = spawnSync("node", [storeCli, "read", "sprint", sprintId], { cwd, encoding: "utf8" });
88
+ if (result.status !== 0)
89
+ return null;
90
+ try {
91
+ const raw = typeof result.stdout === "string" ? result.stdout : String(result.stdout);
92
+ const record = JSON.parse(raw);
93
+ // Validate taskIds is a non-empty array of strings
94
+ if (!Array.isArray(record.taskIds) || record.taskIds.length === 0)
95
+ return null;
96
+ if (!record.taskIds.every((id) => typeof id === "string"))
97
+ return null;
98
+ return record;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ async function dispatchSprintCeremony(params) {
105
+ const { sprintId, mode, completedTaskIds, pausedAfterIndex, cwd, forgeRoot, ctx, registry, streamFnFactory, forgeToolDefs, } = params;
106
+ const startMs = Date.now();
107
+ // Materialized workflow path — already shipped from base pack.
108
+ const workflowName = "architect_review_sprint_completion";
109
+ const personaName = "architect";
110
+ let persona;
111
+ try {
112
+ persona = loadForgePersona(personaName, cwd);
113
+ }
114
+ catch {
115
+ return {
116
+ verdict: "revision-required",
117
+ durationMs: Date.now() - startMs,
118
+ errorMessage: `architect persona not found`,
119
+ };
120
+ }
121
+ const taskLines = [
122
+ `# Sprint Completion Review — ${sprintId}`,
123
+ ``,
124
+ `Mode: ${mode}`,
125
+ `Completed tasks: ${completedTaskIds.join(", ") || "(none)"}`,
126
+ ];
127
+ if (pausedAfterIndex !== undefined) {
128
+ taskLines.push(`Paused after task index: ${pausedAfterIndex}`);
129
+ }
130
+ taskLines.push(``, `Execute the materialized workflow at \`.forge/workflows/${workflowName}.md\`.`, `Do not emit any phase event yourself; the orchestrator owns event emission.`);
131
+ const task = taskLines.join("\n");
132
+ // Use a dedicated session id (NOT a taskId) so the thread-switcher renders it
133
+ // as a sprint-scoped chip distinct from per-task sessions.
134
+ const sessionId = `${sprintId}:ceremony`;
135
+ registry.startSession(sessionId);
136
+ registry.startPhase(sessionId, "ceremony", 0);
137
+ // Bridge: register ceremony phase in OrchestratorTree so the dashboard
138
+ // shows the ceremony as a leaf under the sprint root.
139
+ const tree = getOrchestratorTree();
140
+ tree.startNode(sessionId, { parentId: sprintId, label: "ceremony", kind: "leaf" });
141
+ let model;
142
+ let provider;
143
+ let errorMessage;
144
+ const observer = attachViewportObserver({
145
+ registry,
146
+ sessionId,
147
+ phaseRole: "ceremony",
148
+ displayRole: "ceremony",
149
+ beginHeader: `─── sprint ${sprintId} ceremony begin · ${personaName} ───`,
150
+ });
151
+ // Resolve model routing for the ceremony's architect persona (Plan 16 Slice 2).
152
+ // N-B-E: surface schema errors to caller (Decision 9 — orchestrators fail-fast).
153
+ // See doc/decisions/layered-config-error-policy.md.
154
+ const { merged: ceremonyModelConfig, errors: ceremonyCfgErrors } = loadLayeredConfig(cwd);
155
+ if (ceremonyCfgErrors.length > 0) {
156
+ for (const e of ceremonyCfgErrors) {
157
+ ctx.ui.notify(`× forge:run-sprint ceremony — forge-cli config schema error: ${e}`, "error");
158
+ }
159
+ return {
160
+ verdict: "revision-required",
161
+ durationMs: Date.now() - startMs,
162
+ errorMessage: `forge-cli config schema errors: ${ceremonyCfgErrors.join("; ")}`,
163
+ };
164
+ }
165
+ const ceremonyModelLookup = lookupPersonaModel(personaName, "default", ceremonyModelConfig);
166
+ try {
167
+ const result = await runForgeSubagent({
168
+ persona,
169
+ task,
170
+ cwd,
171
+ exportTag: `${sprintId}__ceremony`,
172
+ tailLog: observer.state.tailLog,
173
+ forgeRoot,
174
+ streamFn: streamFnFactory?.({ kind: "ceremony", persona: personaName }),
175
+ // Sprint-scoped prompt-cache key — every subagent spawned across
176
+ // the sprint (ceremonies + per-task phases) shares this namespace
177
+ // so the system-prompt + persona prefix stays warm.
178
+ cacheSessionId: `forge:${sprintId}`,
179
+ onEvent: observer.onEvent,
180
+ requestedModel: ceremonyModelLookup.model,
181
+ modelRegistry: ctx.modelRegistry,
182
+ customTools: forgeToolDefs ? getSubagentTools(forgeToolDefs, persona.name) : undefined,
183
+ });
184
+ model = result.model;
185
+ provider = result.provider;
186
+ if (result.exitCode !== 0) {
187
+ errorMessage = result.errorMessage ?? "architect subagent exited non-zero";
188
+ }
189
+ }
190
+ catch (e) {
191
+ const err = e;
192
+ errorMessage = err?.message ?? "runForgeSubagent threw";
193
+ }
194
+ finally {
195
+ registry.completeSession(sessionId, errorMessage ? "failed" : "completed");
196
+ tree.completeNode(sessionId, errorMessage ? "failed" : "completed");
197
+ }
198
+ // Parse verdict from store: did the architect actually transition the sprint?
199
+ // The store is the source of truth — verdict text in SPRINT_COMPLETION_REVIEW.md
200
+ // is human-readable but the store status is authoritative.
201
+ let verdict = "revision-required";
202
+ const readResult = spawnSync("node", [`${forgeRoot}/tools/store-cli.cjs`, "read", "sprint", sprintId], {
203
+ cwd,
204
+ encoding: "utf8",
205
+ });
206
+ if (readResult.status === 0) {
207
+ try {
208
+ const sprint = JSON.parse(readResult.stdout);
209
+ if (sprint.status === "completed")
210
+ verdict = "complete";
211
+ else if (sprint.status === "partially-completed")
212
+ verdict = "partial";
213
+ // else: status unchanged → revision-required
214
+ }
215
+ catch {
216
+ // fall through with revision-required
217
+ }
218
+ }
219
+ return {
220
+ verdict,
221
+ model,
222
+ provider,
223
+ durationMs: Date.now() - startMs,
224
+ errorMessage,
225
+ };
226
+ }
227
+ const SPRINT_STATUS_KEY = "forge:run-sprint";
228
+ export function registerRunSprint(pi, options = {}) {
229
+ pi.registerCommand("forge:run-sprint", {
230
+ description: "Run all tasks in a sprint sequentially. " +
231
+ "Usage: /forge:run-sprint <SPRINT_ID>. " +
232
+ "Orchestrator archetype: delegates per-task execution to runTaskPipeline.",
233
+ async handler(args, ctx) {
234
+ const cwd = options.cwd ?? process.cwd();
235
+ let sprintId = args.trim();
236
+ if (!sprintId) {
237
+ ctx.ui.notify("× forge:run-sprint — sprint ID required. Usage: /forge:run-sprint <SPRINT_ID>", "error");
238
+ return;
239
+ }
240
+ // Path traversal validation (advisory #3)
241
+ if (!validateId(sprintId)) {
242
+ ctx.ui.notify(`× forge:run-sprint — invalid sprint ID format: ${sprintId}`, "error");
243
+ return;
244
+ }
245
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, `run-sprint ${sprintId}: initializing…`);
246
+ // ── Discover forge config ────────────────────────────────────────
247
+ const forgeConfig = discoverForgeConfigCached(cwd);
248
+ if (!forgeConfig) {
249
+ ctx.ui.notify("× forge:run-sprint — no Forge project found at cwd. Run /forge:init first.", "error");
250
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
251
+ return;
252
+ }
253
+ const forgeRoot = forgeConfig.forgeRoot;
254
+ // Best-effort transcript-archive sweep: adopt any project-local runs
255
+ // not yet in the central index (crash recovery + pre-existing
256
+ // history). Runs BEFORE any task pipeline creates its transcript
257
+ // writer, so in-flight runs are never swept half-written.
258
+ sweepProjectTranscripts(cwd);
259
+ // ── Resolve sprint ID (prefix-normalize, suffix-match, NLP fallback) ──
260
+ // Handles unprefixed IDs like "S22" → "FORGE-S22".
261
+ // Issue #20: unprefixed entity IDs silently poisoned substitutions.
262
+ const toolDir = resolveToolDir(forgeRoot);
263
+ const resolvedSprintId = await resolveToCanonicalId(sprintId, toolDir, cwd, "sprint", {
264
+ ctx,
265
+ commandLabel: "forge:run-sprint",
266
+ });
267
+ if (!resolvedSprintId) {
268
+ // Error already emitted by resolver
269
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
270
+ return;
271
+ }
272
+ // Replace raw arg with canonical ID for all subsequent operations.
273
+ sprintId = resolvedSprintId;
274
+ // Update status with canonical ID so the user sees the resolved form.
275
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, `run-sprint ${sprintId}: ready`);
276
+ const storeCli = path.join(forgeRoot, "tools", "store-cli.cjs");
277
+ const preflightGate = path.join(forgeRoot, "tools", "preflight-gate.cjs");
278
+ // ── Sprint resolution ────────────────────────────────────────────
279
+ const sprintRecord = readSprintRecord(sprintId, storeCli, cwd);
280
+ if (!sprintRecord) {
281
+ ctx.ui.notify(`× forge:run-sprint — could not read sprint ${sprintId} or sprint has no task IDs.`, "error");
282
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
283
+ return;
284
+ }
285
+ const taskIds = sprintRecord.taskIds;
286
+ ctx.ui.notify(`▶ forge:run-sprint — sprint ${sprintId}: ${taskIds.length} tasks`, "info");
287
+ // ── Audience check (AC B-12) ──────────────────────────────────────
288
+ // Read the run_sprint workflow for audience check.
289
+ const workflowPath = path.join(cwd, ".forge", "workflows", "run_sprint.md");
290
+ let workflowMd;
291
+ let workflowAudience = "any";
292
+ try {
293
+ const loaded = loadWorkflow(workflowPath);
294
+ workflowMd = loaded.rawMarkdown;
295
+ workflowAudience = loaded.audience;
296
+ }
297
+ catch {
298
+ // Workflow file may not exist — default to orchestrator-only since
299
+ // /forge:run-sprint is an orchestrator archetype command.
300
+ workflowMd = "";
301
+ workflowAudience = "orchestrator-only";
302
+ }
303
+ if (!assertAudience({ workflowName: "run_sprint", audience: workflowAudience }, ctx)) {
304
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
305
+ return;
306
+ }
307
+ // ── Materialization-marker check (AC B-5) ────────────────────────
308
+ if (workflowMd) {
309
+ const markerCheck = checkMaterialization(workflowPath, workflowMd);
310
+ if (!markerCheck.ok) {
311
+ for (const marker of markerCheck.missing) {
312
+ ctx.ui.notify(`× workflow regression: ${marker} not found in ${workflowPath}`, "error");
313
+ }
314
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
315
+ return;
316
+ }
317
+ }
318
+ // ── Pre-flight confirm (AC B-7) ───────────────────────────────────
319
+ if (!isNonInteractive()) {
320
+ const proceed = await ctx.ui.confirm(`Begin sprint ${sprintId}?`, `Tasks: ${taskIds.join(", ")}`);
321
+ if (!proceed) {
322
+ ctx.ui.notify("forge:run-sprint — sprint aborted.", "info");
323
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
324
+ return;
325
+ }
326
+ }
327
+ // ── Sprint-level resume detection (AC B-11) ──────────────────────
328
+ const existingSprintState = readSprintState(cwd, sprintId);
329
+ let startTaskIndex = 0;
330
+ let completedTaskIds = [];
331
+ const resumeTaskStates = new Map();
332
+ if (existingSprintState) {
333
+ if (isSprintStateStale(existingSprintState)) {
334
+ ctx.ui.notify(`⚠ forge:run-sprint — cached sprint state for ${sprintId} is stale (>7 days old, saved at ${formatLocalTime(existingSprintState.savedAt)}). Offering purge.`, "warning");
335
+ if (!isNonInteractive()) {
336
+ const purge = await ctx.ui.confirm(`Purge stale sprint state for ${sprintId}?`, "The cached state is older than 7 days. Purge and restart from the beginning?");
337
+ if (purge) {
338
+ deleteSprintState(cwd, sprintId);
339
+ }
340
+ else {
341
+ ctx.ui.notify("forge:run-sprint — stale state kept; aborting.", "info");
342
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
343
+ return;
344
+ }
345
+ }
346
+ else {
347
+ // Non-interactive: auto-abort on stale state (advisory #9)
348
+ ctx.ui.notify("forge:run-sprint — stale sprint state; non-interactive mode auto-aborting.", "info");
349
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
350
+ return;
351
+ }
352
+ }
353
+ else {
354
+ // Fresh state: offer resume
355
+ if (!isNonInteractive()) {
356
+ const resume = await ctx.ui.confirm(`Resume sprint ${sprintId}?`, `Cached state found at task ${existingSprintState.taskIndex} (${existingSprintState.completedTaskIds.length} completed). Resume from here?`);
357
+ if (resume) {
358
+ startTaskIndex = existingSprintState.taskIndex;
359
+ completedTaskIds = existingSprintState.completedTaskIds;
360
+ ctx.ui.notify(`forge:run-sprint — resuming ${sprintId} from task ${taskIds[startTaskIndex] ?? startTaskIndex}`, "info");
361
+ // Collect halted and cancelled task states for mid-task resume
362
+ // (REVIEW FIX #2, option b; ADR-S21-01: cancelled states are
363
+ // resumable from the beginning of the cancelled phase).
364
+ for (const taskId of taskIds.slice(startTaskIndex)) {
365
+ const taskState = readTaskState(cwd, taskId);
366
+ if (taskState && (taskState.halted || taskState.status === "cancelled")) {
367
+ resumeTaskStates.set(taskId, taskState);
368
+ }
369
+ }
370
+ }
371
+ else {
372
+ deleteSprintState(cwd, sprintId);
373
+ }
374
+ }
375
+ else {
376
+ // Non-interactive + existing state: auto-abort (advisory #9)
377
+ ctx.ui.notify(`forge:run-sprint — cached sprint state for ${sprintId} found but non-interactive mode; aborting.`, "info");
378
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
379
+ return;
380
+ }
381
+ }
382
+ }
383
+ // ── Model routing pre-flight (Plan 16 Slice 3) ──────────────────────
384
+ // Validate routing config once at sprint start (before any LLM calls).
385
+ // N-B-E: surface schema errors first (Decision 9 — orchestrators fail-fast).
386
+ // See doc/decisions/layered-config-error-policy.md.
387
+ {
388
+ const { merged: modelRoutingConfig, errors: schemaErrors } = loadLayeredConfig(cwd);
389
+ if (schemaErrors.length > 0) {
390
+ for (const e of schemaErrors) {
391
+ ctx.ui.notify(`× forge:run-sprint — forge-cli config schema error: ${e}`, "error");
392
+ }
393
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
394
+ return;
395
+ }
396
+ const personasDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "forge-payload", ".base-pack", "personas");
397
+ const personaCatalogue = readPersonaDirSprint(personasDir);
398
+ const forgeCfgPath = path.join(cwd, ".forge", "config.json");
399
+ const pipelineCatalogue = readPipelineNamesSprint(forgeCfgPath);
400
+ const availableModels = ctx.modelRegistry?.getAvailable?.() ?? [];
401
+ const strict = process.env.FORGE_STRICT_MODELS === "1";
402
+ const { errors, warnings } = validateModelConfig(personaCatalogue, pipelineCatalogue, modelRoutingConfig, availableModels.map((m) => ({ provider: m.provider, id: m.id })), strict);
403
+ for (const w of warnings) {
404
+ ctx.ui.notify(`⚠ forge:run-sprint — model routing: ${w.message}`, "warning");
405
+ }
406
+ if (errors.length > 0) {
407
+ for (const e of errors) {
408
+ ctx.ui.notify(`× forge:run-sprint — model routing: ${e.message}`, "error");
409
+ }
410
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
411
+ return;
412
+ }
413
+ }
414
+ // ── Per-task loop (AC B-8) ────────────────────────────────────────
415
+ const registry = getSessionRegistry();
416
+ const tree = getOrchestratorTree();
417
+ let lastModel;
418
+ let lastProvider;
419
+ const sprintStartMs = Date.now();
420
+ // Bridge: register sprint root in OrchestratorTree.
421
+ tree.startNode(sprintId, { label: `wfl:run-sprint`, kind: "orchestrator" });
422
+ for (let i = startTaskIndex; i < taskIds.length; i++) {
423
+ const taskId = taskIds[i];
424
+ if (!taskId)
425
+ continue;
426
+ // ── Skip already-completed tasks ──────────────────────────────
427
+ // If the task is already committed/approved in the store, skip the
428
+ // phase pipeline and accumulate it as completed. This handles
429
+ // re-runs where tasks finished in a prior attempt.
430
+ {
431
+ const taskReadResult = spawnSync("node", [`${forgeRoot}/tools/store-cli.cjs`, "read", "task", taskId], {
432
+ cwd,
433
+ encoding: "utf8",
434
+ });
435
+ if (taskReadResult.status === 0) {
436
+ try {
437
+ const taskRecord = JSON.parse(taskReadResult.stdout);
438
+ if (taskRecord.status === "committed" || taskRecord.status === "completed") {
439
+ ctx.ui.notify(`▶ ${sprintId}: task ${i + 1}/${taskIds.length} — ${taskId} already ${taskRecord.status}, skipping.`, "info");
440
+ completedTaskIds.push(taskId);
441
+ lastModel = taskRecord.model ?? lastModel;
442
+ lastProvider = taskRecord.provider ?? lastProvider;
443
+ writeSprintState(cwd, {
444
+ sprintId,
445
+ taskIndex: i + 1,
446
+ completedTaskIds,
447
+ halted: false,
448
+ savedAt: new Date().toISOString(),
449
+ });
450
+ continue;
451
+ }
452
+ }
453
+ catch {
454
+ // Malformed task record — fall through to runTaskPipeline
455
+ }
456
+ }
457
+ }
458
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, `run-sprint ${sprintId}: task ${i + 1}/${taskIds.length} (${taskId})`);
459
+ ctx.ui.notify(`▶ ${sprintId}: task ${i + 1}/${taskIds.length} — ${taskId}`, "info");
460
+ // Determine resumeFromState for mid-task resume (REVIEW FIX #2).
461
+ // If a halted or cancelled task state exists for this task,
462
+ // pass it to runTaskPipeline so it resumes from the saved phase.
463
+ let resumeFromState = resumeTaskStates.get(taskId);
464
+ if (resumeFromState) {
465
+ // Validate the state is not corrupt
466
+ if (typeof resumeFromState.phaseIndex !== "number" ||
467
+ typeof resumeFromState.iterationCounts !== "object") {
468
+ ctx.ui.notify(`⚠ forge:run-sprint — corrupt task state for ${taskId}; starting fresh.`, "warning");
469
+ resumeFromState = undefined;
470
+ }
471
+ }
472
+ if (resumeFromState) {
473
+ const resumephaseRole = resumeFromState.phaseIndex;
474
+ const resumeStatus = resumeFromState.status ?? (resumeFromState.halted ? "halted" : "interrupted");
475
+ ctx.ui.notify(`▶ forge:run-sprint — resuming ${taskId} from phase ${resumephaseRole} (${resumeStatus})`, "info");
476
+ }
477
+ // Stale task state fallback: if task state >7d, delete and start fresh
478
+ if (resumeFromState && isStateStale(resumeFromState)) {
479
+ ctx.ui.notify(`⚠ forge:run-sprint — stale task state for ${taskId} (>7d); starting fresh.`, "warning");
480
+ resumeFromState = undefined;
481
+ }
482
+ // ── Session lifecycle for thread-switcher (REVIEW FIX #2) ──────
483
+ registry.startSession(taskId);
484
+ // Bridge: register task in OrchestratorTree under sprint root.
485
+ tree.startNode(taskId, { parentId: sprintId, label: `▸ wfl:run-task`, kind: "orchestrator" });
486
+ const taskSignal = registry.getAbortSignal(taskId);
487
+ const taskResult = await runTaskPipeline({
488
+ taskId,
489
+ cwd,
490
+ ctx,
491
+ forgeRoot,
492
+ storeCli,
493
+ preflightGate,
494
+ registry,
495
+ resumeFromState,
496
+ signal: taskSignal,
497
+ forgeToolDefs: options.forgeToolDefs,
498
+ extensionFactories: options.extensionFactories,
499
+ streamFnFactory: options.streamFnFactory
500
+ ? (c) => options.streamFnFactory?.({
501
+ kind: "task-phase",
502
+ persona: c.persona,
503
+ phase: c.phase,
504
+ taskId: c.taskId,
505
+ })
506
+ : undefined,
507
+ });
508
+ // Capture model/provider from last task result (REVIEW FIX #1)
509
+ if (taskResult.model)
510
+ lastModel = taskResult.model;
511
+ if (taskResult.provider)
512
+ lastProvider = taskResult.provider;
513
+ // Mirror the task run into the central transcript archive
514
+ // (best-effort — archiveRun never throws). The sprintId rides
515
+ // on the task manifest as a back-reference; a sprint surfaces
516
+ // as N task runs, no synthetic sprint container.
517
+ if (taskResult.orchestratorTranscriptPath) {
518
+ archiveRun({
519
+ cwd,
520
+ orchestratorJsonlPath: taskResult.orchestratorTranscriptPath,
521
+ sprintId,
522
+ });
523
+ }
524
+ // ── Handle task result ──────────────────────────────────────
525
+ if (taskResult.status === "completed") {
526
+ completedTaskIds.push(taskId);
527
+ registry.completeSession(taskId, "completed");
528
+ tree.completeNode(taskId, "completed");
529
+ }
530
+ else if (taskResult.status === "cancelled") {
531
+ // Task was cancelled by user — mark session, persist sprint
532
+ // state, emit sprint-halted with cancellation detail, exit.
533
+ registry.completeSession(taskId, "cancelled");
534
+ tree.completeNode(taskId, "cancelled");
535
+ tree.completeNode(sprintId, "cancelled");
536
+ const cancelledEvent = {
537
+ eventId: `${isoCompact(sprintStartMs)}_${sprintId}_sprint_halted`,
538
+ sprintId,
539
+ role: "orchestrator",
540
+ action: "sprint-halted",
541
+ startTimestamp: new Date(sprintStartMs).toISOString(),
542
+ endTimestamp: new Date(Date.now()).toISOString(),
543
+ durationMinutes: Math.round(((Date.now() - sprintStartMs) / 60000) * 100) / 100,
544
+ model: lastModel ?? "orchestrator",
545
+ provider: lastProvider ?? "orchestrator",
546
+ type: "sprint-halted",
547
+ haltedAtTaskIndex: i,
548
+ haltedAtTaskId: taskId,
549
+ lastError: "cancelled",
550
+ };
551
+ emitEvent(storeCli, cwd, sprintId, cancelledEvent);
552
+ writeSprintState(cwd, {
553
+ sprintId,
554
+ taskIndex: i,
555
+ completedTaskIds,
556
+ halted: true,
557
+ lastError: "cancelled",
558
+ savedAt: new Date().toISOString(),
559
+ });
560
+ ctx.ui.notify(`⊘ forge:run-sprint — ${sprintId} halted: task ${taskId} cancelled.`, "info");
561
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
562
+ return;
563
+ }
564
+ else {
565
+ // Task halted/escalated/failed: mark session failed, persist sprint state, emit sprint-halted, exit.
566
+ registry.completeSession(taskId, "failed");
567
+ tree.completeNode(taskId, "failed");
568
+ tree.completeNode(sprintId, "failed");
569
+ const haltedEvent = {
570
+ eventId: `${isoCompact(sprintStartMs)}_${sprintId}_sprint_halted`,
571
+ sprintId,
572
+ role: "orchestrator",
573
+ action: "sprint-halted",
574
+ startTimestamp: new Date(sprintStartMs).toISOString(),
575
+ endTimestamp: new Date(Date.now()).toISOString(),
576
+ durationMinutes: Math.round(((Date.now() - sprintStartMs) / 60000) * 100) / 100,
577
+ model: lastModel ?? "orchestrator",
578
+ provider: lastProvider ?? "orchestrator",
579
+ type: "sprint-halted",
580
+ haltedAtTaskIndex: i,
581
+ haltedAtTaskId: taskId,
582
+ lastError: taskResult.lastError ?? "unknown",
583
+ };
584
+ emitEvent(storeCli, cwd, sprintId, haltedEvent);
585
+ writeSprintState(cwd, {
586
+ sprintId,
587
+ taskIndex: i,
588
+ completedTaskIds,
589
+ halted: true,
590
+ lastError: taskResult.lastError,
591
+ savedAt: new Date().toISOString(),
592
+ });
593
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
594
+ return;
595
+ }
596
+ // ── Post-task confirm (AC B-9) ────────────────────────────
597
+ // Skip after final task
598
+ if (i < taskIds.length - 1 && !isNonInteractive()) {
599
+ const proceed = await ctx.ui.confirm(`Continue to next task?`, `${taskIds[i + 1]} is next. ${taskIds.length - i - 1} task(s) remaining.`);
600
+ if (!proceed) {
601
+ // Persist sprint state for resume.
602
+ writeSprintState(cwd, {
603
+ sprintId,
604
+ taskIndex: i + 1,
605
+ completedTaskIds,
606
+ halted: false,
607
+ savedAt: new Date().toISOString(),
608
+ });
609
+ // User-paused branch: dispatch ceremony if ≥1 task completed, emit partial event.
610
+ const pauseEndMs = Date.now();
611
+ let ceremonyResult;
612
+ if (completedTaskIds.length > 0) {
613
+ // Only dispatch ceremony if at least one task completed.
614
+ // A zero-progress pause has nothing to review.
615
+ ceremonyResult = await dispatchSprintCeremony({
616
+ sprintId,
617
+ mode: "partial",
618
+ completedTaskIds,
619
+ pausedAfterIndex: i,
620
+ cwd,
621
+ forgeRoot,
622
+ ctx,
623
+ registry,
624
+ streamFnFactory: options.streamFnFactory,
625
+ forgeToolDefs: options.forgeToolDefs,
626
+ });
627
+ }
628
+ const pausedEvent = {
629
+ eventId: `${isoCompact(sprintStartMs)}_${sprintId}_sprint_complete`,
630
+ sprintId,
631
+ role: "architect",
632
+ action: "sprint-complete",
633
+ startTimestamp: new Date(sprintStartMs).toISOString(),
634
+ endTimestamp: new Date(pauseEndMs).toISOString(),
635
+ durationMinutes: Math.round(((pauseEndMs - sprintStartMs) / 60000) * 100) / 100,
636
+ model: ceremonyResult?.model ?? lastModel ?? "orchestrator",
637
+ provider: ceremonyResult?.provider ?? lastProvider ?? "orchestrator",
638
+ type: "sprint-complete",
639
+ taskCount: taskIds.length,
640
+ completedTaskIds,
641
+ verdict: "partial",
642
+ pausedAfterTaskIndex: i,
643
+ waveCount: 1,
644
+ maxConcurrency: 1,
645
+ };
646
+ emitEvent(storeCli, cwd, sprintId, pausedEvent);
647
+ tree.completeNode(sprintId, "completed");
648
+ ctx.ui.notify("forge:run-sprint — sprint paused after task completion.", "info");
649
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
650
+ return;
651
+ }
652
+ }
653
+ // Persist sprint state after each task transition (AC B-10)
654
+ writeSprintState(cwd, {
655
+ sprintId,
656
+ taskIndex: i + 1,
657
+ completedTaskIds,
658
+ halted: false,
659
+ savedAt: new Date().toISOString(),
660
+ });
661
+ }
662
+ // ── All tasks complete — clean-complete branch (Plan 12 §3) ──────
663
+ const sprintEndMs = Date.now();
664
+ // Delete sprint state on successful completion
665
+ deleteSprintState(cwd, sprintId);
666
+ const ceremony = await dispatchSprintCeremony({
667
+ sprintId,
668
+ mode: "complete",
669
+ completedTaskIds,
670
+ cwd,
671
+ forgeRoot,
672
+ ctx,
673
+ registry,
674
+ streamFnFactory: options.streamFnFactory,
675
+ forgeToolDefs: options.forgeToolDefs,
676
+ });
677
+ const sprintEvent = {
678
+ eventId: `${isoCompact(sprintStartMs)}_${sprintId}_sprint_complete`,
679
+ sprintId,
680
+ role: "architect",
681
+ action: "sprint-complete",
682
+ startTimestamp: new Date(sprintStartMs).toISOString(),
683
+ endTimestamp: new Date(sprintEndMs).toISOString(),
684
+ durationMinutes: Math.round(((sprintEndMs - sprintStartMs) / 60000) * 100) / 100,
685
+ model: ceremony.model ?? lastModel ?? "orchestrator",
686
+ provider: ceremony.provider ?? lastProvider ?? "orchestrator",
687
+ type: "sprint-complete",
688
+ taskCount: taskIds.length,
689
+ completedTaskIds,
690
+ verdict: ceremony.verdict === "revision-required" ? "partial" : ceremony.verdict,
691
+ waveCount: 1,
692
+ maxConcurrency: 1,
693
+ };
694
+ const emitResult = emitEvent(storeCli, cwd, sprintId, sprintEvent);
695
+ if (!emitResult.ok) {
696
+ ctx.ui.notify(`⚠ forge:run-sprint — sprint-complete event emit failed: ${emitResult.stderr.trim()}`, "warning");
697
+ }
698
+ // ── Emit synthetic sprint-collate-complete event (FORGE-S21-T05) ──
699
+ // Fires the in-process hook for post-sprint-hook.ts to consume.
700
+ // Best-effort: failure-to-emit notifies but does NOT halt — sprint is
701
+ // already complete at this point.
702
+ try {
703
+ const collateEvent = {
704
+ type: "sprint-collate-complete",
705
+ sprintId,
706
+ cwd,
707
+ };
708
+ await emitSyntheticEvent(collateEvent, ctx);
709
+ }
710
+ catch (err) {
711
+ const e = err;
712
+ ctx.ui.notify(`⚠ forge:run-sprint — sprint-collate-complete synthetic event emit failed: ${e.message ?? "unknown"}`, "warning");
713
+ }
714
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
715
+ // Sprint root: mark completed in the tree. "revision-required" → failed
716
+ // because the architect rejected the sprint; partial → completed (the
717
+ // sprint itself finished, the progress indicator shows N/M tasks done).
718
+ const sprintTreeStatus = ceremony.verdict === "revision-required" ? "failed" : "completed";
719
+ tree.completeNode(sprintId, sprintTreeStatus);
720
+ if (ceremony.verdict === "complete") {
721
+ ctx.ui.notify(`〇 forge:run-sprint — sprint ${sprintId} complete (${completedTaskIds.length}/${taskIds.length} tasks).`, "info");
722
+ }
723
+ else if (ceremony.verdict === "revision-required") {
724
+ // Architect did not approve; surface to user.
725
+ ctx.ui.notify(`▲ forge:run-sprint — sprint ${sprintId} ceremony returned "Revision Required". ` +
726
+ `See engineering/sprints/${sprintId}/SPRINT_COMPLETION_REVIEW.md.`, "warning");
727
+ }
728
+ else {
729
+ ctx.ui.notify(`▲ forge:run-sprint — sprint ${sprintId} marked partially-completed by architect.`, "warning");
730
+ }
731
+ },
732
+ });
733
+ }
734
+ //# sourceMappingURL=run-sprint.js.map