@entelligentsia/forgecli 1.0.25 → 1.0.40

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 (590) hide show
  1. package/CHANGELOG.md +341 -0
  2. package/README.md +2 -0
  3. package/dist/CHANGELOG-forge-plugin.md +264 -0
  4. package/dist/CHANGELOG-pi.md +143 -0
  5. package/dist/bin/argv.d.ts +2 -2
  6. package/dist/bin/argv.js +37 -0
  7. package/dist/bin/argv.js.map +1 -1
  8. package/dist/bin/forge.js +30 -16
  9. package/dist/bin/forge.js.map +1 -1
  10. package/dist/bin/init.d.ts +23 -0
  11. package/dist/bin/init.js +123 -0
  12. package/dist/bin/init.js.map +1 -0
  13. package/dist/bin/reset.d.ts +39 -0
  14. package/dist/bin/reset.js +101 -0
  15. package/dist/bin/reset.js.map +1 -0
  16. package/dist/bin/uninstall.d.ts +20 -0
  17. package/dist/bin/uninstall.js +141 -0
  18. package/dist/bin/uninstall.js.map +1 -0
  19. package/dist/extensions/forgecli/claude-bootstrap/bootstrap.d.ts +40 -0
  20. package/dist/extensions/forgecli/claude-bootstrap/bootstrap.js +384 -0
  21. package/dist/extensions/forgecli/claude-bootstrap/bootstrap.js.map +1 -0
  22. package/dist/extensions/forgecli/claude-bootstrap/settings-merge.d.ts +46 -0
  23. package/dist/extensions/forgecli/claude-bootstrap/settings-merge.js +245 -0
  24. package/dist/extensions/forgecli/claude-bootstrap/settings-merge.js.map +1 -0
  25. package/dist/extensions/forgecli/claude-bootstrap/uninstall.d.ts +23 -0
  26. package/dist/extensions/forgecli/claude-bootstrap/uninstall.js +235 -0
  27. package/dist/extensions/forgecli/claude-bootstrap/uninstall.js.map +1 -0
  28. package/dist/extensions/forgecli/commands/reset.d.ts +16 -0
  29. package/dist/extensions/forgecli/commands/reset.js +83 -0
  30. package/dist/extensions/forgecli/commands/reset.js.map +1 -0
  31. package/dist/extensions/forgecli/dashboard/component.js +10 -7
  32. package/dist/extensions/forgecli/dashboard/component.js.map +1 -1
  33. package/dist/extensions/forgecli/forge-commands.d.ts +7 -2
  34. package/dist/extensions/forgecli/forge-commands.js +19 -5
  35. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  36. package/dist/extensions/forgecli/forge-subagent.d.ts +4 -4
  37. package/dist/extensions/forgecli/hooks/forge-permissions.js +20 -6
  38. package/dist/extensions/forgecli/hooks/forge-permissions.js.map +1 -1
  39. package/dist/extensions/forgecli/index.js +6 -3
  40. package/dist/extensions/forgecli/index.js.map +1 -1
  41. package/dist/extensions/forgecli/lib/forge-root.d.ts +6 -0
  42. package/dist/extensions/forgecli/lib/forge-root.js +52 -0
  43. package/dist/extensions/forgecli/lib/forge-root.js.map +1 -1
  44. package/dist/extensions/forgecli/lib/payload-manifest.d.ts +62 -0
  45. package/dist/extensions/forgecli/lib/payload-manifest.js +151 -0
  46. package/dist/extensions/forgecli/lib/payload-manifest.js.map +1 -0
  47. package/dist/extensions/forgecli/orchestrators/advisory-render.d.ts +9 -0
  48. package/dist/extensions/forgecli/orchestrators/advisory-render.js +107 -0
  49. package/dist/extensions/forgecli/orchestrators/advisory-render.js.map +1 -0
  50. package/dist/extensions/forgecli/orchestrators/bug/bug-body.d.ts +1 -0
  51. package/dist/extensions/forgecli/orchestrators/bug/bug-body.js +65 -0
  52. package/dist/extensions/forgecli/orchestrators/bug/bug-body.js.map +1 -0
  53. package/dist/extensions/forgecli/orchestrators/bug/bug-id.d.ts +23 -0
  54. package/dist/extensions/forgecli/orchestrators/bug/bug-id.js +140 -0
  55. package/dist/extensions/forgecli/orchestrators/bug/bug-id.js.map +1 -0
  56. package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.d.ts +54 -0
  57. package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.js +349 -0
  58. package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.js.map +1 -0
  59. package/dist/extensions/forgecli/orchestrators/bug/bug-phases.d.ts +11 -0
  60. package/dist/extensions/forgecli/orchestrators/bug/bug-phases.js +82 -0
  61. package/dist/extensions/forgecli/orchestrators/bug/bug-phases.js.map +1 -0
  62. package/dist/extensions/forgecli/orchestrators/bug/bug-state.d.ts +14 -0
  63. package/dist/extensions/forgecli/orchestrators/bug/bug-state.js +100 -0
  64. package/dist/extensions/forgecli/orchestrators/bug/bug-state.js.map +1 -0
  65. package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.d.ts +72 -0
  66. package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.js +204 -0
  67. package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.js.map +1 -0
  68. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.d.ts +38 -0
  69. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.js +198 -0
  70. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.js.map +1 -0
  71. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.d.ts +3 -0
  72. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.js +55 -0
  73. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.js.map +1 -0
  74. package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.d.ts +7 -0
  75. package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.js +293 -0
  76. package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.js.map +1 -0
  77. package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.d.ts +2 -0
  78. package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.js +501 -0
  79. package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.js.map +1 -0
  80. package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.d.ts +41 -0
  81. package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.js +5 -0
  82. package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.js.map +1 -0
  83. package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.d.ts +43 -0
  84. package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.js +85 -0
  85. package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.js.map +1 -0
  86. package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.d.ts +8 -0
  87. package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.js +37 -0
  88. package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.js.map +1 -0
  89. package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.d.ts +28 -0
  90. package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.js +45 -0
  91. package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.js.map +1 -0
  92. package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.d.ts +26 -0
  93. package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.js +75 -0
  94. package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.js.map +1 -0
  95. package/dist/extensions/forgecli/orchestrators/common/recovery-menu.d.ts +24 -0
  96. package/dist/extensions/forgecli/orchestrators/common/recovery-menu.js +58 -0
  97. package/dist/extensions/forgecli/orchestrators/common/recovery-menu.js.map +1 -0
  98. package/dist/extensions/forgecli/orchestrators/common/reset-pipeline.d.ts +53 -0
  99. package/dist/extensions/forgecli/orchestrators/common/reset-pipeline.js +131 -0
  100. package/dist/extensions/forgecli/orchestrators/common/reset-pipeline.js.map +1 -0
  101. package/dist/extensions/forgecli/orchestrators/common/summary-recovery.d.ts +24 -0
  102. package/dist/extensions/forgecli/orchestrators/common/summary-recovery.js +37 -0
  103. package/dist/extensions/forgecli/orchestrators/common/summary-recovery.js.map +1 -0
  104. package/dist/extensions/forgecli/orchestrators/fix-bug.d.ts +9 -93
  105. package/dist/extensions/forgecli/orchestrators/fix-bug.js +23 -1721
  106. package/dist/extensions/forgecli/orchestrators/fix-bug.js.map +1 -1
  107. package/dist/extensions/forgecli/orchestrators/halt-advisor.js +25 -3
  108. package/dist/extensions/forgecli/orchestrators/halt-advisor.js.map +1 -1
  109. package/dist/extensions/forgecli/orchestrators/run-sprint.d.ts +3 -12
  110. package/dist/extensions/forgecli/orchestrators/run-sprint.js +48 -270
  111. package/dist/extensions/forgecli/orchestrators/run-sprint.js.map +1 -1
  112. package/dist/extensions/forgecli/orchestrators/run-task.d.ts +10 -214
  113. package/dist/extensions/forgecli/orchestrators/run-task.js +31 -1481
  114. package/dist/extensions/forgecli/orchestrators/run-task.js.map +1 -1
  115. package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.d.ts +33 -0
  116. package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.js +135 -0
  117. package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.js.map +1 -0
  118. package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.d.ts +18 -0
  119. package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.js +55 -0
  120. package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.js.map +1 -0
  121. package/dist/extensions/forgecli/orchestrators/task/run-task-command.d.ts +9 -0
  122. package/dist/extensions/forgecli/orchestrators/task/run-task-command.js +174 -0
  123. package/dist/extensions/forgecli/orchestrators/task/run-task-command.js.map +1 -0
  124. package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.d.ts +2 -0
  125. package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.js +494 -0
  126. package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.js.map +1 -0
  127. package/dist/extensions/forgecli/orchestrators/task/run-task-types.d.ts +62 -0
  128. package/dist/extensions/forgecli/orchestrators/task/run-task-types.js +5 -0
  129. package/dist/extensions/forgecli/orchestrators/task/run-task-types.js.map +1 -0
  130. package/dist/extensions/forgecli/orchestrators/task/task-body.d.ts +4 -0
  131. package/dist/extensions/forgecli/orchestrators/task/task-body.js +48 -0
  132. package/dist/extensions/forgecli/orchestrators/task/task-body.js.map +1 -0
  133. package/dist/extensions/forgecli/orchestrators/task/task-events.d.ts +63 -0
  134. package/dist/extensions/forgecli/orchestrators/task/task-events.js +185 -0
  135. package/dist/extensions/forgecli/orchestrators/task/task-events.js.map +1 -0
  136. package/dist/extensions/forgecli/orchestrators/task/task-gates.d.ts +34 -0
  137. package/dist/extensions/forgecli/orchestrators/task/task-gates.js +78 -0
  138. package/dist/extensions/forgecli/orchestrators/task/task-gates.js.map +1 -0
  139. package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.d.ts +42 -0
  140. package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.js +370 -0
  141. package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.js.map +1 -0
  142. package/dist/extensions/forgecli/orchestrators/task/task-phases.d.ts +17 -0
  143. package/dist/extensions/forgecli/orchestrators/task/task-phases.js +48 -0
  144. package/dist/extensions/forgecli/orchestrators/task/task-phases.js.map +1 -0
  145. package/dist/extensions/forgecli/orchestrators/task/task-record.d.ts +9 -0
  146. package/dist/extensions/forgecli/orchestrators/task/task-record.js +58 -0
  147. package/dist/extensions/forgecli/orchestrators/task/task-record.js.map +1 -0
  148. package/dist/extensions/forgecli/orchestrators/task/task-state.d.ts +14 -0
  149. package/dist/extensions/forgecli/orchestrators/task/task-state.js +35 -0
  150. package/dist/extensions/forgecli/orchestrators/task/task-state.js.map +1 -0
  151. package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.d.ts +36 -0
  152. package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.js +187 -0
  153. package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.js.map +1 -0
  154. package/dist/extensions/forgecli/store/store-resolver.d.ts +15 -0
  155. package/dist/extensions/forgecli/store/store-resolver.js +118 -18
  156. package/dist/extensions/forgecli/store/store-resolver.js.map +1 -1
  157. package/dist/extensions/forgecli/update/forge-update-command.js +10 -7
  158. package/dist/extensions/forgecli/update/forge-update-command.js.map +1 -1
  159. package/dist/forge-payload/.base-pack/workflows/collator_agent.md +5 -6
  160. package/dist/forge-payload/.base-pack/workflows/migrate_structural.md +1 -1
  161. package/dist/forge-payload/.base-pack/workflows-js/wfl-init.js +449 -0
  162. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  163. package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
  164. package/dist/forge-payload/.schemas/migrations.json +170 -0
  165. package/dist/forge-payload/.schemas/payload-manifest.schema.json +100 -0
  166. package/dist/forge-payload/commands/add-pipeline.md +1 -1
  167. package/dist/forge-payload/commands/add-task.md +3 -3
  168. package/dist/forge-payload/{.base-pack/commands → commands}/approve.md +2 -2
  169. package/dist/forge-payload/commands/ask.md +1 -1
  170. package/dist/forge-payload/commands/check-agent.md +8 -24
  171. package/dist/forge-payload/{.base-pack/commands → commands}/collate.md +2 -2
  172. package/dist/forge-payload/{.base-pack/commands → commands}/commit.md +2 -2
  173. package/dist/forge-payload/commands/config.md +1 -1
  174. package/dist/forge-payload/commands/enhance.md +31 -5
  175. package/dist/forge-payload/{.base-pack/commands → commands}/fix-bug.md +2 -2
  176. package/dist/forge-payload/commands/health.md +1 -1
  177. package/dist/forge-payload/{.base-pack/commands → commands}/implement.md +2 -2
  178. package/dist/forge-payload/commands/init.md +186 -67
  179. package/dist/forge-payload/{.base-pack/commands → commands}/new-sprint.md +2 -2
  180. package/dist/forge-payload/{.base-pack/commands → commands}/plan-sprint.md +2 -2
  181. package/dist/forge-payload/{.base-pack/commands → commands}/plan.md +2 -2
  182. package/dist/forge-payload/commands/rebuild.md +3 -3
  183. package/dist/forge-payload/commands/remove.md +1 -1
  184. package/dist/forge-payload/commands/repair.md +1 -1
  185. package/dist/forge-payload/commands/report-bug.md +1 -1
  186. package/dist/forge-payload/commands/reset.md +117 -0
  187. package/dist/forge-payload/{.base-pack/commands → commands}/retro.md +2 -2
  188. package/dist/forge-payload/{.base-pack/commands → commands}/review-code.md +2 -2
  189. package/dist/forge-payload/{.base-pack/commands → commands}/review-plan.md +2 -2
  190. package/dist/forge-payload/{.base-pack/commands → commands}/run-sprint.md +2 -2
  191. package/dist/forge-payload/{.base-pack/commands → commands}/run-task.md +2 -2
  192. package/dist/forge-payload/commands/status.md +1 -1
  193. package/dist/forge-payload/commands/update.md +3 -3
  194. package/dist/forge-payload/{.base-pack/commands → commands}/validate.md +2 -2
  195. package/dist/forge-payload/hooks/forge-permissions.cjs +29 -6
  196. package/dist/forge-payload/hooks/lib/common.cjs +228 -0
  197. package/dist/forge-payload/hooks/lib/plugin-detection.cjs +106 -0
  198. package/dist/forge-payload/hooks/lib/update-msg.cjs +23 -0
  199. package/dist/forge-payload/hooks/lib/update-url.cjs +46 -0
  200. package/dist/forge-payload/hooks/lib/write-registry.js +53 -0
  201. package/dist/forge-payload/init/discovery/discover-database.md +32 -0
  202. package/dist/forge-payload/init/discovery/discover-processes.md +31 -0
  203. package/dist/forge-payload/init/discovery/discover-routing.md +31 -0
  204. package/dist/forge-payload/init/discovery/discover-stack.md +33 -0
  205. package/dist/forge-payload/init/discovery/discover-testing.md +34 -0
  206. package/dist/forge-payload/init/generation/generate-commands.md +171 -0
  207. package/dist/forge-payload/init/generation/generate-kb-doc.md +60 -0
  208. package/dist/forge-payload/init/generation/generate-persona.md +73 -0
  209. package/dist/forge-payload/init/generation/generate-skill.md +66 -0
  210. package/dist/forge-payload/init/generation/generate-template.md +60 -0
  211. package/dist/forge-payload/init/generation/generate-tools.md +133 -0
  212. package/dist/forge-payload/init/generation/generate-workflows.md +78 -0
  213. package/dist/forge-payload/init/phases/phase-1-collect.md +10 -2
  214. package/dist/forge-payload/init/phases/phase-3-materialize.md +5 -1
  215. package/dist/forge-payload/init/phases/phase-4-register.md +8 -0
  216. package/dist/forge-payload/init/workflow-gen-plan.json +17 -0
  217. package/dist/forge-payload/integrity.json +33 -18
  218. package/dist/forge-payload/meta/workflows/meta-collate.md +5 -6
  219. package/dist/forge-payload/meta/workflows/meta-migrate.md +1 -1
  220. package/dist/forge-payload/payload-manifest.json +314 -0
  221. package/dist/forge-payload/schemas/enum-catalog.json +2 -2
  222. package/dist/forge-payload/schemas/payload-manifest.schema.json +100 -0
  223. package/dist/forge-payload/schemas/structure-manifest.json +5 -12
  224. package/dist/forge-payload/tools/forge-preflight.cjs +268 -0
  225. package/dist/forge-payload/tools/lib/paths.cjs +12 -11
  226. package/dist/forge-payload/tools/lib/pricing.cjs +31 -11
  227. package/dist/forge-payload/tools/query-logger.cjs +34 -0
  228. package/dist/forge-payload/tools/reset-plan.cjs +210 -0
  229. package/dist/forge-payload/tools/store.cjs +4 -1
  230. package/dist/forge-payload/tools/substitute-placeholders.cjs +14 -7
  231. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  232. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +8 -0
  233. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  234. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts +1 -1
  235. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
  236. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +1 -1
  237. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
  238. package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.d.ts +4 -0
  239. package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.d.ts.map +1 -0
  240. package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.js +3 -0
  241. package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.js.map +1 -0
  242. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.d.ts +20 -0
  243. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.d.ts.map +1 -0
  244. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.js +92 -0
  245. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.js.map +1 -0
  246. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.d.ts +18 -0
  247. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.d.ts.map +1 -0
  248. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.js +42 -0
  249. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.js.map +1 -0
  250. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.d.ts +10 -0
  251. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.d.ts.map +1 -0
  252. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.js +31 -0
  253. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.js.map +1 -0
  254. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.d.ts +30 -0
  255. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.d.ts.map +1 -0
  256. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.js +170 -0
  257. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.js.map +1 -0
  258. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.d.ts +26 -0
  259. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.d.ts.map +1 -0
  260. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.js +90 -0
  261. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.js.map +1 -0
  262. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +6 -1
  263. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
  264. package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
  265. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  266. package/node_modules/@earendil-works/pi-ai/README.md +12 -4
  267. package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.d.ts.map +1 -1
  268. package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js +3 -0
  269. package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js.map +1 -1
  270. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts +45 -0
  271. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  272. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +45 -0
  273. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
  274. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +1804 -815
  275. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  276. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +2031 -1384
  277. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
  278. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
  279. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +71 -27
  280. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  281. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts +1 -1
  282. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  283. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +24 -16
  284. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
  285. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  286. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +1 -0
  287. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  288. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  289. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +3 -1
  290. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  291. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  292. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +35 -13
  293. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
  294. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
  295. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js +2 -1
  296. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  297. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  298. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +1 -0
  299. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js.map +1 -1
  300. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +12 -4
  301. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  302. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  303. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  304. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +13 -1
  305. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  306. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.d.ts.map +1 -1
  307. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +4 -2
  308. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js.map +1 -1
  309. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -1
  310. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  311. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +3 -2
  312. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  313. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  314. package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +143 -0
  315. package/node_modules/@earendil-works/pi-coding-agent/README.md +26 -4
  316. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.d.ts +1 -0
  317. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  318. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js +11 -0
  319. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js.map +1 -1
  320. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/project-trust.d.ts +10 -0
  321. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/project-trust.d.ts.map +1 -0
  322. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/project-trust.js +48 -0
  323. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/project-trust.js.map +1 -0
  324. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/startup-ui.d.ts +17 -0
  325. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/startup-ui.d.ts.map +1 -0
  326. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/startup-ui.js +128 -0
  327. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/startup-ui.js.map +1 -0
  328. package/node_modules/@earendil-works/pi-coding-agent/dist/config.d.ts.map +1 -1
  329. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +9 -1
  330. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js.map +1 -1
  331. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-runtime.d.ts +3 -1
  332. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-runtime.d.ts.map +1 -1
  333. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-runtime.js +4 -1
  334. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-runtime.js.map +1 -1
  335. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.d.ts +2 -1
  336. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.d.ts.map +1 -1
  337. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.js +2 -2
  338. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.js.map +1 -1
  339. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +4 -1
  340. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  341. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +16 -3
  342. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  343. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  344. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js +4 -3
  345. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  346. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts +3 -1
  347. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  348. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/branch-summarization.js +9 -3
  349. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
  350. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/utils.d.ts +1 -1
  351. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
  352. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/utils.js +1 -1
  353. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  354. package/node_modules/@earendil-works/pi-coding-agent/dist/core/experimental.d.ts +2 -0
  355. package/node_modules/@earendil-works/pi-coding-agent/dist/core/experimental.d.ts.map +1 -0
  356. package/node_modules/@earendil-works/pi-coding-agent/dist/core/experimental.js +4 -0
  357. package/node_modules/@earendil-works/pi-coding-agent/dist/core/experimental.js.map +1 -0
  358. package/node_modules/@earendil-works/pi-coding-agent/dist/core/export-html/template.js +19 -6
  359. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  360. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  361. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  362. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.d.ts +1 -1
  363. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  364. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js +4 -4
  365. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  366. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts +10 -3
  367. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  368. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js +47 -1
  369. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  370. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts +28 -2
  371. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  372. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  373. package/node_modules/@earendil-works/pi-coding-agent/dist/core/footer-data-provider.d.ts +2 -0
  374. package/node_modules/@earendil-works/pi-coding-agent/dist/core/footer-data-provider.d.ts.map +1 -1
  375. package/node_modules/@earendil-works/pi-coding-agent/dist/core/footer-data-provider.js +29 -1
  376. package/node_modules/@earendil-works/pi-coding-agent/dist/core/footer-data-provider.js.map +1 -1
  377. package/node_modules/@earendil-works/pi-coding-agent/dist/core/index.d.ts +1 -0
  378. package/node_modules/@earendil-works/pi-coding-agent/dist/core/index.d.ts.map +1 -1
  379. package/node_modules/@earendil-works/pi-coding-agent/dist/core/index.js +1 -0
  380. package/node_modules/@earendil-works/pi-coding-agent/dist/core/index.js.map +1 -1
  381. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  382. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +1 -0
  383. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  384. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.d.ts +1 -0
  385. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  386. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +44 -5
  387. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  388. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts +3 -0
  389. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  390. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +47 -13
  391. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  392. package/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.d.ts +15 -0
  393. package/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.d.ts.map +1 -0
  394. package/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js +58 -0
  395. package/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js.map +1 -0
  396. package/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.d.ts +2 -1
  397. package/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.d.ts.map +1 -1
  398. package/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js +24 -26
  399. package/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js.map +1 -1
  400. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.d.ts +4 -0
  401. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.d.ts.map +1 -0
  402. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.js +72 -0
  403. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.js.map +1 -0
  404. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-display-names.d.ts.map +1 -1
  405. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-display-names.js +3 -0
  406. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-display-names.js.map +1 -1
  407. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.d.ts +13 -2
  408. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  409. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +112 -37
  410. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  411. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  412. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js +7 -33
  413. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js.map +1 -1
  414. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  415. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +103 -70
  416. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  417. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts +20 -2
  418. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  419. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +97 -30
  420. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  421. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  422. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js +1 -0
  423. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  424. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  425. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js +1 -1
  426. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  427. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  428. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js +1 -1
  429. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  430. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -1
  431. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js +1 -1
  432. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
  433. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -1
  434. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js +1 -1
  435. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js.map +1 -1
  436. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  437. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +1 -1
  438. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  439. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  440. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js +1 -1
  441. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  442. package/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.d.ts +36 -0
  443. package/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.d.ts.map +1 -0
  444. package/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js +202 -0
  445. package/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js.map +1 -0
  446. package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts +5 -4
  447. package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts.map +1 -1
  448. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +2 -1
  449. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js.map +1 -1
  450. package/node_modules/@earendil-works/pi-coding-agent/dist/main.d.ts.map +1 -1
  451. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +72 -32
  452. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js.map +1 -1
  453. package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.d.ts.map +1 -1
  454. package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js +39 -34
  455. package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js.map +1 -1
  456. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/index.d.ts +1 -1
  457. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
  458. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/index.js.map +1 -1
  459. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  460. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +2 -2
  461. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
  462. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
  463. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -0
  464. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/first-time-setup.js +103 -0
  465. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/first-time-setup.js.map +1 -0
  466. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  467. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js +7 -0
  468. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  469. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/index.d.ts +2 -0
  470. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -1
  471. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/index.js +2 -0
  472. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -1
  473. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  474. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  475. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -13
  476. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  477. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  478. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  479. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +20 -0
  480. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  481. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  482. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +22 -0
  483. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  484. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/trust-selector.d.ts +23 -0
  485. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/trust-selector.d.ts.map +1 -0
  486. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/trust-selector.js +91 -0
  487. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/trust-selector.js.map +1 -0
  488. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +7 -0
  489. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  490. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +101 -5
  491. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  492. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
  493. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/print-mode.js +1 -0
  494. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
  495. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  496. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -0
  497. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  498. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.d.ts +6 -2
  499. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.d.ts.map +1 -1
  500. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +111 -10
  501. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js.map +1 -1
  502. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.d.ts +1 -0
  503. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.d.ts.map +1 -1
  504. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js +78 -0
  505. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js.map +1 -1
  506. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/git.d.ts.map +1 -1
  507. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/git.js +54 -22
  508. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/git.js.map +1 -1
  509. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/open-browser.d.ts +9 -0
  510. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/open-browser.d.ts.map +1 -0
  511. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/open-browser.js +22 -0
  512. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/open-browser.js.map +1 -0
  513. package/node_modules/@earendil-works/pi-coding-agent/docs/containerization.md +111 -0
  514. package/node_modules/@earendil-works/pi-coding-agent/docs/docs.json +8 -0
  515. package/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md +67 -13
  516. package/node_modules/@earendil-works/pi-coding-agent/docs/index.md +2 -0
  517. package/node_modules/@earendil-works/pi-coding-agent/docs/models.md +4 -3
  518. package/node_modules/@earendil-works/pi-coding-agent/docs/packages.md +1 -1
  519. package/node_modules/@earendil-works/pi-coding-agent/docs/prompt-templates.md +9 -2
  520. package/node_modules/@earendil-works/pi-coding-agent/docs/providers.md +5 -0
  521. package/node_modules/@earendil-works/pi-coding-agent/docs/rpc.md +1 -1
  522. package/node_modules/@earendil-works/pi-coding-agent/docs/sdk.md +5 -0
  523. package/node_modules/@earendil-works/pi-coding-agent/docs/security.md +59 -0
  524. package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +15 -0
  525. package/node_modules/@earendil-works/pi-coding-agent/docs/skills.md +1 -1
  526. package/node_modules/@earendil-works/pi-coding-agent/docs/terminal-setup.md +36 -2
  527. package/node_modules/@earendil-works/pi-coding-agent/docs/themes.md +1 -1
  528. package/node_modules/@earendil-works/pi-coding-agent/docs/tmux.md +4 -2
  529. package/node_modules/@earendil-works/pi-coding-agent/docs/tui.md +10 -1
  530. package/node_modules/@earendil-works/pi-coding-agent/docs/usage.md +19 -2
  531. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/README.md +2 -0
  532. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-header.ts +1 -1
  533. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
  534. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  535. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/doom-overlay/index.ts +1 -1
  536. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/gondolin/index.ts +531 -0
  537. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/gondolin/package-lock.json +185 -0
  538. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/gondolin/package.json +19 -0
  539. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/handoff.ts +1 -1
  540. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/interactive-shell.ts +1 -1
  541. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/overlay-qa-tests.ts +152 -81
  542. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/project-trust.ts +64 -0
  543. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/qna.ts +1 -1
  544. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/question.ts +1 -1
  545. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/questionnaire.ts +1 -1
  546. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
  547. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/snake.ts +1 -1
  548. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/space-invaders.ts +1 -1
  549. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/summarize.ts +1 -1
  550. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/tic-tac-toe.ts +1 -1
  551. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/todo.ts +1 -1
  552. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/tools.ts +5 -0
  553. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
  554. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -419
  555. package/node_modules/@earendil-works/pi-coding-agent/package.json +5 -8
  556. package/node_modules/@earendil-works/pi-tui/README.md +13 -1
  557. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts +2 -0
  558. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +1 -1
  559. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +1 -1
  560. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +6 -1
  561. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  562. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +102 -43
  563. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  564. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts +2 -1
  565. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +1 -1
  566. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +11 -1
  567. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +1 -1
  568. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts +1 -1
  569. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts.map +1 -1
  570. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js +2 -2
  571. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js.map +1 -1
  572. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +1 -1
  573. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
  574. package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
  575. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +4 -7
  576. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  577. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +38 -77
  578. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  579. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +20 -4
  580. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
  581. package/node_modules/@earendil-works/pi-tui/dist/tui.js +244 -42
  582. package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
  583. package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts +1 -0
  584. package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts.map +1 -1
  585. package/node_modules/@earendil-works/pi-tui/dist/utils.js +46 -15
  586. package/node_modules/@earendil-works/pi-tui/dist/utils.js.map +1 -1
  587. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  588. package/package.json +8 -7
  589. package/dist/forge-payload/.base-pack/commands/check-agent.md +0 -22
  590. package/dist/forge-payload/.base-pack/commands/enhance.md +0 -37
