@entelligentsia/forgecli 1.0.2 → 1.0.10

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 (423) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +2 -1
  3. package/dist/CHANGELOG-forge-plugin.md +124 -0
  4. package/dist/CHANGELOG-pi.md +94 -0
  5. package/dist/extensions/forgecli/audience-gate.js +1 -1
  6. package/dist/extensions/forgecli/audience-gate.js.map +1 -1
  7. package/dist/extensions/forgecli/fix-bug.d.ts +1 -2
  8. package/dist/extensions/forgecli/fix-bug.js +678 -609
  9. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  10. package/dist/extensions/forgecli/forge-artifact-tool.js +42 -7
  11. package/dist/extensions/forgecli/forge-artifact-tool.js.map +1 -1
  12. package/dist/extensions/forgecli/forge-subagent.d.ts +17 -0
  13. package/dist/extensions/forgecli/forge-subagent.js +31 -12
  14. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  15. package/dist/extensions/forgecli/forge-tools.d.ts +6 -0
  16. package/dist/extensions/forgecli/forge-tools.js +71 -8
  17. package/dist/extensions/forgecli/forge-tools.js.map +1 -1
  18. package/dist/extensions/forgecli/run-task.js +461 -391
  19. package/dist/extensions/forgecli/run-task.js.map +1 -1
  20. package/dist/extensions/forgecli/session-registry.d.ts +12 -0
  21. package/dist/extensions/forgecli/session-registry.js +23 -0
  22. package/dist/extensions/forgecli/session-registry.js.map +1 -1
  23. package/dist/extensions/forgecli/subagent/caller-context.d.ts +35 -11
  24. package/dist/extensions/forgecli/subagent/caller-context.js +49 -21
  25. package/dist/extensions/forgecli/subagent/caller-context.js.map +1 -1
  26. package/dist/extensions/forgecli/subagent/orchestrator-transcript.d.ts +66 -0
  27. package/dist/extensions/forgecli/subagent/orchestrator-transcript.js +66 -0
  28. package/dist/extensions/forgecli/subagent/orchestrator-transcript.js.map +1 -0
  29. package/dist/extensions/forgecli/subagent/phase-guard.d.ts +34 -0
  30. package/dist/extensions/forgecli/subagent/phase-guard.js +149 -0
  31. package/dist/extensions/forgecli/subagent/phase-guard.js.map +1 -0
  32. package/dist/extensions/forgecli/subagent/phase-summary-map.d.ts +2 -0
  33. package/dist/extensions/forgecli/subagent/phase-summary-map.js +39 -0
  34. package/dist/extensions/forgecli/subagent/phase-summary-map.js.map +1 -0
  35. package/dist/extensions/forgecli/thread-switcher.js +2 -2
  36. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  37. package/dist/extensions/forgecli/viewport-events.d.ts +4 -0
  38. package/dist/extensions/forgecli/viewport-events.js +18 -1
  39. package/dist/extensions/forgecli/viewport-events.js.map +1 -1
  40. package/dist/extensions/forgecli/viewport-renderer.d.ts +12 -2
  41. package/dist/extensions/forgecli/viewport-renderer.js +8 -6
  42. package/dist/extensions/forgecli/viewport-renderer.js.map +1 -1
  43. package/dist/forge-payload/.base-pack/workflows/_fragments/store-cli-verbs.md +18 -3
  44. package/dist/forge-payload/.base-pack/workflows/architect_approve.md +4 -5
  45. package/dist/forge-payload/.base-pack/workflows/collator_agent.md +1 -1
  46. package/dist/forge-payload/.base-pack/workflows/commit_task.md +2 -3
  47. package/dist/forge-payload/.base-pack/workflows/fix_bug.md +10 -28
  48. package/dist/forge-payload/.base-pack/workflows/implement_plan.md +3 -2
  49. package/dist/forge-payload/.base-pack/workflows/orchestrate_task.md +41 -47
  50. package/dist/forge-payload/.base-pack/workflows/triage.md +190 -0
  51. package/dist/forge-payload/.base-pack/workflows/validate_task.md +2 -3
  52. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  53. package/dist/forge-payload/.schemas/_defs/locator.schema.json +13 -0
  54. package/dist/forge-payload/.schemas/bug.schema.json +1 -0
  55. package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
  56. package/dist/forge-payload/.schemas/migrations.json +72 -0
  57. package/dist/forge-payload/.schemas/sprint.schema.json +1 -0
  58. package/dist/forge-payload/.schemas/task.schema.json +1 -0
  59. package/dist/forge-payload/integrity.json +3 -3
  60. package/dist/forge-payload/meta/fragments/tool-discipline.md +21 -2
  61. package/dist/forge-payload/meta/workflows/_fragments/store-cli-verbs.md +18 -3
  62. package/dist/forge-payload/meta/workflows/meta-approve.md +4 -5
  63. package/dist/forge-payload/meta/workflows/meta-bug-triage.md +210 -0
  64. package/dist/forge-payload/meta/workflows/meta-collate.md +1 -1
  65. package/dist/forge-payload/meta/workflows/meta-commit.md +2 -3
  66. package/dist/forge-payload/meta/workflows/meta-fix-bug.md +10 -28
  67. package/dist/forge-payload/meta/workflows/meta-implement.md +3 -2
  68. package/dist/forge-payload/meta/workflows/meta-orchestrate.md +41 -47
  69. package/dist/forge-payload/meta/workflows/meta-validate.md +2 -3
  70. package/dist/forge-payload/schemas/_defs/locator.schema.json +13 -0
  71. package/dist/forge-payload/schemas/bug.schema.json +1 -0
  72. package/dist/forge-payload/schemas/enum-catalog.json +2 -2
  73. package/dist/forge-payload/schemas/sprint.schema.json +1 -0
  74. package/dist/forge-payload/schemas/structure-manifest.json +22 -2
  75. package/dist/forge-payload/schemas/task.schema.json +1 -0
  76. package/dist/forge-payload/tools/artifact-store.cjs +242 -0
  77. package/dist/forge-payload/tools/artifact.cjs +69 -100
  78. package/dist/forge-payload/tools/lib/artifact-kinds.cjs +95 -0
  79. package/dist/forge-payload/tools/lib/store-nlp.cjs +6 -0
  80. package/dist/forge-payload/tools/lib/store-query-exec.cjs +39 -5
  81. package/dist/forge-payload/tools/lib/suggest.cjs +2 -1
  82. package/dist/forge-payload/tools/preflight-gate.cjs +55 -5
  83. package/dist/forge-payload/tools/store-cli.cjs +50 -15
  84. package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts +5 -2
  85. package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
  86. package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js +81 -18
  87. package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
  88. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  89. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js +1 -0
  90. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js.map +1 -1
  91. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
  92. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +19 -24
  93. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
  94. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts +1 -0
  95. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts.map +1 -1
  96. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +14 -1
  97. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js.map +1 -1
  98. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +22 -8
  99. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  100. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
  101. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  102. package/node_modules/@earendil-works/pi-ai/README.md +1 -1
  103. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +374 -122
  104. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  105. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +424 -232
  106. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
  107. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts +1 -1
  108. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
  109. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +38 -2
  110. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  111. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  112. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +21 -12
  113. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
  114. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  115. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +6 -10
  116. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  117. package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
  118. package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js +1 -1
  119. package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js.map +1 -1
  120. package/node_modules/@earendil-works/pi-ai/dist/providers/google.d.ts.map +1 -1
  121. package/node_modules/@earendil-works/pi-ai/dist/providers/google.js +5 -3
  122. package/node_modules/@earendil-works/pi-ai/dist/providers/google.js.map +1 -1
  123. package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.d.ts.map +1 -1
  124. package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.js +3 -4
  125. package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.js.map +1 -1
  126. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.d.ts.map +1 -1
  127. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.js +2 -3
  128. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.js.map +1 -1
  129. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  130. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +159 -78
  131. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  132. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  133. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +16 -11
  134. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
  135. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
  136. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js +4 -1
  137. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  138. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  139. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +6 -10
  140. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js.map +1 -1
  141. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  142. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +1 -0
  143. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js.map +1 -1
  144. package/node_modules/@earendil-works/pi-ai/dist/stream.d.ts.map +1 -1
  145. package/node_modules/@earendil-works/pi-ai/dist/stream.js +14 -2
  146. package/node_modules/@earendil-works/pi-ai/dist/stream.js.map +1 -1
  147. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +14 -4
  148. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  149. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  150. package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.d.ts +6 -0
  151. package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.d.ts.map +1 -0
  152. package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.js +34 -0
  153. package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.js.map +1 -0
  154. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.d.ts +9 -7
  155. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.d.ts.map +1 -1
  156. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js +8 -7
  157. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js.map +1 -1
  158. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  159. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +1 -1
  160. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  161. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.d.ts +1 -1
  162. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.d.ts.map +1 -1
  163. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.js +1 -1
  164. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.js.map +1 -1
  165. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.d.ts +10 -1
  166. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.d.ts.map +1 -1
  167. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +179 -79
  168. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js.map +1 -1
  169. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  170. package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +94 -0
  171. package/node_modules/@earendil-works/pi-coding-agent/README.md +9 -0
  172. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.d.ts +3 -0
  173. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  174. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js +27 -0
  175. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js.map +1 -1
  176. package/node_modules/@earendil-works/pi-coding-agent/dist/config.d.ts.map +1 -1
  177. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +15 -2
  178. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js.map +1 -1
  179. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.d.ts +1 -0
  180. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.d.ts.map +1 -1
  181. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.js +1 -0
  182. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.js.map +1 -1
  183. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +5 -1
  184. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  185. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +28 -4
  186. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  187. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  188. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +18 -24
  189. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  190. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts +1 -1
  191. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  192. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js +8 -2
  193. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  194. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts +7 -5
  195. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  196. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  197. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  198. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +65 -13
  199. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  200. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  201. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +1 -1
  202. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  203. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.d.ts +9 -1
  204. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -1
  205. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.js +134 -11
  206. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -1
  207. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.d.ts +2 -0
  208. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  209. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js +10 -6
  210. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js.map +1 -1
  211. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.d.ts +6 -7
  212. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  213. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +75 -28
  214. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  215. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts +2 -0
  216. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  217. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +14 -9
  218. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  219. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  220. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js +0 -3
  221. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  222. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  223. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js +7 -10
  224. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  225. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  226. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  227. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -1
  228. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
  229. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -1
  230. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js +5 -7
  231. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js.map +1 -1
  232. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  233. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +6 -7
  234. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  235. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/render-utils.d.ts +5 -2
  236. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
  237. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/render-utils.js +17 -1
  238. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
  239. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  240. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js +5 -6
  241. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  242. package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts +2 -0
  243. package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts.map +1 -1
  244. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +2 -0
  245. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js.map +1 -1
  246. package/node_modules/@earendil-works/pi-coding-agent/dist/main.d.ts.map +1 -1
  247. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +69 -16
  248. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js.map +1 -1
  249. package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.d.ts.map +1 -1
  250. package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js +118 -1
  251. package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js.map +1 -1
  252. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +1 -3
  253. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  254. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +2 -4
  255. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  256. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  257. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js +1 -1
  258. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  259. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  260. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  261. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +59 -6
  262. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  263. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  264. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
  265. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  266. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  267. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +3 -1
  268. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  269. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
  270. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  271. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  272. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/deprecation.d.ts +4 -0
  273. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/deprecation.d.ts.map +1 -0
  274. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/deprecation.js +13 -0
  275. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/deprecation.js.map +1 -0
  276. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/json.d.ts +3 -0
  277. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/json.d.ts.map +1 -0
  278. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/json.js +7 -0
  279. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/json.js.map +1 -0
  280. package/node_modules/@earendil-works/pi-coding-agent/docs/custom-provider.md +13 -10
  281. package/node_modules/@earendil-works/pi-coding-agent/docs/development.md +1 -1
  282. package/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md +12 -6
  283. package/node_modules/@earendil-works/pi-coding-agent/docs/models.md +25 -12
  284. package/node_modules/@earendil-works/pi-coding-agent/docs/providers.md +13 -5
  285. package/node_modules/@earendil-works/pi-coding-agent/docs/quickstart.md +1 -0
  286. package/node_modules/@earendil-works/pi-coding-agent/docs/rpc.md +2 -1
  287. package/node_modules/@earendil-works/pi-coding-agent/docs/sdk.md +6 -0
  288. package/node_modules/@earendil-works/pi-coding-agent/docs/session-format.md +1 -1
  289. package/node_modules/@earendil-works/pi-coding-agent/docs/sessions.md +8 -0
  290. package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +7 -3
  291. package/node_modules/@earendil-works/pi-coding-agent/docs/terminal-setup.md +2 -0
  292. package/node_modules/@earendil-works/pi-coding-agent/docs/tui.md +2 -2
  293. package/node_modules/@earendil-works/pi-coding-agent/docs/usage.md +9 -0
  294. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/README.md +1 -0
  295. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/index.ts +1 -1
  296. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
  297. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/index.ts +54 -3
  298. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  299. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/git-merge-and-resolve.ts +115 -0
  300. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/input-transform-streaming.ts +39 -0
  301. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
  302. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
  303. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +443 -61
  304. package/node_modules/@earendil-works/pi-coding-agent/package.json +6 -6
  305. package/node_modules/@earendil-works/pi-tui/README.md +2 -2
  306. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  307. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +24 -83
  308. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  309. package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts.map +1 -1
  310. package/node_modules/@earendil-works/pi-tui/dist/components/input.js +7 -55
  311. package/node_modules/@earendil-works/pi-tui/dist/components/input.js.map +1 -1
  312. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts +7 -1
  313. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +1 -1
  314. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +12 -2
  315. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +1 -1
  316. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +1 -1
  317. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
  318. package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
  319. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +1 -1
  320. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +1 -1
  321. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +34 -7
  322. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +1 -1
  323. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +33 -10
  324. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  325. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +172 -37
  326. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  327. package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts +6 -1
  328. package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts.map +1 -1
  329. package/node_modules/@earendil-works/pi-tui/dist/utils.js +27 -15
  330. package/node_modules/@earendil-works/pi-tui/dist/utils.js.map +1 -1
  331. package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts +25 -0
  332. package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts.map +1 -0
  333. package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js +96 -0
  334. package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js.map +1 -0
  335. package/node_modules/@earendil-works/pi-tui/package.json +2 -2
  336. package/node_modules/@entelligentsia/forge-compress/LICENSE +21 -0
  337. package/node_modules/@entelligentsia/forge-compress/README.md +85 -0
  338. package/node_modules/@entelligentsia/forge-compress/dist/compressor.d.ts +6 -0
  339. package/node_modules/@entelligentsia/forge-compress/dist/compressor.js +137 -0
  340. package/node_modules/@entelligentsia/forge-compress/dist/entropy.d.ts +3 -0
  341. package/node_modules/@entelligentsia/forge-compress/dist/entropy.js +99 -0
  342. package/node_modules/@entelligentsia/forge-compress/dist/forge/entity.d.ts +8 -0
  343. package/node_modules/@entelligentsia/forge-compress/dist/forge/entity.js +149 -0
  344. package/node_modules/@entelligentsia/forge-compress/dist/forge/index.d.ts +7 -0
  345. package/node_modules/@entelligentsia/forge-compress/dist/forge/index.js +4 -0
  346. package/node_modules/@entelligentsia/forge-compress/dist/forge/markdown.d.ts +5 -0
  347. package/node_modules/@entelligentsia/forge-compress/dist/forge/markdown.js +92 -0
  348. package/node_modules/@entelligentsia/forge-compress/dist/forge/query.d.ts +7 -0
  349. package/node_modules/@entelligentsia/forge-compress/dist/forge/query.js +60 -0
  350. package/node_modules/@entelligentsia/forge-compress/dist/forge/validate.d.ts +1 -0
  351. package/node_modules/@entelligentsia/forge-compress/dist/forge/validate.js +82 -0
  352. package/node_modules/@entelligentsia/forge-compress/dist/index.d.ts +6 -0
  353. package/node_modules/@entelligentsia/forge-compress/dist/index.js +5 -0
  354. package/node_modules/@entelligentsia/forge-compress/dist/progressive.d.ts +1 -0
  355. package/node_modules/@entelligentsia/forge-compress/dist/progressive.js +108 -0
  356. package/node_modules/@entelligentsia/forge-compress/dist/strip.d.ts +4 -0
  357. package/node_modules/@entelligentsia/forge-compress/dist/strip.js +55 -0
  358. package/node_modules/@entelligentsia/forge-compress/dist/tokens.d.ts +2 -0
  359. package/node_modules/@entelligentsia/forge-compress/dist/tokens.js +17 -0
  360. package/node_modules/@entelligentsia/forge-compress/package.json +45 -0
  361. package/node_modules/@entelligentsia/forge-compress/src/__tests__/compress.test.ts +409 -0
  362. package/node_modules/@entelligentsia/forge-compress/src/compressor.ts +147 -0
  363. package/node_modules/@entelligentsia/forge-compress/src/entropy.ts +105 -0
  364. package/node_modules/@entelligentsia/forge-compress/src/forge/entity.ts +184 -0
  365. package/node_modules/@entelligentsia/forge-compress/src/forge/index.ts +10 -0
  366. package/node_modules/@entelligentsia/forge-compress/src/forge/markdown.ts +122 -0
  367. package/node_modules/@entelligentsia/forge-compress/src/forge/query.ts +105 -0
  368. package/node_modules/@entelligentsia/forge-compress/src/forge/validate.ts +86 -0
  369. package/node_modules/@entelligentsia/forge-compress/src/index.ts +22 -0
  370. package/node_modules/@entelligentsia/forge-compress/src/progressive.ts +123 -0
  371. package/node_modules/@entelligentsia/forge-compress/src/strip.ts +58 -0
  372. package/node_modules/@entelligentsia/forge-compress/src/tokens.ts +19 -0
  373. package/node_modules/@mariozechner/clipboard/Cargo.toml +3 -3
  374. package/node_modules/@mariozechner/clipboard/index.d.ts +34 -20
  375. package/node_modules/@mariozechner/clipboard/index.js +546 -257
  376. package/node_modules/@mariozechner/clipboard/package.json +5 -6
  377. package/node_modules/@mariozechner/clipboard/package.json.prepack-backup +14 -14
  378. package/node_modules/@mariozechner/clipboard/src/lib.rs +4 -9
  379. package/node_modules/@mariozechner/clipboard-linux-x64-gnu/clipboard.linux-x64-gnu.node +0 -0
  380. package/node_modules/@mariozechner/clipboard-linux-x64-gnu/package.json +2 -2
  381. package/package.json +11 -16
  382. package/dist/forge-payload/.base-pack/commands/quiz-agent.md +0 -6
  383. package/dist/forge-payload/.base-pack/commands/retrospective.md +0 -6
  384. package/dist/forge-payload/.base-pack/commands/sprint-intake.md +0 -6
  385. package/dist/forge-payload/.base-pack/commands/sprint-plan.md +0 -6
  386. package/dist/forge-payload/commands/calibrate.md +0 -10
  387. package/dist/forge-payload/commands/materialize.md +0 -119
  388. package/dist/forge-payload/commands/migrate.md +0 -12
  389. package/dist/forge-payload/commands/quiz-agent.md +0 -6
  390. package/dist/forge-payload/commands/regenerate.md +0 -6
  391. package/dist/forge-payload/commands/store-query.md +0 -6
  392. package/dist/forge-payload/commands/store-repair.md +0 -6
  393. package/dist/forge-payload/commands/update-tools.md +0 -10
  394. package/dist/forge-payload/meta/templates/meta-retrospective.md +0 -28
  395. package/dist/forge-payload/tools/prompts/sprint-plan-prompt.md +0 -70
  396. package/dist/forge-payload/tools/schemas/task-list.schema.json +0 -53
  397. package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.d.ts +0 -4
  398. package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.d.ts.map +0 -1
  399. package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.js +0 -3
  400. package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.js.map +0 -1
  401. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.d.ts +0 -20
  402. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.d.ts.map +0 -1
  403. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.js +0 -92
  404. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.js.map +0 -1
  405. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.d.ts +0 -18
  406. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.d.ts.map +0 -1
  407. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.js +0 -42
  408. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.js.map +0 -1
  409. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.d.ts +0 -10
  410. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.d.ts.map +0 -1
  411. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.js +0 -31
  412. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.js.map +0 -1
  413. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.d.ts +0 -30
  414. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.d.ts.map +0 -1
  415. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.js +0 -170
  416. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.js.map +0 -1
  417. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.d.ts +0 -26
  418. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.d.ts.map +0 -1
  419. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.js +0 -90
  420. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.js.map +0 -1
  421. package/node_modules/@mariozechner/clipboard-linux-x64-musl/README.md +0 -3
  422. package/node_modules/@mariozechner/clipboard-linux-x64-musl/clipboard.linux-x64-musl.node +0 -0
  423. package/node_modules/@mariozechner/clipboard-linux-x64-musl/package.json +0 -25
