peaks-loop 4.0.0-beta.9 → 4.0.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 (552) hide show
  1. package/CHANGELOG.md +572 -234
  2. package/README-en.md +67 -30
  3. package/README.md +67 -30
  4. package/agents/karpathy-reviewer.md +33 -0
  5. package/bin/peaks.js +0 -0
  6. package/dist/cli/cli-helpers.d.ts +31 -6
  7. package/dist/cli/cli-helpers.js +48 -25
  8. package/dist/cli/commands/_register.d.ts +3 -0
  9. package/dist/cli/commands/_register.js +139 -0
  10. package/dist/cli/commands/_super.d.ts +10 -0
  11. package/dist/cli/commands/_super.js +97 -0
  12. package/dist/cli/commands/adapter-commands-s2a.js +1 -1
  13. package/dist/cli/commands/asset-commands.js +33 -16
  14. package/dist/cli/commands/audit-commands.js +2 -2
  15. package/dist/cli/commands/autonomous-swarm-commands.d.ts +12 -0
  16. package/dist/cli/commands/autonomous-swarm-commands.js +120 -0
  17. package/dist/cli/commands/bee-commands.js +1 -1
  18. package/dist/cli/commands/capability-commands.js +1 -1
  19. package/dist/cli/commands/capability-worker-config-sc-commands.js +0 -2
  20. package/dist/cli/commands/changeset-commands.d.ts +13 -0
  21. package/dist/cli/commands/changeset-commands.js +46 -0
  22. package/dist/cli/commands/code-commands.d.ts +11 -1
  23. package/dist/cli/commands/code-commands.js +19 -699
  24. package/dist/cli/commands/code-job-shape-commands.d.ts +9 -0
  25. package/dist/cli/commands/code-job-shape-commands.js +164 -0
  26. package/dist/cli/commands/code-mode-gate-commands.d.ts +9 -0
  27. package/dist/cli/commands/code-mode-gate-commands.js +223 -0
  28. package/dist/cli/commands/code-review-commands.js +2 -2
  29. package/dist/cli/commands/code-run-command.d.ts +12 -0
  30. package/dist/cli/commands/code-run-command.js +68 -0
  31. package/dist/cli/commands/code-runtime-commands.d.ts +11 -0
  32. package/dist/cli/commands/code-runtime-commands.js +391 -0
  33. package/dist/cli/commands/codegraph-commands.js +1 -1
  34. package/dist/cli/commands/compact-command.js +53 -1
  35. package/dist/cli/commands/complexity-commands.js +1 -1
  36. package/dist/cli/commands/config-commands.js +4 -75
  37. package/dist/cli/commands/container-commands.d.ts +28 -0
  38. package/dist/cli/commands/container-commands.js +221 -0
  39. package/dist/cli/commands/context-commands.js +3 -2
  40. package/dist/cli/commands/core/artifacts-command.js +1 -1
  41. package/dist/cli/commands/core/binding-commands.js +1 -1
  42. package/dist/cli/commands/core/doctor-command.js +63 -3
  43. package/dist/cli/commands/core/memory-command.js +1 -1
  44. package/dist/cli/commands/core/profile-command.js +1 -1
  45. package/dist/cli/commands/core/proxy-command.js +1 -1
  46. package/dist/cli/commands/core/session-command.js +9 -7
  47. package/dist/cli/commands/core/skill-command.js +7 -4
  48. package/dist/cli/commands/core/standards-command.js +1 -1
  49. package/dist/cli/commands/cron-commands.d.ts +57 -0
  50. package/dist/cli/commands/cron-commands.js +257 -0
  51. package/dist/cli/commands/cron-scheduler-commands.d.ts +47 -0
  52. package/dist/cli/commands/cron-scheduler-commands.js +284 -0
  53. package/dist/cli/commands/dashboard-commands.d.ts +4 -0
  54. package/dist/cli/commands/dashboard-commands.js +10 -0
  55. package/dist/cli/commands/dashboard-long-run.d.ts +25 -0
  56. package/dist/cli/commands/dashboard-long-run.js +122 -0
  57. package/dist/cli/commands/dashboard-summary.d.ts +12 -0
  58. package/dist/cli/commands/dashboard-summary.js +58 -0
  59. package/dist/cli/commands/dispatch-commands.d.ts +0 -7
  60. package/dist/cli/commands/dispatch-commands.js +372 -8
  61. package/dist/cli/commands/dispatch-from-dag.js +4 -1
  62. package/dist/cli/commands/doc-commands.js +1 -1
  63. package/dist/cli/commands/ecc-commands.d.ts +30 -0
  64. package/dist/cli/commands/ecc-commands.js +119 -0
  65. package/dist/cli/commands/evolution-commands.js +1 -1
  66. package/dist/cli/commands/feedback-commands.js +1 -1
  67. package/dist/cli/commands/final-review-commands.js +1 -1
  68. package/dist/cli/commands/fixture-commands.js +1 -1
  69. package/dist/cli/commands/fork-commands.js +1 -1
  70. package/dist/cli/commands/gate-commands.js +128 -6
  71. package/dist/cli/commands/governance-classify-contract-commands.d.ts +21 -0
  72. package/dist/cli/commands/governance-classify-contract-commands.js +273 -0
  73. package/dist/cli/commands/heartbeat-commands.js +76 -5
  74. package/dist/cli/commands/heartbeat-watch-command.d.ts +31 -0
  75. package/dist/cli/commands/heartbeat-watch-command.js +146 -0
  76. package/dist/cli/commands/hook-handle.js +1 -1
  77. package/dist/cli/commands/hooks-commands.js +126 -10
  78. package/dist/cli/commands/ide-commands.js +1 -1
  79. package/dist/cli/commands/impact-commands.js +1 -1
  80. package/dist/cli/commands/job-commands.js +34 -1
  81. package/dist/cli/commands/lease-metrics-commands.d.ts +67 -0
  82. package/dist/cli/commands/lease-metrics-commands.js +231 -0
  83. package/dist/cli/commands/lease-stats-commands.d.ts +65 -0
  84. package/dist/cli/commands/lease-stats-commands.js +107 -0
  85. package/dist/cli/commands/legacy-commands.js +1 -1
  86. package/dist/cli/commands/log-commands.js +1 -1
  87. package/dist/cli/commands/loop-commands.js +1 -1
  88. package/dist/cli/commands/loop-eval-commands.js +1 -1
  89. package/dist/cli/commands/memory-commands.js +1 -1
  90. package/dist/cli/commands/migrate-1-4-1-command.js +1 -1
  91. package/dist/cli/commands/migrate-v2-10-to-v2-11-command.js +1 -1
  92. package/dist/cli/commands/mut-commands.d.ts +1 -1
  93. package/dist/cli/commands/mut-commands.js +1 -5
  94. package/dist/cli/commands/observability-commands.js +1 -1
  95. package/dist/cli/commands/openspec-commands.js +65 -4
  96. package/dist/cli/commands/perf-audit-commands.js +3 -3
  97. package/dist/cli/commands/perf-commands.js +1 -1
  98. package/dist/cli/commands/polyrepo-commands.js +1 -1
  99. package/dist/cli/commands/prd-blocks-commands.js +1 -1
  100. package/dist/cli/commands/prd-commands.js +4 -2
  101. package/dist/cli/commands/preferences-commands.js +23 -3
  102. package/dist/cli/commands/project-commands.js +1 -1
  103. package/dist/cli/commands/qa-business-review-commands.js +1 -1
  104. package/dist/cli/commands/qa-commands.d.ts +1 -1
  105. package/dist/cli/commands/qa-commands.js +39 -3
  106. package/dist/cli/commands/release-commands.d.ts +15 -0
  107. package/dist/cli/commands/release-commands.js +113 -21
  108. package/dist/cli/commands/request-commands.js +1 -1
  109. package/dist/cli/commands/retrospective-commands.js +1 -1
  110. package/dist/cli/commands/reviewer-commands.js +1 -1
  111. package/dist/cli/commands/role-commands.js +1 -1
  112. package/dist/cli/commands/runtime-commands-helpers.d.ts +7 -0
  113. package/dist/cli/commands/runtime-commands-helpers.js +24 -0
  114. package/dist/cli/commands/runtime-commands.js +1 -2
  115. package/dist/cli/commands/sc-commands.js +4 -2
  116. package/dist/cli/commands/scan-commands.js +1 -1
  117. package/dist/cli/commands/security-audit-commands.js +3 -3
  118. package/dist/cli/commands/sediment-commands.js +10 -1
  119. package/dist/cli/commands/session-24h-mode.d.ts +26 -0
  120. package/dist/cli/commands/session-24h-mode.js +196 -0
  121. package/dist/cli/commands/share-commands.d.ts +19 -0
  122. package/dist/cli/commands/share-commands.js +121 -2
  123. package/dist/cli/commands/skill-conformance-commands.js +1 -1
  124. package/dist/cli/commands/skill-loop-engineering-readiness-commands.js +1 -1
  125. package/dist/cli/commands/skill-search-commands.js +4 -1
  126. package/dist/cli/commands/slice-commands.js +1 -1
  127. package/dist/cli/commands/slice-integrate-commands.js +1 -1
  128. package/dist/cli/commands/slice-review-commands.js +1 -1
  129. package/dist/cli/commands/smoke-commands.js +1 -1
  130. package/dist/cli/commands/sop-commands.js +1 -1
  131. package/dist/cli/commands/spill-demo-command.d.ts +7 -0
  132. package/dist/cli/commands/spill-demo-command.js +29 -0
  133. package/dist/cli/commands/statusline-commands.d.ts +12 -0
  134. package/dist/cli/commands/statusline-commands.js +79 -17
  135. package/dist/cli/commands/sub-agent-commands.js +2 -1
  136. package/dist/cli/commands/sub-agent-shared.d.ts +10 -0
  137. package/dist/cli/commands/sub-agent-shared.js +24 -1
  138. package/dist/cli/commands/swarm-commands.d.ts +13 -0
  139. package/dist/cli/commands/swarm-commands.js +77 -0
  140. package/dist/cli/commands/tech-commands.d.ts +64 -0
  141. package/dist/cli/commands/tech-commands.js +195 -0
  142. package/dist/cli/commands/understand-commands.js +1 -1
  143. package/dist/cli/commands/upgrade-commands.d.ts +2 -0
  144. package/dist/cli/commands/upgrade-commands.js +40 -5
  145. package/dist/cli/commands/user-touchpoint-commands.js +1 -1
  146. package/dist/cli/commands/verdict-aggregate-command.js +1 -1
  147. package/dist/cli/commands/vm-commands.d.ts +31 -0
  148. package/dist/cli/commands/vm-commands.js +274 -0
  149. package/dist/cli/commands/workflow-commands.js +24 -1
  150. package/dist/cli/commands/workflow-plan-commands.js +1 -1
  151. package/dist/cli/commands/workspace/init-command.js +1 -1
  152. package/dist/cli/commands/workspace/migrate-command.js +1 -1
  153. package/dist/cli/commands/workspace/reconcile-command.js +1 -1
  154. package/dist/cli/commands/worktree-auth-commands.d.ts +29 -0
  155. package/dist/cli/commands/worktree-auth-commands.js +652 -0
  156. package/dist/cli/index.js +58 -13
  157. package/dist/cli/program.d.ts +2 -2
  158. package/dist/cli/program.js +86 -281
  159. package/dist/services/24h-mode/decider.d.ts +103 -0
  160. package/dist/services/24h-mode/decider.js +144 -0
  161. package/dist/services/24h-mode/index.d.ts +12 -0
  162. package/dist/services/24h-mode/index.js +12 -0
  163. package/dist/services/24h-mode/state.d.ts +48 -0
  164. package/dist/services/24h-mode/state.js +51 -0
  165. package/dist/services/24h-mode/store.d.ts +27 -0
  166. package/dist/services/24h-mode/store.js +119 -0
  167. package/dist/services/artifacts/artifact-prerequisites.js +1 -1
  168. package/dist/services/artifacts/request-artifact-service.js +1 -1
  169. package/dist/services/artifacts/workspace-service.js +1 -1
  170. package/dist/services/audit/enforcers/lint-bee-runtime-contract.d.ts +25 -0
  171. package/dist/services/audit/enforcers/lint-bee-runtime-contract.js +91 -0
  172. package/dist/services/audit/enforcers/lint-peaks-code-runtime.d.ts +16 -0
  173. package/dist/services/audit/enforcers/lint-peaks-code-runtime.js +31 -0
  174. package/dist/services/audit/enforcers/lint-peaks-skill-runtime.d.ts +23 -0
  175. package/dist/services/audit/enforcers/lint-peaks-skill-runtime.js +59 -0
  176. package/dist/services/audit/enforcers/lint-peaks-ui-sc-txt-runtime.d.ts +28 -0
  177. package/dist/services/audit/enforcers/lint-peaks-ui-sc-txt-runtime.js +86 -0
  178. package/dist/services/audit/enforcers/lint-prd-artifact-handoff.d.ts +16 -0
  179. package/dist/services/audit/enforcers/lint-prd-artifact-handoff.js +41 -0
  180. package/dist/services/audit/enforcers/lint-prd-source-snapshot.d.ts +27 -0
  181. package/dist/services/audit/enforcers/lint-prd-source-snapshot.js +47 -0
  182. package/dist/services/audit/enforcers/lint-qa-gateguard-and-runtime.d.ts +19 -0
  183. package/dist/services/audit/enforcers/lint-qa-gateguard-and-runtime.js +47 -0
  184. package/dist/services/audit/enforcers/lint-rd-handoff-coverage.d.ts +18 -0
  185. package/dist/services/audit/enforcers/lint-rd-handoff-coverage.js +70 -0
  186. package/dist/services/audit/enforcers/lint-skill-presence-mandatory.d.ts +25 -0
  187. package/dist/services/audit/enforcers/lint-skill-presence-mandatory.js +53 -0
  188. package/dist/services/audit/red-line-catalog-p2-a.d.ts +0 -6
  189. package/dist/services/audit/red-line-catalog-p2-a.js +230 -1
  190. package/dist/services/audit/static-service.d.ts +32 -25
  191. package/dist/services/audit/static-service.js +30 -109
  192. package/dist/services/audit-independent/index.d.ts +2 -0
  193. package/dist/services/audit-independent/index.js +2 -0
  194. package/dist/services/autonomous-swarm/autonomous-swarm-service.d.ts +127 -0
  195. package/dist/services/autonomous-swarm/autonomous-swarm-service.js +446 -0
  196. package/dist/services/changeset/changeset-check-service.d.ts +22 -0
  197. package/dist/services/changeset/changeset-check-service.js +47 -0
  198. package/dist/services/code/auto-compact-modes.d.ts +30 -0
  199. package/dist/services/code/auto-compact-modes.js +38 -0
  200. package/dist/services/code/auto-compact-orchestrator.d.ts +43 -5
  201. package/dist/services/code/auto-compact-orchestrator.js +101 -21
  202. package/dist/services/code/dag-orchestrator.d.ts +30 -1
  203. package/dist/services/code/dag-orchestrator.js +106 -1
  204. package/dist/services/code/status-line-renderer.d.ts +2 -0
  205. package/dist/services/code/status-line-renderer.js +21 -3
  206. package/dist/services/code-review/ocr-service.d.ts +1 -1
  207. package/dist/services/code-review/ocr-service.js +6 -3
  208. package/dist/services/compact-history/compact-history-service.d.ts +41 -0
  209. package/dist/services/compact-history/compact-history-service.js +62 -0
  210. package/dist/services/compact-statusline/compact-statusline-service.d.ts +16 -0
  211. package/dist/services/compact-statusline/compact-statusline-service.js +83 -0
  212. package/dist/services/config/config-service.d.ts +1 -12
  213. package/dist/services/config/config-service.js +3 -72
  214. package/dist/services/config/config-types.d.ts +2 -4
  215. package/dist/services/config/config-types.js +1 -1
  216. package/dist/services/container/container-lease.d.ts +67 -0
  217. package/dist/services/container/container-lease.js +135 -0
  218. package/dist/services/context/auto-compact-dispatcher.d.ts +40 -5
  219. package/dist/services/context/auto-compact-dispatcher.js +32 -102
  220. package/dist/services/context/auto-compact-reader.js +1 -1
  221. package/dist/services/context/auto-compact-types.d.ts +27 -2
  222. package/dist/services/context/auto-compact-types.js +8 -1
  223. package/dist/services/context/build-dispatch-system-prompt.d.ts +75 -0
  224. package/dist/services/context/build-dispatch-system-prompt.js +117 -0
  225. package/dist/services/context/collector.d.ts +2 -2
  226. package/dist/services/context/context-builder.d.ts +4 -4
  227. package/dist/services/context/context-schema.d.ts +48 -48
  228. package/dist/services/context/ide-detect.d.ts +4 -0
  229. package/dist/services/context/ide-detect.js +24 -0
  230. package/dist/services/context/main-session-monitor.d.ts +1 -4
  231. package/dist/services/context/main-session-monitor.js +1 -24
  232. package/dist/services/context/memory-index-reader.d.ts +8 -0
  233. package/dist/services/context/memory-index-reader.js +57 -0
  234. package/dist/services/context/memory-lru-cache.d.ts +12 -0
  235. package/dist/services/context/memory-lru-cache.js +60 -0
  236. package/dist/services/context/memory-preflight-config.d.ts +19 -0
  237. package/dist/services/context/memory-preflight-config.js +25 -0
  238. package/dist/services/context/memory-preflight-service.d.ts +19 -0
  239. package/dist/services/context/memory-preflight-service.js +120 -0
  240. package/dist/services/context/spillover-store.d.ts +23 -0
  241. package/dist/services/context/spillover-store.js +155 -0
  242. package/dist/services/context/spillover-types.d.ts +18 -0
  243. package/dist/services/context/spillover-types.js +1 -0
  244. package/dist/services/crystallization/crystallization-service.d.ts +22 -5
  245. package/dist/services/crystallization/crystallization-service.js +8 -14
  246. package/dist/services/crystallization/crystallization-types.d.ts +6 -6
  247. package/dist/services/crystallization/evidence-brief-builder.d.ts +1 -1
  248. package/dist/services/crystallization/index.d.ts +13 -0
  249. package/dist/services/crystallization/index.js +13 -0
  250. package/dist/services/dashboard/project-dashboard-service.js +2 -2
  251. package/dist/services/dispatch/await-batch.d.ts +70 -0
  252. package/dist/services/dispatch/await-batch.js +272 -0
  253. package/dist/services/dispatch/batch-counter.js +1 -1
  254. package/dist/services/dispatch/dispatch-record-writer.d.ts +126 -4
  255. package/dist/services/dispatch/dispatch-record-writer.js +239 -9
  256. package/dist/services/dispatch/slice-dag.d.ts +46 -0
  257. package/dist/services/dispatch/slice-dag.js +6 -0
  258. package/dist/services/dispatch/stage-enum.d.ts +27 -0
  259. package/dist/services/dispatch/stage-enum.js +48 -0
  260. package/dist/services/dispatch/startup-timeout.d.ts +86 -0
  261. package/dist/services/dispatch/startup-timeout.js +97 -0
  262. package/dist/services/dispatch/sub-agent-dispatcher.js +40 -89
  263. package/dist/services/dispatch/test-tool-detection.d.ts +25 -1
  264. package/dist/services/dispatch/test-tool-detection.js +80 -0
  265. package/dist/services/doctor/doctor-service/checks/check-id-schema.d.ts +18 -0
  266. package/dist/services/doctor/doctor-service/checks/check-id-schema.js +64 -0
  267. package/dist/services/doctor/doctor-service/checks/codegraph-capability.d.ts +14 -0
  268. package/dist/services/doctor/doctor-service/checks/codegraph-capability.js +69 -0
  269. package/dist/services/doctor/doctor-service/checks/dist-source-version.d.ts +32 -0
  270. package/dist/services/doctor/doctor-service/checks/dist-source-version.js +110 -0
  271. package/dist/services/doctor/doctor-service/checks/gateguard-conflict.d.ts +24 -0
  272. package/dist/services/doctor/doctor-service/checks/gateguard-conflict.js +164 -0
  273. package/dist/services/doctor/doctor-service/checks/l3-memory-health.d.ts +16 -0
  274. package/dist/services/doctor/doctor-service/checks/l3-memory-health.js +57 -0
  275. package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.d.ts +19 -0
  276. package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.js +66 -0
  277. package/dist/services/doctor/doctor-service/checks/schema-validity.d.ts +10 -0
  278. package/dist/services/doctor/doctor-service/checks/schema-validity.js +33 -0
  279. package/dist/services/doctor/doctor-service/checks/skill-apply-note.d.ts +20 -0
  280. package/dist/services/doctor/doctor-service/checks/skill-apply-note.js +85 -0
  281. package/dist/services/doctor/doctor-service/checks/skill-existence.d.ts +13 -0
  282. package/dist/services/doctor/doctor-service/checks/skill-existence.js +30 -0
  283. package/dist/services/doctor/doctor-service/checks/skill-name-match.d.ts +10 -0
  284. package/dist/services/doctor/doctor-service/checks/skill-name-match.js +25 -0
  285. package/dist/services/doctor/doctor-service/checks/skill-parse.d.ts +10 -0
  286. package/dist/services/doctor/doctor-service/checks/skill-parse.js +19 -0
  287. package/dist/services/doctor/doctor-service/checks/skill-presence.d.ts +21 -0
  288. package/dist/services/doctor/doctor-service/checks/skill-presence.js +75 -0
  289. package/dist/services/doctor/doctor-service/checks/skill-runbook.d.ts +14 -0
  290. package/dist/services/doctor/doctor-service/checks/skill-runbook.js +47 -0
  291. package/dist/services/doctor/doctor-service/checks/statusline-install.d.ts +14 -0
  292. package/dist/services/doctor/doctor-service/checks/statusline-install.js +32 -0
  293. package/dist/services/doctor/doctor-service/checks/statusline-runtime.d.ts +13 -0
  294. package/dist/services/doctor/doctor-service/checks/statusline-runtime.js +30 -0
  295. package/dist/services/doctor/doctor-service/checks/user-config.d.ts +11 -0
  296. package/dist/services/doctor/doctor-service/checks/user-config.js +27 -0
  297. package/dist/services/doctor/doctor-service/checks/workspace-init.d.ts +16 -0
  298. package/dist/services/doctor/doctor-service/checks/workspace-init.js +34 -0
  299. package/dist/services/doctor/doctor-service/checks/workspace-layout.d.ts +33 -0
  300. package/dist/services/doctor/doctor-service/checks/workspace-layout.js +180 -0
  301. package/dist/services/doctor/doctor-service/index.d.ts +48 -0
  302. package/dist/services/doctor/doctor-service/index.js +254 -0
  303. package/dist/services/doctor/doctor-service/plugin-registry.d.ts +41 -0
  304. package/dist/services/doctor/doctor-service/plugin-registry.js +77 -0
  305. package/dist/services/doctor/doctor-service/report/final-summary.d.ts +20 -0
  306. package/dist/services/doctor/doctor-service/report/final-summary.js +29 -0
  307. package/dist/services/doctor/doctor-service/types.d.ts +245 -0
  308. package/dist/services/doctor/doctor-service/types.js +15 -0
  309. package/dist/services/doctor/doctor-service.d.ts +15 -146
  310. package/dist/services/doctor/doctor-service.js +22 -926
  311. package/dist/services/doctor/index.d.ts +22 -0
  312. package/dist/services/doctor/index.js +22 -0
  313. package/dist/services/env/shell-probe.d.ts +71 -0
  314. package/dist/services/env/shell-probe.js +161 -0
  315. package/dist/services/evolution/evolution-types.d.ts +39 -39
  316. package/dist/services/final-review/final-review-service.d.ts +11 -1
  317. package/dist/services/final-review/index.d.ts +2 -0
  318. package/dist/services/final-review/index.js +1 -0
  319. package/dist/services/hooks/auto-compact-hook-install.d.ts +4 -5
  320. package/dist/services/hooks/auto-compact-hook-install.js +4 -5
  321. package/dist/services/hooks/worktree-authorization-gate.d.ts +205 -0
  322. package/dist/services/hooks/worktree-authorization-gate.js +492 -0
  323. package/dist/services/ide/adapters/claude-code-adapter.js +2 -2
  324. package/dist/services/ide/adapters/qoder-adapter.d.ts +25 -0
  325. package/dist/services/ide/adapters/qoder-adapter.js +57 -0
  326. package/dist/services/ide/adapters/tongyi-lingma-adapter.d.ts +16 -0
  327. package/dist/services/ide/adapters/tongyi-lingma-adapter.js +45 -0
  328. package/dist/services/ide/ide-registry.js +6 -0
  329. package/dist/services/ide/ide-types.d.ts +1 -1
  330. package/dist/services/job/job-types.d.ts +56 -56
  331. package/dist/services/job-snapshot/index.d.ts +8 -0
  332. package/dist/services/job-snapshot/index.js +7 -0
  333. package/dist/services/{job/job-resource-snapshot.d.ts → job-snapshot/snapshot.d.ts} +1 -1
  334. package/dist/services/job-snapshot/types.d.ts +7 -0
  335. package/dist/services/job-snapshot/types.js +1 -0
  336. package/dist/services/karpathy-cost/karpathy-cost-check-service.d.ts +67 -0
  337. package/dist/services/karpathy-cost/karpathy-cost-check-service.js +108 -0
  338. package/dist/services/log/retention.d.ts +16 -0
  339. package/dist/services/log/retention.js +17 -0
  340. package/dist/services/loop/loop-bee-relation-types.d.ts +6 -6
  341. package/dist/services/loop/loop-release-types.d.ts +24 -24
  342. package/dist/services/loop/monotonic-runner.d.ts +17 -11
  343. package/dist/services/loop/monotonic-runner.js +118 -79
  344. package/dist/services/memory/project-memory-service/index/kind-dispatch.d.ts +8 -0
  345. package/dist/services/memory/project-memory-service/index/kind-dispatch.js +253 -0
  346. package/dist/services/memory/project-memory-service/index/ranking.d.ts +6 -0
  347. package/dist/services/memory/project-memory-service/index/ranking.js +171 -0
  348. package/dist/services/memory/project-memory-service/index/search.d.ts +34 -0
  349. package/dist/services/memory/project-memory-service/index/search.js +194 -0
  350. package/dist/services/memory/project-memory-service/index.d.ts +9 -0
  351. package/dist/services/memory/project-memory-service/index.js +24 -0
  352. package/dist/services/memory/project-memory-service/parsers/frontmatter.d.ts +9 -0
  353. package/dist/services/memory/project-memory-service/parsers/frontmatter.js +106 -0
  354. package/dist/services/memory/project-memory-service/parsers/markdown-pure.d.ts +7 -0
  355. package/dist/services/memory/project-memory-service/parsers/markdown-pure.js +96 -0
  356. package/dist/services/memory/project-memory-service/store/atomic-write.d.ts +5 -0
  357. package/dist/services/memory/project-memory-service/store/atomic-write.js +56 -0
  358. package/dist/services/memory/project-memory-service/store/paths.d.ts +8 -0
  359. package/dist/services/memory/project-memory-service/store/paths.js +116 -0
  360. package/dist/services/memory/project-memory-service/types.d.ts +128 -0
  361. package/dist/services/memory/project-memory-service/types.js +9 -0
  362. package/dist/services/memory/project-memory-service.d.ts +1 -167
  363. package/dist/services/memory/project-memory-service.js +19 -816
  364. package/dist/services/migrate-skill-name/schema.d.ts +2 -2
  365. package/dist/services/observability/aggregation.d.ts +27 -0
  366. package/dist/services/observability/aggregation.js +72 -0
  367. package/dist/services/observability/observability-service.d.ts +73 -7
  368. package/dist/services/observability/observability-service.js +87 -1
  369. package/dist/services/openspec/artifact-boundary.d.ts +138 -0
  370. package/dist/services/openspec/artifact-boundary.js +150 -0
  371. package/dist/services/openspec/coverage-evidence-reader.d.ts +191 -0
  372. package/dist/services/openspec/coverage-evidence-reader.js +386 -0
  373. package/dist/services/openspec/openspec-archive-service.d.ts +70 -0
  374. package/dist/services/openspec/openspec-archive-service.js +320 -8
  375. package/dist/services/openspec/openspec-bridge-service.js +1 -1
  376. package/dist/services/openspec/openspec-init-service.js +1 -1
  377. package/dist/services/openspec/openspec-propose-from-doctor-service.js +1 -1
  378. package/dist/services/openspec/openspec-render-service.js +7 -4
  379. package/dist/services/openspec/openspec-scan-service.js +1 -1
  380. package/dist/services/openspec/openspec-validate-service.js +5 -4
  381. package/dist/services/preferences/preferences-service.js +10 -0
  382. package/dist/services/preferences/preferences-types.d.ts +13 -0
  383. package/dist/services/preferences/preferences-types.js +11 -0
  384. package/dist/services/qa/screenshot-archive-service.d.ts +51 -0
  385. package/dist/services/qa/screenshot-archive-service.js +134 -0
  386. package/dist/services/rd/rd-service.js +10 -11
  387. package/dist/services/rd/types.d.ts +32 -32
  388. package/dist/services/rd-swarm/rd-swarm-service.d.ts +60 -0
  389. package/dist/services/rd-swarm/rd-swarm-service.js +93 -0
  390. package/dist/services/recommendations/capability-seed-items.js +0 -1
  391. package/dist/services/recommendations/capability-seed-mappings.js +0 -1
  392. package/dist/services/recommendations/capability-seed-sources.js +0 -1
  393. package/dist/services/release/version-precheck-service.d.ts +51 -0
  394. package/dist/services/release/version-precheck-service.js +257 -0
  395. package/dist/services/scan/acceptance-coverage-service.js +1 -1
  396. package/dist/services/scan/archetype-service.js +1 -1
  397. package/dist/services/scan/existing-system-service.js +1 -1
  398. package/dist/services/scan/libraries-service.js +1 -1
  399. package/dist/services/sediment/json-schema.d.ts +40 -40
  400. package/dist/services/share/bundle-types.d.ts +12 -12
  401. package/dist/services/share/run-state-contract.d.ts +2 -2
  402. package/dist/services/skill/skill-search-service.d.ts +12 -7
  403. package/dist/services/skill/skill-search-service.js +8 -4
  404. package/dist/services/skillhub/migrations/001-initial.sql +71 -0
  405. package/dist/services/skillhub/migrations/002-loop-release.sql +32 -0
  406. package/dist/services/skillhub/migrations/003-loop-bee-relation.sql +35 -0
  407. package/dist/services/skillhub/migrations/004-loop-bee-extension.sql +65 -0
  408. package/dist/services/skillhub/migrations/005-evolution-evaluation.sql +81 -0
  409. package/dist/services/skillhub/migrations/006-crystallization-event.sql +92 -0
  410. package/dist/services/skills/hooks-settings-service.d.ts +75 -0
  411. package/dist/services/skills/hooks-settings-service.js +230 -2
  412. package/dist/services/skills/skill-registry.d.ts +1 -0
  413. package/dist/services/skills/skill-registry.js +6 -4
  414. package/dist/services/skills/skill-runbook-service.js +1 -1
  415. package/dist/services/slice/slice-check-service.js +1 -1
  416. package/dist/services/standards/main-session-monitor-ban-hook.d.ts +14 -0
  417. package/dist/services/standards/main-session-monitor-ban-hook.js +19 -0
  418. package/dist/services/standards/missing-standards-detector.d.ts +25 -0
  419. package/dist/services/standards/missing-standards-detector.js +43 -12
  420. package/dist/services/tech/tech-change-id-service.d.ts +82 -0
  421. package/dist/services/tech/tech-change-id-service.js +117 -0
  422. package/dist/services/understand/understand-hybrid-service.js +1 -1
  423. package/dist/services/understand/understand-scan-service.js +2 -2
  424. package/dist/services/upgrade/upgrade-service.js +4 -3
  425. package/dist/services/verdict/envelopes.d.ts +2 -2
  426. package/dist/services/verdict/envelopes.js +2 -2
  427. package/dist/services/verdict/verdict-aggregator.d.ts +3 -3
  428. package/dist/services/vm/vm-lease.d.ts +40 -0
  429. package/dist/services/vm/vm-lease.js +137 -0
  430. package/dist/services/workflow/autonomous-resume-writer.js +1 -1
  431. package/dist/services/workflow/pipeline-verify-gate-support.d.ts +47 -0
  432. package/dist/services/workflow/pipeline-verify-gate-support.js +194 -0
  433. package/dist/services/workflow/pipeline-verify-service.d.ts +17 -81
  434. package/dist/services/workflow/pipeline-verify-service.js +42 -218
  435. package/dist/services/workflow/pipeline-verify-types.d.ts +90 -0
  436. package/dist/services/workflow/pipeline-verify-types.js +10 -0
  437. package/dist/services/workflow/plan-reader.d.ts +1 -1
  438. package/dist/services/workflow/plan-reader.js +1 -1
  439. package/dist/services/workflow/plan-refresher.d.ts +1 -1
  440. package/dist/services/workflow/plan-refresher.js +1 -1
  441. package/dist/services/workflow/plan-trigger-detector.d.ts +1 -1
  442. package/dist/services/workflow/plan-trigger-detector.js +1 -1
  443. package/dist/services/workflow/workflow-autonomous-resume-helpers.d.ts +1 -1
  444. package/dist/services/workflow/workflow-autonomous-service.d.ts +2 -98
  445. package/dist/services/workflow/workflow-autonomous-types.d.ts +114 -0
  446. package/dist/services/workflow/workflow-autonomous-types.js +21 -0
  447. package/dist/services/workflow/workflow-router-service.js +18 -16
  448. package/dist/services/workflow/workflow-spec-lint.d.ts +13 -0
  449. package/dist/services/workflow/workflow-spec-lint.js +75 -0
  450. package/dist/services/workflow/workflow-spec-types.d.ts +100 -0
  451. package/dist/services/workflow/workflow-spec-types.js +13 -0
  452. package/dist/services/workflow/workflow-spec-yaml.d.ts +17 -0
  453. package/dist/services/workflow/workflow-spec-yaml.js +88 -0
  454. package/dist/services/workflow/workflow-spec.d.ts +11 -91
  455. package/dist/services/workflow/workflow-spec.js +16 -145
  456. package/dist/services/workspace/migrate-service.js +1 -1
  457. package/dist/services/workspace/workspace-service.js +1 -1
  458. package/dist/services/worktree/worktree-lease.d.ts +170 -0
  459. package/dist/services/worktree/worktree-lease.js +242 -0
  460. package/package.json +91 -51
  461. package/scripts/clean-dist.mjs +31 -1
  462. package/scripts/copy-templates.mjs +16 -0
  463. package/scripts/sync-version.mjs +53 -2
  464. package/skills/bee/peaks-perf-audit/SKILL.md +17 -0
  465. package/skills/bee/peaks-prd/SKILL.md +16 -0
  466. package/skills/bee/peaks-prd/test-prompts.json +12 -0
  467. package/skills/bee/peaks-qa/SKILL.md +16 -1
  468. package/skills/bee/peaks-qa/references/qa-sub-agent-dispatch.md +22 -0
  469. package/skills/bee/peaks-qa/test-prompts.json +12 -0
  470. package/skills/bee/peaks-rd/SKILL.md +29 -20
  471. package/skills/bee/peaks-rd/references/rd-sub-agent-dispatch.md +39 -0
  472. package/skills/bee/peaks-rd/test-prompts.json +12 -0
  473. package/skills/bee/peaks-reviewer/SKILL.md +17 -0
  474. package/skills/bee/peaks-sc/SKILL.md +16 -0
  475. package/skills/bee/peaks-security-audit/SKILL.md +17 -0
  476. package/skills/bee/peaks-txt/SKILL.md +16 -0
  477. package/skills/bee/peaks-txt/test-prompts.json +12 -0
  478. package/skills/bee/peaks-ui/SKILL.md +49 -43
  479. package/skills/bee/peaks-ui/test-prompts.json +12 -0
  480. package/skills/peaks-code/SKILL.md +54 -109
  481. package/skills/peaks-code/references/boundaries.md +15 -1
  482. package/skills/peaks-code/references/cron-scheduler-deployment.md +313 -0
  483. package/skills/peaks-code/references/external-skill-invocation.md +27 -0
  484. package/skills/peaks-code/references/lease-dashboard.html +224 -0
  485. package/skills/peaks-code/references/lease-stats.sample.json +26 -0
  486. package/skills/peaks-code/references/peaks-worktree-l2-enforcers.md +208 -0
  487. package/skills/peaks-code/references/periodic-checkpoint.md +4 -4
  488. package/skills/peaks-code/references/runbook.md +63 -1
  489. package/skills/peaks-code/references/startup-sequence.md +110 -0
  490. package/skills/peaks-code/references/step-0-8-gate.md +1 -1
  491. package/skills/peaks-code/references/sub-agent-dispatch.md +92 -1
  492. package/skills/peaks-code/references/worktree-governance.md +123 -0
  493. package/skills/peaks-code/test-prompts.json +12 -0
  494. package/skills/peaks-doctor/SKILL.md +1 -1
  495. package/skills/peaks-ide/SKILL.md +1 -1
  496. package/skills/peaks-solo/SKILL.md +21 -0
  497. package/skills/peaks-solo/references/triage-decision-table.md +6 -0
  498. package/skills/peaks-sop/test-prompts.json +17 -0
  499. package/dist/cli/commands/agent-commands.d.ts +0 -20
  500. package/dist/cli/commands/agent-commands.js +0 -48
  501. package/dist/cli/commands/classify-classify-commands.d.ts +0 -19
  502. package/dist/cli/commands/classify-classify-commands.js +0 -151
  503. package/dist/cli/commands/contract-commands.d.ts +0 -26
  504. package/dist/cli/commands/contract-commands.js +0 -114
  505. package/dist/cli/commands/session-auto-compact-hook-command.d.ts +0 -37
  506. package/dist/cli/commands/session-auto-compact-hook-command.js +0 -127
  507. package/dist/cli/commands/worker-commands.d.ts +0 -3
  508. package/dist/cli/commands/worker-commands.js +0 -52
  509. package/dist/services/agent/ecc-agent-service.d.ts +0 -47
  510. package/dist/services/agent/ecc-agent-service.js +0 -143
  511. package/dist/services/config/provider-service.d.ts +0 -48
  512. package/dist/services/config/provider-service.js +0 -143
  513. package/dist/services/context/dispatch-context-guard.d.ts +0 -27
  514. package/dist/services/context/dispatch-context-guard.js +0 -192
  515. package/dist/services/context/shared-channel.d.ts +0 -93
  516. package/dist/services/context/shared-channel.js +0 -308
  517. package/dist/services/filesystem/file-lock.d.ts +0 -11
  518. package/dist/services/filesystem/file-lock.js +0 -152
  519. package/dist/services/mut/assert-scanner.d.ts +0 -6
  520. package/dist/services/mut/assert-scanner.js +0 -116
  521. package/dist/services/mut/index.d.ts +0 -8
  522. package/dist/services/mut/index.js +0 -7
  523. package/dist/services/mut/mut-runner.d.ts +0 -39
  524. package/dist/services/mut/mut-runner.js +0 -29
  525. package/dist/services/mut/production-stryker.d.ts +0 -2
  526. package/dist/services/mut/production-stryker.js +0 -75
  527. package/dist/services/mut/report-builder.d.ts +0 -11
  528. package/dist/services/mut/report-builder.js +0 -110
  529. package/dist/services/mut/report-loader.d.ts +0 -31
  530. package/dist/services/mut/report-loader.js +0 -78
  531. package/dist/services/mut/thresholds.d.ts +0 -21
  532. package/dist/services/mut/thresholds.js +0 -22
  533. package/dist/services/mut/types.d.ts +0 -336
  534. package/dist/services/mut/types.js +0 -59
  535. package/dist/services/providers/minimax-provider-service.d.ts +0 -24
  536. package/dist/services/providers/minimax-provider-service.js +0 -143
  537. package/dist/services/providers/minimax-worker-service.d.ts +0 -21
  538. package/dist/services/providers/minimax-worker-service.js +0 -83
  539. package/dist/shared/fs.d.ts +0 -4
  540. package/dist/shared/fs.js +0 -26
  541. package/dist/shared/paths.d.ts +0 -6
  542. package/dist/shared/paths.js +0 -50
  543. package/dist/shared/result.d.ts +0 -22
  544. package/dist/shared/result.js +0 -46
  545. package/dist/shared/version.d.ts +0 -1
  546. package/dist/shared/version.js +0 -1
  547. package/examples/video-demo/README.md +0 -142
  548. package/examples/video-demo/out/en-closing-960.png +0 -0
  549. package/examples/video-demo/out/zh-closing-960.png +0 -0
  550. package/examples/video-demo/preview/peaks-loop-demo-en.mp4 +0 -0
  551. package/examples/video-demo/preview/peaks-loop-demo.mp4 +0 -0
  552. /package/dist/services/{job/job-resource-snapshot.js → job-snapshot/snapshot.js} +0 -0
