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
@@ -1,31 +1,72 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
1
3
  import type { Platform, PlatformContext } from "../platform/types.js";
4
+ import { systemPromptText } from "../platform/system-prompt.js";
2
5
  import {
3
6
  parseSystemPrompt,
4
7
  parseIndividualSkills,
5
8
  } from "../context/analyzer.js";
9
+ import type { ParsedSkill, PromptSection } from "../context/analyzer.js";
6
10
  import {
7
11
  detectTechStack,
8
12
  buildContextReport,
9
13
  } from "../context/optimizer.js";
10
14
  import type { ContextReport } from "../context/optimizer.js";
15
+ import {
16
+ buildOptimizationPlan,
17
+ } from "../context/startup-optimizer.js";
18
+ import type {
19
+ ManualOptimizationAction,
20
+ OptimizationPlan,
21
+ WriteRuleAction,
22
+ } from "../context/startup-optimizer.js";
23
+ import { parseManagedRule, renderManagedRule } from "../context/rule-renderer.js";
24
+ import { DEFAULT_TOKENIGNORE_ENTRIES, mergeManagedTokenignore } from "../context/tokenignore.js";
25
+ import {
26
+ parseStartupOptimizerManifest,
27
+ runStartupCheck,
28
+ } from "../context/startup-check.js";
29
+ import type { StartupCheckReport, StartupOptimizerManifest } from "../context/startup-check.js";
30
+ import { getMetricsStore, getSessionId } from "../context-mode/hooks.js";
11
31
 
12
32
  function formatTokens(n: number): string {
13
33
  return n >= 1000 ? `${(n / 1000).toFixed(1)}K` : String(n);
14
34
  }
15
35
 
