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
@@ -1,468 +1,28 @@
1
- import { InvalidArgumentError } from 'commander';
2
- import { scanArchetype } from '../../services/scan/archetype-service.js';
3
- import { scanExistingSystem } from '../../services/scan/existing-system-service.js';
4
- import { checkTypeSanity } from '../../services/scan/type-sanity-service.js';
5
- import { getAcceptanceCoverage, isAcceptanceCoverageError } from '../../services/scan/acceptance-coverage-service.js';
6
- import { getDiffVsScope, isDiffScopeError } from '../../services/scan/diff-scope-service.js';
7
- import { scanFileSize } from '../../services/scan/file-size-scan.js';
8
- import { FILE_SIZE_CAP_DEFAULT, FILE_SIZE_CAP_TESTS } from '../../services/scan/file-size-policy.js';
9
- import { scanLibraries } from '../../services/scan/libraries-service.js';
10
- import { scanApiSurface, formatApiSurfaceMarkdown } from '../../services/scan/api-surface-service.js';
11
- import { scanOrphans, formatOrphanMarkdown } from '../../services/scan/orphan-service.js';
12
- import { scanKarpathy, formatKarpathyMarkdown } from '../../services/scan/karpathy-service.js';
13
- import { isRequestType, VALID_REQUEST_TYPES } from '../../services/artifacts/artifact-prerequisites.js';
14
- import { fail, ok } from 'peaks-loop-shared/result';
15
- import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
16
- function parsePositiveInt(value, fallback) {
17
- if (value === undefined)
18
- return fallback;
19
- if (!/^\d+$/.test(value))
20
- return fallback;
21
- const parsed = Number(value);
22
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
23
- }
24
- function parseRequestType(value) {
25
- if (!isRequestType(value)) {
26
- throw new InvalidArgumentError(`must be one of ${VALID_REQUEST_TYPES.join(', ')}`);
27
- }
28
- return value;
29
- }
1
+ // `peaks scan` — the deterministic project scans. This file owns the parent
2
+ // command only; each group of subcommands lives in its own sibling module:
3
+ //
4
+ // scan-project-commands.ts archetype, existing-system
5
+ // scan-scope-commands.ts request-type-sanity, acceptance-coverage,
6
+ // diff-vs-scope, file-size
7
+ // scan-inventory-commands.ts libraries, api-surface
8
+ // scan-structural-commands.ts orphan, karpathy
9
+ //
10
+ // The group registrars are called IN THE ORIGINAL SUBCOMMAND ORDER below.
11
+ // Commander lists `--help` entries in registration order, so that order is part
12
+ // of the command's observable output.
13
+ //
14
+ // Every runner takes its parsed options plus the ProgramIO, so these modules
15
+ // hold no shared mutable state.
16
+ import { registerScanInventoryCommands } from './scan-inventory-commands.js';
17
+ import { registerScanProjectCommands } from './scan-project-commands.js';
18
+ import { registerScanScopeCommands } from './scan-scope-commands.js';
19
+ import { registerScanStructuralCommands } from './scan-structural-commands.js';
30
20
  export function registerScanCommands(program, io) {
31
21
  const scan = program
32
22
  .command('scan')
33
23
  .description('Deterministic project scans (archetype, existing system) for Peaks workflows');
34
- addJsonOption(scan
35
- .command('archetype')
36
- .description('Detect project archetype, integration mode (three scenarios), frontend-only mode, and supporting signals from the filesystem (read-only)')
37
- .requiredOption('--project <path>', 'target project root')).action(async (options) => {
38
- try {
39
- const report = await scanArchetype({ projectRoot: options.project });
40
- const nextActions = [];
41
- if (report.archetype === 'unknown') {
42
- nextActions.push('Archetype could not be determined; surface to user before proceeding.');
43
- }
44
- else if (report.archetype === 'legacy-frontend' ||
45
- report.archetype === 'legacy-fullstack' ||
46
- report.archetype === 'frontend-monorepo') {
47
- nextActions.push('Run `peaks scan existing-system --project <path>` to extract visual tokens and conventions.');
48
- }
49
- printResult(io, ok('scan.archetype', report, [], nextActions), options.json);
50
- }
51
- catch (error) {
52
- printResult(io, fail('scan.archetype', 'SCAN_ARCHETYPE_FAILED', getErrorMessage(error), { projectRoot: options.project }, ['Verify the project path exists and is readable']), options.json);
53
- process.exitCode = 1;
54
- }
55
- });
56
- addJsonOption(scan
57
- .command('existing-system')
58
- .description('Extract visual tokens (colors/spacing/typography/radii) and code conventions from a legacy project (read-only)')
59
- .requiredOption('--project <path>', 'target project root')
60
- .option('--max-tokens <n>', 'maximum tokens to return per category (default 40)')
61
- .option('--max-samples <n>', 'maximum convention samples per kind (default 5)')).action(async (options) => {
62
- try {
63
- const report = await scanExistingSystem({
64
- projectRoot: options.project,
65
- maxTokens: parsePositiveInt(options.maxTokens, 40),
66
- maxSamplesPerKind: parsePositiveInt(options.maxSamples, 5)
67
- });
68
- const nextActions = [];
69
- if (!report.scanned) {
70
- nextActions.push(report.scanSkippedReason ?? 'Extraction skipped.');
71
- }
72
- else if (report.inconsistencies.length > 0) {
73
- nextActions.push('Surface inconsistencies in the TXT handoff before proceeding.');
74
- }
75
- printResult(io, ok('scan.existing-system', report, [], nextActions), options.json);
76
- }
77
- catch (error) {
78
- printResult(io, fail('scan.existing-system', 'SCAN_EXISTING_SYSTEM_FAILED', getErrorMessage(error), { projectRoot: options.project }, ['Verify the project path exists and is readable']), options.json);
79
- process.exitCode = 1;
80
- }
81
- });
82
- addJsonOption(scan
83
- .command('request-type-sanity')
84
- .description('Cross-verify a declared --type against the actual git diff file mix (catches "feature mis-declared as docs" workflow violations)')
85
- .requiredOption('--project <path>', 'target project root')
86
- .requiredOption('--type <type>', `declared request type (${VALID_REQUEST_TYPES.join(' | ')})`, parseRequestType)
87
- .option('--base-ref <ref>', 'compare working tree against this git ref (default: HEAD)')).action((options) => {
88
- try {
89
- const serviceOptions = {
90
- projectRoot: options.project,
91
- declaredType: options.type
92
- };
93
- if (options.baseRef !== undefined) {
94
- serviceOptions.baseRef = options.baseRef;
95
- }
96
- const report = checkTypeSanity(serviceOptions);
97
- const nextActions = [];
98
- if (!report.consistent) {
99
- nextActions.push(`Re-classify the request — likely correct type: ${report.suggestedTypes.join(' | ')}`);
100
- nextActions.push('Or, if the declared type is correct, surface the mismatch reason to the user in the TXT handoff.');
101
- }
102
- if (!report.gitAvailable) {
103
- nextActions.push('git not available; manual cross-check required.');
104
- }
105
- printResult(io, ok('scan.request-type-sanity', report, [], nextActions), options.json);
106
- if (!report.consistent) {
107
- process.exitCode = 1;
108
- }
109
- }
110
- catch (error) {
111
- if (error instanceof InvalidArgumentError)
112
- throw error;
113
- printResult(io, fail('scan.request-type-sanity', 'REQUEST_TYPE_SANITY_FAILED', getErrorMessage(error), { projectRoot: options.project, type: options.type }, ['Verify the project path is a git repository or omit the check']), options.json);
114
- process.exitCode = 1;
115
- }
116
- });
117
- addJsonOption(scan
118
- .command('acceptance-coverage')
119
- .description('Verify every PRD "Acceptance criteria" item has at least one linked QA test case (via `- **Acceptance:** A1, A2` field in qa/test-cases/<rid>.md)')
120
- .requiredOption('--rid <request-id>', 'request id')
121
- .requiredOption('--project <path>', 'target project root')
122
- .option('--session-id <session>', 'restrict to a specific session id')).action(async (options) => {
123
- try {
124
- const coverageOptions = {
125
- projectRoot: options.project,
126
- requestId: options.rid
127
- };
128
- if (options.sessionId !== undefined) {
129
- coverageOptions.sessionId = options.sessionId;
130
- }
131
- const result = await getAcceptanceCoverage(coverageOptions);
132
- if (isAcceptanceCoverageError(result)) {
133
- const code = result.kind === 'prd-not-found' ? 'PRD_NOT_FOUND' : 'TEST_CASES_NOT_FOUND';
134
- const message = result.kind === 'prd-not-found'
135
- ? `PRD artifact for requestId=${options.rid} not found`
136
- : `QA test-cases file not found at ${result.expectedPath}`;
137
- printResult(io, fail('scan.acceptance-coverage', code, message, { requestId: options.rid }, [
138
- result.kind === 'prd-not-found'
139
- ? 'Run `peaks request init --role prd --id <rid> --apply --type <type>` first.'
140
- : 'Generate qa/test-cases/<rid>.md before running this check.'
141
- ]), options.json);
142
- process.exitCode = 1;
143
- return;
144
- }
145
- const nextActions = [];
146
- if (result.acceptanceItems.length === 0) {
147
- nextActions.push('PRD has no "## Acceptance criteria" bullets. Fill them in before running the coverage check.');
148
- }
149
- if (result.uncovered.length > 0) {
150
- nextActions.push(`${result.uncovered.length} acceptance item(s) have no linked test case. Add a "- **Acceptance:** ${result.uncovered.map((u) => u.id).join(', ')}" field to the relevant test cases.`);
151
- }
152
- if (result.invalidReferences.length > 0) {
153
- nextActions.push(`${result.invalidReferences.length} test case(s) reference an acceptance id that does not exist in the PRD. Fix or remove these references.`);
154
- }
155
- if (result.unlinkedTestCases.length > 0) {
156
- nextActions.push(`${result.unlinkedTestCases.length} test case(s) have no Acceptance: field. Link them to acceptance items, or document why they exist (e.g. defense-in-depth regressions).`);
157
- }
158
- printResult(io, ok('scan.acceptance-coverage', result, [], nextActions), options.json);
159
- if (!result.ok) {
160
- process.exitCode = 1;
161
- }
162
- }
163
- catch (error) {
164
- printResult(io, fail('scan.acceptance-coverage', 'ACCEPTANCE_COVERAGE_FAILED', getErrorMessage(error), { requestId: options.rid }, ['Verify the project path and artifacts before retrying']), options.json);
165
- process.exitCode = 1;
166
- }
167
- });
168
- addJsonOption(scan
169
- .command('diff-vs-scope')
170
- .description('Verify every file in the git diff matches the RD artifact "Red-line scope" patterns; flags out-of-scope writes and unclassified files')
171
- .requiredOption('--rid <request-id>', 'request id')
172
- .requiredOption('--project <path>', 'target project root')
173
- .option('--session-id <session>', 'restrict to a specific session id')
174
- .option('--base-ref <ref>', 'compare working tree against this git ref (default: HEAD)')).action(async (options) => {
175
- try {
176
- const scopeOptions = {
177
- projectRoot: options.project,
178
- requestId: options.rid
179
- };
180
- if (options.sessionId !== undefined)
181
- scopeOptions.sessionId = options.sessionId;
182
- if (options.baseRef !== undefined)
183
- scopeOptions.baseRef = options.baseRef;
184
- const result = await getDiffVsScope(scopeOptions);
185
- if (isDiffScopeError(result)) {
186
- printResult(io, fail('scan.diff-vs-scope', 'RD_NOT_FOUND', `RD artifact for requestId=${options.rid} not found`, { requestId: options.rid }, ['Run `peaks request init --role rd --id <rid> --apply --type <type>` first.']), options.json);
187
- process.exitCode = 1;
188
- return;
189
- }
190
- const nextActions = [];
191
- if (!result.gitAvailable) {
192
- nextActions.push('git not available; scope check skipped. Cross-check the diff manually.');
193
- }
194
- if (!result.patternsDeclared) {
195
- nextActions.push('RD artifact has no in-scope or out-of-scope patterns under "## Red-line scope". Add concrete path/glob patterns (e.g. `src/services/login/**`) before re-running.');
196
- }
197
- if (result.violations.length > 0) {
198
- nextActions.push(`${result.violations.length} file(s) match an explicit out-of-scope pattern. Revert these or expand the RD red-line scope with PRD approval.`);
199
- }
200
- if (result.unclassified.length > 0) {
201
- nextActions.push(`${result.unclassified.length} changed file(s) do not match any declared scope pattern. Either add them to the in-scope list (if intentional) or revert them.`);
202
- }
203
- printResult(io, ok('scan.diff-vs-scope', result, [], nextActions), options.json);
204
- if (!result.ok) {
205
- process.exitCode = 1;
206
- }
207
- }
208
- catch (error) {
209
- printResult(io, fail('scan.diff-vs-scope', 'DIFF_VS_SCOPE_FAILED', getErrorMessage(error), { requestId: options.rid }, ['Verify the project path is a git repository and the RD artifact exists']), options.json);
210
- process.exitCode = 1;
211
- }
212
- });
213
- addJsonOption(scan
214
- .command('file-size')
215
- .description('Check git diff for files exceeding a line count threshold (karpathy-skills "Simplicity First"; tool output, lockfiles and append-only records such as CHANGELOG.md excluded)')
216
- .requiredOption('--project <path>', 'target project root')
217
- .option('--base-ref <ref>', 'compare working tree against this git ref (default: HEAD)')
218
- .option('--threshold <n>', `line count applied to EVERY file (default: the file-size policy's per-directory cap — ${FILE_SIZE_CAP_DEFAULT} raw lines, ${FILE_SIZE_CAP_TESTS} under root tests/)`)).action((options) => {
219
- try {
220
- const threshold = options.threshold !== undefined && /^\d+$/.test(options.threshold)
221
- ? Number(options.threshold)
222
- : undefined;
223
- const result = scanFileSize({
224
- projectRoot: options.project,
225
- ...(options.baseRef !== undefined ? { baseRef: options.baseRef } : {}),
226
- ...(threshold !== undefined ? { threshold } : {})
227
- });
228
- const nextActions = [];
229
- if (!result.ok) {
230
- nextActions.push(`${result.violations.length} file(s) exceed their file-size cap. Split into smaller modules.`);
231
- }
232
- printResult(io, ok('scan.file-size', result, [], nextActions), options.json);
233
- if (!result.ok) {
234
- process.exitCode = 1;
235
- }
236
- }
237
- catch (error) {
238
- printResult(io, fail('scan.file-size', 'FILE_SIZE_SCAN_FAILED', getErrorMessage(error), { projectRoot: options.project }, ['Verify the project path is a git repository']), options.json);
239
- process.exitCode = 1;
240
- }
241
- });
242
- addJsonOption(scan
243
- .command('libraries')
244
- .description('Enumerate every dependency + devDependency + peerDependency + optionalDependency in package.json with parsed major version (read-only). Output goes to ## Library versions in rd/project-scan.md.')
245
- .requiredOption('--project <path>', 'target project root')).action(async (options) => {
246
- try {
247
- const report = await scanLibraries({ projectRoot: options.project });
248
- const nextActions = [];
249
- if (report.libraries.length === 0) {
250
- nextActions.push('No dependencies found — verify package.json exists and is valid JSON.');
251
- }
252
- else {
253
- nextActions.push('Paste the report under `## Library versions` in .peaks/_runtime/<sid>/rd/project-scan.md.');
254
- nextActions.push('peaks-rd preflight will cross-check diff imports against schemas/library-breaking-changes.data.json.');
255
- }
256
- printResult(io, ok('scan.libraries', report, [], nextActions), options.json);
257
- }
258
- catch (error) {
259
- printResult(io, fail('scan.libraries', 'SCAN_LIBRARIES_FAILED', getErrorMessage(error), { projectRoot: options.project }, ['Verify the project path exists and is readable']), options.json);
260
- process.exitCode = 1;
261
- }
262
- });
263
- // Slice 3/6 — `peaks scan api-surface`. Feeds the tech-doc "Existing API /
264
- // Component Inventory" section with a structured inventory of CLI
265
- // subcommands + service-level public exports. Read-only; never writes.
266
- addJsonOption(scan
267
- .command('api-surface')
268
- .description("Enumerate CLI subcommands + service-level public exports for tech-doc 'Existing API / Component Inventory' section")
269
- .requiredOption('--project <path>', 'target project root')
270
- .option('--format <fmt>', 'output format: md (default) | json', (v) => {
271
- if (v !== 'md' && v !== 'json') {
272
- throw new InvalidArgumentError('must be md or json');
273
- }
274
- return v;
275
- })
276
- .option('--include-dirs <globs>', 'comma-separated dirs to scan (default: src/cli,src/services)')
277
- .option('--max-per-kind <n>', 'cap entries per kind (default: no cap)', (v) => {
278
- if (!/^\d+$/.test(v)) {
279
- throw new InvalidArgumentError('must be a non-negative integer');
280
- }
281
- return Number(v);
282
- })).action(async (options) => {
283
- try {
284
- const report = await scanApiSurface({
285
- projectRoot: options.project,
286
- ...(options.includeDirs !== undefined ? { includeDirs: options.includeDirs } : {}),
287
- ...(options.maxPerKind !== undefined ? { maxPerKind: options.maxPerKind } : {})
288
- });
289
- const nextActions = [];
290
- if (report.counts.cli === 0 && report.counts.service === 0) {
291
- nextActions.push('No CLI subcommands or service exports found — verify --include-dirs points at the right paths.');
292
- }
293
- else {
294
- // (v2.11.0 Group A: rd/tech-doc.md removed; the immutable peaks-prd
295
- // handoff at prd/handoff.md is the new home for the inventory section.)
296
- nextActions.push('Paste the `--format md` output under `## API surface inventory` in .peaks/_runtime/<sid>/prd/handoff.md.');
297
- nextActions.push('Use the inventory to fill the handoff "Existing API / Component Inventory" section (karpathy §1 — reuse before create).');
298
- }
299
- if (options.format === 'json' && !options.json) {
300
- // Raw JSON without the ok/data/code envelope.
301
- process.stdout.write(JSON.stringify(report, null, 2) + '\n');
302
- return;
303
- }
304
- if (options.format === 'json') {
305
- printResult(io, ok('scan.api-surface', report, [], nextActions), options.json);
306
- return;
307
- }
308
- // Default: markdown block
309
- const truncated = options.maxPerKind !== undefined
310
- ? {
311
- cli: report.counts.cli,
312
- service: report.counts.service,
313
- type: report.counts.type,
314
- constant: report.counts.constant
315
- }
316
- : undefined;
317
- const md = formatApiSurfaceMarkdown(report, options.maxPerKind !== undefined
318
- ? { maxPerKind: options.maxPerKind, truncatedCounts: truncated }
319
- : {});
320
- if (options.json) {
321
- printResult(io, ok('scan.api-surface', { markdown: md, report }, [], nextActions), options.json);
322
- }
323
- else {
324
- process.stdout.write(md + '\n');
325
- if (nextActions.length > 0) {
326
- process.stderr.write('\nNext actions:\n' + nextActions.map((a) => ' - ' + a).join('\n') + '\n');
327
- }
328
- }
329
- }
330
- catch (error) {
331
- if (error instanceof InvalidArgumentError)
332
- throw error;
333
- printResult(io, fail('scan.api-surface', 'SCAN_API_SURFACE_FAILED', getErrorMessage(error), { projectRoot: options.project }, ['Verify the project path exists and is readable']), options.json);
334
- process.exitCode = 1;
335
- }
336
- });
337
- // Slice 4/6 — karpathy-enforcement orphan-scan-cli
338
- // Detects 4 kinds of orphans: export / import / CLI subcommand / doc
339
- // endpoint. Read-only; never writes. karpathy §3 Surgical Changes.
340
- addJsonOption(scan
341
- .command('orphan')
342
- .description('Detect 4 kinds of orphans (export / import / CLI subcommand / doc endpoint) — karpathy §3 Surgical Changes')
343
- .requiredOption('--project <path>', 'target project root')
344
- .option('--format <fmt>', 'output format: md (default) | json', (v) => {
345
- if (v !== 'md' && v !== 'json') {
346
- throw new InvalidArgumentError('must be md or json');
347
- }
348
- return v;
349
- })
350
- .option('--scope <scope>', 'scope: working-tree (default) | git-diff | all', (v) => {
351
- if (v !== 'working-tree' && v !== 'git-diff' && v !== 'all') {
352
- throw new InvalidArgumentError('must be working-tree, git-diff, or all');
353
- }
354
- return v;
355
- })
356
- .option('--strict', 'strict mode: report exportOrphans even outside working tree', false)).action(async (options) => {
357
- try {
358
- const report = await scanOrphans({
359
- projectRoot: options.project,
360
- ...(options.scope !== undefined ? { scope: options.scope } : {}),
361
- ...(options.strict !== undefined ? { strict: options.strict } : {})
362
- });
363
- const nextActions = [];
364
- const totalOrphans = report.counts.export +
365
- report.counts.import +
366
- report.counts.cliSubcommand +
367
- report.counts.docEndpoint;
368
- if (totalOrphans === 0) {
369
- nextActions.push('No orphans detected in the requested scope. RD may proceed to commit.');
370
- }
371
- else {
372
- nextActions.push(`Found ${totalOrphans} orphan(s): export=${report.counts.export} import=${report.counts.import} cliSubcommand=${report.counts.cliSubcommand} docEndpoint=${report.counts.docEndpoint}.`);
373
- nextActions.push('Clean up before commit (karpathy §3 — remove what your changes made unused).');
374
- nextActions.push('Re-run `peaks scan orphan --project <path>` after cleanup to confirm zero.');
375
- }
376
- if (options.format === 'json' && !options.json) {
377
- process.stdout.write(JSON.stringify(report, null, 2) + '\n');
378
- return;
379
- }
380
- if (options.format === 'json') {
381
- printResult(io, ok('scan.orphan', report, [], nextActions), options.json);
382
- return;
383
- }
384
- const md = formatOrphanMarkdown(report, {});
385
- if (options.json) {
386
- printResult(io, ok('scan.orphan', { markdown: md, report }, [], nextActions), options.json);
387
- }
388
- else {
389
- process.stdout.write(md + '\n');
390
- if (nextActions.length > 0) {
391
- process.stderr.write('\nNext actions:\n' + nextActions.map((a) => ' - ' + a).join('\n') + '\n');
392
- }
393
- }
394
- }
395
- catch (error) {
396
- if (error instanceof InvalidArgumentError)
397
- throw error;
398
- printResult(io, fail('scan.orphan', 'SCAN_ORPHAN_FAILED', getErrorMessage(error), { projectRoot: options.project }, [
399
- 'Verify the project path exists and is readable',
400
- 'Verify the project is a git repository (working-tree scope)'
401
- ]), options.json);
402
- process.exitCode = 1;
403
- }
404
- });
405
- // Karpathy structural scan (Slice 5/6 — karpathy-enforcement 5-way fanout +
406
- // hard Karpathy-Gate). Detects violation counts and section coverage for
407
- // the 4 Karpathy-guidelines. Read-only; never writes. karpathy §3.
408
- addJsonOption(scan
409
- .command('karpathy')
410
- .description('Surface-level scan of rd/karpathy-review.md for the 4 Karpathy guidelines (Think / Simplicity / Surgical / Goal) — karpathy §1 + §3')
411
- .requiredOption('--project <path>', 'target project root')
412
- .option('--format <fmt>', 'output format: md (default) | json', (v) => {
413
- if (v !== 'md' && v !== 'json') {
414
- throw new InvalidArgumentError('must be md or json');
415
- }
416
- return v;
417
- })
418
- .option('--scope <scope>', 'scope: working-tree (default) | all', (v) => {
419
- if (v !== 'working-tree' && v !== 'all') {
420
- throw new InvalidArgumentError('must be working-tree or all');
421
- }
422
- return v;
423
- })).action(async (options) => {
424
- try {
425
- const report = await scanKarpathy({
426
- projectRoot: options.project,
427
- ...(options.scope !== undefined ? { scope: options.scope } : {})
428
- });
429
- const nextActions = [];
430
- if (report.gateAction === 'block') {
431
- nextActions.push('Karpathy review file missing under scope=all. Per karpathy §1 Think Before Coding, the rd:qa-handoff gate wants rd/karpathy-review-<rid>.md; this scanner has no rid and reads only the back-compat name rd/karpathy-review.md, so it cannot go green on the rid-scoped name alone. `peaks request transition --state qa-handoff` is the authoritative gate.');
432
- }
433
- else if (report.gateAction === 'warn') {
434
- nextActions.push(`Karpathy review emitted ${report.totalViolations} violation(s). Review the warnings and re-run after cleanup (karpathy §3 Surgical Changes).`);
435
- }
436
- else {
437
- nextActions.push('Karpathy-Gate passes. All 4 guideline sections present and no anti-patterns detected.');
438
- }
439
- if (options.format === 'json' && !options.json) {
440
- process.stdout.write(JSON.stringify(report, null, 2) + '\n');
441
- return;
442
- }
443
- if (options.format === 'json') {
444
- printResult(io, ok('scan.karpathy', report, [], nextActions), options.json);
445
- return;
446
- }
447
- const md = formatKarpathyMarkdown(report, {});
448
- if (options.json) {
449
- printResult(io, ok('scan.karpathy', { markdown: md, report }, [], nextActions), options.json);
450
- }
451
- else {
452
- process.stdout.write(md + '\n');
453
- if (nextActions.length > 0) {
454
- process.stderr.write('\nNext actions:\n' + nextActions.map((a) => ' - ' + a).join('\n') + '\n');
455
- }
456
- }
457
- }
458
- catch (error) {
459
- if (error instanceof InvalidArgumentError)
460
- throw error;
461
- printResult(io, fail('scan.karpathy', 'SCAN_KARPATHY_FAILED', getErrorMessage(error), { projectRoot: options.project }, [
462
- 'Verify the project path exists and is readable',
463
- 'Verify the project is a readable directory'
464
- ]), options.json);
465
- process.exitCode = 1;
466
- }
467
- });
24
+ registerScanProjectCommands(scan, io);
25
+ registerScanScopeCommands(scan, io);
26
+ registerScanInventoryCommands(scan, io);
27
+ registerScanStructuralCommands(scan, io);
468
28
  }
