peaks-loop 3.0.0 → 3.0.2

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 (244) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/CHANGELOG.md +94 -59
  3. package/README-en.md +2 -2
  4. package/README.md +2 -2
  5. package/agents/karpathy-reviewer.md +2 -2
  6. package/dist/src/cli/commands/agent-commands.js +3 -3
  7. package/dist/src/cli/commands/audit-commands.d.ts +1 -1
  8. package/dist/src/cli/commands/audit-commands.js +6 -6
  9. package/dist/src/cli/commands/classify-classify-commands.js +1 -1
  10. package/dist/src/cli/commands/code-review-commands.d.ts +3 -3
  11. package/dist/src/cli/commands/code-review-commands.js +5 -5
  12. package/dist/src/cli/commands/compact-command.d.ts +21 -0
  13. package/dist/src/cli/commands/compact-command.js +252 -0
  14. package/dist/src/cli/commands/context-commands.d.ts +1 -1
  15. package/dist/src/cli/commands/context-commands.js +1 -1
  16. package/dist/src/cli/commands/contract-commands.d.ts +1 -1
  17. package/dist/src/cli/commands/core/session-command.js +17 -7
  18. package/dist/src/cli/commands/core/skill-command.js +1 -1
  19. package/dist/src/cli/commands/feedback-commands.js +1 -1
  20. package/dist/src/cli/commands/hook-handle.d.ts +1 -1
  21. package/dist/src/cli/commands/hook-handle.js +1 -1
  22. package/dist/src/cli/commands/legacy-commands.d.ts +1 -1
  23. package/dist/src/cli/commands/legacy-commands.js +2 -2
  24. package/dist/src/cli/commands/log-commands.js +2 -2
  25. package/dist/src/cli/commands/loop-commands.d.ts +1 -1
  26. package/dist/src/cli/commands/loop-commands.js +1 -1
  27. package/dist/src/cli/commands/loop-eval-commands.d.ts +1 -1
  28. package/dist/src/cli/commands/loop-eval-commands.js +1 -1
  29. package/dist/src/cli/commands/playwright-commands.d.ts +1 -1
  30. package/dist/src/cli/commands/playwright-commands.js +2 -2
  31. package/dist/src/cli/commands/request-commands.js +40 -1
  32. package/dist/src/cli/commands/role-commands.js +1 -1
  33. package/dist/src/cli/commands/slice-commands.js +5 -5
  34. package/dist/src/cli/commands/understand-commands.js +36 -0
  35. package/dist/src/cli/commands/upgrade-commands.d.ts +1 -1
  36. package/dist/src/cli/commands/upgrade-commands.js +6 -6
  37. package/dist/src/cli/commands/workspace/init-command.js +2 -2
  38. package/dist/src/cli/program.js +10 -1
  39. package/dist/src/services/agent/ecc-agent-service.js +5 -5
  40. package/dist/src/services/audit/artifact-writer.d.ts +1 -1
  41. package/dist/src/services/audit/artifact-writer.js +1 -1
  42. package/dist/src/services/audit/decision-writer.d.ts +1 -1
  43. package/dist/src/services/audit/decision-writer.js +1 -1
  44. package/dist/src/services/audit/enforcers/lint-output-style.js +5 -5
  45. package/dist/src/services/audit/enforcers/mock-placement.d.ts +1 -1
  46. package/dist/src/services/audit/enforcers/mock-placement.js +1 -1
  47. package/dist/src/services/audit/enforcers/no-root-pollution.js +1 -1
  48. package/dist/src/services/audit/red-line-catalog-p2-a.js +2 -2
  49. package/dist/src/services/audit/red-line-catalog-p2-b.js +2 -2
  50. package/dist/src/services/audit/static-service.d.ts +1 -1
  51. package/dist/src/services/audit/static-service.js +5 -5
  52. package/dist/src/services/audit/types.d.ts +1 -1
  53. package/dist/src/services/audit/types.js +1 -1
  54. package/dist/src/services/audit-independent/perf-audit-service.js +1 -1
  55. package/dist/src/services/audit-independent/security-audit-service.js +1 -1
  56. package/dist/src/services/classify/classify-service.d.ts +1 -1
  57. package/dist/src/services/classify/classify-service.js +1 -1
  58. package/dist/src/services/classify/classify-types.d.ts +1 -1
  59. package/dist/src/services/classify/classify-types.js +1 -1
  60. package/dist/src/services/code-review/ecc-bridge.d.ts +2 -2
  61. package/dist/src/services/code-review/ecc-bridge.js +2 -2
  62. package/dist/src/services/code-review/ocr-service.d.ts +9 -9
  63. package/dist/src/services/code-review/ocr-service.js +16 -16
  64. package/dist/src/services/compact/decision-tables.d.ts +66 -0
  65. package/dist/src/services/compact/decision-tables.js +127 -0
  66. package/dist/src/services/compact/request-transition-hook.d.ts +20 -0
  67. package/dist/src/services/compact/request-transition-hook.js +110 -0
  68. package/dist/src/services/compact/suggest-service.d.ts +74 -0
  69. package/dist/src/services/compact/suggest-service.js +203 -0
  70. package/dist/src/services/config/config-migration.js +1 -1
  71. package/dist/src/services/config/config-safety.d.ts +1 -1
  72. package/dist/src/services/config/config-safety.js +1 -1
  73. package/dist/src/services/config/config-service.d.ts +1 -1
  74. package/dist/src/services/config/config-service.js +1 -1
  75. package/dist/src/services/context/auto-compact-dispatcher.js +4 -4
  76. package/dist/src/services/context/auto-compact-types.d.ts +6 -6
  77. package/dist/src/services/context/auto-compact-types.js +5 -5
  78. package/dist/src/services/dispatch/sub-agent-dispatcher.js +1 -1
  79. package/dist/src/services/dispatch/test-tool-detection.d.ts +1 -1
  80. package/dist/src/services/dispatch/test-tool-detection.js +1 -1
  81. package/dist/src/services/doctor/doctor-service.d.ts +1 -1
  82. package/dist/src/services/doctor/doctor-service.js +5 -5
  83. package/dist/src/services/hooks/presence-marker-detector.js +5 -5
  84. package/dist/src/services/ide/adapters/claude-code-adapter.d.ts +1 -1
  85. package/dist/src/services/ide/adapters/claude-code-adapter.js +2 -2
  86. package/dist/src/services/ide/adapters/codex-adapter.d.ts +2 -2
  87. package/dist/src/services/ide/adapters/codex-adapter.js +2 -2
  88. package/dist/src/services/ide/adapters/cursor-adapter.d.ts +2 -2
  89. package/dist/src/services/ide/adapters/cursor-adapter.js +2 -2
  90. package/dist/src/services/ide/adapters/hermes-adapter.d.ts +1 -1
  91. package/dist/src/services/ide/adapters/hermes-adapter.js +1 -1
  92. package/dist/src/services/ide/adapters/openclaw-adapter.d.ts +1 -1
  93. package/dist/src/services/ide/adapters/openclaw-adapter.js +1 -1
  94. package/dist/src/services/ide/adapters/trae-adapter.d.ts +1 -1
  95. package/dist/src/services/ide/adapters/trae-adapter.js +1 -1
  96. package/dist/src/services/ide/ide-types.d.ts +9 -9
  97. package/dist/src/services/legacy/legacy-detector.d.ts +1 -1
  98. package/dist/src/services/legacy/legacy-detector.js +1 -1
  99. package/dist/src/services/log/log-commands-service.d.ts +1 -1
  100. package/dist/src/services/log/log-commands-service.js +1 -1
  101. package/dist/src/services/log/logger.d.ts +3 -3
  102. package/dist/src/services/log/logger.js +3 -3
  103. package/dist/src/services/log/redact.d.ts +1 -1
  104. package/dist/src/services/log/redact.js +1 -1
  105. package/dist/src/services/loop/evaluator-dispatcher.d.ts +1 -1
  106. package/dist/src/services/loop/evaluator-dispatcher.js +16 -15
  107. package/dist/src/services/loop/monotonic-guard.js +35 -18
  108. package/dist/src/services/loop/monotonic-runner.d.ts +3 -0
  109. package/dist/src/services/loop/monotonic-runner.js +62 -28
  110. package/dist/src/services/loop/run-driver.js +45 -24
  111. package/dist/src/services/memory/llm-reranker.d.ts +1 -1
  112. package/dist/src/services/memory/llm-reranker.js +1 -1
  113. package/dist/src/services/mut/production-stryker.d.ts +0 -5
  114. package/dist/src/services/mut/production-stryker.js +19 -10
  115. package/dist/src/services/openspec/openspec-propose-from-doctor-service.d.ts +1 -1
  116. package/dist/src/services/openspec/openspec-propose-from-doctor-service.js +1 -1
  117. package/dist/src/services/perf/perf-baseline-service.d.ts +2 -2
  118. package/dist/src/services/perf/perf-baseline-service.js +2 -2
  119. package/dist/src/services/prd/handoff-auto-regen.js +1 -1
  120. package/dist/src/services/prd/project-scan-sediment.js +3 -3
  121. package/dist/src/services/prd/project-scan-types.d.ts +1 -1
  122. package/dist/src/services/preferences/preferences-types.d.ts +3 -3
  123. package/dist/src/services/preferences/preferences-types.js +2 -2
  124. package/dist/src/services/role/role-registry.d.ts +1 -1
  125. package/dist/src/services/role/role-registry.js +1 -1
  126. package/dist/src/services/sc/sc-service.js +3 -3
  127. package/dist/src/services/skill/resume-detector.js +2 -2
  128. package/dist/src/services/skills/sync-service.d.ts +3 -3
  129. package/dist/src/services/skills/sync-service.js +9 -9
  130. package/dist/src/services/slice/calibration-store.d.ts +1 -1
  131. package/dist/src/services/slice/calibration-store.js +1 -1
  132. package/dist/src/services/solo/auto-compact-orchestrator.d.ts +1 -1
  133. package/dist/src/services/solo/auto-compact-orchestrator.js +7 -7
  134. package/dist/src/services/solo/dag-orchestrator.js +1 -1
  135. package/dist/src/services/solo/mode-gate.d.ts +1 -1
  136. package/dist/src/services/solo/mode-gate.js +4 -4
  137. package/dist/src/services/solo/user-touchpoint-classifier.js +1 -1
  138. package/dist/src/services/standards/migrate-claude-rules-service.js +5 -5
  139. package/dist/src/services/standards/migrate-service.d.ts +3 -3
  140. package/dist/src/services/standards/migrate-service.js +5 -5
  141. package/dist/src/services/standards/project-standards-service.js +1 -1
  142. package/dist/src/services/understand/hybrid-types.d.ts +32 -0
  143. package/dist/src/services/understand/hybrid-types.js +1 -0
  144. package/dist/src/services/understand/understand-hybrid-service.d.ts +10 -0
  145. package/dist/src/services/understand/understand-hybrid-service.js +100 -0
  146. package/dist/src/services/upgrade/gitignore-migrate-service.d.ts +3 -3
  147. package/dist/src/services/upgrade/gitignore-migrate-service.js +4 -4
  148. package/dist/src/services/upgrade/upgrade-service.d.ts +1 -1
  149. package/dist/src/services/upgrade/upgrade-service.js +5 -5
  150. package/dist/src/services/verdict/envelopes.d.ts +1 -1
  151. package/dist/src/services/verdict/envelopes.js +1 -1
  152. package/dist/src/services/workflow/workflow-loader.js +1 -1
  153. package/dist/src/services/workspace/migrate-1-4-1-service.d.ts +1 -1
  154. package/dist/src/services/workspace/migrate-1-4-1-service.js +1 -1
  155. package/dist/src/services/workspace/migrate-types.d.ts +1 -1
  156. package/dist/src/services/workspace/migrate-types.js +1 -1
  157. package/dist/src/services/workspace/workspace-claude-settings-materializer.d.ts +2 -2
  158. package/dist/src/services/workspace/workspace-claude-settings-materializer.js +9 -9
  159. package/dist/src/services/workspace/workspace-service.d.ts +1 -1
  160. package/dist/src/services/workspace/workspace-service.js +1 -1
  161. package/dist/src/shared/path-safety.d.ts +1 -1
  162. package/dist/src/shared/path-safety.js +1 -1
  163. package/dist/src/shared/version.d.ts +1 -1
  164. package/dist/src/shared/version.js +1 -1
  165. package/output-styles/peaks-skill-swarm.md +33 -33
  166. package/package.json +5 -4
  167. package/schemas/decomposition-v1.json +1 -1
  168. package/schemas/decomposition-v2.json +1 -1
  169. package/schemas/replay-fixture.schema.json +1 -1
  170. package/schemas/reviewer-envelope.schema.json +1 -1
  171. package/scripts/install-skills.mjs +11 -11
  172. package/skills/peaks-audit/SKILL.md +2 -2
  173. package/skills/peaks-doctor/SKILL.md +4 -4
  174. package/skills/peaks-final-review/SKILL.md +2 -2
  175. package/skills/peaks-ide/SKILL.md +3 -3
  176. package/skills/peaks-perf-audit/SKILL.md +1 -1
  177. package/skills/peaks-prd/SKILL.md +14 -14
  178. package/skills/peaks-prd/references/workflow.md +1 -1
  179. package/skills/peaks-qa/SKILL.md +17 -17
  180. package/skills/peaks-qa/references/artifact-per-request.md +1 -1
  181. package/skills/peaks-qa/references/browser-validation-contracts.md +2 -2
  182. package/skills/peaks-qa/references/codegraph-regression-focus.md +1 -1
  183. package/skills/peaks-qa/references/external-capability-guidance.md +2 -2
  184. package/skills/peaks-qa/references/qa-fanout-contract.md +1 -1
  185. package/skills/peaks-qa/references/qa-gstack-integration.md +3 -3
  186. package/skills/peaks-qa/references/qa-local-artifacts.md +1 -1
  187. package/skills/peaks-qa/references/qa-matt-pocock-integration.md +1 -1
  188. package/skills/peaks-qa/references/qa-runbook.md +3 -3
  189. package/skills/peaks-qa/references/qa-skill-presence.md +1 -1
  190. package/skills/peaks-qa/references/qa-standards-preflight.md +2 -2
  191. package/skills/peaks-qa/references/qa-transition-gates.md +9 -9
  192. package/skills/peaks-qa/references/regression-gates.md +1 -1
  193. package/skills/peaks-rd/SKILL.md +13 -13
  194. package/skills/peaks-rd/references/artifact-and-standards-output.md +1 -1
  195. package/skills/peaks-rd/references/codegraph-project-analysis.md +1 -1
  196. package/skills/peaks-rd/references/external-references.md +3 -3
  197. package/skills/peaks-rd/references/karpathy-reviewer-prompt.md +1 -1
  198. package/skills/peaks-rd/references/mandatory-perf-baseline.md +1 -1
  199. package/skills/peaks-rd/references/matt-pocock-integration.md +2 -2
  200. package/skills/peaks-rd/references/ocr-integration.md +19 -19
  201. package/skills/peaks-rd/references/openspec-cli.md +2 -2
  202. package/skills/peaks-rd/references/rd-gstack-integration.md +5 -5
  203. package/skills/peaks-rd/references/rd-runbook.md +9 -9
  204. package/skills/peaks-rd/references/rd-standards-preflight.md +1 -1
  205. package/skills/peaks-rd/references/rd-transition-gates.md +13 -13
  206. package/skills/peaks-rd/references/skill-presence-and-title.md +1 -1
  207. package/skills/peaks-reviewer/SKILL.md +2 -2
  208. package/skills/peaks-sc/SKILL.md +9 -9
  209. package/skills/peaks-security-audit/SKILL.md +1 -1
  210. package/skills/peaks-slice-decompose/SKILL.md +2 -2
  211. package/skills/peaks-solo/SKILL.md +49 -49
  212. package/skills/peaks-solo/references/a2a-artifact-mapping.md +8 -8
  213. package/skills/peaks-solo/references/anchoring-and-session-info.md +3 -3
  214. package/skills/peaks-solo/references/boundaries.md +4 -4
  215. package/skills/peaks-solo/references/browser-workflow.md +2 -2
  216. package/skills/peaks-solo/references/codegraph-orchestration.md +1 -1
  217. package/skills/peaks-solo/references/completion-handoff.md +2 -2
  218. package/skills/peaks-solo/references/external-references.md +3 -3
  219. package/skills/peaks-solo/references/external-skill-invocation.md +2 -2
  220. package/skills/peaks-solo/references/frontend-only-mode.md +1 -1
  221. package/skills/peaks-solo/references/gstack-integration.md +4 -4
  222. package/skills/peaks-solo/references/local-artifact-workspace.md +2 -2
  223. package/skills/peaks-solo/references/micro-cycle.md +4 -4
  224. package/skills/peaks-solo/references/mode-selection.md +5 -5
  225. package/skills/peaks-solo/references/openspec-workflow.md +2 -2
  226. package/skills/peaks-solo/references/playwright-mcp-multi-terminal.md +1 -1
  227. package/skills/peaks-solo/references/project-memory-loading.md +1 -1
  228. package/skills/peaks-solo/references/project-scan-checklist.md +1 -1
  229. package/skills/peaks-solo/references/quality-gate-cheatsheet.md +1 -1
  230. package/skills/peaks-solo/references/resume-detection.md +1 -1
  231. package/skills/peaks-solo/references/runbook.md +20 -20
  232. package/skills/peaks-solo/references/skill-presence-and-title.md +2 -2
  233. package/skills/peaks-solo/references/standards-preflight.md +2 -2
  234. package/skills/peaks-solo/references/step-0-55-1x-detection.md +3 -3
  235. package/skills/peaks-solo/references/sub-agent-dispatch.md +3 -3
  236. package/skills/peaks-solo/references/swarm-dispatch-contract.md +3 -3
  237. package/skills/peaks-solo/references/workflow-gates-and-types.md +34 -34
  238. package/skills/peaks-solo-resume/SKILL.md +2 -2
  239. package/skills/peaks-solo-status/SKILL.md +4 -4
  240. package/skills/peaks-solo-test/SKILL.md +4 -4
  241. package/skills/peaks-sop/SKILL.md +5 -5
  242. package/skills/peaks-txt/SKILL.md +17 -17
  243. package/skills/peaks-ui/SKILL.md +17 -17
  244. package/skills/peaks-ui/references/workflow.md +1 -1
