supipowers 1.5.3 → 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 +131 -42
  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 +18 -8
  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 +221 -109
  211. package/src/review/fixer.ts +10 -6
  212. package/src/review/multi-agent-runner.ts +114 -13
  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 +1 -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 +1399 -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,19 +1,29 @@
1
1
  import type { Platform } from "../platform/types.js";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { moduleDir, toBashPath } from "../utils/paths.js";
5
- import { notifyInfo } from "../notifications/renderer.js";
6
- import { loadE2eQaConfig, saveE2eQaConfig, DEFAULT_E2E_QA_CONFIG } from "../qa/config.js";
4
+ import { loadModelConfig } from "../config/model-config.js";
5
+ import { modelRegistry } from "../config/model-registry-instance.js";
6
+ import { applyModelOverride, createModelBridge, resolveModelForAction } from "../config/model-resolver.js";
7
+ import { notifyError, notifyInfo } from "../notifications/renderer.js";
8
+ import { DEFAULT_E2E_QA_CONFIG, loadE2eQaConfig, saveE2eQaConfig } from "../qa/config.js";
9
+ import { detectAppType } from "../qa/detect-app-type.js";
10
+ import { discoverRoutes, type DiscoveredRoute } from "../qa/discover-routes.js";
7
11
  import { loadE2eMatrix } from "../qa/matrix.js";
8
- import { createNewE2eSession } from "../qa/session.js";
9
12
  import { buildE2eOrchestratorPrompt } from "../qa/prompt-builder.js";
13
+ import { createNewE2eSession } from "../qa/session.js";
14
+ import type { AppType, E2eQaConfig, E2eRegression } from "../qa/types.js";
15
+ import type { WorkspaceTarget } from "../types.js";
10
16
  import { findActiveSession, getSessionDir } from "../storage/qa-sessions.js";
11
- import type { E2eQaConfig, AppType, E2eRegression } from "../qa/types.js";
12
- import { modelRegistry } from "../config/model-registry-instance.js";
13
- import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
14
- import { loadModelConfig } from "../config/model-config.js";
15
- import { detectAppType } from "../qa/detect-app-type.js";
16
- import { discoverRoutes } from "../qa/discover-routes.js";
17
+ import { moduleDir } from "../utils/paths.js";
18
+ import { resolvePackageManager } from "../workspace/package-manager.js";
19
+ import { resolveRepoRoot } from "../workspace/repo-root.js";
20
+ import {
21
+ buildWorkspaceTargetOptionLabel,
22
+ parseTargetArg,
23
+ selectWorkspaceTarget,
24
+ type WorkspaceTargetOption,
25
+ } from "../workspace/selector.js";
26
+ import { discoverWorkspaceTargets } from "../workspace/targets.js";
17
27
 
