peaks-loop 4.1.2 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/.claude-plugin/marketplace.json +136 -47
  2. package/CHANGELOG.md +56 -0
  3. package/README-en.md +1 -1
  4. package/README.md +1 -1
  5. package/contracts/readonly-argv-whitelist.json +211 -0
  6. package/dist/cli/commands/audit-artifact-runners.d.ts +16 -0
  7. package/dist/cli/commands/audit-artifact-runners.js +170 -0
  8. package/dist/cli/commands/audit-command-shared.d.ts +97 -0
  9. package/dist/cli/commands/audit-command-shared.js +60 -0
  10. package/dist/cli/commands/audit-commands.d.ts +7 -31
  11. package/dist/cli/commands/audit-commands.js +64 -473
  12. package/dist/cli/commands/audit-goal-runners.d.ts +17 -0
  13. package/dist/cli/commands/audit-goal-runners.js +114 -0
  14. package/dist/cli/commands/audit-red-lines-runners.d.ts +11 -0
  15. package/dist/cli/commands/audit-red-lines-runners.js +43 -0
  16. package/dist/cli/commands/audit-refusal-data.d.ts +23 -0
  17. package/dist/cli/commands/audit-refusal-data.js +69 -0
  18. package/dist/cli/commands/audit-static-runners.d.ts +13 -0
  19. package/dist/cli/commands/audit-static-runners.js +139 -0
  20. package/dist/cli/commands/await-runners.d.ts +11 -0
  21. package/dist/cli/commands/await-runners.js +148 -0
  22. package/dist/cli/commands/code-auto-compact-command.d.ts +3 -0
  23. package/dist/cli/commands/code-auto-compact-command.js +124 -0
  24. package/dist/cli/commands/code-auto-compact-envelope.d.ts +22 -0
  25. package/dist/cli/commands/code-auto-compact-envelope.js +39 -0
  26. package/dist/cli/commands/code-context-audit-command.d.ts +3 -0
  27. package/dist/cli/commands/code-context-audit-command.js +68 -0
  28. package/dist/cli/commands/code-context-now-command.d.ts +3 -0
  29. package/dist/cli/commands/code-context-now-command.js +124 -0
  30. package/dist/cli/commands/code-context-now-envelope.d.ts +66 -0
  31. package/dist/cli/commands/code-context-now-envelope.js +136 -0
  32. package/dist/cli/commands/code-context-now-mapping.d.ts +27 -0
  33. package/dist/cli/commands/code-context-now-mapping.js +43 -0
  34. package/dist/cli/commands/code-emit-handoff-command.d.ts +3 -0
  35. package/dist/cli/commands/code-emit-handoff-command.js +99 -0
  36. package/dist/cli/commands/code-gate-step-08-command.d.ts +3 -0
  37. package/dist/cli/commands/code-gate-step-08-command.js +139 -0
  38. package/dist/cli/commands/code-post-compact-detect-command.d.ts +3 -0
  39. package/dist/cli/commands/code-post-compact-detect-command.js +50 -0
  40. package/dist/cli/commands/code-runtime-commands.d.ts +9 -43
  41. package/dist/cli/commands/code-runtime-commands.js +20 -672
  42. package/dist/cli/commands/code-runtime-session.d.ts +1 -0
  43. package/dist/cli/commands/code-runtime-session.js +15 -0
  44. package/dist/cli/commands/compact-command.d.ts +6 -2
  45. package/dist/cli/commands/compact-command.js +12 -559
  46. package/dist/cli/commands/compact-dry-run-command.d.ts +3 -0
  47. package/dist/cli/commands/compact-dry-run-command.js +87 -0
  48. package/dist/cli/commands/compact-force-command.d.ts +3 -0
  49. package/dist/cli/commands/compact-force-command.js +98 -0
  50. package/dist/cli/commands/compact-harness-window-command.d.ts +3 -0
  51. package/dist/cli/commands/compact-harness-window-command.js +174 -0
  52. package/dist/cli/commands/compact-history-command.d.ts +3 -0
  53. package/dist/cli/commands/compact-history-command.js +89 -0
  54. package/dist/cli/commands/compact-read-commands.d.ts +3 -0
  55. package/dist/cli/commands/compact-read-commands.js +123 -0
  56. package/dist/cli/commands/compact-settle-command.d.ts +3 -0
  57. package/dist/cli/commands/compact-settle-command.js +130 -0
  58. package/dist/cli/commands/compact-shared.d.ts +9 -0
  59. package/dist/cli/commands/compact-shared.js +26 -0
  60. package/dist/cli/commands/core/skill-command.d.ts +1 -44
  61. package/dist/cli/commands/core/skill-command.js +13 -541
  62. package/dist/cli/commands/core/skill-context-verdict.d.ts +38 -0
  63. package/dist/cli/commands/core/skill-context-verdict.js +63 -0
  64. package/dist/cli/commands/core/skill-generated-config-notice.d.ts +41 -0
  65. package/dist/cli/commands/core/skill-generated-config-notice.js +75 -0
  66. package/dist/cli/commands/core/skill-heartbeat-commands.d.ts +3 -0
  67. package/dist/cli/commands/core/skill-heartbeat-commands.js +50 -0
  68. package/dist/cli/commands/core/skill-presence-commands.d.ts +3 -0
  69. package/dist/cli/commands/core/skill-presence-commands.js +123 -0
  70. package/dist/cli/commands/core/skill-presence-housekeeping-commands.d.ts +3 -0
  71. package/dist/cli/commands/core/skill-presence-housekeeping-commands.js +165 -0
  72. package/dist/cli/commands/core/skill-registry-commands.d.ts +3 -0
  73. package/dist/cli/commands/core/skill-registry-commands.js +117 -0
  74. package/dist/cli/commands/core/skill-runbook-command.d.ts +3 -0
  75. package/dist/cli/commands/core/skill-runbook-command.js +29 -0
  76. package/dist/cli/commands/dispatch-command-spec.d.ts +2 -0
  77. package/dist/cli/commands/dispatch-command-spec.js +62 -0
  78. package/dist/cli/commands/dispatch-commands.js +159 -775
  79. package/dist/cli/commands/dispatch-detached-branch.d.ts +3 -0
  80. package/dist/cli/commands/dispatch-detached-branch.js +58 -0
  81. package/dist/cli/commands/dispatch-envelope.d.ts +36 -0
  82. package/dist/cli/commands/dispatch-envelope.js +104 -0
  83. package/dist/cli/commands/dispatch-isolation-lease.d.ts +17 -0
  84. package/dist/cli/commands/dispatch-isolation-lease.js +149 -0
  85. package/dist/cli/commands/dispatch-preflight.d.ts +7 -0
  86. package/dist/cli/commands/dispatch-preflight.js +105 -0
  87. package/dist/cli/commands/dispatch-prompt-blocks.d.ts +10 -0
  88. package/dist/cli/commands/dispatch-prompt-blocks.js +56 -0
  89. package/dist/cli/commands/dispatch-prompt-composition.d.ts +30 -0
  90. package/dist/cli/commands/dispatch-prompt-composition.js +171 -0
  91. package/dist/cli/commands/dispatch-record-write.d.ts +24 -0
  92. package/dist/cli/commands/dispatch-record-write.js +96 -0
  93. package/dist/cli/commands/dispatch-tool-call.d.ts +15 -0
  94. package/dist/cli/commands/dispatch-tool-call.js +128 -0
  95. package/dist/cli/commands/evolution-command-shared.d.ts +78 -0
  96. package/dist/cli/commands/evolution-command-shared.js +107 -0
  97. package/dist/cli/commands/evolution-commands.d.ts +11 -1
  98. package/dist/cli/commands/evolution-commands.js +19 -350
  99. package/dist/cli/commands/evolution-evaluate-command.d.ts +24 -0
  100. package/dist/cli/commands/evolution-evaluate-command.js +138 -0
  101. package/dist/cli/commands/evolution-propose-command.d.ts +23 -0
  102. package/dist/cli/commands/evolution-propose-command.js +145 -0
  103. package/dist/cli/commands/evolution-status-command.d.ts +17 -0
  104. package/dist/cli/commands/evolution-status-command.js +41 -0
  105. package/dist/cli/commands/evolution-verdict-commands.d.ts +30 -0
  106. package/dist/cli/commands/evolution-verdict-commands.js +100 -0
  107. package/dist/cli/commands/finalize-accumulator.d.ts +44 -0
  108. package/dist/cli/commands/finalize-accumulator.js +55 -0
  109. package/dist/cli/commands/finalize-branches.d.ts +34 -0
  110. package/dist/cli/commands/finalize-branches.js +119 -0
  111. package/dist/cli/commands/finalize-runners.d.ts +3 -0
  112. package/dist/cli/commands/finalize-runners.js +92 -0
  113. package/dist/cli/commands/finalize-selection.d.ts +65 -0
  114. package/dist/cli/commands/finalize-selection.js +35 -0
  115. package/dist/cli/commands/gate-commands.js +26 -8
  116. package/dist/cli/commands/job-checkpoint-commands.d.ts +3 -0
  117. package/dist/cli/commands/job-checkpoint-commands.js +157 -0
  118. package/dist/cli/commands/job-command-shared.d.ts +109 -0
  119. package/dist/cli/commands/job-command-shared.js +72 -0
  120. package/dist/cli/commands/job-commands.d.ts +4 -64
  121. package/dist/cli/commands/job-commands.js +42 -552
  122. package/dist/cli/commands/job-cost-check-command.d.ts +3 -0
  123. package/dist/cli/commands/job-cost-check-command.js +41 -0
  124. package/dist/cli/commands/job-init-status-commands.d.ts +3 -0
  125. package/dist/cli/commands/job-init-status-commands.js +149 -0
  126. package/dist/cli/commands/job-run-commands.d.ts +3 -0
  127. package/dist/cli/commands/job-run-commands.js +97 -0
  128. package/dist/cli/commands/job-state-root.d.ts +35 -0
  129. package/dist/cli/commands/job-state-root.js +76 -0
  130. package/dist/cli/commands/loop-eval-capture-index.d.ts +4 -0
  131. package/dist/cli/commands/loop-eval-capture-index.js +44 -0
  132. package/dist/cli/commands/loop-eval-commands.d.ts +8 -5
  133. package/dist/cli/commands/loop-eval-commands.js +12 -626
  134. package/dist/cli/commands/loop-eval-eval-command.d.ts +3 -0
  135. package/dist/cli/commands/loop-eval-eval-command.js +168 -0
  136. package/dist/cli/commands/loop-eval-monotonic-command.d.ts +3 -0
  137. package/dist/cli/commands/loop-eval-monotonic-command.js +90 -0
  138. package/dist/cli/commands/loop-eval-parents.d.ts +7 -0
  139. package/dist/cli/commands/loop-eval-parents.js +20 -0
  140. package/dist/cli/commands/loop-eval-run-code.d.ts +7 -0
  141. package/dist/cli/commands/loop-eval-run-code.js +40 -0
  142. package/dist/cli/commands/loop-eval-run-command.d.ts +3 -0
  143. package/dist/cli/commands/loop-eval-run-command.js +113 -0
  144. package/dist/cli/commands/loop-eval-spec-commands.d.ts +3 -0
  145. package/dist/cli/commands/loop-eval-spec-commands.js +165 -0
  146. package/dist/cli/commands/loop-eval-spec-lint-command.d.ts +3 -0
  147. package/dist/cli/commands/loop-eval-spec-lint-command.js +42 -0
  148. package/dist/cli/commands/loop-eval-workflow-commands.d.ts +3 -0
  149. package/dist/cli/commands/loop-eval-workflow-commands.js +141 -0
  150. package/dist/cli/commands/mcp-commands.d.ts +10 -0
  151. package/dist/cli/commands/mcp-commands.js +157 -0
  152. package/dist/cli/commands/request-command-options.d.ts +49 -0
  153. package/dist/cli/commands/request-command-options.js +1 -0
  154. package/dist/cli/commands/request-commands.d.ts +3 -9
  155. package/dist/cli/commands/request-commands.js +17 -522
  156. package/dist/cli/commands/request-init-command.d.ts +3 -0
  157. package/dist/cli/commands/request-init-command.js +100 -0
  158. package/dist/cli/commands/request-lint-command.d.ts +3 -0
  159. package/dist/cli/commands/request-lint-command.js +50 -0
  160. package/dist/cli/commands/request-list-command.d.ts +10 -0
  161. package/dist/cli/commands/request-list-command.js +53 -0
  162. package/dist/cli/commands/request-repair-status-command.d.ts +3 -0
  163. package/dist/cli/commands/request-repair-status-command.js +61 -0
  164. package/dist/cli/commands/request-show-command.d.ts +3 -0
  165. package/dist/cli/commands/request-show-command.js +56 -0
  166. package/dist/cli/commands/request-transition-command.d.ts +3 -0
  167. package/dist/cli/commands/request-transition-command.js +122 -0
  168. package/dist/cli/commands/request-transition-failures.d.ts +8 -0
  169. package/dist/cli/commands/request-transition-failures.js +90 -0
  170. package/dist/cli/commands/request-transition-hooks.d.ts +50 -0
  171. package/dist/cli/commands/request-transition-hooks.js +115 -0
  172. package/dist/cli/commands/request-transition-preflight.d.ts +10 -0
  173. package/dist/cli/commands/request-transition-preflight.js +107 -0
  174. package/dist/cli/commands/scan-command-shared.d.ts +96 -0
  175. package/dist/cli/commands/scan-command-shared.js +83 -0
  176. package/dist/cli/commands/scan-commands.d.ts +3 -2
  177. package/dist/cli/commands/scan-commands.js +23 -463
  178. package/dist/cli/commands/scan-inventory-commands.d.ts +3 -0
  179. package/dist/cli/commands/scan-inventory-commands.js +96 -0
  180. package/dist/cli/commands/scan-project-commands.d.ts +3 -0
  181. package/dist/cli/commands/scan-project-commands.js +65 -0
  182. package/dist/cli/commands/scan-scope-commands.d.ts +3 -0
  183. package/dist/cli/commands/scan-scope-commands.js +149 -0
  184. package/dist/cli/commands/scan-scope-helpers.d.ts +8 -0
  185. package/dist/cli/commands/scan-scope-helpers.js +54 -0
  186. package/dist/cli/commands/scan-structural-commands.d.ts +3 -0
  187. package/dist/cli/commands/scan-structural-commands.js +113 -0
  188. package/dist/cli/commands/share-commands.d.ts +12 -68
  189. package/dist/cli/commands/share-commands.js +47 -556
  190. package/dist/cli/commands/share-error-hints.d.ts +14 -0
  191. package/dist/cli/commands/share-error-hints.js +38 -0
  192. package/dist/cli/commands/share-identity.d.ts +13 -0
  193. package/dist/cli/commands/share-identity.js +22 -0
  194. package/dist/cli/commands/share-record-paths.d.ts +37 -0
  195. package/dist/cli/commands/share-record-paths.js +68 -0
  196. package/dist/cli/commands/share-runners.d.ts +3 -0
  197. package/dist/cli/commands/share-runners.js +158 -0
  198. package/dist/cli/commands/shared-read-runners.d.ts +3 -0
  199. package/dist/cli/commands/shared-read-runners.js +51 -0
  200. package/dist/cli/commands/slice-check-command.d.ts +3 -0
  201. package/dist/cli/commands/slice-check-command.js +54 -0
  202. package/dist/cli/commands/slice-commands.d.ts +2 -22
  203. package/dist/cli/commands/slice-commands.js +18 -517
  204. package/dist/cli/commands/slice-decompose-artifacts.d.ts +18 -0
  205. package/dist/cli/commands/slice-decompose-artifacts.js +79 -0
  206. package/dist/cli/commands/slice-decompose-command.d.ts +3 -0
  207. package/dist/cli/commands/slice-decompose-command.js +123 -0
  208. package/dist/cli/commands/slice-list-commands.d.ts +3 -0
  209. package/dist/cli/commands/slice-list-commands.js +182 -0
  210. package/dist/cli/commands/slice-pick-command.d.ts +3 -0
  211. package/dist/cli/commands/slice-pick-command.js +60 -0
  212. package/dist/cli/commands/slice-plan-command.d.ts +24 -0
  213. package/dist/cli/commands/slice-plan-command.js +111 -0
  214. package/dist/cli/commands/workflow-commands.d.ts +10 -1
  215. package/dist/cli/commands/workflow-commands.js +6 -594
  216. package/dist/cli/commands/workflow-gate-commands.d.ts +3 -0
  217. package/dist/cli/commands/workflow-gate-commands.js +116 -0
  218. package/dist/cli/commands/workflow-plan-helpers.d.ts +48 -0
  219. package/dist/cli/commands/workflow-plan-helpers.js +103 -0
  220. package/dist/cli/commands/workflow-planning-commands.d.ts +3 -0
  221. package/dist/cli/commands/workflow-planning-commands.js +56 -0
  222. package/dist/cli/commands/workflow-swarm-actions.d.ts +7 -0
  223. package/dist/cli/commands/workflow-swarm-actions.js +109 -0
  224. package/dist/cli/commands/workflow-swarm-commands.d.ts +3 -0
  225. package/dist/cli/commands/workflow-swarm-commands.js +138 -0
  226. package/dist/cli/commands/workflow-tech-actions.d.ts +10 -0
  227. package/dist/cli/commands/workflow-tech-actions.js +150 -0
  228. package/dist/cli/commands/workspace/init-actions.d.ts +10 -0
  229. package/dist/cli/commands/workspace/init-actions.js +199 -0
  230. package/dist/cli/commands/workspace/init-codegraph-stake.d.ts +10 -0
  231. package/dist/cli/commands/workspace/init-codegraph-stake.js +54 -0
  232. package/dist/cli/commands/workspace/init-command.d.ts +8 -124
  233. package/dist/cli/commands/workspace/init-command.js +59 -623
  234. package/dist/cli/commands/workspace/init-context.d.ts +58 -0
  235. package/dist/cli/commands/workspace/init-context.js +121 -0
  236. package/dist/cli/commands/workspace/init-envelope.d.ts +3 -0
  237. package/dist/cli/commands/workspace/init-envelope.js +34 -0
  238. package/dist/cli/commands/workspace/init-failures.d.ts +3 -0
  239. package/dist/cli/commands/workspace/init-failures.js +84 -0
  240. package/dist/cli/commands/workspace/init-hooks-decision.d.ts +24 -0
  241. package/dist/cli/commands/workspace/init-hooks-decision.js +189 -0
  242. package/dist/cli/commands/workspace/init-options.d.ts +102 -0
  243. package/dist/cli/commands/workspace/init-options.js +1 -0
  244. package/dist/cli/commands/worktree-lease-commands.d.ts +10 -6
  245. package/dist/cli/commands/worktree-lease-commands.js +42 -512
  246. package/dist/cli/commands/worktree-lease-gc-command.d.ts +12 -0
  247. package/dist/cli/commands/worktree-lease-gc-command.js +117 -0
  248. package/dist/cli/commands/worktree-lease-git.d.ts +22 -0
  249. package/dist/cli/commands/worktree-lease-git.js +48 -0
  250. package/dist/cli/commands/worktree-lease-list-command.d.ts +12 -0
  251. package/dist/cli/commands/worktree-lease-list-command.js +100 -0
  252. package/dist/cli/commands/worktree-lease-load.d.ts +37 -0
  253. package/dist/cli/commands/worktree-lease-load.js +28 -0
  254. package/dist/cli/commands/worktree-lease-release-command.d.ts +13 -0
  255. package/dist/cli/commands/worktree-lease-release-command.js +105 -0
  256. package/dist/cli/commands/worktree-lease-renew-command.d.ts +11 -0
  257. package/dist/cli/commands/worktree-lease-renew-command.js +99 -0
  258. package/dist/cli/commands/worktree-lease-session.d.ts +22 -0
  259. package/dist/cli/commands/worktree-lease-session.js +29 -0
  260. package/dist/cli/commands/worktree-lease-spawn-command.d.ts +11 -0
  261. package/dist/cli/commands/worktree-lease-spawn-command.js +119 -0
  262. package/dist/cli/commands/worktree-lease-status-command.d.ts +11 -0
  263. package/dist/cli/commands/worktree-lease-status-command.js +86 -0
  264. package/dist/cli/commands/worktree-lease-store.d.ts +19 -0
  265. package/dist/cli/commands/worktree-lease-store.js +24 -0
  266. package/dist/cli/program.js +5 -0
  267. package/dist/services/distribution/mcp-install-runner.d.ts +21 -0
  268. package/dist/services/distribution/mcp-install-runner.js +59 -0
  269. package/dist/services/distribution/mcp-install.d.ts +95 -0
  270. package/dist/services/distribution/mcp-install.js +179 -0
  271. package/dist/services/distribution/plugin-manifest.d.ts +97 -0
  272. package/dist/services/distribution/plugin-manifest.js +126 -0
  273. package/dist/services/hooks/mcp-surface-gate.d.ts +19 -0
  274. package/dist/services/hooks/mcp-surface-gate.js +87 -0
  275. package/dist/services/ide/adapters/claude-code-adapter.d.ts +4 -35
  276. package/dist/services/ide/adapters/claude-code-adapter.js +48 -171
  277. package/dist/services/ide/adapters/claude-code-local-state.d.ts +28 -0
  278. package/dist/services/ide/adapters/claude-code-local-state.js +112 -0
  279. package/dist/services/ide/adapters/claude-code-model-window.d.ts +37 -0
  280. package/dist/services/ide/adapters/claude-code-model-window.js +113 -0
  281. package/dist/services/ide/ide-mcp-install-types.d.ts +78 -0
  282. package/dist/services/ide/ide-mcp-install-types.js +104 -0
  283. package/dist/services/ide/ide-types.d.ts +45 -1
  284. package/dist/services/ide/mcp-tool-matcher.d.ts +34 -0
  285. package/dist/services/ide/mcp-tool-matcher.js +84 -0
  286. package/dist/services/lint/npx-resolver.js +7 -0
  287. package/dist/services/mcp/cli-executor.d.ts +36 -0
  288. package/dist/services/mcp/cli-executor.js +97 -0
  289. package/dist/services/mcp/json-rpc.d.ts +81 -0
  290. package/dist/services/mcp/json-rpc.js +116 -0
  291. package/dist/services/mcp/memory-tool.d.ts +3 -0
  292. package/dist/services/mcp/memory-tool.js +62 -0
  293. package/dist/services/mcp/server-main.d.ts +1 -0
  294. package/dist/services/mcp/server-main.js +66 -0
  295. package/dist/services/mcp/server.d.ts +22 -0
  296. package/dist/services/mcp/server.js +152 -0
  297. package/dist/services/mcp/surface.d.ts +62 -0
  298. package/dist/services/mcp/surface.js +198 -0
  299. package/dist/services/mcp/tool-core.d.ts +42 -0
  300. package/dist/services/mcp/tool-core.js +137 -0
  301. package/dist/services/mcp/tools.d.ts +13 -0
  302. package/dist/services/mcp/tools.js +58 -0
  303. package/dist/services/readonly-surface/argv-guard.d.ts +32 -0
  304. package/dist/services/readonly-surface/argv-guard.js +164 -0
  305. package/dist/services/readonly-surface/readonly-whitelist.d.ts +147 -0
  306. package/dist/services/readonly-surface/readonly-whitelist.js +107 -0
  307. package/dist/services/readonly-surface/whitelist-generator.d.ts +64 -0
  308. package/dist/services/readonly-surface/whitelist-generator.js +212 -0
  309. package/dist/services/skills/hooks-codegate-superpowers.d.ts +1 -0
  310. package/dist/services/skills/hooks-codegate-superpowers.js +26 -0
  311. package/dist/services/skills/hooks-settings-service.js +10 -2
  312. package/package.json +17 -6
  313. package/scripts/install-skills-bundles.mjs +233 -0
  314. package/scripts/install-skills-fs.mjs +148 -0
  315. package/scripts/install-skills-ide.mjs +279 -0
  316. package/scripts/install-skills-output-style-default.mjs +196 -0
  317. package/scripts/install-skills-output-styles.mjs +121 -0
  318. package/scripts/install-skills-report.mjs +42 -0
  319. package/scripts/install-skills-user-config.mjs +198 -0
  320. package/scripts/install-skills.mjs +36 -1066
  321. package/scripts/watch-tree.mjs +140 -0
  322. package/scripts/watch.mjs +4 -126
  323. package/skills/peaks-status/SKILL.md +31 -13
