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,112 @@
1
+ // src/services/ide/adapters/claude-code-local-state.ts
2
+ //
3
+ // The readers for Claude Code's OWN on-disk state — its statusline state file and
4
+ // its transcript directory layout. Split out of `claude-code-adapter.ts`,
5
+ // verbatim and with one import apiece, because that file sits far past the
6
+ // 300-line cap and the repo's size ratchet only ever goes down: the adapter
7
+ // needs room for new declared surface, and a self-contained reader leaving is
8
+ // how a file earns that room instead of raising the ceiling.
9
+ //
10
+ // Nothing here is peaks-loop state. Every path below belongs to the harness, and
11
+ // every function tolerates its absence by answering `null` — a machine without a
12
+ // statusline file is not a broken machine.
13
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
14
+ import { homedir } from 'node:os';
15
+ import { join } from 'node:path';
16
+ /**
17
+ * Above this, a stored value is a percentage rather than a 0–1 fraction. Both
18
+ * spellings occur in the wild, and nothing needs to know which one the harness
19
+ * picked.
20
+ */
21
+ const PERCENT_VERSUS_FRACTION = 1.5;
22
+ /**
23
+ * Read Claude Code's statusline state file
24
+ * (`~/.claude/statusline-state.json`) and parse a context-percent key.
25
+ * Moved from the generic reader in slice
26
+ * only in the Claude Code adapter.
27
+ */
28
+ export function readClaudeStatuslinePercent() {
29
+ const path = join(homedir(), '.claude', 'statusline-state.json');
30
+ if (!existsSync(path))
31
+ return null;
32
+ try {
33
+ const json = JSON.parse(readFileSync(path, 'utf8'));
34
+ const candidates = ['contextPercent', 'context_usage_percent', 'contextPercentUsed'];
35
+ for (const key of candidates) {
36
+ const raw = json[key];
37
+ if (typeof raw === 'number' && Number.isFinite(raw)) {
38
+ return raw > PERCENT_VERSUS_FRACTION ? raw / 100 : Math.max(0, Math.min(1, raw));
39
+ }
40
+ }
41
+ }
42
+ catch (err) {
43
+ // TODO(g2): legacy silent catch — now narrows to IO errors only (grace: 1 minor release, v2.14.0)
44
+ if (err instanceof ReferenceError)
45
+ throw err; // surface module-load bugs
46
+ if (err instanceof SyntaxError)
47
+ throw err; // surface parse bugs (e.g. broken statusline JSON)
48
+ return null; // only swallow IO errors
49
+ }
50
+ return null;
51
+ }
52
+ /** One directory's worth of the walk: recurse into directories, return a matching file. */
53
+ function scanForFile(dir, fileName, pending) {
54
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
55
+ const full = join(dir, entry.name);
56
+ if (entry.isDirectory()) {
57
+ pending.push(full);
58
+ }
59
+ else if (entry.isFile() && entry.name === fileName) {
60
+ return full;
61
+ }
62
+ }
63
+ return null;
64
+ }
65
+ /**
66
+ * Recursive search for `<outerSessionId>.jsonl` under `projectsDir`. The
67
+ * Mac layout encodes the cwd as a single hash directory; on Mac Claude Code
68
+ * nests the transcript under that hash with an extra level of subdirectory we
69
+ * cannot predict ahead of time. A flat readdir misses that branch and returns
70
+ * null — the silent-failure mode this recursion closes.
71
+ *
72
+ * Moved from the generic reader in slice
73
+ * session id (Claude Code names its transcript by the outer session UUID),
74
+ * NOT the peaks session id.
75
+ */
76
+ export function findTranscriptJsonl(projectsDir, outerSessionId) {
77
+ if (!existsSync(projectsDir))
78
+ return null;
79
+ const pending = [projectsDir];
80
+ try {
81
+ while (pending.length > 0) {
82
+ const dir = pending.pop();
83
+ if (dir === undefined)
84
+ continue;
85
+ const found = scanForFile(dir, `${outerSessionId}.jsonl`, pending);
86
+ if (found !== null)
87
+ return found;
88
+ }
89
+ }
90
+ catch (err) {
91
+ // TODO(g2): legacy silent catch — now narrows to IO errors only (grace: 1 minor release, v2.14.0)
92
+ if (err instanceof ReferenceError)
93
+ throw err; // surface module-load bugs
94
+ if (err instanceof SyntaxError)
95
+ throw err; // surface parse bugs
96
+ return null; // only swallow IO errors
97
+ }
98
+ return null;
99
+ }
100
+ /**
101
+ * Resolve the absolute path of a Claude Code transcript jsonl by OUTER
102
+ * session id, searching `~/.claude/projects/**` recursively.
103
+ *
104
+ * `peaks code context-audit` reuses THIS locator instead of re-implementing
105
+ * the recursive find. Returns `null` when the transcript does not exist —
106
+ * callers MUST treat that as "unavailable", never as an error.
107
+ */
108
+ export function resolveClaudeTranscriptPath(outerSessionId, projectsDir = join(homedir(), '.claude', 'projects')) {
109
+ if (typeof outerSessionId !== 'string' || outerSessionId.length === 0)
110
+ return null;
111
+ return findTranscriptJsonl(projectsDir, outerSessionId);
112
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 1M-context window size in tokens (documented single choice: 1,000,000).
3
+ * EXPORTED because the adapter's late 1M rescue (`resolveContextWindowTokens`)
4
+ * names it too — one number, one declaration, rather than a copy that can drift.
5
+ */
6
+ export declare const ONE_MILLION_CONTEXT_TOKENS = 1000000;
7
+ /**
8
+ * Safe-default (non-1M) context window size in tokens. Exported for the same
9
+ * reason: `resolveContextWindow` compares the heuristic against it.
10
+ */
11
+ export declare const DEFAULT_CONTEXT_WINDOW_TOKENS = 200000;
12
+ /**
13
+ * Resolve the currently-active Claude Code model id from a runtime env map.
14
+ * Returns the first non-empty `CLAUDE_CODE_MODEL_ENV_VARS` value (trimmed), or
15
+ * `undefined` when none is present. Pure + exported for tests; the caller falls
16
+ * back to the transcript `message.model` when this returns undefined.
17
+ */
18
+ export declare function resolveClaudeModelFromEnv(env: NodeJS.ProcessEnv | undefined): string | undefined;
19
+ /**
20
+ * Model-aware context-window size in tokens.
21
+ *
22
+ * Detection rule (documented):
23
+ * 1. Empty / unknown model → DEFAULT_CONTEXT_WINDOW_TOKENS (200_000).
24
+ * 2. Suffix heuristic — a model id containing `1m` (case-insensitive) is
25
+ * treated as 1M-context.
26
+ * 3. Explicit allowlist — known 1M Claude model ids
27
+ * (ONE_MILLION_CONTEXT_MODELS).
28
+ * 4. Everything else → 200_000 (the safe default).
29
+ *
30
+ * Callers MAY additionally infer ≥1M from the observed token count: if
31
+ * `contextTokens > DEFAULT_CONTEXT_WINDOW_TOKENS`, the model cannot be a
32
+ * 200K model and must be ≥1M (see `readClaudeTranscriptEstimate`).
33
+ *
34
+ * This is the HEURISTIC layer only. Explicit user overrides sit ABOVE it —
35
+ * see `resolveContextWindow`, which is what the probe actually calls.
36
+ */
37
+ export declare function modelContextWindowTokens(model: string): number;
@@ -0,0 +1,113 @@
1
+ // src/services/ide/adapters/claude-code-model-window.ts
2
+ //
3
+ // Which Claude Code model is active, and how many tokens its context window
4
+ // holds. Those are the two questions `resolveClaudeModelFromEnv` and
5
+ // `modelContextWindowTokens` answer, and they are the pair that left
6
+ // `claude-code-adapter.ts`: that file is far past the
7
+ // 300-line cap, the repo's size ratchet only ever goes down, and the adapter
8
+ // needed room for its `mcpInstall` declaration. Same technique, and the same
9
+ // reason, as the `claude-code-local-state.ts` split before it — a self-contained
10
+ // responsibility leaves, instead of the ceiling going up.
11
+ //
12
+ // WHY THIS BLOCK AND NOT ANOTHER. Both functions are PURE: the env map is an
13
+ // argument, and nothing here reads a file, an env var or a process global. So
14
+ // the move needs no rewiring in either direction — the adapter imports both
15
+ // back and nothing here reaches for the adapter — which is what keeps the move
16
+ // a move. It is also the one block in that file whose eslint findings are zero,
17
+ // which a receiving file with no baseline row must be.
18
+ //
19
+ // The vendor spellings below (`ANTHROPIC_*`, `claude-opus-4`) are Claude Code's
20
+ // own. An adapter is the only place they belong (spec §13), which is why this is
21
+ // a second adapter module and not a shared one.
22
+ /**
23
+ * 1M-context window size in tokens (documented single choice: 1,000,000).
24
+ * EXPORTED because the adapter's late 1M rescue (`resolveContextWindowTokens`)
25
+ * names it too — one number, one declaration, rather than a copy that can drift.
26
+ */
27
+ export const ONE_MILLION_CONTEXT_TOKENS = 1_000_000;
28
+ /**
29
+ * Safe-default (non-1M) context window size in tokens. Exported for the same
30
+ * reason: `resolveContextWindow` compares the heuristic against it.
31
+ */
32
+ export const DEFAULT_CONTEXT_WINDOW_TOKENS = 200_000;
33
+ /**
34
+ * Known 1M-context Claude model id prefixes whose ids do NOT carry a `1m`
35
+ * suffix (e.g. `claude-sonnet-4-5-20250929`). The substring match is
36
+ * intentionally generous — every `claude-sonnet-4*` / `claude-opus-4*`
37
+ * variant is 1M-context.
38
+ */
39
+ const ONE_MILLION_CONTEXT_MODELS = ['claude-opus-4', 'claude-sonnet-4'];
40
+ /**
41
+ * Claude Code runtime env vars that may carry the currently-active model id,
42
+ * in documented precedence order (first non-empty value wins):
43
+ * 1. ANTHROPIC_MODEL — explicit per-run model override
44
+ * 2. ANTHROPIC_DEFAULT_OPUS_MODEL — default Opus fallback
45
+ * 3. ANTHROPIC_DEFAULT_SONNET_MODEL — default Sonnet fallback
46
+ * 4. ANTHROPIC_DEFAULT_HAIKU_MODEL — default Haiku fallback
47
+ * 5. ANTHROPIC_DEFAULT_FABLE_MODEL — default Fable fallback
48
+ * 6. CLAUDE_CODE_SUBAGENT_MODEL — sub-agent model (used when the others
49
+ * are absent, e.g. a sub-agent-only env)
50
+ *
51
+ * Why env-first: the transcript's `message.model` often drops Claude Code's
52
+ * `[1M]` / `[200K]` suffix marker (observed: `deepseek-v4-flash`), while the
53
+ * runtime env vars above carry it (`deepseek-v4-flash[1M]`). Reading them
54
+ * first lets the window resolver see the true context window. This family is
55
+ * vendor-specific, so it lives ONLY in the claude-code adapter.
56
+ */
57
+ const CLAUDE_CODE_MODEL_ENV_VARS = [
58
+ 'ANTHROPIC_MODEL',
59
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL',
60
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL',
61
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL',
62
+ 'ANTHROPIC_DEFAULT_FABLE_MODEL',
63
+ 'CLAUDE_CODE_SUBAGENT_MODEL'
64
+ ];
65
+ /**
66
+ * Resolve the currently-active Claude Code model id from a runtime env map.
67
+ * Returns the first non-empty `CLAUDE_CODE_MODEL_ENV_VARS` value (trimmed), or
68
+ * `undefined` when none is present. Pure + exported for tests; the caller falls
69
+ * back to the transcript `message.model` when this returns undefined.
70
+ */
71
+ export function resolveClaudeModelFromEnv(env) {
72
+ if (!env)
73
+ return undefined;
74
+ for (const name of CLAUDE_CODE_MODEL_ENV_VARS) {
75
+ const value = env[name];
76
+ if (typeof value === 'string') {
77
+ const trimmed = value.trim();
78
+ if (trimmed.length > 0)
79
+ return trimmed;
80
+ }
81
+ }
82
+ return undefined;
83
+ }
84
+ /**
85
+ * Model-aware context-window size in tokens.
86
+ *
87
+ * Detection rule (documented):
88
+ * 1. Empty / unknown model → DEFAULT_CONTEXT_WINDOW_TOKENS (200_000).
89
+ * 2. Suffix heuristic — a model id containing `1m` (case-insensitive) is
90
+ * treated as 1M-context.
91
+ * 3. Explicit allowlist — known 1M Claude model ids
92
+ * (ONE_MILLION_CONTEXT_MODELS).
93
+ * 4. Everything else → 200_000 (the safe default).
94
+ *
95
+ * Callers MAY additionally infer ≥1M from the observed token count: if
96
+ * `contextTokens > DEFAULT_CONTEXT_WINDOW_TOKENS`, the model cannot be a
97
+ * 200K model and must be ≥1M (see `readClaudeTranscriptEstimate`).
98
+ *
99
+ * This is the HEURISTIC layer only. Explicit user overrides sit ABOVE it —
100
+ * see `resolveContextWindow`, which is what the probe actually calls.
101
+ */
102
+ export function modelContextWindowTokens(model) {
103
+ const m = model.trim().toLowerCase();
104
+ if (m.length === 0)
105
+ return DEFAULT_CONTEXT_WINDOW_TOKENS;
106
+ if (m.includes('1m'))
107
+ return ONE_MILLION_CONTEXT_TOKENS;
108
+ for (const known of ONE_MILLION_CONTEXT_MODELS) {
109
+ if (m.includes(known))
110
+ return ONE_MILLION_CONTEXT_TOKENS;
111
+ }
112
+ return DEFAULT_CONTEXT_WINDOW_TOKENS;
113
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The REGISTRATION profile of one harness's MCP entry point (spec §8.3).
3
+ *
4
+ * WHY THIS IS NOT `IdeMcpProfile`. That profile (on `IdeAdapter.mcp`) answers
5
+ * "how does this harness SPELL a peaks tool name", which the pre-tool branch
6
+ * needs. This one answers "how does this harness register a server", which only
7
+ * the distribution path needs. One field would conflate two questions asked by
8
+ * two callers that never run together.
9
+ *
10
+ * WHY IT DECLARES ARGV AND NOT A COMMAND STRING. Registering is the HARNESS's
11
+ * own operation: `peaks mcp install` calls the harness's entry point rather than
12
+ * hand-editing the harness's config file, because that file is full of the
13
+ * user's state and hand-editing it is neither safe nor version-stable (§8.3).
14
+ * The entry is declared as an ARRAY with three placeholders, so the engine that
15
+ * consumes it substitutes values into an array and never builds a command line.
16
+ *
17
+ * WHY add AND remove ARE BOTH DECLARED. A harness's register verb is not
18
+ * idempotent — same name, same scope, and it fails, with no `--force` and no
19
+ * `update` — so the install is delete-then-add (§8.3). Both halves are therefore
20
+ * part of the contract, and `scopes` exists because the entry can already sit in
21
+ * more than one of them: the delete is a sweep, not a targeted call.
22
+ *
23
+ * WHAT IS DELIBERATELY ABSENT: any harness name, any platform name, and any
24
+ * branch on either. A vendor's own spellings belong in its adapter, which is the
25
+ * only module that fills this type in.
26
+ */
27
+ /** Placeholder for the server key the harness files peaks under. */
28
+ export declare const MCP_NAME_PLACEHOLDER = "<name>";
29
+ /** Placeholder for one scope the entry can live in; substituted once per removal. */
30
+ export declare const MCP_SCOPE_PLACEHOLDER = "<scope>";
31
+ /** Placeholder for the launched server argv — the command and every token after it. */
32
+ export declare const MCP_SERVER_ARGV_PLACEHOLDER = "<server-argv>";
33
+ /** The placeholders an argv template may carry, and nothing else may look like one. */
34
+ export declare const MCP_ARGV_PLACEHOLDERS: readonly ["<name>", "<scope>", "<server-argv>"];
35
+ /**
36
+ * How one harness registers, and unregisters, a peaks MCP server.
37
+ *
38
+ * Optional on `IdeAdapter`, like `skillInstall?` / `standardsProfile?`: an
39
+ * adapter that omits it is declaring "this harness has no registration entry
40
+ * peaks may call", and every consumer reads that as "nothing to do" rather than
41
+ * as an error.
42
+ */
43
+ export interface IdeMcpInstallProfile {
44
+ /** The server key this harness files peaks under. The install touches only this one. */
45
+ readonly serverName: string;
46
+ /**
47
+ * Every scope the entry can live in. The delete sweeps all of them, because a
48
+ * previous install may have written a different one and the harness's own
49
+ * removal fails on the scope that is empty.
50
+ */
51
+ readonly scopes: readonly string[];
52
+ /** The scope the entry is (re-)written to. Must be one of `scopes`. */
53
+ readonly targetScope: string;
54
+ /** argv template that registers the server. Carries `<name>` and `<server-argv>`. */
55
+ readonly addArgv: readonly string[];
56
+ /** argv template that unregisters it. Carries `<name>` and `<scope>`. */
57
+ readonly removeArgv: readonly string[];
58
+ }
59
+ /** The values an argv template is expanded with. */
60
+ export interface McpArgvValues {
61
+ readonly serverName: string;
62
+ readonly scope: string;
63
+ readonly serverArgv: readonly string[];
64
+ }
65
+ /**
66
+ * A declaration that cannot drive an install is a defect in the adapter, and it
67
+ * is reported as one rather than silently producing an argv the harness will
68
+ * reject. That is the whole argument for throwing here: a profile missing its
69
+ * server-argv placeholder would install a registration that launches nothing,
70
+ * and nothing would say so.
71
+ */
72
+ export declare function assertMcpInstallProfile(profile: IdeMcpInstallProfile): void;
73
+ /**
74
+ * Expand one argv template. A placeholder that stands for SEVERAL tokens expands
75
+ * to all of them, which is why this returns an array and why the launched server
76
+ * argv is passed as one: the caller never joins and never splits.
77
+ */
78
+ export declare function expandMcpArgv(argv: readonly string[], values: McpArgvValues): readonly string[];
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The REGISTRATION profile of one harness's MCP entry point (spec §8.3).
3
+ *
4
+ * WHY THIS IS NOT `IdeMcpProfile`. That profile (on `IdeAdapter.mcp`) answers
5
+ * "how does this harness SPELL a peaks tool name", which the pre-tool branch
6
+ * needs. This one answers "how does this harness register a server", which only
7
+ * the distribution path needs. One field would conflate two questions asked by
8
+ * two callers that never run together.
9
+ *
10
+ * WHY IT DECLARES ARGV AND NOT A COMMAND STRING. Registering is the HARNESS's
11
+ * own operation: `peaks mcp install` calls the harness's entry point rather than
12
+ * hand-editing the harness's config file, because that file is full of the
13
+ * user's state and hand-editing it is neither safe nor version-stable (§8.3).
14
+ * The entry is declared as an ARRAY with three placeholders, so the engine that
15
+ * consumes it substitutes values into an array and never builds a command line.
16
+ *
17
+ * WHY add AND remove ARE BOTH DECLARED. A harness's register verb is not
18
+ * idempotent — same name, same scope, and it fails, with no `--force` and no
19
+ * `update` — so the install is delete-then-add (§8.3). Both halves are therefore
20
+ * part of the contract, and `scopes` exists because the entry can already sit in
21
+ * more than one of them: the delete is a sweep, not a targeted call.
22
+ *
23
+ * WHAT IS DELIBERATELY ABSENT: any harness name, any platform name, and any
24
+ * branch on either. A vendor's own spellings belong in its adapter, which is the
25
+ * only module that fills this type in.
26
+ */
27
+ /** Placeholder for the server key the harness files peaks under. */
28
+ export const MCP_NAME_PLACEHOLDER = '<name>';
29
+ /** Placeholder for one scope the entry can live in; substituted once per removal. */
30
+ export const MCP_SCOPE_PLACEHOLDER = '<scope>';
31
+ /** Placeholder for the launched server argv — the command and every token after it. */
32
+ export const MCP_SERVER_ARGV_PLACEHOLDER = '<server-argv>';
33
+ /** The placeholders an argv template may carry, and nothing else may look like one. */
34
+ export const MCP_ARGV_PLACEHOLDERS = [
35
+ MCP_NAME_PLACEHOLDER,
36
+ MCP_SCOPE_PLACEHOLDER,
37
+ MCP_SERVER_ARGV_PLACEHOLDER
38
+ ];
39
+ /**
40
+ * One template's structural requirements: it must carry something, its
41
+ * bracket-shaped tokens must be ones the engine knows how to substitute, and the
42
+ * placeholders the caller needs in it must be there. An unknown placeholder is a
43
+ * refusal rather than a literal, because passing it through would hand the
44
+ * harness an argument that means nothing to it.
45
+ */
46
+ function assertArgvTemplate(label, argv, required, refuse) {
47
+ if (argv.length === 0)
48
+ refuse(`${label} is empty`);
49
+ for (const token of argv) {
50
+ const bracketShaped = token.startsWith('<') && token.endsWith('>');
51
+ const known = MCP_ARGV_PLACEHOLDERS.includes(token);
52
+ if (bracketShaped && !known)
53
+ refuse(`${label} carries unknown placeholder '${token}'`);
54
+ }
55
+ for (const placeholder of required) {
56
+ if (!argv.includes(placeholder))
57
+ refuse(`${label} does not carry ${placeholder}`);
58
+ }
59
+ }
60
+ /**
61
+ * A declaration that cannot drive an install is a defect in the adapter, and it
62
+ * is reported as one rather than silently producing an argv the harness will
63
+ * reject. That is the whole argument for throwing here: a profile missing its
64
+ * server-argv placeholder would install a registration that launches nothing,
65
+ * and nothing would say so.
66
+ */
67
+ export function assertMcpInstallProfile(profile) {
68
+ const refuse = (reason) => {
69
+ throw new Error(`IdeMcpInstallProfile is not installable: ${reason}`);
70
+ };
71
+ if (profile.serverName.length === 0)
72
+ refuse('serverName is empty');
73
+ if (profile.scopes.length === 0)
74
+ refuse('scopes is empty');
75
+ if (!profile.scopes.includes(profile.targetScope)) {
76
+ refuse(`targetScope '${profile.targetScope}' is not one of the declared scopes`);
77
+ }
78
+ assertArgvTemplate('addArgv', profile.addArgv, [MCP_NAME_PLACEHOLDER, MCP_SCOPE_PLACEHOLDER, MCP_SERVER_ARGV_PLACEHOLDER], refuse);
79
+ assertArgvTemplate('removeArgv', profile.removeArgv, [MCP_NAME_PLACEHOLDER, MCP_SCOPE_PLACEHOLDER], refuse);
80
+ }
81
+ /**
82
+ * Expand one argv template. A placeholder that stands for SEVERAL tokens expands
83
+ * to all of them, which is why this returns an array and why the launched server
84
+ * argv is passed as one: the caller never joins and never splits.
85
+ */
86
+ export function expandMcpArgv(argv, values) {
87
+ const expanded = [];
88
+ for (const token of argv) {
89
+ if (token === MCP_NAME_PLACEHOLDER) {
90
+ expanded.push(values.serverName);
91
+ continue;
92
+ }
93
+ if (token === MCP_SCOPE_PLACEHOLDER) {
94
+ expanded.push(values.scope);
95
+ continue;
96
+ }
97
+ if (token === MCP_SERVER_ARGV_PLACEHOLDER) {
98
+ expanded.push(...values.serverArgv);
99
+ continue;
100
+ }
101
+ expanded.push(token);
102
+ }
103
+ return expanded;
104
+ }
@@ -13,12 +13,13 @@
13
13
  */
14
14
  import type { SubAgentDispatcher } from '../dispatch/sub-agent-dispatcher.js';
15
15
  import type { ContextPercentFallbackInput, IdeCompactProfile } from './ide-compact-types.js';
16
+ import type { IdeMcpInstallProfile } from './ide-mcp-install-types.js';
16
17
  /**
17
18
  * Re-exported so every importer of `./ide-types.js` keeps seeing the whole
18
19
  * adapter type surface from the original path (b1 file-size campaign: the two
19
20
  * declarations now live in `./ide-compact-types.js`).
20
21
  */
21
- export type { ContextPercentFallbackInput, IdeCompactProfile };
22
+ export type { ContextPercentFallbackInput, IdeCompactProfile, IdeMcpInstallProfile };
22
23
  export type IdeId = 'claude-code' | 'trae' | 'codex' | 'cursor' | 'qoder' | 'tongyi-lingma' | 'hermes' | 'openclaw' | 'zcode';
23
24
  export interface IdeCapabilities {
24
25
  /** peaks gate enforce 是否适用该 IDE(必备) */
@@ -171,6 +172,49 @@ export interface IdeAdapter {
171
172
  * funnelling all vendor signal through this method.
172
173
  */
173
174
  readonly resolveCallerId: (env?: NodeJS.ProcessEnv) => string;
175
+ /**
176
+ * How this harness SPELLS the MCP tool names of a peaks server.
177
+ *
178
+ * Optional, like `skillInstall?` / `standardsProfile?` / `compact?`: an adapter
179
+ * that leaves it undefined declares "this harness has no MCP tool surface I can
180
+ * name", and every consumer treats that as "nothing to match" rather than as an
181
+ * error.
182
+ *
183
+ * It exists because one tool has SEVERAL spellings. A server registered
184
+ * directly and the same server arriving through a plugin produce different
185
+ * tool names, and an enforcement matcher written against one of them does not
186
+ * fire on the other — silently. Declaring them here is what lets the
187
+ * pre-tool branch and the installed hook entry agree on the full set.
188
+ *
189
+ * `resolveCallerId` above is the precedent for the placement rule: a vendor's
190
+ * naming convention is vendor signal, and vendor signal is funnelled through
191
+ * the adapter so no other module has to know it.
192
+ */
193
+ readonly mcp?: IdeMcpProfile;
194
+ /**
195
+ * How this harness REGISTERS an MCP server (spec §8.3), consumed by
196
+ * `peaks mcp install/uninstall`. Optional, like `mcp` above: leaving it
197
+ * undefined declares "no registration entry peaks may call", and the install
198
+ * reads that as "nothing to do". See `ide-mcp-install-types.ts`.
199
+ */
200
+ readonly mcpInstall?: IdeMcpInstallProfile;
201
+ }
202
+ /**
203
+ * The MCP naming profile of one harness.
204
+ *
205
+ * Deliberately ONE field. Templates both MATCH a concrete tool name and
206
+ * CONSTRUCT one, which is what lets a conformance test build a call for a
207
+ * channel it has never seen (spec §6.3) instead of restating the harness's
208
+ * naming in the test — a restatement that would go stale the moment a channel
209
+ * is added.
210
+ */
211
+ export interface IdeMcpProfile {
212
+ /**
213
+ * Tool-name templates. `*` is replaced by the bare MCP tool id, so
214
+ * `mcp__peaks__*` describes every tool of a server registered under the name
215
+ * `peaks` on the direct channel.
216
+ */
217
+ readonly toolNameTemplates: readonly string[];
174
218
  }
175
219
  /**
176
220
  * Per-IDE standards-file location + format profile. Used by the
@@ -0,0 +1,34 @@
1
+ import type { IdeAdapter, IdeMcpProfile } from './ide-types.js';
2
+ /** The placeholder an adapter's tool-name template uses for the bare tool id. */
3
+ export declare const TOOL_ID_PLACEHOLDER = "*";
4
+ /** A concrete tool name matched back to the adapter template that produced it. */
5
+ export interface McpToolMatch {
6
+ /** The bare tool id — the part the template's placeholder stood for. */
7
+ readonly toolId: string;
8
+ /** The template that matched, so a caller can report which channel fired. */
9
+ readonly template: string;
10
+ }
11
+ /** The declared profile, or an empty one for an adapter that opts out. */
12
+ export declare function mcpProfileOf(adapter: IdeAdapter): IdeMcpProfile | undefined;
13
+ /** Every tool-name template the adapter declares, in declaration order. */
14
+ export declare function mcpToolNameTemplates(adapter: IdeAdapter): readonly string[];
15
+ /** The concrete harness tool name for a tool id under one template. */
16
+ export declare function mcpToolName(template: string, toolId: string): string;
17
+ /**
18
+ * The tool id a concrete name carries under one template, or `undefined`. The
19
+ * template is anchored: an unanchored match would let a longer name smuggle the
20
+ * id (and would make `matchMcpToolName` order-dependent).
21
+ */
22
+ export declare function mcpToolIdFrom(template: string, toolName: string): string | undefined;
23
+ /**
24
+ * Match a concrete harness tool name against every template the adapter
25
+ * declares, in declaration order. This is the ONE entry point the enforcement
26
+ * branch uses, so "which channels are covered" is answered by the adapter alone.
27
+ */
28
+ export declare function matchMcpToolName(adapter: IdeAdapter, toolName: string): McpToolMatch | undefined;
29
+ /**
30
+ * The PreToolUse `matcher` source covering every declared channel, ready to be
31
+ * joined into a hook entry's matcher field. `*` becomes `.*` and everything else
32
+ * is escaped, so the source matches exactly the names the templates describe.
33
+ */
34
+ export declare function mcpHookMatcher(adapter: IdeAdapter): string | undefined;
@@ -0,0 +1,84 @@
1
+ // src/services/ide/mcp-tool-matcher.ts
2
+ //
3
+ // The vendor-translation layer for MCP tool NAMES (spec §6.3, §13).
4
+ //
5
+ // An MCP tool's name is harness-specific AND channel-specific: the very same
6
+ // `peaks_status` tool is spelled one way when the server is registered directly
7
+ // and another way when it arrives through a plugin. A matcher hard-coded against
8
+ // one spelling keeps working on the channel it was written for and SILENTLY
9
+ // stops firing on the other — the failure mode is an enforcement layer that
10
+ // reports nothing because it never ran. So the spellings are declared by the
11
+ // adapter (`IdeAdapter.mcp`) and this module is the only place that turns a
12
+ // declaration into (a) a match against a concrete tool name and (b) the
13
+ // PreToolUse matcher written into the harness's settings.
14
+ //
15
+ // Vendor names do not belong here: every string below is either generic syntax
16
+ // or comes from the adapter. The isolation zone lives in `adapters/`.
17
+ /** The placeholder an adapter's tool-name template uses for the bare tool id. */
18
+ export const TOOL_ID_PLACEHOLDER = '*';
19
+ /** The declared profile, or an empty one for an adapter that opts out. */
20
+ export function mcpProfileOf(adapter) {
21
+ return adapter.mcp;
22
+ }
23
+ /** Every tool-name template the adapter declares, in declaration order. */
24
+ export function mcpToolNameTemplates(adapter) {
25
+ return adapter.mcp?.toolNameTemplates ?? [];
26
+ }
27
+ /** Escape a literal segment so it cannot act as a pattern metacharacter. */
28
+ function escapeLiteral(value) {
29
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
30
+ }
31
+ /**
32
+ * Split a template on its placeholder. A template must carry EXACTLY one
33
+ * placeholder: zero would make the tool id unaddressable, and two would make the
34
+ * captured id ambiguous. Both are adapter bugs, so they throw with the template
35
+ * in the message rather than degrading to a matcher that matches nothing.
36
+ */
37
+ function splitTemplate(template) {
38
+ const parts = template.split(TOOL_ID_PLACEHOLDER);
39
+ if (parts.length !== 2 || parts[0] === undefined || parts[1] === undefined) {
40
+ throw new Error(`MCP tool-name template '${template}' must contain exactly one '${TOOL_ID_PLACEHOLDER}' placeholder.`);
41
+ }
42
+ return [parts[0], parts[1]];
43
+ }
44
+ /** The concrete harness tool name for a tool id under one template. */
45
+ export function mcpToolName(template, toolId) {
46
+ const [prefix, suffix] = splitTemplate(template);
47
+ return `${prefix}${toolId}${suffix}`;
48
+ }
49
+ /**
50
+ * The tool id a concrete name carries under one template, or `undefined`. The
51
+ * template is anchored: an unanchored match would let a longer name smuggle the
52
+ * id (and would make `matchMcpToolName` order-dependent).
53
+ */
54
+ export function mcpToolIdFrom(template, toolName) {
55
+ const [prefix, suffix] = splitTemplate(template);
56
+ const pattern = new RegExp(`^${escapeLiteral(prefix)}(.*)${escapeLiteral(suffix)}$`);
57
+ const matched = pattern.exec(toolName);
58
+ return matched?.[1];
59
+ }
60
+ /**
61
+ * Match a concrete harness tool name against every template the adapter
62
+ * declares, in declaration order. This is the ONE entry point the enforcement
63
+ * branch uses, so "which channels are covered" is answered by the adapter alone.
64
+ */
65
+ export function matchMcpToolName(adapter, toolName) {
66
+ for (const template of mcpToolNameTemplates(adapter)) {
67
+ const toolId = mcpToolIdFrom(template, toolName);
68
+ if (toolId !== undefined)
69
+ return { toolId, template };
70
+ }
71
+ return undefined;
72
+ }
73
+ /**
74
+ * The PreToolUse `matcher` source covering every declared channel, ready to be
75
+ * joined into a hook entry's matcher field. `*` becomes `.*` and everything else
76
+ * is escaped, so the source matches exactly the names the templates describe.
77
+ */
78
+ export function mcpHookMatcher(adapter) {
79
+ const sources = mcpToolNameTemplates(adapter).map((template) => {
80
+ const [prefix, suffix] = splitTemplate(template);
81
+ return `${escapeLiteral(prefix)}.*${escapeLiteral(suffix)}`;
82
+ });
83
+ return sources.length === 0 ? undefined : sources.join('|');
84
+ }
@@ -17,6 +17,13 @@ import process from 'node:process';
17
17
  function locateNpmCliScript(scriptFile) {
18
18
  const candidates = process.platform === 'win32'
19
19
  ? [
20
+ // Standard Windows layout — the installer, the zip, and
21
+ // nvm-for-windows all put `node_modules/npm` beside `node.exe`, so the
22
+ // install is ONE level up from `process.execPath`, not two. This is the
23
+ // `npm` belonging to the very `node` that will run the script, so it is
24
+ // tried first; the three entries after it are the backstop for an `npm`
25
+ // installed elsewhere.
26
+ join(process.execPath, '..', 'node_modules', 'npm', 'bin', scriptFile),
20
27
  join(process.execPath, '..', '..', 'node_modules', 'npm', 'bin', scriptFile),
21
28
  `C:/nvm4w/nodejs/node_modules/npm/bin/${scriptFile}`,
22
29
  `C:/Program Files/nodejs/node_modules/npm/bin/${scriptFile}`