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
@@ -6,6 +6,7 @@ import type {
6
6
  ExecResult,
7
7
  } from "./types.js";
8
8
  import { createPaths } from "./types.js";
9
+ import { normalizeSystemPromptBlocks } from "./system-prompt.js";
9
10
 
10
11
  async function execWithEnv(cmd: string, args: string[], opts: ExecOptions): Promise<ExecResult> {
11
12
  const subprocess = Bun.spawn([cmd, ...args], {
@@ -50,6 +51,8 @@ export function createOmpAdapter(api: any): Platform {
50
51
  registerCommand: (name, opts) => api.registerCommand(name, opts),
51
52
  getCommands: () => api.getCommands(),
52
53
  getActiveTools: () => api.getActiveTools(),
54
+ getAllTools:
55
+ typeof api.getAllTools === "function" ? () => api.getAllTools() : undefined,
53
56
  exec: (cmd, args, opts) =>
54
57
  opts?.env ? execWithEnv(cmd, args, opts) : api.exec(cmd, args, opts),
55
58
  sendMessage: (content, opts) => {
@@ -105,14 +108,18 @@ export function createOmpAdapter(api: any): Platform {
105
108
  // Fix: extract `model` from opts and pass it as `modelPattern` (string field
106
109
  // that OMP resolves after extension models are registered).
107
110
  const { model, ...restOpts } = opts;
111
+ const sessionOpts = { ...restOpts };
112
+ if ("systemPrompt" in sessionOpts) {
113
+ sessionOpts.systemPrompt = normalizeSystemPromptBlocks(sessionOpts.systemPrompt);
114
+ }
108
115
  const { session } = await createAgentSession({
109
- cwd: restOpts.cwd ?? process.cwd(),
116
+ cwd: sessionOpts.cwd ?? process.cwd(),
110
117
  hasUI: false,
111
118
  disableExtensionDiscovery: true,
112
119
  skills: [],
113
120
  promptTemplates: [],
114
121
  slashCommands: [],
115
- ...restOpts,
122
+ ...sessionOpts,
116
123
  // Only pass modelPattern when we have an explicit model override.
117
124
  // When undefined, OMP uses its own default (user's configured model).
118
125
  ...(model ? { modelPattern: model } : {}),
@@ -126,6 +133,10 @@ export function createOmpAdapter(api: any): Platform {
126
133
  },
127
134
 
128
135
  registerTool: api.registerTool ? (definition: any) => api.registerTool(definition) : undefined,
136
+ setActiveTools:
137
+ typeof api.setActiveTools === "function"
138
+ ? (names: string[]) => api.setActiveTools(names)
139
+ : undefined,
129
140
 
130
141
  paths: createPaths(".omp"),
131
142
  capabilities: {
@@ -133,6 +144,8 @@ export function createOmpAdapter(api: any): Platform {
133
144
  compactionHooks: true,
134
145
  customWidgets: true,
135
146
  registerTool: typeof api.registerTool === "function",
147
+ activeToolFiltering:
148
+ typeof api.getAllTools === "function" && typeof api.setActiveTools === "function",
136
149
  },
137
150
  };
138
151
  }
@@ -0,0 +1,37 @@
1
+ export type SystemPromptBlocks = string[];
2
+
3
+ function asRecord(value: unknown): Record<string, unknown> | null {
4
+ return value && typeof value === "object" && !Array.isArray(value)
5
+ ? (value as Record<string, unknown>)
6
+ : null;
7
+ }
8
+
9
+ export function normalizeSystemPromptBlocks(value: unknown): SystemPromptBlocks {
10
+ if (Array.isArray(value)) {
11
+ return value.filter((block): block is string => typeof block === "string" && block.length > 0);
12
+ }
13
+ if (typeof value === "string") {
14
+ return value.length > 0 ? [value] : [];
15
+ }
16
+
17
+ const record = asRecord(value);
18
+ if (record && "systemPrompt" in record) {
19
+ return normalizeSystemPromptBlocks(record.systemPrompt);
20
+ }
21
+
22
+ return [];
23
+ }
24
+
25
+ export function systemPromptText(value: unknown): string {
26
+ return normalizeSystemPromptBlocks(value).join("\n\n");
27
+ }
28
+
29
+ export function appendSystemPromptBlock(value: unknown, block: string): SystemPromptBlocks {
30
+ const blocks = normalizeSystemPromptBlocks(value);
31
+ return block.length > 0 ? [...blocks, block] : blocks;
32
+ }
33
+
34
+ export function prependSystemPromptBlock(value: unknown, block: string): SystemPromptBlocks {
35
+ const blocks = normalizeSystemPromptBlocks(value);
36
+ return block.length > 0 ? [block, ...blocks] : blocks;
37
+ }
@@ -12,11 +12,13 @@ export function createMockPlatform(overrides?: Partial<Platform>): Platform {
12
12
  registerCommand: mock(),
13
13
  getCommands: mock(() => []),
14
14
  getActiveTools: mock(() => []),
15
+ getAllTools: mock(() => []),
15
16
  exec: mock(async () => ({ stdout: "", stderr: "", code: 0 })),
16
17
  sendMessage: mock(),
17
18
  sendUserMessage: mock(),
18
19
  registerMessageRenderer: mock(),
19
20
  on: mock(),
21
+ setActiveTools: mock(async () => {}),
20
22
  createAgentSession: mock(async () => ({
21
23
  subscribe: mock(() => () => {}),
22
24
  prompt: mock(async () => {}),
@@ -29,6 +31,7 @@ export function createMockPlatform(overrides?: Partial<Platform>): Platform {
29
31
  compactionHooks: true,
30
32
  customWidgets: true,
31
33
  registerTool: true,
34
+ activeToolFiltering: true,
32
35
  },
33
36
  ...overrides,
34
37
  };
@@ -37,6 +37,7 @@ export interface PlatformCapabilities {
37
37
  compactionHooks: boolean;
38
38
  customWidgets: boolean;
39
39
  registerTool: boolean;
40
+ activeToolFiltering: boolean;
40
41
  }
41
42
 
42
43
  // ── Agent Sessions ─────────────────────────────────────────
@@ -47,6 +48,9 @@ export interface AgentSessionOptions {
47
48
  parentTaskPrefix?: string;
48
49
  model?: string;
49
50
  thinkingLevel?: string | null;
51
+ systemPrompt?: string[];
52
+ agentId?: string;
53
+ agentDisplayName?: string;
50
54
  [key: string]: unknown;
51
55
  }
52
56
 
@@ -130,10 +134,11 @@ export interface Platform {
130
134
 
131
135
  // Introspection
132
136
  getActiveTools(): string[];
137
+ getAllTools?(): string[];
133
138
 
134
139
  // Tool registration
135
140
  registerTool?(definition: any): void;
136
- setActiveTools?(names: string[]): void;
141
+ setActiveTools?(names: string[]): Promise<void>;
137
142
 
138
143
  // Rendering
139
144
  registerMessageRenderer<T>(type: string, renderer: any): void;
package/src/qa/config.ts CHANGED
@@ -1,11 +1,17 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { E2eQaConfig } from "./types.js";
4
3
  import type { PlatformPaths } from "../platform/types.js";
4
+ import type { WorkspaceTarget } from "../types.js";
5
+ import { getLocalTargetStatePath } from "../workspace/state-paths.js"
6
+ import type { E2eQaConfig } from "./types.js";
5
7
 
6
8
  const CONFIG_FILENAME = "e2e-qa.json";
7
9
 
8
- function getConfigPath(paths: PlatformPaths, cwd: string): string {
10
+ function getConfigPath(paths: PlatformPaths, cwd: string, target?: WorkspaceTarget): string {
11
+ if (target) {
12
+ return getLocalTargetStatePath(paths, target, CONFIG_FILENAME);
13
+ }
14
+
9
15
  return paths.project(cwd, CONFIG_FILENAME);
10
16
  }
11
17
 
@@ -26,8 +32,8 @@ export const DEFAULT_E2E_QA_CONFIG: E2eQaConfig = {
26
32
  },
27
33
  };
28
34
 
29
- export function loadE2eQaConfig(paths: PlatformPaths, cwd: string): E2eQaConfig | null {
30
- const configPath = getConfigPath(paths, cwd);
35
+ export function loadE2eQaConfig(paths: PlatformPaths, cwd: string, target?: WorkspaceTarget): E2eQaConfig | null {
36
+ const configPath = getConfigPath(paths, cwd, target);
31
37
  if (!fs.existsSync(configPath)) return null;
32
38
  try {
33
39
  return JSON.parse(fs.readFileSync(configPath, "utf-8")) as E2eQaConfig;
@@ -36,8 +42,8 @@ export function loadE2eQaConfig(paths: PlatformPaths, cwd: string): E2eQaConfig
36
42
  }
37
43
  }
38
44
 
39
- export function saveE2eQaConfig(paths: PlatformPaths, cwd: string, config: E2eQaConfig): void {
40
- const configPath = getConfigPath(paths, cwd);
45
+ export function saveE2eQaConfig(paths: PlatformPaths, cwd: string, config: E2eQaConfig, target?: WorkspaceTarget): void {
46
+ const configPath = getConfigPath(paths, cwd, target);
41
47
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
42
48
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
43
49
  }
@@ -1,11 +1,13 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import type { AppType } from "./types.js";
3
4
 
4
5
  export interface AppDetection {
5
- type: string;
6
+ type: AppType;
6
7
  devCommand: string;
7
8
  port: number;
8
9
  baseUrl: string;
10
+ isLikelyApp: boolean;
9
11
  }
10
12
 
11
13
  function fileExists(cwd: string, ...segments: string[]): boolean {
@@ -29,9 +31,10 @@ function readJson(filePath: string): Record<string, unknown> | null {
29
31
  }
30
32
 
31
33
  export function detectAppType(cwd: string): AppDetection {
32
- let type = "generic";
34
+ let type: AppType = "generic";
33
35
  let devCommand = "npm run dev";
34
36
  let port = 3000;
37
+ let isLikelyApp = false;
35
38
 
36
39
  const hasNextConfig =
37
40
  fileExists(cwd, "next.config.js") ||
@@ -41,7 +44,6 @@ export function detectAppType(cwd: string): AppDetection {
41
44
  const hasNextPagesDir = dirExists(cwd, "pages") || dirExists(cwd, "src", "pages");
42
45
 
43
46
  if (hasNextConfig || hasNextAppDir || hasNextPagesDir) {
44
- // Default nextjs-app; only switch to pages if pages dir exists and app dir does not
45
47
  if (hasNextAppDir) {
46
48
  type = "nextjs-app";
47
49
  } else if (hasNextPagesDir) {
@@ -50,6 +52,7 @@ export function detectAppType(cwd: string): AppDetection {
50
52
  type = "nextjs-app";
51
53
  }
52
54
  port = 3000;
55
+ isLikelyApp = true;
53
56
  } else if (
54
57
  fileExists(cwd, "vite.config.ts") ||
55
58
  fileExists(cwd, "vite.config.js") ||
@@ -57,36 +60,40 @@ export function detectAppType(cwd: string): AppDetection {
57
60
  ) {
58
61
  type = "vite";
59
62
  port = 5173;
63
+ isLikelyApp = true;
60
64
  } else if (fileExists(cwd, "angular.json")) {
61
65
  type = "generic";
62
66
  devCommand = "npm start";
63
67
  port = 4200;
68
+ isLikelyApp = true;
64
69
  } else if (fileExists(cwd, "package.json")) {
65
70
  try {
66
71
  const raw = fs.readFileSync(path.join(cwd, "package.json"), "utf8");
67
72
  if (raw.includes('"express"')) {
68
73
  type = "express";
69
74
  port = 3000;
75
+ isLikelyApp = true;
70
76
  }
71
77
  } catch {
72
78
  // proceed with defaults
73
79
  }
74
80
  }
75
81
 
76
- // Override devCommand from package.json scripts
77
82
  const pkgPath = path.join(cwd, "package.json");
78
83
  const pkg = readJson(pkgPath);
79
84
  if (pkg) {
80
85
  const scripts = pkg.scripts as Record<string, string> | undefined;
81
86
  if (scripts?.dev) {
82
87
  devCommand = "npm run dev";
88
+ isLikelyApp = true;
83
89
  } else if (scripts?.start) {
84
90
  devCommand = "npm start";
91
+ isLikelyApp = true;
85
92
  } else if (scripts?.serve) {
86
93
  devCommand = "npm run serve";
94
+ isLikelyApp = true;
87
95
  }
88
96
 
89
- // Extract port from dev script text
90
97
  const scriptText = scripts?.dev ?? scripts?.start ?? "";
91
98
  if (scriptText) {
92
99
  const portMatch = scriptText.match(/(?:--port|PORT=)\s*(\d+)/);
@@ -98,5 +105,5 @@ export function detectAppType(cwd: string): AppDetection {
98
105
 
99
106
  const baseUrl = `http://localhost:${port}`;
100
107
 
101
- return { type, devCommand, port, baseUrl };
108
+ return { type, devCommand, port, baseUrl, isLikelyApp };
102
109
  }
package/src/qa/matrix.ts CHANGED
@@ -1,11 +1,17 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { E2eMatrix, E2eFlowRecord, E2eTestResult, E2eRegression } from "./types.js";
4
3
  import type { PlatformPaths } from "../platform/types.js";
4
+ import type { WorkspaceTarget } from "../types.js";
5
+ import { getLocalTargetStatePath } from "../workspace/state-paths.js"
6
+ import type { E2eFlowRecord, E2eMatrix, E2eRegression, E2eTestResult } from "./types.js";
5
7
 
6
8
  const MATRIX_FILENAME = "e2e-matrix.json";
7
9
 
8
- function getMatrixPath(paths: PlatformPaths, cwd: string): string {
10
+ function getMatrixPath(paths: PlatformPaths, cwd: string, target?: WorkspaceTarget): string {
11
+ if (target) {
12
+ return getLocalTargetStatePath(paths, target, MATRIX_FILENAME);
13
+ }
14
+
9
15
  return paths.project(cwd, MATRIX_FILENAME);
10
16
  }
11
17
 
@@ -18,8 +24,8 @@ export function createEmptyMatrix(appType: string): E2eMatrix {
18
24
  };
19
25
  }
20
26
 
21
- export function loadE2eMatrix(paths: PlatformPaths, cwd: string): E2eMatrix | null {
22
- const matrixPath = getMatrixPath(paths, cwd);
27
+ export function loadE2eMatrix(paths: PlatformPaths, cwd: string, target?: WorkspaceTarget): E2eMatrix | null {
28
+ const matrixPath = getMatrixPath(paths, cwd, target);
23
29
  if (!fs.existsSync(matrixPath)) return null;
24
30
  try {
25
31
  return JSON.parse(fs.readFileSync(matrixPath, "utf-8")) as E2eMatrix;
@@ -28,8 +34,8 @@ export function loadE2eMatrix(paths: PlatformPaths, cwd: string): E2eMatrix | nu
28
34
  }
29
35
  }
30
36
 
31
- export function saveE2eMatrix(paths: PlatformPaths, cwd: string, matrix: E2eMatrix): void {
32
- const matrixPath = getMatrixPath(paths, cwd);
37
+ export function saveE2eMatrix(paths: PlatformPaths, cwd: string, matrix: E2eMatrix, target?: WorkspaceTarget): void {
38
+ const matrixPath = getMatrixPath(paths, cwd, target);
33
39
  fs.mkdirSync(path.dirname(matrixPath), { recursive: true });
34
40
  fs.writeFileSync(matrixPath, JSON.stringify(matrix, null, 2));
35
41
  }
@@ -13,7 +13,17 @@ export interface E2ePromptOptions {
13
13
  }
14
14
 
15
15
  export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
16
- const { appType, sessionDir, scriptsDir, config, discoveredRoutes, previousMatrix, skillContent, dotDirDisplay } = options;
16
+ const {
17
+ cwd,
18
+ appType,
19
+ sessionDir,
20
+ scriptsDir,
21
+ config,
22
+ discoveredRoutes,
23
+ previousMatrix,
24
+ skillContent,
25
+ dotDirDisplay,
26
+ } = options;
17
27
  const { playwright, execution } = config;
18
28
 
19
29
  const headedFlag = playwright.headless ? "" : " --headed";
@@ -22,7 +32,7 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
22
32
  "# E2E QA Pipeline — Autonomous Execution",
23
33
  "",
24
34
  `You are an autonomous E2E QA pipeline for a **${appType.type}** application.`,
25
- "Run all phases sequentially without stopping. Use `playwright-cli` for browser interactions and the provided scripts for test execution.",
35
+ "Run all phases sequentially without stopping. Use `playwright-cli` for browser interactions and the provided Bun runners for execution.",
26
36
  "",
27
37
  "## Session Context",
28
38
  "",
@@ -37,7 +47,6 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
37
47
  "",
38
48
  ];
39
49
 
40
- // Previous matrix
41
50
  if (previousMatrix) {
42
51
  sections.push(
43
52
  "## Previous Matrix",
@@ -53,7 +62,6 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
53
62
  );
54
63
  }
55
64
 
56
- // Route hints (not authoritative — playwright-cli exploration is the source of truth)
57
65
  sections.push(
58
66
  "## Route Hints",
59
67
  "",
@@ -65,7 +73,6 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
65
73
  "",
66
74
  );
67
75
 
68
- // Skill content
69
76
  if (skillContent) {
70
77
  sections.push(
71
78
  "## E2E Testing Methodology",
@@ -75,7 +82,6 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
75
82
  );
76
83
  }
77
84
 
78
- // Step 1: Flow Discovery — interactive exploration via playwright-cli
79
85
  sections.push(
80
86
  "## Step 1: Flow Discovery",
81
87
  "",
@@ -105,7 +111,6 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
105
111
  "",
106
112
  );
107
113
 
108
- // Step 2: Test Generation
109
114
  sections.push(
110
115
  "## Step 2: Test Generation",
111
116
  "",
@@ -118,7 +123,7 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
118
123
  " - Use `expect(page).toHaveURL()`, `expect(locator).toBeVisible()` for assertions",
119
124
  " - Use `page.waitForSelector()` or `expect(locator).toBeVisible()` before assertions — never use `networkidle` (unreliable for SPAs)",
120
125
  " - Set meaningful test descriptions that describe the user journey",
121
- `4. Import from \`playwright/test\``,
126
+ "4. Import from `playwright/test`",
122
127
  `5. Each test should start with \`await page.goto('${appType.baseUrl}/...')\``,
123
128
  "",
124
129
  "Example test structure:",
@@ -138,23 +143,22 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
138
143
  "",
139
144
  );
140
145
 
141
- // Step 3: Execution
142
146
  sections.push(
143
147
  "## Step 3: Execution",
144
148
  "",
145
149
  "Run the generated tests:",
146
150
  "",
147
- `1. Start the dev server:`,
148
- "```bash",
149
- `bash ${scriptsDir}/start-dev-server.sh "${options.cwd}" "${appType.devCommand}" ${appType.port} 60 "${sessionDir}"`,
151
+ "1. Start the dev server:",
152
+ "```text",
153
+ `bun \"${scriptsDir}/start-dev-server.ts\" \"${cwd}\" \"${appType.devCommand}\" ${appType.port} 60 \"${sessionDir}\"`,
150
154
  "```",
151
155
  " Read the JSON output. If `ready: false`, stop and report the error.",
152
156
  "",
153
- "2. Run the tests — **IMPORTANT: never run playwright directly. Always use the script:**",
154
- "```bash",
155
- `bash ${scriptsDir}/run-e2e-tests.sh "${sessionDir}/tests" "${appType.baseUrl}"`,
157
+ "2. Run the tests — **IMPORTANT: never run playwright directly. Always use the runner:**",
158
+ "```text",
159
+ `bun \"${scriptsDir}/run-e2e-tests.ts\" \"${sessionDir}/tests\" \"${appType.baseUrl}\"`,
156
160
  "```",
157
- " Exit codes: 0 = all passed, 2 = some tests failed, 1 = script/playwright error.",
161
+ " Exit codes: 0 = all passed, 2 = some tests failed, 1 = runner/playwright error.",
158
162
  " Read the JSON output. It contains `total`, `passed`, `failed`, `failures[]`.",
159
163
  "",
160
164
  `3. If there are failures and retries remain (max ${execution.maxRetries}):`,
@@ -164,13 +168,12 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
164
168
  " - If real app bug: note it for the report",
165
169
  "",
166
170
  "4. Stop the dev server:",
167
- "```bash",
168
- `bash ${scriptsDir}/stop-dev-server.sh "${sessionDir}"`,
171
+ "```text",
172
+ `bun \"${scriptsDir}/stop-dev-server.ts\" \"${sessionDir}\"`,
169
173
  "```",
170
174
  "",
171
175
  );
172
176
 
173
- // Step 4: Regression Analysis & Reporting
174
177
  sections.push(
175
178
  "## Step 4: Regression Analysis & Reporting",
176
179
  "",
@@ -195,33 +198,28 @@ export function buildE2eOrchestratorPrompt(options: E2ePromptOptions): string {
195
198
  "",
196
199
  );
197
200
 
198
- // Script paths
199
201
  sections.push(
200
- "## Script Paths",
202
+ "## Runner Paths",
201
203
  "",
202
- `- detect-app-type.sh: \`${scriptsDir}/detect-app-type.sh\``,
203
- `- discover-routes.sh: \`${scriptsDir}/discover-routes.sh\``,
204
- `- start-dev-server.sh: \`${scriptsDir}/start-dev-server.sh\``,
205
- `- run-e2e-tests.sh: \`${scriptsDir}/run-e2e-tests.sh\``,
206
- `- stop-dev-server.sh: \`${scriptsDir}/stop-dev-server.sh\``,
204
+ `- start-dev-server.ts: \`${scriptsDir}/start-dev-server.ts\``,
205
+ `- run-e2e-tests.ts: \`${scriptsDir}/run-e2e-tests.ts\``,
206
+ `- stop-dev-server.ts: \`${scriptsDir}/stop-dev-server.ts\``,
207
207
  "",
208
208
  );
209
209
 
210
- // Token guidance
211
210
  sections.push(
212
211
  "## Token Guidance",
213
212
  "",
214
213
  "To minimize token usage:",
215
- "- Always use `run-e2e-tests.sh` — never run playwright directly for test execution",
214
+ "- Always use `run-e2e-tests.ts` — never run playwright directly for test execution",
216
215
  "- Use `playwright-cli snapshot` instead of `playwright-cli screenshot` when you only need element structure",
217
216
  "- Only read the `failures` array from test results, skip passed tests",
218
217
  "- Don't cat full test files when analyzing failures — read only the failing line range",
219
218
  "- Write tests incrementally by flow group, run after each group to catch issues early",
220
- "- Don't dump raw playwright output — the script produces a compact JSON summary",
219
+ "- Don't dump raw playwright output — the runner produces a compact JSON summary",
221
220
  "",
222
221
  );
223
222
 
224
- // Troubleshooting
225
223
  sections.push(
226
224
  "## Troubleshooting",
227
225
  "",
@@ -0,0 +1,72 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { getShellInvocation } from "../../utils/shell.js";
3
+
4
+ export const DEV_SERVER_PID_FILENAME = "dev-server.pid";
5
+
6
+ const HEALTHCHECK_TIMEOUT_MS = 1_000;
7
+ const FORCE_KILL_WAIT_MS = 250;
8
+
9
+ export async function isServerReachable(port: number): Promise<boolean> {
10
+ try {
11
+ await fetch(`http://localhost:${port}`, {
12
+ signal: AbortSignal.timeout(HEALTHCHECK_TIMEOUT_MS),
13
+ });
14
+ return true;
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+
20
+ export function processExists(pid: number): boolean {
21
+ try {
22
+ process.kill(pid, 0);
23
+ return true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ async function sleep(ms: number): Promise<void> {
30
+ await new Promise((resolve) => setTimeout(resolve, ms));
31
+ }
32
+
33
+ export async function killProcessTree(pid: number): Promise<void> {
34
+ if (!Number.isInteger(pid) || pid <= 0) {
35
+ return;
36
+ }
37
+
38
+ if (process.platform === "win32") {
39
+ spawnSync("taskkill", ["/PID", String(pid), "/T", "/F"], {
40
+ stdio: "ignore",
41
+ });
42
+ await sleep(FORCE_KILL_WAIT_MS);
43
+ return;
44
+ }
45
+
46
+ try {
47
+ process.kill(-pid, "SIGTERM");
48
+ } catch {
49
+ try {
50
+ process.kill(pid, "SIGTERM");
51
+ } catch {
52
+ return;
53
+ }
54
+ }
55
+
56
+ await sleep(1_000);
57
+ if (!processExists(pid)) {
58
+ return;
59
+ }
60
+
61
+ try {
62
+ process.kill(-pid, "SIGKILL");
63
+ } catch {
64
+ try {
65
+ process.kill(pid, "SIGKILL");
66
+ } catch {
67
+ return;
68
+ }
69
+ }
70
+ }
71
+
72
+ export { getShellInvocation };