@@ -11,7 +11,7 @@
11
11
  * current ocr install + config state (5 reasons: ready /
12
12
  * package-missing / binary-missing / config-missing /
13
13
  * detection-failed). Source of truth for the LLM endpoint
14
- * config is `peaksConfig.ocr.llm` in the user's peaks-cli
14
+ * config is `peaksConfig.ocr.llm` in the user's peaks-loop
15
15
  * config (NOT `~/.opencodereview/config.json`).
16
16
  * - `peaks code-review run-ocr [--from --to --commit]` — invokes
17
17
  * `ocr review --format json` and wraps the result in a peaks
@@ -22,11 +22,11 @@
22
22
  * so the ocr subprocess never has to read from a file the
23
23
  * user did not set up themselves.
24
24
  * - `peaks code-review config-template` — prints the JSON snippet
25
- * the user should paste into their peaks-cli config.json. It
25
+ * the user should paste into their peaks-loop config.json. It
26
26
  * does NOT write anything. The user is in control of their
27
27
  * LLM token / URL / model. No `peaks ocr config set`; the user
28
28
  * either edits the JSON directly or uses `peaks config set
29
- * --key ocr.llm.url --value '...'` (a peaks-cli command, not
29
+ * --key ocr.llm.url --value '...'` (a peaks-loop command, not
30
30
  * an ocr command).
31
31
  */
32
32
  import { Command } from 'commander';
@@ -5,7 +5,7 @@ import { fail, ok } from '../../shared/result.js';
5
5
  export function registerCodeReviewCommands(program, io) {
6
6
  const codeReview = program
7
7
  .command('code-review')
8
- .description('Code-review primitives for peaks-rd Gate B3. Wraps the soft-optional `@alibaba-group/open-code-review` (ocr) tool when it is installed + configured; peaks-rd uses the structured JSON output as a second-opinion review alongside its own LLM review. ocr is a peerDependency of peaks-cli 2.8.2+ (was briefly promoted to a hard dependency in 2.0.1/2.0.2, then demoted to optionalDependencies 2.0.3+ — peer in 2.8.2 because its postinstall downloads a Go binary via HTTPS and would otherwise slow `npm i -g peaks-cli` in restricted environments). Install manually with `npm i -g @alibaba-group/open-code-review` if you want second-opinion reviews. LLM endpoint config lives under `peaksConfig.ocr.llm` in the user config — run `peaks code-review config-template` to see the JSON snippet to paste.');
8
+ .description('Code-review primitives for peaks-rd Gate B3. Wraps the soft-optional `@alibaba-group/open-code-review` (ocr) tool when it is installed + configured; peaks-rd uses the structured JSON output as a second-opinion review alongside its own LLM review. ocr is a peerDependency of peaks-loop 2.8.2+ (was briefly promoted to a hard dependency in 2.0.1/2.0.2, then demoted to optionalDependencies 2.0.3+ — peer in 2.8.2 because its postinstall downloads a Go binary via HTTPS and would otherwise slow `npm i -g peaks-loop` in restricted environments). Install manually with `npm i -g @alibaba-group/open-code-review` if you want second-opinion reviews. LLM endpoint config lives under `peaksConfig.ocr.llm` in the user config — run `peaks code-review config-template` to see the JSON snippet to paste.');
9
9
  addJsonOption(codeReview
10
10
  .command('detect-ocr')
11
11
  .description('Read-only probe: returns the ocr install + config state as a JSON envelope (5 reasons: ready / package-missing / binary-missing / config-missing / detection-failed). peaks-rd calls this first to decide whether to invoke `run-ocr`. Reads the LLM endpoint from `peaksConfig.ocr.llm` (not from ~/.opencodereview/config.json).')
@@ -32,7 +32,7 @@ export function registerCodeReviewCommands(program, io) {
32
32
  });
33
33
  addJsonOption(codeReview
34
34
  .command('run-ocr')
35
- .description('Run `ocr review --format json` and return the parsed JSON envelope. Soft-fails (exit 0, ok=false) when ocr is not ready, so peaks-rd can continue without the second-opinion review and surface the install/config nextActions to the user. The peaks-cli `peaksConfig.ocr.llm` block is injected as OCR_LLM_URL / OCR_LLM_TOKEN / ... env vars so the ocr subprocess never has to read ~/.opencodereview/config.json.')
35
+ .description('Run `ocr review --format json` and return the parsed JSON envelope. Soft-fails (exit 0, ok=false) when ocr is not ready, so peaks-rd can continue without the second-opinion review and surface the install/config nextActions to the user. The peaks-loop `peaksConfig.ocr.llm` block is injected as OCR_LLM_URL / OCR_LLM_TOKEN / ... env vars so the ocr subprocess never has to read ~/.opencodereview/config.json.')
36
36
  .option('--project <path>', 'project root (default: cwd)')
37
37
  .option('--from <ref>', 'git ref to diff from (e.g. main, origin/main)')
38
38
  .option('--to <ref>', 'git ref to diff to (e.g. HEAD, feature-branch)')
@@ -62,7 +62,7 @@ export function registerCodeReviewCommands(program, io) {
62
62
  });
63
63
  addJsonOption(codeReview
64
64
  .command('config-template')
65
- .description('Print the JSON snippet the user should paste into their peaks-cli config (`peaksConfig.ocr.llm`). This command does NOT write anything. The user is the only party that touches the LLM token / URL / model — peaks-cli never auto-configures the endpoint. Token value is shown as the placeholder "<your-api-key>"; replace it before pasting.')).action((options) => {
65
+ .description('Print the JSON snippet the user should paste into their peaks-loop config (`peaksConfig.ocr.llm`). This command does NOT write anything. The user is the only party that touches the LLM token / URL / model — peaks-loop never auto-configures the endpoint. Token value is shown as the placeholder "<your-api-key>"; replace it before pasting.')).action((options) => {
66
66
  const template = getOcrConfigTemplate();
67
67
  const targetPath = getUserConfigPath();
68
68
  const currentConfig = getOcrLlmConfig();
@@ -73,8 +73,8 @@ export function registerCodeReviewCommands(program, io) {
73
73
  template,
74
74
  nextActions: [
75
75
  `Edit ${targetPath} and add the "ocr" block from the template above.`,
76
- 'OR use peaks-cli config set with one key at a time: `peaks config set --key ocr.llm.url --value \'<url>\'` etc.',
77
- 'The authToken field is sensitive and is stored in the user layer (`~/.peaks/config.json`); peaks-cli will not commit it.',
76
+ 'OR use peaks-loop config set with one key at a time: `peaks config set --key ocr.llm.url --value \'<url>\'` etc.',
77
+ 'The authToken field is sensitive and is stored in the user layer (`~/.peaks/config.json`); peaks-loop will not commit it.',
78
78
  'Re-run `peaks code-review detect-ocr --json` to verify the new state is `ready`.',
79
79
  ],