@@ -0,0 +1,3 @@
1
+ import { type Command } from 'commander';
2
+ import { type ProgramIO } from '../cli-helpers.js';
3
+ export declare function registerScanInventoryCommands(scan: Command, io: ProgramIO): void;
@@ -0,0 +1,96 @@
1
+ // `peaks scan libraries` and `peaks scan api-surface` — the two inventory
2
+ // scans (package.json dependencies, then CLI subcommands + service exports).
3
+ // Both `.action()` bodies are module-level named runners; the api-surface
4
+ // output tail is shared with the structural scans through emitScanReport.
5
+ import { InvalidArgumentError } from 'commander';
6
+ import { fail, ok } from 'peaks-loop-shared/result';
7
+ import { scanLibraries } from '../../services/scan/libraries-service.js';
8
+ import { scanApiSurface, formatApiSurfaceMarkdown } from '../../services/scan/api-surface-service.js';
9
+ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
10
+ import { emitScanReport, parseNonNegativeIntOption, parseScanOutputFormat } from './scan-command-shared.js';
11
+ const LIBRARIES_DESCRIPTION = 'Enumerate every dependency + devDependency + peerDependency + optionalDependency in package.json with parsed major version (read-only). Output goes to ## Library versions in rd/project-scan.md.';
12
+ const API_SURFACE_DESCRIPTION = "Enumerate CLI subcommands + service-level public exports for tech-doc 'Existing API / Component Inventory' section";
13
+ async function runScanLibraries(options, io) {
14
+ try {
15
+ const report = await scanLibraries({ projectRoot: options.project });
16
+ const nextActions = [];
17
+ if (report.libraries.length === 0) {
18
+ nextActions.push('No dependencies found — verify package.json exists and is valid JSON.');
19
+ }
20
+ else {
21
+ nextActions.push('Paste the report under `## Library versions` in .peaks/_runtime/<sid>/rd/project-scan.md.');
22
+ nextActions.push('peaks-rd preflight will cross-check diff imports against schemas/library-breaking-changes.data.json.');
23
+ }
24
+ printResult(io, ok('scan.libraries', report, [], nextActions), options.json);
25
+ }
26
+ catch (error) {
27
+ printResult(io, fail('scan.libraries', 'SCAN_LIBRARIES_FAILED', getErrorMessage(error), { projectRoot: options.project }, ['Verify the project path exists and is readable']), options.json);
28
+ process.exitCode = 1;
29
+ }
30
+ }
31
+ function apiSurfaceNextActions(report) {
32
+ const nextActions = [];
33
+ if (report.counts.cli === 0 && report.counts.service === 0) {
34
+ nextActions.push('No CLI subcommands or service exports found — verify --include-dirs points at the right paths.');
35
+ }
36
+ else {
37
+ // (v2.11.0 Group A: rd/tech-doc.md removed; the immutable peaks-prd
38
+ // handoff at prd/handoff.md is the new home for the inventory section.)
39
+ nextActions.push('Paste the `--format md` output under `## API surface inventory` in .peaks/_runtime/<sid>/prd/handoff.md.');
40
+ nextActions.push('Use the inventory to fill the handoff "Existing API / Component Inventory" section (karpathy §1 — reuse before create).');
41
+ }
42
+ return nextActions;
43
+ }
44
+ /** The markdown truncation arguments, or `{}` when no cap was requested. */
45
+ function apiSurfaceMarkdownArguments(report, maxPerKind) {
46
+ if (maxPerKind === undefined)
47
+ return {};
48
+ return {
49
+ maxPerKind,
50
+ truncatedCounts: {
51
+ cli: report.counts.cli,
52
+ service: report.counts.service,
53
+ type: report.counts.type,
54
+ constant: report.counts.constant
55
+ }
56
+ };
57
+ }
58
+ async function runScanApiSurface(options, io) {
59
+ try {
60
+ const report = await scanApiSurface({
61
+ projectRoot: options.project,
62
+ ...(options.includeDirs !== undefined ? { includeDirs: options.includeDirs } : {}),
63
+ ...(options.maxPerKind !== undefined ? { maxPerKind: options.maxPerKind } : {})
64
+ });
65
+ emitScanReport(io, {
66
+ command: 'scan.api-surface',
67
+ format: options.format,
68
+ json: options.json,
69
+ report,
70
+ nextActions: apiSurfaceNextActions(report),
71
+ renderMarkdown: () => formatApiSurfaceMarkdown(report, apiSurfaceMarkdownArguments(report, options.maxPerKind))
72
+ });
73
+ }
74
+ catch (error) {
75
+ if (error instanceof InvalidArgumentError)
76
+ throw error;
77
+ printResult(io, fail('scan.api-surface', 'SCAN_API_SURFACE_FAILED', getErrorMessage(error), { projectRoot: options.project }, ['Verify the project path exists and is readable']), options.json);
78
+ process.exitCode = 1;
79
+ }
80
+ }
81
+ export function registerScanInventoryCommands(scan, io) {
82
+ addJsonOption(scan
83
+ .command('libraries')
84
+ .description(LIBRARIES_DESCRIPTION)
85
+ .requiredOption('--project <path>', 'target project root')).action((options) => runScanLibraries(options, io));
86
+ // Slice 3/6 — `peaks scan api-surface`. Feeds the tech-doc "Existing API /
87
+ // Component Inventory" section with a structured inventory of CLI
88
+ // subcommands + service-level public exports. Read-only; never writes.
89
+ addJsonOption(scan
90
+ .command('api-surface')
91
+ .description(API_SURFACE_DESCRIPTION)
92
+ .requiredOption('--project <path>', 'target project root')
93
+ .option('--format <fmt>', 'output format: md (default) | json', parseScanOutputFormat)
94
+ .option('--include-dirs <globs>', 'comma-separated dirs to scan (default: src/cli,src/services)')
95
+ .option('--max-per-kind <n>', 'cap entries per kind (default: no cap)', parseNonNegativeIntOption)).action((options) => runScanApiSurface(options, io));
96
+ }
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ import { type ProgramIO } from '../cli-helpers.js';
3
+ export declare function registerScanProjectCommands(scan: Command, io: ProgramIO): void;