peaks-loop 4.1.2 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/.claude-plugin/marketplace.json +136 -47
  2. package/CHANGELOG.md +56 -0
  3. package/README-en.md +1 -1
  4. package/README.md +1 -1
  5. package/contracts/readonly-argv-whitelist.json +211 -0
  6. package/dist/cli/commands/audit-artifact-runners.d.ts +16 -0
  7. package/dist/cli/commands/audit-artifact-runners.js +170 -0
  8. package/dist/cli/commands/audit-command-shared.d.ts +97 -0
  9. package/dist/cli/commands/audit-command-shared.js +60 -0
  10. package/dist/cli/commands/audit-commands.d.ts +7 -31
  11. package/dist/cli/commands/audit-commands.js +64 -473
  12. package/dist/cli/commands/audit-goal-runners.d.ts +17 -0
  13. package/dist/cli/commands/audit-goal-runners.js +114 -0
  14. package/dist/cli/commands/audit-red-lines-runners.d.ts +11 -0
  15. package/dist/cli/commands/audit-red-lines-runners.js +43 -0
  16. package/dist/cli/commands/audit-refusal-data.d.ts +23 -0
  17. package/dist/cli/commands/audit-refusal-data.js +69 -0
  18. package/dist/cli/commands/audit-static-runners.d.ts +13 -0
  19. package/dist/cli/commands/audit-static-runners.js +139 -0
  20. package/dist/cli/commands/await-runners.d.ts +11 -0
  21. package/dist/cli/commands/await-runners.js +148 -0
  22. package/dist/cli/commands/code-auto-compact-command.d.ts +3 -0
  23. package/dist/cli/commands/code-auto-compact-command.js +124 -0
  24. package/dist/cli/commands/code-auto-compact-envelope.d.ts +22 -0
  25. package/dist/cli/commands/code-auto-compact-envelope.js +39 -0
  26. package/dist/cli/commands/code-context-audit-command.d.ts +3 -0
  27. package/dist/cli/commands/code-context-audit-command.js +68 -0
  28. package/dist/cli/commands/code-context-now-command.d.ts +3 -0
  29. package/dist/cli/commands/code-context-now-command.js +124 -0
  30. package/dist/cli/commands/code-context-now-envelope.d.ts +66 -0
  31. package/dist/cli/commands/code-context-now-envelope.js +136 -0
  32. package/dist/cli/commands/code-context-now-mapping.d.ts +27 -0
  33. package/dist/cli/commands/code-context-now-mapping.js +43 -0
  34. package/dist/cli/commands/code-emit-handoff-command.d.ts +3 -0
  35. package/dist/cli/commands/code-emit-handoff-command.js +99 -0
  36. package/dist/cli/commands/code-gate-step-08-command.d.ts +3 -0
  37. package/dist/cli/commands/code-gate-step-08-command.js +139 -0
  38. package/dist/cli/commands/code-post-compact-detect-command.d.ts +3 -0
  39. package/dist/cli/commands/code-post-compact-detect-command.js +50 -0
  40. package/dist/cli/commands/code-runtime-commands.d.ts +9 -43
  41. package/dist/cli/commands/code-runtime-commands.js +20 -672
  42. package/dist/cli/commands/code-runtime-session.d.ts +1 -0
  43. package/dist/cli/commands/code-runtime-session.js +15 -0
  44. package/dist/cli/commands/compact-command.d.ts +6 -2
  45. package/dist/cli/commands/compact-command.js +12 -559
  46. package/dist/cli/commands/compact-dry-run-command.d.ts +3 -0
  47. package/dist/cli/commands/compact-dry-run-command.js +87 -0
  48. package/dist/cli/commands/compact-force-command.d.ts +3 -0
  49. package/dist/cli/commands/compact-force-command.js +98 -0
  50. package/dist/cli/commands/compact-harness-window-command.d.ts +3 -0
  51. package/dist/cli/commands/compact-harness-window-command.js +174 -0
  52. package/dist/cli/commands/compact-history-command.d.ts +3 -0
  53. package/dist/cli/commands/compact-history-command.js +89 -0
  54. package/dist/cli/commands/compact-read-commands.d.ts +3 -0
  55. package/dist/cli/commands/compact-read-commands.js +123 -0
  56. package/dist/cli/commands/compact-settle-command.d.ts +3 -0
  57. package/dist/cli/commands/compact-settle-command.js +130 -0
  58. package/dist/cli/commands/compact-shared.d.ts +9 -0
  59. package/dist/cli/commands/compact-shared.js +26 -0
  60. package/dist/cli/commands/core/skill-command.d.ts +1 -44
  61. package/dist/cli/commands/core/skill-command.js +13 -541
  62. package/dist/cli/commands/core/skill-context-verdict.d.ts +38 -0
  63. package/dist/cli/commands/core/skill-context-verdict.js +63 -0
  64. package/dist/cli/commands/core/skill-generated-config-notice.d.ts +41 -0
  65. package/dist/cli/commands/core/skill-generated-config-notice.js +75 -0
  66. package/dist/cli/commands/core/skill-heartbeat-commands.d.ts +3 -0
  67. package/dist/cli/commands/core/skill-heartbeat-commands.js +50 -0
  68. package/dist/cli/commands/core/skill-presence-commands.d.ts +3 -0
  69. package/dist/cli/commands/core/skill-presence-commands.js +123 -0
  70. package/dist/cli/commands/core/skill-presence-housekeeping-commands.d.ts +3 -0
  71. package/dist/cli/commands/core/skill-presence-housekeeping-commands.js +165 -0
  72. package/dist/cli/commands/core/skill-registry-commands.d.ts +3 -0
  73. package/dist/cli/commands/core/skill-registry-commands.js +117 -0
  74. package/dist/cli/commands/core/skill-runbook-command.d.ts +3 -0
  75. package/dist/cli/commands/core/skill-runbook-command.js +29 -0
  76. package/dist/cli/commands/dispatch-command-spec.d.ts +2 -0
  77. package/dist/cli/commands/dispatch-command-spec.js +62 -0
  78. package/dist/cli/commands/dispatch-commands.js +159 -775
  79. package/dist/cli/commands/dispatch-detached-branch.d.ts +3 -0
  80. package/dist/cli/commands/dispatch-detached-branch.js +58 -0
  81. package/dist/cli/commands/dispatch-envelope.d.ts +36 -0
  82. package/dist/cli/commands/dispatch-envelope.js +104 -0
  83. package/dist/cli/commands/dispatch-isolation-lease.d.ts +17 -0
  84. package/dist/cli/commands/dispatch-isolation-lease.js +149 -0
  85. package/dist/cli/commands/dispatch-preflight.d.ts +7 -0
  86. package/dist/cli/commands/dispatch-preflight.js +105 -0
  87. package/dist/cli/commands/dispatch-prompt-blocks.d.ts +10 -0
  88. package/dist/cli/commands/dispatch-prompt-blocks.js +56 -0
  89. package/dist/cli/commands/dispatch-prompt-composition.d.ts +30 -0
  90. package/dist/cli/commands/dispatch-prompt-composition.js +171 -0
  91. package/dist/cli/commands/dispatch-record-write.d.ts +24 -0
  92. package/dist/cli/commands/dispatch-record-write.js +96 -0
  93. package/dist/cli/commands/dispatch-tool-call.d.ts +15 -0
  94. package/dist/cli/commands/dispatch-tool-call.js +128 -0
  95. package/dist/cli/commands/evolution-command-shared.d.ts +78 -0
  96. package/dist/cli/commands/evolution-command-shared.js +107 -0
  97. package/dist/cli/commands/evolution-commands.d.ts +11 -1
  98. package/dist/cli/commands/evolution-commands.js +19 -350
  99. package/dist/cli/commands/evolution-evaluate-command.d.ts +24 -0
  100. package/dist/cli/commands/evolution-evaluate-command.js +138 -0
  101. package/dist/cli/commands/evolution-propose-command.d.ts +23 -0
  102. package/dist/cli/commands/evolution-propose-command.js +145 -0
  103. package/dist/cli/commands/evolution-status-command.d.ts +17 -0
  104. package/dist/cli/commands/evolution-status-command.js +41 -0
  105. package/dist/cli/commands/evolution-verdict-commands.d.ts +30 -0
  106. package/dist/cli/commands/evolution-verdict-commands.js +100 -0
  107. package/dist/cli/commands/finalize-accumulator.d.ts +44 -0
  108. package/dist/cli/commands/finalize-accumulator.js +55 -0
  109. package/dist/cli/commands/finalize-branches.d.ts +34 -0
  110. package/dist/cli/commands/finalize-branches.js +119 -0
  111. package/dist/cli/commands/finalize-runners.d.ts +3 -0
  112. package/dist/cli/commands/finalize-runners.js +92 -0
  113. package/dist/cli/commands/finalize-selection.d.ts +65 -0
  114. package/dist/cli/commands/finalize-selection.js +35 -0
  115. package/dist/cli/commands/gate-commands.js +26 -8
  116. package/dist/cli/commands/job-checkpoint-commands.d.ts +3 -0
  117. package/dist/cli/commands/job-checkpoint-commands.js +157 -0
  118. package/dist/cli/commands/job-command-shared.d.ts +109 -0
  119. package/dist/cli/commands/job-command-shared.js +72 -0
  120. package/dist/cli/commands/job-commands.d.ts +4 -64
  121. package/dist/cli/commands/job-commands.js +42 -552
  122. package/dist/cli/commands/job-cost-check-command.d.ts +3 -0
  123. package/dist/cli/commands/job-cost-check-command.js +41 -0
  124. package/dist/cli/commands/job-init-status-commands.d.ts +3 -0
  125. package/dist/cli/commands/job-init-status-commands.js +149 -0
  126. package/dist/cli/commands/job-run-commands.d.ts +3 -0
  127. package/dist/cli/commands/job-run-commands.js +97 -0
  128. package/dist/cli/commands/job-state-root.d.ts +35 -0
  129. package/dist/cli/commands/job-state-root.js +76 -0
  130. package/dist/cli/commands/loop-eval-capture-index.d.ts +4 -0
  131. package/dist/cli/commands/loop-eval-capture-index.js +44 -0
  132. package/dist/cli/commands/loop-eval-commands.d.ts +8 -5
  133. package/dist/cli/commands/loop-eval-commands.js +12 -626
  134. package/dist/cli/commands/loop-eval-eval-command.d.ts +3 -0
  135. package/dist/cli/commands/loop-eval-eval-command.js +168 -0
  136. package/dist/cli/commands/loop-eval-monotonic-command.d.ts +3 -0
  137. package/dist/cli/commands/loop-eval-monotonic-command.js +90 -0
  138. package/dist/cli/commands/loop-eval-parents.d.ts +7 -0
  139. package/dist/cli/commands/loop-eval-parents.js +20 -0
  140. package/dist/cli/commands/loop-eval-run-code.d.ts +7 -0
  141. package/dist/cli/commands/loop-eval-run-code.js +40 -0
  142. package/dist/cli/commands/loop-eval-run-command.d.ts +3 -0
  143. package/dist/cli/commands/loop-eval-run-command.js +113 -0
  144. package/dist/cli/commands/loop-eval-spec-commands.d.ts +3 -0
  145. package/dist/cli/commands/loop-eval-spec-commands.js +165 -0
  146. package/dist/cli/commands/loop-eval-spec-lint-command.d.ts +3 -0
  147. package/dist/cli/commands/loop-eval-spec-lint-command.js +42 -0
  148. package/dist/cli/commands/loop-eval-workflow-commands.d.ts +3 -0
  149. package/dist/cli/commands/loop-eval-workflow-commands.js +141 -0
  150. package/dist/cli/commands/mcp-commands.d.ts +10 -0
  151. package/dist/cli/commands/mcp-commands.js +157 -0
  152. package/dist/cli/commands/request-command-options.d.ts +49 -0
  153. package/dist/cli/commands/request-command-options.js +1 -0
  154. package/dist/cli/commands/request-commands.d.ts +3 -9
  155. package/dist/cli/commands/request-commands.js +17 -522
  156. package/dist/cli/commands/request-init-command.d.ts +3 -0
  157. package/dist/cli/commands/request-init-command.js +100 -0
  158. package/dist/cli/commands/request-lint-command.d.ts +3 -0
  159. package/dist/cli/commands/request-lint-command.js +50 -0
  160. package/dist/cli/commands/request-list-command.d.ts +10 -0
  161. package/dist/cli/commands/request-list-command.js +53 -0
  162. package/dist/cli/commands/request-repair-status-command.d.ts +3 -0
  163. package/dist/cli/commands/request-repair-status-command.js +61 -0
  164. package/dist/cli/commands/request-show-command.d.ts +3 -0
  165. package/dist/cli/commands/request-show-command.js +56 -0
  166. package/dist/cli/commands/request-transition-command.d.ts +3 -0
  167. package/dist/cli/commands/request-transition-command.js +122 -0
  168. package/dist/cli/commands/request-transition-failures.d.ts +8 -0
  169. package/dist/cli/commands/request-transition-failures.js +90 -0
  170. package/dist/cli/commands/request-transition-hooks.d.ts +50 -0
  171. package/dist/cli/commands/request-transition-hooks.js +115 -0
  172. package/dist/cli/commands/request-transition-preflight.d.ts +10 -0
  173. package/dist/cli/commands/request-transition-preflight.js +107 -0
  174. package/dist/cli/commands/scan-command-shared.d.ts +96 -0
  175. package/dist/cli/commands/scan-command-shared.js +83 -0
  176. package/dist/cli/commands/scan-commands.d.ts +3 -2
  177. package/dist/cli/commands/scan-commands.js +23 -463
  178. package/dist/cli/commands/scan-inventory-commands.d.ts +3 -0
  179. package/dist/cli/commands/scan-inventory-commands.js +96 -0
  180. package/dist/cli/commands/scan-project-commands.d.ts +3 -0
  181. package/dist/cli/commands/scan-project-commands.js +65 -0
  182. package/dist/cli/commands/scan-scope-commands.d.ts +3 -0
  183. package/dist/cli/commands/scan-scope-commands.js +149 -0
  184. package/dist/cli/commands/scan-scope-helpers.d.ts +8 -0
  185. package/dist/cli/commands/scan-scope-helpers.js +54 -0
  186. package/dist/cli/commands/scan-structural-commands.d.ts +3 -0
  187. package/dist/cli/commands/scan-structural-commands.js +113 -0
  188. package/dist/cli/commands/share-commands.d.ts +12 -68
  189. package/dist/cli/commands/share-commands.js +47 -556
  190. package/dist/cli/commands/share-error-hints.d.ts +14 -0
  191. package/dist/cli/commands/share-error-hints.js +38 -0
  192. package/dist/cli/commands/share-identity.d.ts +13 -0
  193. package/dist/cli/commands/share-identity.js +22 -0
  194. package/dist/cli/commands/share-record-paths.d.ts +37 -0
  195. package/dist/cli/commands/share-record-paths.js +68 -0
  196. package/dist/cli/commands/share-runners.d.ts +3 -0
  197. package/dist/cli/commands/share-runners.js +158 -0
  198. package/dist/cli/commands/shared-read-runners.d.ts +3 -0
  199. package/dist/cli/commands/shared-read-runners.js +51 -0
  200. package/dist/cli/commands/slice-check-command.d.ts +3 -0
  201. package/dist/cli/commands/slice-check-command.js +54 -0
  202. package/dist/cli/commands/slice-commands.d.ts +2 -22
  203. package/dist/cli/commands/slice-commands.js +18 -517
  204. package/dist/cli/commands/slice-decompose-artifacts.d.ts +18 -0
  205. package/dist/cli/commands/slice-decompose-artifacts.js +79 -0
  206. package/dist/cli/commands/slice-decompose-command.d.ts +3 -0
  207. package/dist/cli/commands/slice-decompose-command.js +123 -0
  208. package/dist/cli/commands/slice-list-commands.d.ts +3 -0
  209. package/dist/cli/commands/slice-list-commands.js +182 -0
  210. package/dist/cli/commands/slice-pick-command.d.ts +3 -0
  211. package/dist/cli/commands/slice-pick-command.js +60 -0
  212. package/dist/cli/commands/slice-plan-command.d.ts +24 -0
  213. package/dist/cli/commands/slice-plan-command.js +111 -0
  214. package/dist/cli/commands/workflow-commands.d.ts +10 -1
  215. package/dist/cli/commands/workflow-commands.js +6 -594
  216. package/dist/cli/commands/workflow-gate-commands.d.ts +3 -0
  217. package/dist/cli/commands/workflow-gate-commands.js +116 -0
  218. package/dist/cli/commands/workflow-plan-helpers.d.ts +48 -0
  219. package/dist/cli/commands/workflow-plan-helpers.js +103 -0
  220. package/dist/cli/commands/workflow-planning-commands.d.ts +3 -0
  221. package/dist/cli/commands/workflow-planning-commands.js +56 -0
  222. package/dist/cli/commands/workflow-swarm-actions.d.ts +7 -0
  223. package/dist/cli/commands/workflow-swarm-actions.js +109 -0
  224. package/dist/cli/commands/workflow-swarm-commands.d.ts +3 -0
  225. package/dist/cli/commands/workflow-swarm-commands.js +138 -0
  226. package/dist/cli/commands/workflow-tech-actions.d.ts +10 -0
  227. package/dist/cli/commands/workflow-tech-actions.js +150 -0
  228. package/dist/cli/commands/workspace/init-actions.d.ts +10 -0
  229. package/dist/cli/commands/workspace/init-actions.js +199 -0
  230. package/dist/cli/commands/workspace/init-codegraph-stake.d.ts +10 -0
  231. package/dist/cli/commands/workspace/init-codegraph-stake.js +54 -0
  232. package/dist/cli/commands/workspace/init-command.d.ts +8 -124
  233. package/dist/cli/commands/workspace/init-command.js +59 -623
  234. package/dist/cli/commands/workspace/init-context.d.ts +58 -0
  235. package/dist/cli/commands/workspace/init-context.js +121 -0
  236. package/dist/cli/commands/workspace/init-envelope.d.ts +3 -0
  237. package/dist/cli/commands/workspace/init-envelope.js +34 -0
  238. package/dist/cli/commands/workspace/init-failures.d.ts +3 -0
  239. package/dist/cli/commands/workspace/init-failures.js +84 -0
  240. package/dist/cli/commands/workspace/init-hooks-decision.d.ts +24 -0
  241. package/dist/cli/commands/workspace/init-hooks-decision.js +189 -0
  242. package/dist/cli/commands/workspace/init-options.d.ts +102 -0
  243. package/dist/cli/commands/workspace/init-options.js +1 -0
  244. package/dist/cli/commands/worktree-lease-commands.d.ts +10 -6
  245. package/dist/cli/commands/worktree-lease-commands.js +42 -512
  246. package/dist/cli/commands/worktree-lease-gc-command.d.ts +12 -0
  247. package/dist/cli/commands/worktree-lease-gc-command.js +117 -0
  248. package/dist/cli/commands/worktree-lease-git.d.ts +22 -0
  249. package/dist/cli/commands/worktree-lease-git.js +48 -0
  250. package/dist/cli/commands/worktree-lease-list-command.d.ts +12 -0
  251. package/dist/cli/commands/worktree-lease-list-command.js +100 -0
  252. package/dist/cli/commands/worktree-lease-load.d.ts +37 -0
  253. package/dist/cli/commands/worktree-lease-load.js +28 -0
  254. package/dist/cli/commands/worktree-lease-release-command.d.ts +13 -0
  255. package/dist/cli/commands/worktree-lease-release-command.js +105 -0
  256. package/dist/cli/commands/worktree-lease-renew-command.d.ts +11 -0
  257. package/dist/cli/commands/worktree-lease-renew-command.js +99 -0
  258. package/dist/cli/commands/worktree-lease-session.d.ts +22 -0
  259. package/dist/cli/commands/worktree-lease-session.js +29 -0
  260. package/dist/cli/commands/worktree-lease-spawn-command.d.ts +11 -0
  261. package/dist/cli/commands/worktree-lease-spawn-command.js +119 -0
  262. package/dist/cli/commands/worktree-lease-status-command.d.ts +11 -0
  263. package/dist/cli/commands/worktree-lease-status-command.js +86 -0
  264. package/dist/cli/commands/worktree-lease-store.d.ts +19 -0
  265. package/dist/cli/commands/worktree-lease-store.js +24 -0
  266. package/dist/cli/program.js +5 -0
  267. package/dist/services/distribution/mcp-install-runner.d.ts +21 -0
  268. package/dist/services/distribution/mcp-install-runner.js +59 -0
  269. package/dist/services/distribution/mcp-install.d.ts +95 -0
  270. package/dist/services/distribution/mcp-install.js +179 -0
  271. package/dist/services/distribution/plugin-manifest.d.ts +97 -0
  272. package/dist/services/distribution/plugin-manifest.js +126 -0
  273. package/dist/services/hooks/mcp-surface-gate.d.ts +19 -0
  274. package/dist/services/hooks/mcp-surface-gate.js +87 -0
  275. package/dist/services/ide/adapters/claude-code-adapter.d.ts +4 -35
  276. package/dist/services/ide/adapters/claude-code-adapter.js +48 -171
  277. package/dist/services/ide/adapters/claude-code-local-state.d.ts +28 -0
  278. package/dist/services/ide/adapters/claude-code-local-state.js +112 -0
  279. package/dist/services/ide/adapters/claude-code-model-window.d.ts +37 -0
  280. package/dist/services/ide/adapters/claude-code-model-window.js +113 -0
  281. package/dist/services/ide/ide-mcp-install-types.d.ts +78 -0
  282. package/dist/services/ide/ide-mcp-install-types.js +104 -0
  283. package/dist/services/ide/ide-types.d.ts +45 -1
  284. package/dist/services/ide/mcp-tool-matcher.d.ts +34 -0
  285. package/dist/services/ide/mcp-tool-matcher.js +84 -0
  286. package/dist/services/lint/npx-resolver.js +7 -0
  287. package/dist/services/mcp/cli-executor.d.ts +36 -0
  288. package/dist/services/mcp/cli-executor.js +97 -0
  289. package/dist/services/mcp/json-rpc.d.ts +81 -0
  290. package/dist/services/mcp/json-rpc.js +116 -0
  291. package/dist/services/mcp/memory-tool.d.ts +3 -0
  292. package/dist/services/mcp/memory-tool.js +62 -0
  293. package/dist/services/mcp/server-main.d.ts +1 -0
  294. package/dist/services/mcp/server-main.js +66 -0
  295. package/dist/services/mcp/server.d.ts +22 -0
  296. package/dist/services/mcp/server.js +152 -0
  297. package/dist/services/mcp/surface.d.ts +62 -0
  298. package/dist/services/mcp/surface.js +198 -0
  299. package/dist/services/mcp/tool-core.d.ts +42 -0
  300. package/dist/services/mcp/tool-core.js +137 -0
  301. package/dist/services/mcp/tools.d.ts +13 -0
  302. package/dist/services/mcp/tools.js +58 -0
  303. package/dist/services/readonly-surface/argv-guard.d.ts +32 -0
  304. package/dist/services/readonly-surface/argv-guard.js +164 -0
  305. package/dist/services/readonly-surface/readonly-whitelist.d.ts +147 -0
  306. package/dist/services/readonly-surface/readonly-whitelist.js +107 -0
  307. package/dist/services/readonly-surface/whitelist-generator.d.ts +64 -0
  308. package/dist/services/readonly-surface/whitelist-generator.js +212 -0
  309. package/dist/services/skills/hooks-codegate-superpowers.d.ts +1 -0
  310. package/dist/services/skills/hooks-codegate-superpowers.js +26 -0
  311. package/dist/services/skills/hooks-settings-service.js +10 -2
  312. package/package.json +17 -6
  313. package/scripts/install-skills-bundles.mjs +233 -0
  314. package/scripts/install-skills-fs.mjs +148 -0
  315. package/scripts/install-skills-ide.mjs +279 -0
  316. package/scripts/install-skills-output-style-default.mjs +196 -0
  317. package/scripts/install-skills-output-styles.mjs +121 -0
  318. package/scripts/install-skills-report.mjs +42 -0
  319. package/scripts/install-skills-user-config.mjs +198 -0
  320. package/scripts/install-skills.mjs +36 -1066
  321. package/scripts/watch-tree.mjs +140 -0
  322. package/scripts/watch.mjs +4 -126
  323. package/skills/peaks-status/SKILL.md +31 -13
