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.
- package/.claude-plugin/marketplace.json +136 -47
- package/CHANGELOG.md +56 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/contracts/readonly-argv-whitelist.json +211 -0
- package/dist/cli/commands/audit-artifact-runners.d.ts +16 -0
- package/dist/cli/commands/audit-artifact-runners.js +170 -0
- package/dist/cli/commands/audit-command-shared.d.ts +97 -0
- package/dist/cli/commands/audit-command-shared.js +60 -0
- package/dist/cli/commands/audit-commands.d.ts +7 -31
- package/dist/cli/commands/audit-commands.js +64 -473
- package/dist/cli/commands/audit-goal-runners.d.ts +17 -0
- package/dist/cli/commands/audit-goal-runners.js +114 -0
- package/dist/cli/commands/audit-red-lines-runners.d.ts +11 -0
- package/dist/cli/commands/audit-red-lines-runners.js +43 -0
- package/dist/cli/commands/audit-refusal-data.d.ts +23 -0
- package/dist/cli/commands/audit-refusal-data.js +69 -0
- package/dist/cli/commands/audit-static-runners.d.ts +13 -0
- package/dist/cli/commands/audit-static-runners.js +139 -0
- package/dist/cli/commands/await-runners.d.ts +11 -0
- package/dist/cli/commands/await-runners.js +148 -0
- package/dist/cli/commands/code-auto-compact-command.d.ts +3 -0
- package/dist/cli/commands/code-auto-compact-command.js +124 -0
- package/dist/cli/commands/code-auto-compact-envelope.d.ts +22 -0
- package/dist/cli/commands/code-auto-compact-envelope.js +39 -0
- package/dist/cli/commands/code-context-audit-command.d.ts +3 -0
- package/dist/cli/commands/code-context-audit-command.js +68 -0
- package/dist/cli/commands/code-context-now-command.d.ts +3 -0
- package/dist/cli/commands/code-context-now-command.js +124 -0
- package/dist/cli/commands/code-context-now-envelope.d.ts +66 -0
- package/dist/cli/commands/code-context-now-envelope.js +136 -0
- package/dist/cli/commands/code-context-now-mapping.d.ts +27 -0
- package/dist/cli/commands/code-context-now-mapping.js +43 -0
- package/dist/cli/commands/code-emit-handoff-command.d.ts +3 -0
- package/dist/cli/commands/code-emit-handoff-command.js +99 -0
- package/dist/cli/commands/code-gate-step-08-command.d.ts +3 -0
- package/dist/cli/commands/code-gate-step-08-command.js +139 -0
- package/dist/cli/commands/code-post-compact-detect-command.d.ts +3 -0
- package/dist/cli/commands/code-post-compact-detect-command.js +50 -0
- package/dist/cli/commands/code-runtime-commands.d.ts +9 -43
- package/dist/cli/commands/code-runtime-commands.js +20 -672
- package/dist/cli/commands/code-runtime-session.d.ts +1 -0
- package/dist/cli/commands/code-runtime-session.js +15 -0
- package/dist/cli/commands/compact-command.d.ts +6 -2
- package/dist/cli/commands/compact-command.js +12 -559
- package/dist/cli/commands/compact-dry-run-command.d.ts +3 -0
- package/dist/cli/commands/compact-dry-run-command.js +87 -0
- package/dist/cli/commands/compact-force-command.d.ts +3 -0
- package/dist/cli/commands/compact-force-command.js +98 -0
- package/dist/cli/commands/compact-harness-window-command.d.ts +3 -0
- package/dist/cli/commands/compact-harness-window-command.js +174 -0
- package/dist/cli/commands/compact-history-command.d.ts +3 -0
- package/dist/cli/commands/compact-history-command.js +89 -0
- package/dist/cli/commands/compact-read-commands.d.ts +3 -0
- package/dist/cli/commands/compact-read-commands.js +123 -0
- package/dist/cli/commands/compact-settle-command.d.ts +3 -0
- package/dist/cli/commands/compact-settle-command.js +130 -0
- package/dist/cli/commands/compact-shared.d.ts +9 -0
- package/dist/cli/commands/compact-shared.js +26 -0
- package/dist/cli/commands/core/skill-command.d.ts +1 -44
- package/dist/cli/commands/core/skill-command.js +13 -541
- package/dist/cli/commands/core/skill-context-verdict.d.ts +38 -0
- package/dist/cli/commands/core/skill-context-verdict.js +63 -0
- package/dist/cli/commands/core/skill-generated-config-notice.d.ts +41 -0
- package/dist/cli/commands/core/skill-generated-config-notice.js +75 -0
- package/dist/cli/commands/core/skill-heartbeat-commands.d.ts +3 -0
- package/dist/cli/commands/core/skill-heartbeat-commands.js +50 -0
- package/dist/cli/commands/core/skill-presence-commands.d.ts +3 -0
- package/dist/cli/commands/core/skill-presence-commands.js +123 -0
- package/dist/cli/commands/core/skill-presence-housekeeping-commands.d.ts +3 -0
- package/dist/cli/commands/core/skill-presence-housekeeping-commands.js +165 -0
- package/dist/cli/commands/core/skill-registry-commands.d.ts +3 -0
- package/dist/cli/commands/core/skill-registry-commands.js +117 -0
- package/dist/cli/commands/core/skill-runbook-command.d.ts +3 -0
- package/dist/cli/commands/core/skill-runbook-command.js +29 -0
- package/dist/cli/commands/dispatch-command-spec.d.ts +2 -0
- package/dist/cli/commands/dispatch-command-spec.js +62 -0
- package/dist/cli/commands/dispatch-commands.js +159 -775
- package/dist/cli/commands/dispatch-detached-branch.d.ts +3 -0
- package/dist/cli/commands/dispatch-detached-branch.js +58 -0
- package/dist/cli/commands/dispatch-envelope.d.ts +36 -0
- package/dist/cli/commands/dispatch-envelope.js +104 -0
- package/dist/cli/commands/dispatch-isolation-lease.d.ts +17 -0
- package/dist/cli/commands/dispatch-isolation-lease.js +149 -0
- package/dist/cli/commands/dispatch-preflight.d.ts +7 -0
- package/dist/cli/commands/dispatch-preflight.js +105 -0
- package/dist/cli/commands/dispatch-prompt-blocks.d.ts +10 -0
- package/dist/cli/commands/dispatch-prompt-blocks.js +56 -0
- package/dist/cli/commands/dispatch-prompt-composition.d.ts +30 -0
- package/dist/cli/commands/dispatch-prompt-composition.js +171 -0
- package/dist/cli/commands/dispatch-record-write.d.ts +24 -0
- package/dist/cli/commands/dispatch-record-write.js +96 -0
- package/dist/cli/commands/dispatch-tool-call.d.ts +15 -0
- package/dist/cli/commands/dispatch-tool-call.js +128 -0
- package/dist/cli/commands/evolution-command-shared.d.ts +78 -0
- package/dist/cli/commands/evolution-command-shared.js +107 -0
- package/dist/cli/commands/evolution-commands.d.ts +11 -1
- package/dist/cli/commands/evolution-commands.js +19 -350
- package/dist/cli/commands/evolution-evaluate-command.d.ts +24 -0
- package/dist/cli/commands/evolution-evaluate-command.js +138 -0
- package/dist/cli/commands/evolution-propose-command.d.ts +23 -0
- package/dist/cli/commands/evolution-propose-command.js +145 -0
- package/dist/cli/commands/evolution-status-command.d.ts +17 -0
- package/dist/cli/commands/evolution-status-command.js +41 -0
- package/dist/cli/commands/evolution-verdict-commands.d.ts +30 -0
- package/dist/cli/commands/evolution-verdict-commands.js +100 -0
- package/dist/cli/commands/finalize-accumulator.d.ts +44 -0
- package/dist/cli/commands/finalize-accumulator.js +55 -0
- package/dist/cli/commands/finalize-branches.d.ts +34 -0
- package/dist/cli/commands/finalize-branches.js +119 -0
- package/dist/cli/commands/finalize-runners.d.ts +3 -0
- package/dist/cli/commands/finalize-runners.js +92 -0
- package/dist/cli/commands/finalize-selection.d.ts +65 -0
- package/dist/cli/commands/finalize-selection.js +35 -0
- package/dist/cli/commands/gate-commands.js +26 -8
- package/dist/cli/commands/job-checkpoint-commands.d.ts +3 -0
- package/dist/cli/commands/job-checkpoint-commands.js +157 -0
- package/dist/cli/commands/job-command-shared.d.ts +109 -0
- package/dist/cli/commands/job-command-shared.js +72 -0
- package/dist/cli/commands/job-commands.d.ts +4 -64
- package/dist/cli/commands/job-commands.js +42 -552
- package/dist/cli/commands/job-cost-check-command.d.ts +3 -0
- package/dist/cli/commands/job-cost-check-command.js +41 -0
- package/dist/cli/commands/job-init-status-commands.d.ts +3 -0
- package/dist/cli/commands/job-init-status-commands.js +149 -0
- package/dist/cli/commands/job-run-commands.d.ts +3 -0
- package/dist/cli/commands/job-run-commands.js +97 -0
- package/dist/cli/commands/job-state-root.d.ts +35 -0
- package/dist/cli/commands/job-state-root.js +76 -0
- package/dist/cli/commands/loop-eval-capture-index.d.ts +4 -0
- package/dist/cli/commands/loop-eval-capture-index.js +44 -0
- package/dist/cli/commands/loop-eval-commands.d.ts +8 -5
- package/dist/cli/commands/loop-eval-commands.js +12 -626
- package/dist/cli/commands/loop-eval-eval-command.d.ts +3 -0
- package/dist/cli/commands/loop-eval-eval-command.js +168 -0
- package/dist/cli/commands/loop-eval-monotonic-command.d.ts +3 -0
- package/dist/cli/commands/loop-eval-monotonic-command.js +90 -0
- package/dist/cli/commands/loop-eval-parents.d.ts +7 -0
- package/dist/cli/commands/loop-eval-parents.js +20 -0
- package/dist/cli/commands/loop-eval-run-code.d.ts +7 -0
- package/dist/cli/commands/loop-eval-run-code.js +40 -0
- package/dist/cli/commands/loop-eval-run-command.d.ts +3 -0
- package/dist/cli/commands/loop-eval-run-command.js +113 -0
- package/dist/cli/commands/loop-eval-spec-commands.d.ts +3 -0
- package/dist/cli/commands/loop-eval-spec-commands.js +165 -0
- package/dist/cli/commands/loop-eval-spec-lint-command.d.ts +3 -0
- package/dist/cli/commands/loop-eval-spec-lint-command.js +42 -0
- package/dist/cli/commands/loop-eval-workflow-commands.d.ts +3 -0
- package/dist/cli/commands/loop-eval-workflow-commands.js +141 -0
- package/dist/cli/commands/mcp-commands.d.ts +10 -0
- package/dist/cli/commands/mcp-commands.js +157 -0
- package/dist/cli/commands/request-command-options.d.ts +49 -0
- package/dist/cli/commands/request-command-options.js +1 -0
- package/dist/cli/commands/request-commands.d.ts +3 -9
- package/dist/cli/commands/request-commands.js +17 -522
- package/dist/cli/commands/request-init-command.d.ts +3 -0
- package/dist/cli/commands/request-init-command.js +100 -0
- package/dist/cli/commands/request-lint-command.d.ts +3 -0
- package/dist/cli/commands/request-lint-command.js +50 -0
- package/dist/cli/commands/request-list-command.d.ts +10 -0
- package/dist/cli/commands/request-list-command.js +53 -0
- package/dist/cli/commands/request-repair-status-command.d.ts +3 -0
- package/dist/cli/commands/request-repair-status-command.js +61 -0
- package/dist/cli/commands/request-show-command.d.ts +3 -0
- package/dist/cli/commands/request-show-command.js +56 -0
- package/dist/cli/commands/request-transition-command.d.ts +3 -0
- package/dist/cli/commands/request-transition-command.js +122 -0
- package/dist/cli/commands/request-transition-failures.d.ts +8 -0
- package/dist/cli/commands/request-transition-failures.js +90 -0
- package/dist/cli/commands/request-transition-hooks.d.ts +50 -0
- package/dist/cli/commands/request-transition-hooks.js +115 -0
- package/dist/cli/commands/request-transition-preflight.d.ts +10 -0
- package/dist/cli/commands/request-transition-preflight.js +107 -0
- package/dist/cli/commands/scan-command-shared.d.ts +96 -0
- package/dist/cli/commands/scan-command-shared.js +83 -0
- package/dist/cli/commands/scan-commands.d.ts +3 -2
- package/dist/cli/commands/scan-commands.js +23 -463
- package/dist/cli/commands/scan-inventory-commands.d.ts +3 -0
- package/dist/cli/commands/scan-inventory-commands.js +96 -0
- package/dist/cli/commands/scan-project-commands.d.ts +3 -0
- package/dist/cli/commands/scan-project-commands.js +65 -0
- package/dist/cli/commands/scan-scope-commands.d.ts +3 -0
- package/dist/cli/commands/scan-scope-commands.js +149 -0
- package/dist/cli/commands/scan-scope-helpers.d.ts +8 -0
- package/dist/cli/commands/scan-scope-helpers.js +54 -0
- package/dist/cli/commands/scan-structural-commands.d.ts +3 -0
- package/dist/cli/commands/scan-structural-commands.js +113 -0
- package/dist/cli/commands/share-commands.d.ts +12 -68
- package/dist/cli/commands/share-commands.js +47 -556
- package/dist/cli/commands/share-error-hints.d.ts +14 -0
- package/dist/cli/commands/share-error-hints.js +38 -0
- package/dist/cli/commands/share-identity.d.ts +13 -0
- package/dist/cli/commands/share-identity.js +22 -0
- package/dist/cli/commands/share-record-paths.d.ts +37 -0
- package/dist/cli/commands/share-record-paths.js +68 -0
- package/dist/cli/commands/share-runners.d.ts +3 -0
- package/dist/cli/commands/share-runners.js +158 -0
- package/dist/cli/commands/shared-read-runners.d.ts +3 -0
- package/dist/cli/commands/shared-read-runners.js +51 -0
- package/dist/cli/commands/slice-check-command.d.ts +3 -0
- package/dist/cli/commands/slice-check-command.js +54 -0
- package/dist/cli/commands/slice-commands.d.ts +2 -22
- package/dist/cli/commands/slice-commands.js +18 -517
- package/dist/cli/commands/slice-decompose-artifacts.d.ts +18 -0
- package/dist/cli/commands/slice-decompose-artifacts.js +79 -0
- package/dist/cli/commands/slice-decompose-command.d.ts +3 -0
- package/dist/cli/commands/slice-decompose-command.js +123 -0
- package/dist/cli/commands/slice-list-commands.d.ts +3 -0
- package/dist/cli/commands/slice-list-commands.js +182 -0
- package/dist/cli/commands/slice-pick-command.d.ts +3 -0
- package/dist/cli/commands/slice-pick-command.js +60 -0
- package/dist/cli/commands/slice-plan-command.d.ts +24 -0
- package/dist/cli/commands/slice-plan-command.js +111 -0
- package/dist/cli/commands/workflow-commands.d.ts +10 -1
- package/dist/cli/commands/workflow-commands.js +6 -594
- package/dist/cli/commands/workflow-gate-commands.d.ts +3 -0
- package/dist/cli/commands/workflow-gate-commands.js +116 -0
- package/dist/cli/commands/workflow-plan-helpers.d.ts +48 -0
- package/dist/cli/commands/workflow-plan-helpers.js +103 -0
- package/dist/cli/commands/workflow-planning-commands.d.ts +3 -0
- package/dist/cli/commands/workflow-planning-commands.js +56 -0
- package/dist/cli/commands/workflow-swarm-actions.d.ts +7 -0
- package/dist/cli/commands/workflow-swarm-actions.js +109 -0
- package/dist/cli/commands/workflow-swarm-commands.d.ts +3 -0
- package/dist/cli/commands/workflow-swarm-commands.js +138 -0
- package/dist/cli/commands/workflow-tech-actions.d.ts +10 -0
- package/dist/cli/commands/workflow-tech-actions.js +150 -0
- package/dist/cli/commands/workspace/init-actions.d.ts +10 -0
- package/dist/cli/commands/workspace/init-actions.js +199 -0
- package/dist/cli/commands/workspace/init-codegraph-stake.d.ts +10 -0
- package/dist/cli/commands/workspace/init-codegraph-stake.js +54 -0
- package/dist/cli/commands/workspace/init-command.d.ts +8 -124
- package/dist/cli/commands/workspace/init-command.js +59 -623
- package/dist/cli/commands/workspace/init-context.d.ts +58 -0
- package/dist/cli/commands/workspace/init-context.js +121 -0
- package/dist/cli/commands/workspace/init-envelope.d.ts +3 -0
- package/dist/cli/commands/workspace/init-envelope.js +34 -0
- package/dist/cli/commands/workspace/init-failures.d.ts +3 -0
- package/dist/cli/commands/workspace/init-failures.js +84 -0
- package/dist/cli/commands/workspace/init-hooks-decision.d.ts +24 -0
- package/dist/cli/commands/workspace/init-hooks-decision.js +189 -0
- package/dist/cli/commands/workspace/init-options.d.ts +102 -0
- package/dist/cli/commands/workspace/init-options.js +1 -0
- package/dist/cli/commands/worktree-lease-commands.d.ts +10 -6
- package/dist/cli/commands/worktree-lease-commands.js +42 -512
- package/dist/cli/commands/worktree-lease-gc-command.d.ts +12 -0
- package/dist/cli/commands/worktree-lease-gc-command.js +117 -0
- package/dist/cli/commands/worktree-lease-git.d.ts +22 -0
- package/dist/cli/commands/worktree-lease-git.js +48 -0
- package/dist/cli/commands/worktree-lease-list-command.d.ts +12 -0
- package/dist/cli/commands/worktree-lease-list-command.js +100 -0
- package/dist/cli/commands/worktree-lease-load.d.ts +37 -0
- package/dist/cli/commands/worktree-lease-load.js +28 -0
- package/dist/cli/commands/worktree-lease-release-command.d.ts +13 -0
- package/dist/cli/commands/worktree-lease-release-command.js +105 -0
- package/dist/cli/commands/worktree-lease-renew-command.d.ts +11 -0
- package/dist/cli/commands/worktree-lease-renew-command.js +99 -0
- package/dist/cli/commands/worktree-lease-session.d.ts +22 -0
- package/dist/cli/commands/worktree-lease-session.js +29 -0
- package/dist/cli/commands/worktree-lease-spawn-command.d.ts +11 -0
- package/dist/cli/commands/worktree-lease-spawn-command.js +119 -0
- package/dist/cli/commands/worktree-lease-status-command.d.ts +11 -0
- package/dist/cli/commands/worktree-lease-status-command.js +86 -0
- package/dist/cli/commands/worktree-lease-store.d.ts +19 -0
- package/dist/cli/commands/worktree-lease-store.js +24 -0
- package/dist/cli/program.js +5 -0
- package/dist/services/distribution/mcp-install-runner.d.ts +21 -0
- package/dist/services/distribution/mcp-install-runner.js +59 -0
- package/dist/services/distribution/mcp-install.d.ts +95 -0
- package/dist/services/distribution/mcp-install.js +179 -0
- package/dist/services/distribution/plugin-manifest.d.ts +97 -0
- package/dist/services/distribution/plugin-manifest.js +126 -0
- package/dist/services/hooks/mcp-surface-gate.d.ts +19 -0
- package/dist/services/hooks/mcp-surface-gate.js +87 -0
- package/dist/services/ide/adapters/claude-code-adapter.d.ts +4 -35
- package/dist/services/ide/adapters/claude-code-adapter.js +48 -171
- package/dist/services/ide/adapters/claude-code-local-state.d.ts +28 -0
- package/dist/services/ide/adapters/claude-code-local-state.js +112 -0
- package/dist/services/ide/adapters/claude-code-model-window.d.ts +37 -0
- package/dist/services/ide/adapters/claude-code-model-window.js +113 -0
- package/dist/services/ide/ide-mcp-install-types.d.ts +78 -0
- package/dist/services/ide/ide-mcp-install-types.js +104 -0
- package/dist/services/ide/ide-types.d.ts +45 -1
- package/dist/services/ide/mcp-tool-matcher.d.ts +34 -0
- package/dist/services/ide/mcp-tool-matcher.js +84 -0
- package/dist/services/lint/npx-resolver.js +7 -0
- package/dist/services/mcp/cli-executor.d.ts +36 -0
- package/dist/services/mcp/cli-executor.js +97 -0
- package/dist/services/mcp/json-rpc.d.ts +81 -0
- package/dist/services/mcp/json-rpc.js +116 -0
- package/dist/services/mcp/memory-tool.d.ts +3 -0
- package/dist/services/mcp/memory-tool.js +62 -0
- package/dist/services/mcp/server-main.d.ts +1 -0
- package/dist/services/mcp/server-main.js +66 -0
- package/dist/services/mcp/server.d.ts +22 -0
- package/dist/services/mcp/server.js +152 -0
- package/dist/services/mcp/surface.d.ts +62 -0
- package/dist/services/mcp/surface.js +198 -0
- package/dist/services/mcp/tool-core.d.ts +42 -0
- package/dist/services/mcp/tool-core.js +137 -0
- package/dist/services/mcp/tools.d.ts +13 -0
- package/dist/services/mcp/tools.js +58 -0
- package/dist/services/readonly-surface/argv-guard.d.ts +32 -0
- package/dist/services/readonly-surface/argv-guard.js +164 -0
- package/dist/services/readonly-surface/readonly-whitelist.d.ts +147 -0
- package/dist/services/readonly-surface/readonly-whitelist.js +107 -0
- package/dist/services/readonly-surface/whitelist-generator.d.ts +64 -0
- package/dist/services/readonly-surface/whitelist-generator.js +212 -0
- package/dist/services/skills/hooks-codegate-superpowers.d.ts +1 -0
- package/dist/services/skills/hooks-codegate-superpowers.js +26 -0
- package/dist/services/skills/hooks-settings-service.js +10 -2
- package/package.json +17 -6
- package/scripts/install-skills-bundles.mjs +233 -0
- package/scripts/install-skills-fs.mjs +148 -0
- package/scripts/install-skills-ide.mjs +279 -0
- package/scripts/install-skills-output-style-default.mjs +196 -0
- package/scripts/install-skills-output-styles.mjs +121 -0
- package/scripts/install-skills-report.mjs +42 -0
- package/scripts/install-skills-user-config.mjs +198 -0
- package/scripts/install-skills.mjs +36 -1066
- package/scripts/watch-tree.mjs +140 -0
- package/scripts/watch.mjs +4 -126
- 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}`
|