supipowers 1.5.2 → 2.0.0

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 (340) hide show
  1. package/README.md +14 -8
  2. package/bin/install.mjs +20 -5
  3. package/bin/install.ts +95 -0
  4. package/package.json +8 -4
  5. package/skills/context-mode/SKILL.md +17 -10
  6. package/skills/harness/SKILL.md +94 -0
  7. package/skills/ui-design/SKILL.md +63 -0
  8. package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
  9. package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
  10. package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
  11. package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
  12. package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
  13. package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
  14. package/skills/ultraplan-discover/SKILL.md +96 -0
  15. package/skills/ultraplan-intake/SKILL.md +89 -0
  16. package/skills/ultraplan-research/SKILL.md +129 -0
  17. package/skills/ultraplan-review/SKILL.md +86 -0
  18. package/skills/ultraplan-review-scope/SKILL.md +111 -0
  19. package/skills/ultraplan-review-structure/SKILL.md +120 -0
  20. package/skills/ultraplan-review-tdd/SKILL.md +142 -0
  21. package/skills/ultraplan-scout/SKILL.md +110 -0
  22. package/skills/ultraplan-synthesize/SKILL.md +124 -0
  23. package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
  24. package/src/ai/schema-text.ts +129 -0
  25. package/src/ai/structured-output.ts +274 -0
  26. package/src/ai/template.ts +27 -0
  27. package/src/bootstrap.ts +63 -28
  28. package/src/commands/agents.ts +149 -45
  29. package/src/commands/ai-review.ts +251 -30
  30. package/src/commands/clear.ts +434 -0
  31. package/src/commands/commit.ts +1 -0
  32. package/src/commands/config.ts +242 -44
  33. package/src/commands/context.ts +55 -28
  34. package/src/commands/doctor.ts +234 -6
  35. package/src/commands/fix-pr.ts +306 -131
  36. package/src/commands/generate.ts +111 -21
  37. package/src/commands/memory.ts +192 -0
  38. package/src/commands/model-picker.ts +28 -21
  39. package/src/commands/model.ts +19 -9
  40. package/src/commands/optimize-context.ts +408 -29
  41. package/src/commands/plan.ts +2 -0
  42. package/src/commands/qa.ts +312 -137
  43. package/src/commands/release.ts +259 -76
  44. package/src/commands/review.ts +293 -59
  45. package/src/commands/status.ts +200 -13
  46. package/src/commands/supi.ts +3 -35
  47. package/src/commands/ui-design.ts +394 -0
  48. package/src/commands/ultraplan.ts +1518 -0
  49. package/src/commands/update.ts +86 -0
  50. package/src/config/defaults.ts +62 -0
  51. package/src/config/loader.ts +448 -60
  52. package/src/config/schema.ts +108 -2
  53. package/src/context/optimizer.ts +25 -33
  54. package/src/context/rule-renderer.ts +223 -0
  55. package/src/context/savings.ts +258 -0
  56. package/src/context/startup-check.ts +380 -0
  57. package/src/context/startup-optimizer.ts +355 -0
  58. package/src/context/tokenignore.ts +146 -0
  59. package/src/context-mode/cache-handle.ts +49 -0
  60. package/src/context-mode/cache-preview.ts +71 -0
  61. package/src/context-mode/cache-store.ts +738 -0
  62. package/src/context-mode/compressor.ts +131 -26
  63. package/src/context-mode/dedup.ts +108 -0
  64. package/src/context-mode/detector.ts +35 -4
  65. package/src/context-mode/event-extractor.ts +14 -12
  66. package/src/context-mode/event-store.ts +91 -36
  67. package/src/context-mode/hooks.ts +798 -56
  68. package/src/context-mode/knowledge/store.ts +255 -11
  69. package/src/context-mode/memory-store.ts +325 -0
  70. package/src/context-mode/metrics-recorder.ts +158 -0
  71. package/src/context-mode/metrics-store.ts +765 -0
  72. package/src/context-mode/model.ts +24 -0
  73. package/src/context-mode/processor-keys.ts +29 -0
  74. package/src/context-mode/processors/build.ts +66 -0
  75. package/src/context-mode/processors/docker.ts +57 -0
  76. package/src/context-mode/processors/git.ts +111 -0
  77. package/src/context-mode/processors/json.ts +112 -0
  78. package/src/context-mode/processors/k8s.ts +67 -0
  79. package/src/context-mode/processors/lint.ts +67 -0
  80. package/src/context-mode/processors/log.ts +86 -0
  81. package/src/context-mode/processors/registry.ts +116 -0
  82. package/src/context-mode/processors/test-runner.ts +102 -0
  83. package/src/context-mode/processors/types.ts +20 -0
  84. package/src/context-mode/repomap.ts +400 -0
  85. package/src/context-mode/routing.ts +97 -24
  86. package/src/context-mode/sandbox/runners.ts +5 -1
  87. package/src/context-mode/snapshot-builder.ts +106 -11
  88. package/src/context-mode/source-hash.ts +173 -0
  89. package/src/context-mode/tool-name.ts +11 -0
  90. package/src/context-mode/tools.ts +654 -22
  91. package/src/context-mode/web/fetcher.ts +31 -12
  92. package/src/debug/logger.ts +2 -1
  93. package/src/deps/registry.ts +1 -1
  94. package/src/discipline/failure-summarizer.ts +170 -0
  95. package/src/discipline/failure-taxonomy.ts +131 -0
  96. package/src/discipline/workflow-invariants.ts +125 -0
  97. package/src/discovery/index.ts +31 -0
  98. package/src/discovery/lsp.ts +87 -0
  99. package/src/discovery/rank.ts +144 -0
  100. package/src/discovery/sources.ts +89 -0
  101. package/src/discovery/workflow.ts +87 -0
  102. package/src/docs/contracts.ts +39 -0
  103. package/src/docs/drift.ts +117 -87
  104. package/src/fix-pr/assessment.ts +200 -0
  105. package/src/fix-pr/contracts.ts +47 -0
  106. package/src/fix-pr/fetch-comments.ts +80 -0
  107. package/src/fix-pr/prompt-builder.ts +58 -40
  108. package/src/fix-pr/scripts/exec.ts +34 -0
  109. package/src/fix-pr/scripts/trigger-review.ts +106 -0
  110. package/src/fix-pr/scripts/wait-and-check.ts +108 -0
  111. package/src/fix-pr/types.ts +4 -0
  112. package/src/git/branch-finish.ts +5 -0
  113. package/src/git/commit-contract.ts +83 -0
  114. package/src/git/commit.ts +121 -184
  115. package/src/git/status.ts +62 -8
  116. package/src/harness/anti_slop/architecture-parser.ts +210 -0
  117. package/src/harness/anti_slop/backend-factory.ts +30 -0
  118. package/src/harness/anti_slop/backend.ts +140 -0
  119. package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
  120. package/src/harness/anti_slop/fallow-adapter.ts +305 -0
  121. package/src/harness/anti_slop/installer.ts +227 -0
  122. package/src/harness/anti_slop/queue.ts +216 -0
  123. package/src/harness/anti_slop/recommend.ts +84 -0
  124. package/src/harness/anti_slop/score.ts +180 -0
  125. package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
  126. package/src/harness/artifacts/agents-md.ts +88 -0
  127. package/src/harness/artifacts/checks-wiring.ts +57 -0
  128. package/src/harness/artifacts/docs-tree.ts +79 -0
  129. package/src/harness/artifacts/lint-configs.ts +136 -0
  130. package/src/harness/artifacts/review-agents.ts +67 -0
  131. package/src/harness/bare-entry.ts +108 -0
  132. package/src/harness/command.ts +1010 -0
  133. package/src/harness/default-agents/design.md +23 -0
  134. package/src/harness/default-agents/discover.md +18 -0
  135. package/src/harness/default-agents/implement.md +24 -0
  136. package/src/harness/default-agents/plan.md +19 -0
  137. package/src/harness/default-agents/research.md +21 -0
  138. package/src/harness/default-agents/validate.md +22 -0
  139. package/src/harness/gc/reporter.ts +28 -0
  140. package/src/harness/gc/runner.ts +136 -0
  141. package/src/harness/hooks/layer-context-inject.ts +155 -0
  142. package/src/harness/hooks/post-session-sweep.ts +130 -0
  143. package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
  144. package/src/harness/hooks/register.ts +118 -0
  145. package/src/harness/model.ts +117 -0
  146. package/src/harness/pipeline.ts +348 -0
  147. package/src/harness/project-paths.ts +235 -0
  148. package/src/harness/stage-runner.ts +107 -0
  149. package/src/harness/stages/design.ts +386 -0
  150. package/src/harness/stages/discover.ts +454 -0
  151. package/src/harness/stages/implement.ts +162 -0
  152. package/src/harness/stages/plan.ts +335 -0
  153. package/src/harness/stages/research.ts +263 -0
  154. package/src/harness/stages/validate.ts +684 -0
  155. package/src/harness/storage.ts +467 -0
  156. package/src/harness/tools.ts +426 -0
  157. package/src/lsp/bridge.ts +56 -95
  158. package/src/lsp/capabilities.ts +108 -0
  159. package/src/lsp/contracts.ts +35 -0
  160. package/src/lsp/detector.ts +8 -12
  161. package/src/markdown-frontmatter.ts +68 -0
  162. package/src/mempalace/bridge.ts +129 -0
  163. package/src/mempalace/config.ts +75 -0
  164. package/src/mempalace/format.ts +163 -0
  165. package/src/mempalace/hooks.ts +370 -0
  166. package/src/mempalace/installer-helper.ts +194 -0
  167. package/src/mempalace/python/mempalace_bridge.py +440 -0
  168. package/src/mempalace/runtime.ts +565 -0
  169. package/src/mempalace/schema.ts +264 -0
  170. package/src/mempalace/session-summary.ts +198 -0
  171. package/src/mempalace/tool.ts +186 -0
  172. package/src/mempalace/uv.ts +256 -0
  173. package/src/migrate/runner.ts +354 -0
  174. package/src/planning/approval-flow.ts +206 -9
  175. package/src/planning/plan-writer-prompt.ts +4 -3
  176. package/src/planning/planning-ask-tool.ts +39 -0
  177. package/src/planning/render-markdown.ts +74 -0
  178. package/src/planning/spec.ts +42 -0
  179. package/src/planning/system-prompt.ts +11 -8
  180. package/src/planning/validate.ts +84 -0
  181. package/src/platform/omp.ts +15 -2
  182. package/src/platform/system-prompt.ts +37 -0
  183. package/src/platform/test-utils.ts +3 -0
  184. package/src/platform/types.ts +6 -1
  185. package/src/qa/config.ts +12 -6
  186. package/src/qa/detect-app-type.ts +13 -6
  187. package/src/qa/matrix.ts +12 -6
  188. package/src/qa/prompt-builder.ts +28 -30
  189. package/src/qa/scripts/dev-server-utils.ts +72 -0
  190. package/src/qa/scripts/run-e2e-tests.ts +226 -0
  191. package/src/qa/scripts/start-dev-server.ts +138 -0
  192. package/src/qa/scripts/stop-dev-server.ts +77 -0
  193. package/src/qa/session.ts +13 -7
  194. package/src/quality/ai-setup.ts +27 -25
  195. package/src/quality/contracts.ts +34 -0
  196. package/src/quality/gates/ai-review.ts +20 -58
  197. package/src/quality/gates/command.ts +249 -46
  198. package/src/quality/review-gates.ts +18 -2
  199. package/src/quality/runner.ts +63 -22
  200. package/src/quality/schemas.ts +37 -2
  201. package/src/quality/setup.ts +96 -16
  202. package/src/release/changelog.ts +1 -1
  203. package/src/release/channels/custom.ts +13 -3
  204. package/src/release/channels/types.ts +5 -0
  205. package/src/release/contracts.ts +90 -0
  206. package/src/release/executor.ts +122 -45
  207. package/src/release/prompt.ts +18 -2
  208. package/src/release/targets.ts +86 -0
  209. package/src/release/version.ts +96 -71
  210. package/src/review/agent-loader.ts +298 -127
  211. package/src/review/fixer.ts +10 -6
  212. package/src/review/multi-agent-runner.ts +115 -14
  213. package/src/review/output.ts +12 -139
  214. package/src/review/runner.ts +12 -6
  215. package/src/review/scope.ts +144 -24
  216. package/src/review/types.ts +11 -20
  217. package/src/review/validator.ts +12 -6
  218. package/src/storage/fix-pr-sessions.ts +21 -14
  219. package/src/storage/plans.ts +14 -5
  220. package/src/storage/qa-sessions.ts +25 -19
  221. package/src/storage/reliability-metrics.ts +180 -0
  222. package/src/storage/reports.ts +8 -7
  223. package/src/storage/review-sessions.ts +55 -20
  224. package/src/tool-catalog/active-tool-controller.ts +164 -0
  225. package/src/tool-catalog/active-tool-planner.ts +212 -0
  226. package/src/tool-catalog/tool-groups.ts +102 -0
  227. package/src/types.ts +1401 -5
  228. package/src/ui-design/backend-adapter.ts +78 -0
  229. package/src/ui-design/backends/local-html.ts +82 -0
  230. package/src/ui-design/backends/pencil-mcp.ts +111 -0
  231. package/src/ui-design/components-scanner.ts +124 -0
  232. package/src/ui-design/config.ts +55 -0
  233. package/src/ui-design/pen-scanner.ts +95 -0
  234. package/src/ui-design/pen-selector.ts +72 -0
  235. package/src/ui-design/prompt-builder.ts +73 -0
  236. package/src/ui-design/scanner.ts +136 -0
  237. package/src/ui-design/session.ts +974 -0
  238. package/src/ui-design/system-prompt.ts +312 -0
  239. package/src/ui-design/tokens-scanner.ts +181 -0
  240. package/src/ui-design/types.ts +96 -0
  241. package/src/ultraplan/agent-catalog.ts +522 -0
  242. package/src/ultraplan/authoring/agent-catalog.ts +310 -0
  243. package/src/ultraplan/authoring/authoring-tools.ts +552 -0
  244. package/src/ultraplan/authoring/command-handlers.ts +339 -0
  245. package/src/ultraplan/authoring/markdown.ts +510 -0
  246. package/src/ultraplan/authoring/model.ts +162 -0
  247. package/src/ultraplan/authoring/pipeline.ts +319 -0
  248. package/src/ultraplan/authoring/stage-runner.ts +141 -0
  249. package/src/ultraplan/authoring/stages/approve.ts +249 -0
  250. package/src/ultraplan/authoring/stages/discover.ts +289 -0
  251. package/src/ultraplan/authoring/stages/intake.ts +203 -0
  252. package/src/ultraplan/authoring/stages/research.ts +399 -0
  253. package/src/ultraplan/authoring/stages/review.ts +333 -0
  254. package/src/ultraplan/authoring/stages/scout.ts +188 -0
  255. package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
  256. package/src/ultraplan/authoring/storage.ts +594 -0
  257. package/src/ultraplan/authoring/synth-gate.ts +165 -0
  258. package/src/ultraplan/authoring-draft.ts +653 -0
  259. package/src/ultraplan/authoring-persist.ts +180 -0
  260. package/src/ultraplan/authoring-tool.ts +608 -0
  261. package/src/ultraplan/authoring-wizard.ts +587 -0
  262. package/src/ultraplan/batch/merge.ts +98 -0
  263. package/src/ultraplan/batch/planner.ts +150 -0
  264. package/src/ultraplan/batch/presenter.ts +97 -0
  265. package/src/ultraplan/batch/storage.ts +420 -0
  266. package/src/ultraplan/batch/supervisor.ts +317 -0
  267. package/src/ultraplan/batch/worker.ts +26 -0
  268. package/src/ultraplan/batch/worktree.ts +110 -0
  269. package/src/ultraplan/contracts.ts +1593 -0
  270. package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
  271. package/src/ultraplan/default-agents/authoring/intake.md +12 -0
  272. package/src/ultraplan/default-agents/authoring/planner.md +12 -0
  273. package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
  274. package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
  275. package/src/ultraplan/default-agents/authoring/scout.md +12 -0
  276. package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
  277. package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
  278. package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
  279. package/src/ultraplan/default-agents/backend-executor.md +10 -0
  280. package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
  281. package/src/ultraplan/default-agents/backend-tester.md +10 -0
  282. package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
  283. package/src/ultraplan/default-agents/frontend-executor.md +10 -0
  284. package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
  285. package/src/ultraplan/default-agents/frontend-tester.md +10 -0
  286. package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
  287. package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
  288. package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
  289. package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
  290. package/src/ultraplan/execution/contract.ts +71 -0
  291. package/src/ultraplan/execution/policy.ts +217 -0
  292. package/src/ultraplan/execution/runtime-tools.ts +107 -0
  293. package/src/ultraplan/execution/session-runner.ts +281 -0
  294. package/src/ultraplan/next-router.ts +85 -0
  295. package/src/ultraplan/presenter.ts +359 -0
  296. package/src/ultraplan/project-paths.ts +342 -0
  297. package/src/ultraplan/runtime/active-execution.ts +72 -0
  298. package/src/ultraplan/runtime/apply-mutation.ts +416 -0
  299. package/src/ultraplan/runtime/blockers.ts +243 -0
  300. package/src/ultraplan/runtime/hook-bridge.ts +486 -0
  301. package/src/ultraplan/runtime/launch-context.ts +207 -0
  302. package/src/ultraplan/runtime/migration.ts +524 -0
  303. package/src/ultraplan/runtime/normalize.ts +281 -0
  304. package/src/ultraplan/runtime/proof.ts +260 -0
  305. package/src/ultraplan/runtime/reducer.ts +416 -0
  306. package/src/ultraplan/runtime/repair.ts +251 -0
  307. package/src/ultraplan/runtime/tracker-storage.ts +368 -0
  308. package/src/ultraplan/session-selection.ts +291 -0
  309. package/src/ultraplan/storage.ts +374 -0
  310. package/src/utils/editor.ts +38 -0
  311. package/src/utils/executable.ts +80 -0
  312. package/src/utils/paths.ts +1 -20
  313. package/src/utils/shell.ts +31 -0
  314. package/src/visual/companion.ts +2 -1
  315. package/src/visual/scripts/frame-template.html +60 -0
  316. package/src/visual/scripts/index.js +59 -13
  317. package/src/visual/scripts/package.json +3 -0
  318. package/src/visual/start-server.ts +2 -1
  319. package/src/workspace/git-scope.ts +64 -0
  320. package/src/workspace/locks.ts +23 -0
  321. package/src/workspace/package-manager.ts +117 -0
  322. package/src/workspace/path-mapping.ts +75 -0
  323. package/src/workspace/project-slug.ts +92 -0
  324. package/src/workspace/repo-root.ts +137 -0
  325. package/src/workspace/selector.ts +115 -0
  326. package/src/workspace/state-paths.ts +118 -0
  327. package/src/workspace/targets.ts +313 -0
  328. package/src/fix-pr/scripts/diff-comments.sh +0 -33
  329. package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
  330. package/src/fix-pr/scripts/trigger-review.sh +0 -36
  331. package/src/fix-pr/scripts/wait-and-check.sh +0 -37
  332. package/src/qa/scripts/detect-app-type.sh +0 -68
  333. package/src/qa/scripts/discover-routes.sh +0 -143
  334. package/src/qa/scripts/run-e2e-tests.sh +0 -131
  335. package/src/qa/scripts/start-dev-server.sh +0 -46
  336. package/src/qa/scripts/stop-dev-server.sh +0 -36
  337. package/src/review/prompts/fix-output-schema.md +0 -18
  338. package/src/review/prompts/review-output-schema.md +0 -38
  339. package/src/review/template.ts +0 -15
  340. /package/src/{review → ai}/prompts/invalid-output-retry.md +0 -0