16
- export function handleOptimizeContext(platform: Platform, ctx: PlatformContext): void {
17
- void (async () => {
18
- if (!ctx.hasUI) return;
36
+ interface OptimizeContextArgs {
37
+ apply: boolean;
38
+ check: boolean;
39
+ dryRun: boolean;
40
+ }
19
41
 
20
- // 1. Detect tech stack
21
- const techStack = await detectTechStack(platform, ctx.cwd);
42
+ function parseOptimizeContextArgs(args: string | undefined): OptimizeContextArgs {
43
+ const tokens = (args ?? "").trim().split(/\s+/).filter(Boolean);
44
+ return {
45
+ apply: tokens.includes("--apply"),
46
+ check: tokens.includes("--check"),
47
+ dryRun: tokens.includes("--dry-run"),
48
+ };
49
+ }
22
50
 
23
- // 2. Get system prompt
24
- let systemPrompt = "";
25
- try {
26
- systemPrompt = (ctx as any).getSystemPrompt?.() ?? "";
27
- } catch {
28
- // getSystemPrompt not available
51
+ /**
52
+ * Handle the `/supi:optimize-context` command. Returns a Promise so that callers
53
+ * (and tests) can await the full flow; the registered command handler does not
54
+ * need to await exceptions are caught and surfaced via `ctx.ui.notify`.
55
+ */
56
+ export async function handleOptimizeContext(
57
+ platform: Platform,
58
+ ctx: PlatformContext,
59
+ args?: string,
60
+ ): Promise<void> {
61
+ if (!ctx.hasUI) return;
62
+
63
+ try {
64
+ const parsedArgs = parseOptimizeContextArgs(args);
65
+ const systemPrompt = readSystemPrompt(ctx);
66
+
67
+ if (parsedArgs.check) {
68
+ await runCheck(platform, ctx, systemPrompt);
69
+ return;
29
70
  }
30
71
 
31
72
  if (!systemPrompt) {
@@ -33,35 +74,367 @@ export function handleOptimizeContext(platform: Platform, ctx: PlatformContext):
33
74
  return;
34
75
  }
35
76
 
36
- // 3. Parse
77
+ const techStack = await detectTechStack(platform, ctx.cwd);
37
78
  const sections = parseSystemPrompt(systemPrompt);
38
79
  const skills = parseIndividualSkills(systemPrompt);
39
-
40
- // 4. Build raw report
41
80
  const report = buildContextReport(sections, skills, techStack);
81
+ const plan = buildOptimizationPlan({
82
+ prompt: systemPrompt,
83
+ sections,
84
+ skills,
85
+ techStack,
86
+ });
42
87
 
43
- // 5. Show TUI
44
- await showReport(platform, ctx, report);
45
- })().catch((err) => {
88
+ if (parsedArgs.dryRun) {
89
+ await showDryRun(ctx, plan);
90
+ return;
91
+ }
92
+
93
+ if (parsedArgs.apply) {
94
+ await applyOptimizationPlan(platform, ctx, plan);
95
+ return;
96
+ }
97
+
98
+ await showReport(platform, ctx, report, plan);
99
+ } catch (err) {
46
100
  ctx.ui.notify(`Optimize error: ${(err as Error).message}`, "error");
47
- });
101
+ }
48
102
  }
49
103
 
50
104
  export function registerOptimizeContextCommand(platform: Platform): void {
51
105
  platform.registerCommand("supi:optimize-context", {
52
106
  description: "Analyze context usage and suggest token optimizations",
53
- async handler(_args: string | undefined, ctx: any) {
54
- handleOptimizeContext(platform, ctx);
107
+ async handler(args: string | undefined, ctx: any) {
108
+ await handleOptimizeContext(platform, ctx, args);
55
109
  },
56
110
  });
57
111
  }
58
112
 
59
113
  // ── Internal ──────────────────────────────────────────────
60
114
 
115
+ function readSystemPrompt(ctx: PlatformContext): string | null {
116
+ try {
117
+ const value = (ctx as any).getSystemPrompt?.();
118
+ const text = systemPromptText(value);
119
+ return text.length > 0 ? text : null;
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
124
+
125
+ async function runCheck(
126
+ platform: Platform,
127
+ ctx: PlatformContext,
128
+ currentPrompt: string | null,
129
+ ): Promise<void> {
130
+ const manifestPath = platform.paths.project(ctx.cwd, "context-optimizer", "manifest.json");
131
+ const tokenignorePath = platform.paths.project(ctx.cwd, ".tokenignore");
132
+ const manifestText = readOptionalFile(manifestPath);
133
+ // Use the same parser the checker uses so the command cannot accept a manifest
134
+ // shape that the check would later reject.
135
+ const parsedManifest = parseStartupOptimizerManifest(manifestText, manifestPath);
136
+ const manifest = typeof parsedManifest === "string" ? null : parsedManifest;
137
+
138
+ const ruleFiles: Record<string, string | null> = {};
139
+ if (manifest) {
140
+ for (const rule of manifest.rules) {
141
+ ruleFiles[rule.path] = readOptionalFile(path.join(ctx.cwd, rule.path));
142
+ }
143
+ }
144
+
145
+ const currentSkills: ParsedSkill[] = currentPrompt ? parseIndividualSkills(currentPrompt) : [];
146
+ const currentSections: PromptSection[] = currentPrompt ? parseSystemPrompt(currentPrompt) : [];
147
+
148
+ const report = runStartupCheck({
149
+ manifestPath,
150
+ manifestText,
151
+ ruleFiles,
152
+ tokenignorePath,
153
+ tokenignoreText: readOptionalFile(tokenignorePath),
154
+ currentPrompt,
155
+ currentSkills,
156
+ currentSections,
157
+ });
158
+
159
+ if (report.status === "pass") {
160
+ recordStartupOptimizerMetric(report);
161
+ }
162
+
163
+ ctx.ui.notify(formatCheckReport(report), report.status === "pass" ? "info" : "error");
164
+ }
165
+
166
+ function readOptionalFile(filePath: string): string | null {
167
+ try {
168
+ return fs.readFileSync(filePath, "utf-8");
169
+ } catch {
170
+ return null;
171
+ }
172
+ }
173
+
174
+ function formatCheckReport(report: StartupCheckReport): string {
175
+ const lines = [`Startup optimization check: ${report.status}`];
176
+ if (report.currentBytes != null) {
177
+ lines.push(`Current prompt: ${report.currentBytes} bytes (~${formatTokens(Math.ceil(report.currentBytes / 4))} tokens estimated)`);
178
+ }
179
+ if (report.targetBytes != null) {
180
+ lines.push(`Target: ${report.targetBytes} bytes (~${formatTokens(Math.ceil(report.targetBytes / 4))} tokens estimated)`);
181
+ }
182
+ if (report.issues.length > 0) {
183
+ lines.push("Issues:");
184
+ for (const issue of report.issues) {
185
+ const location = issue.path ? ` at ${issue.path}` : issue.sourceId ? ` for ${issue.sourceId}` : "";
186
+ lines.push(`- ${issue.reason}${location}: ${issue.remediation}`);
187
+ }
188
+ if (report.issues.some((entry) => RUNTIME_DEPENDENT_REASONS.has(entry.reason))) {
189
+ lines.push(
190
+ "Note: rule discovery happens at OMP process startup. If you just ran --apply, restart OMP before running --check so the runtime actually picks up the managed rules in .omp/rules.",
191
+ );
192
+ }
193
+ }
194
+ return lines.join("\n");
195
+ }
196
+
197
+ const RUNTIME_DEPENDENT_REASONS = new Set([
198
+ "still-loaded-source",
199
+ "prompt-over-target",
200
+ "unresolved-manual-action",
201
+ ]);
202
+
203
+ function recordStartupOptimizerMetric(report: StartupCheckReport): void {
204
+ const store = getMetricsStore();
205
+ if (!store || report.currentBytes == null || report.beforeBytes == null) return;
206
+ try {
207
+ store.record({
208
+ session_id: getSessionId() || "startup-optimizer-check",
209
+ ts: Date.now(),
210
+ layer: "L6",
211
+ tool: "(system)",
212
+ processor: "startup-optimizer",
213
+ before_bytes: report.beforeBytes,
214
+ after_bytes: report.currentBytes,
215
+ cache_hit: 0,
216
+ unique_source_hash: report.sourceSetHash,
217
+ context_tokens: null,
218
+ context_window: null,
219
+ context_percent: null,
220
+ });
221
+ } catch {
222
+ // Metrics are best-effort; check results must still be surfaced.
223
+ }
224
+ }
225
+
226
+ async function showDryRun(ctx: PlatformContext, plan: OptimizationPlan): Promise<void> {
227
+ const lines = buildPlanPreview(plan);
228
+ await ctx.ui.select("Context Optimization Dry Run", lines, {
229
+ helpText: "Dry-run: no files will be written · Esc to close",
230
+ });
231
+ }
232
+
233
+ function buildPlanPreview(plan: OptimizationPlan): string[] {
234
+ const writeRuleCount = plan.actions.filter((action) => action.kind === "write-rule").length;
235
+ const manualCount = plan.actions.filter((action) => action.kind !== "write-rule").length;
236
+ const lines = [
237
+ `Source set: ${plan.sourceSetHash.slice(0, 12)}`,
238
+ `Current: ~${formatTokens(Math.ceil(plan.beforeBytes / 4))} tokens | Estimated after planned removals: ~${formatTokens(Math.ceil(plan.estimatedAfterBytes / 4))} tokens`,
239
+ `Actions: ${writeRuleCount} write-rule, ${manualCount} manual`,
240
+ "",
241
+ ];
242
+
243
+ for (const action of plan.actions) {
244
+ if (action.kind === "write-rule") {
245
+ lines.push(`write-rule ${action.mode}: ${action.targetPath}`);
246
+ } else if (action.kind === "manual-disable") {
247
+ lines.push(`manual-disable ${action.sourceName}: ${action.reason}`);
248
+ } else {
249
+ lines.push(`manual-agents-split ${action.sourceName}: ${action.sourceBytes} bytes`);
250
+ }
251
+ }
252
+
253
+ lines.push("", "Close");
254
+ return lines;
255
+ }
256
+
257
+ async function applyOptimizationPlan(
258
+ platform: Platform,
259
+ ctx: PlatformContext,
260
+ plan: OptimizationPlan,
261
+ ): Promise<void> {
262
+ const manifestPath = platform.paths.project(ctx.cwd, "context-optimizer", "manifest.json");
263
+ const tokenignorePath = platform.paths.project(ctx.cwd, ".tokenignore");
264
+ const manifestPreflight = preflightExistingManifest(manifestPath);
265
+ if (manifestPreflight) {
266
+ ctx.ui.notify(manifestPreflight, "error");
267
+ return;
268
+ }
269
+
270
+ const writeRules = plan.actions.filter((action): action is WriteRuleAction => action.kind === "write-rule");
271
+ const preflight = preflightRuleWrites(ctx.cwd, writeRules);
272
+ if (preflight.conflicts.length > 0) {
273
+ ctx.ui.notify(`Apply blocked: ${preflight.conflicts.join("; ")}`, "error");
274
+ return;
275
+ }
276
+
277
+ if (fs.existsSync(tokenignorePath) && !fs.statSync(tokenignorePath).isFile()) {
278
+ ctx.ui.notify(`Apply blocked: ${tokenignorePath} is not a file.`, "error");
279
+ return;
280
+ }
281
+
282
+ const summary = buildApplySummary(plan, preflight.updates);
283
+ const accepted = await confirmApply(ctx, summary);
284
+ if (!accepted) {
285
+ ctx.ui.notify("Apply cancelled.", "info");
286
+ return;
287
+ }
288
+
289
+ const manifest = buildManifest(plan);
290
+ const tokenignoreExisting = fs.existsSync(tokenignorePath)
291
+ ? fs.readFileSync(tokenignorePath, "utf-8")
292
+ : null;
293
+ const tokenignore = mergeManagedTokenignore(tokenignoreExisting, DEFAULT_TOKENIGNORE_ENTRIES);
294
+
295
+ try {
296
+ for (const action of writeRules) {
297
+ const target = absoluteRulePath(ctx.cwd, action.targetPath);
298
+ fs.mkdirSync(path.dirname(target), { recursive: true });
299
+ fs.writeFileSync(target, renderManagedRule(action));
300
+ }
301
+
302
+ fs.mkdirSync(path.dirname(tokenignorePath), { recursive: true });
303
+ fs.writeFileSync(tokenignorePath, tokenignore.content);
304
+
305
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
306
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
307
+ } catch (error) {
308
+ ctx.ui.notify(`Apply failed before manifest completion: ${(error as Error).message}`, "error");
309
+ return;
310
+ }
311
+
312
+ // OMP discovers rules during session construction (sdk.ts `createAgentSession`)
313
+ // and the in-process rebuild callback reuses captured rule arrays. Neither
314
+ // `ctx.reload()` nor `newSession()` re-runs that discovery, so the current
315
+ // process can't see the just-written .omp/rules without a full restart.
316
+ // Be honest about this rather than silently calling reload() and lying.
317
+ ctx.ui.notify(
318
+ "Applied deterministic context migration. Restart OMP so the new managed rules in .omp/rules are picked up by rule discovery, then disable the original sources and run /supi:optimize-context --check to validate runtime savings.",
319
+ "info",
320
+ );
321
+ }
322
+
323
+ function preflightExistingManifest(manifestPath: string): string | null {
324
+ if (!fs.existsSync(manifestPath)) return null;
325
+ if (!fs.statSync(manifestPath).isFile()) {
326
+ return `Remove or repair ${manifestPath}: existing manifest path is not a file.`;
327
+ }
328
+ const text = fs.readFileSync(manifestPath, "utf-8");
329
+ const parsed = parseStartupOptimizerManifest(text, manifestPath);
330
+ if (typeof parsed === "string") {
331
+ return `Remove or repair malformed startup optimizer manifest at ${manifestPath} before applying.`;
332
+ }
333
+ return null;
334
+ }
335
+
336
+ function preflightRuleWrites(
337
+ cwd: string,
338
+ actions: WriteRuleAction[],
339
+ ): { conflicts: string[]; updates: string[] } {
340
+ const conflicts: string[] = [];
341
+ const updates: string[] = [];
342
+
343
+ for (const action of actions) {
344
+ const target = absoluteRulePath(cwd, action.targetPath);
345
+ if (!fs.existsSync(target)) continue;
346
+ const stat = fs.statSync(target);
347
+ if (!stat.isFile()) {
348
+ conflicts.push(`${action.targetPath} exists and is not a file`);
349
+ continue;
350
+ }
351
+
352
+ const parsed = parseManagedRule(fs.readFileSync(target, "utf-8"));
353
+ if (parsed.status === "unmanaged") {
354
+ conflicts.push(`${action.targetPath} is unmanaged`);
355
+ continue;
356
+ }
357
+ if (parsed.status === "malformed") {
358
+ conflicts.push(`${action.targetPath} is malformed: ${parsed.error}`);
359
+ continue;
360
+ }
361
+ if (
362
+ parsed.metadata.sourceHash !== action.sourceHash ||
363
+ parsed.metadata.sourceId !== action.sourceId ||
364
+ parsed.metadata.mode !== action.mode
365
+ ) {
366
+ updates.push(action.targetPath);
367
+ }
368
+ }
369
+
370
+ return { conflicts, updates };
371
+ }
372
+
373
+ function buildApplySummary(plan: OptimizationPlan, updates: string[]): string {
374
+ const writeRuleCount = plan.actions.filter((action) => action.kind === "write-rule").length;
375
+ const manualCount = plan.actions.filter((action) => action.kind !== "write-rule").length;
376
+ const lines = [
377
+ `Write ${writeRuleCount} managed rule file${writeRuleCount === 1 ? "" : "s"}.`,
378
+ `Merge ${DEFAULT_TOKENIGNORE_ENTRIES.length} managed .tokenignore entries.`,
379
+ `Record ${manualCount} manual follow-up action${manualCount === 1 ? "" : "s"}.`,
380
+ `Estimated savings after planned removals: ~${formatTokens(Math.ceil(plan.estimatedSavedBytes / 4))} tokens.`,
381
+ ];
382
+ if (updates.length > 0) {
383
+ lines.push(`Managed update candidate${updates.length === 1 ? "" : "s"}: ${updates.join(", ")}`);
384
+ }
385
+ lines.push("Manifest is written last after managed rule and tokenignore writes succeed.");
386
+ return lines.join("\n");
387
+ }
388
+
389
+ async function confirmApply(ctx: PlatformContext, summary: string): Promise<boolean> {
390
+ const choice = await ctx.ui.select("Apply deterministic context migration?", ["Apply", "Cancel"], {
391
+ helpText: summary,
392
+ });
393
+ return choice === "Apply";
394
+ }
395
+
396
+ function buildManifest(plan: OptimizationPlan): StartupOptimizerManifest {
397
+ const tokenignore = mergeManagedTokenignore(null, DEFAULT_TOKENIGNORE_ENTRIES);
398
+ const rules = plan.actions
399
+ .filter((action): action is WriteRuleAction => action.kind === "write-rule")
400
+ .map((action) => ({
401
+ path: action.targetPath,
402
+ mode: action.mode,
403
+ sourceId: action.sourceId,
404
+ sourceName: action.sourceName,
405
+ sourceHash: action.sourceHash,
406
+ slug: action.slug,
407
+ sourceBytes: action.sourceBytes,
408
+ ...(action.condition ? { condition: action.condition } : {}),
409
+ ...(action.description ? { description: action.description } : {}),
410
+ }));
411
+
412
+ return {
413
+ version: 1,
414
+ targetBytes: plan.targetBytes,
415
+ sourceSetHash: plan.sourceSetHash,
416
+ beforeBytes: plan.beforeBytes,
417
+ estimatedAfterBytes: plan.estimatedAfterBytes,
418
+ estimatedSavedBytes: plan.estimatedSavedBytes,
419
+ rules,
420
+ tokenignore: {
421
+ path: ".omp/supipowers/.tokenignore",
422
+ entries: tokenignore.entries,
423
+ hash: tokenignore.hash,
424
+ },
425
+ manualActions: plan.actions.filter((action): action is ManualOptimizationAction => action.kind !== "write-rule"),
426
+ };
427
+ }
428
+
429
+ function absoluteRulePath(cwd: string, targetPath: string): string {
430
+ return path.join(cwd, targetPath);
431
+ }
432
+
61
433
  async function showReport(
62
434
  platform: Platform,
63
435
  ctx: PlatformContext,
64
436
  report: ContextReport,
437
+ plan: OptimizationPlan,
65
438
  ): Promise<void> {
66
439
  const techList = [
67
440
  ...report.techStack.languages,
@@ -76,7 +449,6 @@ async function showReport(
76
449
  "",
77
450
  ];
78
451
 
79
- // Skills breakdown
80
452
  if (report.skills.length === 0) {
81
453
  lines.push("No skills detected in system prompt.");
82
454
  } else {
@@ -90,7 +462,6 @@ async function showReport(
90
462
  }
91
463
  }
92
464
 
93
- // Non-skill sections
94
465
  if (report.sections.length > 0) {
95
466
  lines.push("");
96
467
  lines.push("Other sections:");
@@ -105,12 +476,22 @@ async function showReport(
105
476
 
106
477
  const message = lines.join("\n");
107
478
 
108
- // confirm() may not be available on all platforms
109
- const shouldOptimize = ctx.ui.confirm
110
- ? await ctx.ui.confirm("Context Optimization", message)
111
- : (await ctx.ui.select("Context Optimization", [message, "▶ Optimize with AI", "Close"]))?.includes("Optimize");
479
+ const choice = await ctx.ui.select(
480
+ "Context Optimization",
481
+ [message, "▶ Optimize with AI", "Apply deterministic migration", "Run check", "Close"],
482
+ { helpText: "Select an action · Esc to close" },
483
+ );
112
484
 
113
- if (!shouldOptimize) return;
485
+ if (!choice || choice === "Close") return;
486
+ if (choice === "Apply deterministic migration") {
487
+ await applyOptimizationPlan(platform, ctx, plan);
488
+ return;
489
+ }
490
+ if (choice === "Run check") {
491
+ await runCheck(platform, ctx, readSystemPrompt(ctx));
492
+ return;
493
+ }
494
+ if (!choice.includes("Optimize")) return;
114
495
 
115
496
  platform.sendMessage(
116
497
  {
@@ -138,7 +519,6 @@ function buildOptimizationPrompt(report: ContextReport): string {
138
519
  lines.push(`Project tech stack: **${techList || "unknown"}**`);
139
520
  lines.push("");
140
521
 
141
- // Skill inventory
142
522
  if (report.skills.length > 0) {
143
523
  lines.push("## Skills currently loaded");
144
524
  lines.push("");
@@ -151,7 +531,6 @@ function buildOptimizationPrompt(report: ContextReport): string {
151
531
  lines.push("");
152
532
  }
153
533
 
154
- // Section inventory
155
534
  if (report.sections.length > 0) {
156
535
  lines.push("## Other prompt sections");
157
536
  lines.push("");
@@ -16,6 +16,7 @@ import * as path from "node:path";
16
16
  import { modelRegistry } from "../config/model-registry-instance.js";
17
17
  import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
18
18
  import { loadModelConfig } from "../config/model-config.js";
19
+ import { getProjectStatePath } from "../workspace/state-paths.js";
19
20
  import { cancelPlanTracking, startPlanTracking } from "../planning/approval-flow.js";
20
21
  import { stopVisualServer } from "../visual/stop-server.js";
21
22
 
@@ -158,6 +159,7 @@ export function registerPlanCommand(platform: Platform): void {
158
159
  topic: planningTopic,
159
160
  skillContent: skillContent || undefined,
160
161
  dotDirDisplay: platform.paths.dotDirDisplay,
162
+ plansDir: getProjectStatePath(platform.paths, ctx.cwd, "plans"),
161
163
  isQuick: quickMode,
162
164
  };
163
165