@entelligentsia/forgecli 1.1.0 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +4 -0
  3. package/dist/CHANGELOG-forge-plugin.md +114 -0
  4. package/dist/CHANGELOG-pi.md +56 -0
  5. package/dist/extensions/forgecli/forge-subagent.js +58 -0
  6. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  7. package/dist/extensions/forgecli/mcp-bridge/grove.d.ts +38 -0
  8. package/dist/extensions/forgecli/mcp-bridge/grove.js +88 -2
  9. package/dist/extensions/forgecli/mcp-bridge/grove.js.map +1 -1
  10. package/dist/extensions/forgecli/mcp-bridge/json-rpc-stdio.d.ts +26 -2
  11. package/dist/extensions/forgecli/mcp-bridge/json-rpc-stdio.js +68 -13
  12. package/dist/extensions/forgecli/mcp-bridge/json-rpc-stdio.js.map +1 -1
  13. package/dist/extensions/forgecli/mcp-bridge/mcp-bridge.js +13 -2
  14. package/dist/extensions/forgecli/mcp-bridge/mcp-bridge.js.map +1 -1
  15. package/dist/extensions/forgecli/mcp-bridge/mcp-session.d.ts +3 -3
  16. package/dist/extensions/forgecli/mcp-bridge/mcp-session.js +3 -6
  17. package/dist/extensions/forgecli/mcp-bridge/mcp-session.js.map +1 -1
  18. package/dist/extensions/forgecli/orchestrators/init/init-phase-dispatch.d.ts +49 -40
  19. package/dist/extensions/forgecli/orchestrators/init/init-phase-dispatch.js +59 -188
  20. package/dist/extensions/forgecli/orchestrators/init/init-phase-dispatch.js.map +1 -1
  21. package/dist/extensions/forgecli/orchestrators/init/init-phases.d.ts +0 -43
  22. package/dist/extensions/forgecli/orchestrators/init/init-phases.js +11 -59
  23. package/dist/extensions/forgecli/orchestrators/init/init-phases.js.map +1 -1
  24. package/dist/extensions/forgecli/orchestrators/init/init-steps.d.ts +115 -0
  25. package/dist/extensions/forgecli/orchestrators/init/init-steps.js +125 -0
  26. package/dist/extensions/forgecli/orchestrators/init/init-steps.js.map +1 -0
  27. package/dist/extensions/forgecli/orchestrators/init/run-init-pipeline.js +400 -211
  28. package/dist/extensions/forgecli/orchestrators/init/run-init-pipeline.js.map +1 -1
  29. package/dist/extensions/forgecli/orchestrators/init/run-init-types.d.ts +8 -2
  30. package/dist/extensions/forgecli/orchestrators/init/run-init-types.js +15 -5
  31. package/dist/extensions/forgecli/orchestrators/init/run-init-types.js.map +1 -1
  32. package/dist/forge-payload/.base-pack/workflows-js/wfl-init.js +99 -38
  33. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  34. package/dist/forge-payload/.init/generation/generate-kb-doc.md +24 -0
  35. package/dist/forge-payload/.schemas/migrations.json +77 -0
  36. package/dist/forge-payload/init/generation/generate-kb-doc.md +24 -0
  37. package/dist/forge-payload/init/phases/phase-2/context.md +23 -0
  38. package/dist/forge-payload/init/phases/phase-2/database.md +26 -0
  39. package/dist/forge-payload/init/phases/phase-2/deployment.md +26 -0
  40. package/dist/forge-payload/init/phases/phase-2/domain-concepts.md +26 -0
  41. package/dist/forge-payload/init/phases/phase-2/domain-model.md +26 -0
  42. package/dist/forge-payload/init/phases/phase-2/entity-model.md +26 -0
  43. package/dist/forge-payload/init/phases/phase-2/index.md +25 -0
  44. package/dist/forge-payload/init/phases/phase-2/processes.md +26 -0
  45. package/dist/forge-payload/init/phases/phase-2/routing.md +25 -0
  46. package/dist/forge-payload/init/phases/phase-2/stack-checklist.md +25 -0
  47. package/dist/forge-payload/init/phases/phase-2/stack.md +27 -0
  48. package/dist/forge-payload/init/phases/phase-2/testing.md +26 -0
  49. package/dist/forge-payload/init/phases/phase-2-discover.md +15 -9
  50. package/dist/forge-payload/payload-manifest.json +2 -2
  51. package/dist/forge-payload/tools/seed-store.cjs +14 -0
  52. package/dist/forge-payload/tools/verify-phase.cjs +61 -4
  53. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +3 -1
  54. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
  55. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +10 -1
  56. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
  57. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
  58. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +4 -6
  59. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
  60. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  61. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +20 -2
  62. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  63. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  64. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.d.ts.map +1 -1
  65. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js +2 -14
  66. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js.map +1 -1
  67. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  68. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +16 -5
  69. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
  70. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.d.ts.map +1 -1
  71. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +2 -1
  72. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js.map +1 -1
  73. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.d.ts.map +1 -1
  74. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +2 -1
  75. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js.map +1 -1
  76. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
  77. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +61 -41
  78. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  79. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  80. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +8 -3
  81. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  82. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  83. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +2 -14
  84. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  85. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.d.ts.map +1 -1
  86. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js +2 -1
  87. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js.map +1 -1
  88. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts +15 -45
  89. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  90. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +15 -45
  91. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
  92. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +461 -22
  93. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  94. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts +140 -0
  95. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
  96. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +141 -4
  97. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js.map +1 -1
  98. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.d.ts +20 -0
  99. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.d.ts.map +1 -1
  100. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js +18 -0
  101. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js.map +1 -1
  102. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.d.ts +21 -0
  103. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.d.ts.map +1 -1
  104. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js +18 -0
  105. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js.map +1 -1
  106. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts +21 -0
  107. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
  108. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js +18 -0
  109. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  110. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.d.ts +10 -5
  111. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.d.ts.map +1 -1
  112. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js +4 -3
  113. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js.map +1 -1
  114. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts +82 -0
  115. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts.map +1 -1
  116. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js +57 -0
  117. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js.map +1 -1
  118. package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.d.ts.map +1 -1
  119. package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.js +1 -1
  120. package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.js.map +1 -1
  121. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.d.ts +1 -1
  122. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.d.ts.map +1 -1
  123. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.js +4 -4
  124. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.js.map +1 -1
  125. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.d.ts +1 -1
  126. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.d.ts.map +1 -1
  127. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js +4 -4
  128. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js.map +1 -1
  129. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts.map +1 -1
  130. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js +5 -5
  131. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js.map +1 -1
  132. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts +88 -0
  133. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts.map +1 -1
  134. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +75 -2
  135. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js.map +1 -1
  136. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts +33 -15
  137. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts.map +1 -1
  138. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +101 -84
  139. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
  140. package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.d.ts.map +1 -1
  141. package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js +2 -2
  142. package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js.map +1 -1
  143. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts +44 -0
  144. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
  145. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +41 -4
  146. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  147. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
  148. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js +12 -12
  149. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
  150. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
  151. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js +12 -12
  152. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
  153. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
  154. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js +12 -12
  155. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
  156. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.d.ts.map +1 -1
  157. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.js +15 -15
  158. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.js.map +1 -1
  159. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts +25 -0
  160. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts.map +1 -0
  161. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js +109 -0
  162. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js.map +1 -0
  163. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.d.ts +2 -0
  164. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.d.ts.map +1 -1
  165. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js +15 -2
  166. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js.map +1 -1
  167. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  168. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +3 -2
  169. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  170. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -0
  171. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  172. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +3 -0
  173. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  174. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  175. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +1 -0
  176. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  177. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  178. package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +56 -0
  179. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +6 -1
  180. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  181. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +39 -15
  182. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  183. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  184. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js +13 -5
  185. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  186. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  187. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +5 -8
  188. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  189. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  190. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  191. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  192. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  193. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js +6 -0
  194. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  195. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
  196. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  197. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js +33 -1
  198. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  199. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts +16 -2
  200. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  201. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  202. package/node_modules/@earendil-works/pi-coding-agent/dist/core/http-dispatcher.d.ts.map +1 -1
  203. package/node_modules/@earendil-works/pi-coding-agent/dist/core/http-dispatcher.js +28 -1
  204. package/node_modules/@earendil-works/pi-coding-agent/dist/core/http-dispatcher.js.map +1 -1
  205. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.d.ts +10 -0
  206. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  207. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +14 -17
  208. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  209. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.d.ts.map +1 -1
  210. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.js +0 -10
  211. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.js.map +1 -1
  212. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.d.ts +20 -1
  213. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  214. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +87 -68
  215. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  216. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  217. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  218. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +8 -0
  219. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  220. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  221. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js +20 -5
  222. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  223. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  224. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js +2 -2
  225. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  226. package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts +3 -2
  227. package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts.map +1 -1
  228. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +2 -1
  229. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js.map +1 -1
  230. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  231. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  232. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +15 -7
  233. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  234. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-entry.d.ts +19 -0
  235. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-entry.d.ts.map +1 -0
  236. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-entry.js +52 -0
  237. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-entry.js.map +1 -0
  238. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  239. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  240. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +13 -1
  241. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  242. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  243. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
  244. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.js +60 -0
  245. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.js.map +1 -0
  246. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +6 -2
  247. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  248. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js +18 -5
  249. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  250. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +10 -10
  251. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  252. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +143 -130
  253. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  254. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +15 -0
  255. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  256. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js +14 -0
  257. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  258. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  259. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +16 -0
  260. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  261. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +26 -0
  262. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  263. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  264. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.d.ts.map +1 -1
  265. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +7 -0
  266. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md +55 -8
  268. package/node_modules/@earendil-works/pi-coding-agent/docs/rpc.md +58 -0
  269. package/node_modules/@earendil-works/pi-coding-agent/docs/sdk.md +28 -0
  270. package/node_modules/@earendil-works/pi-coding-agent/docs/session-format.md +18 -8
  271. package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +1 -0
  272. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/README.md +1 -0
  273. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
  274. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  275. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/entry-renderer.ts +41 -0
  276. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/gondolin/package-lock.json +2 -2
  277. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/gondolin/package.json +1 -1
  278. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/question.ts +1 -0
  279. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
  280. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
  281. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
  282. package/node_modules/@earendil-works/pi-coding-agent/package.json +4 -4
  283. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  284. package/package.json +7 -7