@@ -0,0 +1,97 @@
1
+ import { type SkillMetadata } from '../skills/skill-registry.js';
2
+ /** Where the generator writes, relative to the package root. */
3
+ export declare const PLUGIN_MANIFEST_RELATIVE_PATH = ".claude-plugin/marketplace.json";
4
+ /**
5
+ * The server key peaks registers itself under. It is OUR key, not a harness's —
6
+ * the adapter profiles that name it (`IdeMcpInstallProfile.serverName`) and the
7
+ * tool-name templates that match it (`IdeAdapter.mcp`) must agree with this one,
8
+ * which is why it is declared once.
9
+ */
10
+ export declare const MCP_SERVER_KEY = "peaks";
11
+ /** The subset of `package.json` this render reads. */
12
+ interface PackageManifest {
13
+ readonly name: string;
14
+ readonly version: string;
15
+ readonly description?: string;
16
+ readonly author?: string;
17
+ readonly homepage?: string;
18
+ readonly repository?: {
19
+ readonly url?: string;
20
+ } | string;
21
+ readonly license?: string;
22
+ readonly keywords?: readonly string[];
23
+ }
24
+ /** One plugin entry's skill list item — the shape `peaks skill:visibility` reads. */
25
+ interface PluginSkillEntry {
26
+ readonly name: string;
27
+ readonly path: string;
28
+ readonly description: string;
29
+ readonly userInvocable?: boolean;
30
+ }
31
+ interface GeneratedPluginManifest {
32
+ readonly metadata: {
33
+ readonly pluginRoot: string;
34
+ readonly version: string;
35
+ };
36
+ readonly plugins: readonly {
37
+ readonly name: string;
38
+ readonly description: string;
39
+ readonly version: string;
40
+ readonly author: {
41
+ readonly name: string;
42
+ };
43
+ readonly homepage: string;
44
+ readonly repository: string;
45
+ readonly license: string;
46
+ readonly keywords: readonly string[];
47
+ /**
48
+ * Declared, and deliberately not brought live: the plugin channel is not
49
+ * published by this slice (PRD non-goal), so this is the statement of what
50
+ * the channel WOULD run, kept true by generation rather than by memory.
51
+ *
52
+ * The command is the CLI's own name and subcommand, with no path in it: a
53
+ * manifest is built on one machine and read on another, so an absolute
54
+ * interpreter path resolved here would be the build host's. The RUNTIME
55
+ * registration is a different artefact with a different consumer and does
56
+ * resolve the pair — see `mcp-install.ts::resolveMcpServerArgv`.
57
+ */
58
+ readonly mcpServers: Record<string, {
59
+ readonly command: string;
60
+ readonly args: readonly string[];
61
+ }>;
62
+ readonly skills: readonly PluginSkillEntry[];
63
+ }[];
64
+ }
65
+ /** `package.json`, parsed. A missing version is a refusal, not a default. */
66
+ export declare function loadPackageManifest(root?: string): PackageManifest;
67
+ /**
68
+ * The skill entries, in the registry's order.
69
+ *
70
+ * `visibility: internal` is the skill's own declaration that it is an internal
71
+ * role rather than a front door, and it is the ONE source for `userInvocable` —
72
+ * the field is emitted only when it is `false`, which is the contract
73
+ * `skill:visibility` reads ("internal: userInvocable false; public: omitted").
74
+ */
75
+ export declare function skillEntriesOf(skills: readonly SkillMetadata[]): readonly PluginSkillEntry[];
76
+ /**
77
+ * Render the manifest. `skills` is passed in rather than loaded here so the one
78
+ * caller that must fail loudly on an unreadable SKILL.md can do so once.
79
+ */
80
+ export declare function generatePluginManifest(manifest: PackageManifest, skills: readonly SkillMetadata[]): GeneratedPluginManifest;
81
+ /** Pretty JSON, newline-terminated — the same serialization contract as the whitelist. */
82
+ export declare function serializePluginManifest(manifest: GeneratedPluginManifest): string;
83
+ /** Absolute path of the committed artifact for a package root. */
84
+ export declare function pluginManifestPath(root?: string): string;
85
+ /**
86
+ * The whole render for a package root: read the two sources, refuse a skill tree
87
+ * that did not fully parse, and serialize.
88
+ *
89
+ * The `failures` refusal is the load-bearing part. `loadSkillRegistry` reports an
90
+ * unreadable `SKILL.md` in a `failures` array instead of throwing, so a render
91
+ * that ignored it would DROP a skill from the manifest and still be
92
+ * byte-stable — a green CI over a manifest that had silently lost entries. A
93
+ * drift assertion cannot be stronger than its input, so the input is required to
94
+ * be complete.
95
+ */
96
+ export declare function renderPluginManifest(root?: string): Promise<string>;
97
+ export {};
@@ -0,0 +1,126 @@
1
+ // src/services/distribution/plugin-manifest.ts
2
+ //
3
+ // The plugin/distribution manifest as a GENERATED artefact (spec §4.1). One
4
+ // source, two renders: the whitelist artifact is what the read-only claim is
5
+ // made of, and this file is what the distribution channel is told.
6
+ //
7
+ // WHY IT IS GENERATED. The hand-authored `.claude-plugin/marketplace.json` had
8
+ // rotted in the only two fields anybody could check and nobody did: it declared
9
+ // version `2.0.3` while the package was `4.1.2`, and 12 skills while the tree
10
+ // held 22. Root cause is not carelessness — it is that the current distribution
11
+ // channel is `npm i -g`, so nothing ever read the plugin manifest and no failure
12
+ // could be observed from it. A file whose author is a human and whose reader is
13
+ // nobody will drift; the fix is to remove the human from the loop, not to be
14
+ // more careful.
15
+ //
16
+ // CHANGE THE SOURCE, NOT THIS FILE. Everything below is derived, and
17
+ // `tests/unit/services/distribution/plugin-manifest.test.ts` fails the moment
18
+ // the committed artefact differs from a fresh render. A hand edit is therefore
19
+ // not merely overwritten, it is RED. To change the manifest, change
20
+ // `package.json` (a plugin-level field) or the skill's own `SKILL.md`
21
+ // (a skill entry) and re-run `pnpm plugin:manifest`.
22
+ //
23
+ // DETERMINISM. No timestamp, no `Date`, no unordered iteration: skills come back
24
+ // from `loadSkillRegistry`, which sorts by name, and every plugin-level field is
25
+ // copied from `package.json` in a fixed order. Two consecutive runs are
26
+ // byte-identical, which is what makes the CI equality assertion mean something.
27
+ import { readFileSync } from 'node:fs';
28
+ import { dirname, join, relative } from 'node:path';
29
+ import { repoRoot } from 'peaks-loop-shared/paths';
30
+ import { loadSkillRegistry } from '../skills/skill-registry.js';
31
+ /** Where the generator writes, relative to the package root. */
32
+ export const PLUGIN_MANIFEST_RELATIVE_PATH = '.claude-plugin/marketplace.json';
33
+ /**
34
+ * The server key peaks registers itself under. It is OUR key, not a harness's —
35
+ * the adapter profiles that name it (`IdeMcpInstallProfile.serverName`) and the
36
+ * tool-name templates that match it (`IdeAdapter.mcp`) must agree with this one,
37
+ * which is why it is declared once.
38
+ */
39
+ export const MCP_SERVER_KEY = 'peaks';
40
+ /** `package.json`, parsed. A missing version is a refusal, not a default. */
41
+ export function loadPackageManifest(root = repoRoot) {
42
+ const parsed = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
43
+ if (typeof parsed.version !== 'string' || parsed.version.length === 0) {
44
+ throw new Error('package.json version must be a non-empty string');
45
+ }
46
+ return parsed;
47
+ }
48
+ /** A repository URL as a manifest reads it: npm's `.git` suffix is not part of the link. */
49
+ function repositoryUrl(manifest) {
50
+ const raw = typeof manifest.repository === 'string' ? manifest.repository : manifest.repository?.url;
51
+ return (raw ?? '').replace(/\.git$/, '');
52
+ }
53
+ /** POSIX-separated, so the artifact is byte-identical whichever OS generated it. */
54
+ function posix(value) {
55
+ return value.split('\\').join('/');
56
+ }
57
+ /**
58
+ * The skill entries, in the registry's order.
59
+ *
60
+ * `visibility: internal` is the skill's own declaration that it is an internal
61
+ * role rather than a front door, and it is the ONE source for `userInvocable` —
62
+ * the field is emitted only when it is `false`, which is the contract
63
+ * `skill:visibility` reads ("internal: userInvocable false; public: omitted").
64
+ */
65
+ export function skillEntriesOf(skills) {
66
+ return skills
67
+ .filter((skill) => skill.name.startsWith('peaks-'))
68
+ .map((skill) => ({
69
+ name: skill.name,
70
+ path: `./${posix(relative(repoRoot, dirname(skill.skillPath)))}`,
71
+ description: skill.description,
72
+ ...(skill.visibility === 'internal' ? { userInvocable: false } : {})
73
+ }));
74
+ }
75
+ /**
76
+ * Render the manifest. `skills` is passed in rather than loaded here so the one
77
+ * caller that must fail loudly on an unreadable SKILL.md can do so once.
78
+ */
79
+ export function generatePluginManifest(manifest, skills) {
80
+ return {
81
+ metadata: { pluginRoot: '.', version: manifest.version },
82
+ plugins: [
83
+ {
84
+ name: manifest.name,
85
+ description: manifest.description ?? '',
86
+ version: manifest.version,
87
+ author: { name: manifest.author ?? '' },
88
+ homepage: manifest.homepage ?? '',
89
+ repository: repositoryUrl(manifest),
90
+ license: manifest.license ?? '',
91
+ keywords: [...(manifest.keywords ?? [])],
92
+ mcpServers: {
93
+ [MCP_SERVER_KEY]: { command: MCP_SERVER_KEY, args: ['mcp', 'serve'] }
94
+ },
95
+ skills: skillEntriesOf(skills)
96
+ }
97
+ ]
98
+ };
99
+ }
100
+ /** Pretty JSON, newline-terminated — the same serialization contract as the whitelist. */
101
+ export function serializePluginManifest(manifest) {
102
+ return `${JSON.stringify(manifest, null, 2)}\n`;
103
+ }
104
+ /** Absolute path of the committed artifact for a package root. */
105
+ export function pluginManifestPath(root = repoRoot) {
106
+ return join(root, PLUGIN_MANIFEST_RELATIVE_PATH);
107
+ }
108
+ /**
109
+ * The whole render for a package root: read the two sources, refuse a skill tree
110
+ * that did not fully parse, and serialize.
111
+ *
112
+ * The `failures` refusal is the load-bearing part. `loadSkillRegistry` reports an
113
+ * unreadable `SKILL.md` in a `failures` array instead of throwing, so a render
114
+ * that ignored it would DROP a skill from the manifest and still be
115
+ * byte-stable — a green CI over a manifest that had silently lost entries. A
116
+ * drift assertion cannot be stronger than its input, so the input is required to
117
+ * be complete.
118
+ */
119
+ export async function renderPluginManifest(root = repoRoot) {
120
+ const skills = await loadSkillRegistry(join(root, 'skills'));
121
+ if (skills.failures.length > 0) {
122
+ const named = skills.failures.map((failure) => failure.skillPath).join(', ');
123
+ throw new Error(`Refusing to render the plugin manifest: unreadable skill(s): ${named}`);
124
+ }
125
+ return serializePluginManifest(generatePluginManifest(loadPackageManifest(root), skills.skills));
126
+ }
@@ -0,0 +1,19 @@
1
+ import type { ProgramIO } from '../../cli/cli-helpers.js';
2
+ import type { IdeAdapter } from '../ide/ide-types.js';
3
+ /** The tool names the proven surface declares. */
4
+ export type McpSurfaceLookup = () => readonly string[];
5
+ /** The production lookup. Injectable so a test can make it throw and watch the refusal. */
6
+ export declare function lookupAllowedMcpTools(): readonly string[];
7
+ /** The adapter whose harness sent this payload. */
8
+ export declare function detectAdapter(parsedStdin: unknown): IdeAdapter;
9
+ /** The deny emission, in the shape the host already treats as a hard block. */
10
+ export declare function emitMcpDeny(io: ProgramIO, code: string, reason: string): void;
11
+ /**
12
+ * Run the MCP branch over one parsed hook payload.
13
+ *
14
+ * @returns `true` when the call was BLOCKED (a deny has already been emitted).
15
+ * `false` means "this is not an MCP call for this surface" — every
16
+ * other tool name, and every harness declaring no MCP naming, lands
17
+ * there and keeps the allow/deny result it had before.
18
+ */
19
+ export declare function handleMcpSurfaceGate(io: ProgramIO, parsedStdin: unknown, lookup?: McpSurfaceLookup, adapterOf?: (payload: unknown) => IdeAdapter): boolean;
@@ -0,0 +1,87 @@
1
+ // src/services/hooks/mcp-surface-gate.ts
2
+ //
3
+ // L3 for the read-only MCP surface: the pre-tool branch that covers MCP tool
4
+ // calls (spec §6.1, §6.2, §6.3).
5
+ //
6
+ // WHY IT IS ITS OWN MODULE. It shares an entry point with the Bash and worktree
7
+ // branches but nothing else: different surface (an MCP tool name, not a command
8
+ // text), different failure direction (fail-CLOSED, where the others fail open),
9
+ // and a different thing it polices. Keeping it beside them made the interceptor
10
+ // file carry three unrelated decisions; it lives here so the file that owns the
11
+ // hook's control flow stays the file that owns the hook's control flow.
12
+ //
13
+ // WHAT IT POLICES, AND WHAT IT DOES NOT. It does not second-guess what a tool
14
+ // would return. It answers one question: "is this MCP tool call one the proven
15
+ // surface declares?" It exists for DRIFT — a stale server process, a hand-edited
16
+ // harness config pointing somewhere else, a tool added to a config that was
17
+ // never proved read-only. The read-only proof itself is L2's job; this is the
18
+ // belt over it.
19
+ //
20
+ // FAIL-CLOSED, AND THE ASYMMETRY WITH THE BASH BRANCH IS DELIBERATE. The Bash
21
+ // branch guards command TEXT, where a false block interrupts real work, so it
22
+ // fails open. This branch guards CONFIG CONSISTENCY, where a false block costs
23
+ // one call that should have been consistent while a false allow costs a call
24
+ // that bypassed the chain. The costs are not the same size, so they do not share
25
+ // a direction — and they do not share a `catch` either.
26
+ //
27
+ // IT READS THE SURFACE AS DATA, NEVER AS CODE. The allowed tool names come from
28
+ // the generated whitelist artifact through its loader, so this branch keeps
29
+ // working when `src/services/mcp/` is deleted (spec §1) — which is also what
30
+ // `tests/unit/standards/no-mcp-source-import.test.ts` enforces mechanically.
31
+ import { getErrorMessage } from 'peaks-loop-shared/result';
32
+ import { emitBlock } from './output.js';
33
+ import { getAdapter } from '../ide/ide-registry.js';
34
+ import { detectIdeFromContext, parseClaudeShapeStdin } from '../ide/hook-translator.js';
35
+ import { matchMcpToolName } from '../ide/mcp-tool-matcher.js';
36
+ import { loadReadOnlyWhitelist, toolIdsOf } from '../readonly-surface/readonly-whitelist.js';
37
+ /** The production lookup. Injectable so a test can make it throw and watch the refusal. */
38
+ export function lookupAllowedMcpTools() {
39
+ return toolIdsOf(loadReadOnlyWhitelist());
40
+ }
41
+ /** The adapter whose harness sent this payload. */
42
+ export function detectAdapter(parsedStdin) {
43
+ const ide = detectIdeFromContext({ env: process.env, cwd: process.cwd(), parsedStdin });
44
+ return getAdapter(ide);
45
+ }
46
+ /** The deny emission, in the shape the host already treats as a hard block. */
47
+ export function emitMcpDeny(io, code, reason) {
48
+ emitBlock(io, `[mcp-surface-gate:${code}] ${reason}. Blocked by fail-closed policy — ` +
49
+ 'run the read-only command through the CLI instead, or add its argv to ' +
50
+ 'contracts/readonly-surface.json and regenerate the whitelist.');
51
+ }
52
+ /**
53
+ * Run the MCP branch over one parsed hook payload.
54
+ *
55
+ * @returns `true` when the call was BLOCKED (a deny has already been emitted).
56
+ * `false` means "this is not an MCP call for this surface" — every
57
+ * other tool name, and every harness declaring no MCP naming, lands
58
+ * there and keeps the allow/deny result it had before.
59
+ */
60
+ export function handleMcpSurfaceGate(io, parsedStdin, lookup = lookupAllowedMcpTools, adapterOf = detectAdapter) {
61
+ let adapter;
62
+ try {
63
+ adapter = adapterOf(parsedStdin);
64
+ }
65
+ catch (error) {
66
+ // Detection itself failed. Falling through would let the call pass
67
+ // unchecked, which is the one outcome this branch exists to prevent.
68
+ emitMcpDeny(io, 'IDE_DETECTION_FAILED', `the harness could not be identified (${getErrorMessage(error)})`);
69
+ return true;
70
+ }
71
+ const { toolName } = parseClaudeShapeStdin(parsedStdin);
72
+ const match = toolName === undefined ? undefined : matchMcpToolName(adapter, toolName);
73
+ if (match === undefined)
74
+ return false;
75
+ let allowed;
76
+ try {
77
+ allowed = lookup();
78
+ }
79
+ catch (error) {
80
+ emitMcpDeny(io, 'SURFACE_UNREADABLE', `the read-only surface could not be read (${getErrorMessage(error)})`);
81
+ return true;
82
+ }
83
+ if (allowed.includes(match.toolId))
84
+ return false;
85
+ emitMcpDeny(io, 'NOT_IN_READONLY_SURFACE', `"${toolName}" is not one of the read-only tools this project declares (${allowed.join(', ') || 'none'})`);
86
+ return true;
87
+ }
@@ -1,39 +1,8 @@
1
1
  import type { IdeAdapter } from '../ide-types.js';
