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,116 @@
1
+ import type { ContextModeProcessorFamily, ContextModeProcessorsConfig } from "../../types.js";
2
+ import type { Processor } from "./types.js";
3
+ import { gitProcessor } from "./git.js";
4
+ import { testRunnerProcessor } from "./test-runner.js";
5
+ import { jsonContentSniff, jsonProcessor } from "./json.js";
6
+ import { logContentSniff, logProcessor } from "./log.js";
7
+ import { lintProcessor } from "./lint.js";
8
+ import { buildProcessor } from "./build.js";
9
+ import { k8sProcessor } from "./k8s.js";
10
+ import { dockerProcessor } from "./docker.js";
11
+
12
+ export interface ProcessorLookupOptions {
13
+ processors?: ContextModeProcessorsConfig;
14
+ }
15
+
16
+ export interface ProcessorMatch {
17
+ key: ContextModeProcessorFamily;
18
+ processor: Processor;
19
+ }
20
+
21
+ interface ArgvRoute {
22
+ key: ContextModeProcessorFamily;
23
+ pattern: RegExp;
24
+ }
25
+
26
+ interface ContentSniffRoute {
27
+ key: ContextModeProcessorFamily;
28
+ predicate: (text: string) => boolean;
29
+ }
30
+
31
+ export const ARGV_TABLE: readonly ArgvRoute[] = [
32
+ { key: "git", pattern: /^\s*git\s+(?:status|diff|log|show|branch|stash)\b/ },
33
+ { key: "test", pattern: /^\s*(?:bun\s+test|(?:npx\s+)?(?:vitest|jest))\b/ },
34
+ { key: "log", pattern: /^\s*(?:tail\s+-f|journalctl|less\s+\+F)\b/ },
35
+ { key: "lint", pattern: /^\s*(?:eslint\b|biome\s+(?:check|lint)\b|prettier\s+--check\b)/ },
36
+ { key: "build", pattern: /^\s*(?:tsc\b|cargo\s+(?:build|check)\b|go\s+build\b|esbuild\b|next\s+build\b|bun\s+run\s+build\b)/ },
37
+ { key: "k8s", pattern: /^\s*kubectl\s+(?:get|describe|logs|top)\b/ },
38
+ { key: "docker", pattern: /^\s*docker\s+(?:ps|images|logs|inspect|build)\b/ },
39
+ ];
40
+ export const CONTENT_SNIFF: readonly ContentSniffRoute[] = [
41
+ { key: "json", predicate: jsonContentSniff },
42
+ { key: "log", predicate: logContentSniff },
43
+ ];
44
+
45
+ function isEnabled(
46
+ key: ContextModeProcessorFamily,
47
+ config: ContextModeProcessorsConfig | undefined,
48
+ ): boolean {
49
+ if (config?.enabled === false) return false;
50
+ return !(config?.disable ?? []).includes(key);
51
+ }
52
+
53
+ export function getProcessor(key: ContextModeProcessorFamily): Processor | null {
54
+ switch (key) {
55
+ case "git":
56
+ return gitProcessor;
57
+ case "test":
58
+ return testRunnerProcessor;
59
+ case "json":
60
+ return jsonProcessor;
61
+ case "log":
62
+ return logProcessor;
63
+ case "lint":
64
+ return lintProcessor;
65
+ case "build":
66
+ return buildProcessor;
67
+ case "k8s":
68
+ return k8sProcessor;
69
+ case "docker":
70
+ return dockerProcessor;
71
+ default:
72
+ return null;
73
+ }
74
+ }
75
+
76
+ function commandFromInput(input: Record<string, unknown>): string {
77
+ const command = input.command;
78
+ return typeof command === "string" ? command : "";
79
+ }
80
+
81
+ export function lookupProcessor(
82
+ canonicalTool: string,
83
+ input: Record<string, unknown>,
84
+ text: string,
85
+ options: ProcessorLookupOptions = {},
86
+ ): ProcessorMatch | null {
87
+ const processorsConfig = options.processors;
88
+ if (processorsConfig?.enabled === false) return null;
89
+
90
+ if (canonicalTool === "bash") {
91
+ const command = commandFromInput(input);
92
+ for (const route of ARGV_TABLE) {
93
+ if (!isEnabled(route.key, processorsConfig)) continue;
94
+ if (!route.pattern.test(command)) continue;
95
+ if (
96
+ (route.key === "k8s" || route.key === "docker")
97
+ && isEnabled("json", processorsConfig)
98
+ && jsonContentSniff(text)
99
+ ) {
100
+ const json = getProcessor("json");
101
+ return json ? { key: "json", processor: json } : null;
102
+ }
103
+ const processor = getProcessor(route.key);
104
+ return processor ? { key: route.key, processor } : null;
105
+ }
106
+ }
107
+
108
+ for (const route of CONTENT_SNIFF) {
109
+ if (!isEnabled(route.key, processorsConfig)) continue;
110
+ if (!route.predicate(text)) continue;
111
+ const processor = getProcessor(route.key);
112
+ return processor ? { key: route.key, processor } : null;
113
+ }
114
+
115
+ return null;
116
+ }
@@ -0,0 +1,102 @@
1
+ import type { ProcessorContext, ProcessorInvariant, ProcessorOutput } from "./types.js";
2
+
3
+ const encoder = new TextEncoder();
4
+
5
+ export const TEST_RUNNER_INVARIANT: ProcessorInvariant = {
6
+ key: "test",
7
+ maxBytes: 16384,
8
+ preserve: [
9
+ "failure labels",
10
+ "passing labels",
11
+ "file:line:column stack locations",
12
+ "expectation messages",
13
+ "final summary",
14
+ "test counts",
15
+ ],
16
+ };
17
+
18
+ function byteLength(text: string): number {
19
+ return encoder.encode(text).byteLength;
20
+ }
21
+
22
+ function normalizeEol(text: string, eol: ProcessorContext["eol"]): string {
23
+ return text.replace(/\r?\n/g, eol);
24
+ }
25
+
26
+ function hasTestRunnerTokens(text: string): boolean {
27
+ return /\b(?:bun test|vitest|jest)\b/i.test(text)
28
+ || /\b(?:FAIL|PASS)\b/.test(text)
29
+ || /\((?:fail|pass)\)/i.test(text)
30
+ || /\b(?:Test Files|Test Suites|Tests:|Ran \d+ tests?)\b/i.test(text);
31
+ }
32
+
33
+ function isFailureRun(text: string): boolean {
34
+ return /\bFAIL\b/.test(text)
35
+ || /\(fail\)/i.test(text)
36
+ || /\b[1-9]\d*\s+fail(?:ed)?\b/i.test(text);
37
+ }
38
+
39
+ function isSummaryLine(line: string): boolean {
40
+ return /\b(?:Test Files|Test Suites|Tests:|Snapshots:|Time:|Ran |Duration|Start at)\b/i.test(line)
41
+ || /^\s*\d+\s+(?:pass|fail)\b/i.test(line)
42
+ || /^\s*\d+\s+expect\(\) calls\b/i.test(line);
43
+ }
44
+
45
+ function isImportantFailureLine(line: string): boolean {
46
+ return /\b(?:FAIL|PASS)\b/.test(line)
47
+ || /\((?:fail|pass)\)/i.test(line)
48
+ || /(?:^|[\s(])\S+\.test\.[tj]sx?:\d+:\d+/.test(line)
49
+ || /\b(?:error|Error|AssertionError|Expected|Received|expect\()\b/.test(line)
50
+ || isSummaryLine(line);
51
+ }
52
+
53
+ function capText(lines: string[], maxBytes: number, eol: ProcessorContext["eol"]): string {
54
+ let output = lines.join(eol);
55
+ if (byteLength(output) <= maxBytes) return output;
56
+
57
+ const marker = `[...test-runner processor omitted ${lines.length} lines to stay under ${maxBytes} bytes...]`;
58
+ const kept: string[] = [];
59
+ for (const line of lines) {
60
+ const candidate = [...kept, line, marker].join(eol);
61
+ if (byteLength(candidate) > maxBytes) break;
62
+ kept.push(line);
63
+ }
64
+ output = [...kept, marker].join(eol);
65
+ while (byteLength(output) > maxBytes && kept.length > 0) {
66
+ kept.pop();
67
+ output = [...kept, marker].join(eol);
68
+ }
69
+ return output;
70
+ }
71
+
72
+ function compressPassingRun(lines: string[], ctx: ProcessorContext): string {
73
+ const summary = lines.filter(isSummaryLine);
74
+ return capText(summary.length > 0 ? summary : lines.slice(-10), TEST_RUNNER_INVARIANT.maxBytes, ctx.eol);
75
+ }
76
+
77
+ function compressFailingRun(lines: string[], ctx: ProcessorContext): string {
78
+ const keep = new Set<number>();
79
+ for (let index = 0; index < lines.length; index += 1) {
80
+ if (!isImportantFailureLine(lines[index])) continue;
81
+ keep.add(index);
82
+ if (index > 0) keep.add(index - 1);
83
+ if (index + 1 < lines.length) keep.add(index + 1);
84
+ }
85
+
86
+ const kept = [...keep].sort((a, b) => a - b).map((index) => lines[index]);
87
+ return capText(kept.length > 0 ? kept : lines, TEST_RUNNER_INVARIANT.maxBytes, ctx.eol);
88
+ }
89
+
90
+ export function testRunnerProcessor(text: string, ctx: ProcessorContext): ProcessorOutput {
91
+ if (!hasTestRunnerTokens(text)) {
92
+ return { text, processorKey: "test", passthrough: true };
93
+ }
94
+
95
+ const normalized = normalizeEol(text, ctx.eol);
96
+ const lines = normalized.split(ctx.eol);
97
+ const compressed = isFailureRun(normalized)
98
+ ? compressFailingRun(lines, ctx)
99
+ : compressPassingRun(lines, ctx);
100
+
101
+ return { text: compressed, processorKey: "test", passthrough: false };
102
+ }
@@ -0,0 +1,20 @@
1
+ import type { ContextModeProcessorFamily } from "../../types.js";
2
+
3
+ export interface ProcessorInvariant {
4
+ key: ContextModeProcessorFamily;
5
+ maxBytes: number;
6
+ preserve: ReadonlyArray<string>;
7
+ }
8
+
9
+ export interface ProcessorOutput {
10
+ text: string;
11
+ processorKey: ContextModeProcessorFamily;
12
+ passthrough: boolean;
13
+ }
14
+
15
+ export interface ProcessorContext {
16
+ exitCode: number | null;
17
+ eol: "\n" | "\r\n";
18
+ }
19
+
20
+ export type Processor = (text: string, ctx: ProcessorContext) => ProcessorOutput;
@@ -0,0 +1,400 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { Platform } from "../platform/types.js";
4
+
5
+ export interface RepoMapOptions {
6
+ cwd: string;
7
+ focus?: string[];
8
+ tokenBudget?: number;
9
+ maxFiles?: number;
10
+ }
11
+
12
+ export interface RepoMapResult {
13
+ text: string;
14
+ fileCount: number;
15
+ emittedBytes: number;
16
+ emittedSourceBytes: number;
17
+ consideredFiles: number;
18
+ }
19
+
20
+ const DEFAULT_TOKEN_BUDGET = 4000;
21
+ const DEFAULT_MAX_FILES = 500;
22
+ const MAX_FILE_BYTES = 256 * 1024;
23
+ const PAGERANK_ITERATIONS = 16;
24
+ const PAGERANK_DAMPING = 0.85;
25
+
26
+ const BINARY_EXTENSIONS = new Set([
27
+ ".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".pdf", ".zip", ".gz", ".br",
28
+ ".sqlite", ".db", ".lock", ".woff", ".woff2", ".ttf", ".eot", ".mp4", ".webm",
29
+ ]);
30
+
31
+ const SOURCE_EXTENSIONS = new Set([
32
+ ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts",
33
+ ".py", ".rb", ".rs", ".go", ".java", ".kt", ".swift", ".cs", ".php", ".lua",
34
+ ".vue", ".svelte", ".astro",
35
+ ]);
36
+
37
+ interface CandidateFile {
38
+ relPath: string;
39
+ absPath: string;
40
+ size: number;
41
+ }
42
+
43
+ interface RepoMapEntry {
44
+ file: string;
45
+ sourceBytes: number;
46
+ symbols: string[];
47
+ imports: string[];
48
+ resolvedImports: string[];
49
+ baseScore: number;
50
+ rank: number;
51
+ finalScore: number;
52
+ }
53
+
54
+ export async function buildRepoMap(platform: Platform, opts: RepoMapOptions): Promise<RepoMapResult> {
55
+ const cwd = opts.cwd;
56
+ const tokenBudget = normalizePositiveInteger(opts.tokenBudget, DEFAULT_TOKEN_BUDGET);
57
+ const maxFiles = normalizePositiveInteger(opts.maxFiles, DEFAULT_MAX_FILES);
58
+ const budgetBytes = tokenBudget * 4;
59
+ const ignorePatterns = loadIgnorePatterns(cwd);
60
+ const focus = new Set((opts.focus ?? []).map(normalizePath));
61
+
62
+ const candidates = await listCandidateFiles(platform, cwd, ignorePatterns);
63
+ const fileSet = new Set(candidates.map((file) => file.relPath));
64
+
65
+ const entries: RepoMapEntry[] = [];
66
+ for (const candidate of candidates) {
67
+ if (entries.length >= maxFiles) break;
68
+ const entry = summarizeFile(candidate, fileSet);
69
+ if (entry) entries.push(entry);
70
+ }
71
+
72
+ applyPageRank(entries, focus);
73
+ entries.sort((a, b) => b.finalScore - a.finalScore || a.file.localeCompare(b.file));
74
+
75
+ const headerLines = [
76
+ "# Repository map",
77
+ "",
78
+ `Files considered: ${candidates.length}`,
79
+ `Files emitted: ${entries.length}`,
80
+ `Token budget (estimated): ${tokenBudget}`,
81
+ "",
82
+ ];
83
+ const lines: string[] = [...headerLines];
84
+ let usedBytes = byteLength(lines.join("\n"));
85
+ let emittedFiles = 0;
86
+ let emittedSourceBytes = 0;
87
+ for (const entry of entries) {
88
+ const section = renderEntry(entry);
89
+ const nextBytes = usedBytes + byteLength("\n" + section);
90
+ if (nextBytes > budgetBytes) break;
91
+ lines.push(section);
92
+ usedBytes = nextBytes;
93
+ emittedFiles += 1;
94
+ emittedSourceBytes += entry.sourceBytes;
95
+ }
96
+ const text = lines.join("\n");
97
+ return {
98
+ text,
99
+ fileCount: emittedFiles,
100
+ emittedBytes: byteLength(text),
101
+ emittedSourceBytes,
102
+ consideredFiles: candidates.length,
103
+ };
104
+ }
105
+
106
+ async function listCandidateFiles(
107
+ platform: Platform,
108
+ cwd: string,
109
+ ignorePatterns: { gitignore: RegExp[]; tokenignore: RegExp[] },
110
+ ): Promise<CandidateFile[]> {
111
+ const git = await safeExec(platform, "git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd });
112
+ const usedGit = git && git.code === 0 && git.stdout.trim().length > 0;
113
+ const rawFiles = usedGit
114
+ ? git.stdout.split(/\r?\n/).filter(Boolean)
115
+ : walkFiles(cwd).map((file) => normalizePath(path.relative(cwd, file)));
116
+ const activeIgnorePatterns = usedGit
117
+ ? ignorePatterns.tokenignore
118
+ : [...ignorePatterns.gitignore, ...ignorePatterns.tokenignore];
119
+
120
+ const files: CandidateFile[] = [];
121
+ for (const raw of rawFiles) {
122
+ const relPath = normalizePath(raw);
123
+ if (!relPath || relPath.startsWith("..")) continue;
124
+ if (isIgnored(relPath, activeIgnorePatterns)) continue;
125
+ const ext = path.extname(relPath).toLowerCase();
126
+ if (BINARY_EXTENSIONS.has(ext)) continue;
127
+ const absPath = path.join(cwd, relPath);
128
+ let size: number;
129
+ try {
130
+ const stat = fs.statSync(absPath);
131
+ if (!stat.isFile()) continue;
132
+ size = stat.size;
133
+ } catch {
134
+ continue;
135
+ }
136
+ if (size === 0 || size > MAX_FILE_BYTES) continue;
137
+ files.push({ relPath, absPath, size });
138
+ }
139
+ files.sort((a, b) => a.relPath.localeCompare(b.relPath));
140
+ return files;
141
+ }
142
+
143
+ async function safeExec(
144
+ platform: Platform,
145
+ cmd: string,
146
+ args: string[],
147
+ opts: { cwd: string },
148
+ ): Promise<{ stdout: string; stderr: string; code: number } | null> {
149
+ try {
150
+ if (typeof platform.exec !== "function") return null;
151
+ return await platform.exec(cmd, args, opts);
152
+ } catch {
153
+ return null;
154
+ }
155
+ }
156
+
157
+ function summarizeFile(file: CandidateFile, fileSet: Set<string>): RepoMapEntry | null {
158
+ if (!SOURCE_EXTENSIONS.has(path.extname(file.relPath).toLowerCase())) {
159
+ return null;
160
+ }
161
+ let text: string;
162
+ try {
163
+ text = fs.readFileSync(file.absPath, "utf8");
164
+ } catch {
165
+ return null;
166
+ }
167
+ const symbols = extractSymbols(text).slice(0, 25);
168
+ const rawImports = extractImports(text);
169
+ const imports = rawImports.slice(0, 25);
170
+ if (symbols.length === 0 && imports.length === 0) return null;
171
+ const resolvedImports = rawImports
172
+ .map((spec) => resolveImport(spec, file.relPath, fileSet))
173
+ .filter((value): value is string => Boolean(value));
174
+
175
+ const baseScore = symbols.length * 3 + imports.length;
176
+ return {
177
+ file: file.relPath,
178
+ sourceBytes: file.size,
179
+ symbols,
180
+ imports,
181
+ resolvedImports,
182
+ baseScore,
183
+ rank: 0,
184
+ finalScore: baseScore,
185
+ };
186
+ }
187
+
188
+ function applyPageRank(entries: RepoMapEntry[], focus: Set<string>): void {
189
+ if (entries.length === 0) return;
190
+ const indexByFile = new Map(entries.map((entry, index) => [entry.file, index] as const));
191
+ const outEdges: number[][] = entries.map((entry) =>
192
+ entry.resolvedImports
193
+ .map((target) => indexByFile.get(target))
194
+ .filter((value): value is number => value !== undefined),
195
+ );
196
+
197
+ const focusIndices = new Set<number>();
198
+ for (const file of focus) {
199
+ const idx = indexByFile.get(file);
200
+ if (idx !== undefined) focusIndices.add(idx);
201
+ }
202
+
203
+ const initial = focusIndices.size === 0
204
+ ? entries.map(() => 1 / entries.length)
205
+ : entries.map((_, index) => (focusIndices.has(index) ? 1 / focusIndices.size : 0));
206
+
207
+ let rank = initial.slice();
208
+ for (let iteration = 0; iteration < PAGERANK_ITERATIONS; iteration += 1) {
209
+ const next = entries.map(() => 0);
210
+ for (let i = 0; i < entries.length; i += 1) {
211
+ const targets = outEdges[i];
212
+ if (targets.length === 0) {
213
+ // Distribute dangling rank uniformly to focus set or all nodes.
214
+ const recipients = focusIndices.size === 0
215
+ ? entries.map((_, idx) => idx)
216
+ : [...focusIndices];
217
+ for (const idx of recipients) {
218
+ next[idx] += rank[i] / recipients.length;
219
+ }
220
+ continue;
221
+ }
222
+ const share = rank[i] / targets.length;
223
+ for (const target of targets) {
224
+ next[target] += share;
225
+ }
226
+ }
227
+ rank = next.map((value, index) => {
228
+ const personalization = focusIndices.size === 0
229
+ ? 1 / entries.length
230
+ : focusIndices.has(index) ? 1 / focusIndices.size : 0;
231
+ return PAGERANK_DAMPING * value + (1 - PAGERANK_DAMPING) * personalization;
232
+ });
233
+ }
234
+
235
+ for (let i = 0; i < entries.length; i += 1) {
236
+ entries[i].rank = rank[i];
237
+ entries[i].finalScore = entries[i].baseScore * (1 + rank[i] * entries.length);
238
+ if (focusIndices.has(i)) {
239
+ entries[i].finalScore += 1000;
240
+ }
241
+ }
242
+ }
243
+
244
+ function renderEntry(entry: RepoMapEntry): string {
245
+ const lines = [`## ${entry.file}`];
246
+ if (entry.symbols.length > 0) lines.push(`symbols: ${entry.symbols.join(", ")}`);
247
+ if (entry.imports.length > 0) lines.push(`imports: ${entry.imports.join(", ")}`);
248
+ if (entry.resolvedImports.length > 0) {
249
+ lines.push(`resolved: ${entry.resolvedImports.join(", ")}`);
250
+ }
251
+ return lines.join("\n") + "\n";
252
+ }
253
+
254
+ function extractSymbols(text: string): string[] {
255
+ const symbols = new Set<string>();
256
+ const patterns = [
257
+ /\bexport\s+(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/g,
258
+ /\bfunction\s+([A-Za-z_$][\w$]*)/g,
259
+ /\bexport\s+(?:const|let|var|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/g,
260
+ /\b(?:class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/g,
261
+ /\bdef\s+([A-Za-z_][\w]*)/g,
262
+ ];
263
+ for (const pattern of patterns) {
264
+ for (const match of text.matchAll(pattern)) symbols.add(match[1]);
265
+ }
266
+ return [...symbols].sort();
267
+ }
268
+
269
+ function extractImports(text: string): string[] {
270
+ const imports = new Set<string>();
271
+ const patterns = [
272
+ /\bimport\s+(?:[^"']+\s+from\s+)?["']([^"']+)["']/g,
273
+ /\brequire\(["']([^"']+)["']\)/g,
274
+ /\bfrom\s+["']([^"']+)["']/g,
275
+ ];
276
+ for (const pattern of patterns) {
277
+ for (const match of text.matchAll(pattern)) imports.add(match[1]);
278
+ }
279
+ return [...imports].sort();
280
+ }
281
+
282
+ function resolveImport(spec: string, importer: string, fileSet: Set<string>): string | null {
283
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return null;
284
+ const importerDir = path.posix.dirname(importer);
285
+ const base = spec.startsWith("/") ? spec.slice(1) : path.posix.join(importerDir, spec);
286
+ const normalized = normalizePath(base);
287
+ const candidates: string[] = [];
288
+ if (fileSet.has(normalized)) candidates.push(normalized);
289
+ for (const ext of [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".py", ".rb"]) {
290
+ if (normalized.endsWith(ext)) continue;
291
+ const withExt = normalizePath(`${normalized}${ext}`);
292
+ if (fileSet.has(withExt)) candidates.push(withExt);
293
+ }
294
+ for (const indexExt of [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]) {
295
+ const indexPath = normalizePath(path.posix.join(normalized, `index${indexExt}`));
296
+ if (fileSet.has(indexPath)) candidates.push(indexPath);
297
+ }
298
+ return candidates[0] ?? null;
299
+ }
300
+
301
+ function loadIgnorePatterns(cwd: string): { gitignore: RegExp[]; tokenignore: RegExp[] } {
302
+ return {
303
+ gitignore: loadIgnoreFile(path.join(cwd, ".gitignore")),
304
+ tokenignore: loadIgnoreFile(path.join(cwd, ".omp", "supipowers", ".tokenignore")),
305
+ };
306
+ }
307
+
308
+ function loadIgnoreFile(file: string): RegExp[] {
309
+ try {
310
+ return fs.readFileSync(file, "utf8")
311
+ .split(/\r?\n/)
312
+ .map(parseIgnorePattern)
313
+ .filter((pattern): pattern is RegExp => Boolean(pattern));
314
+ } catch {
315
+ return [];
316
+ }
317
+ }
318
+
319
+ function parseIgnorePattern(line: string): RegExp | null {
320
+ const pattern = line.trim();
321
+ if (!pattern || pattern.startsWith("#") || pattern.startsWith("!")) return null;
322
+ return globToRegExp(pattern);
323
+ }
324
+
325
+ function isIgnored(file: string, patterns: RegExp[]): boolean {
326
+ if (file.includes("node_modules/") || file.startsWith(".git/")) return true;
327
+ return patterns.some((pattern) => pattern.test(file));
328
+ }
329
+
330
+ function globToRegExp(glob: string): RegExp | null {
331
+ const normalized = normalizePath(glob).replace(/^\//, "");
332
+ const directoryOnly = normalized.endsWith("/");
333
+ const body = directoryOnly ? normalized.slice(0, -1) : normalized;
334
+ if (!body) return null;
335
+ const anchored = glob.startsWith("/") || body.includes("/");
336
+ const prefix = anchored ? "" : "(?:.*/)?";
337
+ const source = globToRegExpSource(body);
338
+ const suffix = directoryOnly ? "(?:/.*)?$" : "$";
339
+ return new RegExp(`^${prefix}${source}${suffix}`);
340
+ }
341
+
342
+ function globToRegExpSource(glob: string): string {
343
+ let source = "";
344
+ for (let index = 0; index < glob.length; index += 1) {
345
+ const char = glob[index];
346
+ if (char === "*") {
347
+ if (glob[index + 1] === "*") {
348
+ source += ".*";
349
+ index += 1;
350
+ } else {
351
+ source += "[^/]*";
352
+ }
353
+ continue;
354
+ }
355
+ if (char === "?") {
356
+ source += "[^/]";
357
+ continue;
358
+ }
359
+ source += escapeRegExpChar(char);
360
+ }
361
+ return source;
362
+ }
363
+
364
+ function escapeRegExpChar(char: string): string {
365
+ return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
366
+ }
367
+
368
+ function walkFiles(root: string): string[] {
369
+ const out: string[] = [];
370
+ const stack = [root];
371
+ while (stack.length > 0) {
372
+ const dir = stack.pop()!;
373
+ let entries: fs.Dirent[];
374
+ try {
375
+ entries = fs.readdirSync(dir, { withFileTypes: true });
376
+ } catch {
377
+ continue;
378
+ }
379
+ for (const entry of entries) {
380
+ if (entry.name === ".git" || entry.name === "node_modules" || entry.name === "dist" || entry.name === ".next") continue;
381
+ const abs = path.join(dir, entry.name);
382
+ if (entry.isDirectory()) stack.push(abs);
383
+ else if (entry.isFile()) out.push(abs);
384
+ }
385
+ }
386
+ return out;
387
+ }
388
+
389
+ function normalizePath(value: string): string {
390
+ return value.replace(/\\/g, "/").replace(/^\.\//, "");
391
+ }
392
+
393
+ function byteLength(value: string): number {
394
+ return new TextEncoder().encode(value).byteLength;
395
+ }
396
+
397
+ function normalizePositiveInteger(value: number | undefined, fallback: number): number {
398
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return fallback;
399
+ return Math.floor(value);
400
+ }