@@ -1,1491 +1,41 @@
1
1
  // run-task.ts — /forge:run-task native Orchestrator handler (FORGE-S21-T02).
2
2
  //
3
- // Promotes /forge:run-task from stub to a full TS-driven Orchestrator-archetype
4
- // native handler. Reads `.forge/workflows/orchestrate_task.md`, chains phases
5
- // (plan review-plan implement → review-code validate approve
6
- // writeback commit) by spawning a fresh runForgeSubagent per phase (IL10).
3
+ // BARREL (FORGE-S31 file-size refactor). The implementation was decomposed into
4
+ // themed modules under ./task/ and ./common/ to satisfy the per-file
5
+ // architectural line cap. This file re-exports the full public surface so
6
+ // existing importers (run-sprint.ts, fix-bug.ts, index.ts, bin/config.ts, the
7
+ // test suite, calibrate.ts, wf-engine/engine.ts) keep resolving unchanged.
7
8
  //
8
- // Iron Laws enforced here:
9
+ // Iron Laws (enforced in the extracted modules):
9
10
  // IL1 — code only under forge-cli/src/extensions/forgecli/
10
11
  // IL6 — no shell-string interpolation; all external calls via spawnSync argv arrays
11
12
  // IL7 — every failure path emits ctx.ui.notify and returns; no silent continuation