2
- /**
3
- * Resolve the absolute path of a Claude Code transcript jsonl by OUTER
4
- * session id, searching `~/.claude/projects/**` recursively.
5
- *
6
- * `peaks code context-audit` reuses THIS locator instead of re-implementing
7
- * the recursive find. Returns `null` when the transcript does not exist —
8
- * callers MUST treat that as "unavailable", never as an error.
9
- */
10
- export declare function resolveClaudeTranscriptPath(outerSessionId: string, projectsDir?: string): string | null;
11
- /**
12
- * Resolve the currently-active Claude Code model id from a runtime env map.
13
- * Returns the first non-empty `CLAUDE_CODE_MODEL_ENV_VARS` value (trimmed), or
14
- * `undefined` when none is present. Pure + exported for tests; the caller falls
15
- * back to the transcript `message.model` when this returns undefined.
16
- */
17
- export declare function resolveClaudeModelFromEnv(env: NodeJS.ProcessEnv | undefined): string | undefined;
18
- /**
19
- * Model-aware context-window size in tokens.
20
- *
21
- * Detection rule (documented):
22
- * 1. Empty / unknown model → DEFAULT_CONTEXT_WINDOW_TOKENS (200_000).
23
- * 2. Suffix heuristic — a model id containing `1m` (case-insensitive) is
24
- * treated as 1M-context.
25
- * 3. Explicit allowlist — known 1M Claude model ids
26
- * (ONE_MILLION_CONTEXT_MODELS).
27
- * 4. Everything else → 200_000 (the safe default).
28
- *
29
- * Callers MAY additionally infer ≥1M from the observed token count: if
30
- * `contextTokens > DEFAULT_CONTEXT_WINDOW_TOKENS`, the model cannot be a
31
- * 200K model and must be ≥1M (see `readClaudeTranscriptEstimate`).
32
- *
33
- * This is the HEURISTIC layer only. Explicit user overrides sit ABOVE it —
34
- * see `resolveContextWindow`, which is what the probe actually calls.
35
- */
36
- export declare function modelContextWindowTokens(model: string): number;
2
+ import { resolveClaudeTranscriptPath } from './claude-code-local-state.js';
3
+ import { modelContextWindowTokens, resolveClaudeModelFromEnv } from './claude-code-model-window.js';
4
+ export { resolveClaudeTranscriptPath };
5
+ export { modelContextWindowTokens, resolveClaudeModelFromEnv };
37
6
  /**
38
7
  * the context-window size in tokens. Third-party / proxied models whose id
39
8
  * carries no `[1M]` suffix (and is absent from the hardcoded allowlist) are
@@ -1,7 +1,16 @@
1
- import { closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, statSync } from 'node:fs';
1
+ import { closeSync, openSync, readSync, statSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { join, resolve } from 'node:path';
4
4
  import { claudeCodeSubAgentDispatcher } from '../../dispatch/sub-agent-dispatcher.js';
5
+ import { findTranscriptJsonl, readClaudeStatuslinePercent, resolveClaudeTranscriptPath } from './claude-code-local-state.js';
6
+ import { DEFAULT_CONTEXT_WINDOW_TOKENS, ONE_MILLION_CONTEXT_TOKENS, modelContextWindowTokens, resolveClaudeModelFromEnv } from './claude-code-model-window.js';
7
+ // Re-exported so the public surface of this module is unchanged by the split:
8
+ // the readers live in `claude-code-local-state.ts` now, but every existing
9
+ // importer keeps resolving them from here.
10
+ export { resolveClaudeTranscriptPath };
11
+ // Likewise the model-id / window-size pair, which moved into
12
+ // `claude-code-model-window.ts` to make room for `mcpInstall` below.
13
+ export { modelContextWindowTokens, resolveClaudeModelFromEnv };
5
14
  /**
6
15
  * Claude Code adapter —— peaks-loop 的"起源 IDE"。
7
16
  *
@@ -17,178 +26,8 @@ import { claudeCodeSubAgentDispatcher } from '../../dispatch/sub-agent-dispatche
17
26
  *
18
27
  * 不可消除的 per-IDE 字段(见 tech-doc.md §1.3)。
19
28
  */