@@ -0,0 +1,652 @@
1
+ /**
2
+ * `peaks worktree auth <grant|revoke|status>` — slice 2026-07-27-worktree-user-auth.
3
+ *
4
+ * Records (or revokes / inspects) a current-task user authorization for
5
+ * a worktree-mutating operation. The PreToolUse gate
6
+ * (`src/services/hooks/worktree-authorization-gate.ts`) reads the
7
+ * resulting file before allowing `git worktree ...`, `git stash ...`,
8
+ * or `Agent(isolation: worktree)` tool calls.
9
+ *
10
+ * Sub-commands:
11
+ * - grant : append a one-shot (or multi-use) authorization
12
+ * - revoke : remove all unconsumed grants
13
+ * - status : list current grants + fingerprint
14
+ *
15
+ * Default TTL: 5 min, single-use. Multi-use is opt-in via --multi.
16
+ * Default operation: `git-worktree` (the most common ask). Specify
17
+ * `--operation agent-isolation-worktree` or `--operation git-stash-mutating`
18
+ * when authorizing a different shape.
19
+ *
20
+ * This command is invoked by the LLM after the user has explicitly
21
+ * authorized the operation in the current task. It must NOT be invoked
22
+ * autonomously without a user prompt that names the operation. The
23
+ * command itself does not enforce user confirmation — that is the
24
+ * peaks-code orchestrator's responsibility (see
25
+ * `skills/peaks-code/SKILL.md` "Worktree authorization" red line).
26
+ */
27
+ import { fail, ok } from 'peaks-loop-shared/result';
28
+ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
29
+ import { findProjectRoot } from '../../services/config/config-safety.js';
30
+ import { getCurrentSessionId } from '../../services/skills/skill-presence-service.js';
31
+ import { clearAllGrants, readAuthorization, writeAuthorization, } from '../../services/hooks/worktree-authorization-gate.js';
32
+ import { atomicWriteJson } from '../../services/ide/shared/atomic-json.js';
33
+ import { emitLeaseEvent } from '../../services/observability/observability-service.js';
34
+ import { deserializeLease, finalizeLease, generateLeaseId, isLeaseActive, isLeaseGcEligible, leaseFilePath, leaseStoreDir, listLeasesSync, markExpired, markGc, markReleased, renewLease, ttlForRole, worktreePath, } from '../../services/worktree/worktree-lease.js';
35
+ import { execSync } from 'node:child_process';
36
+ import { existsSync, readdirSync, readFileSync as readFileSyncNode, statSync } from 'node:fs';
37
+ const DEFAULT_TTL_MS = 5 * 60 * 1_000;
38
+ const ALLOWED_OPERATIONS = [
39
+ 'git-worktree',
40
+ 'agent-isolation-worktree',
41
+ 'git-stash-mutating',
42
+ 'git-worktree-other'
43
+ ];
44
+ function parseOperation(raw) {
45
+ return ALLOWED_OPERATIONS.includes(raw) ? raw : null;
46
+ }
47
+ function resolveSessionId(options, projectRoot) {
48
+ if (typeof options.session === 'string' && options.session.length > 0)
49
+ return options.session;
50
+ // Reuse the same precedence as the rest of peaks: explicit --session > PEAKS_SESSION_ID > active session.json
51
+ return process.env.PEAKS_SESSION_ID ?? getCurrentSessionId(projectRoot) ?? 'unknown-sid';
52
+ }
53
+ function resolveProjectRoot(options) {
54
+ return options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
55
+ }
56
+ export function registerWorktreeAuthCommand(program, io) {
57
+ const auth = program
58
+ .command('worktree')
59
+ .description('worktree authorization gate (slice 2026-07-27-worktree-user-auth)')
60
+ .addHelpText('after', 'Examples:\n' +
61
+ ' peaks worktree auth grant --operation git-worktree --reason "rd sub-agent for rid-006"\n' +
62
+ ' peaks worktree auth grant --operation agent-isolation-worktree --reason "explore worktree dispatch demo" --multi\n' +
63
+ ' peaks worktree auth revoke\n' +
64
+ ' peaks worktree auth status\n\n' +
65
+ 'The grant is current-task scoped: the LLM must invoke grant after the user has explicitly ' +
66
+ 'asked for the operation. The PreToolUse gate fail-closes on missing or expired grants.');
67
+ const auth_ = auth.command('auth').description('Manage worktree authorization grants (granted by the LLM after explicit user opt-in).');
68
+ addJsonOption(auth_
69
+ .command('grant')
70
+ .description('Append a single grant to the current session\'s worktree authorization file.')
71
+ .requiredOption('--operation <op>', `operation type: ${ALLOWED_OPERATIONS.join(' | ')}`)
72
+ .requiredOption('--reason <text>', 'why the user authorized this operation (logged for audit)')
73
+ .option('--ttl <ms>', `time-to-live in ms (default ${DEFAULT_TTL_MS} = 5 min)`)
74
+ .option('--multi', 'multi-use grant (default: single-use, consumed on first match)')
75
+ .option('--request-id <rid>', 'scope the grant to a specific peaks request id (defense in depth)')
76
+ .option('--no-request-id', 'explicitly mark this grant as NOT scoped to any rid (default behavior)')
77
+ .option('--prompt-hash <hex>', '16-hex prefix of the user prompt at grant time (optional, traceability)')
78
+ .option('--session <sid>', 'override session id (default: read .peaks/_runtime/session.json)')
79
+ .option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
80
+ try {
81
+ const op = parseOperation(options.operation);
82
+ if (op === null) {
83
+ printResult(io, fail('worktree.auth.grant', 'INVALID_OPERATION', `--operation must be one of: ${ALLOWED_OPERATIONS.join(' | ')}`, { operation: options.operation }, ['Re-run with a valid --operation value.']), options.json);
84
+ process.exitCode = 1;
85
+ return;
86
+ }
87
+ if (options.reason.trim().length === 0) {
88
+ printResult(io, fail('worktree.auth.grant', 'EMPTY_REASON', '--reason must not be empty', { reason: options.reason }, ['Provide a non-empty --reason for the audit log.']), options.json);
89
+ process.exitCode = 1;
90
+ return;
91
+ }
92
+ const ttlMs = options.ttl === undefined ? DEFAULT_TTL_MS : Number.parseInt(options.ttl, 10);
93
+ if (!Number.isInteger(ttlMs) || ttlMs <= 0) {
94
+ printResult(io, fail('worktree.auth.grant', 'INVALID_TTL', '--ttl must be a positive integer (ms)', { ttl: options.ttl }, ['Re-run with --ttl 300000 for a 5-minute window.']), options.json);
95
+ process.exitCode = 1;
96
+ return;
97
+ }
98
+ const projectRoot = resolveProjectRoot(options);
99
+ const sessionId = resolveSessionId(options, projectRoot);
100
+ const issuedAt = new Date();
101
+ const expiresAt = new Date(issuedAt.getTime() + ttlMs);
102
+ const consume = options.multi !== true;
103
+ const requestId = options.noRequestId
104
+ ? null
105
+ : (typeof options.requestId === 'string' && options.requestId.length > 0
106
+ ? options.requestId
107
+ : null);
108
+ const promptHash = typeof options.promptHash === 'string' && /^[a-f0-9]{1,16}$/.test(options.promptHash)
109
+ ? options.promptHash
110
+ : null;
111
+ const authorization = {
112
+ operation: op,
113
+ reason: options.reason,
114
+ promptHash,
115
+ requestId,
116
+ issuedAt: issuedAt.toISOString(),
117
+ expiresAt: expiresAt.toISOString(),
118
+ consume,
119
+ consumed: false
120
+ };
121
+ writeAuthorization(projectRoot, sessionId, authorization);
122
+ printResult(io, ok('worktree.auth.grant', {
123
+ sessionId,
124
+ projectRoot,
125
+ authorization,
126
+ ttlMs,
127
+ file: '.peaks/_runtime/' + sessionId + '/worktree-auth.json'
128
+ }, [], [
129
+ 'The PreToolUse gate now permits the operation in this session until the grant expires or is consumed.',
130
+ 'Run `peaks worktree auth status` to inspect, or `peaks worktree auth revoke` to clear.'
131
+ ]), options.json);
132
+ }
133
+ catch (error) {
134
+ printResult(io, fail('worktree.auth.grant', 'GRANT_FAILED', getErrorMessage(error), { operation: options.operation }, ['Re-run after fixing the failure (see cause in the error message).']), options.json);
135
+ process.exitCode = 1;
136
+ }
137
+ });
138
+ addJsonOption(auth_
139
+ .command('revoke')
140
+ .description('Remove all unconsumed grants for the current session.')
141
+ .option('--session <sid>', 'override session id (default: read .peaks/_runtime/session.json)')
142
+ .option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
143
+ try {
144
+ const projectRoot = resolveProjectRoot(options);
145
+ const sessionId = resolveSessionId(options, projectRoot);
146
+ const result = clearAllGrants(projectRoot, sessionId);
147
+ printResult(io, ok('worktree.auth.revoke', { sessionId, projectRoot, ...result }, [], [
148
+ result.removed > 0
149
+ ? `Cleared ${result.removed} grant(s). The PreToolUse gate now fail-closes again.`
150
+ : 'No grants to clear. The gate is already fail-closed.'
151
+ ]), options.json);
152
+ }
153
+ catch (error) {
154
+ printResult(io, fail('worktree.auth.revoke', 'REVOKE_FAILED', getErrorMessage(error), {}, ['Re-run after fixing the failure (see cause in the error message).']), options.json);
155
+ process.exitCode = 1;
156
+ }
157
+ });
158
+ addJsonOption(auth_
159
+ .command('status')
160
+ .description('Inspect the current session\'s worktree-authorization file (granted operations + expiry).')
161
+ .option('--session <sid>', 'override session id (default: read .peaks/_runtime/session.json)')
162
+ .option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
163
+ try {
164
+ const projectRoot = resolveProjectRoot(options);
165
+ const sessionId = resolveSessionId(options, projectRoot);
166
+ let file;
167
+ try {
168
+ file = readAuthorization(projectRoot, sessionId);
169
+ }
170
+ catch (error) {
171
+ printResult(io, fail('worktree.auth.status', 'FILE_INVALID', getErrorMessage(error), { sessionId }, [
172
+ 'Delete the malformed worktree-auth.json and re-grant.',
173
+ 'For security, the gate never fails open on a malformed grant file.'
174
+ ]), options.json);
175
+ process.exitCode = 1;
176
+ return;
177
+ }
178
+ if (file === null) {
179
+ printResult(io, ok('worktree.auth.status', { sessionId, projectRoot, grants: [], file: null }, [], ['No grants on file. The PreToolUse gate will fail-close on worktree-mutating tool calls.']), options.json);
180
+ return;
181
+ }
182
+ const now = Date.now();
183
+ const live = file.grants.map((g) => ({
184
+ ...g,
185
+ expired: Date.parse(g.expiresAt) <= now
186
+ }));
187
+ printResult(io, ok('worktree.auth.status', { sessionId, projectRoot, file: '.peaks/_runtime/' + sessionId + '/worktree-auth.json', grants: live }, [], [
188
+ `${file.grants.length} grant(s) recorded. ${live.filter((g) => !g.expired).length} still valid.`
189
+ ]), options.json);
190
+ }
191
+ catch (error) {
192
+ printResult(io, fail('worktree.auth.status', 'STATUS_FAILED', getErrorMessage(error), {}, ['Re-run after fixing the failure (see cause in the error message).']), options.json);
193
+ process.exitCode = 1;
194
+ }
195
+ });
196
+ function resolveTtlMs(raw, role) {
197
+ if (typeof raw !== 'string' || raw.length === 0)
198
+ return ttlForRole(role);
199
+ const parsed = Number(raw);
200
+ if (!Number.isFinite(parsed) || parsed <= 0)
201
+ return ttlForRole(role);
202
+ return parsed;
203
+ }
204
+ function deriveBranch(rid) {
205
+ // Branch names must be safe for git ref-format. Strip leading
206
+ // `rid-` if present and replace any non-safe chars with `-`.
207
+ return rid.replace(/[^A-Za-z0-9._/-]/g, '-').slice(0, 80);
208
+ }
209
+ addJsonOption(auth
210
+ .command('spawn')
211
+ .description(`Spawn a worktree under .peaks/_runtime/<sid>/worktrees/<leaseId>/ with a managed lease. ` +
212
+ `The lease is the source of truth for the L2 hook gate (Part 2 of this slice); ` +
213
+ `until the hook integration ships, sub-agents may still invoke raw \`git worktree add\` ` +
214
+ `with a current \`peaks worktree auth grant\` token. Default TTL is role-aware ` +
215
+ `(rd=30m / qa=15m / ui=1h); pass --ttl <ms> to override.`)
216
+ .requiredOption('--rid <rid>', 'peaks request id the lease is associated with')
217
+ .requiredOption('--role <role>', 'sub-agent role (rd | qa | ui | sc | prd | general-purpose)')
218
+ .requiredOption('--purpose <text>', 'why this worktree was spawned (audit log)')
219
+ .option('--ttl <ms>', `time-to-live in ms (default role-aware; override with positive number)`)
220
+ .option('--branch <name>', 'git branch name (default: derived from rid)')
221
+ .option('--session <sid>', 'override session id')
222
+ .option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
223
+ const projectRoot = resolveProjectRoot(options);
224
+ const sessionId = resolveSessionId(options, projectRoot);
225
+ try {
226
+ const leaseId = generateLeaseId();
227
+ const now = Date.now();
228
+ const ttlMs = resolveTtlMs(options.ttl, options.role);
229
+ const branch = options.branch ?? deriveBranch(options.rid);
230
+ const wtPath = worktreePath(joinPathSession(projectRoot, sessionId), leaseId);
231
+ const lease = finalizeLease({
232
+ leaseId,
233
+ rid: options.rid,
234
+ role: options.role,
235
+ path: wtPath,
236
+ branch,
237
+ createdAt: now,
238
+ expiresAt: now + ttlMs,
239
+ purpose: options.purpose
240
+ });
241
+ // Run `git worktree add` from the project root. The PreToolUse hook
242
+ // (Part 2 of this slice) will consult the lease file we just wrote
243
+ // and authorize this very `git worktree add`; for Part 1 we still
244
+ // require a current `peaks worktree auth grant` token to remain
245
+ // consistent with the slice-027 hard gate contract.
246
+ execSync(`git worktree add "${wtPath}" -b "${branch}"`, { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8' });
247
+ atomicWriteJson(leaseFilePath(joinPathSession(projectRoot, sessionId), leaseId), lease);
248
+ // Part 4.A: emit spawn metric (fire-and-forget; never blocks).
249
+ emitLeaseEvent({
250
+ sessionId,
251
+ projectRoot,
252
+ kind: 'spawn',
253
+ leaseId,
254
+ rid: options.rid,
255
+ role: options.role
256
+ });
257
+ printResult(io, ok('worktree.spawn', {
258
+ lease,
259
+ sessionId,
260
+ projectRoot,
261
+ ttlMs,
262
+ nextActions: [
263
+ `Worktree path: ${wtPath}`,
264
+ `Branch: ${branch}`,
265
+ `Lease expires at: ${new Date(lease.expiresAt).toISOString()}`,
266
+ 'Run `peaks worktree release --lease-id <leaseId>` when done',
267
+ 'Hook integration (lease-aware gate) ships in Part 2'
268
+ ]
269
+ }, [], []), options.json);
270
+ }
271
+ catch (error) {
272
+ printResult(io, fail('worktree.spawn', 'SPAWN_FAILED', getErrorMessage(error), { rid: options.rid, role: options.role, sessionId }, [
273
+ 'Verify `git worktree add` succeeded (output above).',
274
+ 'If the lease file was NOT written, retry; the lease directory is .peaks/_runtime/<sid>/worktree-leases/.',
275
+ 'For an existing branch, pass --branch <name> explicitly (the spawn refuses to overwrite an active branch).'
276
+ ]), options.json);
277
+ process.exitCode = 1;
278
+ }
279
+ });
280
+ addJsonOption(auth
281
+ .command('release')
282
+ .description('Transition a lease to released and run `git worktree remove`. Idempotent on already-released leases.')
283
+ .requiredOption('--lease-id <id>', 'lease id returned by `peaks worktree spawn`')
284
+ .option('--session <sid>', 'override session id')
285
+ .option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
286
+ const projectRoot = resolveProjectRoot(options);
287
+ const sessionId = resolveSessionId(options, projectRoot);
288
+ try {
289
+ const file = leaseFilePath(joinPathSession(projectRoot, sessionId), options.leaseId);
290
+ let lease;
291
+ try {
292
+ if (!existsSync(file)) {
293
+ printResult(io, fail('worktree.release', 'LEASE_NOT_FOUND', `no lease on disk at ${file}`, { leaseId: options.leaseId, file }, [
294
+ 'Run `peaks worktree list` to inspect active leases.',
295
+ 'For a never-spawned lease, this is a no-op — no further action needed.'
296
+ ]), options.json);
297
+ process.exitCode = 1;
298
+ return;
299
+ }
300
+ const raw = readFileSyncNode(file, 'utf8');
301
+ lease = deserializeLease(raw);
302
+ }
303
+ catch (error) {
304
+ printResult(io, fail('worktree.release', 'LEASE_FILE_INVALID', getErrorMessage(error), { leaseId: options.leaseId, file }, [
305
+ 'Delete the malformed lease file manually and re-issue spawn.',
306
+ 'For security, release never fails open on a malformed lease.'
307
+ ]), options.json);
308
+ process.exitCode = 1;
309
+ return;
310
+ }
311
+ if (lease.status === 'released') {
312
+ // Idempotent: already released, nothing to do.
313
+ printResult(io, ok('worktree.release', { lease, sessionId, projectRoot, alreadyReleased: true }, [], [`Lease ${lease.leaseId} already released; nothing to do.`]), options.json);
314
+ return;
315
+ }
316
+ // Run `git worktree remove` from the project root. If the path is
317
+ // missing on disk (e.g. manually pruned), this fails; we still
318
+ // update the lease state so the L2 hook (Part 2) treats it as
319
+ // released.
320
+ let gitWorktreeRemoveFailed = false;
321
+ try {
322
+ execSync(`git worktree remove --force "${lease.path}"`, { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8' });
323
+ }
324
+ catch {
325
+ gitWorktreeRemoveFailed = true;
326
+ }
327
+ const released = markReleased(lease);
328
+ atomicWriteJson(file, released);
329
+ // Part 4.A: emit release metric (manual path; auto-release is
330
+ // recorded in dispatch-record-writer's tryAutoReleaseLease).
331
+ emitLeaseEvent({
332
+ sessionId,
333
+ projectRoot,
334
+ kind: 'release',
335
+ leaseId: released.leaseId,
336
+ rid: released.rid,
337
+ role: released.role
338
+ });
339
+ printResult(io, ok('worktree.release', { lease: released, sessionId, projectRoot, gitWorktreeRemoveFailed }, gitWorktreeRemoveFailed ? ['git worktree remove failed (likely the path was already pruned); lease marked released.'] : [], [
340
+ `Lease ${lease.leaseId} marked released.`,
341
+ gitWorktreeRemoveFailed
342
+ ? 'Manual `git worktree prune` may be needed.'
343
+ : `Worktree ${lease.path} removed.`
344
+ ]), options.json);
345
+ }
346
+ catch (error) {
347
+ printResult(io, fail('worktree.release', 'RELEASE_FAILED', getErrorMessage(error), { leaseId: options.leaseId, sessionId }, [
348
+ 'Verify the lease id and re-run.',
349
+ 'If the lease was never spawned, no-op.'
350
+ ]), options.json);
351
+ process.exitCode = 1;
352
+ }
353
+ });
354
+ addJsonOption(auth
355
+ .command('renew')
356
+ .description('Extend an active lease\'s `expiresAt` and persist it. Idempotent for already-active leases. ' +
357
+ 'Default TTL uses `DEFAULT_TTL_BY_ROLE[<lease.role>]`; pass --ttl <ms> to override.')
358
+ .requiredOption('--lease-id <id>', 'lease id returned by `peaks worktree spawn`')
359
+ .option('--ttl <ms>', 'time-to-live in ms from now (default: role-default)')
360
+ .option('--session <sid>', 'override session id')
361
+ .option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
362
+ const projectRoot = resolveProjectRoot(options);
363
+ const sessionId = resolveSessionId(options, projectRoot);
364
+ try {
365
+ const file = leaseFilePath(joinPathSession(projectRoot, sessionId), options.leaseId);
366
+ if (!existsSync(file)) {
367
+ printResult(io, fail('worktree.renew', 'LEASE_NOT_FOUND', `no lease on disk at ${file}`, { leaseId: options.leaseId, file }, [
368
+ 'Run `peaks worktree list` to inspect active leases.',
369
+ 'For a never-spawned lease, this is a no-op.'
370
+ ]), options.json);
371
+ process.exitCode = 1;
372
+ return;
373
+ }
374
+ let lease;
375
+ try {
376
+ lease = deserializeLease(readFileSyncNode(file, 'utf8'));
377
+ }
378
+ catch (err) {
379
+ printResult(io, fail('worktree.renew', 'LEASE_FILE_INVALID', getErrorMessage(err), { leaseId: options.leaseId, file }, [
380
+ 'Delete the malformed lease file manually and re-spawn.',
381
+ 'For security, renew never fails open on a malformed lease.'
382
+ ]), options.json);
383
+ process.exitCode = 1;
384
+ return;
385
+ }
386
+ if (lease.status === 'released' || lease.status === 'gc') {
387
+ printResult(io, fail('worktree.renew', 'LEASE_NOT_RENEWABLE', `lease is in status '${lease.status}'; only active/expired leases may be renewed`, { leaseId: lease.leaseId, status: lease.status }, [
388
+ 'Released leases cannot be renewed. Run `peaks worktree spawn` to start a new lease.',
389
+ 'Gc-marked leases are terminal — re-spawn.'
390
+ ]), options.json);
391
+ process.exitCode = 1;
392
+ return;
393
+ }
394
+ const now = Date.now();
395
+ const ttlMs = options.ttl === undefined ? ttlForRole(lease.role) : Number.parseInt(options.ttl, 10);
396
+ if (!Number.isInteger(ttlMs) || ttlMs <= 0) {
397
+ printResult(io, fail('worktree.renew', 'INVALID_TTL', '--ttl must be a positive integer (ms)', { ttl: options.ttl }, [
398
+ 'Re-run with --ttl 1800000 (30 min) or omit to use role default.'
399
+ ]), options.json);
400
+ process.exitCode = 1;
401
+ return;
402
+ }
403
+ const renewed = renewLease(lease, now + ttlMs);
404
+ atomicWriteJson(file, renewed);
405
+ // Part 4.A: emit renew metric.
406
+ emitLeaseEvent({
407
+ sessionId,
408
+ projectRoot,
409
+ kind: 'renew',
410
+ leaseId: renewed.leaseId,
411
+ rid: renewed.rid,
412
+ role: renewed.role
413
+ });
414
+ printResult(io, ok('worktree.renew', { lease: renewed, sessionId, projectRoot, ttlMs, previousExpiresAt: lease.expiresAt }, [], [
415
+ `Lease ${renewed.leaseId} renewed; new expiresAt: ${new Date(renewed.expiresAt).toISOString()}.`,
416
+ `Branch ${renewed.branch} at ${renewed.path} remains intact (no ` +
417
+ '`git worktree` operation was performed — the worktree was always there).'
418
+ ]), options.json);
419
+ }
420
+ catch (err) {
421
+ printResult(io, fail('worktree.renew', 'RENEW_FAILED', getErrorMessage(err), { leaseId: options.leaseId, sessionId }, [
422
+ 'Re-run after fixing the failure (see cause in the error message).'
423
+ ]), options.json);
424
+ process.exitCode = 1;
425
+ }
426
+ });
427
+ addJsonOption(auth
428
+ .command('list')
429
+ .description('List every lease under the current session\'s lease store. ' +
430
+ 'Optionally filter by --status (active|released|expired|gc) and/or --expired-only. ' +
431
+ 'Leases past their expiresAt with status=active are still listed under "active" by default ' +
432
+ '(`isLeaseActive` returns false for them, but the on-disk status only flips to "expired" ' +
433
+ 'after `peaks worktree gc` runs).')
434
+ .option('--status <state>', 'filter by lease status: active | released | expired | gc')
435
+ .option('--expired-only', 'only show leases whose expiresAt is in the past')
436
+ .option('--session <sid>', 'override session id')
437
+ .option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
438
+ const projectRoot = resolveProjectRoot(options);
439
+ const sessionId = resolveSessionId(options, projectRoot);
440
+ try {
441
+ const storeDir = leaseStoreDir(joinPathSession(projectRoot, sessionId));
442
+ const result = listLeasesSync(storeDir, {
443
+ readdir: (p) => readdirSync(p),
444
+ readFile: (p) => readFileSyncNode(p, 'utf8'),
445
+ existsSync: (p) => existsSync(p)
446
+ });
447
+ if (result.kind === 'store-missing') {
448
+ printResult(io, ok('worktree.list', { sessionId, projectRoot, leases: [], errors: [], storeMissing: true }, [], [
449
+ `No lease store at ${storeDir}. Spawn a worktree first (\`peaks worktree spawn ...\`).`
450
+ ]), options.json);
451
+ return;
452
+ }
453
+ const now = Date.now();
454
+ const annotated = result.leases.map((l) => ({
455
+ ...l,
456
+ live: isLeaseActive(l, now),
457
+ elapsedMs: now - l.createdAt,
458
+ remainingMs: l.expiresAt - now
459
+ }));
460
+ let filtered = annotated;
461
+ if (options.status) {
462
+ filtered = filtered.filter((l) => l.status === options.status);
463
+ }
464
+ if (options.expiredOnly === true) {
465
+ filtered = filtered.filter((l) => !l.live);
466
+ }
467
+ // Sort by createdAt desc — most-recent first. Stable for diffs.
468
+ filtered = [...filtered].sort((a, b) => b.createdAt - a.createdAt);
469
+ printResult(io, ok('worktree.list', {
470
+ sessionId,
471
+ projectRoot,
472
+ storeDir,
473
+ totalOnDisk: result.leases.length,
474
+ returned: filtered.length,
475
+ errors: result.errors,
476
+ leases: filtered
477
+ }, result.errors.map((e) => `Malformed lease: ${e.file} (${e.error})`), [
478
+ `${filtered.length} lease(s) matched (${result.leases.length} on disk).`,
479
+ result.errors.length > 0 ? 'Some lease files were malformed — see errors[]; they were skipped.' : ''
480
+ ].filter(Boolean)), options.json);
481
+ }
482
+ catch (err) {
483
+ printResult(io, fail('worktree.list', 'LIST_FAILED', getErrorMessage(err), { sessionId }, [
484
+ 'Re-run after fixing the failure (see cause in the error message).'
485
+ ]), options.json);
486
+ process.exitCode = 1;
487
+ }
488
+ });
489
+ addJsonOption(auth
490
+ .command('gc')
491
+ .description('Sweep released/expired leases: remove their git worktree (if still attached), prune git\'s ' +
492
+ 'worktree references, and mark the lease as "gc". With --lease-id <id>, only that lease is ' +
493
+ 'considered. With --dry-run, report what would be gc\'d without mutating. Expired-active ' +
494
+ 'leases (status=active but past expiresAt) are eligible — they are first marked "expired" ' +
495
+ 'then their worktree is removed.')
496
+ .option('--lease-id <id>', 'only consider this specific lease')
497
+ .option('--dry-run', 'report what would be gc\'d without mutating')
498
+ .option('--session <sid>', 'override session id')
499
+ .option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
500
+ const projectRoot = resolveProjectRoot(options);
501
+ const sessionId = resolveSessionId(options, projectRoot);
502
+ try {
503
+ const storeDir = leaseStoreDir(joinPathSession(projectRoot, sessionId));
504
+ const result = listLeasesSync(storeDir, {
505
+ readdir: (p) => readdirSync(p),
506
+ readFile: (p) => readFileSyncNode(p, 'utf8'),
507
+ existsSync: (p) => existsSync(p)
508
+ });
509
+ if (result.kind === 'store-missing') {
510
+ printResult(io, ok('worktree.gc', { sessionId, projectRoot, swept: 0, storeMissing: true }, [], [
511
+ `No lease store at ${storeDir}; nothing to gc.`
512
+ ]), options.json);
513
+ return;
514
+ }
515
+ const now = Date.now();
516
+ const candidates = result.leases
517
+ .filter((l) => (options.leaseId ? l.leaseId === options.leaseId : true))
518
+ .filter((l) => isLeaseGcEligible(l, now));
519
+ const dryRun = options.dryRun === true;
520
+ const swept = [];
521
+ for (const lease of candidates) {
522
+ let prevStatus = lease.status;
523
+ let updated = lease;
524
+ // If the lease is still marked active but past expiresAt, transition to expired first.
525
+ if (lease.status === 'active' && lease.expiresAt <= now) {
526
+ updated = markExpired(lease);
527
+ prevStatus = 'active';
528
+ }
529
+ if (!dryRun) {
530
+ // `git worktree remove --force` is best-effort — if the path is
531
+ // already gone we still mark the lease gc.
532
+ let gitWorktreeRemoveFailed = false;
533
+ try {
534
+ execSync(`git worktree remove --force "${updated.path}"`, { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8' });
535
+ }
536
+ catch {
537
+ gitWorktreeRemoveFailed = true;
538
+ }
539
+ // `git worktree prune` clears any stale admin entries. Best-effort.
540
+ try {
541
+ execSync('git worktree prune', { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8' });
542
+ }
543
+ catch {
544
+ // ignore — prune is idempotent
545
+ }
546
+ const finalLease = markGc(updated);
547
+ atomicWriteJson(leaseFilePath(joinPathSession(projectRoot, sessionId), lease.leaseId), finalLease);
548
+ // Part 4.A: emit gc metric (per swept lease).
549
+ emitLeaseEvent({
550
+ sessionId,
551
+ projectRoot,
552
+ kind: 'gc',
553
+ leaseId: lease.leaseId,
554
+ rid: lease.rid,
555
+ role: lease.role
556
+ });
557
+ swept.push({ leaseId: lease.leaseId, path: lease.path, prevStatus, gitWorktreeRemoveFailed });
558
+ }
559
+ else {
560
+ swept.push({ leaseId: lease.leaseId, path: lease.path, prevStatus, gitWorktreeRemoveFailed: false });
561
+ }
562
+ }
563
+ printResult(io, ok('worktree.gc', { sessionId, projectRoot, dryRun, candidates: candidates.length, swept, errors: result.errors }, result.errors.map((e) => `Malformed lease: ${e.file} (${e.error})`), [
564
+ dryRun
565
+ ? `[dry-run] Would gc ${swept.length} lease(s); no filesystem changes were made.`
566
+ : `Gc'd ${swept.length} lease(s).`,
567
+ 'For per-lease detail, run `peaks worktree status --lease-id <id>`.'
568
+ ]), options.json);
569
+ }
570
+ catch (err) {
571
+ printResult(io, fail('worktree.gc', 'GC_FAILED', getErrorMessage(err), { sessionId }, [
572
+ 'Re-run after fixing the failure (see cause in the error message).'
573
+ ]), options.json);
574
+ process.exitCode = 1;
575
+ }
576
+ });
577
+ addJsonOption(auth
578
+ .command('lease-status')
579
+ .description('Show one lease in detail: full lease record + computed `live` flag (active AND not past ' +
580
+ 'expiry) + path/branch/path-exists-on-disk diagnostics. Use this when triaging why a ' +
581
+ 'sub-agent cannot write to a worktree.')
582
+ .requiredOption('--lease-id <id>', 'lease id to inspect')
583
+ .option('--session <sid>', 'override session id')
584
+ .option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
585
+ const projectRoot = resolveProjectRoot(options);
586
+ const sessionId = resolveSessionId(options, projectRoot);
587
+ try {
588
+ const file = leaseFilePath(joinPathSession(projectRoot, sessionId), options.leaseId);
589
+ if (!existsSync(file)) {
590
+ printResult(io, fail('worktree.lease-status', 'LEASE_NOT_FOUND', `no lease on disk at ${file}`, { leaseId: options.leaseId, file }, [
591
+ 'Run `peaks worktree list` to inspect available leases.'
592
+ ]), options.json);
593
+ process.exitCode = 1;
594
+ return;
595
+ }
596
+ let lease;
597
+ try {
598
+ lease = deserializeLease(readFileSyncNode(file, 'utf8'));
599
+ }
600
+ catch (err) {
601
+ printResult(io, fail('worktree.lease-status', 'LEASE_FILE_INVALID', getErrorMessage(err), { leaseId: options.leaseId, file }, [
602
+ 'Delete the malformed lease file manually and re-spawn.'
603
+ ]), options.json);
604
+ process.exitCode = 1;
605
+ return;
606
+ }
607
+ const now = Date.now();
608
+ const pathExists = existsSync(lease.path);
609
+ let pathIsDirectory = false;
610
+ if (pathExists) {
611
+ try {
612
+ pathIsDirectory = statSync(lease.path).isDirectory();
613
+ }
614
+ catch {
615
+ pathIsDirectory = false;
616
+ }
617
+ }
618
+ printResult(io, ok('worktree.lease-status', {
619
+ sessionId,
620
+ projectRoot,
621
+ file: '.peaks/_runtime/' + sessionId + '/worktree-leases/' + lease.leaseId + '.json',
622
+ lease,
623
+ live: isLeaseActive(lease, now),
624
+ diagnostics: {
625
+ now,
626
+ remainingMs: lease.expiresAt - now,
627
+ pathExists,
628
+ pathIsDirectory
629
+ }
630
+ }, [], [
631
+ `Lease ${lease.leaseId} is ${isLeaseActive(lease, now) ? 'LIVE' : 'NOT LIVE'} ` +
632
+ `(status=${lease.status}, remaining=${lease.expiresAt - now}ms).`,
633
+ `Worktree path ${lease.path} ${pathExists ? (pathIsDirectory ? 'exists (dir)' : 'exists (NOT a dir)') : 'MISSING'}.`
634
+ ]), options.json);
635
+ }
636
+ catch (err) {
637
+ printResult(io, fail('worktree.lease-status', 'STATUS_FAILED', getErrorMessage(err), { leaseId: options.leaseId, sessionId }, [
638
+ 'Re-run after fixing the failure (see cause in the error message).'
639
+ ]), options.json);
640
+ process.exitCode = 1;
641
+ }
642
+ });
643
+ }
644
+ /**
645
+ * Resolve the per-session runtime directory: `<projectRoot>/.peaks/_runtime/<sessionId>`.
646
+ * The runtime tree is gitignored; the spawn CLI writes the lease file
647
+ * under `<this>/worktree-leases/<leaseId>.json` and the worktree itself
648
+ * under `<this>/worktrees/<leaseId>/`.
649
+ */
650
+ function joinPathSession(projectRoot, sessionId) {
651
+ return `${projectRoot.replace(/[\\/]+$/, '')}/.peaks/_runtime/${sessionId}`;
652
+ }