80
80
  };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `peaks compact *` — strategic-compact CLI primitives.
3
+ *
4
+ * Slice 2026-07-01-strategic-compact-cli. Five subcommands under a new
5
+ * top-level `peaks compact` group:
6
+ *
7
+ * - suggest: PreToolUse-style two-signal suggestion (context size
8
+ * + tool-call count), read-only by default.
9
+ * - recommend: Pure (from, to) phase-pair → severity lookup.
10
+ * - survival: Static SKILL.md "What Survives Compaction" table.
11
+ * - dry-run: Composite of (suggest + recommend + survival), no
12
+ * writes.
13
+ * - force: Write a pre-compact checkpoint via
14
+ * `peaks session checkpoint`; the IDE-side `/compact`
15
+ * is still the LLM's call.
16
+ *
17
+ * Each subcommand returns a `--json` envelope via `printResult`.
18
+ */
19
+ import type { Command } from 'commander';
20
+ import { type ProgramIO } from '../cli-helpers.js';
21
+ export declare function registerCompactCommands(program: Command, io: ProgramIO): void;
@@ -0,0 +1,252 @@
1
+ import { resolveCanonicalProjectRoot } from '../../services/config/config-service.js';
2
+ import { findProjectRoot } from '../../services/config/config-safety.js';
3
+ import { fail, ok, getErrorMessage } from '../../shared/result.js';
4
+ import { addJsonOption, printResult } from '../cli-helpers.js';
5
+ import { writeCheckpoint } from '../../services/session/session-checkpoint-service.js';
6
+ import { getSessionIdCanonical } from '../../services/session/session-manager.js';
7
+ import { buildRecommendEnvelopePure, dryRunCompact, suggestCompact } from '../../services/compact/suggest-service.js';
8
+ import { PHASES, SURVIVAL_TABLE, isPhase, lookupPhaseTransition } from '../../services/compact/decision-tables.js';
9
+ function splitList(value) {
10
+ if (!value)
11
+ return [];
12
+ return value
13
+ .split(/\r?\n/)
14
+ .map((s) => s.trim())
15
+ .filter((s) => s.length > 0);
16
+ }
17
+ function resolveSessionId(projectRoot, explicit) {
18
+ if (explicit !== undefined && explicit.length > 0) {
19
+ return { sid: explicit, error: null };
20
+ }
21
+ // Statically-imported `getSessionIdCanonical` (see import block
22
+ // at the top of this file). It is the single source of truth for
23
+ // "what session is bound to this project"; we use it instead of
24
+ // re-implementing the resolution here to keep one fix point when
25
+ // the binding layout changes.
26
+ const sid = getSessionIdCanonical(projectRoot) ?? null;
27
+ if (sid === null) {
28
+ return {
29
+ sid: null,
30
+ error: {
31
+ code: 'NO_ACTIVE_SESSION',
32
+ message: 'No active session bound. Run `peaks workspace init --project <repo> --json` to bind one.',
33
+ nextActions: [`peaks workspace init --project ${projectRoot} --json`]
34
+ }
35
+ };
36
+ }
37
+ return { sid, error: null };
38
+ }
39
+ export function registerCompactCommands(program, io) {
40
+ const compact = program
41
+ .command('compact')
42
+ .description('Strategic-compact primitives: suggest / recommend / survival / dry-run / force');
43
+ // -----------------------------------------------------------------
44
+ // 1. peaks compact suggest [--json]
45
+ // -----------------------------------------------------------------
46
+ addJsonOption(compact
47
+ .command('suggest')
48
+ .description('Two-signal suggestion (context-size + tool-call-count). ' +
49
+ 'Honor COMPACT_THRESHOLD / COMPACT_CONTEXT_THRESHOLD / COMPACT_CONTEXT_INTERVAL env vars. ' +
50
+ 'Read-only by default; pass --apply to also append a one-line info row to the session log.')
51
+ .option('--project <path>', 'project root (defaults to git root or cwd)')
52
+ .option('--session-id <sid>', 'override the active session id (defaults to the canonical binding)')
53
+ .option('--apply', 'append a one-line info row to .peaks/_runtime/<sid>/session.json (default: dry-run)')).action((options) => {
54
+ try {
55
+ const projectRoot = options.project !== undefined
56
+ ? resolveCanonicalProjectRoot(options.project)
57
+ : (findProjectRoot(process.cwd()) ?? process.cwd());
58
+ const session = resolveSessionId(projectRoot, options.sessionId);
59
+ if (session.error !== null) {
60
+ printResult(io, fail('compact.suggest', session.error.code, session.error.message, { projectRoot }, session.error.nextActions), options.json);
61
+ process.exitCode = 1;
62
+ return;
63
+ }
64
+ const result = suggestCompact({
65
+ projectRoot,
66
+ sessionId: session.sid
67
+ });
68
+ printResult(io, ok('compact.suggest', result, [], [
69
+ result.shouldSuggest
70
+ ? `Run \`peaks compact force --reason "<short note>"\` to checkpoint, then /compact.`
71
+ : `Context at ${(result.ratio * 100).toFixed(1)}% (${result.tokensUsed} of ${result.windowKind === '1m' ? '1M' : '200k'}); below threshold.`
72
+ ]), options.json);
73
+ }
74
+ catch (error) {
75
+ printResult(io, fail('compact.suggest', 'COMPACT_SUGGEST_FAILED', getErrorMessage(error), {}, ['Verify project root and session binding before retrying']), options.json);
76
+ process.exitCode = 1;
77
+ }
78
+ });
79
+ // -----------------------------------------------------------------
80
+ // 2. peaks compact recommend --from <phase> --to <phase> [--json]
81
+ // -----------------------------------------------------------------
82
+ addJsonOption(compact
83
+ .command('recommend')
84
+ .description(`Strategic-compact "Compaction Decision Guide" lookup. ` +
85
+ `Valid phases: ${PHASES.join(', ')}. Pure function over (from, to); no I/O.`)
86
+ .requiredOption('--from <phase>', `source phase (one of ${PHASES.join(', ')})`)
87
+ .requiredOption('--to <phase>', `target phase (one of ${PHASES.join(', ')})`)).action((options) => {
88
+ try {
89
+ if (!isPhase(options.from)) {
90
+ printResult(io, fail('compact.recommend', 'INVALID_PHASE', `--from must be one of ${PHASES.join(', ')} (got "${options.from}")`, { from: options.from }, [`Use --from ${PHASES.join('|')}`]), options.json);
91
+ process.exitCode = 1;
92
+ return;
93
+ }
94
+ if (!isPhase(options.to)) {
95
+ printResult(io, fail('compact.recommend', 'INVALID_PHASE', `--to must be one of ${PHASES.join(', ')} (got "${options.to}")`, { to: options.to }, [`Use --to ${PHASES.join('|')}`]), options.json);
96
+ process.exitCode = 1;
97
+ return;
98
+ }
99
+ const envelope = buildRecommendEnvelopePure(options.from, options.to);
100
+ const lookup = lookupPhaseTransition(options.from, options.to);
101
+ printResult(io, ok('compact.recommend', {
102
+ from: envelope.from,
103
+ to: envelope.to,
104
+ shouldCompact: envelope.shouldCompact,
105
+ severity: envelope.severity,
106
+ rationale: envelope.rationale,
107
+ suggestedMessage: envelope.suggestedMessage,
108
+ notInTable: lookup.notInTable
109
+ }, lookup.notInTable ? [`Transition ${options.from} → ${options.to} is not in the strategic-compact table; defaulting to severity=no.`] : []), options.json);
110
+ }
111
+ catch (error) {
112
+ printResult(io, fail('compact.recommend', 'COMPACT_RECOMMEND_FAILED', getErrorMessage(error), { from: options.from, to: options.to }, ['Verify the phase pair against the documented transitions']), options.json);
113
+ process.exitCode = 1;
114
+ }
115
+ });
116
+ // -----------------------------------------------------------------
117
+ // 3. peaks compact survival [--json]
118
+ // -----------------------------------------------------------------
119
+ addJsonOption(compact
120
+ .command('survival')
121
+ .description('Strategic-compact "What Survives Compaction" table. Pure static data; no I/O.')).action((options) => {
122
+ printResult(io, ok('compact.survival', {
123
+ persists: [...SURVIVAL_TABLE.persists],
124
+ lost: [...SURVIVAL_TABLE.lost]
125
+ }, [], [
126
+ 'Persists = guaranteed across `/compact`. Lost = not preserved; persist to disk before compacting.'
127
+ ]), options.json);
128
+ });
129
+ // -----------------------------------------------------------------
130
+ // 4. peaks compact dry-run [--from <phase>] [--to <phase>] [--json]
131
+ // -----------------------------------------------------------------
132
+ addJsonOption(compact
133
+ .command('dry-run')
134
+ .description('Composite preview of (suggest + recommend + survival). ' +
135
+ 'No writes. The LLM calls this every tool-call cycle to stay informed.')
136
+ .option('--from <phase>', `optional source phase for recommend lookup (one of ${PHASES.join(', ')})`)
137
+ .option('--to <phase>', `optional target phase for recommend lookup (one of ${PHASES.join(', ')})`)
138
+ .option('--project <path>', 'project root (defaults to git root or cwd)')
139
+ .option('--session-id <sid>', 'override the active session id')).action((options) => {
140
+ try {
141
+ const projectRoot = options.project !== undefined
142
+ ? resolveCanonicalProjectRoot(options.project)
143
+ : (findProjectRoot(process.cwd()) ?? process.cwd());
144
+ const session = resolveSessionId(projectRoot, options.sessionId);
145
+ if (session.error !== null) {
146
+ printResult(io, fail('compact.dry-run', session.error.code, session.error.message, { projectRoot }, session.error.nextActions), options.json);
147
+ process.exitCode = 1;
148
+ return;
149
+ }
150
+ if ((options.from === undefined) !== (options.to === undefined)) {
151
+ printResult(io, fail('compact.dry-run', 'PHASE_PAIR_INCOMPLETE', 'Both --from and --to must be provided together', { from: options.from, to: options.to }, ['Pass both --from and --to, or omit both for a suggest-only dry-run']), options.json);
152
+ process.exitCode = 1;
153
+ return;
154
+ }
155
+ if (options.from !== undefined && !isPhase(options.from)) {
156
+ printResult(io, fail('compact.dry-run', 'INVALID_PHASE', `--from must be one of ${PHASES.join(', ')} (got "${options.from}")`, { from: options.from }, [`Use --from ${PHASES.join('|')}`]), options.json);
157
+ process.exitCode = 1;
158
+ return;
159
+ }
160
+ if (options.to !== undefined && !isPhase(options.to)) {
161
+ printResult(io, fail('compact.dry-run', 'INVALID_PHASE', `--to must be one of ${PHASES.join(', ')} (got "${options.to}")`, { to: options.to }, [`Use --to ${PHASES.join('|')}`]), options.json);
162
+ process.exitCode = 1;
163
+ return;
164
+ }
165
+ const hasPhasePair = options.from !== undefined && options.to !== undefined;
166
+ const dryRunInput = {
167
+ projectRoot,
168
+ sessionId: session.sid
169
+ };
170
+ if (hasPhasePair) {
171
+ dryRunInput.from = options.from;
172
+ dryRunInput.to = options.to;
173
+ }
174
+ const result = dryRunCompact(dryRunInput);
175
+ printResult(io, ok('compact.dry-run', result, [], [
176
+ result.action === 'compact'
177
+ ? `Action=compact: run \`peaks compact force --reason "<note>"\`, then /compact.`
178
+ : `Action=skip: continue without compacting.`
179
+ ]), options.json);
180
+ }
181
+ catch (error) {
182
+ printResult(io, fail('compact.dry-run', 'COMPACT_DRY_RUN_FAILED', getErrorMessage(error), {}, ['Verify project root, session binding, and phase pair before retrying']), options.json);
183
+ process.exitCode = 1;
184
+ }
185
+ });
186
+ // -----------------------------------------------------------------
187
+ // 5. peaks compact force [--reason <text>] [--json]
188
+ // -----------------------------------------------------------------
189
+ addJsonOption(compact
190
+ .command('force')
191
+ .description('Write a pre-compact checkpoint via `peaks session checkpoint --reason context-fill`. ' +
192
+ 'The IDE-side `/compact` is still the LLM\'s call; this primitive guarantees ' +
193
+ 'the pre-compact state is persisted. NO sleep, NO wait for IDE response.')
194
+ .option('--reason <text>', 'human-readable reason for the pre-compact checkpoint', 'pre-force-compact')
195
+ .option('--project <path>', 'project root (defaults to git root or cwd)')
196
+ .option('--session-id <sid>', 'override the active session id')
197
+ .option('--current-plan <text>', 'current plan summary (forwarded to the checkpoint snapshot)')
198
+ .option('--open-questions <list>', 'newline-separated open questions')
199
+ .option('--recent-decisions <list>', 'newline-separated recent decisions')
200
+ .option('--recent-artifact-paths <list>', 'newline-separated recent artifact paths')
201
+ .option('--git-status <text>', 'recent git status')
202
+ .option('--skills-active <list>', 'newline-separated active skill names')
203
+ .option('--todo-state <list>', 'newline-separated todo lines')).action((options) => {
204
+ try {
205
+ const projectRoot = options.project !== undefined
206
+ ? resolveCanonicalProjectRoot(options.project)
207
+ : (findProjectRoot(process.cwd()) ?? process.cwd());
208
+ const session = resolveSessionId(projectRoot, options.sessionId);
209
+ if (session.error !== null) {
210
+ printResult(io, fail('compact.force', session.error.code, session.error.message, { projectRoot }, session.error.nextActions), options.json);
211
+ process.exitCode = 1;
212
+ return;
213
+ }
214
+ const reason = (options.reason ?? 'pre-force-compact').slice(0, 200);
215
+ // The session-checkpoint-service restricts `reason` to a fixed
216
+ // enum. The strategic-compact `force` primitive uses
217
+ // 'context-fill' (the closest semantic match: the LLM is
218
+ // compacting because of context pressure) and records the
219
+ // caller's free-form reason in `gitStatus` so the snapshot
220
+ // is self-describing without inventing a new enum value.
221
+ const checkpointOptions = {
222
+ sessionId: session.sid,
223
+ reason: 'context-fill',
224
+ gitStatus: `compact.force: ${reason}`,
225
+ openQuestions: splitList(options.openQuestions),
226
+ recentDecisions: splitList(options.recentDecisions),
227
+ recentArtifactPaths: splitList(options.recentArtifactPaths),
228
+ skillsActive: splitList(options.skillsActive),
229
+ todoState: splitList(options.todoState)
230
+ };
231
+ if (options.currentPlan !== undefined) {
232
+ checkpointOptions.currentPlan = options.currentPlan;
233
+ }
234
+ const result = writeCheckpoint(projectRoot, checkpointOptions);
235
+ printResult(io, ok('compact.force', {
236
+ checkpointPath: result.path,
237
+ reason: 'pre-force-compact',
238
+ callerReason: reason,
239
+ sessionId: result.sessionId,
240
+ createdAt: result.createdAt,
241
+ totalRetained: result.totalRetained,
242
+ message: 'Pre-compact checkpoint written. The IDE-side /compact is still the LLM\'s call; this CLI does NOT invoke the IDE slash command.'
243
+ }, [], [
244
+ 'After the LLM fires the IDE-side /compact, call `peaks compact survival` to see what to persist before the next compact.'
245
+ ]), options.json);
246
+ }
247
+ catch (error) {
248
+ printResult(io, fail('compact.force', 'COMPACT_FORCE_FAILED', getErrorMessage(error), {}, ['Verify the project path is writable and a session is bound']), options.json);
249
+ process.exitCode = 1;
250
+ }
251
+ });
252
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * peaks context * CLI (Slice #3 L1c) — context 4-layer loader.
3
3
  *
4
- * Per docs/superpowers/specs/2026-06-11-peaks-cli-l1-l2-l3-redesign.md §4 L1c:
4
+ * Per docs/superpowers/specs/2026-06-11-peaks-loop-l1-l2-l3-redesign.md §4 L1c:
5
5
  * - L0: full read (every SKILL.md in full)
6
6
  * - L1: summary (one paragraph per skill + the skill list)
7
7
  * - L2: index (just names + paths)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * peaks context * CLI (Slice #3 L1c) — context 4-layer loader.
3
3
  *
4
- * Per docs/superpowers/specs/2026-06-11-peaks-cli-l1-l2-l3-redesign.md §4 L1c:
4
+ * Per docs/superpowers/specs/2026-06-11-peaks-loop-l1-l2-l3-redesign.md §4 L1c:
5
5
  * - L0: full read (every SKILL.md in full)
6
6
  * - L1: summary (one paragraph per skill + the skill list)
7
7
  * - L2: index (just names + paths)
@@ -14,7 +14,7 @@
14
14
  * `src/services/dispatch/contract-store.ts`; it accepts the same
15
15
  * shape, splits the comma-separated `--exports` / `--types` /
16
16
  * `--signatures` flag values into arrays, and surfaces the result
17
- * via the standard `ok` / `fail` envelope used across peaks-cli.
17
+ * via the standard `ok` / `fail` envelope used across peaks-loop.
18
18
  *
19
19
  * Skill-first / CLI-auxiliary red line: users do not invoke this
20
20
  * directly. The peaks-solo / peaks-rd LLM-side runner (the
@@ -5,6 +5,14 @@ import { resolveCanonicalProjectRoot } from '../../../services/config/config-ser
5
5
  import { findProjectRoot } from '../../../services/config/config-safety.js';
6
6
  import { fail, ok } from '../../../shared/result.js';
7
7
  import { addJsonOption, getErrorMessage, printResult } from '../../cli-helpers.js';
8
+ // Slice 2026-07-01-strategic-compact-cli fix: register the
9
+ // `peaks session checkpoint` and `peaks session resume` subcommands
10
+ // EAGERLY so they appear in `peaks session --help` output. The
11
+ // previous lazy import ran AFTER commander had already produced
12
+ // the help text, so an LLM calling `peaks <TAB>`-discovery never
13
+ // saw them — they were effectively hidden.
14
+ import { registerSessionCheckpointCommand } from '../session-checkpoint-command.js';
15
+ import { registerSessionResumeCommand } from '../session-resume-command.js';
8
16
  export function registerSessionCommand(program, io) {
9
17
  const session = program.command('session').description('Manage Peaks session directories');
10
18
  addJsonOption(session
@@ -169,13 +177,15 @@ export function registerSessionCommand(program, io) {
169
177
  });
170
178
  // Slice 011: `peaks session checkpoint` and `peaks session resume`.
171
179
  // Skill-level primitives — the LLM is the decision-maker; CLI is the muscle.
172
- // Lazy-import to avoid circular resolution with the session-manager.
173
- void (async () => {
174
- const { registerSessionCheckpointCommand } = await import('../session-checkpoint-command.js');
175
- const { registerSessionResumeCommand } = await import('../session-resume-command.js');
176
- registerSessionCheckpointCommand(session, io);
177
- registerSessionResumeCommand(session, io);
178
- })();
180
+ // Slice 2026-07-01-strategic-compact-cli fix: register EAGERLY (not
181
+ // via `void (async () => …)`) so commander walks the subcommand
182
+ // tree synchronously and `peaks session --help` includes both
183
+ // `checkpoint` and `resume`. The previous async-IIFE registered
184
+ // them on a microtask tick AFTER the program-level help builder
185
+ // had already serialised its output, so they were hidden from
186
+ // `<TAB>`-discovery and the LLM never saw them.
187
+ registerSessionCheckpointCommand(session, io);
188
+ registerSessionResumeCommand(session, io);
179
189
  addJsonOption(session
180
190
  .command('rotate')
181
191
  .description('Drop the project-level session binding so the next peaks call auto-generates a fresh session id. The on-disk session directory is left intact — only .peaks/.session.json is removed.')
@@ -264,7 +264,7 @@ export function registerSkillCommand(program, io) {
264
264
  });
265
265
  addJsonOption(skill
266
266
  .command('detect-marker-loss')
267
- .description('Detect whether the latest assistant message lost the Peaks-Cli status header while a peaks skill is still active (slice 028 detection primitive).')
267
+ .description('Detect whether the latest assistant message lost the Peaks-Loop status header while a peaks skill is still active (slice 028 detection primitive).')
268
268
  .option('--project <path>', 'project root path (auto-detected from cwd when omitted)')
269
269
  .option('--message <text>', 'latest assistant message text to scan (defaults to reading the most recent LLM response from the stdin pipe, or empty string when no pipe is attached)')).action((options) => {
270
270
  const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
@@ -22,7 +22,7 @@ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
22
22
  export function registerFeedbackCommands(program, io) {
23
23
  const feedback = program
24
24
  .command('feedback')
25
- .description('v2.15.0 slice 002 AC-3: promote user-given feedback memories to peaks-cli enforcement layers (A: sop, B: hooks, C: hard-floor).');
25
+ .description('v2.15.0 slice 002 AC-3: promote user-given feedback memories to peaks-loop enforcement layers (A: sop, B: hooks, C: hard-floor).');
26
26
  addJsonOption(feedback
27
27
  .command('promote <memory-file>')
28
28
  .description('Promote a feedback memory to one of the 3 enforcement layers (A=peaks-sop gate, B=peaks-hooks PreToolUse, C=mode-gate hardFloorCategory). ' +
@@ -3,7 +3,7 @@ import { type ProgramIO } from '../cli-helpers.js';
3
3
  /**
4
4
  * `peaks hook handle` —— peaks 自有 hook 协议的单一入口。
5
5
  *
6
- * 该命令是 peaks-cli 拥有的 hook 处理总入口。它:
6
+ * 该命令是 peaks-loop 拥有的 hook 处理总入口。它:
7
7
  * 1. 读 stdin
8
8
  * 2. auto-detect 来源 IDE(env / stdin shape / cwd)
9
9
  * 3. 归一化到 peaks canonical schema
@@ -31,7 +31,7 @@ async function readStdin() {
31
31
  /**
32
32
  * `peaks hook handle` —— peaks 自有 hook 协议的单一入口。
33
33
  *
34
- * 该命令是 peaks-cli 拥有的 hook 处理总入口。它:
34
+ * 该命令是 peaks-loop 拥有的 hook 处理总入口。它:
35
35
  * 1. 读 stdin
36
36
  * 2. auto-detect 来源 IDE(env / stdin shape / cwd)
37
37
  * 3. 归一化到 peaks canonical schema
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * - `peaks legacy detect --dir <path>` — scan a directory for legacy
5
5
  * smells (TODO/FIXME/HACK, console.log, any-type, large files, ts-ignore).
6
- * Useful as a "where to start" inventory when peaks-cli is applied
6
+ * Useful as a "where to start" inventory when peaks-loop is applied
7
7
  * to a 存量老项目.
8
8
  */
9
9
  import type { Command } from 'commander';
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * - `peaks legacy detect --dir <path>` — scan a directory for legacy
5
5
  * smells (TODO/FIXME/HACK, console.log, any-type, large files, ts-ignore).
6
- * Useful as a "where to start" inventory when peaks-cli is applied
6
+ * Useful as a "where to start" inventory when peaks-loop is applied
7
7
  * to a 存量老项目.
8
8
  */
9
9
  import { findProjectRoot } from '../../services/config/config-safety.js';
@@ -17,7 +17,7 @@ export function registerLegacyCommands(program, io) {
17
17
  'console.log, any-type, large files, ts-ignore). Returns a smell grade ' +
18
18
  '(low / medium / high) and per-kind counts. Lightweight heuristics — ' +
19
19
  'NOT a replacement for a real linter. Useful as a "where to start" ' +
20
- 'inventory when peaks-cli is applied to a legacy codebase.')
20
+ 'inventory when peaks-loop is applied to a legacy codebase.')
21
21
  .option('--dir <path>', 'directory to scan (default: src)', 'src')
22
22
  .option('--project <path>', 'project root (default: cwd)')).action((opts) => {
23
23
  const projectRoot = opts.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
@@ -20,10 +20,10 @@ import { ok, fail } from '../../shared/result.js';
20
20
  export function registerLogCommands(program, io) {
21
21
  const log = program
22
22
  .command('log')
23
- .description('Inspect the peaks-cli JSONL log directory (default: ~/.peaks/logs/)');
23
+ .description('Inspect the peaks-loop JSONL log directory (default: ~/.peaks/logs/)');
24
24
  addJsonOption(log
25
25
  .command('tail')
26
- .description('Print the last 50 lines of today\'s peaks-cli log (JSONL). Pass --lines N to change the count, --date YYYY-MM-DD to read a different day, --batch <id> to filter by batchId, --json to print a JSON envelope.')
26
+ .description('Print the last 50 lines of today\'s peaks-loop log (JSONL). Pass --lines N to change the count, --date YYYY-MM-DD to read a different day, --batch <id> to filter by batchId, --json to print a JSON envelope.')
27
27
  .option('--lines <n>', 'number of lines to show (default 50)', (value) => Number(value))
28
28
  .option('--date <YYYY-MM-DD>', 'read the log for this UTC date instead of today (PRD AC2: PEAKS_LOG_DATE_OVERRIDE)')
29
29
  // Slice 2026-06-23-audit-4th #B2: --batch filter for cross-run
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * peaks loop * CLI (Slice #14) — L4 Agent Loop Integration.
3
3
  *
4
- * Per docs/superpowers/specs/2026-06-11-peaks-cli-l1-l2-l3-redesign.md §5.4
4
+ * Per docs/superpowers/specs/2026-06-11-peaks-loop-l1-l2-l3-redesign.md §5.4
5
5
  * Slice #14, ships 5 sub-features:
6
6
  *
7
7
  * 14.1 peaks loop distill — extract patterns from past sessions
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * peaks loop * CLI (Slice #14) — L4 Agent Loop Integration.
3
3
  *
4
- * Per docs/superpowers/specs/2026-06-11-peaks-cli-l1-l2-l3-redesign.md §5.4
4
+ * Per docs/superpowers/specs/2026-06-11-peaks-loop-l1-l2-l3-redesign.md §5.4
5
5
  * Slice #14, ships 5 sub-features:
6
6
  *
7
7
  * 14.1 peaks loop distill — extract patterns from past sessions
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * New commands (added under the existing `peaks workflow` group and the
7
7
  * existing `peaks loop` group — never as new top-level commands, per the
8
- * peaks-cli add-a-new-subcommand-check-for-existing-top-level-first rule):
8
+ * peaks-loop add-a-new-subcommand-check-for-existing-top-level-first rule):
9
9
  *
10
10
  * peaks workflow run <id> --session <sid> --project <repo> --json
11
11
  * peaks workflow plan <id> --session <sid> --json (dry-run)
@@ -199,7 +199,7 @@ export function registerWorkflowEvalCommands(program, io) {
199
199
  ...(capture !== null ? { score: capture.score, capture } : {})
200
200
  }, [], envelope.degraded
201
201
  ? ['Evaluator ran in degraded mode (peaks CLI unavailable). Verify the verdict by running `peaks verdict aggregate --from-rid ' + rid + '`.',
202
- 'Re-run on a fully-installed peaks-cli environment for a real verdict.']
202
+ 'Re-run on a fully-installed peaks-loop environment for a real verdict.']
203
203
  : [`Verifier verdict-aggregate can consume this envelope via \`peaks verdict aggregate --from-rid ${rid}\`.`]), options.json);
204
204
  process.exitCode = exitCode;
205
205
  }
@@ -25,7 +25,7 @@
25
25
  * 3. hash(process.ppid + process.env.SSH_TTY || 'no-tty')
26
26
  *
27
27
  * The CLI does NOT bundle `playwright-mcp`; it shells out to
28
- * `npx playwright-mcp@latest` (G22 / NG3). peaks-cli is the lifecycle
28
+ * `npx playwright-mcp@latest` (G22 / NG3). peaks-loop is the lifecycle
29
29
  * orchestrator, not the install medium.
30
30
  */
31
31
  import { spawn } from 'node:child_process';
@@ -25,7 +25,7 @@
25
25
  * 3. hash(process.ppid + process.env.SSH_TTY || 'no-tty')
26
26
  *
27
27
  * The CLI does NOT bundle `playwright-mcp`; it shells out to
28
- * `npx playwright-mcp@latest` (G22 / NG3). peaks-cli is the lifecycle
28
+ * `npx playwright-mcp@latest` (G22 / NG3). peaks-loop is the lifecycle
29
29
  * orchestrator, not the install medium.
30
30
  */
31
31
  import { createHash } from 'node:crypto';
@@ -428,5 +428,5 @@ export function resolveUserDataDir(raw, projectRoot) {
428
428
  * the `runBrowserAction(..., caller)` overload.
429
429
  */
430
430
  async function mockOrPassThroughCaller(tool, args) {
431
- return { tool, args, routedBy: 'peaks-cli-browser-wrapper' };
431
+ return { tool, args, routedBy: 'peaks-loop-browser-wrapper' };
432
432
  }
@@ -342,6 +342,33 @@ export function registerRequestCommands(program, io) {
342
342
  process.exitCode = 1;
343
343
  return;
344
344
  }
345
+ // Slice 2026-07-01-strategic-compact-cli — slice-boundary pre-compact
346
+ // hook. When transitioning across the RD → QA boundary (rd:qa-handoff),
347
+ // check the active session's context-fill ratio. If it is in the
348
+ // 0.85–0.95 pre-compact zone, write a checkpoint BEFORE the
349
+ // transition completes and attach `preCompactCheckpoint: true` to
350
+ // the response envelope. The LLM remains the decision-maker; this
351
+ // hook only surfaces the signal. The 0.95 red-line behaviour is
352
+ // unchanged — the existing auto-compact orchestrator continues to
353
+ // refuse dispatch at ratio ≥ 0.95.
354
+ let preCompact = null;
355
+ if (role === 'rd' && newState === 'qa-handoff') {
356
+ try {
357
+ const { maybePreCompactCheckpoint } = await import('../../services/compact/request-transition-hook.js');
358
+ const hook = maybePreCompactCheckpoint({
359
+ projectRoot: options.project,
360
+ sessionId: result.sessionId,
361
+ transitionKey: `${role}:${newState}`
362
+ });
363
+ preCompact = hook.triggered
364
+ ? ok('request.transition.preCompact', hook)
365
+ : null;
366
+ }
367
+ catch {
368
+ // The hook is best-effort; never block the transition.
369
+ preCompact = null;
370
+ }
371
+ }
345
372
  // v2.13.2 AC-4 — auto-regen prd/handoff.md on prd:handed-off success.
346
373
  // Only fires when the handoff is missing; existing handoffs are not overwritten.
347
374
  if (role === 'prd' && newState === 'handed-off' && options.sessionId !== undefined) {
@@ -365,7 +392,19 @@ export function registerRequestCommands(program, io) {
365
392
  printResult(io, ok('request.transition', { ...result, handoffAutoRegen: { status: 'failed', reason: regen.reason } }, [`prd handoff auto-regen failed: ${regen.reason}`]), options.json);
366
393
  return;
367
394
  }
368
- printResult(io, ok('request.transition', result), options.json);
395
+ // Slice 2026-07-01-strategic-compact-cli: stitch the pre-compact
396
+ // checkpoint signal into the final response envelope. When the
397
+ // hook did not trigger (zone is not pre-compact OR the
398
+ // transition is not a slice boundary), preCompact is null and
399
+ // we emit a `preCompactCheckpoint: null` field so LLM callers
400
+ // can branch on it without re-deriving zone membership.
401
+ printResult(io, ok('request.transition', { ...result, preCompactCheckpoint: preCompact?.data ?? null }, preCompact !== null
402
+ ? [
403
+ `Pre-compact checkpoint written at ratio=${typeof preCompact.data === 'object' && preCompact.data !== null && 'ratio' in preCompact.data
404
+ ? String(preCompact.data.ratio)
405
+ : 'unknown'} (zone=pre-compact)`
406
+ ]
407
+ : []), options.json);
369
408
  }
370
409
  catch (error) {
371
410
  if (error instanceof InvalidArgumentError) {
@@ -15,7 +15,7 @@ import { addJsonOption, printResult } from '../cli-helpers.js';
15
15
  export function registerRoleCommands(program, io) {
16
16
  const role = program
17
17
  .command('role')
18
- .description('v2.15.0 follow-up G9: lightweight role registry (RBAC for multi-user peaks-cli teams).');
18
+ .description('v2.15.0 follow-up G9: lightweight role registry (RBAC for multi-user peaks-loop teams).');
19
19
  addJsonOption(role
20
20
  .command('list')
21
21
  .description('List all roles in the registry.')