@@ -1,10 +1,13 @@
1
1
  // run-init-pipeline.ts — top-level FSM for the /forge:init orchestrated pipeline.
2
2
  // FORGE-S33-T03.
3
3
  //
4
- // Provides `runInitPipeline(opts)` which drives the four-phase loop:
5
- // - startPhase routing (resume from any phase 1–4)
6
- // - per-phase dispatch via dispatchInitPhase (T02) for LLM phases
7
- // - deterministic Phase 3/4 execution via existing helpers
4
+ // Provides `runInitPipeline(opts)` which drives the step machine (Slice 1):
5
+ // - builds the flat INIT_STEPS table, topo-sorts it into waves
6
+ // - startPhase routing (resume from any coarse phase 1–4 via PHASE_TO_WAVE)
7
+ // - runs each wave concurrently (runWave/Promise.all); per step:
8
+ // check precondition → run (deterministic thunk OR subagent) → check
9
+ // requiredOutput → advance / rerun (retryPolicy) / halt
10
+ // - deterministic Phase 3/4 execution via existing helpers (now steps)
8
11
  // - checkpoint writes via writeInitProgress
9
12
  // - orchestrator transcript + OrchestratorTree nodes
10
13
  // - IL10-compliant phase event emission (orchestrator-only; subagents never emit)
@@ -13,7 +16,7 @@
13
16
  // Iron Laws:
14
17
  // IL6 — no shell-string interpolation; all external calls via spawnSync argv arrays
15
18
  // IL7 — every failure path emits ctx.ui.notify and returns; no silent continuation
16
- // IL10 — ALL LLM dispatch goes through dispatchInitPhase → runForgeSubagent (NO sendKickoff)
19
+ // IL10 — ALL LLM dispatch goes through dispatchSingleAgent → runForgeSubagent (NO sendKickoff)
17
20
  // Orchestrator is the sole emitter of phase events; subagents NEVER call store-cli emit.
18
21
  //
19
22
  // Layering: may import from orchestrators/init/ siblings, orchestrators/common/,
@@ -31,8 +34,10 @@ import { createOrchestratorNotifier } from "../common/orchestrator-notify.js";
31
34
  import { runPipelinePreflight } from "../common/orchestrator-entry.js";
32
35
  import { withOrchestratorTranscript, } from "../common/orchestrator-transcript-session.js";
33
36
  import { emitEvent } from "../task/task-events.js";
34
- import { INIT_PHASES, INIT_SESSION_ID } from "./init-phases.js";
35
- import { dispatchInitPhase, } from "./init-phase-dispatch.js";
37
+ import { INIT_SESSION_ID } from "./init-phases.js";
38
+ import { dispatchSingleAgent, readInitPhasePrompt, readInitSharedProcedure, readInitPhase2Fragment, } from "./init-phase-dispatch.js";
39
+ import { runStep, runWave, topoSortWaves, } from "./init-steps.js";
40
+ import { DISCOVERY_SCHEMA, DOMAINS, KB_DOC_IDS, KB_DOC_SCHEMA } from "./run-init-types.js";
36
41
  import { getSessionRegistry } from "../../session-registry.js";
37
42
  import { getOrchestratorTree } from "../../orchestrator-tree.js";
38
43
  // ── Status / message keys ─────────────────────────────────────────────────────
@@ -41,14 +46,23 @@ const MESSAGE_KEY = "forge:init:message";
41
46
  // ── buildInitPhaseEvent ───────────────────────────────────────────────────────
42
47
  /**
43
48
  * Build an init-specific phase event object for IL10 emission.
44
- * The orchestrator calls this after each LLM phase completes.
49
+ * The orchestrator calls this after each successful subagent STEP completes.
50
+ *
51
+ * `stepId` is folded into `eventId` (and per-step start/end timestamps are used)
52
+ * so every step in a concurrent fan-out wave produces a DISTINCT event file.
53
+ * Deriving the id from phaseName + waveStartMs alone collided every step in a
54
+ * wave onto one <eventId>.json (store.writeEvent is a plain overwrite), silently
55
+ * discarding all but the last step's token accounting.
45
56
  */
