@entelligentsia/forgecli 0.7.10 → 0.9.0

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 (385) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/dist/CHANGELOG-forge-plugin.md +70 -0
  3. package/dist/CHANGELOG-pi.md +63 -0
  4. package/dist/bin/argv.d.ts +2 -2
  5. package/dist/bin/argv.js +27 -0
  6. package/dist/bin/argv.js.map +1 -1
  7. package/dist/bin/config.d.ts +69 -0
  8. package/dist/bin/config.js +315 -0
  9. package/dist/bin/config.js.map +1 -0
  10. package/dist/bin/doctor.d.ts +1 -0
  11. package/dist/bin/doctor.js +12 -0
  12. package/dist/bin/doctor.js.map +1 -1
  13. package/dist/bin/env-defaults.d.ts +1 -0
  14. package/dist/bin/env-defaults.js +13 -0
  15. package/dist/bin/env-defaults.js.map +1 -0
  16. package/dist/bin/forge.js +16 -0
  17. package/dist/bin/forge.js.map +1 -1
  18. package/dist/bin/update-cli.d.ts +9 -0
  19. package/dist/bin/update-cli.js +120 -0
  20. package/dist/bin/update-cli.js.map +1 -0
  21. package/dist/extensions/forgecli/config-command.d.ts +8 -0
  22. package/dist/extensions/forgecli/config-command.js +66 -0
  23. package/dist/extensions/forgecli/config-command.js.map +1 -0
  24. package/dist/extensions/forgecli/config-layer.d.ts +38 -0
  25. package/dist/extensions/forgecli/config-layer.js +68 -0
  26. package/dist/extensions/forgecli/config-layer.js.map +1 -0
  27. package/dist/extensions/forgecli/config-tui/component.d.ts +35 -0
  28. package/dist/extensions/forgecli/config-tui/component.js +236 -0
  29. package/dist/extensions/forgecli/config-tui/component.js.map +1 -0
  30. package/dist/extensions/forgecli/config-tui/handler.d.ts +40 -0
  31. package/dist/extensions/forgecli/config-tui/handler.js +240 -0
  32. package/dist/extensions/forgecli/config-tui/handler.js.map +1 -0
  33. package/dist/extensions/forgecli/config-tui/index.d.ts +5 -0
  34. package/dist/extensions/forgecli/config-tui/index.js +5 -0
  35. package/dist/extensions/forgecli/config-tui/index.js.map +1 -0
  36. package/dist/extensions/forgecli/config-tui/keys.d.ts +26 -0
  37. package/dist/extensions/forgecli/config-tui/keys.js +33 -0
  38. package/dist/extensions/forgecli/config-tui/keys.js.map +1 -0
  39. package/dist/extensions/forgecli/config-tui/plugin-config-reader.d.ts +23 -0
  40. package/dist/extensions/forgecli/config-tui/plugin-config-reader.js +58 -0
  41. package/dist/extensions/forgecli/config-tui/plugin-config-reader.js.map +1 -0
  42. package/dist/extensions/forgecli/config-tui/screens/advanced-menu.d.ts +7 -0
  43. package/dist/extensions/forgecli/config-tui/screens/advanced-menu.js +83 -0
  44. package/dist/extensions/forgecli/config-tui/screens/advanced-menu.js.map +1 -0
  45. package/dist/extensions/forgecli/config-tui/screens/confirm-quit.d.ts +11 -0
  46. package/dist/extensions/forgecli/config-tui/screens/confirm-quit.js +54 -0
  47. package/dist/extensions/forgecli/config-tui/screens/confirm-quit.js.map +1 -0
  48. package/dist/extensions/forgecli/config-tui/screens/override-editor.d.ts +11 -0
  49. package/dist/extensions/forgecli/config-tui/screens/override-editor.js +233 -0
  50. package/dist/extensions/forgecli/config-tui/screens/override-editor.js.map +1 -0
  51. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.d.ts +7 -0
  52. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js +91 -0
  53. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js.map +1 -0
  54. package/dist/extensions/forgecli/config-tui/screens/overrides-list.d.ts +7 -0
  55. package/dist/extensions/forgecli/config-tui/screens/overrides-list.js +71 -0
  56. package/dist/extensions/forgecli/config-tui/screens/overrides-list.js.map +1 -0
  57. package/dist/extensions/forgecli/config-tui/screens/persona-editor.d.ts +10 -0
  58. package/dist/extensions/forgecli/config-tui/screens/persona-editor.js +182 -0
  59. package/dist/extensions/forgecli/config-tui/screens/persona-editor.js.map +1 -0
  60. package/dist/extensions/forgecli/config-tui/screens/persona-picker.d.ts +7 -0
  61. package/dist/extensions/forgecli/config-tui/screens/persona-picker.js +76 -0
  62. package/dist/extensions/forgecli/config-tui/screens/persona-picker.js.map +1 -0
  63. package/dist/extensions/forgecli/config-tui/screens/personas-list.d.ts +7 -0
  64. package/dist/extensions/forgecli/config-tui/screens/personas-list.js +98 -0
  65. package/dist/extensions/forgecli/config-tui/screens/personas-list.js.map +1 -0
  66. package/dist/extensions/forgecli/config-tui/screens/shared.d.ts +29 -0
  67. package/dist/extensions/forgecli/config-tui/screens/shared.js +100 -0
  68. package/dist/extensions/forgecli/config-tui/screens/shared.js.map +1 -0
  69. package/dist/extensions/forgecli/config-tui/screens/show-resolved.d.ts +23 -0
  70. package/dist/extensions/forgecli/config-tui/screens/show-resolved.js +128 -0
  71. package/dist/extensions/forgecli/config-tui/screens/show-resolved.js.map +1 -0
  72. package/dist/extensions/forgecli/config-tui/screens/tier-menu.d.ts +7 -0
  73. package/dist/extensions/forgecli/config-tui/screens/tier-menu.js +135 -0
  74. package/dist/extensions/forgecli/config-tui/screens/tier-menu.js.map +1 -0
  75. package/dist/extensions/forgecli/config-tui/screens/tier-picker.d.ts +9 -0
  76. package/dist/extensions/forgecli/config-tui/screens/tier-picker.js +122 -0
  77. package/dist/extensions/forgecli/config-tui/screens/tier-picker.js.map +1 -0
  78. package/dist/extensions/forgecli/config-tui/screens/types.d.ts +24 -0
  79. package/dist/extensions/forgecli/config-tui/screens/types.js +5 -0
  80. package/dist/extensions/forgecli/config-tui/screens/types.js.map +1 -0
  81. package/dist/extensions/forgecli/config-tui/screens.d.ts +24 -0
  82. package/dist/extensions/forgecli/config-tui/screens.js +78 -0
  83. package/dist/extensions/forgecli/config-tui/screens.js.map +1 -0
  84. package/dist/extensions/forgecli/config-tui/state/buffer.d.ts +11 -0
  85. package/dist/extensions/forgecli/config-tui/state/buffer.js +91 -0
  86. package/dist/extensions/forgecli/config-tui/state/buffer.js.map +1 -0
  87. package/dist/extensions/forgecli/config-tui/state/constants.d.ts +4 -0
  88. package/dist/extensions/forgecli/config-tui/state/constants.js +14 -0
  89. package/dist/extensions/forgecli/config-tui/state/constants.js.map +1 -0
  90. package/dist/extensions/forgecli/config-tui/state/index.d.ts +6 -0
  91. package/dist/extensions/forgecli/config-tui/state/index.js +9 -0
  92. package/dist/extensions/forgecli/config-tui/state/index.js.map +1 -0
  93. package/dist/extensions/forgecli/config-tui/state/init.d.ts +2 -0
  94. package/dist/extensions/forgecli/config-tui/state/init.js +30 -0
  95. package/dist/extensions/forgecli/config-tui/state/init.js.map +1 -0
  96. package/dist/extensions/forgecli/config-tui/state/model.d.ts +192 -0
  97. package/dist/extensions/forgecli/config-tui/state/model.js +4 -0
  98. package/dist/extensions/forgecli/config-tui/state/model.js.map +1 -0
  99. package/dist/extensions/forgecli/config-tui/state/reducer.d.ts +2 -0
  100. package/dist/extensions/forgecli/config-tui/state/reducer.js +212 -0
  101. package/dist/extensions/forgecli/config-tui/state/reducer.js.map +1 -0
  102. package/dist/extensions/forgecli/config-tui/state/selectors.d.ts +91 -0
  103. package/dist/extensions/forgecli/config-tui/state/selectors.js +231 -0
  104. package/dist/extensions/forgecli/config-tui/state/selectors.js.map +1 -0
  105. package/dist/extensions/forgecli/config-tui/state.d.ts +6 -0
  106. package/dist/extensions/forgecli/config-tui/state.js +11 -0
  107. package/dist/extensions/forgecli/config-tui/state.js.map +1 -0
  108. package/dist/extensions/forgecli/config-tui/theme.d.ts +37 -0
  109. package/dist/extensions/forgecli/config-tui/theme.js +88 -0
  110. package/dist/extensions/forgecli/config-tui/theme.js.map +1 -0
  111. package/dist/extensions/forgecli/config-tui/tier-meta.d.ts +28 -0
  112. package/dist/extensions/forgecli/config-tui/tier-meta.js +69 -0
  113. package/dist/extensions/forgecli/config-tui/tier-meta.js.map +1 -0
  114. package/dist/extensions/forgecli/config-writer.d.ts +16 -0
  115. package/dist/extensions/forgecli/config-writer.js +63 -0
  116. package/dist/extensions/forgecli/config-writer.js.map +1 -0
  117. package/dist/extensions/forgecli/fix-bug.js +85 -1
  118. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  119. package/dist/extensions/forgecli/forge-cli-schema.json +54 -0
  120. package/dist/extensions/forgecli/forge-commands.js +3 -8
  121. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  122. package/dist/extensions/forgecli/forge-subagent.d.ts +13 -0
  123. package/dist/extensions/forgecli/forge-subagent.js +19 -0
  124. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  125. package/dist/extensions/forgecli/index.js +19 -3
  126. package/dist/extensions/forgecli/index.js.map +1 -1
  127. package/dist/extensions/forgecli/input-router.d.ts +33 -0
  128. package/dist/extensions/forgecli/input-router.js +133 -0
  129. package/dist/extensions/forgecli/input-router.js.map +1 -0
  130. package/dist/extensions/forgecli/model-resolver.d.ts +32 -0
  131. package/dist/extensions/forgecli/model-resolver.js +65 -0
  132. package/dist/extensions/forgecli/model-resolver.js.map +1 -0
  133. package/dist/extensions/forgecli/model-validator.d.ts +29 -0
  134. package/dist/extensions/forgecli/model-validator.js +107 -0
  135. package/dist/extensions/forgecli/model-validator.js.map +1 -0
  136. package/dist/extensions/forgecli/run-sprint.js +59 -0
  137. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  138. package/dist/extensions/forgecli/run-task.js +93 -1
  139. package/dist/extensions/forgecli/run-task.js.map +1 -1
  140. package/dist/extensions/forgecli/thread-switcher.js +5 -2
  141. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  142. package/dist/extensions/forgecli/update-check.js +1 -1
  143. package/dist/extensions/forgecli/update-check.js.map +1 -1
  144. package/dist/extensions/forgecli/whats-new-widget.d.ts +5 -5
  145. package/dist/extensions/forgecli/whats-new-widget.js +16 -13
  146. package/dist/extensions/forgecli/whats-new-widget.js.map +1 -1
  147. package/dist/extensions/forgecli/whats-new.js +6 -5
  148. package/dist/extensions/forgecli/whats-new.js.map +1 -1
  149. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  150. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +27 -98
  151. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  152. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +62 -132
  153. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
  154. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
  155. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +25 -15
  156. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  157. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  158. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +1 -0
  159. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
  160. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  161. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +17 -1
  162. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  163. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  164. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +8 -2
  165. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
  166. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  167. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +17 -1
  168. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js.map +1 -1
  169. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  170. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +8 -1
  171. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js.map +1 -1
  172. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  173. package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +63 -0
  174. package/node_modules/@earendil-works/pi-coding-agent/README.md +1 -1
  175. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.d.ts.map +1 -1
  176. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.js +1 -1
  177. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.js.map +1 -1
  178. package/node_modules/@earendil-works/pi-coding-agent/dist/cli.d.ts.map +1 -1
  179. package/node_modules/@earendil-works/pi-coding-agent/dist/cli.js +6 -10
  180. package/node_modules/@earendil-works/pi-coding-agent/dist/cli.js.map +1 -1
  181. package/node_modules/@earendil-works/pi-coding-agent/dist/config.d.ts.map +1 -1
  182. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +12 -3
  183. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js.map +1 -1
  184. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +1 -0
  185. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  186. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +30 -15
  187. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  188. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts +3 -3
  189. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  190. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +23 -13
  191. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  192. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts +4 -0
  193. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  194. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +58 -38
  195. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  196. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  197. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js +0 -1
  198. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  199. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  200. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js +3 -2
  201. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  202. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts +2 -2
  203. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  204. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js +7 -4
  205. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js.map +1 -1
  206. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  207. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +6 -2
  208. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  209. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.d.ts.map +1 -1
  210. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +3 -4
  211. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js.map +1 -1
  212. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.d.ts.map +1 -1
  213. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js +2 -2
  214. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js.map +1 -1
  215. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.d.ts +7 -1
  216. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.d.ts.map +1 -1
  217. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.js +60 -7
  218. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.js.map +1 -1
  219. package/node_modules/@earendil-works/pi-coding-agent/docs/packages.md +2 -2
  220. package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +1 -3
  221. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
  222. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  223. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
  224. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
  225. package/node_modules/@earendil-works/pi-coding-agent/package.json +6 -6
  226. package/node_modules/@earendil-works/pi-tui/package.json +2 -2
  227. package/node_modules/@protobufjs/fetch/CHANGELOG.md +8 -0
  228. package/node_modules/@protobufjs/fetch/index.d.ts +7 -7
  229. package/node_modules/@protobufjs/fetch/index.js +4 -7
  230. package/node_modules/@protobufjs/fetch/package.json +7 -5
  231. package/node_modules/@protobufjs/fetch/tests/data/file.txt +1 -0
  232. package/node_modules/@protobufjs/fetch/tests/index.js +150 -8
  233. package/node_modules/@protobufjs/fetch/util/fs.js +11 -0
  234. package/node_modules/@protobufjs/inquire/CHANGELOG.md +8 -0
  235. package/node_modules/@protobufjs/inquire/index.d.ts +1 -0
  236. package/node_modules/@protobufjs/inquire/index.js +1 -0
  237. package/node_modules/@protobufjs/inquire/package.json +1 -1
  238. package/node_modules/protobufjs/dist/light/protobuf.js +187 -153
  239. package/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
  240. package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
  241. package/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
  242. package/node_modules/protobufjs/dist/minimal/protobuf.js +14 -5
  243. package/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
  244. package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
  245. package/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
  246. package/node_modules/protobufjs/dist/protobuf.js +207 -173
  247. package/node_modules/protobufjs/dist/protobuf.js.map +1 -1
  248. package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
  249. package/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
  250. package/node_modules/protobufjs/package.json +6 -3
  251. package/node_modules/protobufjs/src/util/fs.js +11 -0
  252. package/node_modules/protobufjs/src/util/minimal.js +10 -2
  253. package/node_modules/protobufjs/src/util.js +1 -1
  254. package/node_modules/undici/README.md +14 -5
  255. package/node_modules/undici/docs/docs/api/Client.md +4 -2
  256. package/node_modules/undici/docs/docs/api/Dispatcher.md +62 -27
  257. package/node_modules/undici/docs/docs/api/GlobalInstallation.md +7 -5
  258. package/node_modules/undici/docs/docs/api/H2CClient.md +1 -1
  259. package/node_modules/undici/docs/docs/api/RedirectHandler.md +14 -9
  260. package/node_modules/undici/docs/docs/api/RetryAgent.md +0 -1
  261. package/node_modules/undici/docs/docs/api/RetryHandler.md +12 -14
  262. package/node_modules/undici/docs/docs/api/SnapshotAgent.md +23 -0
  263. package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +231 -0
  264. package/node_modules/undici/index.js +4 -2
  265. package/node_modules/undici/lib/api/api-connect.js +13 -11
  266. package/node_modules/undici/lib/api/api-pipeline.js +26 -13
  267. package/node_modules/undici/lib/api/api-request.js +45 -21
  268. package/node_modules/undici/lib/api/api-stream.js +81 -20
  269. package/node_modules/undici/lib/api/api-upgrade.js +21 -11
  270. package/node_modules/undici/lib/api/readable.js +3 -2
  271. package/node_modules/undici/lib/cache/memory-cache-store.js +1 -1
  272. package/node_modules/undici/lib/cache/sqlite-cache-store.js +6 -4
  273. package/node_modules/undici/lib/core/connect.js +17 -1
  274. package/node_modules/undici/lib/core/constants.js +1 -24
  275. package/node_modules/undici/lib/core/errors.js +2 -2
  276. package/node_modules/undici/lib/core/request.js +115 -18
  277. package/node_modules/undici/lib/core/socks5-client.js +24 -9
  278. package/node_modules/undici/lib/core/socks5-utils.js +32 -23
  279. package/node_modules/undici/lib/core/symbols.js +1 -0
  280. package/node_modules/undici/lib/core/util.js +70 -43
  281. package/node_modules/undici/lib/dispatcher/agent.js +47 -33
  282. package/node_modules/undici/lib/dispatcher/balanced-pool.js +21 -26
  283. package/node_modules/undici/lib/dispatcher/client-h1.js +98 -39
  284. package/node_modules/undici/lib/dispatcher/client-h2.js +603 -272
  285. package/node_modules/undici/lib/dispatcher/client.js +12 -5
  286. package/node_modules/undici/lib/dispatcher/dispatcher-base.js +24 -5
  287. package/node_modules/undici/lib/dispatcher/dispatcher.js +0 -4
  288. package/node_modules/undici/lib/dispatcher/dispatcher1-wrapper.js +107 -0
  289. package/node_modules/undici/lib/dispatcher/h2c-client.js +5 -5
  290. package/node_modules/undici/lib/dispatcher/pool-base.js +28 -10
  291. package/node_modules/undici/lib/dispatcher/pool.js +31 -6
  292. package/node_modules/undici/lib/dispatcher/proxy-agent.js +38 -13
  293. package/node_modules/undici/lib/dispatcher/round-robin-pool.js +31 -9
  294. package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +95 -80
  295. package/node_modules/undici/lib/global.js +13 -1
  296. package/node_modules/undici/lib/handler/cache-handler.js +16 -8
  297. package/node_modules/undici/lib/handler/decorator-handler.js +1 -2
  298. package/node_modules/undici/lib/handler/redirect-handler.js +5 -51
  299. package/node_modules/undici/lib/handler/retry-handler.js +15 -2
  300. package/node_modules/undici/lib/interceptor/cache.js +30 -17
  301. package/node_modules/undici/lib/interceptor/decompress.js +28 -2
  302. package/node_modules/undici/lib/interceptor/dns.js +1 -1
  303. package/node_modules/undici/lib/interceptor/redirect.js +3 -3
  304. package/node_modules/undici/lib/llhttp/llhttp-wasm.js +1 -1
  305. package/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +1 -1
  306. package/node_modules/undici/lib/mock/mock-agent.js +8 -8
  307. package/node_modules/undici/lib/mock/mock-call-history.js +15 -15
  308. package/node_modules/undici/lib/mock/mock-utils.js +37 -22
  309. package/node_modules/undici/lib/mock/snapshot-agent.js +16 -6
  310. package/node_modules/undici/lib/mock/snapshot-recorder.js +38 -3
  311. package/node_modules/undici/lib/util/cache.js +8 -7
  312. package/node_modules/undici/lib/util/runtime-features.js +3 -34
  313. package/node_modules/undici/lib/web/cache/cache.js +6 -8
  314. package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +245 -150
  315. package/node_modules/undici/lib/web/fetch/body.js +3 -9
  316. package/node_modules/undici/lib/web/fetch/formdata-parser.js +17 -6
  317. package/node_modules/undici/lib/web/fetch/formdata.js +21 -2
  318. package/node_modules/undici/lib/web/fetch/index.js +214 -221
  319. package/node_modules/undici/lib/web/webidl/index.js +7 -9
  320. package/node_modules/undici/lib/web/websocket/frame.js +1 -7
  321. package/node_modules/undici/lib/web/websocket/permessage-deflate.js +13 -31
  322. package/node_modules/undici/lib/web/websocket/receiver.js +62 -22
  323. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +11 -17
  324. package/node_modules/undici/lib/web/websocket/websocket.js +6 -1
  325. package/node_modules/undici/package.json +9 -9
  326. package/node_modules/undici/types/agent.d.ts +0 -2
  327. package/node_modules/undici/types/client.d.ts +25 -19
  328. package/node_modules/undici/types/dispatcher.d.ts +7 -27
  329. package/node_modules/undici/types/dispatcher1-wrapper.d.ts +7 -0
  330. package/node_modules/undici/types/formdata.d.ts +0 -6
  331. package/node_modules/undici/types/h2c-client.d.ts +6 -6
  332. package/node_modules/undici/types/header.d.ts +5 -0
  333. package/node_modules/undici/types/index.d.ts +3 -1
  334. package/node_modules/undici/types/interceptors.d.ts +1 -1
  335. package/node_modules/undici/types/pool.d.ts +0 -2
  336. package/node_modules/undici/types/proxy-agent.d.ts +2 -2
  337. package/node_modules/undici/types/round-robin-pool.d.ts +0 -2
  338. package/node_modules/undici/types/snapshot-agent.d.ts +4 -0
  339. package/node_modules/undici/types/socks5-proxy-agent.d.ts +2 -2
  340. package/node_modules/undici/types/webidl.d.ts +0 -1
  341. package/package.json +16 -9
  342. package/dist/extensions/forgecli/review-command.d.ts +0 -2
  343. package/dist/extensions/forgecli/review-command.js +0 -184
  344. package/dist/extensions/forgecli/review-command.js.map +0 -1
  345. package/dist/forge-payload/.tools/banners.cjs +0 -435
  346. package/dist/forge-payload/.tools/build-context-pack.cjs +0 -290
  347. package/dist/forge-payload/.tools/build-init-context.cjs +0 -322
  348. package/dist/forge-payload/.tools/build-overlay.cjs +0 -326
  349. package/dist/forge-payload/.tools/build-persona-pack.cjs +0 -226
  350. package/dist/forge-payload/.tools/collate.cjs +0 -1041
  351. package/dist/forge-payload/.tools/generation-manifest.cjs +0 -311
  352. package/dist/forge-payload/.tools/lib/forge-root.cjs +0 -59
  353. package/dist/forge-payload/.tools/lib/paths.cjs +0 -29
  354. package/dist/forge-payload/.tools/lib/pricing.cjs +0 -165
  355. package/dist/forge-payload/.tools/lib/project-root.cjs +0 -32
  356. package/dist/forge-payload/.tools/lib/result.js +0 -40
  357. package/dist/forge-payload/.tools/lib/store-facade.cjs +0 -162
  358. package/dist/forge-payload/.tools/lib/store-nlp.cjs +0 -250
  359. package/dist/forge-payload/.tools/lib/store-query-exec.cjs +0 -272
  360. package/dist/forge-payload/.tools/lib/validate.js +0 -141
  361. package/dist/forge-payload/.tools/manage-config.cjs +0 -340
  362. package/dist/forge-payload/.tools/manage-versions.cjs +0 -365
  363. package/dist/forge-payload/.tools/package.json +0 -3
  364. package/dist/forge-payload/.tools/parse-gates.cjs +0 -151
  365. package/dist/forge-payload/.tools/parse-verdict.cjs +0 -67
  366. package/dist/forge-payload/.tools/preflight-gate.cjs +0 -350
  367. package/dist/forge-payload/.tools/prompts/sprint-plan-prompt.md +0 -70
  368. package/dist/forge-payload/.tools/schemas/task-list.schema.json +0 -53
  369. package/dist/forge-payload/.tools/seed-store.cjs +0 -237
  370. package/dist/forge-payload/.tools/store-cli.cjs +0 -1226
  371. package/dist/forge-payload/.tools/store-query.cjs +0 -319
  372. package/dist/forge-payload/.tools/store.cjs +0 -315
  373. package/dist/forge-payload/.tools/substitute-placeholders.cjs +0 -625
  374. package/dist/forge-payload/.tools/validate-store.cjs +0 -593
  375. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json +0 -24
  376. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package-lock.json +0 -92
  377. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package-lock.json +0 -31
  378. package/node_modules/undici/lib/handler/unwrap-handler.js +0 -100
  379. package/node_modules/undici/lib/handler/wrap-handler.js +0 -105
  380. package/node_modules/undici/lib/llhttp/.gitkeep +0 -0
  381. package/node_modules/undici/lib/util/promise.js +0 -28
  382. package/skills/refresh-kb-links/SKILL.md +0 -217
  383. package/skills/store-custodian/SKILL.md +0 -163
  384. package/skills/store-query-grammar/SKILL.md +0 -145
  385. package/skills/store-query-nlp/SKILL.md +0 -110