@@ -14,37 +14,26 @@
14
14
 
15
15
  const fs = require('fs');
16
16
  const path = require('path');
17
- const { execFileSync } = require('child_process');
18
17
  const { findProjectRoot } = require('./lib/project-root.cjs');
19
18
 
20
19
  // ── Artifact catalog ─────────────────────────────────────────────────────────
21
-
22
- const ARTIFACT_CATALOG = {
23
- 'plan': { filename: 'PLAN.md', type: 'md' },
24
- 'plan-review': { filename: 'PLAN_REVIEW.md', type: 'md' },
25
- 'progress': { filename: 'PROGRESS.md', type: 'md' },
26
- 'code-review': { filename: 'CODE_REVIEW.md', type: 'md' },
27
- 'validation-report': { filename: 'VALIDATION_REPORT.md', type: 'md' },
28
- 'architect-approval': { filename: 'ARCHITECT_APPROVAL.md', type: 'md' },
29
- 'triage': { filename: 'TRIAGE.md', type: 'md' },
30
- 'bug-report': { filename: 'BUG_REPORT.md', type: 'md' },
31
- 'index': { filename: 'INDEX.md', type: 'md' },
32
- 'cost-report': { filename: 'COST_REPORT.md', type: 'md' },
33
- 'timesheet': { filename: 'TIMESHEET.md', type: 'md' },
34
- 'plan-summary': { filename: 'PLAN-SUMMARY.json', type: 'json' },
35
- 'review-plan-summary': { filename: 'REVIEW-PLAN-SUMMARY.json', type: 'json' },
36
- 'implementation-summary': { filename: 'IMPLEMENTATION-SUMMARY.json', type: 'json' },
37
- 'review-code-summary': { filename: 'REVIEW-CODE-SUMMARY.json', type: 'json' },
38
- 'review-impl-summary': { filename: 'REVIEW-IMPL-SUMMARY.json', type: 'json' },
39
- 'validation-summary': { filename: 'VALIDATION-SUMMARY.json', type: 'json' },
40
- 'approve-summary': { filename: 'APPROVE-SUMMARY.json', type: 'json' },
41
- 'commit-summary': { filename: 'COMMIT-SUMMARY.json', type: 'json' },
42
- 'triage-summary': { filename: 'TRIAGE-SUMMARY.json', type: 'json' },
43
- 'writeback-summary': { filename: 'WRITEBACK-SUMMARY.json', type: 'json' },
44
- 'collation-summary': { filename: 'COLLATION-SUMMARY.json', type: 'json' },
45
- };
46
-
47
- const ARTIFACT_NAMES = Object.keys(ARTIFACT_CATALOG).sort();
20
+ //
21
+ // The catalog, bug-mode overrides, and filename resolution now live in the
22
+ // canonical registry at lib/artifact-kinds.cjs (ADR artifact-resolution Phase 1),
23
+ // consumed here and by store-cli.cjs so there is ONE source of truth.
24
+ const {
25
+ ARTIFACT_CATALOG,
26
+ ARTIFACT_FILENAME_OVERRIDES,
27
+ ARTIFACT_NAMES,
28
+ resolveArtifactFilename,
29
+ } = require('./lib/artifact-kinds.cjs');
30
+
31
+ // The provider seam (ArtifactStore/FsArtifactImpl) and entity-dir resolution
32
+ // live in artifact-store.cjs (ADR Phase 3). This CLI is a thin layer over the
33
+ // facade: it owns arg parsing, @-file expansion, JSON validation, and display;
34
+ // all filesystem access goes through the provider.
35
+ const artifactStore = require('./artifact-store.cjs');
36
+ const { ArtifactStore, FsArtifactImpl, resolveEntityDir } = artifactStore;
48
37
 