@@ -0,0 +1,164 @@
1
+ import { createHash } from "node:crypto";
2
+ import { basename } from "node:path";
3
+ import { getMetricsStore, getSessionId } from "../context-mode/hooks.js";
4
+ import { getProjectStateDir } from "../workspace/state-paths.js";
5
+ import type { Platform } from "../platform/types.js";
6
+ import { normalizeSystemPromptBlocks, systemPromptText } from "../platform/system-prompt.js";
7
+ import type { McpRegistry } from "../mcp/types.js";
8
+ import type { SupipowersConfig } from "../types.js";
9
+ import { planActiveTools } from "./active-tool-planner.js";
10
+ import { detectContextMode } from "../context-mode/detector.js";
11
+ import { getShadowedNativeTools } from "../context-mode/routing.js";
12
+
13
+ export interface ActiveToolControllerDeps {
14
+ loadMcpRegistryForCwd(cwd: string): McpRegistry;
15
+ consumePendingTags(): string[];
16
+ }
17
+
18
+ type BeforeAgentStartEventLike = {
19
+ prompt?: string;
20
+ systemPrompt?: string | string[];
21
+ };
22
+
23
+ type BeforeAgentStartContextLike = {
24
+ cwd?: string;
25
+ getSystemPrompt?: () => unknown;
26
+ getContextUsage?: () => {
27
+ tokens?: number | null;
28
+ contextWindow?: number | null;
29
+ percent?: number | null;
30
+ } | null;
31
+ };
32
+
33
+ export function registerActiveToolController(
34
+ platform: Platform,
35
+ config: SupipowersConfig,
36
+ _deps: ActiveToolControllerDeps,
37
+ ): void {
38
+ if (!config.contextMode.enabled || !config.contextMode.lazyTools.enabled) return;
39
+
40
+ platform.on("before_agent_start", async (
41
+ event: BeforeAgentStartEventLike,
42
+ ctx: BeforeAgentStartContextLike = {},
43
+ ) => {
44
+ if (typeof platform.getAllTools !== "function") return undefined;
45
+ if (typeof platform.setActiveTools !== "function") return undefined;
46
+ if (typeof ctx.getSystemPrompt !== "function") return undefined;
47
+
48
+ const cwd = typeof ctx.cwd === "string" && ctx.cwd.length > 0 ? ctx.cwd : process.cwd();
49
+ let registry: McpRegistry = { schemaVersion: 1, servers: {} };
50
+ try {
51
+ registry = _deps.loadMcpRegistryForCwd(cwd);
52
+ } catch (error) {
53
+ (platform as any).logger?.warn?.("supi-lazy-tools: failed to load MCP registry", error);
54
+ }
55
+
56
+ let pendingTags: string[] = [];
57
+ try {
58
+ pendingTags = _deps.consumePendingTags();
59
+ } catch (error) {
60
+ (platform as any).logger?.warn?.("supi-lazy-tools: failed to consume MCP tags", error);
61
+ }
62
+
63
+ let plan;
64
+ try {
65
+ plan = planActiveTools({
66
+ prompt: event.prompt ?? "",
67
+ currentActive: platform.getActiveTools(),
68
+ allTools: platform.getAllTools(),
69
+ lazyTools: config.contextMode.lazyTools,
70
+ mcpServers: registry.servers,
71
+ pendingTags,
72
+ cacheHandlesEnabled: config.contextMode.cacheHandles.enabled,
73
+ });
74
+ } catch (error) {
75
+ (platform as any).logger?.warn?.("supi-lazy-tools: active-tool planning failed", error);
76
+ return { systemPrompt: normalizeSystemPromptBlocks(event.systemPrompt) };
77
+ }
78
+
79
+ // Hide native tools fully shadowed by an active ctx_* replacement.
80
+ // Without this, the LLM sees Search/Find/Fetch in the tool catalog and
81
+ // routinely tries them, only to receive routing-block errors. Filtering
82
+ // here removes them from the system prompt that OMP rebuilds below.
83
+ if (config.contextMode.enforceRouting) {
84
+ const status = detectContextMode(plan.activeTools);
85
+ const shadowed = new Set(getShadowedNativeTools(status));
86
+ if (shadowed.size > 0) {
87
+ plan.activeTools = plan.activeTools.filter((tool) => !shadowed.has(tool));
88
+ }
89
+ }
90
+
91
+ if (plan.activeTools.length === 0 || arraysEqual(plan.activeTools, platform.getActiveTools())) {
92
+ return undefined;
93
+ }
94
+
95
+ try {
96
+ await platform.setActiveTools(plan.activeTools);
97
+ } catch (error) {
98
+ (platform as any).logger?.warn?.("supi-lazy-tools: setActiveTools failed", error);
99
+ return { systemPrompt: normalizeSystemPromptBlocks(event.systemPrompt) };
100
+ }
101
+
102
+ const rebuiltPrompt = ctx.getSystemPrompt();
103
+ const rebuiltPromptBlocks = normalizeSystemPromptBlocks(rebuiltPrompt);
104
+ recordLazyToolsMetric({
105
+ platform,
106
+ cwd,
107
+ beforePrompt: systemPromptText(event.systemPrompt),
108
+ afterPrompt: systemPromptText(rebuiltPrompt),
109
+ activeTools: plan.activeTools,
110
+ contextUsage: ctx.getContextUsage?.() ?? null,
111
+ });
112
+
113
+ return { systemPrompt: rebuiltPromptBlocks };
114
+ });
115
+ }
116
+
117
+ function arraysEqual(a: readonly string[], b: readonly string[]): boolean {
118
+ if (a.length !== b.length) return false;
119
+ return a.every((value, index) => value === b[index]);
120
+ }
121
+
122
+
123
+ function recordLazyToolsMetric(opts: {
124
+ platform: Platform;
125
+ cwd: string;
126
+ beforePrompt: string;
127
+ afterPrompt: string;
128
+ activeTools: string[];
129
+ contextUsage: { tokens?: number | null; contextWindow?: number | null; percent?: number | null } | null;
130
+ }): void {
131
+ const store = getMetricsStore();
132
+ if (!store) return;
133
+
134
+ try {
135
+ const projectSlug = basename(getProjectStateDir(opts.platform.paths, opts.cwd));
136
+ const sortedActiveTools = [...opts.activeTools].sort();
137
+ const sourceHash = createHash("sha256")
138
+ .update(projectSlug)
139
+ .update("\0")
140
+ .update(sortedActiveTools.join("\0"))
141
+ .digest("hex");
142
+
143
+ store.record({
144
+ session_id: getSessionId(),
145
+ ts: Date.now(),
146
+ layer: "L7",
147
+ tool: "(system)",
148
+ processor: "lazy-tools",
149
+ before_bytes: byteLength(opts.beforePrompt),
150
+ after_bytes: byteLength(opts.afterPrompt),
151
+ cache_hit: 0,
152
+ unique_source_hash: sourceHash,
153
+ context_tokens: opts.contextUsage?.tokens ?? null,
154
+ context_window: opts.contextUsage?.contextWindow ?? null,
155
+ context_percent: opts.contextUsage?.percent ?? null,
156
+ });
157
+ } catch (error) {
158
+ (opts.platform as any).logger?.warn?.("supi-lazy-tools: metrics record failed", error);
159
+ }
160
+ }
161
+
162
+ function byteLength(value: string): number {
163
+ return new TextEncoder().encode(value).byteLength;
164
+ }
@@ -0,0 +1,212 @@
1
+ import { computeActiveServers } from "../mcp/activation.js";
2
+ import type { ServerConfig } from "../mcp/types.js";
3
+ import type { ContextModeLazyToolsConfig } from "../types.js";
4
+ import {
5
+ BALANCED_KEYWORD_TOOLS,
6
+ CONTEXT_MODE_TOOL_NAMES,
7
+ MCPC_MANAGER_TOOL_NAME,
8
+ isSupiOwnedTool,
9
+ orderOwnedTools,
10
+ } from "./tool-groups.js";
11
+
12
+ export interface ActiveToolPlannerDiagnostics {
13
+ unknownConfiguredTools: string[];
14
+ unavailableTools: string[];
15
+ unmatchedTags: string[];
16
+ missingMcpGatewayTools: string[];
17
+ }
18
+
19
+ export interface PlanActiveToolsInput {
20
+ prompt: string;
21
+ currentActive: string[];
22
+ allTools: string[];
23
+ lazyTools: ContextModeLazyToolsConfig;
24
+ mcpServers?: Record<string, ServerConfig>;
25
+ pendingTags?: string[];
26
+ cacheHandlesEnabled?: boolean;
27
+ }
28
+
29
+ export interface ActiveToolPlan {
30
+ activeTools: string[];
31
+ activated: string[];
32
+ deactivated: string[];
33
+ diagnostics: ActiveToolPlannerDiagnostics;
34
+ }
35
+
36
+ const RARE_CONTEXT_TOOLS = new Set(["ctx_stats", "ctx_purge"]);
37
+
38
+ export function planActiveTools(input: PlanActiveToolsInput): ActiveToolPlan {
39
+ const registeredOwnedTools = new Set(input.allTools.filter(isSupiOwnedTool));
40
+ const selectedOwnedTools = new Set<string>();
41
+ const diagnostics: ActiveToolPlannerDiagnostics = {
42
+ unknownConfiguredTools: [],
43
+ unavailableTools: [],
44
+ unmatchedTags: [],
45
+ missingMcpGatewayTools: [],
46
+ };
47
+
48
+ const addRegisteredTool = (toolName: string, source: "config" | "policy"): void => {
49
+ if (!isSupiOwnedTool(toolName)) {
50
+ if (source === "config") diagnostics.unknownConfiguredTools.push(toolName);
51
+ return;
52
+ }
53
+ if (!registeredOwnedTools.has(toolName)) {
54
+ if (source === "config") diagnostics.unavailableTools.push(toolName);
55
+ return;
56
+ }
57
+ selectedOwnedTools.add(toolName);
58
+ };
59
+
60
+ for (const toolName of input.lazyTools.alwaysKeep) {
61
+ addRegisteredTool(toolName, "config");
62
+ }
63
+
64
+ if (input.cacheHandlesEnabled) {
65
+ addRegisteredTool("ctx_open_cached", "policy");
66
+ }
67
+
68
+ if (input.lazyTools.mode === "conservative") {
69
+ for (const toolName of CONTEXT_MODE_TOOL_NAMES) {
70
+ if (!RARE_CONTEXT_TOOLS.has(toolName)) addRegisteredTool(toolName, "policy");
71
+ }
72
+ addRegisteredTool(MCPC_MANAGER_TOOL_NAME, "policy");
73
+ }
74
+
75
+ for (const toolName of getTriggeredTools(input.prompt, BALANCED_KEYWORD_TOOLS)) {
76
+ addRegisteredTool(toolName, "policy");
77
+ }
78
+
79
+ for (const toolName of getTriggeredTools(input.prompt, input.lazyTools.keywordTools)) {
80
+ addRegisteredTool(toolName, "config");
81
+ }
82
+
83
+
84
+ const mcpServers = input.mcpServers ?? {};
85
+ const pendingTags = input.pendingTags ?? [];
86
+ const knownServerNames = new Set(Object.keys(mcpServers));
87
+ for (const tag of pendingTags) {
88
+ if (!knownServerNames.has(tag)) diagnostics.unmatchedTags.push(tag);
89
+ }
90
+ for (const serverName of computeActiveServers(mcpServers, input.prompt, pendingTags)) {
91
+ const gatewayToolName = `mcpc_${serverName}`;
92
+ if (registeredOwnedTools.has(gatewayToolName)) {
93
+ selectedOwnedTools.add(gatewayToolName);
94
+ } else {
95
+ diagnostics.missingMcpGatewayTools.push(gatewayToolName);
96
+ }
97
+ }
98
+ for (const toolName of getCommandAllowlistTools(input.prompt, input.lazyTools.commandAllowlist)) {
99
+ addRegisteredTool(toolName, "config");
100
+ }
101
+
102
+ const activeTools = orderPlannedTools({
103
+ currentActive: input.currentActive,
104
+ selectedOwnedTools,
105
+ });
106
+
107
+ return {
108
+ activeTools,
109
+ activated: activeTools.filter((toolName) => !input.currentActive.includes(toolName)),
110
+ deactivated: input.currentActive.filter((toolName) => !activeTools.includes(toolName)),
111
+ diagnostics: dedupeDiagnostics(diagnostics),
112
+ };
113
+ }
114
+
115
+ function orderPlannedTools(input: {
116
+ currentActive: string[];
117
+ selectedOwnedTools: Set<string>;
118
+ }): string[] {
119
+ const activeTools: string[] = [];
120
+ const push = (toolName: string): void => {
121
+ if (!activeTools.includes(toolName)) activeTools.push(toolName);
122
+ };
123
+
124
+ for (const toolName of input.currentActive) {
125
+ if (!isSupiOwnedTool(toolName)) push(toolName);
126
+ }
127
+
128
+ for (const toolName of input.currentActive) {
129
+ if (input.selectedOwnedTools.has(toolName)) push(toolName);
130
+ }
131
+
132
+ const newlySelectedOwnedTools = [...input.selectedOwnedTools].filter(
133
+ (toolName) => !input.currentActive.includes(toolName),
134
+ );
135
+ for (const toolName of orderOwnedTools(newlySelectedOwnedTools)) {
136
+ push(toolName);
137
+ }
138
+
139
+ return activeTools;
140
+ }
141
+
142
+ function getTriggeredTools(prompt: string, keywordTools: Record<string, string[]>): string[] {
143
+ const normalizedPrompt = normalizePrompt(prompt);
144
+ const triggeredTools: string[] = [];
145
+
146
+ if (/https?:\/\/\S+/i.test(prompt)) {
147
+ triggeredTools.push("ctx_fetch_and_index");
148
+ }
149
+ if (/cache:\/\/[a-f0-9]{8,}/i.test(prompt)) {
150
+ triggeredTools.push("ctx_open_cached");
151
+ }
152
+
153
+ for (const [term, tools] of Object.entries(keywordTools)) {
154
+ if (literalTermMatches(normalizedPrompt, term)) {
155
+ triggeredTools.push(...tools);
156
+ }
157
+ }
158
+
159
+ return triggeredTools;
160
+ }
161
+
162
+ function getCommandAllowlistTools(
163
+ prompt: string,
164
+ commandAllowlist: Record<string, string[]>,
165
+ ): string[] {
166
+ const commandName = parseLeadingCommandName(prompt);
167
+ if (!commandName) return [];
168
+ return commandAllowlist[commandName] ?? [];
169
+ }
170
+
171
+ function parseLeadingCommandName(prompt: string): string | null {
172
+ const trimmed = prompt.trimStart();
173
+ if (!trimmed.startsWith("/")) return null;
174
+ const command = trimmed.slice(1).split(/\s+/, 1)[0]?.toLowerCase();
175
+ return command || null;
176
+ }
177
+
178
+ function literalTermMatches(normalizedPrompt: string, rawTerm: string): boolean {
179
+ const normalizedTerm = normalizePrompt(rawTerm);
180
+ if (!normalizedTerm) return false;
181
+ if (/\s/.test(normalizedTerm)) return normalizedPrompt.includes(normalizedTerm);
182
+ return containsSingleTokenLiteral(normalizedPrompt, normalizedTerm);
183
+ }
184
+
185
+ function containsSingleTokenLiteral(normalizedPrompt: string, normalizedTerm: string): boolean {
186
+ let start = normalizedPrompt.indexOf(normalizedTerm);
187
+ while (start !== -1) {
188
+ const before = start === 0 ? "" : normalizedPrompt[start - 1];
189
+ const afterIndex = start + normalizedTerm.length;
190
+ const after = afterIndex >= normalizedPrompt.length ? "" : normalizedPrompt[afterIndex];
191
+ if (!isAsciiWordChar(before) && !isAsciiWordChar(after)) return true;
192
+ start = normalizedPrompt.indexOf(normalizedTerm, start + 1);
193
+ }
194
+ return false;
195
+ }
196
+
197
+ function isAsciiWordChar(char: string): boolean {
198
+ return /^[A-Za-z0-9_]$/.test(char);
199
+ }
200
+
201
+ function normalizePrompt(value: string): string {
202
+ return value.toLowerCase().replace(/\s+/g, " ").trim();
203
+ }
204
+
205
+ function dedupeDiagnostics(diagnostics: ActiveToolPlannerDiagnostics): ActiveToolPlannerDiagnostics {
206
+ return {
207
+ unknownConfiguredTools: [...new Set(diagnostics.unknownConfiguredTools)],
208
+ unavailableTools: [...new Set(diagnostics.unavailableTools)],
209
+ unmatchedTags: [...new Set(diagnostics.unmatchedTags)],
210
+ missingMcpGatewayTools: [...new Set(diagnostics.missingMcpGatewayTools)],
211
+ };
212
+ }
@@ -0,0 +1,102 @@
1
+ export const CONTEXT_MODE_TOOL_NAMES = [
2
+ "ctx_execute",
3
+ "ctx_execute_file",
4
+ "ctx_batch_execute",
5
+ "ctx_index",
6
+ "ctx_search",
7
+ "ctx_open_cached",
8
+ "ctx_fetch_and_index",
9
+ "ctx_stats",
10
+ "ctx_purge",
11
+ "ctx_repomap",
12
+ "ctx_symbol",
13
+ ] as const;
14
+
15
+ export type ContextModeToolName = (typeof CONTEXT_MODE_TOOL_NAMES)[number];
16
+
17
+ export const MCPC_MANAGER_TOOL_NAME = "mcpc_manager";
18
+ export const MCPC_TOOL_PREFIX = "mcpc_";
19
+
20
+ export const OWNED_TOOL_PRIORITY = [
21
+ "ctx_execute",
22
+ "ctx_search",
23
+ "ctx_open_cached",
24
+ "ctx_batch_execute",
25
+ "ctx_execute_file",
26
+ "ctx_fetch_and_index",
27
+ "ctx_index",
28
+ "ctx_stats",
29
+ "ctx_purge",
30
+ "ctx_repomap",
31
+ "ctx_symbol",
32
+ MCPC_MANAGER_TOOL_NAME,
33
+ ] as const;
34
+
35
+ const CONTEXT_MODE_TOOL_SET = new Set<string>(CONTEXT_MODE_TOOL_NAMES);
36
+ const OWNED_PRIORITY_INDEX = new Map<string, number>(
37
+ OWNED_TOOL_PRIORITY.map((name, index) => [name, index]),
38
+ );
39
+
40
+ export const BALANCED_KEYWORD_TOOLS: Record<string, string[]> = {
41
+ search: ["ctx_batch_execute"],
42
+ grep: ["ctx_batch_execute"],
43
+ find: ["ctx_batch_execute"],
44
+ scan: ["ctx_batch_execute"],
45
+ inspect: ["ctx_batch_execute"],
46
+ explore: ["ctx_batch_execute"],
47
+ "analyze repo": ["ctx_batch_execute"],
48
+ "project-wide": ["ctx_batch_execute"],
49
+ todo: ["ctx_batch_execute"],
50
+ references: ["ctx_batch_execute"],
51
+ "large file": ["ctx_execute_file"],
52
+ "summarize file": ["ctx_execute_file"],
53
+ "extract from file": ["ctx_execute_file"],
54
+ "process file without reading": ["ctx_execute_file"],
55
+ http: ["ctx_fetch_and_index"],
56
+ https: ["ctx_fetch_and_index"],
57
+ curl: ["ctx_fetch_and_index"],
58
+ wget: ["ctx_fetch_and_index"],
59
+ "fetch docs": ["ctx_fetch_and_index"],
60
+ "web page": ["ctx_fetch_and_index"],
61
+ "index this": ["ctx_index"],
62
+ "store in knowledge": ["ctx_index"],
63
+ "make searchable": ["ctx_index"],
64
+ "context stats": ["ctx_stats"],
65
+ "token usage": ["ctx_stats"],
66
+ savings: ["ctx_stats"],
67
+ "ctx stats": ["ctx_stats"],
68
+ "ctx purge": ["ctx_purge"],
69
+ "purge knowledge": ["ctx_purge"],
70
+ "reset knowledge index": ["ctx_purge"],
71
+ "open cached": ["ctx_open_cached"],
72
+ "cached handle": ["ctx_open_cached"],
73
+ ctx_open_cached: ["ctx_open_cached"],
74
+ repomap: ["ctx_repomap"],
75
+ "repo map": ["ctx_repomap"],
76
+ symbol: ["ctx_symbol"],
77
+ symbols: ["ctx_symbol"],
78
+ };
79
+
80
+ export function isContextModeTool(name: string): boolean {
81
+ return CONTEXT_MODE_TOOL_SET.has(name);
82
+ }
83
+
84
+ export function isMcpcGatewayTool(name: string): boolean {
85
+ return name.startsWith(MCPC_TOOL_PREFIX) && /^mcpc_[^_].+/.test(name);
86
+ }
87
+
88
+ export function isSupiOwnedTool(name: string): boolean {
89
+ return isContextModeTool(name) || name === MCPC_MANAGER_TOOL_NAME || isMcpcGatewayTool(name);
90
+ }
91
+
92
+ export function orderOwnedTools(names: Iterable<string>): string[] {
93
+ return [...new Set([...names].filter(isSupiOwnedTool))].sort((a, b) => {
94
+ const aPriority = OWNED_PRIORITY_INDEX.get(a);
95
+ const bPriority = OWNED_PRIORITY_INDEX.get(b);
96
+
97
+ if (aPriority !== undefined && bPriority !== undefined) return aPriority - bPriority;
98
+ if (aPriority !== undefined) return -1;
99
+ if (bPriority !== undefined) return 1;
100
+ return a.localeCompare(b);
101
+ });
102
+ }