20
- /**
21
- * Read Claude Code's statusline state file
22
- * (`~/.claude/statusline-state.json`) and parse a context-percent key.
23
- * Moved from the generic reader in slice
24
- * only in the Claude Code adapter.
25
- */
26
- function readClaudeStatuslinePercent() {
27
- const path = join(homedir(), '.claude', 'statusline-state.json');
28
- if (!existsSync(path))
29
- return null;
30
- try {
31
- const json = JSON.parse(readFileSync(path, 'utf8'));
32
- const candidates = ['contextPercent', 'context_usage_percent', 'contextPercentUsed'];
33
- for (const key of candidates) {
34
- const raw = json[key];
35
- if (typeof raw === 'number' && Number.isFinite(raw)) {
36
- return raw > 1.5 ? raw / 100 : Math.max(0, Math.min(1, raw));
37
- }
38
- }
39
- }
40
- catch (err) {
41
- // TODO(g2): legacy silent catch — now narrows to IO errors only (grace: 1 minor release, v2.14.0)
42
- if (err instanceof ReferenceError)
43
- throw err; // surface module-load bugs
44
- if (err instanceof SyntaxError)
45
- throw err; // surface parse bugs (e.g. broken statusline JSON)
46
- return null; // only swallow IO errors
47
- }
48
- return null;
49
- }
50
- /**
51
- * Recursive search for `<outerSessionId>.jsonl` under `projectsDir`. The
52
- * Mac layout encodes the cwd as a single hash directory; on Mac Claude Code
53
- * nests the transcript under that hash with an extra level of subdirectory we
54
- * cannot predict ahead of time. A flat readdir misses that branch and returns
55
- * null — the silent-failure mode this recursion closes.
56
- *
57
- * Moved from the generic reader in slice
58
- * session id (Claude Code names its transcript by the outer session UUID),
59
- * NOT the peaks session id.
60
- */
61
- function findTranscriptJsonl(projectsDir, outerSessionId) {
62
- if (!existsSync(projectsDir))
63
- return null;
64
- try {
65
- const stack = [projectsDir];
66
- while (stack.length > 0) {
67
- const dir = stack.pop();
68
- if (dir === undefined)
69
- break;
70
- const entries = readdirSync(dir, { withFileTypes: true });
71
- for (const entry of entries) {
72
- const full = join(dir, entry.name);
73
- if (entry.isDirectory()) {
74
- stack.push(full);
75
- }
76
- else if (entry.isFile() && entry.name === `${outerSessionId}.jsonl`) {
77
- return full;
78
- }
79
- }
80
- }
81
- }
82
- catch (err) {
83
- // TODO(g2): legacy silent catch — now narrows to IO errors only (grace: 1 minor release, v2.14.0)
84
- if (err instanceof ReferenceError)
85
- throw err; // surface module-load bugs
86
- if (err instanceof SyntaxError)
87
- throw err; // surface parse bugs
88
- return null; // only swallow IO errors
89
- }
90
- return null;
91
- }
92
- /**
93
- * Resolve the absolute path of a Claude Code transcript jsonl by OUTER
94
- * session id, searching `~/.claude/projects/**` recursively.
95
- *
96
- * `peaks code context-audit` reuses THIS locator instead of re-implementing
97
- * the recursive find. Returns `null` when the transcript does not exist —
98
- * callers MUST treat that as "unavailable", never as an error.
99
- */
100
- export function resolveClaudeTranscriptPath(outerSessionId, projectsDir = join(homedir(), '.claude', 'projects')) {
101
- if (typeof outerSessionId !== 'string' || outerSessionId.length === 0)
102
- return null;
103
- return findTranscriptJsonl(projectsDir, outerSessionId);
104
- }
105
- /** 1M-context window size in tokens (documented single choice: 1,000,000). */
106
- const ONE_MILLION_CONTEXT_TOKENS = 1_000_000;
107
- /** Safe-default (non-1M) context window size in tokens. */
108
- const DEFAULT_CONTEXT_WINDOW_TOKENS = 200_000;
109
29
  /** Reverse-scan chunk size in bytes (keeps memory bounded on multi-MB transcripts). */