12
13
  // IL10 — ALL LLM dispatch goes through runForgeSubagent (NO sendKickoff calls here)
13
14
  //
14
- // sendKickoff is NEVER called from this file.
15
- // Audit-grep: grep -n "sendKickoff(" run-task.ts must return empty.
16
- import { spawnSync } from "node:child_process";
17
- import * as fs from "node:fs";
18
- import * as path from "node:path";
19
- import { fileURLToPath } from "node:url";
20
- import { assertAudience, CallerContextStore } from "../audience-gate.js";
21
- import { loadLayeredConfig } from "../config/config-layer.js";
22
- import { buildGovernorFactory } from "../context-governor.js";
23
- import { buildForgeCompactionFactory } from "../context-governor-compaction.js";
24
- import { loadForgePersona, runForgeSubagent } from "../forge-subagent.js";
25
- import { getSubagentTools } from "../forge-tools.js";
26
- import { readPersonaDir, readPipelineNames } from "../lib/catalog-helpers.js";
27
- import { discoverForgeConfigCached } from "../lib/forge-config.js";
28
- import { resolveAdvisorModel, runHaltAdvisor } from "./halt-advisor.js";
29
- import { checkMaterialization } from "../lib/manifest-checker.js";
30
- import { runOrchestratorPreflight } from "./orchestrator-preflight.js";
31
- import { isStateStale as isJsonStateStale, readJsonState, taskStateFilePath, writeJsonState, } from "../lib/state-helpers.js";
32
- import { resolveModelForPhase } from "../config/model-resolver.js";
33
- import { loadWorkflow } from "../parsers/workflow-loader.js";
34
- import { getSessionRegistry } from "../session-registry.js";
35
- import { getOrchestratorTree } from "../orchestrator-tree.js";
36
- import { OrchestratorTranscriptWriter } from "../subagent/orchestrator-transcript.js";
37
- import { archiveRun, sweepProjectTranscripts } from "../transcript-archive.js";
38
- import { resolveToCanonicalId, resolveToolDir } from "../store/store-resolver.js";
39
- import { attachViewportObserver } from "../viewport/events.js";
40
- import { fmtPhaseSummary } from "../viewport/renderer.js";
41
- // ── Non-interactive helpers ───────────────────────────────────────────────
42
- export function isNonInteractive() {
43
- return process.env.FORGE_YES === "1" || process.env.FORGE_NON_INTERACTIVE === "1";
44
- }
45
- export const PHASES = [
46
- { role: "plan", workflowFile: "plan_task", personaNoun: "engineer", isReview: false, maxIterations: 1 },
47
- { role: "review-plan", workflowFile: "review_plan", personaNoun: "supervisor", isReview: true, maxIterations: 3 },
48
- { role: "implement", workflowFile: "implement_plan", personaNoun: "engineer", isReview: false, maxIterations: 1 },
49
- { role: "review-code", workflowFile: "review_code", personaNoun: "supervisor", isReview: true, maxIterations: 3 },
50
- { role: "validate", workflowFile: "validate_task", personaNoun: "qa-engineer", isReview: true, maxIterations: 3 },
51
- { role: "approve", workflowFile: "architect_approve", personaNoun: "architect", isReview: true, maxIterations: 3 },
52
- { role: "writeback", workflowFile: "collator_agent", personaNoun: "collator", isReview: false, maxIterations: 1 },
53
- { role: "commit", workflowFile: "commit_task", personaNoun: "engineer", isReview: false, maxIterations: 1 },
54
- ];
55
- // Map phase.role → canonical summary key written by base-pack workflows
56
- // (see forge/forge/tools/store-cli.cjs VALID_SUMMARY_PHASES). Phases whose
57
- // workflows do not write a summaries entry (e.g. approve, which transitions
58
- // task.status=approved instead) map to null and are verdict-checked via
59
- // task status rather than the summaries map.
60
- export const SUMMARY_KEY_BY_ROLE = {
61
- plan: "plan",
62
- "review-plan": "review_plan",
63
- implement: "implementation",
64
- "review-code": "code_review",
65
- validate: "validation",
66
- approve: null,
67
- };
68
- /** Validate that an ID contains no path-traversal characters. */
69
- export function validateId(id) {
70
- return /^[A-Za-z0-9_-]+$/.test(id) && !id.includes("..");
71
- }
72
- // FORGE-S25-T16 (N-H-B): state helpers delegate to lib/state-helpers.ts.
73
- // Public API (readState, writeState, deleteState, isStateStale) is preserved —
74
- // run-sprint.ts imports readState as readTaskState from this file.
75
- function stateFilePath(cwd, taskId) {
76
- if (!validateId(taskId)) {
77
- throw new Error(`Invalid taskId for state file path: ${taskId}`);
78
- }
79
- return taskStateFilePath(cwd, taskId);
80
- }
81
- export function readState(cwd, taskId) {
82
- return readJsonState(stateFilePath(cwd, taskId));
83
- }
84
- export function writeState(cwd, state) {
85
- writeJsonState(stateFilePath(cwd, state.taskId), state);
86
- }
87
- export function deleteState(cwd, taskId) {
88
- const fp = stateFilePath(cwd, taskId);
89
- try {
90
- if (fs.existsSync(fp))
91
- fs.unlinkSync(fp);
92
- }
93
- catch {
94
- // non-fatal
95
- }
96
- }
97
- export function isStateStale(state) {
98
- return isJsonStateStale(state.savedAt);
99
- }
100
- /**
101
- * Format an ISO timestamp for human display in the user's local timezone.
102
- * Falls back to the raw ISO string if parsing fails.
103
- */
104
- export function formatLocalTime(iso) {
105
- const d = new Date(iso);
106
- if (Number.isNaN(d.getTime()))
107
- return iso;
108
- const date = d.toLocaleString(undefined, {
109
- year: "numeric",
110
- month: "short",
111
- day: "2-digit",
112
- hour: "2-digit",
113
- minute: "2-digit",
114
- second: "2-digit",
115
- hour12: false,
116
- });
117
- // Append short timezone abbreviation for unambiguous reading.
118
- const tz = new Intl.DateTimeFormat(undefined, { timeZoneName: "short" })
119
- .formatToParts(d)
120
- .find((p) => p.type === "timeZoneName")?.value ?? "";
121
- return tz ? `${date} ${tz}` : date;
122
- }
123
- export function readVerdict(taskId, phaseRole, storeCli, cwd) {
124
- const result = spawnSync("node", [storeCli, "read", "task", taskId], { cwd, encoding: "utf8" });
125
- if (result.status !== 0)
126
- return "missing";
127
- try {
128
- const raw = typeof result.stdout === "string" ? result.stdout : String(result.stdout);
129
- const record = JSON.parse(raw);
130
- // Phases like `approve` do not write a summaries entry; they
131
- // transition task.status to "approved" instead. For those, the
132
- // verdict source is task.status.
133
- const summaryKey = SUMMARY_KEY_BY_ROLE[phaseRole];
134
- if (summaryKey === null) {
135
- return record.status === "approved" ? "approved" : "missing";
136
- }
137
- // Verdict lookup with three fallbacks:
138
- // 1. Canonical mapped summary key (e.g. "code_review" for review-code).
139
- // 2. Underscore-swapped phase role ("review_code") — legacy/defensive.
140
- // 3. Raw hyphenated phase role ("review-code") — defensive only.
141
- const summaries = record.summaries ?? {};
142
- const underscoreKey = phaseRole.replace(/-/g, "_");
143
- const candidates = [summaryKey ?? "", underscoreKey, phaseRole].filter(Boolean);
144
- let verdict;
145
- for (const k of candidates) {
146
- if (summaries[k]?.verdict) {
147
- verdict = summaries[k].verdict;
148
- break;
149
- }
150
- }
151
- if (!verdict)
152
- return "missing";
153
- if (verdict === "approved")
154
- return "approved";
155
- if (verdict === "revision")
156
- return "revision";
157
- return "missing";
158
- }
159
- catch {
160
- return "missing";
161
- }
162
- }
163
- export function readTaskRecord(taskId, storeCli, cwd) {
164
- const result = spawnSync("node", [storeCli, "read", "task", taskId], { cwd, encoding: "utf8" });
165
- if (result.status !== 0)
166
- return null;
167
- try {
168
- const raw = typeof result.stdout === "string" ? result.stdout : String(result.stdout);
169
- return JSON.parse(raw);
170
- }
171
- catch {
172
- return null;
173
- }
174
- }
175
- // Map phase.role → action token used in event.action / eventId.
176
- export function actionForRole(role) {
177
- return role.replace(/-/g, "_");
178
- }
179
- export function isoCompact(ms) {
180
- return new Date(ms)
181
- .toISOString()
182
- .replace(/[-:]/g, "")
183
- .replace(/\.\d{3}Z$/, "Z");
184
- }
185
- export function buildPhaseEvent(ec) {
186
- const action = actionForRole(ec.phase.role);
187
- const entityId = ec.entityType === "bug" ? ec.bugId : ec.taskId;
188
- const eventId = `${isoCompact(ec.startMs)}_${entityId}_${ec.phase.personaNoun}_${action}`;
189
- const durationMs = Math.max(0, ec.endMs - ec.startMs);
190
- const event = {
191
- eventId,
192
- sprintId: ec.sprintId,
193
- role: ec.phase.role,
194
- action: `/forge:${action.replace(/_/g, "-")}`,
195
- phase: ec.phase.role,
196
- iteration: ec.iteration,
197
- startTimestamp: new Date(ec.startMs).toISOString(),
198
- endTimestamp: new Date(ec.endMs).toISOString(),
199
- durationMinutes: Math.round((durationMs / 60000) * 100) / 100,
200
- model: ec.model,
201
- provider: ec.provider,
202
- };
203
- if (ec.entityType === "bug") {
204
- event.bugId = ec.bugId;
205
- }
206
- else {
207
- event.taskId = ec.taskId;
208
- }
209
- if (ec.usage.input > 0 || ec.usage.output > 0 || ec.usage.cacheRead > 0 || ec.usage.cacheWrite > 0) {
210
- event.inputTokens = ec.usage.input;
211
- event.outputTokens = ec.usage.output;
212
- event.cacheReadTokens = ec.usage.cacheRead;
213
- event.cacheWriteTokens = ec.usage.cacheWrite;
214
- event.tokenSource = "reported";
215
- }
216
- if (ec.judgement && typeof ec.judgement === "object") {
217
- const j = ec.judgement;
218
- if (typeof j.verdict === "string")
219
- event.verdict = j.verdict;
220
- if (typeof j.notes === "string")
221
- event.notes = j.notes;
222
- }
223
- return event;
224
- }
225
- export function emitEvent(storeCli, cwd, sprintId, event) {
226
- const result = spawnSync("node", [storeCli, "emit", sprintId, JSON.stringify(event)], {
227
- cwd,
228
- encoding: "utf8",
229
- });
230
- return { ok: result.status === 0, stderr: typeof result.stderr === "string" ? result.stderr : "" };
231
- }
232
- /**
233
- * Emit a phase event for an INCOMPLETE attempt (cancelled / failed) so its
234
- * provider-billed tokens reach the store. Bug B: the cancel and halt-on-failure
235
- * branches used to return without emitting, so collate's COST_REPORT
236
- * under-counted real spend by exactly the aborted passes (CART-S02-T03
237
- * baseline: 259,950 tokens across two aborted plan attempts, invisible).
238
- *
239
- * The event is the canonical phase event (schema-unchanged) with
240
- * `verdict: "aborted" | "failed"` marking the outcome.
241
- *
242
- * Zero-token attempts are skipped — there is no spend to account, and a
243
- * token-less event would be flagged as a husk by collate's ingestion-quality
244
- * pass. Never throws: emission must not perturb the cancel/halt return paths.
245
- *
246
- * @param opts.decorate Optional event mutation hook applied before emit
247
- * (fix-bug uses it for the BUG_TYPE_TOKENS `type` field).
248
- * @returns true when the event was emitted and store-cli accepted it.
249
- */
250
- export function emitIncompletePhaseEvent(opts) {
251
- try {
252
- const { emitCtx, outcome } = opts;
253
- const u = emitCtx.usage;
254
- if (u.input + u.output + u.cacheRead + u.cacheWrite <= 0) {
255
- opts.onDebug?.({ kind: "incomplete_emit_skipped", reason: "no-tokens", outcome });
256
- return false;
257
- }
258
- const judgement = { verdict: outcome };
259
- if (opts.notes)
260
- judgement.notes = opts.notes;
261
- const event = buildPhaseEvent({ ...emitCtx, judgement });
262
- opts.decorate?.(event);
263
- const res = emitEvent(emitCtx.storeCli, emitCtx.cwd, emitCtx.sprintId, event);
264
- opts.onDebug?.(res.ok
265
- ? { kind: "incomplete_emit_ok", eventId: event.eventId, outcome }
266
- : { kind: "incomplete_emit_failed", stderr: res.stderr, outcome });
267
- return res.ok;
268
- }
269
- catch (err) {
270
- const msg = err instanceof Error ? err.message : String(err);
271
- opts.onDebug?.({ kind: "incomplete_emit_failed", stderr: msg, outcome: opts.outcome });
272
- return false;
273
- }
274
- }
275
- export function judgementFromSummary(record, phaseRole, summaryKeyByRole) {
276
- if (!record || !record.summaries)
277
- return undefined;
278
- const keyMap = summaryKeyByRole ?? SUMMARY_KEY_BY_ROLE;
279
- const summaryKey = keyMap[phaseRole];
280
- if (!summaryKey)
281
- return undefined;
282
- const blob = record.summaries[summaryKey];
283
- return blob && typeof blob === "object" ? blob : undefined;
284
- }
285
- // Drain .forge/cache/FRICTION-{phase}.jsonl: stamp each judgement-only record
286
- // with the subagent's runtime attribution and emit as event type "friction".
287
- // Truncate only after all emits succeed (Plan-11 open-question A.3).
288
- export function drainFrictionFile(frictionPath, ec) {
289
- if (!fs.existsSync(frictionPath))
290
- return { emitted: 0, failed: 0 };
291
- const raw = fs.readFileSync(frictionPath, "utf8");
292
- const lines = raw.split("\n").filter((l) => l.trim().length > 0);
293
- if (lines.length === 0)
294
- return { emitted: 0, failed: 0 };
295
- let emitted = 0;
296
- let failed = 0;
297
- for (let i = 0; i < lines.length; i++) {
298
- let judgement;
299
- try {
300
- judgement = JSON.parse(lines[i]);
301
- }
302
- catch {
303
- failed++;
304
- continue;
305
- }
306
- const action = actionForRole(ec.phase.role);
307
- const entityId = ec.entityType === "bug" ? ec.bugId : ec.taskId;
308
- const eventId = `${isoCompact(ec.startMs)}_${entityId}_${ec.phase.personaNoun}_friction_${i}`;
309
- const event = {
310
- eventId,
311
- sprintId: ec.sprintId,
312
- role: ec.phase.role,
313
- action: `/forge:${action.replace(/_/g, "-")}`,
314
- phase: ec.phase.role,
315
- iteration: ec.iteration,
316
- startTimestamp: new Date(ec.startMs).toISOString(),
317
- endTimestamp: new Date(ec.endMs).toISOString(),
318
- durationMinutes: Math.round(((ec.endMs - ec.startMs) / 60000) * 100) / 100,
319
- model: ec.model,
320
- provider: ec.provider,
321
- type: "friction",
322
- workflow: typeof judgement.workflow === "string" ? judgement.workflow : ec.phase.role,
323
- persona: typeof judgement.persona === "string" ? judgement.persona : ec.phase.personaNoun,
324
- issue: judgement.issue,
325
- };
326
- if (ec.entityType === "bug") {
327
- event.bugId = ec.bugId;
328
- }
329
- else {
330
- event.taskId = ec.taskId;
331
- }
332
- if (judgement.subkind !== undefined)
333
- event.subkind = judgement.subkind;
334
- if (judgement.evidence !== undefined)
335
- event.evidence = judgement.evidence;
336
- if (judgement.notes !== undefined)
337
- event.notes = judgement.notes;
338
- const r = emitEvent(ec.storeCli, ec.cwd, ec.sprintId, event);
339
- if (r.ok)
340
- emitted++;
341
- else
342
- failed++;
343
- }
344
- if (failed === 0) {
345
- try {
346
- fs.unlinkSync(frictionPath);
347
- }
348
- catch {
349
- /* non-fatal */
350
- }
351
- }
352
- return { emitted, failed };
353
- }
354
- // ── Find predecessor non-review phase for revision loop ───────────────────
355
- export function findPredecessorIndex(phases, reviewIndex) {
356
- for (let i = reviewIndex - 1; i >= 0; i--) {
357
- if (!phases[i].isReview)
358
- return i;
359
- }
360
- return 0;
361
- }
362
- // Phase ordering for summary injection — earlier phases first.
363
- const PHASE_ORDER = ["plan", "review_plan", "implementation", "code_review", "validation"];
364
- export function buildSummariesBlock(summaries) {
365
- if (!summaries)
366
- return "";
367
- const lines = [];
368
- for (const key of PHASE_ORDER) {
369
- const raw = summaries[key];
370
- if (!raw || typeof raw !== "object")
371
- continue;
372
- const s = raw;
373
- const parts = [`### ${key}`];
374
- if (s.objective)
375
- parts.push(`Objective: ${s.objective}`);
376
- if (s.verdict)
377
- parts.push(`Verdict: ${s.verdict}`);
378
- if (s.key_changes?.length)
379
- parts.push(`Key changes: ${s.key_changes.join("; ")}`);
380
- if (s.findings?.length)
381
- parts.push(`Findings: ${s.findings.join("; ")}`);
382
- if (s.artifact_ref)
383
- parts.push(`Full artifact: ${s.artifact_ref}`);
384
- lines.push(parts.join("\n"));
385
- }
386
- if (lines.length === 0)
387
- return "";
388
- return ["## Prior phase summaries (carry-forward)", "", ...lines].join("\n");
389
- }
390
- export function composeTaskBody(subWorkflowMd, taskId, summariesBlock) {
391
- const parts = [`Read the workflow below and follow it. Task ID: ${taskId}.`, "", "---", ""];
392
- if (summariesBlock) {
393
- parts.push(summariesBlock, "", "---", "");
394
- }
395
- parts.push(subWorkflowMd.trim());
396
- return parts.join("\n");
397
- }
398
- export function runPreflightGate(preflightGate, role, taskId, cwd, entityType) {
399
- const outcome = runPreflightGateWithData(preflightGate, role, taskId, cwd, entityType);
400
- return outcome.result;
401
- }
402
- /**
403
- * Run postflight-gate.cjs after a phase subagent returns, before FSM advance.
404
- * Mirrors runPreflightGateWithData — same argv-array discipline, same structured-JSON
405
- * parsing from stdout on exit 1.
406
- *
407
- * Returns:
408
- * "ok" — gate passed (or no outputs block for this phase); advance may proceed.
409
- * "unsatisfied" — gate failed; do NOT advance FSM; halt and call runHaltAdvisor.
410
- * "error" — gate binary missing or parse error; treat as pass-through (additive).
411
- */
412
- export function runPostflightGate(postflightGate, role, taskId, cwd) {
413
- if (!fs.existsSync(postflightGate)) {
414
- // postflight-gate.cjs not present in this forgeRoot — pass through (additive).
415
- return { result: "ok", gateFailure: null };
416
- }
417
- const spawnResult = spawnSync("node", [postflightGate, "--phase", role, "--task", taskId], { cwd, encoding: "utf8" });
418
- if (spawnResult.status === 0)
419
- return { result: "ok", gateFailure: null };
420
- if (spawnResult.status === 2)
421
- return { result: "error", gateFailure: null };
422
- // Exit 1: parse structured JSON from stdout
423
- let gateFailure = null;
424
- try {
425
- const stdout = typeof spawnResult.stdout === "string" ? spawnResult.stdout.trim() : "";
426
- if (stdout) {
427
- const parsed = JSON.parse(stdout);
428
- if (parsed && typeof parsed.reasonCode === "string") {
429
- gateFailure = parsed;
430
- }
431
- }
432
- }
433
- catch {
434
- // stdout not valid JSON — gate failure but no structured data
435
- }
436
- return { result: "unsatisfied", gateFailure };
437
- }
438
- /**
439
- * Upgraded variant that returns structured failure data alongside the status enum.
440
- * Callers that need the advisory data should use this function directly.
441
- */
442
- export function runPreflightGateWithData(preflightGate, role, taskId, cwd, entityType) {
443
- const entityFlag = entityType === "bug" ? "--bug" : "--task";
444
- const spawnResult = spawnSync("node", [preflightGate, "--phase", role, entityFlag, taskId], { cwd, encoding: "utf8" });
445
- if (spawnResult.status === 0)
446
- return { result: "proceed", gateFailure: null };
447
- if (spawnResult.status === 2)
448
- return { result: "escalate", gateFailure: null };
449
- // Exit 1: parse structured JSON from stdout
450
- let gateFailure = null;
451
- try {
452
- const stdout = typeof spawnResult.stdout === "string" ? spawnResult.stdout.trim() : "";
453
- if (stdout) {
454
- const parsed = JSON.parse(stdout);
455
- if (parsed && typeof parsed.reasonCode === "string") {
456
- gateFailure = parsed;
457
- }
458
- }
459
- }
460
- catch {
461
- // stdout not valid JSON — gate failure but no structured data
462
- }
463
- return { result: "halt", gateFailure };
464
- }
465
- // ── Per-task orchestrator pipeline (FORGE-S21-T03 extracted) ──────────────
466
- // The entire phase loop was inline in registerRunTask. It is now a standalone
467
- // exported function so that run-sprint.ts can delegate per-task execution
468
- // without duplicating phase-loop logic. registerRunTask becomes a thin
469
- // wrapper: config discovery, resume detection, then delegate to runTaskPipeline.
470
- const STATUS_KEY = "forge:run-task";
471
- const MESSAGE_KEY = "forge:run-task:message";
472
- // extractTurnPreview moved to viewport/renderer.ts; re-exported below for
473
- // backwards-compatibility with existing imports (e.g. tests).
15
+ // sendKickoff is NEVER called from this file or its extracted modules.
16
+ //
17
+ // Orchestrator emit path (Plan 11 / Slice 2): the pipeline composes each phase
18
+ // event and emits it via `node <forgeRoot>/tools/store-cli.cjs "emit" <sprintId>
19
+ // <eventJson>` see emitEvent in ./task/task-events.js. The subagent never
20
+ // calls store-cli emit itself; the orchestrator owns the emit path here.
21
+ // ── Generic orchestrator helpers (common) ─────────────────────────────────
22
+ export { formatLocalTime, isNonInteractive, validateId } from "./common/orchestrator-misc.js";
23
+ // ── Phase descriptor table ─────────────────────────────────────────────────
24
+ export { PHASES, SUMMARY_KEY_BY_ROLE } from "./task/task-phases.js";
25
+ // ── State persistence ─────────────────────────────────────────────────────
26
+ export { deleteState, isStateStale, readState, writeState, } from "./task/task-state.js";
27
+ // ── Verdict + task-record reads ────────────────────────────────────────────
28
+ export { readTaskRecord, readVerdict } from "./task/task-record.js";
29
+ // ── Event composition + emission ───────────────────────────────────────────
30
+ export { actionForRole, buildPhaseEvent, drainFrictionFile, emitEvent, emitIncompletePhaseEvent, isoCompact, judgementFromSummary, } from "./task/task-events.js";
31
+ // ── Gates ──────────────────────────────────────────────────────────────────
32
+ export { runPostflightGate, runPreflightGate, runPreflightGateWithData, } from "./task/task-gates.js";
33
+ // ── Task body composition + predecessor finder ─────────────────────────────
34
+ export { buildSummariesBlock, composeTaskBody, findPredecessorIndex } from "./task/task-body.js";
35
+ // ── Pipeline + command handler ─────────────────────────────────────────────
36
+ export { runTaskPipeline } from "./task/run-task-pipeline.js";
37
+ export { registerRunTask } from "./task/run-task-command.js";
38
+ // extractTurnPreview moved to viewport/renderer.ts; re-exported here for
39
+ // backwards-compatibility with existing imports (e.g. tests, wf-engine/engine).
474
40
  export { extractTurnPreview } from "../viewport/renderer.js";