@@ -0,0 +1,123 @@
1
+ import { mkdirSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { resolveCanonicalProjectRoot } from '../../services/config/config-service.js';
4
+ import { decomposeSlices } from '../../services/slice/slice-decompose-service.js';
5
+ import { decomposeSlicesWithBenchmark } from '../../services/slice/slice-benchmark-service.js';
6
+ import { decompose as multiPassDecompose } from '../../services/slice/multi-pass-orchestrator.js';
7
+ import { writeResult as writeSchemaResult } from '../../services/slice/schema-router.js';
8
+ import { fail, ok } from 'peaks-loop-shared/result';
9
+ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
10
+ import { readPrdBody, writeBenchmarkArtifact, writeDecompositionFile } from './slice-decompose-artifacts.js';
11
+ const DECOMPOSE_DESCRIPTION = 'Run the 6-stage slice-decomposition algorithm on a PRD. ' +
12
+ 'Inputs: PRD body + peaks codegraph (project analysis is codegraph-first; structural import edges are the fallback). ' +
13
+ 'Outputs: .peaks/sc/slice-decomposition/<rid>.json with critical-path, ' +
14
+ 'parallel-batches, and per-slice work estimates. ' +
15
+ 'Algorithm is fzf-free. Replay vs hand-derived 2.1.0 dry-run: +-10% on p50. ' +
16
+ 'Pass --benchmark to also emit a SliceBenchmark envelope (totalMs, codegraphQueries, ' +
17
+ 'p50ConfidenceDistribution, outputJsonBytes) and persist it under ' +
18
+ '.peaks/_runtime/<sid>/benchmarks/<rid>.benchmark.json for cross-version comparison.';
19
+ const DECOMPOSE_PROJECT_HELP = 'target project root';
20
+ const DECOMPOSE_REFRESH_HELP = 're-run `peaks codegraph index` before reading';
21
+ const DECOMPOSE_BENCHMARK_HELP = 'record per-run metrics and attach to the result envelope (2.1.1 algorithm optimization comparison)';
22
+ const DECOMPOSE_GRANULARITY_HELP = 'service | file | both | auto. Default "both" keeps the v1 6-stage path; service / file / auto enable v2 multi-pass decomposition (peaks-loop 2.9+) and emit a DecompositionResultV2 envelope via SchemaRouter';
23
+ const DECOMPOSE_FAILED_HINTS = [
24
+ 'Verify codegraph is initialised (npx codegraph init && npx codegraph index), the rid is correct, and the PRD body is non-empty'
25
+ ];
26
+ function isValidGranularity(value) {
27
+ return value === 'service' || value === 'file' || value === 'both' || value === 'auto';
28
+ }
29
+ function failInvalidGranularity(io, rid, options, granularity) {
30
+ printResult(io, fail('slice.decompose', 'SLICE_DECOMPOSE_FAILED', `Invalid --granularity '${granularity}'.`, { rid, projectRoot: options.project }, [
31
+ `--granularity accepts one of: service, file, both, auto`,
32
+ `Default (omit the flag or pass "both") keeps the existing v1 path.`,
33
+ `Non-default values (service / file / auto) enable v2 multi-pass decomposition (peaks-loop 2.9+).`
34
+ ]), options.json ?? false);
35
+ process.exitCode = 1;
36
+ }
37
+ async function runDecomposeV2(ctx, granularity) {
38
+ const { rid, prdMarkdown, projectRoot, options, io } = ctx;
39
+ const v2Result = await multiPassDecompose(rid, prdMarkdown, projectRoot, {
40
+ ...(options.refresh ? { refresh: true } : {}),
41
+ granularity
42
+ });
43
+ const outDir = join(projectRoot, '.peaks', 'sc', 'slice-decomposition');
44
+ if (!existsSync(outDir)) {
45
+ mkdirSync(outDir, { recursive: true });
46
+ }
47
+ const outPath = join(outDir, `${rid}.json`);
48
+ writeSchemaResult(outPath, v2Result);
49
+ const nextActions = [
50
+ `Decomposition (v2) written to ${outPath}`,
51
+ `peaks slice pick/plan: v2 schemas require SchemaRouter-aware consumers (peaks-loop 2.9+).`
52
+ ];
53
+ printResult(io, ok('slice.decompose', { ...v2Result, outputPath: outPath }, [], nextActions), options.json ?? false);
54
+ }
55
+ function benchmarkNextAction(rid, benchmark, projectRoot) {
56
+ const benchPath = writeBenchmarkArtifact(rid, benchmark, projectRoot);
57
+ return (`Benchmark: totalMs=${benchmark.totalMs} codegraphQueries=${benchmark.codegraphQueries} ` +
58
+ `p50Conf={low:${benchmark.p50ConfidenceDistribution.low},mid:${benchmark.p50ConfidenceDistribution.mid},high:${benchmark.p50ConfidenceDistribution.high}} ` +
59
+ `outputJsonBytes=${benchmark.outputJsonBytes}. Persisted to ${benchPath}`);
60
+ }
61
+ async function runDecomposeV1(ctx) {
62
+ const { rid, prdMarkdown, projectRoot, options, io } = ctx;
63
+ let result;
64
+ let benchmark = null;
65
+ if (options.benchmark === true) {
66
+ const out = await decomposeSlicesWithBenchmark(rid, prdMarkdown, projectRoot, {
67
+ ...(options.refresh ? { refresh: true } : {})
68
+ });
69
+ result = out.result;
70
+ benchmark = out.benchmark;
71
+ }
72
+ else {
73
+ result = await decomposeSlices(rid, prdMarkdown, projectRoot, {
74
+ ...(options.refresh ? { refresh: true } : {})
75
+ });
76
+ }
77
+ const outPath = writeDecompositionFile(rid, result, projectRoot);
78
+ const nextActions = [
79
+ `Decomposition written to ${outPath}`,
80
+ `Next: peaks slice pick ${rid} (requires fzf >= 0.38)`,
81
+ `Or manually craft -picked.json from the JSON output, then peaks slice plan ${rid}`
82
+ ];
83
+ if (result.pickHint) {
84
+ nextActions.push(result.pickHint);
85
+ }
86
+ if (benchmark !== null) {
87
+ nextActions.push(benchmarkNextAction(rid, benchmark, projectRoot));
88
+ }
89
+ printResult(io, ok('slice.decompose', { ...result, outputPath: outPath, ...(benchmark !== null ? { benchmark } : {}) }, [], nextActions), options.json ?? false);
90
+ }
91
+ async function runSliceDecompose(rid, options, io) {
92
+ // Validate --granularity BEFORE any I/O so invalid values fail fast with a
93
+ // nextActions hint listing the four allowed strings.
94
+ const granularity = options.granularity ?? 'both';
95
+ if (!isValidGranularity(granularity)) {
96
+ failInvalidGranularity(io, rid, options, granularity);
97
+ return;
98
+ }
99
+ try {
100
+ const projectRoot = resolveCanonicalProjectRoot(options.project);
101
+ const prdMarkdown = readPrdBody(rid, projectRoot);
102
+ const ctx = { rid, prdMarkdown, projectRoot, options, io };
103
+ // Non-default granularity takes the v2 path; default ("both") keeps v1.
104
+ if (granularity !== 'both') {
105
+ await runDecomposeV2(ctx, granularity);
106
+ return;
107
+ }
108
+ await runDecomposeV1(ctx);
109
+ }
110
+ catch (error) {
111
+ printResult(io, fail('slice.decompose', 'SLICE_DECOMPOSE_FAILED', getErrorMessage(error), { rid, projectRoot: options.project }, DECOMPOSE_FAILED_HINTS), options.json ?? false);
112
+ process.exitCode = 1;
113
+ }
114
+ }
115
+ export function registerSliceDecomposeCommand(slice, io) {
116
+ addJsonOption(slice
117
+ .command('decompose <rid>')
118
+ .description(DECOMPOSE_DESCRIPTION)
119
+ .option('--project <path>', DECOMPOSE_PROJECT_HELP, '.')
120
+ .option('--refresh', DECOMPOSE_REFRESH_HELP, false)
121
+ .option('--benchmark', DECOMPOSE_BENCHMARK_HELP, false)
122
+ .option('--granularity <value>', DECOMPOSE_GRANULARITY_HELP, 'both')).action((rid, options) => runSliceDecompose(rid, options, io));
123
+ }
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ import { type ProgramIO } from '../cli-helpers.js';
3
+ export declare function registerSliceListCommand(slice: Command, io: ProgramIO): void;
@@ -0,0 +1,182 @@
1
+ import { existsSync, readdirSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { resolveCanonicalProjectRoot } from '../../services/config/config-service.js';
4
+ import { fail, ok } from 'peaks-loop-shared/result';
5
+ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
6
+ /**
7
+ * PRD-002b slice 2 — extract CLI listing-pagination + table-format
8
+ * constants. Values are bytewise-identical to the original literals.
9
+ */
10
+ const SLICE_LIST_DEFAULT_LIMIT = 50;
11
+ const SLICE_LIST_MAX_LIMIT = 500;
12
+ const SLICE_LIST_RID_COL_WIDTH = 34;
13
+ const SLICE_LIST_MTIME_COL_WIDTH = 22;
14
+ const SLICE_LIST_SIZE_COL_WIDTH = 8;
15
+ const SLICE_LIST_PICKED_COL_WIDTH = 6;
16
+ const SLICE_LIST_SEPARATOR_WIDTH = 76;
17
+ const LIST_DESCRIPTION = 'List slice decomposition artifacts under .peaks/sc/slice-decomposition/. ' +
18
+ 'Returns one row per distinct rid with mtime, sizeBytes, pickedPath, and isStale ' +
19
+ '(mtime > 30d). Use --stale-only to filter, --rid <substring> to narrow, ' +
20
+ '--limit <n> to cap. To remove stale entries, use `peaks slice cleanup` (separate slice).';
21
+ const LIST_PROJECT_HELP = 'target project root';
22
+ const LIST_LIMIT_HELP = 'cap result count (default 50, max 500)';
23
+ const LIST_STALE_ONLY_HELP = 'only include rids older than the stale threshold';
24
+ const LIST_RID_HELP = 'case-insensitive substring filter on rid';
25
+ /**
26
+ * Stale threshold for slice decomposition artifacts. Matches the default
27
+ * recorded in .peaks/memory/ for follow-up). If that slice ships with a
28
+ * different default, both must update here.
29
+ */
30
+ // eslint-disable-next-line no-magic-numbers -- canonical 30-day-in-ms math
31
+ const STALE_THRESHOLD_MS = 30 * 24 * 60 * 60 * 1000;
32
+ /**
33
+ * Enumerate every slice decomposition artifact under
34
+ * `.peaks/sc/slice-decomposition/`. Returns one row per distinct rid.
35
+ * Missing directory is treated as empty (returns []), matching the
36
+ * "fresh peaks repo" UX (AC5+AC6).
37
+ *
38
+ * Sorted by mtime descending so the most recent rid is first (AC3).
39
+ */
40
+ function listDecompositions(projectRoot) {
41
+ const dir = join(projectRoot, '.peaks', 'sc', 'slice-decomposition');
42
+ if (!existsSync(dir))
43
+ return [];
44
+ const nowMs = Date.now();
45
+ const byRid = new Map();
46
+ for (const entry of readdirSync(dir)) {
47
+ // Skip non-matching files (e.g., stray .DS_Store)
48
+ if (!entry.endsWith('.json'))
49
+ continue;
50
+ let rid;
51
+ let isPicked;
52
+ if (entry.endsWith('-picked.json')) {
53
+ rid = entry.slice(0, -'-picked.json'.length);
54
+ isPicked = true;
55
+ }
56
+ else {
57
+ rid = entry.slice(0, -'.json'.length);
58
+ isPicked = false;
59
+ }
60
+ const absPath = join(dir, entry);
61
+ const st = statSync(absPath);
62
+ const existing = byRid.get(rid);
63
+ if (isPicked) {
64
+ // Picked file is supplementary; only fill in pickedPath on the existing row
65
+ if (existing) {
66
+ byRid.set(rid, { ...existing, pickedPath: absPath });
67
+ }
68
+ else {
69
+ // -picked.json without a corresponding <rid>.json: still surface it
70
+ byRid.set(rid, {
71
+ rid,
72
+ decompositionPath: '',
73
+ pickedPath: absPath,
74
+ mtime: st.mtime.toISOString(),
75
+ sizeBytes: 0,
76
+ isStale: nowMs - st.mtime.getTime() > STALE_THRESHOLD_MS
77
+ });
78
+ }
79
+ continue;
80
+ }
81
+ byRid.set(rid, {
82
+ rid,
83
+ decompositionPath: absPath,
84
+ pickedPath: existing?.pickedPath ?? null,
85
+ mtime: st.mtime.toISOString(),
86
+ sizeBytes: st.size,
87
+ isStale: nowMs - st.mtime.getTime() > STALE_THRESHOLD_MS
88
+ });
89
+ }
90
+ return Array.from(byRid.values()).sort((a, b) => a.mtime < b.mtime ? 1 : a.mtime > b.mtime ? -1 : 0);
91
+ }
92
+ function filterDecompositions(all, options) {
93
+ let filtered = all;
94
+ const ridSub = (options.rid ?? '').toLowerCase();
95
+ if (ridSub.length > 0) {
96
+ filtered = filtered.filter((row) => row.rid.toLowerCase().includes(ridSub));
97
+ }
98
+ if (options.staleOnly === true) {
99
+ filtered = filtered.filter((row) => row.isStale);
100
+ }
101
+ return filtered;
102
+ }
103
+ function sliceListNextActions(page, truncated, limit) {
104
+ const nextActions = [];
105
+ if (truncated) {
106
+ nextActions.push(`Result truncated to ${limit} rows; pass --limit <n> to see more.`);
107
+ }
108
+ if (page.some((r) => r.isStale)) {
109
+ nextActions.push('Some entries are stale (>30d). Use `peaks slice cleanup` to remove them.');
110
+ }
111
+ return nextActions;
112
+ }
113
+ function printSliceListJson(io, page, ctx) {
114
+ printResult(io, ok('slice.ls', {
115
+ rids: page,
116
+ truncated: ctx.truncated,
117
+ totalBeforeFilter: ctx.filteredCount,
118
+ totalScanned: ctx.allCount
119
+ }, [], sliceListNextActions(page, ctx.truncated, ctx.limit)), true);
120
+ }
121
+ function printSliceListTable(io, page, ctx) {
122
+ const lines = [];
123
+ lines.push('RID'.padEnd(SLICE_LIST_RID_COL_WIDTH) +
124
+ 'MTIME'.padEnd(SLICE_LIST_MTIME_COL_WIDTH) +
125
+ 'SIZE'.padEnd(SLICE_LIST_SIZE_COL_WIDTH) +
126
+ 'PICKED'.padEnd(SLICE_LIST_PICKED_COL_WIDTH) +
127
+ 'STALE');
128
+ lines.push('-'.repeat(SLICE_LIST_SEPARATOR_WIDTH));
129
+ for (const r of page) {
130
+ lines.push(r.rid.padEnd(SLICE_LIST_RID_COL_WIDTH) +
131
+ r.mtime.padEnd(SLICE_LIST_MTIME_COL_WIDTH) +
132
+ String(r.sizeBytes).padEnd(SLICE_LIST_SIZE_COL_WIDTH) +
133
+ (r.pickedPath ? 'yes' : 'no').padEnd(SLICE_LIST_PICKED_COL_WIDTH) +
134
+ (r.isStale ? 'yes' : 'no'));
135
+ }
136
+ if (ctx.truncated) {
137
+ lines.push(`... (${ctx.filteredCount - ctx.limit} more, --limit to see more)`);
138
+ }
139
+ if (page.length === 0) {
140
+ lines.push('(no slice decompositions found)');
141
+ }
142
+ process.stdout.write(lines.join('\n') + '\n');
143
+ }
144
+ function runSliceList(options, io) {
145
+ try {
146
+ const projectRoot = resolveCanonicalProjectRoot(options.project);
147
+ const limit = Math.max(1, Math.min(SLICE_LIST_MAX_LIMIT, options.limit ?? SLICE_LIST_DEFAULT_LIMIT));
148
+ const all = listDecompositions(projectRoot);
149
+ const filtered = filterDecompositions(all, options);
150
+ const truncated = filtered.length > limit;
151
+ const page = filtered.slice(0, limit);
152
+ const ctx = {
153
+ truncated,
154
+ filteredCount: filtered.length,
155
+ allCount: all.length,
156
+ limit
157
+ };
158
+ if (options.json === true) {
159
+ printSliceListJson(io, page, ctx);
160
+ return;
161
+ }
162
+ printSliceListTable(io, page, ctx);
163
+ }
164
+ catch (error) {
165
+ printResult(io, fail('slice.ls', 'SLICE_LS_FAILED', getErrorMessage(error), { projectRoot: options.project }, [
166
+ 'Verify the project path is a peaks repo and .peaks/sc/slice-decomposition/ exists or can be created'
167
+ ]), options.json ?? false);
168
+ process.exitCode = 1;
169
+ }
170
+ }
171
+ // Read-only listing of every decomposition artifact under
172
+ // .peaks/sc/slice-decomposition/. Used by operators to see what's
173
+ // accumulated; companion to a future `peaks slice cleanup` subcommand.
174
+ export function registerSliceListCommand(slice, io) {
175
+ addJsonOption(slice
176
+ .command('ls')
177
+ .description(LIST_DESCRIPTION)
178
+ .option('--project <path>', LIST_PROJECT_HELP, '.')
179
+ .option('--limit <n>', LIST_LIMIT_HELP, (v) => parseInt(v, 10), SLICE_LIST_DEFAULT_LIMIT)
180
+ .option('--stale-only', LIST_STALE_ONLY_HELP, false)
181
+ .option('--rid <substring>', LIST_RID_HELP, '')).action((options) => runSliceList(options, io));
182
+ }
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ import { type ProgramIO } from '../cli-helpers.js';
3
+ export declare function registerSlicePickCommand(slice: Command, io: ProgramIO): void;
@@ -0,0 +1,60 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { resolveCanonicalProjectRoot } from '../../services/config/config-service.js';
4
+ import { pickSlicesInteractive } from '../../services/slice/slice-pick-service.js';
5
+ import { readResult as readDecompositionResult } from '../../services/slice/schema-router.js';
6
+ import { fail, ok } from 'peaks-loop-shared/result';
7
+ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
8
+ const PICK_DESCRIPTION = 'Interactively select which candidate slices to ship, via fzf. ' +
9
+ 'Reads .peaks/sc/slice-decomposition/<rid>.json, ' +
10
+ 'spawns fzf --multi, parses selection, writes -picked.json. ' +
11
+ 'Requires fzf >= 0.38. Algorithm is fzf-free; this is the only fzf dependency.';
12
+ const PICK_PROJECT_HELP = 'target project root';
13
+ const PICK_PREVIEW_HELP = 'render side-by-side preview window in fzf';
14
+ const PICK_FZF_BIN_HELP = 'override fzf binary path (default: fzf on PATH)';
15
+ async function runSlicePick(rid, options, io) {
16
+ try {
17
+ const projectRoot = resolveCanonicalProjectRoot(options.project);
18
+ const decompPath = join(projectRoot, '.peaks', 'sc', 'slice-decomposition', `${rid}.json`);
19
+ if (!existsSync(decompPath)) {
20
+ throw new Error(`decomposition not found at ${decompPath}. ` + `Run \`peaks slice decompose ${rid}\` first.`);
21
+ }
22
+ const parsed = readDecompositionResult(decompPath);
23
+ if ('schemaVersion' in parsed) {
24
+ throw new Error(`decomposition at ${decompPath} is a v2 envelope (schemaVersion: 'v2'). ` +
25
+ `peaks slice pick supports v1 only in peaks-loop 2.9.0. ` +
26
+ `Re-run \`peaks slice decompose ${rid}\` without --granularity to get a v1 file, ` +
27
+ `or upgrade peaks-loop when v2 pick lands.`);
28
+ }
29
+ const decomposition = parsed;
30
+ const result = await pickSlicesInteractive(rid, decomposition, projectRoot, {
31
+ ...(options.preview !== undefined ? { preview: options.preview } : {}),
32
+ ...(options.fzfBin ? { fzfBin: options.fzfBin } : {})
33
+ });
34
+ const nextActions = [
35
+ `Picked ${result.picked.length} slice(s); written to ${result.outputPath}`,
36
+ `Next: peaks slice plan ${rid} (--apply to call peaks request init for each chosen slice)`
37
+ ];
38
+ printResult(io, ok('slice.pick', result, [], nextActions), options.json ?? false);
39
+ }
40
+ catch (error) {
41
+ const msg = getErrorMessage(error);
42
+ if (/brew install fzf|apt-get install fzf|older than required/.test(msg)) {
43
+ process.exitCode = 127;
44
+ }
45
+ else {
46
+ process.exitCode = 1;
47
+ }
48
+ printResult(io, fail('slice.pick', 'SLICE_PICK_FAILED', msg, { rid, projectRoot: options.project }, [
49
+ 'Verify the decomposition file exists, fzf >= 0.38 is on PATH, and the rid is correct'
50
+ ]), options.json ?? false);
51
+ }
52
+ }
53
+ export function registerSlicePickCommand(slice, io) {
54
+ addJsonOption(slice
55
+ .command('pick <rid>')
56
+ .description(PICK_DESCRIPTION)
57
+ .option('--project <path>', PICK_PROJECT_HELP, '.')
58
+ .option('--preview', PICK_PREVIEW_HELP, false)
59
+ .option('--fzf-bin <path>', PICK_FZF_BIN_HELP, 'fzf')).action((rid, options) => runSlicePick(rid, options, io));
60
+ }
@@ -0,0 +1,24 @@
1
+ import type { Command } from 'commander';
2
+ import { type ProgramIO } from '../cli-helpers.js';
3
+ interface PickedEnvelope {
4
+ readonly rid: string;
5
+ readonly picked: readonly {
6
+ readonly rid: string;
7
+ readonly files: readonly string[];
8
+ readonly label: string;
9
+ }[];
10
+ }
11
+ /**
12
+ * Validate a -picked.json envelope. Throws a CLI-friendly Error on any
13
+ * shape violation; the catch in the slice.plan action converts it to
14
+ * a fail() envelope with `code: 'PICKED_ENVELOPE_INVALID'`.
15
+ *
16
+ * Schema:
17
+ * { rid: string; picked: Array<{ rid, files: string[], label }> }
18
+ *
19
+ * Rejects: missing rid, missing/invalid picked array, picked[i] missing
20
+ * rid / files / label, picked[i].files empty.
21
+ */
22
+ export declare function parsePickedFile(pickedPath: string): PickedEnvelope;
23
+ export declare function registerSlicePlanCommand(slice: Command, io: ProgramIO): void;
24
+ export {};
@@ -0,0 +1,111 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { resolveCanonicalProjectRoot } from '../../services/config/config-service.js';
4
+ import { fail, ok } from 'peaks-loop-shared/result';
5
+ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
6
+ const PLAN_DESCRIPTION = 'Apply a picked batch: read -picked.json, call `peaks request init` for each chosen slice ' +
7
+ 'with --depends-on edges from the decomposition. Dry-run by default; pass --apply to ' +
8
+ 'actually create rids.';
9
+ const PLAN_PROJECT_HELP = 'target project root';
10
+ const PLAN_APPLY_HELP = 'actually call peaks request init (default: dry-run only)';
11
+ const PLAN_PICKED_MISSING_HINT = 'Verify the picked file exists, the rid is correct, and peaks request init is available';
12
+ const PLAN_PICKED_ENVELOPE_TOO_LONG = 'Verify the -picked.json envelope matches the schema: { rid: string, picked: Array<{ rid, files: string[], label }> }';
13
+ /**
14
+ * Validate a -picked.json envelope. Throws a CLI-friendly Error on any
15
+ * shape violation; the catch in the slice.plan action converts it to
16
+ * a fail() envelope with `code: 'PICKED_ENVELOPE_INVALID'`.
17
+ *
18
+ * Schema:
19
+ * { rid: string; picked: Array<{ rid, files: string[], label }> }
20
+ *
21
+ * Rejects: missing rid, missing/invalid picked array, picked[i] missing
22
+ * rid / files / label, picked[i].files empty.
23
+ */
24
+ export function parsePickedFile(pickedPath) {
25
+ let raw;
26
+ try {
27
+ raw = JSON.parse(readFileSync(pickedPath, 'utf8'));
28
+ }
29
+ catch (err) {
30
+ // JSON.parse errors are always Error-shaped (SyntaxError extends Error),
31
+ // so the cast is safe; we only need `.message`.
32
+ throw new Error(`picked envelope at ${pickedPath} is not valid JSON: ${err.message}`);
33
+ }
34
+ if (typeof raw !== 'object' || raw === null) {
35
+ throw new Error(`picked envelope at ${pickedPath} must be a JSON object, got ${typeof raw}`);
36
+ }
37
+ const obj = raw;
38
+ if (typeof obj.rid !== 'string' || obj.rid.length === 0) {
39
+ throw new Error(`picked envelope at ${pickedPath} is missing required string field 'rid'`);
40
+ }
41
+ if (!Array.isArray(obj.picked)) {
42
+ throw new Error(`picked envelope at ${pickedPath} is missing required array field 'picked'`);
43
+ }
44
+ const picked = obj.picked.map((item, idx) => {
45
+ if (typeof item !== 'object' || item === null) {
46
+ throw new Error(`picked[${idx}] must be an object, got ${typeof item}`);
47
+ }
48
+ const it = item;
49
+ if (typeof it.rid !== 'string' || it.rid.length === 0) {
50
+ throw new Error(`picked[${idx}] is missing required string field 'rid'`);
51
+ }
52
+ if (!Array.isArray(it.files) || it.files.length === 0) {
53
+ throw new Error(`picked[${idx}] is missing or has empty required array field 'files'`);
54
+ }
55
+ if (!it.files.every((f) => typeof f === 'string')) {
56
+ throw new Error(`picked[${idx}].files must contain only strings`);
57
+ }
58
+ if (typeof it.label !== 'string' || it.label.length === 0) {
59
+ throw new Error(`picked[${idx}] is missing required string field 'label'`);
60
+ }
61
+ return { rid: it.rid, files: it.files, label: it.label };
62
+ });
63
+ return { rid: obj.rid, picked };
64
+ }
65
+ function buildPlanEntries(rid, picked) {
66
+ return picked.picked.map((slice, idx) => ({
67
+ newRid: `${rid}-${idx + 1}-${slice.rid}`,
68
+ type: 'feat',
69
+ dependsOn: idx === 0 ? [] : [picked.picked[idx - 1].rid],
70
+ files: slice.files,
71
+ label: slice.label,
72
+ applied: false
73
+ }));
74
+ }
75
+ function slicePlanNextActions(picked, options) {
76
+ return [
77
+ `Planned ${picked.picked.length} new rids from ${picked.picked.length} picked slices`,
78
+ options.apply
79
+ ? 'Apply mode: would call peaks request init for each (v1.1: wire to spawn child)'
80
+ : 'Dry-run: pass --apply to actually create the rids (v1.1: wire to peaks request init spawn)'
81
+ ];
82
+ }
83
+ function printSlicePlanFailure(io, options, target, error) {
84
+ const msg = getErrorMessage(error);
85
+ const isEnvelopeError = msg.startsWith('picked envelope at');
86
+ printResult(io, fail('slice.plan', isEnvelopeError ? 'PICKED_ENVELOPE_INVALID' : 'SLICE_PLAN_FAILED', msg, { rid: target.rid, projectRoot: options.project, pickedPath: target.pickedPath }, isEnvelopeError ? [PLAN_PICKED_ENVELOPE_TOO_LONG] : [PLAN_PICKED_MISSING_HINT]), options.json ?? false);
87
+ process.exitCode = 1;
88
+ }
89
+ function runSlicePlan(rid, options, io) {
90
+ const projectRoot = resolveCanonicalProjectRoot(options.project);
91
+ const pickedPath = join(projectRoot, '.peaks', 'sc', 'slice-decomposition', `${rid}-picked.json`);
92
+ try {
93
+ if (!existsSync(pickedPath)) {
94
+ throw new Error(`picked file not found at ${pickedPath}. ` +
95
+ `Run \`peaks slice pick ${rid}\` first, or manually craft the file.`);
96
+ }
97
+ const picked = parsePickedFile(pickedPath);
98
+ const plan = buildPlanEntries(rid, picked);
99
+ printResult(io, ok('slice.plan', { parentRid: rid, plan, apply: options.apply ?? false }, [], slicePlanNextActions(picked, options)), options.json ?? false);
100
+ }
101
+ catch (error) {
102
+ printSlicePlanFailure(io, options, { rid, projectRoot, pickedPath }, error);
103
+ }
104
+ }
105
+ export function registerSlicePlanCommand(slice, io) {
106
+ addJsonOption(slice
107
+ .command('plan <rid>')
108
+ .description(PLAN_DESCRIPTION)
109
+ .option('--project <path>', PLAN_PROJECT_HELP, '.')
110
+ .option('--apply', PLAN_APPLY_HELP, false)).action((rid, options) => runSlicePlan(rid, options, io));
111
+ }
@@ -1,3 +1,12 @@
1
+ /**
2
+ * The `peaks refactor | tech | workflow | route | autonomous | swarm | recommend`
3
+ * planning surface.
4
+ *
5
+ * This module is the registrar: each family owns its own file, and the two helpers
6
+ * they share live in `workflow-plan-helpers.ts`. The `peaks workflow` parent is
7
+ * created by the planning family and handed back, so the gate family attaches to
8
+ * the same group instead of creating a second one.
9
+ */
1
10
  import type { Command } from 'commander';
2
- import { type ProgramIO } from '../cli-helpers.js';
11
+ import type { ProgramIO } from '../cli-helpers.js';
3
12
  export declare function registerWorkflowCommands(program: Command, io: ProgramIO): void;