46
- function buildInitPhaseEvent(phaseName, sprintId, startMs, endMs, model, provider, usage) {
57
+ function buildInitPhaseEvent(phaseName, stepId, sprintId, startMs, endMs, model, provider, usage) {
47
58
  const durationMs = Math.max(0, endMs - startMs);
48
59
  const startIso = new Date(startMs).toISOString();
49
60
  const compactTs = startIso.replace(/[-:.Z]/g, "");
61
+ // Sanitize the step id for use inside a filesystem-safe eventId (step ids
62
+ // carry ':' e.g. "discovery:routing", "kb-doc:stack").
63
+ const safeStepId = stepId.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
50
64
  return {
51
- eventId: `forge-init_${phaseName}_${compactTs}`,
65
+ eventId: `forge-init_${phaseName}_${safeStepId}_${compactTs}`,
52
66
  sprintId,
53
67
  role: `init-${phaseName}`,
54
68
  action: `forge:init:${phaseName}`,
@@ -179,8 +193,6 @@ async function runInitPipelineInner(opts, modelRoutingConfig, session) {
179
193
  }
180
194
  // Resolve kbFolder from opts or configCache (default: "engineering").
181
195
  const kbFolder = opts.kbFolder ?? "engineering";
182
- // kbPathFinal is resolved by Phase 4 or from configCache after Phase 1.
183
- let kbPathFinal;
184
196
  // Build initial configCache from .forge/config.json (fallback: {}).
185
197
  let configCache = {};
186
198
  try {
@@ -189,237 +201,414 @@ async function runInitPipelineInner(opts, modelRoutingConfig, session) {
189
201
  catch {
190
202
  // File not yet present — Phase 1 will create it
191
203
  }
192
- // Resolve start phase index (0-based).
193
- const startPhaseIndex = Math.max(0, (opts.startPhase ?? 1) - 1);
194
- let currentPhaseIndex = startPhaseIndex;
195
- let lastPhase = startPhaseIndex;
196
- while (currentPhaseIndex < INIT_PHASES.length) {
197
- const phase = INIT_PHASES[currentPhaseIndex];
198
- if (!phase) {
199
- ctx.ui.notify(`× forge:init — invalid phase index ${currentPhaseIndex}`, "error");
200
- return makeFailResult({ ok: false, lastPhase, failure: `invalid phase index ${currentPhaseIndex}` });
204
+ // ── Coarse-phase wave bridge ────────────────────────────────────────────
205
+ // The step machine groups steps into topo waves, but the on-disk
206
+ // `.forge/cache/init-progress` checkpoint + resume tests speak the coarse
207
+ // phase number (1–4). These tables bridge the two with NO checkpoint-format
208
+ // change (PLAN design #8). Wave layout (from topoSortWaves on INIT_STEPS):
209
+ // 0 discovery×5 · 1 config-writer · 2 enforce-config → phase 1 (collect)
210
+ // 3 kb-doc×10 · 4 index · 5 context · 6 verify-discover → phase 2 (discover)
211
+ // 7 materialize → phase 3
212
+ // 8 register → phase 4
213
+ const WAVE_PHASE_NUM = [1, 1, 1, 2, 2, 2, 2, 3, 4];
214
+ const WAVE_PHASE_NAME = [
215
+ "collect", "collect", "collect",
216
+ "discover", "discover", "discover", "discover",
217
+ "materialize", "register",
218
+ ];
219
+ // Resume: coarse startPhase (1–4) → first wave of that phase.
220
+ const PHASE_TO_WAVE = { 1: 0, 2: 3, 3: 7, 4: 8 };
221
+ const startWave = PHASE_TO_WAVE[opts.startPhase ?? 1] ?? 0;
222
+ const state = {
223
+ configCache,
224
+ verifyOk: true,
225
+ phase3Ok: true,
226
+ phase4Ok: true,
227
+ };
228
+ // Pre-read the bundled phase prompts needed for the resume range. A prompt
229
+ // read failure is a graceful pipeline failure (IL7), not a thrown crash.
230
+ //
231
+ // Slice 2 (FORGE-S35-T03): the Phase-2 base prompt is NO LONGER the whole
232
+ // phase-2-discover.md rulebook. It is the shared procedure (generate-kb-doc.md);
233
+ // each Phase-2 step appends its OWN substance fragment + AGENT PARAMS in its
234
+ // buildPrompt, so a subagent sees only its own docId's work. Fragments are
235
+ // keyed by KB_DOC_ID basename plus "index" / "context".
236
+ const promptCache = {};
237
+ const phase2Fragments = {};
238
+ try {
239
+ if (startWave <= 1)
240
+ promptCache[1] = readInitPhasePrompt(bundleRoot, 1);
241
+ if (startWave <= 5) {
242
+ promptCache[2] = readInitSharedProcedure(bundleRoot);
243
+ const fragmentNames = [
244
+ ...KB_DOC_IDS.map((id) => id.slice(id.lastIndexOf("/") + 1)),
245
+ "index",
246
+ "context",
247
+ ];
248
+ for (const name of fragmentNames) {
249
+ phase2Fragments[name] = readInitPhase2Fragment(bundleRoot, name);
250
+ }
201
251
  }
202
- const phaseNum = currentPhaseIndex + 1;
203
- ctx.ui.setStatus?.(STATUS_KEY, `forge:init: phase ${phaseNum}/${INIT_PHASES.length} (${phase.name})`);
204
- ctx.ui.notify(`→ init: phase ${phase.name} (${phaseNum}/${INIT_PHASES.length})`, "info");
205
- orchTranscript.record({
206
- kind: "phase-start",
207
- ts: new Date().toISOString(),
208
- phase: phase.name,
209
- phaseIndex: currentPhaseIndex,
210
- phaseCount: INIT_PHASES.length,
211
- attempt: 1,
212
- workflowFile: `init-${phase.name}`,
213
- persona: phase.persona ?? "engineer",
214
- });
215
- const phaseStartMs = Date.now();
216
- // Dispatch the phase.
217
- const outcome = await dispatchInitPhase({
252
+ }
253
+ catch (err) {
254
+ const e = err;
255
+ const failure = `phase prompt read failed: ${e.message ?? "unknown"}`;
256
+ ctx.ui.notify(`× forge:init — ${failure}`, "error");
257
+ return makeFailResult({ ok: false, lastPhase: WAVE_PHASE_NUM[startWave], failure });
258
+ }
259
+ // ── Subagent step runner (IL10: dispatch via dispatchSingleAgent) ─────────
260
+ const dispatchCounts = {};
261
+ let currentWaveIndex = startWave;
262
+ const dispatchSubagent = async (run) => {
263
+ const base = promptCache[run.promptPhase];
264
+ if (base === undefined) {
265
+ throw new Error(`init: phase-${run.promptPhase} prompt not loaded for step ${run.subLabel}`);
266
+ }
267
+ const prompt = run.buildPrompt(base, state);
268
+ const dispatchParams = {
218
269
  opts,
219
- phase,
220
- phaseIndex: currentPhaseIndex,
221
270
  cwd,
222
271
  ctx,
223
272
  bundleRoot,
224
- kbFolder,
225
- isoTimestamp: opts.isoTimestamp,
226
273
  modelRoutingConfig,
227
274
  forgeToolDefs: opts.forgeToolDefs,
228
- dispatchCounts: {},
275
+ dispatchCounts,
276
+ orderHint: currentWaveIndex,
277
+ };
278
+ return dispatchSingleAgent(run.subLabel, run.subRole, run.modelRole, prompt, run.schema, run.persona, dispatchParams);
279
+ };
280
+ // ── INIT_STEPS: the flat step table that supersedes coarse INIT_PHASES ─────
281
+ function buildInitSteps() {
282
+ const iso = opts.isoTimestamp;
283
+ // Deterministic postcondition for a subagent step: its own dispatch exited 0.
284
+ const exitOk = async (_c, lastResult) => ({
285
+ ok: lastResult?.exitCode === 0,
286
+ reason: lastResult && lastResult.exitCode !== 0 ? `subagent exited ${lastResult.exitCode}` : undefined,
229
287
  });
230
- if (outcome.kind === "return") {
231
- // Early failure / cancel.
232
- const failure = outcome.result.failure ?? `Phase ${phaseNum} (${phase.name}) failed`;
233
- ctx.ui.notify(`× forge:init phase ${phase.name} failed: ${failure}`, "error");
234
- orchTranscript.record({
235
- kind: "phase-end",
236
- ts: new Date().toISOString(),
237
- phase: phase.name,
238
- phaseIndex: currentPhaseIndex,
239
- attempt: 1,
240
- verdict: "error",
241
- elapsedMs: Date.now() - phaseStartMs,
288
+ // Deterministic precondition replacing the DELETED Phase-2 gate subagent:
289
+ // verify phase-1 config is ready before kb-doc generation fans out.
290
+ const kbReady = async (c) => {
291
+ const cc = c.configCache;
292
+ const ok = !!cc && typeof cc === "object";
293
+ return { ok, reason: ok ? undefined : "phase-1 config not ready (gate)" };
294
+ };
295
+ const steps = [];
296
+ // Wave 0 — 5 domain discovery agents (independent fan-out).
297
+ for (const domain of DOMAINS) {
298
+ steps.push({
299
+ id: `discovery:${domain}`,
300
+ dependsOn: [],
301
+ retryPolicy: { maxReruns: 0 },
302
+ requiredOutput: exitOk,
303
+ run: {
304
+ kind: "subagent",
305
+ promptPhase: 1,
306
+ subLabel: `discovery:${domain}`,
307
+ subRole: "plan",
308
+ modelRole: "discovery",
309
+ persona: "engineer",
310
+ phaseGroup: "collect",
311
+ schema: DISCOVERY_SCHEMA,
312
+ buildPrompt: (b) => `${b}\n\n<!-- AGENT PARAMS -->\ndomain: ${domain}\nkbFolder: ${kbFolder}\nisoTimestamp: ${iso}\n`,
313
+ },
242
314
  });
243
- return makeFailResult({ ok: false, lastPhase: phaseNum, failure });
244
315
  }
245
- if (outcome.kind === "skip") {
246
- // Deterministic phase — run Phase 3 or Phase 4 directly.
247
- if (phase.name === "materialize") {
248
- // Phase 3: runPhase3 internally calls verifyPhase3 and hard-halts on failure.
249
- const phase3Result = await runPhase3(cwd, bundleRoot, toolsRoot, ctx);
250
- if (phase3Result === "abort") {
251
- const failure = "Phase 3 abort (verify failed or tools missing)";
252
- ctx.ui.notify(`× forge:init — ${failure}`, "error");
253
- orchTranscript.record({
254
- kind: "phase-end",
255
- ts: new Date().toISOString(),
256
- phase: phase.name,
257
- phaseIndex: currentPhaseIndex,
258
- attempt: 1,
259
- verdict: "error",
260
- elapsedMs: Date.now() - phaseStartMs,
261
- });
262
- return makeFailResult({ ok: false, lastPhase: phaseNum, failure });
263
- }
264
- // Phase 3 succeededwrite checkpoint.
265
- writeInitProgress(cwd, 3);
266
- }
267
- else if (phase.name === "register") {
268
- // Phase 4: runPhase4 is deterministic + internally deletes init-progress.
269
- const isPiRuntime = opts.isPiRuntime ?? (() => false);
270
- const getBundledToolsRoot = opts.getBundledToolsRoot ?? (() => toolsRoot);
271
- const phase4Ctx = {
272
- cwd,
273
- bundleRoot,
274
- toolsRoot,
275
- projectName,
276
- configCache,
277
- ctx,
278
- isPiRuntime,
279
- getBundledToolsRoot,
280
- };
281
- const phase4Result = await runPhase4(phase4Ctx);
282
- if (phase4Result === "abort") {
283
- const failure = "Phase 4 abort";
284
- ctx.ui.notify( forge:init — ${failure}`, "error");
285
- orchTranscript.record({
286
- kind: "phase-end",
287
- ts: new Date().toISOString(),
288
- phase: phase.name,
289
- phaseIndex: currentPhaseIndex,
290
- attempt: 1,
291
- verdict: "error",
292
- elapsedMs: Date.now() - phaseStartMs,
293
- });
294
- return makeFailResult({ ok: false, lastPhase: phaseNum, failure });
295
- }
296
- // Capture kbPathFinal from Phase 4 result for InitReport.
297
- kbPathFinal = phase4Result.kbPathFinal;
298
- // Phase 4 internally calls deleteInitProgress — no checkpoint write needed.
299
- }
316
+ // Wave 1 — config-writer (depends on all domains; retries once).
317
+ steps.push({
318
+ id: "config-writer",
319
+ dependsOn: DOMAINS.map((d) => `discovery:${d}`),
320
+ retryPolicy: { maxReruns: 1 },
321
+ requiredOutput: exitOk,
322
+ run: {
323
+ kind: "subagent",
324
+ promptPhase: 1,
325
+ subLabel: "config-writer",
326
+ subRole: "plan",
327
+ modelRole: "config",
328
+ persona: "engineer",
329
+ phaseGroup: "collect",
330
+ buildPrompt: (b) => `${b}\n\n<!-- AGENT PARAMS -->\nrole: config-writer\nkbFolder: ${kbFolder}\nisoTimestamp: ${iso}\n`,
331
+ },
332
+ });
333
+ // Wave 2 deterministic: orchestrator-owned KB-folder + prefix enforcement,
334
+ // configCache refresh, coarse phase-1 checkpoint. (Migrated verbatim from the
335
+ // old inlined post-collect hook the KB folder + prefix are NOT LLM-routed.)
336
+ steps.push({
337
+ id: "enforce-config",
338
+ dependsOn: ["config-writer"],
339
+ retryPolicy: { maxReruns: 0 },
340
+ run: {
341
+ kind: "deterministic",
342
+ thunk: async () => {
343
+ const manageConfigTool = path.join(toolsRoot, "manage-config.cjs");
344
+ if (fs.existsSync(manageConfigTool)) {
345
+ await runToolAdvisory(manageConfigTool, ["set", "paths.engineering", kbFolder], cwd, ctx, "manage-config paths.engineering");
346
+ const projectPrefix = opts.projectPrefix ?? deriveProjectPrefix(projectName);
347
+ await runToolAdvisory(manageConfigTool, ["set", "project.prefix", projectPrefix], cwd, ctx, "manage-config project.prefix");
348
+ }
349
+ try {
350
+ state.configCache = JSON.parse(fs.readFileSync(path.join(cwd, ".forge", "config.json"), "utf8"));
351
+ }
352
+ catch {
353
+ // keep existing cache
354
+ }
355
+ writeInitProgress(cwd, 1);
356
+ },
357
+ },
358
+ });
359
+ // Wave 3 — 10 kb-doc agents (independent fan-out; gate is now a precondition).
360
+ for (const docId of KB_DOC_IDS) {
361
+ steps.push({
362
+ id: `kb-doc:${docId}`,
363
+ dependsOn: ["enforce-config"],
364
+ precondition: kbReady,
365
+ retryPolicy: { maxReruns: 1 },
366
+ requiredOutput: exitOk,
367
+ run: {
368
+ kind: "subagent",
369
+ promptPhase: 2,
370
+ subLabel: `kb-doc:${docId}`,
371
+ subRole: "plan",
372
+ modelRole: "kb-doc",
373
+ persona: "engineer",
374
+ phaseGroup: "discover",
375
+ schema: KB_DOC_SCHEMA,
376
+ buildPrompt: (b) => {
377
+ const fragment = phase2Fragments[docId.slice(docId.lastIndexOf("/") + 1)] ?? "";
378
+ return `${b}\n\n${fragment}\n\n<!-- AGENT PARAMS -->\nrole: kb-doc\ndocId: ${docId}\nkbFolder: ${kbFolder}\nisoTimestamp: ${iso}\n`;
379
+ },
380
+ },
381
+ });
300
382
  }
301
- else {
302
- // outcome.kind === "ok" — LLM phase succeeded.
303
- const result = outcome.result;
304
- // Phase 1 post-success: deterministically own the KB-folder decision,
305
- // then re-read configCache.
306
- if (phase.name === "collect") {
307
- // The KB folder is NOT an LLM-routed decision. forge-cli resolves
308
- // kbFolder deterministically in the forge-init.ts handler prologue
309
- // (user prompt or the "engineering" default). Enforce it onto
310
- // paths.engineering here — after the config-writer agent ran — so
311
- // the orchestrator-owned value is authoritative regardless of what
312
- // the agent wrote. This is what lets the subagents skip the prompt
313
- // entirely (they have no say in the outcome).
314
- const manageConfigTool = path.join(toolsRoot, "manage-config.cjs");
315
- if (fs.existsSync(manageConfigTool)) {
316
- await runToolAdvisory(manageConfigTool, ["set", "paths.engineering", kbFolder], cwd, ctx, "manage-config paths.engineering");
317
- // project.prefix is likewise deterministic — handler-resolved
318
- // (deriveProjectPrefix + confirm/override), or derived here from
319
- // projectName when a non-handler caller omitted it. The LLM
320
- // config-writer never owns it.
321
- const projectPrefix = opts.projectPrefix ?? deriveProjectPrefix(projectName);
322
- await runToolAdvisory(manageConfigTool, ["set", "project.prefix", projectPrefix], cwd, ctx, "manage-config project.prefix");
323
- }
324
- try {
325
- configCache = JSON.parse(fs.readFileSync(path.join(cwd, ".forge", "config.json"), "utf8"));
326
- }
327
- catch {
328
- // Fall back to existing cache
329
- }
330
- }
331
- // Phase 2 post-success: run verifyPhase2 + post-verify hooks.
332
- if (phase.name === "discover") {
333
- // Resolve kbPath from configCache (matches run-phases.ts:runPhase2 logic).
334
- let kbPath = kbFolder;
335
- try {
336
- const cachePaths = configCache.paths;
337
- if (cachePaths && typeof cachePaths.engineering === "string") {
338
- kbPath = cachePaths.engineering;
383
+ // Wave 4 — index (depends on all kb-docs).
384
+ steps.push({
385
+ id: "index",
386
+ dependsOn: KB_DOC_IDS.map((d) => `kb-doc:${d}`),
387
+ retryPolicy: { maxReruns: 0 },
388
+ requiredOutput: exitOk,
389
+ run: {
390
+ kind: "subagent",
391
+ promptPhase: 2,
392
+ subLabel: "index",
393
+ subRole: "plan",
394
+ modelRole: "index",
395
+ persona: "engineer",
396
+ phaseGroup: "discover",
397
+ buildPrompt: (b) => `${b}\n\n${phase2Fragments["index"] ?? ""}\n\n<!-- AGENT PARAMS -->\nrole: index\nkbFolder: ${kbFolder}\nisoTimestamp: ${iso}\n`,
398
+ },
399
+ });
400
+ // Wave 5 context (depends on index; retries once).
401
+ steps.push({
402
+ id: "context",
403
+ dependsOn: ["index"],
404
+ retryPolicy: { maxReruns: 1 },
405
+ requiredOutput: exitOk,
406
+ run: {
407
+ kind: "subagent",
408
+ promptPhase: 2,
409
+ subLabel: "context",
410
+ subRole: "plan",
411
+ modelRole: "context",
412
+ persona: "engineer",
413
+ phaseGroup: "discover",
414
+ buildPrompt: (b) => `${b}\n\n${phase2Fragments["context"] ?? ""}\n\n<!-- AGENT PARAMS -->\nrole: context\nkbFolder: ${kbFolder}\nisoTimestamp: ${iso}\n`,
415
+ },
416
+ });
417
+ // Wave 6 — deterministic: verifyPhase2 + post-verify hooks + coarse phase-2
418
+ // checkpoint. requiredOutput gates on the verify result.
419
+ steps.push({
420
+ id: "verify-discover",
421
+ dependsOn: ["context"],
422
+ retryPolicy: { maxReruns: 0 },
423
+ requiredOutput: async (c) => {
424
+ const s = c;
425
+ return { ok: s.verifyOk, reason: s.verifyReason };
426
+ },
427
+ run: {
428
+ kind: "deterministic",
429
+ thunk: async () => {
430
+ let kbPath = kbFolder;
431
+ try {
432
+ const cachePaths = state.configCache.paths;
433
+ if (cachePaths && typeof cachePaths.engineering === "string") {
434
+ kbPath = cachePaths.engineering;
435
+ }
339
436
  }
340
- }
341
- catch {
342
- // use kbFolder default
343
- }
344
- const verifyResult = await verifyPhase2(cwd, kbPath);
345
- if (!verifyResult.ok) {
346
- // T02 already did retry-once inside dispatchInitPhase; this is the
347
- // orchestrator-side confirm. On failure, halt (non-interactive path).
348
- const failure = `Phase 2 verify failed: ${verifyResult.missing.join(", ")}`;
349
- ctx.ui.notify(`× forge:init — ${failure}`, "error");
350
- orchTranscript.record({
351
- kind: "phase-end",
352
- ts: new Date().toISOString(),
353
- phase: phase.name,
354
- phaseIndex: currentPhaseIndex,
355
- attempt: 1,
356
- verdict: "error",
357
- elapsedMs: Date.now() - phaseStartMs,
358
- });
359
- return makeFailResult({ ok: false, lastPhase: phaseNum, failure });
360
- }
361
- // Run post-verify hooks (project-context.json + calibration baseline).
362
- await runPhase2PostVerifyHooks(cwd, bundleRoot, toolsRoot, projectName, configCache, ctx);
363
- }
364
- // IL10: orchestrator emits phase event from result.{model,provider,usage}.
365
- // Subagents NEVER call store-cli emit for phase events.
366
- const phaseEndMs = Date.now();
367
- const { sprintId } = opts;
437
+ catch {
438
+ // use kbFolder default
439
+ }
440
+ const verifyResult = await verifyPhase2(cwd, kbPath);
441
+ if (!verifyResult.ok) {
442
+ state.verifyOk = false;
443
+ state.verifyReason = `Phase 2 verify failed: ${verifyResult.missing.join(", ")}`;
444
+ return;
445
+ }
446
+ state.verifyOk = true;
447
+ await runPhase2PostVerifyHooks(cwd, bundleRoot, toolsRoot, projectName, state.configCache, ctx);
448
+ writeInitProgress(cwd, 2);
449
+ },
450
+ },
451
+ });
452
+ // Wave 7 — deterministic: Phase 3 materialize (scaffold + verify) + checkpoint.
453
+ steps.push({
454
+ id: "materialize",
455
+ dependsOn: ["verify-discover"],
456
+ retryPolicy: { maxReruns: 0 },
457
+ requiredOutput: async (c) => {
458
+ const s = c;
459
+ return {
460
+ ok: s.phase3Ok,
461
+ reason: s.phase3Ok ? undefined : "Phase 3 abort (verify failed or tools missing)",
462
+ };
463
+ },
464
+ run: {
465
+ kind: "deterministic",
466
+ thunk: async () => {
467
+ const phase3Result = await runPhase3(cwd, bundleRoot, toolsRoot, ctx);
468
+ if (phase3Result === "abort") {
469
+ state.phase3Ok = false;
470
+ return;
471
+ }
472
+ state.phase3Ok = true;
473
+ writeInitProgress(cwd, 3);
474
+ },
475
+ },
476
+ });
477
+ // Wave 8 — deterministic: Phase 4 register (internally deletes init-progress).
478
+ steps.push({
479
+ id: "register",
480
+ dependsOn: ["materialize"],
481
+ retryPolicy: { maxReruns: 0 },
482
+ requiredOutput: async (c) => {
483
+ const s = c;
484
+ return { ok: s.phase4Ok, reason: s.phase4Ok ? undefined : "Phase 4 abort" };
485
+ },
486
+ run: {
487
+ kind: "deterministic",
488
+ thunk: async () => {
489
+ const isPiRuntime = opts.isPiRuntime ?? (() => false);
490
+ const getBundledToolsRoot = opts.getBundledToolsRoot ?? (() => toolsRoot);
491
+ const phase4Ctx = {
492
+ cwd,
493
+ bundleRoot,
494
+ toolsRoot,
495
+ projectName,
496
+ configCache: state.configCache,
497
+ ctx,
498
+ isPiRuntime,
499
+ getBundledToolsRoot,
500
+ };
501
+ const phase4Result = await runPhase4(phase4Ctx);
502
+ if (phase4Result === "abort") {
503
+ state.phase4Ok = false;
504
+ return;
505
+ }
506
+ state.phase4Ok = true;
507
+ state.kbPathFinal = phase4Result.kbPathFinal;
508
+ },
509
+ },
510
+ });
511
+ return steps;
512
+ }
513
+ // ── Drive the waves ────────────────────────────────────────────────────────
514
+ const steps = buildInitSteps();
515
+ const waves = topoSortWaves(steps);
516
+ let lastPhase = startWave === 0 ? 0 : WAVE_PHASE_NUM[startWave - 1] ?? 0;
517
+ const pipelineStartMs = Date.now();
518
+ for (let w = startWave; w < waves.length; w++) {
519
+ currentWaveIndex = w;
520
+ const wave = waves[w];
521
+ const phaseName = WAVE_PHASE_NAME[w];
522
+ const phaseNum = WAVE_PHASE_NUM[w];
523
+ ctx.ui.setStatus?.(STATUS_KEY, `forge:init: wave ${w + 1}/${waves.length} (${phaseName})`);
524
+ ctx.ui.notify(`→ init: ${phaseName} · wave ${w + 1}/${waves.length} [${wave.map((s) => s.id).join(", ")}]`, "info");
525
+ orchTranscript.record({
526
+ kind: "phase-start",
527
+ ts: new Date().toISOString(),
528
+ phase: phaseName,
529
+ phaseIndex: w,
530
+ phaseCount: waves.length,
531
+ attempt: 1,
532
+ workflowFile: `init-${phaseName}`,
533
+ persona: "engineer",
534
+ });
535
+ const waveStartMs = Date.now();
536
+ const outcomes = await runWave(wave, (step) => runStep(step, { ctx: state, dispatchSubagent }));
537
+ // IL10: orchestrator emits ONE phase event per successful subagent step,
538
+ // composed from captured result.{model,provider,usage} + the step's OWN
539
+ // start/end bracket (outcome.startMs/endMs) so each event is distinct and
540
+ // duration is attributed per step, not per wave. Deterministic steps emit
541
+ // nothing (no subagent telemetry to attribute). Subagents never emit.
542
+ const { sprintId } = opts;
543
+ for (let i = 0; i < wave.length; i++) {
544
+ const step = wave[i];
545
+ const outcome = outcomes[i];
546
+ if (step.run.kind !== "subagent" || !outcome.ok || !outcome.result)
547
+ continue;
368
548
  if (!sprintId) {
369
- ctx.ui.notify(`⚠ forge:init — sprintId not provided; skipping phase event emit for ${phase.name}`, "warning");
549
+ ctx.ui.notify(`⚠ forge:init — sprintId not provided; skipping phase event emit for ${step.id}`, "warning");
550
+ continue;
370
551
  }
371
- else {
372
- const phaseEvent = buildInitPhaseEvent(phase.name, sprintId, phaseStartMs, phaseEndMs, result.model ?? "unknown", result.provider ?? "unknown", {
373
- input: result.usage.input,
374
- output: result.usage.output,
375
- cacheRead: result.usage.cacheRead,
376
- cacheWrite: result.usage.cacheWrite,
377
- });
378
- const emitResult = emitEvent(storeCli, cwd, sprintId, phaseEvent);
379
- if (!emitResult.ok) {
380
- ctx.ui.notify(`⚠ forge:init — phase event emit failed for ${phase.name}: ${emitResult.stderr.trim()}`, "warning");
381
- }
382
- }
383
- // Write checkpoint after LLM phase (phases 1 and 2).
384
- if (phase.name === "collect") {
385
- writeInitProgress(cwd, 1);
386
- }
387
- else if (phase.name === "discover") {
388
- writeInitProgress(cwd, 2);
552
+ const r = outcome.result;
553
+ const phaseEvent = buildInitPhaseEvent(step.run.phaseGroup, step.id, sprintId, outcome.startMs, outcome.endMs, r.model ?? "unknown", r.provider ?? "unknown", {
554
+ input: r.usage.input,
555
+ output: r.usage.output,
556
+ cacheRead: r.usage.cacheRead,
557
+ cacheWrite: r.usage.cacheWrite,
558
+ });
559
+ const emitResult = emitEvent(storeCli, cwd, sprintId, phaseEvent);
560
+ if (!emitResult.ok) {
561
+ ctx.ui.notify(`⚠ forge:init — phase event emit failed for ${step.id}: ${emitResult.stderr.trim()}`, "warning");
389
562
  }
390
563
  }
391
- // Record phase-end to orchestrator transcript.
564
+ // Halt on the first failed step in this wave (declaration order).
565
+ const failedIdx = outcomes.findIndex((o) => !o.ok);
566
+ if (failedIdx >= 0) {
567
+ const failedStep = wave[failedIdx];
568
+ const failure = outcomes[failedIdx].reason ?? `step "${failedStep.id}" failed`;
569
+ ctx.ui.notify(`× forge:init — ${phaseName} step "${failedStep.id}" failed: ${failure}`, "error");
570
+ orchTranscript.record({
571
+ kind: "phase-end",
572
+ ts: new Date().toISOString(),
573
+ phase: phaseName,
574
+ phaseIndex: w,
575
+ attempt: 1,
576
+ verdict: "error",
577
+ elapsedMs: Date.now() - waveStartMs,
578
+ });
579
+ return makeFailResult({ ok: false, lastPhase: phaseNum, failure });
580
+ }
392
581
  orchTranscript.record({
393
582
  kind: "phase-end",
394
583
  ts: new Date().toISOString(),
395
- phase: phase.name,
396
- phaseIndex: currentPhaseIndex,
584
+ phase: phaseName,
585
+ phaseIndex: w,
397
586
  attempt: 1,
398
587
  verdict: "n/a",
399
- elapsedMs: Date.now() - phaseStartMs,
588
+ elapsedMs: Date.now() - waveStartMs,
400
589
  });
401
- const elapsed = Math.floor((Date.now() - phaseStartMs) / 1000);
402
- ctx.ui.notify(`✓ init: ${phase.name} complete (${elapsed}s)`, "info");
590
+ const elapsed = Math.floor((Date.now() - waveStartMs) / 1000);
591
+ ctx.ui.notify(`✓ init: ${phaseName} wave ${w + 1}/${waves.length} complete (${elapsed}s)`, "info");
403
592
  lastPhase = phaseNum;
404
- currentPhaseIndex++;
405
593
  }
406
- // ── All phases complete — assemble InitReport ──────────────────────────────
407
- const pipelineEndMs = Date.now();
594
+ // ── All waves complete — assemble InitReport ───────────────────────────────
408
595
  orchTranscript.record({
409
596
  kind: "pipeline-end",
410
597
  ts: new Date().toISOString(),
411
598
  outcome: "complete",
412
- elapsedMs: pipelineEndMs,
599
+ elapsedMs: Date.now() - pipelineStartMs,
413
600
  });
414
- const stack = typeof configCache.project?.stack === "string"
415
- ? configCache.project.stack
601
+ const finalCache = state.configCache;
602
+ const stack = typeof finalCache.project?.stack === "string"
603
+ ? finalCache.project.stack
416
604
  : undefined;
417
- const skillMatches = Array.isArray(configCache.installedSkills)
418
- ? configCache.installedSkills
605
+ const skillMatches = Array.isArray(finalCache.installedSkills)
606
+ ? finalCache.installedSkills
419
607
  : undefined;
420
- // If Phase 4 didn't run (resume at Phase 1–3), derive kbPathFinal from configCache.
608
+ // If Phase 4 didn't run (resume at phase 1–3), derive kbPathFinal from configCache.
609
+ let kbPathFinal = state.kbPathFinal;
421
610
  if (!kbPathFinal) {
422
- const p = configCache.paths;
611
+ const p = finalCache.paths;
423
612
  if (p && typeof p.engineering === "string" && p.engineering) {
424
613
  kbPathFinal = p.engineering;
425
614
  }