@@ -1,1226 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- // Forge tool: store-cli
5
- // Deterministic store custodian CLI — wraps store.cjs facade.
6
- // Enforces schema validation on write and status transition rules.
7
- // Usage: node store-cli.cjs <command> <args>
8
-
9
- let _store;
10
- function _getStore() { return _store || (_store = require('./store.cjs')); }
11
- let _projectRoot;
12
- function _getProjectRoot() { return _projectRoot || (_projectRoot = require('./lib/project-root.cjs').findProjectRoot()); }
13
-
14
- const _path = require('path');
15
-
16
- // Path traversal guard — resolves the events directory and validates that
17
- // sprintOrBugId doesn't escape it. Same pattern as store.cjs purgeEvents.
18
- function _resolveEventsDir(sprintOrBugId) {
19
- const root = _getProjectRoot();
20
- const eventsBase = root
21
- ? _path.resolve(root, '.forge', 'store', 'events')
22
- : _path.resolve('.forge', 'store', 'events');
23
- const resolvedDir = _path.resolve(eventsBase, sprintOrBugId);
24
- if (!resolvedDir.startsWith(eventsBase + _path.sep) && resolvedDir !== eventsBase) {
25
- console.error(`Path traversal blocked: '${sprintOrBugId}' resolves outside events directory`);
26
- process.exit(1);
27
- }
28
- return resolvedDir;
29
- }
30
-
31
- let _schemas;
32
- function _getSchemas() {
33
- if (_schemas) return _schemas;
34
- const fs = require('fs');
35
- const path = require('path');
36
-
37
- const ENTITY_TYPES = ['sprint', 'task', 'bug', 'event', 'feature'];
38
-
39
- const MINIMAL_REQUIRED = {
40
- sprint: ['sprintId', 'title', 'status', 'taskIds', 'createdAt'],
41
- task: ['taskId', 'sprintId', 'title', 'status', 'path'],
42
- bug: ['bugId', 'title', 'severity', 'status', 'path', 'reportedAt'],
43
- event: ['eventId', 'taskId', 'sprintId', 'role', 'action', 'phase', 'iteration', 'startTimestamp', 'endTimestamp', 'durationMinutes', 'model'],
44
- feature: ['id', 'title', 'status', 'created_at']
45
- };
46
-
47
- const schemas = {};
48
- const projectDir = path.join('.forge', 'schemas');
49
- const inTreeDir = path.join('forge', 'schemas');
50
- const pluginDir = path.join(__dirname, '..', 'schemas');
51
-
52
- const AUX_SCHEMAS = {
53
- 'event-sidecar': 'event-sidecar.schema.json',
54
- 'progress-entry': 'progress-entry.schema.json',
55
- 'collation-state': 'collation-state.schema.json',
56
- };
57
-
58
- const allTypes = [...ENTITY_TYPES, ...Object.keys(AUX_SCHEMAS)];
59
- for (const type of allTypes) {
60
- const schemaFile = AUX_SCHEMAS[type] || `${type}.schema.json`;
61
- let schema = null;
62
-
63
- // 1. Try project-installed schemas first
64
- const projectPath = path.join(projectDir, schemaFile);
65
- try {
66
- if (fs.existsSync(projectPath)) {
67
- schema = JSON.parse(fs.readFileSync(projectPath, 'utf8'));
68
- }
69
- } catch (_) {}
70
-
71
- // 2. Fall back to in-tree source schemas (development mode)
72
- if (!schema) {
73
- const inTreePath = path.join(inTreeDir, schemaFile);
74
- try {
75
- if (fs.existsSync(inTreePath)) {
76
- schema = JSON.parse(fs.readFileSync(inTreePath, 'utf8'));
77
- }
78
- } catch (_) {}
79
- }
80
-
81
- // 3. Fall back to plugin-installed schemas (production mode)
82
- // store-cli.cjs lives at $FORGE_ROOT/tools/, so __dirname/../schemas/
83
- // resolves to $FORGE_ROOT/schemas/ — always correct for installed plugins.
84
- if (!schema) {
85
- const pluginPath = path.join(pluginDir, schemaFile);
86
- try {
87
- if (fs.existsSync(pluginPath)) {
88
- schema = JSON.parse(fs.readFileSync(pluginPath, 'utf8'));
89
- }
90
- } catch (_) {}
91
- }
92
-
93
- if (schema) {
94
- schemas[type] = schema;
95
- } else {
96
- console.error(`WARN: schema file ${schemaFile} not found, using minimal fallback`);
97
- schemas[type] = { type: 'object', required: MINIMAL_REQUIRED[type] || [], properties: {} };
98
- }
99
- }
100
-
101
- _schemas = schemas;
102
- return _schemas;
103
- }
104
-
105
- // ---------------------------------------------------------------------------
106
- // Schema loading — same resolution as validate-store.cjs
107
- // ---------------------------------------------------------------------------
108
-
109
- const ENTITY_TYPES = ['sprint', 'task', 'bug', 'event', 'feature'];
110
-
111
- const MINIMAL_REQUIRED = {
112
- sprint: ['sprintId', 'title', 'status', 'taskIds', 'createdAt'],
113
- task: ['taskId', 'sprintId', 'title', 'status', 'path'],
114
- bug: ['bugId', 'title', 'severity', 'status', 'path', 'reportedAt'],
115
- event: ['eventId', 'taskId', 'sprintId', 'role', 'action', 'phase', 'iteration', 'startTimestamp', 'endTimestamp', 'durationMinutes', 'model'],
116
- feature: ['id', 'title', 'status', 'created_at']
117
- };
118
-
119
- // Shared validator + nullable-field set live in ./lib/validate.js so the
120
- // write-boundary hook can reuse the exact same validation logic as tool writes.
121
- const { validateRecord, NULLABLE_FIELDS } = require('./lib/validate.js');
122
-
123
- // Valid phase keys for summaries (dot-delimited → underscore in JSON key)
124
- const VALID_SUMMARY_PHASES = new Set(['plan', 'review_plan', 'implementation', 'code_review', 'validation']);
125
-
126
- // Schema for a single phase summary (used by set-summary / set-bug-summary)
127
- const PHASE_SUMMARY_SCHEMA = {
128
- type: 'object',
129
- required: ['objective', 'written_at'],
130
- properties: {
131
- objective: { type: 'string', maxLength: 280 },
132
- key_changes: { type: 'array', items: { type: 'string', maxLength: 200 }, maxItems: 12 },
133
- findings: { type: 'array', items: { type: 'string', maxLength: 200 }, maxItems: 12 },
134
- verdict: { type: 'string', enum: ['approved', 'revision', 'n/a'] },
135
- written_at: { type: 'string' },
136
- artifact_ref:{ type: 'string' }
137
- },
138
- additionalProperties: false
139
- };
140
-
141
- // validateRecord imported from ./lib/validate.js above.
142
-
143
- // ---------------------------------------------------------------------------
144
- // Transition tables
145
- // ---------------------------------------------------------------------------
146
-
147
- const TASK_TRANSITIONS = {
148
- draft: ['planned', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
149
- planned: ['plan-approved', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
150
- 'plan-approved': ['implementing', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
151
- implementing: ['implemented', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
152
- implemented: ['review-approved', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
153
- 'review-approved': ['approved', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
154
- approved: ['committed', 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned'],
155
- // Terminal: committed, abandoned — no transitions out
156
- };
157
-
158
- const SPRINT_TRANSITIONS = {
159
- planning: ['active', 'blocked', 'partially-completed', 'abandoned'],
160
- active: ['completed', 'blocked', 'partially-completed', 'abandoned'],
161
- completed: ['retrospective-done', 'blocked', 'partially-completed', 'abandoned'],
162
- // Terminal: retrospective-done, abandoned
163
- };
164
-
165
- const BUG_TRANSITIONS = {
166
- reported: ['triaged'],
167
- triaged: ['in-progress'],
168
- 'in-progress': ['fixed'],
169
- fixed: ['verified'],
170
- // Terminal: verified
171
- };
172
-
173
- const FEATURE_TRANSITIONS = {
174
- draft: ['active'],
175
- active: ['shipped', 'retired'],
176
- // Terminal: shipped, retired
177
- };
178
-
179
- const TRANSITION_MAP = {
180
- task: TASK_TRANSITIONS,
181
- sprint: SPRINT_TRANSITIONS,
182
- bug: BUG_TRANSITIONS,
183
- feature: FEATURE_TRANSITIONS
184
- };
185
-
186
- const TERMINAL_STATES = new Set([
187
- 'committed', 'abandoned', // task
188
- 'retrospective-done', // sprint
189
- 'verified', // bug
190
- 'shipped', 'retired' // feature
191
- ]);
192
-
193
- // Failed states that may be entered from any non-terminal state
194
- const FAILED_STATES = new Set([
195
- 'plan-revision-required', 'code-revision-required', 'blocked', 'escalated', 'abandoned', // task
196
- 'blocked', 'partially-completed' // sprint
197
- ]);
198
-
199
- function isLegalTransition(entityType, field, currentValue, newValue) {
200
- if (currentValue === newValue) return true; // no-op
201
-
202
- const table = TRANSITION_MAP[entityType];
203
- if (!table) return true; // no transition rules for this entity type
204
-
205
- // Terminal states cannot transition out
206
- if (TERMINAL_STATES.has(currentValue)) return false;
207
-
208
- // Failed states may be entered from any non-terminal state
209
- if (FAILED_STATES.has(newValue)) return true;
210
-
211
- // Check the explicit transition table
212
- const allowed = table[currentValue];
213
- if (!allowed) return false; // current state not in table (unknown state)
214
-
215
- return allowed.includes(newValue);
216
- }
217
-
218
- // ---------------------------------------------------------------------------
219
- // Bug timestamp normalization helpers
220
- // ---------------------------------------------------------------------------
221
-
222
- // Returns true if the string is a date-only value (YYYY-MM-DD without time).
223
- // These are rejected by the date-time format validator but agents commonly
224
- // supply them for reportedAt/resolvedAt.
225
- function _isDateOnly(ts) {
226
- return typeof ts === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(ts);
227
- }
228
-
229
- // Convert a date-only string (YYYY-MM-DD) into a full ISO datetime by
230
- // appending the current time-of-day in UTC. The date portion is preserved
231
- // from the input; only the time component is auto-populated.
232
- function _dateOnlyToISO(dateStr) {
233
- const now = new Date();
234
- const timePart = now.toISOString().slice(10); // e.g. "T14:32:07.123Z"
235
- return dateStr + timePart;
236
- }
237
-
238
- // Normalize bug datetime fields before writing. When agents supply date-only
239
- // values (YYYY-MM-DD) for reportedAt or resolvedAt, auto-populates the time
240
- // component from the current time-of-day so the value passes date-time format
241
- // validation. Full ISO datetimes are left untouched.
242
- function _normalizeBugTimestamps(data) {
243
- if (_isDateOnly(data.reportedAt)) data.reportedAt = _dateOnlyToISO(data.reportedAt);
244
- if (_isDateOnly(data.resolvedAt)) data.resolvedAt = _dateOnlyToISO(data.resolvedAt);
245
- return data;
246
- }
247
-
248
- // ---------------------------------------------------------------------------
249
- // Model discovery
250
- // ---------------------------------------------------------------------------
251
-
252
- // Deterministic model discovery — probes environment variables in priority
253
- // order to resolve the actual runtime model identifier. Returns "unknown"
254
- // when no signal is available instead of guessing an Anthropic model name.
255
- function discoverModel() {
256
- const candidates = [
257
- process.env.CLAUDE_CODE_SUBAGENT_MODEL,
258
- process.env.ANTHROPIC_MODEL,
259
- process.env.CLAUDE_MODEL,
260
- ];
261
- for (const val of candidates) {
262
- if (val && val.trim()) return val.trim();
263
- }
264
- return 'unknown';
265
- }
266
-
267
- module.exports = { isLegalTransition, validateRecord, TRANSITION_MAP, TERMINAL_STATES, FAILED_STATES, ENTITY_TYPES, MINIMAL_REQUIRED, NULLABLE_FIELDS, VALID_SUMMARY_PHASES, PHASE_SUMMARY_SCHEMA, _isDateOnly, _dateOnlyToISO, _normalizeBugTimestamps, discoverModel };
268
-
269
- // ---------------------------------------------------------------------------
270
- // CLI
271
- // ---------------------------------------------------------------------------
272
- if (require.main === module) {
273
-
274
- process.on('uncaughtException', (error) => {
275
- console.error('Fatal store-cli error:', error);
276
- process.exit(1);
277
- });
278
-
279
- try {
280
- const fs = require('fs');
281
- const path = require('path');
282
- const store = _getStore();
283
- const schemas = _getSchemas();
284
-
285
- const DRY_RUN = process.argv.includes('--dry-run');
286
- const VERBOSE = process.argv.includes('--verbose');
287
-
288
- // ---------------------------------------------------------------------------
289
- // Entity ID field mapping
290
- // ---------------------------------------------------------------------------
291
-
292
- const ENTITY_ID_FIELD = {
293
- sprint: 'sprintId',
294
- task: 'taskId',
295
- bug: 'bugId',
296
- event: 'eventId',
297
- feature: 'id'
298
- };
299
-
300
- // ---------------------------------------------------------------------------
301
- // Store accessor mapping
302
- // ---------------------------------------------------------------------------
303
-
304
- function getEntity(entity, id) {
305
- switch (entity) {
306
- case 'sprint': return store.getSprint(id);
307
- case 'task': return store.getTask(id);
308
- case 'bug': return store.getBug(id);
309
- case 'event': return store.getEvent(id, null); // needs sprintId separately
310
- case 'feature': return store.getFeature(id);
311
- default: return null;
312
- }
313
- }
314
-
315
- function writeEntity(entity, data) {
316
- switch (entity) {
317
- case 'sprint': return store.writeSprint(data);
318
- case 'task': return store.writeTask(data);
319
- case 'bug': return store.writeBug(data);
320
- case 'event': return store.writeEvent(data.sprintId, data);
321
- case 'feature': return store.writeFeature(data);
322
- }
323
- }
324
-
325
- function deleteEntity(entity, id) {
326
- switch (entity) {
327
- case 'sprint': return store.deleteSprint(id);
328
- case 'task': return store.deleteTask(id);
329
- case 'bug': return store.deleteBug(id);
330
- case 'feature': return store.deleteFeature(id);
331
- default:
332
- console.error(`Unknown entity type: ${entity}`);
333
- process.exit(1);
334
- }
335
- }
336
-
337
- function listEntities(entity, filter) {
338
- switch (entity) {
339
- case 'sprint': return store.listSprints(filter);
340
- case 'task': return store.listTasks(filter);
341
- case 'bug': return store.listBugs(filter);
342
- case 'feature': return store.listFeatures(filter);
343
- default:
344
- console.error(`Unknown entity type: ${entity}`);
345
- process.exit(1);
346
- }
347
- }
348
-
349
- // ---------------------------------------------------------------------------
350
- // Sidecar handling
351
- // ---------------------------------------------------------------------------
352
-
353
- // Canonical event schema token fields
354
- const CANONICAL_TOKEN_FIELDS = [
355
- 'inputTokens', 'outputTokens', 'cacheReadTokens', 'cacheWriteTokens',
356
- 'estimatedCostUSD', 'model', 'durationMinutes', 'startTimestamp', 'endTimestamp',
357
- 'tokenSource'
358
- ];
359
-
360
- // Accepted sidecar fields (includes aliases)
361
- const SIDECAR_ACCEPTED_FIELDS = new Set([
362
- 'inputTokens', 'outputTokens', 'cacheReadTokens', 'cacheWriteTokens',
363
- 'estimatedCostUSD', 'model', 'cost', 'durationMinutes',
364
- 'startTimestamp', 'endTimestamp', 'cacheCreationTokens',
365
- 'tokenSource'
366
- ]);
367
-
368
- // Alias mapping for sidecar → canonical event
369
- const SIDECAR_ALIASES = {
370
- 'cacheCreationTokens': 'cacheWriteTokens',
371
- 'cost': 'estimatedCostUSD'
372
- };
373
-
374
- function resolveSidecarDir(sprintId) {
375
- const storeRoot = store.impl.storeRoot;
376
- return path.join(storeRoot, 'events', sprintId);
377
- }
378
-
379
- function sidecarPath(sprintId, eventId) {
380
- return path.join(resolveSidecarDir(sprintId), `_${eventId}_usage.json`);
381
- }
382
-
383
- function canonicalEventPath(sprintId, eventId) {
384
- return path.join(resolveSidecarDir(sprintId), `${eventId}.json`);
385
- }
386
-
387
- // ---------------------------------------------------------------------------
388
- // CLI argument parsing
389
- // ---------------------------------------------------------------------------
390
-
391
- const args = process.argv.slice(2);
392
-
393
- if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
394
- console.log(`Forge Store Custodian CLI
395
-
396
- Usage: node store-cli.cjs <command> <args> [--dry-run]
397
-
398
- Commands:
399
- write <entity> '<json>' Write a full entity record
400
- read <entity> <id> [--json] Read an entity record
401
- list <entity> [key=value ...] List entities with optional filter
402
- delete <entity> <id> Delete an entity record
403
- update-status <entity> <id> <field> <value> [--force]
404
- Update status/enum field with transition check
405
- emit <sprintId> '<json>' [--sidecar] Write an event (or sidecar)
406
- merge-sidecar <sprintId> <eventId> Merge sidecar into canonical event
407
- record-usage <sprintId> <eventId> [flags] Write a token-usage sidecar
408
- purge-events <sprintId> Delete all events for a sprint
409
- progress <sprintOrBugId> <agentName> <bannerKey> <status> [detail]
410
- Append a progress entry to the log
411
- progress-clear <sprintOrBugId> Clear (truncate) the progress log
412
- write-collation-state '<json>' Write COLLATION_STATE.json
413
- validate <entity> '<json>' Validate against schema without writing
414
- set-summary <taskId> <phase> <jsonFile> Set a phase summary on a task record
415
- set-bug-summary <bugId> <phase> <jsonFile> Set a phase summary on a bug record
416
- describe <entity> Print the JSON Schema for an entity
417
- template <entity> Print a canonical sample record (required fields populated)
418
-
419
- Entities: sprint, task, bug, event, feature
420
- Phases (summaries): plan, review_plan, implementation, code_review, validation
421
-
422
- Flags:
423
- --dry-run Validate and preview without writing (applies to all write commands)
424
- --force Bypass transition check on update-status (emits warning)
425
- --json Output raw JSON on read (no pretty-print)
426
- --sidecar Write as sidecar file on emit (ephemeral _-prefixed)
427
-
428
- Exit codes: 0 on success, 1 on failure`);
429
- process.exit(0);
430
- }
431
-
432
- const command = args[0];
433
-
434
- // ---------------------------------------------------------------------------
435
- // Command implementations
436
- // ---------------------------------------------------------------------------
437
-
438
- function cmdWrite() {
439
- const entity = args[1];
440
- const jsonStr = args[2];
441
-
442
- if (!entity || !jsonStr) {
443
- console.error('Usage: store-cli.cjs write <entity> \'<json>\'');
444
- process.exit(1);
445
- }
446
-
447
- if (!ENTITY_TYPES.includes(entity)) {
448
- console.error(`Unknown entity type: ${entity}`);
449
- process.exit(1);
450
- }
451
-
452
- let data;
453
- try {
454
- data = JSON.parse(jsonStr);
455
- } catch (e) {
456
- console.error(`Invalid JSON: ${e.message}`);
457
- process.exit(1);
458
- }
459
-
460
- // Auto-populate date-only YYYY-MM-DD values in bug datetime fields.
461
- // Agents commonly supply date-only values for reportedAt/resolvedAt;
462
- // this normalizes them to full ISO datetimes before schema validation.
463
- if (entity === 'bug') {
464
- _normalizeBugTimestamps(data);
465
- }
466
-
467
- const errors = validateRecord(data, schemas[entity], { entity });
468
- if (errors.length > 0) {
469
- for (const e of errors) console.error(e);
470
- process.exit(1);
471
- }
472
-
473
- if (DRY_RUN) {
474
- console.log(`[dry-run] would write ${entity} ${data[ENTITY_ID_FIELD[entity]]}`);
475
- } else {
476
- writeEntity(entity, data);
477
- }
478
- if (VERBOSE) console.log(JSON.stringify({ ok: true, entity, id: data[ENTITY_ID_FIELD[entity]], dryRun: DRY_RUN }));
479
- }
480
-
481
- function cmdRead() {
482
- const entity = args[1];
483
- const id = args[2];
484
- const asJson = args.includes('--json');
485
-
486
- if (!entity || !id) {
487
- console.error('Usage: store-cli.cjs read <entity> <id> [--json]');
488
- process.exit(1);
489
- }
490
-
491
- if (!ENTITY_TYPES.includes(entity)) {
492
- console.error(`Unknown entity type: ${entity}`);
493
- process.exit(1);
494
- }
495
-
496
- // Events need sprintId for lookup — read by eventId with sprintId resolution
497
- let record;
498
- if (entity === 'event') {
499
- // For events, try to find by scanning sprint directories
500
- const sprints = store.listSprints();
501
- record = null;
502
- for (const sprint of sprints) {
503
- if (!sprint) continue;
504
- const found = store.getEvent(id, sprint.sprintId);
505
- if (found) { record = found; break; }
506
- }
507
- } else {
508
- record = getEntity(entity, id);
509
- }
510
-
511
- if (!record) {
512
- console.error(`Entity not found: ${entity} ${id}`);
513
- process.exit(1);
514
- }
515
-
516
- if (asJson) {
517
- console.log(JSON.stringify(record));
518
- } else {
519
- console.log(JSON.stringify(record, null, 2));
520
- }
521
- }
522
-
523
- function cmdList() {
524
- const entity = args[1];
525
-
526
- if (!entity) {
527
- console.error('Usage: store-cli.cjs list <entity> [key=value ...]');
528
- process.exit(1);
529
- }
530
-
531
- if (!ENTITY_TYPES.includes(entity)) {
532
- console.error(`Unknown entity type: ${entity}`);
533
- process.exit(1);
534
- }
535
-
536
- // Parse key=value filter pairs from remaining args
537
- const filter = {};
538
- for (let i = 2; i < args.length; i++) {
539
- const eqIdx = args[i].indexOf('=');
540
- if (eqIdx > 0) {
541
- const key = args[i].slice(0, eqIdx);
542
- const val = args[i].slice(eqIdx + 1);
543
- // Try to parse numeric values
544
- const num = Number(val);
545
- filter[key] = (val !== '' && !isNaN(num) && val === String(num)) ? num : val;
546
- }
547
- }
548
-
549
- const records = listEntities(entity, Object.keys(filter).length > 0 ? filter : undefined);
550
- console.log(JSON.stringify(records, null, 2));
551
- }
552
-
553
- function cmdDelete() {
554
- const entity = args[1];
555
- const id = args[2];
556
-
557
- if (!entity || !id) {
558
- console.error('Usage: store-cli.cjs delete <entity> <id>');
559
- process.exit(1);
560
- }
561
-
562
- if (!ENTITY_TYPES.includes(entity)) {
563
- console.error(`Unknown entity type: ${entity}`);
564
- process.exit(1);
565
- }
566
-
567
- deleteEntity(entity, id);
568
- if (VERBOSE) console.log(JSON.stringify({ ok: true, deleted: `${entity}/${id}` }));
569
- }
570
-
571
- function cmdUpdateStatus() {
572
- const entity = args[1];
573
- const id = args[2];
574
- const field = args[3];
575
- const value = args[4];
576
- const force = args.includes('--force');
577
-
578
- if (!entity || !id || !field || !value) {
579
- console.error('Usage: store-cli.cjs update-status <entity> <id> <field> <value> [--force]');
580
- process.exit(1);
581
- }
582
-
583
- if (!TRANSITION_MAP[entity]) {
584
- console.error(`No transition rules for entity type: ${entity}`);
585
- process.exit(1);
586
- }
587
-
588
- // Read current record
589
- const record = getEntity(entity, id);
590
- if (!record) {
591
- console.error(`Entity not found: ${entity} ${id}`);
592
- process.exit(1);
593
- }
594
-
595
- const currentValue = record[field];
596
- if (currentValue === undefined) {
597
- console.error(`Field "${field}" not found on ${entity} ${id}`);
598
- process.exit(1);
599
- }
600
-
601
- // Check transition legality
602
- if (!force && !isLegalTransition(entity, field, currentValue, value)) {
603
- console.error(`Illegal transition: ${entity} ${id} ${field}: ${currentValue} → ${value}`);
604
- process.exit(1);
605
- }
606
-
607
- if (force && !isLegalTransition(entity, field, currentValue, value)) {
608
- console.error(`WARN: --force bypassing illegal transition: ${entity} ${id} ${field}: ${currentValue} → ${value}`);
609
- }
610
-
611
- // Apply update and write back
612
- if (DRY_RUN) {
613
- console.log(`[dry-run] would update ${entity} ${id} ${field}: ${currentValue} → ${value}`);
614
- } else {
615
- record[field] = value;
616
- writeEntity(entity, record);
617
- }
618
- if (VERBOSE) console.log(JSON.stringify({ ok: true, entity, id, field, from: currentValue, to: value, force, dryRun: DRY_RUN }));
619
- }
620
-
621
- // ---------------------------------------------------------------------------
622
- // Timestamp normalization helpers (#56)
623
- // ---------------------------------------------------------------------------
624
-
625
- // Returns true if the timestamp string has a zeroed time component (T00:00:00),
626
- // which indicates the caller provided a date-only value instead of a real
627
- // time-of-day. Midnight UTC is treated as "not set" for event timing purposes.
628
- function _isZeroedTimestamp(ts) {
629
- if (typeof ts !== 'string') return true; // null / missing → normalize
630
- return /T00:00:00/.test(ts);
631
- }
632
-
633
- // Normalize event timestamps before writing. Replaces any zeroed or absent
634
- // startTimestamp / endTimestamp with the current real time, then recomputes
635
- // durationMinutes from the two timestamps so cost reports are accurate.
636
- function _normalizeEventTimestamps(data) {
637
- const now = new Date().toISOString();
638
-
639
- if (_isZeroedTimestamp(data.startTimestamp)) data.startTimestamp = now;
640
- if (_isZeroedTimestamp(data.endTimestamp)) data.endTimestamp = now;
641
-
642
- // Recompute durationMinutes whenever both timestamps are present.
643
- if (data.startTimestamp && data.endTimestamp) {
644
- const diffMs = new Date(data.endTimestamp) - new Date(data.startTimestamp);
645
- data.durationMinutes = Math.max(0, diffMs / 60000);
646
- }
647
-
648
- return data;
649
- }
650
-
651
- function cmdEmit() {
652
- const sprintId = args[1];
653
- const jsonStr = args[2];
654
- const isSidecar = args.includes('--sidecar');
655
-
656
- if (!sprintId || !jsonStr) {
657
- console.error('Usage: store-cli.cjs emit <sprintId> \'<json>\' [--sidecar]');
658
- process.exit(1);
659
- }
660
-
661
- let data;
662
- try {
663
- data = JSON.parse(jsonStr);
664
- } catch (e) {
665
- console.error(`Invalid JSON: ${e.message}`);
666
- process.exit(1);
667
- }
668
-
669
- if (isSidecar) {
670
- // Write sidecar file — validate against the sidecar schema (eventId +
671
- // optional token/cost fields). Full event-shape enforcement happens
672
- // on merge into the canonical event.
673
- const sidecarErrors = validateRecord(data, schemas['event-sidecar']);
674
- if (sidecarErrors.length > 0) {
675
- for (const e of sidecarErrors) console.error(e);
676
- process.exit(1);
677
- }
678
-
679
- const sidecarDir = resolveSidecarDir(sprintId);
680
- const filePath = sidecarPath(sprintId, data.eventId);
681
-
682
- if (DRY_RUN) {
683
- console.log(`[dry-run] would write sidecar _${data.eventId}_usage.json`);
684
- } else {
685
- // Ensure directory exists
686
- if (!fs.existsSync(sidecarDir)) {
687
- fs.mkdirSync(sidecarDir, { recursive: true });
688
- }
689
-
690
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
691
- }
692
- if (VERBOSE) console.log(JSON.stringify({ ok: true, sidecar: true, eventId: data.eventId, sprintId, dryRun: DRY_RUN }));
693
- } else {
694
- // Normalize zeroed timestamps before validation so agents that provide
695
- // date-only values (T00:00:00Z) get real time-of-day stamped in (#56).
696
- _normalizeEventTimestamps(data);
697
-
698
- // Auto-populate model from environment when missing or empty (FORGE-S12-T06).
699
- // Callers who set model explicitly take priority — discoverModel() is only
700
- // used as a fallback so we never silently record a wrong model name.
701
- if (!data.model || !data.model.trim()) {
702
- data.model = discoverModel();
703
- }
704
-
705
- // Validate as event entity
706
- const errors = validateRecord(data, schemas.event);
707
- if (errors.length > 0) {
708
- for (const e of errors) console.error(e);
709
- process.exit(1);
710
- }
711
-
712
- if (DRY_RUN) {
713
- console.log(`[dry-run] would emit event ${data.eventId}`);
714
- } else {
715
- store.writeEvent(sprintId, data);
716
- }
717
- if (VERBOSE) console.log(JSON.stringify({ ok: true, event: true, eventId: data.eventId, sprintId, dryRun: DRY_RUN }));
718
- }
719
- }
720
-
721
- function cmdMergeSidecar() {
722
- const sprintId = args[1];
723
- const eventId = args[2];
724
-
725
- if (!sprintId || !eventId) {
726
- console.error('Usage: store-cli.cjs merge-sidecar <sprintId> <eventId>');
727
- process.exit(1);
728
- }
729
-
730
- // Read sidecar
731
- const scPath = sidecarPath(sprintId, eventId);
732
- if (!fs.existsSync(scPath)) {
733
- console.error(`Sidecar not found: ${scPath}`);
734
- process.exit(1);
735
- }
736
-
737
- let sidecar;
738
- try {
739
- sidecar = JSON.parse(fs.readFileSync(scPath, 'utf8'));
740
- } catch (e) {
741
- console.error(`Invalid sidecar JSON: ${e.message}`);
742
- process.exit(1);
743
- }
744
-
745
- // Read canonical event
746
- const canPath = canonicalEventPath(sprintId, eventId);
747
- if (!fs.existsSync(canPath)) {
748
- console.error(`Canonical event not found: ${canPath}`);
749
- process.exit(1);
750
- }
751
-
752
- let event;
753
- try {
754
- event = JSON.parse(fs.readFileSync(canPath, 'utf8'));
755
- } catch (e) {
756
- console.error(`Invalid canonical event JSON: ${e.message}`);
757
- process.exit(1);
758
- }
759
-
760
- // Merge token fields from sidecar into event
761
- for (const [key, value] of Object.entries(sidecar)) {
762
- // Resolve aliases
763
- const canonicalKey = SIDECAR_ALIASES[key] || key;
764
- if (CANONICAL_TOKEN_FIELDS.includes(canonicalKey)) {
765
- event[canonicalKey] = value;
766
- }
767
- }
768
-
769
- // Re-validate the merged canonical event against the event schema. Catches
770
- // the case where a sidecar's token field is present but the canonical event
771
- // was already malformed — we do not want to silently persist invalid data.
772
- const mergedErrors = validateRecord(event, schemas.event);
773
- if (mergedErrors.length > 0) {
774
- console.error(`Merged event ${eventId} failed schema validation:`);
775
- for (const e of mergedErrors) console.error(` ${e}`);
776
- process.exit(1);
777
- }
778
-
779
- if (DRY_RUN) {
780
- console.log(`[dry-run] would merge sidecar into ${eventId} and delete sidecar`);
781
- } else {
782
- // Write updated event via facade (handles ghost-file logic)
783
- store.writeEvent(sprintId, event);
784
-
785
- // Delete sidecar
786
- fs.unlinkSync(scPath);
787
- }
788
-
789
- if (VERBOSE) console.log(JSON.stringify({ ok: true, merged: true, eventId, sprintId, dryRun: DRY_RUN }));
790
- }
791
-
792
- function cmdRecordUsage() {
793
- const sprintId = args[1];
794
- const eventId = args[2];
795
-
796
- if (!sprintId || !eventId) {
797
- console.error('Usage: store-cli.cjs record-usage <sprintId> <eventId> [flags]');
798
- console.error(' Flags:');
799
- console.error(' --input-tokens <n> Input token count');
800
- console.error(' --output-tokens <n> Output token count');
801
- console.error(' --cache-read-tokens <n> Cache read token count');
802
- console.error(' --cache-write-tokens <n> Cache write token count');
803
- console.error(' --estimated-cost-usd <n> Estimated cost in USD');
804
- console.error(' --token-source <src> reported | estimated');
805
- console.error(' --model <model> Model identifier');
806
- console.error(' --duration-minutes <n> Duration in minutes');
807
- process.exit(1);
808
- }
809
-
810
- // Parse flag arguments from remaining args
811
- const flagArgs = args.slice(3);
812
- const sidecar = { eventId };
813
-
814
- for (let i = 0; i < flagArgs.length; i++) {
815
- const arg = flagArgs[i];
816
- if (arg === '--input-tokens' && flagArgs[i + 1]) {
817
- sidecar.inputTokens = parseInt(flagArgs[++i], 10);
818
- } else if (arg === '--output-tokens' && flagArgs[i + 1]) {
819
- sidecar.outputTokens = parseInt(flagArgs[++i], 10);
820
- } else if (arg === '--cache-read-tokens' && flagArgs[i + 1]) {
821
- sidecar.cacheReadTokens = parseInt(flagArgs[++i], 10);
822
- } else if (arg === '--cache-write-tokens' && flagArgs[i + 1]) {
823
- sidecar.cacheWriteTokens = parseInt(flagArgs[++i], 10);
824
- } else if (arg === '--estimated-cost-usd' && flagArgs[i + 1]) {
825
- sidecar.estimatedCostUSD = parseFloat(flagArgs[++i]);
826
- } else if (arg === '--token-source' && flagArgs[i + 1]) {
827
- sidecar.tokenSource = flagArgs[++i];
828
- } else if (arg === '--model' && flagArgs[i + 1]) {
829
- sidecar.model = flagArgs[++i];
830
- } else if (arg === '--duration-minutes' && flagArgs[i + 1]) {
831
- sidecar.durationMinutes = parseFloat(flagArgs[++i]);
832
- }
833
- }
834
-
835
- // Auto-populate model from environment when --model flag not provided (FORGE-S12-T06).
836
- // An explicit --model flag takes priority — discoverModel() is only a fallback.
837
- if (!sidecar.model) {
838
- sidecar.model = discoverModel();
839
- }
840
-
841
- // Validate against sidecar schema
842
- const sidecarErrors = validateRecord(sidecar, schemas['event-sidecar']);
843
- if (sidecarErrors.length > 0) {
844
- for (const e of sidecarErrors) console.error(e);
845
- process.exit(1);
846
- }
847
-
848
- const sidecarDir = resolveSidecarDir(sprintId);
849
- const filePath = sidecarPath(sprintId, eventId);
850
-
851
- if (DRY_RUN) {
852
- console.log(`[dry-run] would write sidecar _${eventId}_usage.json`);
853
- } else {
854
- if (!fs.existsSync(sidecarDir)) {
855
- fs.mkdirSync(sidecarDir, { recursive: true });
856
- }
857
- fs.writeFileSync(filePath, JSON.stringify(sidecar, null, 2) + '\n', 'utf8');
858
- }
859
- if (VERBOSE) console.log(JSON.stringify({ ok: true, sidecar: true, eventId, sprintId, dryRun: DRY_RUN }));
860
- }
861
-
862
- function cmdPurgeEvents() {
863
- const sprintId = args[1];
864
-
865
- if (!sprintId) {
866
- console.error('Usage: store-cli.cjs purge-events <sprintId>');
867
- process.exit(1);
868
- }
869
-
870
- const result = store.purgeEvents(sprintId, { dryRun: DRY_RUN });
871
- if (DRY_RUN && !result.purged) {
872
- console.log(`[dry-run] would purge ${result.fileCount} event(s) for ${sprintId}`);
873
- }
874
- if (VERBOSE) console.log(JSON.stringify(result, null, 2));
875
- }
876
-
877
- function cmdWriteCollationState() {
878
- const jsonStr = args[1];
879
-
880
- if (!jsonStr) {
881
- console.error('Usage: store-cli.cjs write-collation-state \'<json>\'');
882
- process.exit(1);
883
- }
884
-
885
- let data;
886
- try {
887
- data = JSON.parse(jsonStr);
888
- } catch (e) {
889
- console.error(`Invalid JSON: ${e.message}`);
890
- process.exit(1);
891
- }
892
-
893
- const csErrors = validateRecord(data, schemas['collation-state']);
894
- if (csErrors.length > 0) {
895
- for (const e of csErrors) console.error(e);
896
- process.exit(1);
897
- }
898
-
899
- if (DRY_RUN) {
900
- console.log('[dry-run] would write COLLATION_STATE.json');
901
- } else {
902
- store.writeCollationState(data);
903
- }
904
- if (VERBOSE) console.log(JSON.stringify({ ok: true, dryRun: DRY_RUN }));
905
- }
906
-
907
- function cmdProgress() {
908
- const sprintOrBugId = args[1];
909
- const agentName = args[2];
910
- const bannerKey = args[3];
911
- const status = args[4];
912
- const detail = args.slice(5).join(' ');
913
-
914
- if (!sprintOrBugId || !agentName || !bannerKey || !status) {
915
- console.error('Usage: store-cli.cjs progress <sprintOrBugId> <agentName> <bannerKey> <status> [detail]');
916
- console.error(' status: start | progress | done | error');
917
- process.exit(1);
918
- }
919
-
920
- const timestamp = new Date().toISOString();
921
-
922
- const progressErrors = validateRecord(
923
- { timestamp, agentName, bannerKey, status, detail: detail || '' },
924
- schemas['progress-entry']
925
- );
926
- if (progressErrors.length > 0) {
927
- for (const e of progressErrors) console.error(e);
928
- process.exit(1);
929
- }
930
-
931
- const line = `${timestamp}|${agentName}|${bannerKey}|${status}|${detail}\n`;
932
-
933
- const dir = _resolveEventsDir(sprintOrBugId);
934
- if (!fs.existsSync(dir)) {
935
- fs.mkdirSync(dir, { recursive: true });
936
- }
937
-
938
- const logPath = path.join(dir, 'progress.log');
939
- fs.appendFileSync(logPath, line, 'utf8');
940
-
941
- // Emit human-readable summary to stdout
942
- let banners;
943
- try { banners = require('./banners.cjs'); } catch { banners = null; }
944
- let emoji = bannerKey;
945
- if (banners && typeof banners.mark === 'function') {
946
- try { emoji = banners.mark(bannerKey); } catch { emoji = bannerKey; }
947
- }
948
- const summary = `${emoji} ${agentName} [${status}]${detail ? ' ' + detail : ''}`;
949
- if (VERBOSE) process.stdout.write(summary + '\n');
950
- }
951
-
952
- function cmdProgressClear() {
953
- const sprintOrBugId = args[1];
954
-
955
- if (!sprintOrBugId) {
956
- console.error('Usage: store-cli.cjs progress-clear <sprintOrBugId>');
957
- process.exit(1);
958
- }
959
-
960
- const dir = _resolveEventsDir(sprintOrBugId);
961
- if (!fs.existsSync(dir)) {
962
- fs.mkdirSync(dir, { recursive: true });
963
- }
964
-
965
- const logPath = path.join(dir, 'progress.log');
966
- fs.writeFileSync(logPath, '', 'utf8');
967
- if (VERBOSE) console.log(`Cleared ${logPath}`);
968
- }
969
-
970
- function cmdValidate() {
971
- const entity = args[1];
972
- const jsonStr = args[2];
973
-
974
- if (!entity || !jsonStr) {
975
- console.error('Usage: store-cli.cjs validate <entity> \'<json>\'');
976
- process.exit(1);
977
- }
978
-
979
- if (!ENTITY_TYPES.includes(entity)) {
980
- console.error(`Unknown entity type: ${entity}`);
981
- process.exit(1);
982
- }
983
-
984
- let data;
985
- try {
986
- data = JSON.parse(jsonStr);
987
- } catch (e) {
988
- console.error(`Invalid JSON: ${e.message}`);
989
- process.exit(1);
990
- }
991
-
992
- const errors = validateRecord(data, schemas[entity], { entity });
993
- if (errors.length > 0) {
994
- for (const e of errors) console.error(e);
995
- process.exit(1);
996
- }
997
-
998
- if (VERBOSE) console.log(JSON.stringify({ ok: true, entity, valid: true }));
999
- }
1000
-
1001
- function _setSummaryOnEntity(entityKind, entityId, phase, summaryFilePath) {
1002
- if (!VALID_SUMMARY_PHASES.has(phase)) {
1003
- console.error(`Unknown phase "${phase}". Valid phases: ${[...VALID_SUMMARY_PHASES].join(', ')}`);
1004
- process.exit(1);
1005
- }
1006
-
1007
- // Read and validate summary JSON
1008
- if (!fs.existsSync(summaryFilePath)) {
1009
- console.error(`Summary file not found: ${summaryFilePath}`);
1010
- process.exit(1);
1011
- }
1012
-
1013
- let summary;
1014
- try {
1015
- summary = JSON.parse(fs.readFileSync(summaryFilePath, 'utf8'));
1016
- } catch (e) {
1017
- console.error(`Invalid JSON in summary file: ${e.message}`);
1018
- process.exit(1);
1019
- }
1020
-
1021
- const errors = validateRecord(summary, PHASE_SUMMARY_SCHEMA);
1022
- if (errors.length > 0) {
1023
- for (const e of errors) console.error(e);
1024
- process.exit(1);
1025
- }
1026
-
1027
- // Load entity
1028
- const record = entityKind === 'task' ? store.getTask(entityId) : store.getBug(entityId);
1029
- if (!record) {
1030
- console.error(`${entityKind} not found: ${entityId}`);
1031
- process.exit(1);
1032
- }
1033
-
1034
- // Merge summary
1035
- if (!record.summaries) record.summaries = {};
1036
- record.summaries[phase] = summary;
1037
-
1038
- // Atomic write: tmp + rename
1039
- const entityDirKey = entityKind === 'task' ? 'tasks' : 'bugs';
1040
- const idField = entityKind === 'task' ? record.taskId : record.bugId;
1041
- const storeRoot = store.impl.storeRoot;
1042
- const filePath = path.join(storeRoot, entityDirKey, `${idField}.json`);
1043
- const tmpPath = filePath + '.tmp';
1044
-
1045
- if (DRY_RUN) {
1046
- console.log(`[dry-run] would set ${entityKind} ${entityId} summaries.${phase}`);
1047
- } else {
1048
- fs.writeFileSync(tmpPath, JSON.stringify(record, null, 2) + '\n', 'utf8');
1049
- fs.renameSync(tmpPath, filePath);
1050
- }
1051
-
1052
- if (VERBOSE) console.log(JSON.stringify({ ok: true, entityKind, id: entityId, phase, dryRun: DRY_RUN }));
1053
- }
1054
-
1055
- function cmdSetSummary() {
1056
- const taskId = args[1];
1057
- const phase = args[2];
1058
- const summaryFile = args[3];
1059
-
1060
- if (!taskId || !phase || !summaryFile) {
1061
- console.error('Usage: store-cli.cjs set-summary <taskId> <phase> <jsonFile>');
1062
- process.exit(1);
1063
- }
1064
-
1065
- _setSummaryOnEntity('task', taskId, phase, summaryFile);
1066
- }
1067
-
1068
- function cmdSetBugSummary() {
1069
- const bugId = args[1];
1070
- const phase = args[2];
1071
- const summaryFile = args[3];
1072
-
1073
- if (!bugId || !phase || !summaryFile) {
1074
- console.error('Usage: store-cli.cjs set-bug-summary <bugId> <phase> <jsonFile>');
1075
- process.exit(1);
1076
- }
1077
-
1078
- _setSummaryOnEntity('bug', bugId, phase, summaryFile);
1079
- }
1080
-
1081
- // ---------------------------------------------------------------------------
1082
- // describe / template — schema introspection helpers (FORGE-BUG-029-friction)
1083
- //
1084
- // Reduce write→reject→retry friction. `describe <entity>` returns the raw
1085
- // JSON Schema; `template <entity>` walks the schema and returns a canonical
1086
- // sample record with required fields populated.
1087
- // ---------------------------------------------------------------------------
1088
-
1089
- const VALID_ENTITY_DESCRIBE = new Set(['sprint', 'task', 'bug', 'event', 'feature']);
1090
-
1091
- function cmdDescribe() {
1092
- const entity = args[1];
1093
- if (!entity) {
1094
- console.error('Usage: store-cli.cjs describe <entity>');
1095
- console.error(`Entities: ${Array.from(VALID_ENTITY_DESCRIBE).join(', ')}`);
1096
- process.exit(1);
1097
- }
1098
- if (!VALID_ENTITY_DESCRIBE.has(entity)) {
1099
- console.error(`Unknown entity: ${entity}`);
1100
- console.error(`Entities: ${Array.from(VALID_ENTITY_DESCRIBE).join(', ')}`);
1101
- process.exit(1);
1102
- }
1103
- const schema = _getSchemas()[entity];
1104
- if (!schema) {
1105
- console.error(`Schema not found for entity: ${entity}`);
1106
- process.exit(1);
1107
- }
1108
- console.log(JSON.stringify(schema, null, 2));
1109
- }
1110
-
1111
- // Field-name → placeholder ID heuristics. Covers the common shapes without
1112
- // inventing schema-specific knowledge in the generator itself.
1113
- function _idPlaceholder(entity, fieldName) {
1114
- if (fieldName === 'sprintId') return 'PROJECT-S01';
1115
- if (fieldName === 'taskId') return 'PROJECT-S01-T01';
1116
- if (fieldName === 'bugId') return 'PROJECT-BUG-001';
1117
- if (fieldName === 'feature_id' || fieldName === 'featureId') return 'PROJECT-F01';
1118
- if (fieldName === 'eventId') return '20260101T000000000Z_PROJECT-S01-T01_phase_start';
1119
- if (entity === 'feature' && fieldName === 'id') return 'PROJECT-F01';
1120
- return `<${fieldName}>`;
1121
- }
1122
-
1123
- function _generateSample(schema, entity) {
1124
- const sample = {};
1125
- const required = schema.required || [];
1126
- const properties = schema.properties || {};
1127
- for (const field of required) {
1128
- const def = properties[field] || {};
1129
- sample[field] = _generateValue(def, field, entity);
1130
- }
1131
- return sample;
1132
- }
1133
-
1134
- function _generateValue(def, fieldName, entity) {
1135
- // Resolve type — handle union (array of types) by picking the first non-null.
1136
- let t = def.type;
1137
- if (Array.isArray(t)) t = t.find((x) => x !== 'null') || t[0];
1138
-
1139
- if (def.enum && Array.isArray(def.enum) && def.enum.length > 0) {
1140
- return def.enum[0];
1141
- }
1142
- if (t === 'string') {
1143
- if (def.format === 'date-time') return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
1144
- if (/Id$|^id$/.test(fieldName) || fieldName === 'feature_id' || fieldName === 'eventId') {
1145
- return _idPlaceholder(entity, fieldName);
1146
- }
1147
- if (fieldName === 'title') return 'Sample title';
1148
- if (fieldName === 'description') return 'Sample description';
1149
- if (fieldName === 'path') return `engineering/sprints/PROJECT-S01/${fieldName}-sample.md`;
1150
- return `<${fieldName}>`;
1151
- }
1152
- if (t === 'integer' || t === 'number') return 0;
1153
- if (t === 'boolean') return false;
1154
- if (t === 'array') return [];
1155
- if (t === 'object') return {};
1156
- return null;
1157
- }
1158
-
1159
- function cmdTemplate() {
1160
- const entity = args[1];
1161
- if (!entity) {
1162
- console.error('Usage: store-cli.cjs template <entity>');
1163
- console.error(`Entities: ${Array.from(VALID_ENTITY_DESCRIBE).join(', ')}`);
1164
- process.exit(1);
1165
- }
1166
- if (!VALID_ENTITY_DESCRIBE.has(entity)) {
1167
- console.error(`Unknown entity: ${entity}`);
1168
- console.error(`Entities: ${Array.from(VALID_ENTITY_DESCRIBE).join(', ')}`);
1169
- process.exit(1);
1170
- }
1171
- const schema = _getSchemas()[entity];
1172
- if (!schema) {
1173
- console.error(`Schema not found for entity: ${entity}`);
1174
- process.exit(1);
1175
- }
1176
- const sample = _generateSample(schema, entity);
1177
- console.log(JSON.stringify(sample, null, 2));
1178
- }
1179
-
1180
- // ---------------------------------------------------------------------------
1181
- // Command dispatch
1182
- // ---------------------------------------------------------------------------
1183
-
1184
- switch (command) {
1185
- case 'write': cmdWrite(); break;
1186
- case 'read': cmdRead(); break;
1187
- case 'list': cmdList(); break;
1188
- case 'delete': cmdDelete(); break;
1189
- case 'update-status': cmdUpdateStatus(); break;
1190
- case 'emit': cmdEmit(); break;
1191
- case 'merge-sidecar': cmdMergeSidecar(); break;
1192
- case 'record-usage': cmdRecordUsage(); break;
1193
- case 'purge-events': cmdPurgeEvents(); break;
1194
- case 'write-collation-state': cmdWriteCollationState(); break;
1195
- case 'validate': cmdValidate(); break;
1196
- case 'progress': cmdProgress(); break;
1197
- case 'progress-clear': cmdProgressClear(); break;
1198
- case 'set-summary': cmdSetSummary(); break;
1199
- case 'set-bug-summary': cmdSetBugSummary(); break;
1200
- case 'describe': cmdDescribe(); break;
1201
- case 'template': cmdTemplate(); break;
1202
- case 'query':
1203
- case 'nlp':
1204
- case 'schema': {
1205
- // Delegate to store-query.cjs — query engine lives there
1206
- const { spawnSync } = require('child_process');
1207
- const queryBin = path.join(__dirname, 'store-query.cjs');
1208
- const result = spawnSync(process.execPath, [queryBin, command, ...args.slice(1)], {
1209
- stdio: 'inherit',
1210
- cwd: process.cwd(),
1211
- });
1212
- process.exit(result.status ?? 1);
1213
- break;
1214
- }
1215
- default:
1216
- console.error(`Unknown command: ${command}`);
1217
- console.error('Run with --help for usage information.');
1218
- process.exit(1);
1219
- }
1220
-
1221
- } catch (err) {
1222
- console.error(err.message);
1223
- process.exit(1);
1224
- }
1225
-
1226
- } // end if (require.main === module)