475
- // ── runTaskPipeline ──────────────────────────────────────────────────────
476
- /** Map a pipeline status to the orchestrator-transcript outcome vocabulary.
477
- * Shared with fix-bug.ts (RunBugPipelineStatus is the same string union). */
478
- export function transcriptOutcomeFor(status) {
479
- switch (status) {
480
- case "completed":
481
- return "complete";
482
- case "cancelled":
483
- return "cancelled";
484
- case "halted":
485
- case "escalated":
486
- return "halted";
487
- case "failed":
488
- return "error";
489
- }
490
- }
491
- export async function runTaskPipeline(opts) {
492
- // Thin wrapper: capture the orchestrator transcript writer the inner
493
- // pipeline creates, surface its path on the result (so callers can
494
- // archive the run), and GUARANTEE a pipeline-end on every outcome —
495
- // cancel/halt/failure paths that return without recording one left runs
496
- // archived as "incomplete" instead of their true outcome.
497
- let orchTranscript;
498
- const result = await runTaskPipelineInner(opts, (writer) => {
499
- orchTranscript = writer;
500
- });
501
- if (orchTranscript) {
502
- orchTranscript.close(transcriptOutcomeFor(result.status), result.lastError);
503
- result.orchestratorTranscriptPath = orchTranscript.filePath;
504
- }
505
- return result;
506
- }
507
- async function runTaskPipelineInner(opts, onOrchestratorTranscript) {
508
- const { taskId, cwd, ctx, forgeRoot, storeCli, preflightGate, registry, resumeFromState, onPhaseEvent } = opts;
509
- // Bridge: OrchestratorTree for the dashboard overlay.
510
- const tree = getOrchestratorTree();
511
- // Load per-phase model routing config once at task entry (Plan 16 Slice 2).
512
- // Empty / absent config produces inherit for every phase — no behaviour change.
513
- // N-B-E: surface schema errors to caller (Decision 9 — orchestrators fail-fast).
514
- // See doc/decisions/layered-config-error-policy.md.
515
- const { merged: modelRoutingConfig, errors: layeredConfigErrors } = loadLayeredConfig(cwd);
516
- if (layeredConfigErrors.length > 0) {
517
- for (const e of layeredConfigErrors) {
518
- ctx.ui.notify(`× forge:run-task — forge-cli config schema error: ${e}`, "error");
519
- }
520
- return {
521
- status: "failed",
522
- lastPhaseIndex: 0,
523
- iterationCounts: {},
524
- lastError: `forge-cli config schema errors: ${layeredConfigErrors.join("; ")}`,
525
- };
526
- }
527
- // Pre-flight model config validation (Plan 16 Slice 3).
528
- // Warns on unknown persona names / unavailable models; errors on unresolvable
529
- // overrides / unknown pipelines. With FORGE_STRICT_MODELS=1, warnings → errors.
530
- // FORGE-S25-T17: delegated to orchestrator-preflight.ts (H-13).
531
- {
532
- const personasDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "forge-payload", ".base-pack", "personas");
533
- const personaCatalogue = readPersonaDir(personasDir);
534
- const forgeCfgPath = path.join(cwd, ".forge", "config.json");
535
- const pipelineCatalogue = readPipelineNames(forgeCfgPath);
536
- const availableModels = ctx.modelRegistry?.getAvailable?.() ?? [];
537
- const preflightResult = runOrchestratorPreflight({
538
- mode: "task",
539
- ctx,
540
- notifyPrefix: "forge:run-task",
541
- personaCatalogue,
542
- pipelineCatalogue,
543
- modelRoutingConfig,
544
- availableModels: availableModels.map((m) => ({ provider: m.provider, id: m.id })),
545
- });
546
- if (!preflightResult.proceed) {
547
- return preflightResult.result;
548
- }
549
- }
550
- // Determine starting phase from resumeFromState (if provided) or phase 0.
551
- let currentPhaseIndex = resumeFromState?.phaseIndex ?? 0;
552
- const iterationCounts = resumeFromState?.iterationCounts ?? {};
553
- // Per-role dispatch counter for OrchestratorTree node identity. Distinct
554
- // from iterationCounts (which only tracks review-verdict revisions): every
555
- // dispatch of a role — including a plan re-run after a review loopback —
556
- // gets its own `<taskId>:<role>:<attempt>` node, so the dashboard renders
557
- // one leaf per run in dispatch order instead of merging attempts into one
558
- // node (CART-BUG-003 dashboard regression). Seeded from resume state so
559
- // resumed runs continue numbering past prior attempts.
560
- const dispatchCounts = { ...(resumeFromState?.iterationCounts ?? {}) };
561
- // Track model/provider from last successful subagent result (REVIEW FIX #1).
562
- let lastModel;
563
- let lastProvider;
564
- // Resolve the task's sprintId once for prompt-cache session affinity. All
565
- // phases in this task share the cache key `forge:${sprintId}` so the
566
- // system-prompt + project orientation prefix (which is identical across
567
- // personas) stays warm on Anthropic and shares a stable prompt_cache_key
568
- // on OpenAI. Falls back to a task-scoped key if the task is unattached.
569
- const taskRecordAtStart = readTaskRecord(taskId, storeCli, cwd);
570
- const cacheSessionId = taskRecordAtStart?.sprintId ? `forge:${taskRecordAtStart.sprintId}` : `forge:task:${taskId}`;
571
- // ── Orchestrator transcript ──────────────────────────────────────────
572
- // FORGE-BUG-040 follow-up: one JSONL file per pipeline run, ISO-prefixed
573
- // in its filename so review-loop iterations preserve their own logs
574
- // instead of overwriting. Captures every ctx.ui.notify line plus
575
- // structured phase-boundary events.
576
- const orchTranscript = new OrchestratorTranscriptWriter({
577
- cwd,
578
- entityKind: "task",
579
- entityId: taskId,
580
- });
581
- onOrchestratorTranscript(orchTranscript);
582
- const __origNotify = ctx.ui.notify.bind(ctx.ui);
583
- ctx.ui.notify = ((msg, level) => {
584
- __origNotify(msg, level);
585
- orchTranscript.record({
586
- kind: "notify",
587
- ts: new Date().toISOString(),
588
- level: (level ?? "info"),
589
- message: typeof msg === "string" ? msg : String(msg),
590
- });
591
- });
592
- const pipelineStartMs = Date.now();
593
- try {
594
- while (currentPhaseIndex < PHASES.length) {
595
- // ── Between-phase cancellation gate ────────────────────────────
596
- if (opts.signal?.aborted) {
597
- ctx.ui.notify(`⊘ forge:run-task — ${taskId} cancelled by user.`, "info");
598
- registry.completePhase(taskId, PHASES[currentPhaseIndex]?.role ?? "unknown", "cancelled");
599
- registry.confirmCancelled(taskId);
600
- // ADR-S21-01: preserve state file so cancelled runs are resumable
601
- // from the beginning of the cancelled phase (not deleted).
602
- writeState(cwd, {
603
- taskId,
604
- phaseIndex: currentPhaseIndex,
605
- iterationCounts,
606
- halted: false,
607
- status: "cancelled",
608
- lastError: undefined,
609
- savedAt: new Date().toISOString(),
610
- });
611
- return { status: "cancelled", lastPhaseIndex: currentPhaseIndex, iterationCounts };
612
- }
613
- const phase = PHASES[currentPhaseIndex];
614
- if (!phase) {
615
- ctx.ui.notify(`× forge:run-task — invalid phase index ${currentPhaseIndex}`, "error");
616
- return {
617
- status: "failed",
618
- lastPhaseIndex: currentPhaseIndex,
619
- iterationCounts,
620
- lastError: `invalid phase index ${currentPhaseIndex}`,
621
- };
622
- }
623
- ctx.ui.setStatus?.(STATUS_KEY, `run-task ${taskId}: phase ${currentPhaseIndex + 1}/${PHASES.length} (${phase.role})`);
624
- ctx.ui.notify(`→ ${taskId}: ${phase.role} (phase ${currentPhaseIndex + 1}/${PHASES.length})`, "info");
625
- orchTranscript.record({
626
- kind: "phase-start",
627
- ts: new Date().toISOString(),
628
- phase: phase.role,
629
- phaseIndex: currentPhaseIndex,
630
- phaseCount: PHASES.length,
631
- attempt: (iterationCounts[phase.role] ?? 0) + 1,
632
- workflowFile: phase.workflowFile,
633
- persona: phase.personaNoun,
634
- });
635
- const subWorkflowPath = path.join(cwd, ".forge", "workflows", `${phase.workflowFile}.md`);
636
- // ── Read sub-workflow ─────────────────────────────────────────
637
- let subWorkflowMd;
638
- let subWorkflowAudience = "any";
639
- try {
640
- const loaded = loadWorkflow(subWorkflowPath);
641
- subWorkflowMd = loaded.rawMarkdown;
642
- subWorkflowAudience = loaded.audience;
643
- }
644
- catch (err) {
645
- const e = err;
646
- ctx.ui.notify(`× forge:run-task — failed to read sub-workflow for ${phase.role}: ${e.message ?? "unknown"}`, "error");
647
- writeState(cwd, {
648
- taskId,
649
- phaseIndex: currentPhaseIndex,
650
- iterationCounts,
651
- halted: true,
652
- lastError: `sub-workflow read failed: ${e.message ?? "unknown"}`,
653
- savedAt: new Date().toISOString(),
654
- });
655
- return {
656
- status: "failed",
657
- lastPhaseIndex: currentPhaseIndex,
658
- iterationCounts,
659
- lastError: `sub-workflow read failed: ${e.message ?? "unknown"}`,
660
- };
661
- }
662
- // ── 6a. Preflight gate ────────────────────────────────────────
663
- if (fs.existsSync(preflightGate)) {
664
- const preflightOutcome = runPreflightGateWithData(preflightGate, phase.role, taskId, cwd);
665
- if (preflightOutcome.result === "halt") {
666
- // Render structured failure reason if available.
667
- if (preflightOutcome.gateFailure) {
668
- ctx.ui.notify(`× forge:run-task — preflight gate failed for phase ${phase.role} ` +
669
- `[${preflightOutcome.gateFailure.reasonCode}]: ${preflightOutcome.gateFailure.detail}`, "error");
670
- }
671
- else {
672
- ctx.ui.notify(`× forge:run-task — preflight gate failed for phase ${phase.role} (exit 1); halting.`, "error");
673
- }
674
- writeState(cwd, {
675
- taskId,
676
- phaseIndex: currentPhaseIndex,
677
- iterationCounts,
678
- halted: true,
679
- lastError: `preflight gate exit 1 for ${phase.role}`,
680
- savedAt: new Date().toISOString(),
681
- });
682
- // Spawn halt-recovery advisor (Tier 1, best-effort — non-fatal).
683
- if (preflightOutcome.gateFailure) {
684
- const advisorModel = resolveAdvisorModel(modelRoutingConfig, ctx.model);
685
- void runHaltAdvisor({
686
- gateFailure: preflightOutcome.gateFailure,
687
- advisorModel,
688
- taskId,
689
- cwd,
690
- ctx: { ui: ctx.ui },
691
- forgeRoot,
692
- });
693
- }
694
- return {
695
- status: "halted",
696
- lastPhaseIndex: currentPhaseIndex,
697
- iterationCounts,
698
- lastError: `preflight gate exit 1 for ${phase.role}`,
699
- };
700
- }
701
- if (preflightOutcome.result === "escalate") {
702
- ctx.ui.notify(`× forge:run-task — preflight gate escalated for phase ${phase.role} (exit 2); manual intervention required.`, "error");
703
- writeState(cwd, {
704
- taskId,
705
- phaseIndex: currentPhaseIndex,
706
- iterationCounts,
707
- halted: true,
708
- lastError: `preflight gate exit 2 (escalate) for ${phase.role}`,
709
- savedAt: new Date().toISOString(),
710
- });
711
- return {
712
- status: "escalated",
713
- lastPhaseIndex: currentPhaseIndex,
714
- iterationCounts,
715
- lastError: `preflight gate exit 2 (escalate) for ${phase.role}`,
716
- };
717
- }
718
- }
719
- // ── 6. Materialization-marker check ───────────────────────────
720
- const markerCheck = checkMaterialization(subWorkflowPath, subWorkflowMd);
721
- if (!markerCheck.ok) {
722
- for (const marker of markerCheck.missing) {
723
- ctx.ui.notify(`× workflow regression: ${marker} not found in ${subWorkflowPath}`, "error");
724
- }
725
- return {
726
- status: "failed",
727
- lastPhaseIndex: currentPhaseIndex,
728
- iterationCounts,
729
- lastError: `materialization markers missing: ${markerCheck.missing.join(", ")}`,
730
- };
731
- }
732
- // ── 5. Audience check ─────────────────────────────────────────
733
- // Wrap with CallerContextStore.asSubagent so assertAudience treats
734
- // this as a subagent context (IL10: we ARE dispatching from subagent chain).
735
- const audienceOk = CallerContextStore.asSubagent(phase.role, () => assertAudience({ workflowName: phase.workflowFile, audience: subWorkflowAudience }, ctx));
736
- if (!audienceOk) {
737
- writeState(cwd, {
738
- taskId,
739
- phaseIndex: currentPhaseIndex,
740
- iterationCounts,
741
- halted: true,
742
- lastError: `audience check failed for ${phase.workflowFile}`,
743
- savedAt: new Date().toISOString(),
744
- });
745
- return {
746
- status: "failed",
747
- lastPhaseIndex: currentPhaseIndex,
748
- iterationCounts,
749
- lastError: `audience check failed for ${phase.workflowFile}`,
750
- };
751
- }
752
- // ── Persona load ──────────────────────────────────────────────
753
- let persona;
754
- try {
755
- persona = loadForgePersona(phase.personaNoun, cwd);
756
- }
757
- catch (err) {
758
- const e = err;
759
- ctx.ui.notify(`× forge:run-task — persona '${phase.personaNoun}' not found for phase ${phase.role}: ${e.message ?? "unknown"}. ` +
760
- "Run /forge:regenerate to materialize persona files.", "error");
761
- writeState(cwd, {
762
- taskId,
763
- phaseIndex: currentPhaseIndex,
764
- iterationCounts,
765
- halted: true,
766
- lastError: `persona load failed: ${e.message ?? "unknown"}`,
767
- savedAt: new Date().toISOString(),
768
- });
769
- return {
770
- status: "failed",
771
- lastPhaseIndex: currentPhaseIndex,
772
- iterationCounts,
773
- lastError: `persona load failed: ${e.message ?? "unknown"}`,
774
- };
775
- }
776
- // ── 4. Dispatch via runForgeSubagent (IL10) ───────────────────
777
- // NEVER sendKickoff here — that would reproduce issue #30 (same-context inline = no fork).
778
- // Read fresh task record to carry forward prior phase summaries (forge-cli#19).
779
- const taskRecordForSummaries = currentPhaseIndex > 0 ? readTaskRecord(taskId, storeCli, cwd) : null;
780
- const summariesBlock = buildSummariesBlock(taskRecordForSummaries?.summaries);
781
- const taskBody = composeTaskBody(subWorkflowMd, taskId, summariesBlock || undefined);
782
- // Log whether carry-forward summaries were injected (forge-cli#19).
783
- if (summariesBlock) {
784
- const debugCarryPath = path.join(cwd, ".forge", "cache", `run-task-debug-${taskId}.jsonl`);
785
- try {
786
- fs.mkdirSync(path.dirname(debugCarryPath), { recursive: true });
787
- fs.appendFileSync(debugCarryPath, `${JSON.stringify({ ts: new Date().toISOString(), phase: phase.role, kind: "carry_forward_injected", summariesLength: summariesBlock.length, summariesBlock })}\n`, "utf8");
788
- }
789
- catch { /* best-effort debug log */ }
790
- }
791
- // Resolve per-phase model from layered config (Plan 16 Slice 2).
792
- // Pipeline name "default" matches the Forge plugin's shipped pipeline.
793
- // When config is absent or cascade bottoms out, resolves to inherit
794
- // (model: undefined) — setModel is skipped and pi's current model is used.
795
- const modelResolution = resolveModelForPhase("default", phase.role, phase.personaNoun, modelRoutingConfig);
796
- const phaseStart = Date.now();
797
- // Stabilization debug log — every subagent event appended as JSONL.
798
- const debugLogPath = path.join(cwd, ".forge", "cache", `run-task-debug-${taskId}.jsonl`);
799
- const writeDebug = (rec) => {
800
- try {
801
- fs.mkdirSync(path.dirname(debugLogPath), { recursive: true });
802
- fs.appendFileSync(debugLogPath, `${JSON.stringify({ ts: new Date().toISOString(), phase: phase.role, ...rec })}\n`, "utf8");
803
- }
804
- catch {
805
- // non-fatal; debug log is best-effort
806
- }
807
- };
808
- writeDebug({ kind: "phase_start", phaseIndex: currentPhaseIndex });
809
- writeDebug({
810
- kind: "requested_model",
811
- requested: modelResolution.model ?? null,
812
- source: modelResolution.source,
813
- persona: phase.personaNoun,
814
- });
815
- registry.startPhase(taskId, phase.role, currentPhaseIndex);
816
- // Bridge: register phase in OrchestratorTree. Node identity is
817
- // per-dispatch (see dispatchCounts above) — never reuse an ID for a
818
- // re-dispatched role.
819
- const iteration = (dispatchCounts[phase.role] = (dispatchCounts[phase.role] ?? 0) + 1);
820
- const phaseNodeId = `${taskId}:${phase.role}:${iteration}`;
821
- tree.startNode(phaseNodeId, {
822
- parentId: taskId,
823
- label: `${phase.role}:${iteration}`,
824
- kind: "leaf",
825
- // Full body — display clamping/expansion is the view's decision
826
- // (the tree applies only a storage cap).
827
- promptPreview: taskBody,
828
- });
829
- // Capture the first stream-observed model on turn_end (IL10 visibility).
830
- // If pi auto-substitutes or setModel silently no-ops, this line will diverge
831
- // from requested_model — exactly the diagnostic signal we want.
832
- let modelObservedLogged = false;
833
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
834
- const wrappedOnEvent = (event) => {
835
- if (!modelObservedLogged && event?.type === "turn_end" && typeof event?.message?.model === "string") {
836
- modelObservedLogged = true;
837
- writeDebug({
838
- kind: "model_observed",
839
- provider: event.message.provider ?? null,
840
- model: event.message.model,
841
- });
842
- }
843
- observer.onEvent(event);
844
- };
845
- const refreshStatus = () => {
846
- if (process.env.FORGE_VERBOSE !== "1")
847
- return;
848
- const elapsed = Math.floor((Date.now() - phaseStart) / 1000);
849
- const tail = observer.state.lastTool ? ` · ${observer.state.lastTool}` : "";
850
- ctx.ui.setStatus?.(STATUS_KEY, `run-task ${taskId}: ${phase.role} · t${observer.state.turn} · tools ${observer.state.toolCount}${observer.state.errCount ? ` · err ${observer.state.errCount}` : ""} · ${elapsed}s${tail}`);
851
- };
852
- const observer = attachViewportObserver({
853
- registry,
854
- sessionId: taskId,
855
- phaseRole: phase.role,
856
- nodeId: phaseNodeId,
857
- beginHeader: `─── phase ${currentPhaseIndex + 1}/${PHASES.length} ${phase.role} begin · ${taskId} ───`,
858
- writeDebug,
859
- notify: (msg, level) => ctx.ui.notify(msg, level),
860
- setStatusVerbose: process.env.FORGE_VERBOSE === "1" ? (k, v) => ctx.ui.setStatus?.(k, v) : undefined,
861
- verboseKeys: { messageKey: MESSAGE_KEY },
862
- afterEach: refreshStatus,
863
- });
864
- // ── Context governor injection (completes FORGE-S30-T07) ──────
865
- // Per-phase factories built HERE because only the pipeline knows the
866
- // `${personaNoun}/${role}` phase key — pi never sets persona/phase on
867
- // ExtensionContext, and the parent session's registerHookDispatcher
868
- // governor never sees subagent tool traffic (dormant-governor defect,
869
- // CART-S02-T03 benchmark). Flag-gated: FORGE_CTX_GOVERNOR=1.
870
- // buildGovernorFactory — Mechanisms A/B/C/D in the subagent
871
- // buildForgeCompactionFactory — Mechanism E with warm-tier path opts
872
- // (previously injected from index.ts with NO opts → warm-tier dead)
873
- const phaseKey = `${phase.personaNoun}/${phase.role}`;
874
- // Sprint ID from the task record's sprint FK (the store owns that
875
- // relationship); the taskId-shape regex is only a fallback for records
876
- // missing the FK (FORGE-BUG-043 PR 2).
877
- const sprintIdForSummaries = taskRecordAtStart?.sprintId ?? /^(.*)-T\d+$/.exec(taskId)?.[1];
878
- const governorFactories = process.env.FORGE_CTX_GOVERNOR === "1"
879
- ? [
880
- buildGovernorFactory({ phaseKey, cwd }),
881
- buildForgeCompactionFactory({
882
- cwd,
883
- phaseKey,
884
- entityId: taskId,
885
- sprintId: sprintIdForSummaries,
886
- }),
887
- ]
888
- : [];
889
- const phaseExtensionFactories = [...(opts.extensionFactories ?? []), ...governorFactories];
890
- let result;
891
- try {
892
- // FORGE-BUG-040: wrap the runForgeSubagent dispatch in the phase
893
- // caller context (parity with fix-bug.ts) so the phase-ownership
894
- // guard can verify tool calls from the subagent. Single setter
895
- // of phase context for the task pipeline.
896
- result = await CallerContextStore.asSubagent(phase.role, () => runForgeSubagent({
897
- persona,
898
- task: taskBody,
899
- cwd,
900
- exportTag: `${taskId}__${phase.role}`,
901
- tailLog: observer.state.tailLog,
902
- cacheSessionId,
903
- streamFn: opts.streamFnFactory?.({ kind: "task-phase", persona: persona.name, phase: phase.role, taskId }),
904
- onEvent: wrappedOnEvent,
905
- requestedModel: modelResolution.model,
906
- modelRegistry: ctx.modelRegistry,
907
- signal: opts.signal,
908
- customTools: opts.forgeToolDefs ? getSubagentTools(opts.forgeToolDefs, persona.name) : undefined,
909
- extensionFactories: phaseExtensionFactories.length > 0 ? phaseExtensionFactories : undefined,
910
- }));
911
- }
912
- catch (err) {
913
- const e = err;
914
- ctx.ui.notify(`× forge:run-task — runForgeSubagent threw for phase ${phase.role}: ${e.message ?? "unknown"}`, "error");
915
- tree.completeNode(phaseNodeId, "failed");
916
- writeState(cwd, {
917
- taskId,
918
- phaseIndex: currentPhaseIndex,
919
- iterationCounts,
920
- halted: true,
921
- lastError: `runForgeSubagent threw: ${e.message ?? "unknown"}`,
922
- savedAt: new Date().toISOString(),
923
- });
924
- return {
925
- status: "failed",
926
- lastPhaseIndex: currentPhaseIndex,
927
- iterationCounts,
928
- lastError: `runForgeSubagent threw: ${e.message ?? "unknown"}`,
929
- };
930
- }
931
- // Close this dispatch's tree node with final usage/model — MUST be
932
- // called on every exit path below (failure, halt, escalation,
933
- // loopback, advance). A node left `running` keeps a live spinner in
934
- // the dashboard forever AND absorbs the next same-role dispatch's
935
- // telemetry via the observer's legacy role-prefix scan.
936
- const finishPhaseNode = (status) => {
937
- tree.setNodeUsage(phaseNodeId, {
938
- input: result.usage.input,
939
- output: result.usage.output,
940
- cacheRead: result.usage.cacheRead,
941
- });
942
- if (result.model)
943
- tree.setNodeModel(phaseNodeId, result.model, result.provider ?? "");
944
- tree.completeNode(phaseNodeId, status);
945
- };
946
- // ── Post-subagent abort detection ─────────────────────────────────
947
- // If the abort signal fired during the subagent run, treat it as
948
- // cancellation regardless of the exit code (subagent may have been
949
- // mid-turn when aborted — exitCode could be 0 or 1).
950
- // This check MUST come before halt-on-failure so that
951
- // stopReason="aborted" + exitCode=1 is classified as cancellation,
952
- // not a phase failure.
953
- if (result.stopReason === "aborted" || opts.signal?.aborted) {
954
- ctx.ui.notify(`⊘ forge:run-task — ${taskId} phase ${phase.role} cancelled.`, "info");
955
- registry.completePhase(taskId, phase.role, "cancelled");
956
- tree.completeNode(phaseNodeId, "cancelled");
957
- registry.confirmCancelled(taskId);
958
- // Bug B: account the billed tokens of this aborted attempt before returning.
959
- {
960
- const abortSprintId = readTaskRecord(taskId, storeCli, cwd)?.sprintId;
961
- if (abortSprintId) {
962
- emitIncompletePhaseEvent({
963
- emitCtx: {
964
- entityType: "task",
965
- taskId,
966
- sprintId: abortSprintId,
967
- phase,
968
- iteration: (iterationCounts[phase.role] ?? 0) + 1,
969
- startMs: phaseStart,
970
- endMs: Date.now(),
971
- model: result.model ?? "unknown",
972
- provider: result.provider ?? "unknown",
973
- usage: {
974
- input: result.usage.input,
975
- output: result.usage.output,
976
- cacheRead: result.usage.cacheRead,
977
- cacheWrite: result.usage.cacheWrite,
978
- },
979
- judgement: undefined,
980
- storeCli,
981
- cwd,
982
- },
983
- outcome: "aborted",
984
- notes: result.errorMessage ?? result.stopReason ?? undefined,
985
- onDebug: writeDebug,
986
- });
987
- }
988
- else {
989
- writeDebug({ kind: "incomplete_emit_skipped", reason: "no-sprintId", outcome: "aborted" });
990
- }
991
- }
992
- // ADR-S21-01: preserve state file so cancelled runs are resumable
993
- writeState(cwd, {
994
- taskId,
995
- phaseIndex: currentPhaseIndex,
996
- iterationCounts,
997
- halted: false,
998
- status: "cancelled",
999
- lastError: undefined,
1000
- savedAt: new Date().toISOString(),
1001
- });
1002
- return { status: "cancelled", lastPhaseIndex: currentPhaseIndex, iterationCounts };
1003
- }
1004
- // ── Halt-on-failure ───────────────────────────────────────────
1005
- if (result.exitCode !== 0) {
1006
- ctx.ui.notify(`× forge:run-task — phase ${phase.role} failed (exit ${result.exitCode})` +
1007
- (result.errorMessage ? `: ${result.errorMessage}` : "") +
1008
- (result.stopReason ? ` [${result.stopReason}]` : ""), "error");
1009
- finishPhaseNode("failed");
1010
- // Bug B: account the billed tokens of this failed attempt before returning.
1011
- {
1012
- const failSprintId = readTaskRecord(taskId, storeCli, cwd)?.sprintId;
1013
- if (failSprintId) {
1014
- emitIncompletePhaseEvent({
1015
- emitCtx: {
1016
- entityType: "task",
1017
- taskId,
1018
- sprintId: failSprintId,
1019
- phase,
1020
- iteration: (iterationCounts[phase.role] ?? 0) + 1,
1021
- startMs: phaseStart,
1022
- endMs: Date.now(),
1023
- model: result.model ?? "unknown",
1024
- provider: result.provider ?? "unknown",
1025
- usage: {
1026
- input: result.usage.input,
1027
- output: result.usage.output,
1028
- cacheRead: result.usage.cacheRead,
1029
- cacheWrite: result.usage.cacheWrite,
1030
- },
1031
- judgement: undefined,
1032
- storeCli,
1033
- cwd,
1034
- },
1035
- outcome: "failed",
1036
- notes: result.errorMessage ?? result.stopReason ?? undefined,
1037
- onDebug: writeDebug,
1038
- });
1039
- }
1040
- else {
1041
- writeDebug({ kind: "incomplete_emit_skipped", reason: "no-sprintId", outcome: "failed" });
1042
- }
1043
- }
1044
- writeState(cwd, {
1045
- taskId,
1046
- phaseIndex: currentPhaseIndex,
1047
- iterationCounts,
1048
- halted: true,
1049
- lastError: result.errorMessage ?? result.stopReason ?? "subagent exit non-zero",
1050
- savedAt: new Date().toISOString(),
1051
- });
1052
- return {
1053
- status: "failed",
1054
- lastPhaseIndex: currentPhaseIndex,
1055
- iterationCounts,
1056
- lastError: result.errorMessage ?? result.stopReason ?? "subagent exit non-zero",
1057
- };
1058
- }
1059
- // Capture model/provider from subagent result (REVIEW FIX #1).
1060
- if (result.model)
1061
- lastModel = result.model;
1062
- if (result.provider)
1063
- lastProvider = result.provider;
1064
- // Phase-complete liveliness ping (counts + duration).
1065
- {
1066
- const elapsed = Math.floor((Date.now() - phaseStart) / 1000);
1067
- const { turn, toolCount, errCount, cumUsage } = observer.state;
1068
- ctx.ui.notify(`✓ ${phase.role}: ${turn} turn${turn === 1 ? "" : "s"} · ${toolCount} tool call${toolCount === 1 ? "" : "s"}${errCount ? ` · ${errCount} err` : ""} · ${elapsed}s`, "info");
1069
- orchTranscript.record({
1070
- kind: "phase-end",
1071
- ts: new Date().toISOString(),
1072
- phase: phase.role,
1073
- phaseIndex: currentPhaseIndex,
1074
- attempt: (iterationCounts[phase.role] ?? 0) + 1,
1075
- verdict: "n/a",
1076
- elapsedMs: Date.now() - phaseStart,
1077
- turns: turn,
1078
- toolCount,
1079
- errCount,
1080
- subagentTranscriptPath: result.subagentTranscriptPath,
1081
- });
1082
- const { cumCompression } = observer.state;
1083
- registry.appendTail(taskId, phase.role, fmtPhaseSummary({
1084
- role: phase.role,
1085
- turns: turn,
1086
- tools: toolCount,
1087
- errors: errCount,
1088
- wallSeconds: elapsed,
1089
- usage: cumUsage,
1090
- model: result.model,
1091
- provider: result.provider,
1092
- compression: cumCompression.tokensSaved > 0 ? cumCompression : undefined,
1093
- }));
1094
- }
1095
- // ── Plan 11 / Slice 2: orchestrator emits phase event ─────────
1096
- const phaseEndMs = Date.now();
1097
- const taskRecord = readTaskRecord(taskId, storeCli, cwd);
1098
- const sprintId = taskRecord?.sprintId;
1099
- if (!sprintId) {
1100
- ctx.ui.notify(`⚠ forge:run-task — could not resolve sprintId for ${taskId}; ` +
1101
- `skipping orchestrator emit for phase ${phase.role}`, "warning");
1102
- writeDebug({ kind: "emit_skipped", reason: "no-sprintId" });
1103
- }
1104
- else {
1105
- const phaseIteration = (iterationCounts[phase.role] ?? 0) + 1;
1106
- const emitCtx = {
1107
- entityType: "task",
1108
- taskId,
1109
- sprintId,
1110
- phase,
1111
- iteration: phaseIteration,
1112
- startMs: phaseStart,
1113
- endMs: phaseEndMs,
1114
- model: result.model ?? "unknown",
1115
- provider: result.provider ?? "unknown",
1116
- usage: {
1117
- input: result.usage.input,
1118
- output: result.usage.output,
1119
- cacheRead: result.usage.cacheRead,
1120
- cacheWrite: result.usage.cacheWrite,
1121
- },
1122
- judgement: judgementFromSummary(taskRecord, phase.role),
1123
- storeCli,
1124
- cwd,
1125
- };
1126
- const phaseEvent = buildPhaseEvent(emitCtx);
1127
- const emitResult = emitEvent(storeCli, cwd, sprintId, phaseEvent);
1128
- if (!emitResult.ok) {
1129
- ctx.ui.notify(`⚠ forge:run-task — phase event emit failed for ${phase.role}: ${emitResult.stderr.trim()}`, "warning");
1130
- writeDebug({ kind: "emit_failed", stderr: emitResult.stderr });
1131
- }
1132
- else {
1133
- writeDebug({ kind: "emit_ok", eventId: phaseEvent.eventId });
1134
- }
1135
- // Notify sprint-level observer (FORGE-S21-T03).
1136
- if (onPhaseEvent)
1137
- onPhaseEvent(phaseEvent);
1138
- // Drain friction file for this phase, if any.
1139
- const frictionPath = path.join(cwd, ".forge", "cache", `FRICTION-${phase.role}.jsonl`);
1140
- const drain = drainFrictionFile(frictionPath, emitCtx);
1141
- if (drain.emitted + drain.failed > 0) {
1142
- writeDebug({ kind: "friction_drain", ...drain });
1143
- if (drain.failed > 0) {
1144
- ctx.ui.notify(`⚠ forge:run-task — friction drain for ${phase.role}: ${drain.emitted} ok, ${drain.failed} failed`, "warning");
1145
- }
1146
- }
1147
- }
1148
- // ── 6b. Verdict check (review phases only) ────────────────────
1149
- if (phase.isReview) {
1150
- const verdict = readVerdict(taskId, phase.role, storeCli, cwd);
1151
- if (verdict === "missing") {
1152
- ctx.ui.notify(`× forge:run-task — verdict missing for phase ${phase.role} after subagent completed. ` +
1153
- "Subagent may have crashed or failed to write summaries. Halting for advisory.", "error");
1154
- finishPhaseNode("failed");
1155
- writeState(cwd, {
1156
- taskId,
1157
- phaseIndex: currentPhaseIndex,
1158
- iterationCounts,
1159
- halted: true,
1160
- lastError: `verdict missing for ${phase.role}`,
1161
- savedAt: new Date().toISOString(),
1162
- });
1163
- // A missing verdict IS a postflight-outputs failure: the canonical
1164
- // phase summary the subagent must write (e.g. summaries.code_review)
1165
- // was never recorded, so there is no verdict to route on. review-phase
1166
- // workflows declare no `outputs` block, so runPostflightGate is a
1167
- // pass-through here — this readVerdict check is the effective gate.
1168
- // Route it through the halt-recovery advisor (FORGE-S26-T18), the same
1169
- // hand-off the postflight-gate-unsatisfied branch uses, instead of a
1170
- // bare escalation — so the strongest configured model can diagnose the
1171
- // missing-summary cause. Best-effort, non-fatal (FORGE-S26-T19 parity).
1172
- const gateFailure = {
1173
- phase: phase.role,
1174
- reasonCode: "verdict-missing",
1175
- detail: `Phase '${phase.role}' completed but no verdict was found in the store. ` +
1176
- "The canonical phase summary was not written, so the orchestrator has no verdict to route on.",
1177
- remediation: "Re-run the phase and ensure the subagent's forge_store set-summary call " +
1178
- 'uses args:["<recordId>", "<phaseKey>"] with the literal phase key as args[1] ' +
1179
- "(e.g. code_review), and that the call exits zero before the subagent returns.",
1180
- };
1181
- const advisorModel = resolveAdvisorModel(modelRoutingConfig, ctx.model);
1182
- void runHaltAdvisor({
1183
- gateFailure,
1184
- advisorModel,
1185
- taskId,
1186
- cwd,
1187
- ctx: { ui: ctx.ui },
1188
- forgeRoot,
1189
- });
1190
- return {
1191
- status: "halted",
1192
- lastPhaseIndex: currentPhaseIndex,
1193
- iterationCounts,
1194
- lastError: `verdict missing for ${phase.role}`,
1195
- };
1196
- }
1197
- if (verdict === "revision") {
1198
- // Increment iteration count for this review phase
1199
- iterationCounts[phase.role] = (iterationCounts[phase.role] ?? 0) + 1;
1200
- if (iterationCounts[phase.role] >= phase.maxIterations) {
1201
- ctx.ui.notify(`× forge:run-task — revision cap reached for phase ${phase.role} ` +
1202
- `(${iterationCounts[phase.role]}/${phase.maxIterations} iterations). Escalating.`, "error");
1203
- finishPhaseNode("escalated");
1204
- writeState(cwd, {
1205
- taskId,
1206
- phaseIndex: currentPhaseIndex,
1207
- iterationCounts,
1208
- halted: true,
1209
- lastError: `revision cap reached for ${phase.role}`,
1210
- savedAt: new Date().toISOString(),
1211
- });
1212
- return {
1213
- status: "escalated",
1214
- lastPhaseIndex: currentPhaseIndex,
1215
- iterationCounts,
1216
- lastError: `revision cap reached for ${phase.role}`,
1217
- };
1218
- }
1219
- // Loop back to predecessor non-review phase. The review
1220
- // subagent itself finished cleanly — `revision` is its verdict,
1221
- // not a failure — so its node closes as completed before the
1222
- // predecessor re-dispatches under a fresh node ID.
1223
- finishPhaseNode("completed");
1224
- const predIndex = findPredecessorIndex(PHASES, currentPhaseIndex);
1225
- ctx.ui.notify(`⟳ forge:run-task — ${phase.role} returned revision; looping to ${PHASES[predIndex]?.role ?? predIndex} ` +
1226
- `(attempt ${iterationCounts[phase.role]}/${phase.maxIterations})`, "info");
1227
- orchTranscript.record({
1228
- kind: "phase-loopback",
1229
- ts: new Date().toISOString(),
1230
- fromPhase: phase.role,
1231
- toPhase: PHASES[predIndex]?.role ?? String(predIndex),
1232
- fromPhaseIndex: currentPhaseIndex,
1233
- toPhaseIndex: predIndex,
1234
- reason: `${phase.role} returned revision (attempt ${iterationCounts[phase.role]}/${phase.maxIterations})`,
1235
- });
1236
- // Write intermediate state (not halted — still running)
1237
- writeState(cwd, {
1238
- taskId,
1239
- phaseIndex: predIndex,
1240
- iterationCounts,
1241
- halted: false,
1242
- savedAt: new Date().toISOString(),
1243
- });
1244
- currentPhaseIndex = predIndex;
1245
- continue;
1246
- }
1247
- // verdict === "approved": fall through to advance
1248
- }
1249
- // Postflight gate: evaluate `outputs` block after subagent returns,
1250
- // before FSM status advance (FORGE-S26-T19). Hard enforcement in forge-cli;
1251
- // plugin LLM route treats postflight as advisory. On UNSATISFIED: do not
1252
- // advance currentPhaseIndex, halt, hand off to existing runHaltAdvisor.
1253
- {
1254
- const postflightGatePath = preflightGate.replace("preflight-gate.cjs", "postflight-gate.cjs");
1255
- const postflightOutcome = runPostflightGate(postflightGatePath, phase.role, taskId, cwd);
1256
- if (postflightOutcome.result === "unsatisfied") {
1257
- finishPhaseNode("failed");
1258
- if (postflightOutcome.gateFailure) {
1259
- ctx.ui.notify(`× forge:run-task — postflight gate failed for phase ${phase.role} ` +
1260
- `[${postflightOutcome.gateFailure.reasonCode}]: ${postflightOutcome.gateFailure.detail}`, "error");
1261
- }
1262
- else {
1263
- ctx.ui.notify(`× forge:run-task — postflight gate failed for phase ${phase.role}; halting.`, "error");
1264
- }
1265
- // Do NOT advance FSM — write state at current phaseIndex (halted)
1266
- writeState(cwd, {
1267
- taskId,
1268
- phaseIndex: currentPhaseIndex,
1269
- iterationCounts,
1270
- halted: true,
1271
- lastError: `postflight gate exit 1 for ${phase.role}`,
1272
- savedAt: new Date().toISOString(),
1273
- });
1274
- // Spawn halt-recovery advisor (Tier 1, best-effort — non-fatal).
1275
- if (postflightOutcome.gateFailure) {
1276
- const advisorModel = resolveAdvisorModel(modelRoutingConfig, ctx.model);
1277
- void runHaltAdvisor({
1278
- gateFailure: postflightOutcome.gateFailure,
1279
- advisorModel,
1280
- taskId,
1281
- cwd,
1282
- ctx: { ui: ctx.ui },
1283
- forgeRoot,
1284
- });
1285
- }
1286
- return {
1287
- status: "halted",
1288
- lastPhaseIndex: currentPhaseIndex,
1289
- iterationCounts,
1290
- lastError: `postflight gate exit 1 for ${phase.role}`,
1291
- };
1292
- }
1293
- // "ok" or "error" — proceed to advance
1294
- }
1295
- // ── Advance to next phase ─────────────────────────────────────
1296
- registry.completePhase(taskId, phase.role, "completed");
1297
- finishPhaseNode("completed");
1298
- writeState(cwd, {
1299
- taskId,
1300
- phaseIndex: currentPhaseIndex,
1301
- iterationCounts,
1302
- halted: false,
1303
- savedAt: new Date().toISOString(),
1304
- });
1305
- currentPhaseIndex++;
1306
- }
1307
- // ── All phases complete ───────────────────────────────────────────
1308
- deleteState(cwd, taskId);
1309
- orchTranscript.record({
1310
- kind: "pipeline-end",
1311
- ts: new Date().toISOString(),
1312
- outcome: "complete",
1313
- elapsedMs: Date.now() - pipelineStartMs,
1314
- });
1315
- return {
1316
- status: "completed",
1317
- lastPhaseIndex: PHASES.length - 1,
1318
- iterationCounts,
1319
- model: lastModel,
1320
- provider: lastProvider,
1321
- };
1322
- }
1323
- finally {
1324
- ctx.ui.notify = __origNotify;
1325
- }
1326
- }
1327
- export function registerRunTask(pi, options = {}) {
1328
- pi.registerCommand("forge:run-task", {
1329
- description: "Run the full task pipeline (plan → review → implement → validate → approve → commit). " +
1330
- "Usage: /forge:run-task <TASK_ID>. " +
1331
- "Orchestrator archetype: each phase is an isolated subagent session (IL10).",
1332
- async handler(args, ctx) {
1333
- const cwd = options.cwd ?? process.cwd();
1334
- let taskId = args.trim();
1335
- if (!taskId) {
1336
- ctx.ui.notify("× forge:run-task — task ID required. Usage: /forge:run-task <TASK_ID>", "error");
1337
- return;
1338
- }
1339
- ctx.ui.setStatus?.(STATUS_KEY, `run-task ${taskId}: initializing…`);
1340
- // ── Discover forge config ────────────────────────────────────────
1341
- const forgeConfig = discoverForgeConfigCached(cwd);
1342
- if (!forgeConfig) {
1343
- ctx.ui.notify("× forge:run-task — no Forge project found at cwd. Run /forge:init first.", "error");
1344
- ctx.ui.setStatus?.(STATUS_KEY, undefined);
1345
- ctx.ui.setStatus?.(MESSAGE_KEY, undefined);
1346
- return;
1347
- }
1348
- const forgeRoot = forgeConfig.forgeRoot;
1349
- // Best-effort transcript-archive sweep: adopt any project-local runs
1350
- // not yet in the central index (crash recovery + pre-existing
1351
- // history). Runs BEFORE this pipeline creates its own transcript
1352
- // writer, so the in-flight run is never swept half-written.
1353
- sweepProjectTranscripts(cwd);
1354
- // ── Resolve task ID (prefix-normalize, suffix-match, NLP fallback) ──
1355
- // Handles unprefixed IDs like "S22-T03" → "FORGE-S22-T03".
1356
- // Issue #20: unprefixed task IDs silently poisoned substitutions.
1357
- // NOTE: resolveToCanonicalId may surface ctx.ui.select (disambiguation)
1358
- // or ctx.ui.confirm prompts. The session must NOT be registered yet
1359
- // at this point — the chip strip would appear before the user has
1360
- // chosen which task they meant, stealing arrow keys from the dialog.
1361
- const toolDir = resolveToolDir(forgeRoot);
1362
- const resolvedTaskId = await resolveToCanonicalId(taskId, toolDir, cwd, "task", {
1363
- ctx,
1364
- commandLabel: "forge:run-task",
1365
- });
1366
- if (!resolvedTaskId) {
1367
- // Error already emitted by resolver
1368
- ctx.ui.setStatus?.(STATUS_KEY, undefined);
1369
- ctx.ui.setStatus?.(MESSAGE_KEY, undefined);
1370
- return;
1371
- }
1372
- // Replace raw arg with canonical ID for all subsequent operations
1373
- // (state files, store reads, preflight gates, orchestrator emits).
1374
- // Issue #20: unprefixed task IDs silently poisoned substitutions.
1375
- taskId = resolvedTaskId;
1376
- // Update status with canonical ID so the user sees the resolved form.
1377
- ctx.ui.setStatus?.(STATUS_KEY, `run-task ${taskId}: ready`);
1378
- // Tool paths
1379
- const storeCli = path.join(forgeRoot, "tools", "store-cli.cjs");
1380
- const preflightGate = path.join(forgeRoot, "tools", "preflight-gate.cjs");
1381
- // ── Resume detection ─────────────────────────────────────────────
1382
- const existing = readState(cwd, taskId);
1383
- let resumeFromState;
1384
- if (existing) {
1385
- if (isStateStale(existing)) {
1386
- // Stale state: notify + offer purge
1387
- ctx.ui.notify(`⚠ forge:run-task — cached state for ${taskId} is stale (>7 days old, saved at ${formatLocalTime(existing.savedAt)}). ` +
1388
- "Offering purge.", "warning");
1389
- if (!isNonInteractive()) {
1390
- const purge = await ctx.ui.confirm(`Purge stale state for ${taskId}?`, "The cached state is older than 7 days. Purge and restart from the beginning?");
1391
- if (purge) {
1392
- deleteState(cwd, taskId);
1393
- }
1394
- else {
1395
- ctx.ui.notify("forge:run-task — stale state kept; aborting.", "info");
1396
- ctx.ui.setStatus?.(STATUS_KEY, undefined);
1397
- ctx.ui.setStatus?.(MESSAGE_KEY, undefined);
1398
- return;
1399
- }
1400
- }
1401
- else {
1402
- // Non-interactive: auto-abort on stale state
1403
- ctx.ui.notify("forge:run-task — stale state; non-interactive mode auto-aborting.", "info");
1404
- ctx.ui.setStatus?.(STATUS_KEY, undefined);
1405
- ctx.ui.setStatus?.(MESSAGE_KEY, undefined);
1406
- return;
1407
- }
1408
- }
1409
- else {
1410
- // Fresh state: offer resume for ALL non-stale states — halted=true
1411
- // (explicit failure), halted=false (cancelled/interrupted), and
1412
- // any state with existing.status set (ADR-S21-01).
1413
- const stateStatus = existing.status ?? (existing.halted ? "halted" : "interrupted");
1414
- const statusLabel = stateStatus === "cancelled" ? "cancelled" : stateStatus === "halted" ? "halted" : "interrupted";
1415
- const phaseRole = PHASES[existing.phaseIndex]?.role ?? existing.phaseIndex;
1416
- if (!isNonInteractive()) {
1417
- const resume = await ctx.ui.confirm(`Resume ${taskId}?`, `Cached state — phase ${existing.phaseIndex} (${phaseRole}), ${statusLabel}, ` +
1418
- `saved at ${formatLocalTime(existing.savedAt)}. Resume from here?`);
1419
- if (resume) {
1420
- resumeFromState = existing;
1421
- ctx.ui.notify(`forge:run-task — resuming ${taskId} from phase ${phaseRole} (${statusLabel})`, "info");
1422
- }
1423
- else {
1424
- deleteState(cwd, taskId);
1425
- }
1426
- }
1427
- else {
1428
- // Non-interactive: auto-resume from state (no confirmation).
1429
- // Cancelled/interrupted states are valid resume points.
1430
- resumeFromState = existing;
1431
- ctx.ui.notify(`forge:run-task — resuming ${taskId} from phase ${phaseRole} (${statusLabel})`, "info");
1432
- }
1433
- }
1434
- }
1435
- // ── Register session & delegate to pipeline ────────────────────
1436
- // Session registration MUST happen after all interactive disambiguation
1437
- // (resolveToCanonicalId, resume confirm) so the chip strip doesn't appear
1438
- // before the user has confirmed which task they meant — the strip would
1439
- // steal arrow keys from ctx.ui.select / ctx.ui.confirm dialogs.
1440
- const registry = getSessionRegistry();
1441
- registry.startSession(taskId);
1442
- // Bridge: also register in OrchestratorTree for the dashboard overlay.
1443
- const tree = getOrchestratorTree();
1444
- tree.startNode(taskId, { label: taskId, kind: "orchestrator" });
1445
- const signal = registry.getAbortSignal(taskId);
1446
- const pipelineResult = await runTaskPipeline({
1447
- taskId,
1448
- cwd,
1449
- ctx,
1450
- forgeRoot,
1451
- storeCli,
1452
- preflightGate,
1453
- registry,
1454
- resumeFromState,
1455
- signal,
1456
- forgeToolDefs: options.forgeToolDefs,
1457
- extensionFactories: options.extensionFactories,
1458
- });
1459
- // ── Handle result ────────────────────────────────────────────────
1460
- if (pipelineResult.status === "completed") {
1461
- registry.completeSession(taskId, "completed");
1462
- tree.completeNode(taskId, "completed");
1463
- ctx.ui.notify(`〇 forge:run-task — ${taskId} pipeline complete (${PHASES.length} phases).`, "info");
1464
- }
1465
- else if (pipelineResult.status === "cancelled") {
1466
- // confirmCancelled was already called by the pipeline, but
1467
- // completeSession("cancelled") ensures the session ends cleanly.
1468
- registry.completeSession(taskId, "cancelled");
1469
- tree.completeNode(taskId, "cancelled");
1470
- }
1471
- else {
1472
- registry.completeSession(taskId, "failed");
1473
- tree.completeNode(taskId, "failed");
1474
- }
1475
- // Mirror this run into the central transcript archive (best-effort —
1476
- // archiveRun never throws). sprintId back-reference from the task
1477
- // record; list/timeline group on it (no synthetic sprint container).
1478
- if (pipelineResult.orchestratorTranscriptPath) {
1479
- const sprintIdForArchive = readTaskRecord(taskId, storeCli, cwd)?.sprintId;
1480
- archiveRun({
1481
- cwd,
1482
- orchestratorJsonlPath: pipelineResult.orchestratorTranscriptPath,
1483
- ...(sprintIdForArchive ? { sprintId: sprintIdForArchive } : {}),
1484
- });
1485
- }
1486
- ctx.ui.setStatus?.(STATUS_KEY, undefined);
1487
- ctx.ui.setStatus?.(MESSAGE_KEY, undefined);
1488
- },
1489
- });
1490
- }
1491
41
  //# sourceMappingURL=run-task.js.map