18
28
  modelRegistry.register({
19
29
  id: "qa",
@@ -22,8 +32,48 @@ modelRegistry.register({
22
32
  harnessRoleHint: "slow",
23
33
  });
24
34
 
35
+ export interface QaCommandDependencies {
36
+ loadModelConfig: typeof loadModelConfig;
37
+ createModelBridge: typeof createModelBridge;
38
+ resolveModelForAction: typeof resolveModelForAction;
39
+ applyModelOverride: typeof applyModelOverride;
40
+ resolvePackageManager: typeof resolvePackageManager;
41
+ discoverWorkspaceTargets: typeof discoverWorkspaceTargets;
42
+ selectWorkspaceTarget: typeof selectWorkspaceTarget;
43
+ loadE2eQaConfig: typeof loadE2eQaConfig;
44
+ saveE2eQaConfig: typeof saveE2eQaConfig;
45
+ loadE2eMatrix: typeof loadE2eMatrix;
46
+ createNewE2eSession: typeof createNewE2eSession;
47
+ findActiveSession: typeof findActiveSession;
48
+ getSessionDir: typeof getSessionDir;
49
+ detectAppType: typeof detectAppType;
50
+ discoverRoutes: typeof discoverRoutes;
51
+ notifyError: typeof notifyError;
52
+ notifyInfo: typeof notifyInfo;
53
+ }
54
+
55
+ const QA_COMMAND_DEPENDENCIES: QaCommandDependencies = {
56
+ loadModelConfig,
57
+ createModelBridge,
58
+ resolveModelForAction,
59
+ applyModelOverride,
60
+ resolvePackageManager,
61
+ discoverWorkspaceTargets,
62
+ selectWorkspaceTarget,
63
+ loadE2eQaConfig,
64
+ saveE2eQaConfig,
65
+ loadE2eMatrix,
66
+ createNewE2eSession,
67
+ findActiveSession,
68
+ getSessionDir,
69
+ detectAppType,
70
+ discoverRoutes,
71
+ notifyError,
72
+ notifyInfo,
73
+ };
74
+
25
75
  function getScriptsDir(): string {
26
- return toBashPath(path.join(moduleDir(import.meta.url), "..", "qa", "scripts"));
76
+ return path.join(moduleDir(import.meta.url), "..", "qa", "scripts");
27
77
  }
28
78
 
29
79
  function findSkillPath(skillName: string): string | null {
@@ -52,13 +102,69 @@ const RETRY_OPTIONS = [
52
102
  "3",
53
103
  ];
54
104
 
105
+ function buildQaTargetOptionLabel(option: WorkspaceTargetOption<WorkspaceTarget>): string {
106
+ return buildWorkspaceTargetOptionLabel(option, [option.target.kind === "root" ? "repo root" : "workspace package"]);
107
+ }
108
+
109
+ function describeTarget(target: WorkspaceTarget): string {
110
+ return target.kind === "root" ? target.name : `${target.name} (${target.relativeDir})`;
111
+ }
112
+
113
+ interface QaTargetInspection {
114
+ detectedType: AppType | null;
115
+ detectedDevCommand: string | null;
116
+ detectedPort: number | null;
117
+ detectedIsLikelyApp: boolean;
118
+ preliminaryRoutes: DiscoveredRoute[];
119
+ isRunnable: boolean;
120
+ isRunnableForPrefilter: boolean;
121
+ }
122
+
123
+ function inspectQaTarget(target: WorkspaceTarget, deps: QaCommandDependencies): QaTargetInspection {
124
+ let detectedType: AppType | null = null;
125
+ let detectedDevCommand: string | null = null;
126
+ let detectedPort: number | null = null;
127
+ let detectedIsLikelyApp = false;
128
+ let preliminaryRoutes: DiscoveredRoute[] = [];
129
+ let detectionFailed = false;
130
+
131
+ try {
132
+ const detected = deps.detectAppType(target.packageDir);
133
+ detectedType = detected.type;
134
+ detectedDevCommand = detected.devCommand;
135
+ detectedPort = detected.port;
136
+ detectedIsLikelyApp = detected.isLikelyApp;
137
+ } catch {
138
+ detectionFailed = true;
139
+ }
140
+
141
+ if (!detectionFailed && detectedType) {
142
+ try {
143
+ preliminaryRoutes = deps.discoverRoutes(target.packageDir, detectedType);
144
+ } catch {
145
+ detectionFailed = true;
146
+ }
147
+ }
148
+
149
+ const isRunnable = detectedIsLikelyApp || preliminaryRoutes.length > 0;
150
+
151
+ return {
152
+ detectedType,
153
+ detectedDevCommand,
154
+ detectedPort,
155
+ detectedIsLikelyApp,
156
+ preliminaryRoutes,
157
+ isRunnable,
158
+ isRunnableForPrefilter: !detectionFailed && isRunnable,
159
+ };
160
+ }
161
+
55
162
  async function runSetupWizard(
56
163
  ctx: any,
57
164
  detectedAppType: string | null,
58
165
  detectedDevCommand: string | null,
59
166
  detectedPort: number | null,
60
167
  ): Promise<E2eQaConfig | null> {
61
- // 1. App type
62
168
  const appTypeChoice = await ctx.ui.select(
63
169
  "App type",
64
170
  APP_TYPE_OPTIONS,
@@ -67,7 +173,6 @@ async function runSetupWizard(
67
173
  if (!appTypeChoice) return null;
68
174
  const appType = appTypeChoice.split(" ")[0] as AppType;
69
175
 
70
- // 2. Dev command
71
176
  const defaultDev = detectedDevCommand || "npm run dev";
72
177
  const devCommand = await ctx.ui.input(
73
178
  "Dev server command",
@@ -76,7 +181,6 @@ async function runSetupWizard(
76
181
  );
77
182
  if (devCommand === undefined) return null;
78
183
 
79
- // 3. Port
80
184
  const defaultPort = String(detectedPort || 3000);
81
185
  const portStr = await ctx.ui.input(
82
186
  "Dev server port",
@@ -86,7 +190,6 @@ async function runSetupWizard(
86
190
  if (portStr === undefined) return null;
87
191
  const port = parseInt(portStr, 10) || 3000;
88
192
 
89
- // 4. Max retries
90
193
  const retryChoice = await ctx.ui.select(
91
194
  "Max test retries",
92
195
  RETRY_OPTIONS,
@@ -113,134 +216,206 @@ async function runSetupWizard(
113
216
  };
114
217
  }
115
218
 
116
- export function registerQaCommand(platform: Platform): void {
117
- platform.registerCommand("supi:qa", {
118
- description: "Run autonomous E2E product testing pipeline with playwright",
119
- async handler(args: string | undefined, ctx: any) {
120
- const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
121
- const bridge = createModelBridge(platform);
122
- const resolved = resolveModelForAction("qa", modelRegistry, modelCfg, bridge);
123
- await applyModelOverride(platform, ctx, "qa", resolved);
124
-
125
- const scriptsDir = getScriptsDir();
126
-
127
- // ── Step 1: Detect app type ─────────────────────────────────────
128
- let detectedType: string | null = null;
129
- let detectedDevCommand: string | null = null;
130
- let detectedPort: number | null = null;
131
-
132
- try {
133
- const detected = detectAppType(ctx.cwd);
134
- detectedType = detected.type;
135
- detectedDevCommand = detected.devCommand;
136
- detectedPort = detected.port;
137
- } catch { /* proceed without detection */ }
138
-
139
- // ── Step 2: Load or create config ────────────────────────────────
140
- let config = loadE2eQaConfig(platform.paths, ctx.cwd);
141
-
142
- if (!config && ctx.hasUI) {
143
- config = await runSetupWizard(ctx, detectedType, detectedDevCommand, detectedPort);
144
- if (!config) return; // user cancelled
145
- saveE2eQaConfig(platform.paths, ctx.cwd, config);
146
- ctx.ui.notify(`E2E QA config saved to ${platform.paths.dotDirDisplay}/supipowers/e2e-qa.json`, "info");
147
- }
219
+ export async function handleQa(
220
+ platform: Platform,
221
+ ctx: any,
222
+ args: string | undefined,
223
+ deps: QaCommandDependencies = QA_COMMAND_DEPENDENCIES,
224
+ ): Promise<void> {
225
+ const modelCfg = deps.loadModelConfig(platform.paths, ctx.cwd);
226
+ const bridge = deps.createModelBridge(platform);
227
+ const resolved = deps.resolveModelForAction("qa", modelRegistry, modelCfg, bridge);
228
+ await deps.applyModelOverride(platform, ctx, "qa", resolved);
148
229
 
149
- if (!config) {
150
- // Use defaults with detected values
151
- config = {
152
- ...DEFAULT_E2E_QA_CONFIG,
153
- app: {
154
- type: (detectedType as AppType) || "generic",
155
- devCommand: detectedDevCommand || "npm run dev",
156
- port: detectedPort || 3000,
157
- baseUrl: `http://localhost:${detectedPort || 3000}`,
158
- },
159
- };
160
- }
230
+ const repoRoot = await resolveRepoRoot(platform, ctx.cwd);
231
+ const packageManager = deps.resolvePackageManager(repoRoot);
232
+ const targets = deps.discoverWorkspaceTargets(repoRoot, packageManager.id);
233
+ if (targets.length === 0) {
234
+ deps.notifyError(ctx, "QA target not found", "Create a package.json with name and version before running /supi:qa.");
235
+ return;
236
+ }
161
237
 
162
- // ── Step 3: Check for unresolved regressions ─────────────────────
163
- const activeSession = findActiveSession(platform.paths, ctx.cwd);
164
- if (activeSession && activeSession.regressions.length > 0 && ctx.hasUI) {
165
- const unresolvedRegressions = activeSession.regressions.filter((r: E2eRegression) => !r.resolution);
166
- if (unresolvedRegressions.length > 0) {
167
- for (const regression of unresolvedRegressions) {
168
- const choice = await ctx.ui.select(
169
- `Regression: ${regression.flowName}`,
170
- [
171
- "This is a bug — needs fixing",
172
- "Behavior changed intentionally — update the test",
173
- "Skip for now",
174
- ],
175
- { helpText: `Was passing, now fails: ${regression.error}` },
176
- );
177
- if (!choice) continue;
178
- if (choice.startsWith("This is a bug")) regression.resolution = "bug";
179
- else if (choice.startsWith("Behavior changed")) regression.resolution = "intentional-change";
180
- else regression.resolution = "skipped";
181
- }
182
- }
183
- }
238
+ const requestedTarget = parseTargetArg(args);
239
+ const targetInspections = new Map<string, QaTargetInspection>();
240
+ const getTargetInspection = (target: WorkspaceTarget): QaTargetInspection => {
241
+ const existing = targetInspections.get(target.id);
242
+ if (existing) return existing;
184
243
 
185
- // ── Step 4: Route discovery ──────────────────────────────────────
186
- let discoveredRoutes = "";
187
- try {
188
- const routes = discoverRoutes(ctx.cwd, config.app.type);
189
- if (routes.length > 0) {
190
- discoveredRoutes = routes.map((r) => JSON.stringify(r)).join("\n");
191
- }
192
- } catch { /* agent will discover routes manually */ }
193
-
194
- if (!discoveredRoutes) {
195
- discoveredRoutes = '{"path": "/", "file": "unknown", "type": "page", "hasForm": false}';
196
- }
244
+ const inspection = inspectQaTarget(target, deps);
245
+ targetInspections.set(target.id, inspection);
246
+ return inspection;
247
+ };
248
+
249
+ let selectedTarget: WorkspaceTarget | null = null;
250
+ let selectedInspection: QaTargetInspection | null = null;
251
+
252
+ if (!ctx.hasUI && !requestedTarget && targets.length > 1) {
253
+ const runnableTargets = targets
254
+ .map((target) => ({ target, inspection: getTargetInspection(target) }))
255
+ .filter(({ inspection }) => inspection.isRunnableForPrefilter);
197
256
 
198
- // ── Step 5: Load previous matrix ─────────────────────────────────
199
- const previousMatrix = loadE2eMatrix(platform.paths, ctx.cwd);
200
- const matrixJson = previousMatrix ? JSON.stringify(previousMatrix, null, 2) : null;
201
-
202
- // ── Step 6: Create session ───────────────────────────────────────
203
- const ledger = createNewE2eSession(platform.paths, ctx.cwd, config);
204
- const sessionDir = toBashPath(getSessionDir(platform.paths, ctx.cwd, ledger.id));
205
-
206
- // ── Step 7: Load skill ───────────────────────────────────────────
207
- let skillContent = "";
208
- const skillPath = findSkillPath("qa-strategy");
209
- if (skillPath) {
210
- try {
211
- skillContent = fs.readFileSync(skillPath, "utf-8");
212
- } catch { /* proceed without */ }
257
+ if (runnableTargets.length === 1) {
258
+ selectedTarget = runnableTargets[0]!.target;
259
+ selectedInspection = runnableTargets[0]!.inspection;
260
+ } else {
261
+ deps.notifyError(ctx, "QA target required", "Pass --target <package> when running /supi:qa outside interactive mode.");
262
+ return;
263
+ }
264
+ }
265
+
266
+ if (!selectedTarget) {
267
+ selectedTarget = await deps.selectWorkspaceTarget(
268
+ ctx,
269
+ targets.map((target) => ({ target, changed: false, label: buildQaTargetOptionLabel({ target, changed: false }) })),
270
+ requestedTarget,
271
+ {
272
+ title: "QA target",
273
+ helpText: "Pick one app package to test. QA runs only within the selected target.",
274
+ },
275
+ );
276
+ }
277
+ if (requestedTarget && !selectedTarget) {
278
+ deps.notifyError(ctx, "QA target not found", requestedTarget);
279
+ return;
280
+ }
281
+ if (!selectedTarget) {
282
+ return;
283
+ }
284
+
285
+ selectedInspection ??= getTargetInspection(selectedTarget);
286
+
287
+ const scriptsDir = getScriptsDir();
288
+ const targetDir = selectedTarget.packageDir;
289
+ const targetLabel = describeTarget(selectedTarget);
290
+
291
+ const {
292
+ detectedType,
293
+ detectedDevCommand,
294
+ detectedPort,
295
+ detectedIsLikelyApp,
296
+ preliminaryRoutes,
297
+ } = selectedInspection;
298
+
299
+ if (!detectedIsLikelyApp && preliminaryRoutes.length === 0) {
300
+ deps.notifyError(
301
+ ctx,
302
+ "Selected target is not a runnable app",
303
+ `${targetLabel} has no detectable app framework, routes, or dev/start script. Pick an app package instead.`,
304
+ );
305
+ return;
306
+ }
307
+
308
+ let config = deps.loadE2eQaConfig(platform.paths, ctx.cwd, selectedTarget);
309
+
310
+ if (!config && ctx.hasUI) {
311
+ config = await runSetupWizard(ctx, detectedType, detectedDevCommand, detectedPort);
312
+ if (!config) return;
313
+ deps.saveE2eQaConfig(platform.paths, ctx.cwd, config, selectedTarget);
314
+ ctx.ui.notify(`E2E QA config saved for ${targetLabel}`, "info");
315
+ }
316
+
317
+ if (!config) {
318
+ config = {
319
+ ...DEFAULT_E2E_QA_CONFIG,
320
+ app: {
321
+ type: (detectedType as AppType) || "generic",
322
+ devCommand: detectedDevCommand || "npm run dev",
323
+ port: detectedPort || 3000,
324
+ baseUrl: `http://localhost:${detectedPort || 3000}`,
325
+ },
326
+ };
327
+ }
328
+
329
+ const activeSession = deps.findActiveSession(platform.paths, ctx.cwd, selectedTarget);
330
+ if (activeSession && activeSession.regressions.length > 0 && ctx.hasUI) {
331
+ const unresolvedRegressions = activeSession.regressions.filter((r: E2eRegression) => !r.resolution);
332
+ if (unresolvedRegressions.length > 0) {
333
+ for (const regression of unresolvedRegressions) {
334
+ const choice = await ctx.ui.select(
335
+ `Regression: ${regression.flowName}`,
336
+ [
337
+ "This is a bug — needs fixing",
338
+ "Behavior changed intentionally — update the test",
339
+ "Skip for now",
340
+ ],
341
+ { helpText: `Was passing, now fails: ${regression.error}` },
342
+ );
343
+ if (!choice) continue;
344
+ if (choice.startsWith("This is a bug")) regression.resolution = "bug";
345
+ else if (choice.startsWith("Behavior changed")) regression.resolution = "intentional-change";
346
+ else regression.resolution = "skipped";
213
347
  }
348
+ }
349
+ }
214
350
 
215
- // ── Step 8: Build and send prompt ────────────────────────────────
216
- const routeCount = discoveredRoutes.split("\n").filter(Boolean).length;
217
-
218
- const prompt = buildE2eOrchestratorPrompt({
219
- cwd: toBashPath(ctx.cwd),
220
- appType: config.app,
221
- sessionDir,
222
- scriptsDir,
223
- config,
224
- discoveredRoutes,
225
- previousMatrix: matrixJson,
226
- skillContent,
227
- dotDirDisplay: platform.paths.dotDirDisplay,
228
- });
229
-
230
- platform.sendMessage(
231
- {
232
- customType: "supi-qa",
233
- content: [{ type: "text", text: prompt }],
234
- display: "none",
235
- },
236
- { deliverAs: "steer", triggerTurn: true },
237
- );
238
-
239
- notifyInfo(
240
- ctx,
241
- `E2E QA started: ${config.app.type}`,
242
- `${routeCount} routes discovered | session ${ledger.id}`,
243
- );
351
+ let routes = preliminaryRoutes;
352
+ try {
353
+ if (config.app.type !== detectedType) {
354
+ routes = deps.discoverRoutes(targetDir, config.app.type);
355
+ }
356
+ } catch {
357
+ routes = [];
358
+ }
359
+
360
+ let discoveredRoutes = "";
361
+ if (routes.length > 0) {
362
+ discoveredRoutes = routes.map((r) => JSON.stringify(r)).join("\n");
363
+ }
364
+ if (!discoveredRoutes) {
365
+ discoveredRoutes = '{"path": "/", "file": "unknown", "type": "page", "hasForm": false}';
366
+ }
367
+
368
+ const previousMatrix = deps.loadE2eMatrix(platform.paths, ctx.cwd, selectedTarget);
369
+ const matrixJson = previousMatrix ? JSON.stringify(previousMatrix, null, 2) : null;
370
+
371
+ const ledger = deps.createNewE2eSession(platform.paths, ctx.cwd, config, selectedTarget);
372
+ const sessionDir = deps.getSessionDir(platform.paths, ctx.cwd, ledger.id, selectedTarget);
373
+
374
+ let skillContent = "";
375
+ const skillPath = findSkillPath("qa-strategy");
376
+ if (skillPath) {
377
+ try {
378
+ skillContent = fs.readFileSync(skillPath, "utf-8");
379
+ } catch {
380
+ // proceed without skill content
381
+ }
382
+ }
383
+
384
+ const routeCount = discoveredRoutes.split("\n").filter(Boolean).length;
385
+
386
+ const prompt = buildE2eOrchestratorPrompt({
387
+ cwd: targetDir,
388
+ appType: config.app,
389
+ sessionDir,
390
+ scriptsDir,
391
+ config,
392
+ discoveredRoutes,
393
+ previousMatrix: matrixJson,
394
+ skillContent,
395
+ dotDirDisplay: platform.paths.dotDirDisplay,
396
+ });
397
+
398
+ platform.sendMessage(
399
+ {
400
+ customType: "supi-qa",
401
+ content: [{ type: "text", text: prompt }],
402
+ display: "none",
403
+ },
404
+ { deliverAs: "steer", triggerTurn: true },
405
+ );
406
+
407
+ deps.notifyInfo(
408
+ ctx,
409
+ `E2E QA started: ${config.app.type}`,
410
+ `${routeCount} routes discovered | session ${ledger.id} | ${targetLabel}`,
411
+ );
412
+ }
413
+
414
+ export function registerQaCommand(platform: Platform): void {
415
+ platform.registerCommand("supi:qa", {
416
+ description: "Run autonomous E2E product testing pipeline with playwright",
417
+ async handler(args: string | undefined, ctx: any) {
418
+ await handleQa(platform, ctx, args, QA_COMMAND_DEPENDENCIES);
244
419
  },
245
420
  });
246
421
  }