110
30
  const TRANSCRIPT_SCAN_CHUNK_BYTES = 64 * 1024;
111
- /**
112
- * Known 1M-context Claude model id prefixes whose ids do NOT carry a `1m`
113
- * suffix (e.g. `claude-sonnet-4-5-20250929`). The substring match is
114
- * intentionally generous — every `claude-sonnet-4*` / `claude-opus-4*`
115
- * variant is 1M-context.
116
- */
117
- const ONE_MILLION_CONTEXT_MODELS = ['claude-opus-4', 'claude-sonnet-4'];
118
- /**
119
- * Claude Code runtime env vars that may carry the currently-active model id,
120
- * in documented precedence order (first non-empty value wins):
121
- * 1. ANTHROPIC_MODEL — explicit per-run model override
122
- * 2. ANTHROPIC_DEFAULT_OPUS_MODEL — default Opus fallback
123
- * 3. ANTHROPIC_DEFAULT_SONNET_MODEL — default Sonnet fallback
124
- * 4. ANTHROPIC_DEFAULT_HAIKU_MODEL — default Haiku fallback
125
- * 5. ANTHROPIC_DEFAULT_FABLE_MODEL — default Fable fallback
126
- * 6. CLAUDE_CODE_SUBAGENT_MODEL — sub-agent model (used when the others
127
- * are absent, e.g. a sub-agent-only env)
128
- *
129
- * Why env-first: the transcript's `message.model` often drops Claude Code's
130
- * `[1M]` / `[200K]` suffix marker (observed: `deepseek-v4-flash`), while the
131
- * runtime env vars above carry it (`deepseek-v4-flash[1M]`). Reading them
132
- * first lets the window resolver see the true context window. This family is
133
- * vendor-specific, so it lives ONLY in the claude-code adapter.
134
- */
135
- const CLAUDE_CODE_MODEL_ENV_VARS = [
136
- 'ANTHROPIC_MODEL',
137
- 'ANTHROPIC_DEFAULT_OPUS_MODEL',
138
- 'ANTHROPIC_DEFAULT_SONNET_MODEL',
139
- 'ANTHROPIC_DEFAULT_HAIKU_MODEL',
140
- 'ANTHROPIC_DEFAULT_FABLE_MODEL',
141
- 'CLAUDE_CODE_SUBAGENT_MODEL'
142
- ];
143
- /**
144
- * Resolve the currently-active Claude Code model id from a runtime env map.
145
- * Returns the first non-empty `CLAUDE_CODE_MODEL_ENV_VARS` value (trimmed), or
146
- * `undefined` when none is present. Pure + exported for tests; the caller falls
147
- * back to the transcript `message.model` when this returns undefined.
148
- */
149
- export function resolveClaudeModelFromEnv(env) {
150
- if (!env)
151
- return undefined;
152
- for (const name of CLAUDE_CODE_MODEL_ENV_VARS) {
153
- const value = env[name];
154
- if (typeof value === 'string') {
155
- const trimmed = value.trim();
156
- if (trimmed.length > 0)
157
- return trimmed;
158
- }
159
- }
160
- return undefined;
161
- }
162
- /**
163
- * Model-aware context-window size in tokens.
164
- *
165
- * Detection rule (documented):
166
- * 1. Empty / unknown model → DEFAULT_CONTEXT_WINDOW_TOKENS (200_000).
167
- * 2. Suffix heuristic — a model id containing `1m` (case-insensitive) is
168
- * treated as 1M-context.
169
- * 3. Explicit allowlist — known 1M Claude model ids
170
- * (ONE_MILLION_CONTEXT_MODELS).
171
- * 4. Everything else → 200_000 (the safe default).
172
- *
173
- * Callers MAY additionally infer ≥1M from the observed token count: if
174
- * `contextTokens > DEFAULT_CONTEXT_WINDOW_TOKENS`, the model cannot be a
175
- * 200K model and must be ≥1M (see `readClaudeTranscriptEstimate`).
176
- *
177
- * This is the HEURISTIC layer only. Explicit user overrides sit ABOVE it —
178
- * see `resolveContextWindow`, which is what the probe actually calls.
179
- */
180
- export function modelContextWindowTokens(model) {
181
- const m = model.trim().toLowerCase();
182
- if (m.length === 0)
183
- return DEFAULT_CONTEXT_WINDOW_TOKENS;
184
- if (m.includes('1m'))
185
- return ONE_MILLION_CONTEXT_TOKENS;
186
- for (const known of ONE_MILLION_CONTEXT_MODELS) {
187
- if (m.includes(known))
188
- return ONE_MILLION_CONTEXT_TOKENS;
189
- }
190
- return DEFAULT_CONTEXT_WINDOW_TOKENS;
191
- }
192
31
  /**
193
32
  * the context-window size in tokens. Third-party / proxied models whose id
194
33
  * carries no `[1M]` suffix (and is absent from the hardcoded allowlist) are
@@ -602,6 +441,44 @@ export const CLAUDE_CODE_ADAPTER = {
602
441
  envVar: 'CLAUDE_PROJECT_DIR',
603
442
  hookEvent: 'PreToolUse',
604
443
  toolMatcher: 'Bash',
444
+ // BOTH channels a peaks MCP server can reach this harness through. The hook
445
+ // entry installed below covers the pair, and the pre-tool branch matches
446
+ // against the pair; one template would leave the other channel unenforced and
447
+ // nobody would notice, because a matcher that never fires looks like a clean
448
+ // call.
449
+ mcp: {
450
+ toolNameTemplates: ['mcp__peaks__*', 'mcp__plugin_peaks-loop_peaks__*']
451
+ },
452
+ // How this harness REGISTERS a peaks MCP server. The entry
453
+ // is the harness's OWN verb, not a hand-edit of its config file, which is
454
+ // full of the user's state (spec §8.3). The shape is recorded in spec §13 —
455
+ // it is read off that table, not invented here:
456
+ // - the `--` before the server argv is LOAD-BEARING: everything after it is
457
+ // passed to the server verbatim, otherwise the harness reads the server's
458
+ // own flags as its own;
459
+ // - the verb is NOT idempotent — same name, same scope fails, with no
460
+ // `--force` and no `update` — which is why the engine removes first.
461
+ // `scopes` is `user` alone: that is the scope a peaks install writes (spec
462
+ // §8.4), and sweeping a scope peaks never wrote would delete a user's own
463
+ // entry under the same name.
464
+ mcpInstall: {
465
+ serverName: 'peaks',
466
+ scopes: ['user'],
467
+ targetScope: 'user',
468
+ addArgv: [
469
+ 'claude',
470
+ 'mcp',
471
+ 'add',
472
+ '--scope',
473
+ '<scope>',
474
+ '--transport',
475
+ 'stdio',
476
+ '<name>',
477
+ '--',
478
+ '<server-argv>'
479
+ ],
480
+ removeArgv: ['claude', 'mcp', 'remove', '<name>', '--scope', '<scope>']
481
+ },
605
482
  // Slice #009: Claude Code uses the `Task` tool for sub-agent dispatch.
606
483
  // The CLI calls `claudeCodeSubAgentDispatcher.buildToolCall` to construct
607
484
  // the exact args shape the `Task` tool expects.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Read Claude Code's statusline state file
3
+ * (`~/.claude/statusline-state.json`) and parse a context-percent key.
4
+ * Moved from the generic reader in slice
5
+ * only in the Claude Code adapter.
6
+ */
7
+ export declare function readClaudeStatuslinePercent(): number | null;
8
+ /**
9
+ * Recursive search for `<outerSessionId>.jsonl` under `projectsDir`. The
10
+ * Mac layout encodes the cwd as a single hash directory; on Mac Claude Code
11
+ * nests the transcript under that hash with an extra level of subdirectory we
12
+ * cannot predict ahead of time. A flat readdir misses that branch and returns
13
+ * null — the silent-failure mode this recursion closes.
14
+ *
15
+ * Moved from the generic reader in slice
16
+ * session id (Claude Code names its transcript by the outer session UUID),
17
+ * NOT the peaks session id.
18
+ */
19
+ export declare function findTranscriptJsonl(projectsDir: string, outerSessionId: string): string | null;
20
+ /**
21
+ * Resolve the absolute path of a Claude Code transcript jsonl by OUTER
22
+ * session id, searching `~/.claude/projects/**` recursively.
23
+ *
24
+ * `peaks code context-audit` reuses THIS locator instead of re-implementing
25
+ * the recursive find. Returns `null` when the transcript does not exist —
26
+ * callers MUST treat that as "unavailable", never as an error.
27
+ */
28
+ export declare function resolveClaudeTranscriptPath(outerSessionId: string, projectsDir?: string): string | null;