49
38
  // ── Summary JSON validation ──────────────────────────────────────────────────
50
39
 
@@ -66,66 +55,6 @@ function validateSummaryJson(content) {
66
55
  return null;
67
56
  }
68
57
 
69
- // ── Entity path resolution ───────────────────────────────────────────────────
70
-
71
- /** Read a store record via store-cli and return its `path` field, or null on failure. */
72
- function readStorePath(entity, entityId, toolDir, projectRoot) {
73
- const cliPath = path.join(toolDir, 'store-cli.cjs');
74
- try {
75
- const result = execFileSync('node', [cliPath, 'read', entity, entityId, '--json'], {
76
- cwd: projectRoot,
77
- encoding: 'utf8',
78
- timeout: 10_000,
79
- });
80
- const record = JSON.parse(result);
81
- if (typeof record.path === 'string' && record.path.length > 0) {
82
- // Defensive: if the path ends with a file extension, the store record
83
- // was written with a filename (e.g. "…/PROGRESS.md") instead of the
84
- // directory. Strip the trailing filename to get the entity directory.
85
- const p = record.path;
86
- if (/\.(md|json)$/i.test(p)) return path.dirname(p);
87
- return p;
88
- }
89
- } catch (_) {
90
- // Store unavailable or record not found — fall through.
91
- }
92
- return null;
93
- }
94
-
95
- /**
96
- * Resolve entity directory using the store record's `path` field when available,
97
- * falling back to ID-only construction.
98
- */
99
- function resolveEntityDir(entity, entityId, engineeringPath, toolDir, projectRoot) {
100
- switch (entity) {
101
- case 'bug': {
102
- const storePath = readStorePath('bug', entityId, toolDir, projectRoot);
103
- if (storePath) return storePath;
104
- return path.join(engineeringPath, 'bugs', entityId);
105
- }
106
- case 'sprint': {
107
- const storePath = readStorePath('sprint', entityId, toolDir, projectRoot);
108
- if (storePath) return storePath;
109
- return path.join(engineeringPath, 'sprints', entityId);
110
- }
111
- case 'task': {
112
- const storePath = readStorePath('task', entityId, toolDir, projectRoot);
113
- if (storePath) return storePath;
114
- // Fallback: derive from sprint prefix + sprint record path.
115
- const match = entityId.match(/^(.+-S\d+)-T\d+$/);
116
- if (!match) return null;
117
- const sprintId = match[1];
118
- const sprintPath = readStorePath('sprint', sprintId, toolDir, projectRoot);
119
- if (sprintPath) {
120
- return path.join(sprintPath, entityId);
121
- }
122
- return path.join(engineeringPath, 'sprints', sprintId, entityId);
123
- }
124
- default:
125
- return null;
126
- }
127
- }
128
-
129
58
  // ── CLI ───────────────────────────────────────────────────────────────────────
130
59
 
131
60
  if (require.main === module) {
@@ -141,6 +70,9 @@ if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
141
70
  ' read <entity> <entityId> <artifact> Read artifact content',
142
71
  ' write <entity> <entityId> <artifact> <content|@file>',
143
72
  ' Write artifact (content or @/path/to/file)',
73
+ ' exists <entity> <entityId> <artifact> Exit 0 if present, 2 if absent',
74
+ ' url <entity> <entityId> <artifact> Print the backend URL (file:// for fs)',
75
+ ' delete <entity> <entityId> <artifact> Delete an artifact',
144
76
  '',
145
77
  'Entities: task, bug, sprint',
146
78
  `Known artifacts: ${ARTIFACT_NAMES.join(', ')}`,
@@ -189,6 +121,14 @@ if (!entityDir) {
189
121
 
190
122
  const absDir = path.resolve(projectRoot, entityDir);
191
123
 
124
+ // Provider instance — all filesystem access for read/write/exists/url/delete
125
+ // goes through the facade (ADR Phase 3). Wired with the already-resolved dir so
126
+ // the CLI and the provider agree on location without a second store read.
127
+ const store = new ArtifactStore(new FsArtifactImpl({
128
+ projectRoot, engineeringPath, toolDir,
129
+ resolveDir: () => entityDir,
130
+ }));
131
+
192
132
  // ── list ────────────────────────────────────────────────────────────────────
193
133
 
194
134
  if (subcmd === 'list') {
@@ -200,7 +140,13 @@ if (subcmd === 'list') {
200
140
  const known = [];
201
141
  const other = [];
202
142
  for (const f of files) {
203
- const catalogEntry = Object.entries(ARTIFACT_CATALOG).find(([, v]) => v.filename === f);
143
+ // Recognise the per-entity overrides as well, so e.g. BUG_FIX_PLAN.md
144
+ // surfaces as the `plan` artifact in bug-mode listings.
145
+ const overrideEntry = ARTIFACT_FILENAME_OVERRIDES[entity]
146
+ ? Object.entries(ARTIFACT_FILENAME_OVERRIDES[entity]).find(([, fn]) => fn === f)
147
+ : undefined;
148
+ const catalogEntry = overrideEntry
149
+ || Object.entries(ARTIFACT_CATALOG).find(([, v]) => v.filename === f);
204
150
  if (catalogEntry) {
205
151
  known.push(` ${catalogEntry[0]} → ${f}`);
206
152
  } else {
@@ -233,16 +179,42 @@ if (!catalogEntry) {
233
179
  process.exit(1);
234
180
  }
235
181
 
236
- const filePath = path.join(absDir, catalogEntry.filename);
182
+ const resolvedFilename = resolveArtifactFilename(entity, artifactName);
183
+ const displayPath = path.join(entityDir, resolvedFilename);
184
+ const handle = { entity, entityId, kind: artifactName };
237
185
 
238
186
  // ── read ─────────────────────────────────────────────────────────────────────
239
187
 
240
188
  if (subcmd === 'read') {
241
- if (!fs.existsSync(filePath)) {
242
- process.stderr.write(`Artifact not found: ${path.join(entityDir, catalogEntry.filename)}\n`);
189
+ if (!store.exists(handle)) {
190
+ process.stderr.write(`Artifact not found: ${displayPath}\n`);
243
191
  process.exit(2);
244
192
  }
245
- process.stdout.write(fs.readFileSync(filePath, 'utf8'));
193
+ process.stdout.write(store.read(handle));
194
+ process.exit(0);
195
+ }
196
+
197
+ // ── exists ─────────────────────────────────────────────────────────────────────
198
+
199
+ if (subcmd === 'exists') {
200
+ // Boolean check: always exit 0 with a "true"/"false" token so callers
201
+ // (including the forge-cli subprocess surface) never treat absence as an error.
202
+ process.stdout.write(store.exists(handle) ? 'true\n' : 'false\n');
203
+ process.exit(0);
204
+ }
205
+
206
+ // ── url ────────────────────────────────────────────────────────────────────────
207
+
208
+ if (subcmd === 'url') {
209
+ process.stdout.write(store.url(handle) + '\n');
210
+ process.exit(0);
211
+ }
212
+
213
+ // ── delete ─────────────────────────────────────────────────────────────────────
214
+
215
+ if (subcmd === 'delete') {
216
+ const removed = store.delete(handle);
217
+ process.stdout.write(removed ? `Deleted ${displayPath}\n` : `Nothing to delete: ${displayPath}\n`);
246
218
  process.exit(0);
247
219
  }
248
220
 
@@ -272,22 +244,19 @@ if (subcmd === 'write') {
272
244
  const validationError = validateSummaryJson(content);
273
245
  if (validationError) {
274
246
  process.stderr.write(
275
- `Summary validation failed for ${catalogEntry.filename}: ${validationError}. ` +
247
+ `Summary validation failed for ${resolvedFilename}: ${validationError}. ` +
276
248
  `Required fields: ${SUMMARY_REQUIRED.join(', ')}.\n`
277
249
  );
278
250
  process.exit(1);
279
251
  }
280
252
  }
281
253
 
282
- fs.mkdirSync(absDir, { recursive: true });
283
- fs.writeFileSync(filePath, content, 'utf8');
284
- process.stdout.write(
285
- `Wrote ${Buffer.byteLength(content, 'utf8')} bytes to ${path.join(entityDir, catalogEntry.filename)}\n`
286
- );
254
+ const res = store.write(handle, content);
255
+ process.stdout.write(`Wrote ${res.bytes} bytes to ${displayPath}\n`);
287
256
  process.exit(0);
288
257
  }
289
258
 
290
- process.stderr.write(`Unknown subcommand: ${subcmd}. Valid: list, read, write\n`);
259
+ process.stderr.write(`Unknown subcommand: ${subcmd}. Valid: list, read, write, exists, url, delete\n`);
291
260
  process.exit(1);
292
261
 
293
262
  } // end if (require.main === module)
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ // ── Canonical artifact-kind registry ─────────────────────────────────────────
4
+ //
5
+ // Single source of truth for artifact filenames, bug-mode filename overrides,
6
+ // and the phase→kind mapping. Extracted from artifact.cjs per ADR
7
+ // `doc/decisions/artifact-resolution-abstraction.md` (Phase 1) so that
8
+ // artifact.cjs, store-cli.cjs (set-summary / set-bug-summary), and the
9
+ // forge-cli `forge_artifact` surface all consume ONE catalog instead of
10
+ // maintaining parallel copies.
11
+ //
12
+ // Renaming an artifact file is a one-line edit here.
13
+
14
+ const ARTIFACT_CATALOG = {
15
+ 'plan': { filename: 'PLAN.md', type: 'md' },
16
+ 'plan-review': { filename: 'PLAN_REVIEW.md', type: 'md' },
17
+ 'progress': { filename: 'PROGRESS.md', type: 'md' },
18
+ 'code-review': { filename: 'CODE_REVIEW.md', type: 'md' },
19
+ 'validation-report': { filename: 'VALIDATION_REPORT.md', type: 'md' },
20
+ 'architect-approval': { filename: 'ARCHITECT_APPROVAL.md', type: 'md' },
21
+ 'triage': { filename: 'TRIAGE.md', type: 'md' },
22
+ 'bug-report': { filename: 'BUG_REPORT.md', type: 'md' },
23
+ 'index': { filename: 'INDEX.md', type: 'md' },
24
+ 'task-prompt': { filename: 'TASK_PROMPT.md', type: 'md' },
25
+ 'sprint-requirements': { filename: 'SPRINT_REQUIREMENTS.md', type: 'md' },
26
+ 'sprint-completion-review': { filename: 'SPRINT_COMPLETION_REVIEW.md', type: 'md' },
27
+ 'cost-report': { filename: 'COST_REPORT.md', type: 'md' },
28
+ 'timesheet': { filename: 'TIMESHEET.md', type: 'md' },
29
+ 'plan-summary': { filename: 'PLAN-SUMMARY.json', type: 'json' },
30
+ 'review-plan-summary': { filename: 'REVIEW-PLAN-SUMMARY.json', type: 'json' },
31
+ 'implementation-summary': { filename: 'IMPLEMENTATION-SUMMARY.json', type: 'json' },
32
+ 'review-code-summary': { filename: 'REVIEW-CODE-SUMMARY.json', type: 'json' },
33
+ 'review-impl-summary': { filename: 'REVIEW-IMPL-SUMMARY.json', type: 'json' },
34
+ 'validation-summary': { filename: 'VALIDATION-SUMMARY.json', type: 'json' },
35
+ 'approve-summary': { filename: 'APPROVE-SUMMARY.json', type: 'json' },
36
+ 'commit-summary': { filename: 'COMMIT-SUMMARY.json', type: 'json' },
37
+ 'triage-summary': { filename: 'TRIAGE-SUMMARY.json', type: 'json' },
38
+ 'writeback-summary': { filename: 'WRITEBACK-SUMMARY.json', type: 'json' },
39
+ 'collation-summary': { filename: 'COLLATION-SUMMARY.json', type: 'json' },
40
+ };
41
+
42
+ // Per-entity filename overrides. Bug-mode plans and plan-summaries use the
43
+ // BUG_FIX_PLAN prefix to match the long-standing forge convention and the
44
+ // preflight-gate.cjs expectations for review-plan in bug mode. Without this
45
+ // override, plan-fix (routed via plan_task.md post FORGE-BUG-040) writes
46
+ // PLAN.md and review-plan preflight then fails "artifact missing:
47
+ // BUG_FIX_PLAN.md" — see FORGE-BUG-041.
48
+ const ARTIFACT_FILENAME_OVERRIDES = {
49
+ bug: {
50
+ 'plan': 'BUG_FIX_PLAN.md',
51
+ 'plan-summary': 'BUG-FIX-PLAN-SUMMARY.json',
52
+ },
53
+ };
54
+
55
+ // Phase → artifact-kind. Phases use underscore spellings (mirror of
56
+ // store-helpers VALID_SUMMARY_PHASES); kinds use the hyphenated catalog keys.
57
+ // This is the mapping set-summary / set-bug-summary use to self-resolve the
58
+ // sidecar JSON file from a record's path, so callers never hand-build paths.
59
+ const PHASE_TO_KIND = {
60
+ 'plan': 'plan-summary',
61
+ 'review_plan': 'review-plan-summary',
62
+ 'implementation': 'implementation-summary',
63
+ 'code_review': 'review-code-summary',
64
+ 'validation': 'validation-summary',
65
+ 'triage': 'triage-summary',
66
+ 'approve': 'approve-summary',
67
+ };
68
+
69
+ function resolveArtifactFilename(entity, artifactName) {
70
+ const override = ARTIFACT_FILENAME_OVERRIDES[entity];
71
+ if (override && override[artifactName]) return override[artifactName];
72
+ const entry = ARTIFACT_CATALOG[artifactName];
73
+ if (!entry) throw new Error(`unknown artifact kind: ${artifactName}`);
74
+ return entry.filename;
75
+ }
76
+
77
+ // Resolve the phase-summary sidecar filename for an entity. Applies the
78
+ // phase→kind map, then the per-entity filename override (so a bug's "plan"
79
+ // summary becomes BUG-FIX-PLAN-SUMMARY.json).
80
+ function resolveSummaryFilename(entity, phase) {
81
+ const kind = PHASE_TO_KIND[phase];
82
+ if (!kind) throw new Error(`unknown phase: ${phase}`);
83
+ return resolveArtifactFilename(entity, kind);
84
+ }
85
+
86
+ const ARTIFACT_NAMES = Object.keys(ARTIFACT_CATALOG).sort();
87
+
88
+ module.exports = {
89
+ ARTIFACT_CATALOG,
90
+ ARTIFACT_FILENAME_OVERRIDES,
91
+ ARTIFACT_NAMES,
92
+ PHASE_TO_KIND,
93
+ resolveArtifactFilename,
94
+ resolveSummaryFilename,
95
+ };
@@ -99,6 +99,12 @@ function parseIntentNLP(intent) {
99
99
  outer:
100
100
  for (let i = 0; i < words.length; i++) {
101
101
  if (consumed.has(i)) continue;
102
+ // "with <entity>" (and "<entity> for") are FK-follow directives handled in
103
+ // Stage 4 — they must NOT be treated as the primary entity. Otherwise an
104
+ // explicit anchored ID (e.g. "WI-S19-T04 with sprint with feature") loses
105
+ // its entity to the follow-word, its filter is stripped as invalid, and the
106
+ // query degrades into a full-store scan.
107
+ if (i > 0 && words[i - 1] === 'with') continue;
102
108
  const w = words[i];
103
109
  if (i + 1 < words.length) {
104
110
  const bigram = w + ' ' + words[i + 1];
@@ -5,6 +5,14 @@ const path = require('path');
5
5
  const { extractExcerpt, loadForgeConfig, findIndexPath } = require('./store-facade.cjs');
6
6
  const { extractKeywordsFromIntent } = require('./store-nlp.cjs');
7
7
 
8
+ // Default safety cap for NLP listings without an explicit limit. Prevents an
9
+ // unbounded query from dumping the entire store into an LLM's context (a single
10
+ // uncapped query consumed ~28% of a plan run's input tokens — see
11
+ // doc/plans/store-query-nlp-response-shape-improvement.md). Callers that need
12
+ // everything use the exact `--sprint`/`--task` flag paths (which bypass this) or
13
+ // pass an explicit limit.
14
+ const DEFAULT_NLP_LIMIT = 25;
15
+
8
16
  function escapeRe(s) {
9
17
  return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
10
18
  }
@@ -128,7 +136,7 @@ function executeQuery(plan, store, cfg) {
128
136
  const trace = [];
129
137
  const traverse = plan.traverse || plan;
130
138
  const singMap = { bug: 'bugs', task: 'tasks', sprint: 'sprints', feature: 'features' };
131
- const primary = singMap[traverse.primary] || traverse.primary || 'tasks';
139
+ let primary = singMap[traverse.primary] || traverse.primary || 'tasks';
132
140
  let filter = { ...(traverse.filter || {}) };
133
141
  const follow = traverse.follow || [];
134
142
  const kwMatch = traverse.keywordMatch || {};
@@ -138,6 +146,7 @@ function executeQuery(plan, store, cfg) {
138
146
 
139
147
  // Validate + strip invalid filters
140
148
  const validation = validatePlan(plan);
149
+ const stripped = {};
141
150
  if (validation.warnings.length > 0) {
142
151
  const validators = buildFieldValidators();
143
152
  const fields = validators[primary];
@@ -145,14 +154,17 @@ function executeQuery(plan, store, cfg) {
145
154
  for (const key of Object.keys(filter)) {
146
155
  if (!fields[key]) {
147
156
  trace.push(`stripped invalid filter key: ${key}`);
157
+ stripped[key] = filter[key];
148
158
  delete filter[key];
149
159
  } else {
150
160
  const spec = fields[key];
151
161
  if (spec instanceof RegExp && !spec.test(String(filter[key]))) {
152
162
  trace.push(`stripped filter ${key}="${filter[key]}" (value mismatch)`);
163
+ stripped[key] = filter[key];
153
164
  delete filter[key];
154
165
  } else if (Array.isArray(spec) && !spec.includes(String(filter[key]))) {
155
166
  trace.push(`stripped filter ${key}="${filter[key]}" (not in enum)`);
167
+ stripped[key] = filter[key];
156
168
  delete filter[key];
157
169
  }
158
170
  }
@@ -160,6 +172,21 @@ function executeQuery(plan, store, cfg) {
160
172
  }
161
173
  }
162
174
 
175
+ // Re-route on a stripped record-anchoring ID: an explicit taskId/bugId that
176
+ // was stripped because `primary` disagreed means the caller named a specific
177
+ // record. Honour it (re-route primary + restore the filter) instead of
178
+ // degrading into a full-collection scan. This is the engine-layer floor that
179
+ // backs up the parser fix in store-nlp.cjs.
180
+ const ID_ROUTE = { taskId: 'tasks', bugId: 'bugs' };
181
+ for (const idKey of Object.keys(ID_ROUTE)) {
182
+ if (stripped[idKey] !== undefined && primary !== ID_ROUTE[idKey]) {
183
+ trace.push(`re-routed primary ${primary} → ${ID_ROUTE[idKey]} (anchored ${idKey}="${stripped[idKey]}")`);
184
+ primary = ID_ROUTE[idKey];
185
+ filter[idKey] = stripped[idKey];
186
+ break;
187
+ }
188
+ }
189
+
163
190
  // List primary entities
164
191
  let entities;
165
192
  switch (primary) {
@@ -201,10 +228,16 @@ function executeQuery(plan, store, cfg) {
201
228
  return { query: cfg.query || '', path: 'intent-nlp', traversalTrace: trace, count: totalMatched, results: [], relatedFileRefs: [], totalMatched };
202
229
  }
203
230
 
204
- // Limit
205
- if (traverse.limit && entities.length > traverse.limit) {
206
- trace.push(`limited to ${traverse.limit} (of ${totalMatched})`);
207
- entities = entities.slice(0, traverse.limit);
231
+ // Limit — honour an explicit limit; otherwise apply a default safety cap so an
232
+ // unbounded NLP listing never dumps the whole collection into the model's
233
+ // context. `truncated` + `totalMatched` let the caller detect and, if needed,
234
+ // re-query with an explicit limit or the exact-flag path.
235
+ const effectiveLimit = traverse.limit || DEFAULT_NLP_LIMIT;
236
+ let truncated = false;
237
+ if (entities.length > effectiveLimit) {
238
+ trace.push(`limited to ${effectiveLimit} (of ${totalMatched})${traverse.limit ? '' : ' [default cap]'}`);
239
+ entities = entities.slice(0, effectiveLimit);
240
+ truncated = true;
208
241
  }
209
242
 
210
243
  // Build results + follow FKs
@@ -263,6 +296,7 @@ function executeQuery(plan, store, cfg) {
263
296
  results: allResults,
264
297
  totalMatched,
265
298
  returned: allResults.length,
299
+ truncated,
266
300
  limit: traverse.limit || null,
267
301
  sort: traverse.sort || null,
268
302
  relatedFileRefs: [...new Set(relatedFiles)],
@@ -90,7 +90,8 @@ const DRIFT_MAP = {
90
90
  'task-completed': ['task-committed'],
91
91
  'implemented': ['implementing'],
92
92
  'set': ['set-summary'],
93
- 'start': ['startTimestamp']
93
+ 'start': ['startTimestamp'],
94
+ 'create': ['write']
94
95
  };
95
96
 
96
97
  // ---------------------------------------------------------------------------
@@ -113,7 +113,49 @@ function describePredicate(pred) {
113
113
  return `${pred.field} ${pred.op} ${pred.value}`;
114
114
  }
115
115
 
116
- module.exports = { preflight, resolveTaskArtifactDir, resolveSprintArtifactDir };
116
+ module.exports = { preflight, resolveTaskArtifactDir, resolveSprintArtifactDir, deriveSprintTaskFromArtifactPath };
117
+
118
+ // Derive {sprint} and {task} substitutions directly from a task's artifact path
119
+ // (task.path) when that path is a directory under `<engineeringRoot>/sprints/`.
120
+ //
121
+ // task.path is the authoritative artifact location — it is where the plan /
122
+ // implement phases actually write PLAN.md, REVIEW.md, etc. Splitting it tolerates
123
+ // ANY nesting depth and ANY on-disk naming, so a single rule covers both the
124
+ // modern 2-level layout (`sprints/<sprint>/<task>/`) and legacy 3-level layouts
125
+ // (`sprints/<sprint>/tasks/<task>/`) whose on-disk sprint dir name need not equal
126
+ // sprintId (e.g. sprintId "S19" → dir "sprint_19_platform_ux_improvements"). The
127
+ // directory-scan resolution (resolveSprintArtifactDir + resolveTaskArtifactDir)
128
+ // cannot express the `tasks/` nesting, which is what broke review-plan preflight
129
+ // for heterogeneous-layout projects (walkinto.in S19/S32/S33).
130
+ //
131
+ // {task} = the last path segment (the artifact directory name)
132
+ // {sprint} = everything between `sprints/` and that last segment, joined by "/"
133
+ //
134
+ // File-vs-dir guard: if task.path ends in a filename (has an extension), the
135
+ // artifact directory is its PARENT — strip the filename first. This prevents the
136
+ // bogus `.../TASK_PROMPT.md/PLAN.md` (ENOTDIR) seen when a project stores
137
+ // task.path as a file inside the artifact dir.
138
+ //
139
+ // Returns null when the path is absent, or not under `<engineeringRoot>/sprints/`
140
+ // (e.g. a forge/ source-file path — see regression S12-T06), or resolves to
141
+ // fewer than 2 directory segments. In every null case the caller falls back to
142
+ // the directory-scan resolution, preserving prior behaviour.
143
+ function deriveSprintTaskFromArtifactPath(taskPath, engineeringRoot) {
144
+ if (!taskPath) return null;
145
+ let norm = String(taskPath).replace(/\\/g, '/').replace(/\/+$/, '');
146
+ // File-vs-dir guard — drop a trailing filename so we split on the dir.
147
+ if (/\.[a-zA-Z0-9]+$/.test(norm)) {
148
+ norm = norm.replace(/\/[^/]*$/, '');
149
+ }
150
+ const er = String(engineeringRoot || 'engineering').replace(/\/+$/, '');
151
+ const prefix = er + '/sprints/';
152
+ if (!norm.startsWith(prefix)) return null;
153
+ const segs = norm.slice(prefix.length).split('/').filter(Boolean);
154
+ if (segs.length < 2) return null;
155
+ const task = segs[segs.length - 1];
156
+ const sprint = segs.slice(0, -1).join('/');
157
+ return { sprint, task };
158
+ }
117
159
 
118
160
  // CLI shim: `node preflight-gate.cjs --phase <name> --task <taskId> [--bug <bugId>] [--workflow <name>]`
119
161
  // exit codes: 0 ok, 1 gate(s) failed, 2 invalid args / missing definitions
@@ -246,10 +288,18 @@ if (require.main === module) {
246
288
  // name is used both as the {sprint} substitution and as the directory in
247
289
  // which resolveTaskArtifactDir scans for task subdirectories.
248
290
  const sprintId = taskRecord ? taskRecord.sprintId : undefined;
249
- const resolvedSprintDir = sprintId
250
- ? (resolveSprintArtifactDir(sprintId, engineeringRoot) || sprintId)
251
- : undefined;
252
- const taskArtifactDir = resolveTaskArtifactDir(taskRecord, engineeringRoot, undefined, resolvedSprintDir);
291
+ // Primary resolution: derive {sprint}/{task} straight from task.path (the
292
+ // authoritative artifact directory). This honours arbitrary layout nesting —
293
+ // including legacy 3-level `sprints/<sprint>/tasks/<task>/` trees the
294
+ // directory-scan resolution below cannot express. Falls back to the scan when
295
+ // task.path is not an artifact dir under sprints/ (e.g. a forge/ source file).
296
+ const derived = taskRecord ? deriveSprintTaskFromArtifactPath(taskRecord.path, engineeringRoot) : null;
297
+ const resolvedSprintDir = derived
298
+ ? derived.sprint
299
+ : (sprintId ? (resolveSprintArtifactDir(sprintId, engineeringRoot) || sprintId) : undefined);
300
+ const taskArtifactDir = derived
301
+ ? derived.task
302
+ : resolveTaskArtifactDir(taskRecord, engineeringRoot, undefined, resolvedSprintDir);
253
303
  // Fallback: if task.path points to a file (e.g., TASK_PROMPT.md) we want
254
304
  // the *directory* segment, not the filename. Without this, a project that
255
305
  // stores task.path as the prompt file would propagate "TASK_PROMPT.md" as
@@ -55,6 +55,7 @@ const { validateRecord, NULLABLE_FIELDS } = require('./lib/validate.js');
55
55
 
56
56
  // FORGE-S22-T03: suggestion engine for "Did you mean?" on validation/transition errors
57
57
  const { suggest, suggestEntityType, formatSuggestion } = require('./lib/suggest.cjs');
58
+ const { resolveSummaryFilename, PHASE_TO_KIND } = require('./lib/artifact-kinds.cjs');
58
59
 
59
60
  // Valid phase keys for summaries (dot-delimited → underscore in JSON key)
60
61
  const VALID_SUMMARY_PHASES = new Set(['plan', 'review_plan', 'implementation', 'code_review', 'validation', 'triage', 'approve']);
@@ -432,8 +433,8 @@ Commands:
432
433
  progress-clear <sprintOrBugId> Clear (truncate) the progress log
433
434
  write-collation-state '<json>' Write COLLATION_STATE.json
434
435
  validate <entity> '<json>' Validate against schema without writing
435
- set-summary <taskId> <phase> <jsonFile> Set a phase summary on a task record
436
- set-bug-summary <bugId> <phase> <jsonFile> Set a phase summary on a bug record
436
+ set-summary <taskId> <phase> [<jsonFile>] Set a phase summary on a task record (sidecar auto-resolved from record.path when jsonFile omitted)
437
+ set-bug-summary <bugId> <phase> [<jsonFile>] Set a phase summary on a bug record (sidecar auto-resolved from record.path when jsonFile omitted)
437
438
  describe <entity> Print the JSON Schema for an entity
438
439
  template <entity> Print a canonical sample record (required fields populated)
439
440
 
@@ -1289,9 +1290,50 @@ function _setSummaryOnEntity(entityKind, entityId, phase, summaryFilePath) {
1289
1290
  process.exit(1);
1290
1291
  }
1291
1292
 
1293
+ // Load entity first — its `path` is the authoritative artifact directory and
1294
+ // is needed both to self-resolve the sidecar (when no file arg is given) and
1295
+ // to merge the summary below.
1296
+ const record = entityKind === 'task' ? store.getTask(entityId) : store.getBug(entityId);
1297
+ if (!record) {
1298
+ console.error(`${entityKind} not found: ${entityId}`);
1299
+ process.exit(1);
1300
+ }
1301
+
1302
+ // ADR artifact-resolution Phase 1: when the caller omits the JSON file, derive
1303
+ // the sidecar from record.path + the canonical phase→filename map. The agent
1304
+ // never hand-builds the path (kills the Class-1 failures + the arity bug).
1305
+ let selfResolved = false;
1306
+ if (!summaryFilePath) {
1307
+ if (typeof record.path !== 'string' || record.path.length === 0) {
1308
+ console.error(`Cannot self-resolve summary sidecar: ${entityKind} ${entityId} has no "path". Pass an explicit <jsonFile>.`);
1309
+ process.exit(1);
1310
+ }
1311
+ // record.path is the entity directory; defensively strip a trailing filename.
1312
+ const dir = /\.(md|json)$/i.test(record.path) ? path.dirname(record.path) : record.path;
1313
+ summaryFilePath = path.join(dir, resolveSummaryFilename(entityKind, phase));
1314
+ selfResolved = true;
1315
+ }
1316
+
1292
1317
  // Read and validate summary JSON
1293
1318
  if (!fs.existsSync(summaryFilePath)) {
1294
- console.error(`Summary file not found: ${summaryFilePath}`);
1319
+ // v1.0.10: self-resolve looks for the CANONICAL filename. If an agent wrote a
1320
+ // non-canonical sidecar (e.g. VALIDATE-SUMMARY.json via the Write tool instead
1321
+ // of forge_artifact's VALIDATION-SUMMARY.json), surface the near-name file so
1322
+ // the error is fixable in one step rather than a silent dead-end.
1323
+ let hint = '';
1324
+ if (selfResolved) {
1325
+ try {
1326
+ const dir = path.dirname(summaryFilePath);
1327
+ const expected = path.basename(summaryFilePath);
1328
+ const nearby = fs.readdirSync(dir).filter((f) => f !== expected && f.endsWith('.json') && /summary/i.test(f));
1329
+ if (nearby.length > 0) {
1330
+ const kind = PHASE_TO_KIND[phase];
1331
+ hint = ` (expected ${expected}; found ${nearby.join(', ')} in the same dir — write the sidecar via ` +
1332
+ `forge_artifact artifact:"${kind}", or rename it to ${expected})`;
1333
+ }
1334
+ } catch (_) { /* dir unreadable — fall back to the plain message */ }
1335
+ }
1336
+ console.error(`Summary file not found: ${summaryFilePath}${hint}`);
1295
1337
  process.exit(1);
1296
1338
  }
1297
1339
 
@@ -1309,14 +1351,7 @@ function _setSummaryOnEntity(entityKind, entityId, phase, summaryFilePath) {
1309
1351
  process.exit(1);
1310
1352
  }
1311
1353
 
1312
- // Load entity
1313
- const record = entityKind === 'task' ? store.getTask(entityId) : store.getBug(entityId);
1314
- if (!record) {
1315
- console.error(`${entityKind} not found: ${entityId}`);
1316
- process.exit(1);
1317
- }
1318
-
1319
- // Merge summary
1354
+ // Merge summary (record was loaded above for path resolution)
1320
1355
  if (!record.summaries) record.summaries = {};
1321
1356
  record.summaries[phase] = summary;
1322
1357
 
@@ -1394,8 +1429,8 @@ function cmdSetSummary() {
1394
1429
  const phase = args[2];
1395
1430
  const summaryFile = args[3];
1396
1431
 
1397
- if (!taskId || !phase || !summaryFile) {
1398
- console.error('Usage: store-cli.cjs set-summary <taskId> <phase> <jsonFile>');
1432
+ if (!taskId || !phase) {
1433
+ console.error('Usage: store-cli.cjs set-summary <taskId> <phase> [<jsonFile>]');
1399
1434
  process.exit(1);
1400
1435
  }
1401
1436
 
@@ -1407,8 +1442,8 @@ function cmdSetBugSummary() {
1407
1442
  const phase = args[2];
1408
1443
  const summaryFile = args[3];
1409
1444
 
1410
- if (!bugId || !phase || !summaryFile) {
1411
- console.error('Usage: store-cli.cjs set-bug-summary <bugId> <phase> <jsonFile>');
1445
+ if (!bugId || !phase) {
1446
+ console.error('Usage: store-cli.cjs set-bug-summary <bugId> <phase> [<jsonFile>]');
1412
1447
  process.exit(1);
1413
1448
  }
1414
1449