supipowers 1.5.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/README.md +14 -8
  2. package/bin/install.mjs +20 -5
  3. package/bin/install.ts +95 -0
  4. package/package.json +8 -4
  5. package/skills/context-mode/SKILL.md +17 -10
  6. package/skills/harness/SKILL.md +94 -0
  7. package/skills/ui-design/SKILL.md +63 -0
  8. package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
  9. package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
  10. package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
  11. package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
  12. package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
  13. package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
  14. package/skills/ultraplan-discover/SKILL.md +96 -0
  15. package/skills/ultraplan-intake/SKILL.md +89 -0
  16. package/skills/ultraplan-research/SKILL.md +129 -0
  17. package/skills/ultraplan-review/SKILL.md +86 -0
  18. package/skills/ultraplan-review-scope/SKILL.md +111 -0
  19. package/skills/ultraplan-review-structure/SKILL.md +120 -0
  20. package/skills/ultraplan-review-tdd/SKILL.md +142 -0
  21. package/skills/ultraplan-scout/SKILL.md +110 -0
  22. package/skills/ultraplan-synthesize/SKILL.md +124 -0
  23. package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
  24. package/src/ai/schema-text.ts +129 -0
  25. package/src/ai/structured-output.ts +274 -0
  26. package/src/ai/template.ts +27 -0
  27. package/src/bootstrap.ts +63 -28
  28. package/src/commands/agents.ts +149 -45
  29. package/src/commands/ai-review.ts +251 -30
  30. package/src/commands/clear.ts +434 -0
  31. package/src/commands/commit.ts +1 -0
  32. package/src/commands/config.ts +242 -44
  33. package/src/commands/context.ts +55 -28
  34. package/src/commands/doctor.ts +234 -6
  35. package/src/commands/fix-pr.ts +306 -131
  36. package/src/commands/generate.ts +111 -21
  37. package/src/commands/memory.ts +192 -0
  38. package/src/commands/model-picker.ts +28 -21
  39. package/src/commands/model.ts +19 -9
  40. package/src/commands/optimize-context.ts +408 -29
  41. package/src/commands/plan.ts +2 -0
  42. package/src/commands/qa.ts +312 -137
  43. package/src/commands/release.ts +259 -76
  44. package/src/commands/review.ts +293 -59
  45. package/src/commands/status.ts +200 -13
  46. package/src/commands/supi.ts +3 -35
  47. package/src/commands/ui-design.ts +394 -0
  48. package/src/commands/ultraplan.ts +1518 -0
  49. package/src/commands/update.ts +86 -0
  50. package/src/config/defaults.ts +62 -0
  51. package/src/config/loader.ts +448 -60
  52. package/src/config/schema.ts +108 -2
  53. package/src/context/optimizer.ts +25 -33
  54. package/src/context/rule-renderer.ts +223 -0
  55. package/src/context/savings.ts +258 -0
  56. package/src/context/startup-check.ts +380 -0
  57. package/src/context/startup-optimizer.ts +355 -0
  58. package/src/context/tokenignore.ts +146 -0
  59. package/src/context-mode/cache-handle.ts +49 -0
  60. package/src/context-mode/cache-preview.ts +71 -0
  61. package/src/context-mode/cache-store.ts +738 -0
  62. package/src/context-mode/compressor.ts +131 -26
  63. package/src/context-mode/dedup.ts +108 -0
  64. package/src/context-mode/detector.ts +35 -4
  65. package/src/context-mode/event-extractor.ts +14 -12
  66. package/src/context-mode/event-store.ts +91 -36
  67. package/src/context-mode/hooks.ts +798 -56
  68. package/src/context-mode/knowledge/store.ts +255 -11
  69. package/src/context-mode/memory-store.ts +325 -0
  70. package/src/context-mode/metrics-recorder.ts +158 -0
  71. package/src/context-mode/metrics-store.ts +765 -0
  72. package/src/context-mode/model.ts +24 -0
  73. package/src/context-mode/processor-keys.ts +29 -0
  74. package/src/context-mode/processors/build.ts +66 -0
  75. package/src/context-mode/processors/docker.ts +57 -0
  76. package/src/context-mode/processors/git.ts +111 -0
  77. package/src/context-mode/processors/json.ts +112 -0
  78. package/src/context-mode/processors/k8s.ts +67 -0
  79. package/src/context-mode/processors/lint.ts +67 -0
  80. package/src/context-mode/processors/log.ts +86 -0
  81. package/src/context-mode/processors/registry.ts +116 -0
  82. package/src/context-mode/processors/test-runner.ts +102 -0
  83. package/src/context-mode/processors/types.ts +20 -0
  84. package/src/context-mode/repomap.ts +400 -0
  85. package/src/context-mode/routing.ts +97 -24
  86. package/src/context-mode/sandbox/runners.ts +5 -1
  87. package/src/context-mode/snapshot-builder.ts +106 -11
  88. package/src/context-mode/source-hash.ts +173 -0
  89. package/src/context-mode/tool-name.ts +11 -0
  90. package/src/context-mode/tools.ts +654 -22
  91. package/src/context-mode/web/fetcher.ts +31 -12
  92. package/src/debug/logger.ts +2 -1
  93. package/src/deps/registry.ts +1 -1
  94. package/src/discipline/failure-summarizer.ts +170 -0
  95. package/src/discipline/failure-taxonomy.ts +131 -0
  96. package/src/discipline/workflow-invariants.ts +125 -0
  97. package/src/discovery/index.ts +31 -0
  98. package/src/discovery/lsp.ts +87 -0
  99. package/src/discovery/rank.ts +144 -0
  100. package/src/discovery/sources.ts +89 -0
  101. package/src/discovery/workflow.ts +87 -0
  102. package/src/docs/contracts.ts +39 -0
  103. package/src/docs/drift.ts +117 -87
  104. package/src/fix-pr/assessment.ts +200 -0
  105. package/src/fix-pr/contracts.ts +47 -0
  106. package/src/fix-pr/fetch-comments.ts +80 -0
  107. package/src/fix-pr/prompt-builder.ts +58 -40
  108. package/src/fix-pr/scripts/exec.ts +34 -0
  109. package/src/fix-pr/scripts/trigger-review.ts +106 -0
  110. package/src/fix-pr/scripts/wait-and-check.ts +108 -0
  111. package/src/fix-pr/types.ts +4 -0
  112. package/src/git/branch-finish.ts +5 -0
  113. package/src/git/commit-contract.ts +83 -0
  114. package/src/git/commit.ts +121 -184
  115. package/src/git/status.ts +62 -8
  116. package/src/harness/anti_slop/architecture-parser.ts +210 -0
  117. package/src/harness/anti_slop/backend-factory.ts +30 -0
  118. package/src/harness/anti_slop/backend.ts +140 -0
  119. package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
  120. package/src/harness/anti_slop/fallow-adapter.ts +305 -0
  121. package/src/harness/anti_slop/installer.ts +227 -0
  122. package/src/harness/anti_slop/queue.ts +216 -0
  123. package/src/harness/anti_slop/recommend.ts +84 -0
  124. package/src/harness/anti_slop/score.ts +180 -0
  125. package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
  126. package/src/harness/artifacts/agents-md.ts +88 -0
  127. package/src/harness/artifacts/checks-wiring.ts +57 -0
  128. package/src/harness/artifacts/docs-tree.ts +79 -0
  129. package/src/harness/artifacts/lint-configs.ts +136 -0
  130. package/src/harness/artifacts/review-agents.ts +67 -0
  131. package/src/harness/bare-entry.ts +108 -0
  132. package/src/harness/command.ts +1010 -0
  133. package/src/harness/default-agents/design.md +23 -0
  134. package/src/harness/default-agents/discover.md +18 -0
  135. package/src/harness/default-agents/implement.md +24 -0
  136. package/src/harness/default-agents/plan.md +19 -0
  137. package/src/harness/default-agents/research.md +21 -0
  138. package/src/harness/default-agents/validate.md +22 -0
  139. package/src/harness/gc/reporter.ts +28 -0
  140. package/src/harness/gc/runner.ts +136 -0
  141. package/src/harness/hooks/layer-context-inject.ts +155 -0
  142. package/src/harness/hooks/post-session-sweep.ts +130 -0
  143. package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
  144. package/src/harness/hooks/register.ts +118 -0
  145. package/src/harness/model.ts +117 -0
  146. package/src/harness/pipeline.ts +348 -0
  147. package/src/harness/project-paths.ts +235 -0
  148. package/src/harness/stage-runner.ts +107 -0
  149. package/src/harness/stages/design.ts +386 -0
  150. package/src/harness/stages/discover.ts +454 -0
  151. package/src/harness/stages/implement.ts +162 -0
  152. package/src/harness/stages/plan.ts +335 -0
  153. package/src/harness/stages/research.ts +263 -0
  154. package/src/harness/stages/validate.ts +684 -0
  155. package/src/harness/storage.ts +467 -0
  156. package/src/harness/tools.ts +426 -0
  157. package/src/lsp/bridge.ts +56 -95
  158. package/src/lsp/capabilities.ts +108 -0
  159. package/src/lsp/contracts.ts +35 -0
  160. package/src/lsp/detector.ts +8 -12
  161. package/src/markdown-frontmatter.ts +68 -0
  162. package/src/mempalace/bridge.ts +129 -0
  163. package/src/mempalace/config.ts +75 -0
  164. package/src/mempalace/format.ts +163 -0
  165. package/src/mempalace/hooks.ts +370 -0
  166. package/src/mempalace/installer-helper.ts +194 -0
  167. package/src/mempalace/python/mempalace_bridge.py +440 -0
  168. package/src/mempalace/runtime.ts +565 -0
  169. package/src/mempalace/schema.ts +264 -0
  170. package/src/mempalace/session-summary.ts +198 -0
  171. package/src/mempalace/tool.ts +186 -0
  172. package/src/mempalace/uv.ts +256 -0
  173. package/src/migrate/runner.ts +354 -0
  174. package/src/planning/approval-flow.ts +206 -9
  175. package/src/planning/plan-writer-prompt.ts +4 -3
  176. package/src/planning/planning-ask-tool.ts +39 -0
  177. package/src/planning/render-markdown.ts +74 -0
  178. package/src/planning/spec.ts +42 -0
  179. package/src/planning/system-prompt.ts +11 -8
  180. package/src/planning/validate.ts +84 -0
  181. package/src/platform/omp.ts +15 -2
  182. package/src/platform/system-prompt.ts +37 -0
  183. package/src/platform/test-utils.ts +3 -0
  184. package/src/platform/types.ts +6 -1
  185. package/src/qa/config.ts +12 -6
  186. package/src/qa/detect-app-type.ts +13 -6
  187. package/src/qa/matrix.ts +12 -6
  188. package/src/qa/prompt-builder.ts +28 -30
  189. package/src/qa/scripts/dev-server-utils.ts +72 -0
  190. package/src/qa/scripts/run-e2e-tests.ts +226 -0
  191. package/src/qa/scripts/start-dev-server.ts +138 -0
  192. package/src/qa/scripts/stop-dev-server.ts +77 -0
  193. package/src/qa/session.ts +13 -7
  194. package/src/quality/ai-setup.ts +27 -25
  195. package/src/quality/contracts.ts +34 -0
  196. package/src/quality/gates/ai-review.ts +20 -58
  197. package/src/quality/gates/command.ts +249 -46
  198. package/src/quality/review-gates.ts +18 -2
  199. package/src/quality/runner.ts +63 -22
  200. package/src/quality/schemas.ts +37 -2
  201. package/src/quality/setup.ts +96 -16
  202. package/src/release/changelog.ts +1 -1
  203. package/src/release/channels/custom.ts +13 -3
  204. package/src/release/channels/types.ts +5 -0
  205. package/src/release/contracts.ts +90 -0
  206. package/src/release/executor.ts +122 -45
  207. package/src/release/prompt.ts +18 -2
  208. package/src/release/targets.ts +86 -0
  209. package/src/release/version.ts +96 -71
  210. package/src/review/agent-loader.ts +298 -127
  211. package/src/review/fixer.ts +10 -6
  212. package/src/review/multi-agent-runner.ts +115 -14
  213. package/src/review/output.ts +12 -139
  214. package/src/review/runner.ts +12 -6
  215. package/src/review/scope.ts +144 -24
  216. package/src/review/types.ts +11 -20
  217. package/src/review/validator.ts +12 -6
  218. package/src/storage/fix-pr-sessions.ts +21 -14
  219. package/src/storage/plans.ts +14 -5
  220. package/src/storage/qa-sessions.ts +25 -19
  221. package/src/storage/reliability-metrics.ts +180 -0
  222. package/src/storage/reports.ts +8 -7
  223. package/src/storage/review-sessions.ts +55 -20
  224. package/src/tool-catalog/active-tool-controller.ts +164 -0
  225. package/src/tool-catalog/active-tool-planner.ts +212 -0
  226. package/src/tool-catalog/tool-groups.ts +102 -0
  227. package/src/types.ts +1401 -5
  228. package/src/ui-design/backend-adapter.ts +78 -0
  229. package/src/ui-design/backends/local-html.ts +82 -0
  230. package/src/ui-design/backends/pencil-mcp.ts +111 -0
  231. package/src/ui-design/components-scanner.ts +124 -0
  232. package/src/ui-design/config.ts +55 -0
  233. package/src/ui-design/pen-scanner.ts +95 -0
  234. package/src/ui-design/pen-selector.ts +72 -0
  235. package/src/ui-design/prompt-builder.ts +73 -0
  236. package/src/ui-design/scanner.ts +136 -0
  237. package/src/ui-design/session.ts +974 -0
  238. package/src/ui-design/system-prompt.ts +312 -0
  239. package/src/ui-design/tokens-scanner.ts +181 -0
  240. package/src/ui-design/types.ts +96 -0
  241. package/src/ultraplan/agent-catalog.ts +522 -0
  242. package/src/ultraplan/authoring/agent-catalog.ts +310 -0
  243. package/src/ultraplan/authoring/authoring-tools.ts +552 -0
  244. package/src/ultraplan/authoring/command-handlers.ts +339 -0
  245. package/src/ultraplan/authoring/markdown.ts +510 -0
  246. package/src/ultraplan/authoring/model.ts +162 -0
  247. package/src/ultraplan/authoring/pipeline.ts +319 -0
  248. package/src/ultraplan/authoring/stage-runner.ts +141 -0
  249. package/src/ultraplan/authoring/stages/approve.ts +249 -0
  250. package/src/ultraplan/authoring/stages/discover.ts +289 -0
  251. package/src/ultraplan/authoring/stages/intake.ts +203 -0
  252. package/src/ultraplan/authoring/stages/research.ts +399 -0
  253. package/src/ultraplan/authoring/stages/review.ts +333 -0
  254. package/src/ultraplan/authoring/stages/scout.ts +188 -0
  255. package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
  256. package/src/ultraplan/authoring/storage.ts +594 -0
  257. package/src/ultraplan/authoring/synth-gate.ts +165 -0
  258. package/src/ultraplan/authoring-draft.ts +653 -0
  259. package/src/ultraplan/authoring-persist.ts +180 -0
  260. package/src/ultraplan/authoring-tool.ts +608 -0
  261. package/src/ultraplan/authoring-wizard.ts +587 -0
  262. package/src/ultraplan/batch/merge.ts +98 -0
  263. package/src/ultraplan/batch/planner.ts +150 -0
  264. package/src/ultraplan/batch/presenter.ts +97 -0
  265. package/src/ultraplan/batch/storage.ts +420 -0
  266. package/src/ultraplan/batch/supervisor.ts +317 -0
  267. package/src/ultraplan/batch/worker.ts +26 -0
  268. package/src/ultraplan/batch/worktree.ts +110 -0
  269. package/src/ultraplan/contracts.ts +1593 -0
  270. package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
  271. package/src/ultraplan/default-agents/authoring/intake.md +12 -0
  272. package/src/ultraplan/default-agents/authoring/planner.md +12 -0
  273. package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
  274. package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
  275. package/src/ultraplan/default-agents/authoring/scout.md +12 -0
  276. package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
  277. package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
  278. package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
  279. package/src/ultraplan/default-agents/backend-executor.md +10 -0
  280. package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
  281. package/src/ultraplan/default-agents/backend-tester.md +10 -0
  282. package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
  283. package/src/ultraplan/default-agents/frontend-executor.md +10 -0
  284. package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
  285. package/src/ultraplan/default-agents/frontend-tester.md +10 -0
  286. package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
  287. package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
  288. package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
  289. package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
  290. package/src/ultraplan/execution/contract.ts +71 -0
  291. package/src/ultraplan/execution/policy.ts +217 -0
  292. package/src/ultraplan/execution/runtime-tools.ts +107 -0
  293. package/src/ultraplan/execution/session-runner.ts +281 -0
  294. package/src/ultraplan/next-router.ts +85 -0
  295. package/src/ultraplan/presenter.ts +359 -0
  296. package/src/ultraplan/project-paths.ts +342 -0
  297. package/src/ultraplan/runtime/active-execution.ts +72 -0
  298. package/src/ultraplan/runtime/apply-mutation.ts +416 -0
  299. package/src/ultraplan/runtime/blockers.ts +243 -0
  300. package/src/ultraplan/runtime/hook-bridge.ts +486 -0
  301. package/src/ultraplan/runtime/launch-context.ts +207 -0
  302. package/src/ultraplan/runtime/migration.ts +524 -0
  303. package/src/ultraplan/runtime/normalize.ts +281 -0
  304. package/src/ultraplan/runtime/proof.ts +260 -0
  305. package/src/ultraplan/runtime/reducer.ts +416 -0
  306. package/src/ultraplan/runtime/repair.ts +251 -0
  307. package/src/ultraplan/runtime/tracker-storage.ts +368 -0
  308. package/src/ultraplan/session-selection.ts +291 -0
  309. package/src/ultraplan/storage.ts +374 -0
  310. package/src/utils/editor.ts +38 -0
  311. package/src/utils/executable.ts +80 -0
  312. package/src/utils/paths.ts +1 -20
  313. package/src/utils/shell.ts +31 -0
  314. package/src/visual/companion.ts +2 -1
  315. package/src/visual/scripts/frame-template.html +60 -0
  316. package/src/visual/scripts/index.js +59 -13
  317. package/src/visual/scripts/package.json +3 -0
  318. package/src/visual/start-server.ts +2 -1
  319. package/src/workspace/git-scope.ts +64 -0
  320. package/src/workspace/locks.ts +23 -0
  321. package/src/workspace/package-manager.ts +117 -0
  322. package/src/workspace/path-mapping.ts +75 -0
  323. package/src/workspace/project-slug.ts +92 -0
  324. package/src/workspace/repo-root.ts +137 -0
  325. package/src/workspace/selector.ts +115 -0
  326. package/src/workspace/state-paths.ts +118 -0
  327. package/src/workspace/targets.ts +313 -0
  328. package/src/fix-pr/scripts/diff-comments.sh +0 -33
  329. package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
  330. package/src/fix-pr/scripts/trigger-review.sh +0 -36
  331. package/src/fix-pr/scripts/wait-and-check.sh +0 -37
  332. package/src/qa/scripts/detect-app-type.sh +0 -68
  333. package/src/qa/scripts/discover-routes.sh +0 -143
  334. package/src/qa/scripts/run-e2e-tests.sh +0 -131
  335. package/src/qa/scripts/start-dev-server.sh +0 -46
  336. package/src/qa/scripts/stop-dev-server.sh +0 -36
  337. package/src/review/prompts/fix-output-schema.md +0 -18
  338. package/src/review/prompts/review-output-schema.md +0 -38
  339. package/src/review/template.ts +0 -15
  340. /package/src/{review → ai}/prompts/invalid-output-retry.md +0 -0
@@ -1,3 +1,4 @@
1
+ import type { KnowledgeOwner } from "../../types.js";
1
2
  import { type Chunk, chunkMarkdown } from "../knowledge/chunker.js";
2
3
  import type { KnowledgeStore } from "../knowledge/store.js";
3
4
  import { htmlToMarkdown } from "./html-to-md.js";
@@ -7,6 +8,8 @@ export interface FetchOptions {
7
8
  source?: string;
8
9
  /** Bypass 24h TTL cache. */
9
10
  force?: boolean;
11
+ /** Ownership scope for indexed/cached content. Defaults to project-owned when omitted. */
12
+ owner?: KnowledgeOwner;
10
13
  }
11
14
 
12
15
  export interface FetchResult {
@@ -28,15 +31,22 @@ export async function fetchAndIndex(
28
31
  options?: FetchOptions,
29
32
  ): Promise<FetchResult> {
30
33
  const source = options?.source ?? new URL(url).hostname;
34
+ const owner = options?.owner;
35
+ const resolvedOwner = resolveOwner(owner);
31
36
 
32
- // Check cache unless forced
37
+ // Check cache unless forced. Explicit owners must be isolated exactly; the
38
+ // default project-owned path also accepts migrated legacy rows so upgraded
39
+ // stores do not refetch and duplicate visible search results.
33
40
  if (!options?.force) {
34
- const cached = store.db
35
- .prepare("SELECT fetched_at FROM url_cache WHERE url = ? AND source = ?")
36
- .get(url, source) as { fetched_at: number } | null;
41
+ const cacheOwners = owner ? [resolvedOwner] : [resolvedOwner, { ownerScope: "legacy" as const, ownerId: "" }];
42
+ for (const cacheOwner of cacheOwners) {
43
+ const cached = store.db
44
+ .prepare("SELECT fetched_at FROM url_cache WHERE url = ? AND source = ? AND owner_scope = ? AND owner_id = ?")
45
+ .get(url, source, cacheOwner.ownerScope, cacheOwner.ownerId) as { fetched_at: number } | null;
37
46
 
38
- if (cached && Date.now() - cached.fetched_at < TTL_MS) {
39
- return buildCachedResult(store, source);
47
+ if (cached && Date.now() - cached.fetched_at < TTL_MS) {
48
+ return buildCachedResult(store, source, cacheOwner);
49
+ }
40
50
  }
41
51
  }
42
52
 
@@ -51,11 +61,11 @@ export async function fetchAndIndex(
51
61
  const markdown = toMarkdown(rawText, contentType);
52
62
 
53
63
  const chunks = chunkMarkdown(markdown, source);
54
- store.index(chunks, source);
64
+ store.index(chunks, source, owner);
55
65
 
56
66
  store.db.run(
57
- "INSERT OR REPLACE INTO url_cache (url, source, fetched_at) VALUES (?, ?, ?)",
58
- [url, source, Date.now()],
67
+ "INSERT OR REPLACE INTO url_cache (url, source, owner_scope, owner_id, fetched_at) VALUES (?, ?, ?, ?, ?)",
68
+ [url, source, resolvedOwner.ownerScope, resolvedOwner.ownerId, Date.now()],
59
69
  );
60
70
 
61
71
  return {
@@ -102,11 +112,20 @@ function buildPreview(chunks: Chunk[]): string {
102
112
  return out;
103
113
  }
104
114
 
115
+ function resolveOwner(owner: KnowledgeOwner | undefined): Required<KnowledgeOwner> {
116
+ return {
117
+ ownerScope: owner?.ownerScope ?? "project",
118
+ ownerId: owner?.ownerId ?? "",
119
+ };
120
+ }
121
+
105
122
  /** Reconstruct a cached result by querying stored chunks. */
106
- function buildCachedResult(store: KnowledgeStore, source: string): FetchResult {
123
+ function buildCachedResult(store: KnowledgeStore, source: string, owner: Required<KnowledgeOwner>): FetchResult {
107
124
  const rows = store.db
108
- .prepare("SELECT title, body, content_type AS contentType FROM content_chunks WHERE source = ? ORDER BY id")
109
- .all(source) as Chunk[];
125
+ .prepare(
126
+ "SELECT title, body, content_type AS contentType FROM content_chunks WHERE source = ? AND owner_scope = ? AND owner_id = ? ORDER BY id",
127
+ )
128
+ .all(source, owner.ownerScope, owner.ownerId) as Chunk[];
110
129
 
111
130
  return {
112
131
  preview: buildPreview(rows),
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import type { PlatformPaths } from "../platform/types.js";
4
+ import { getProjectStatePath } from "../workspace/state-paths.js";
4
5
 
5
6
  export type DebugSessionContext = {
6
7
  cwd?: string;
@@ -74,7 +75,7 @@ export function createDebugLogger(
74
75
  };
75
76
  }
76
77
 
77
- const filePath = paths.project(cwd, "debug", `tool-${sanitizedTool}__session-${sessionId}.jsonl`);
78
+ const filePath = getProjectStatePath(paths, cwd, "debug", `tool-${sanitizedTool}__session-${sessionId}.jsonl`);
78
79
 
79
80
  const logger: DebugLogger = {
80
81
  enabled: true,
@@ -148,7 +148,7 @@ export const DEPENDENCIES: Dependency[] = [
148
148
  binary: "playwright",
149
149
  required: false,
150
150
  category: "testing",
151
- description: "Test runner for E2E tests (run-e2e-tests.sh)",
151
+ description: "Test runner used by the portable QA Bun entrypoints",
152
152
  checkFn: (exec) => checkBinary(exec, "playwright"),
153
153
  installCmd: null, // Compound command (&&) — not compatible with installDep's naive split
154
154
  url: "https://playwright.dev",
@@ -0,0 +1,170 @@
1
+ // src/discipline/failure-summarizer.ts
2
+ //
3
+ // Offline analyzer that walks stored reliability records and persisted
4
+ // session artifacts, classifies each failure via the failure taxonomy,
5
+ // and produces a compact deterministic report.
6
+ //
7
+ // The summarizer is pure: given the same input records it produces the
8
+ // same report. Every data source is optional — callers pass what they
9
+ // have, the summarizer copes with partial inputs.
10
+ //
11
+ // Phase 8 exit gate: recurring failures are aggregated so the next
12
+ // hardening target is evidence-driven, not anecdotal.
13
+
14
+ import type { PlatformPaths } from "../platform/types.js";
15
+ import type { ReliabilityRecord } from "../types.js";
16
+ import { readReliabilityRecords } from "../storage/reliability-metrics.js";
17
+ import {
18
+ FAILURE_CLASSES,
19
+ classifyFailure,
20
+ describeFailureClass,
21
+ type FailureClass,
22
+ } from "./failure-taxonomy.js";
23
+
24
+ export interface FailureOccurrence {
25
+ /** Timestamp of the underlying event. */
26
+ ts: string;
27
+ /** Command that produced the failure. */
28
+ command: string;
29
+ /** Specific operation (e.g. "commit-plan"), when known. */
30
+ operation?: string;
31
+ /** All classes that fired for this occurrence. */
32
+ classes: FailureClass[];
33
+ /** Truthful reason from the record. */
34
+ reason?: string;
35
+ }
36
+
37
+ export interface FailureClassAggregate {
38
+ class: FailureClass;
39
+ description: string;
40
+ /** Total occurrences of this class in the input. */
41
+ count: number;
42
+ /** Count per command, sorted alphabetically. */
43
+ byCommand: Array<{ command: string; count: number }>;
44
+ /** Up to `exampleCount` representative records for review. */
45
+ examples: FailureOccurrence[];
46
+ }
47
+
48
+ export interface FailureSummary {
49
+ /** Total non-ok records considered. */
50
+ totalFailures: number;
51
+ /** Failure classes that fired at least once, sorted by taxonomy order. */
52
+ aggregates: FailureClassAggregate[];
53
+ /** Non-ok records that did NOT match any taxonomy class. */
54
+ unclassified: FailureOccurrence[];
55
+ }
56
+
57
+ export interface SummarizeOptions {
58
+ /** Number of example occurrences per class. Default 3. */
59
+ exampleCount?: number;
60
+ }
61
+
62
+ function isFailureRecord(record: ReliabilityRecord): boolean {
63
+ return record.outcome !== "ok";
64
+ }
65
+
66
+ function classifyRecord(record: ReliabilityRecord): FailureOccurrence {
67
+ const classes = classifyFailure({
68
+ outcome: record.outcome,
69
+ reason: record.reason,
70
+ // attempts used by unproductive-retry rule
71
+ attempts: record.attempts,
72
+ } as any);
73
+ return {
74
+ ts: record.ts,
75
+ command: record.command,
76
+ operation: record.operation,
77
+ classes,
78
+ reason: record.reason,
79
+ };
80
+ }
81
+
82
+ function aggregate(
83
+ occurrences: FailureOccurrence[],
84
+ exampleCount: number,
85
+ ): FailureClassAggregate[] {
86
+ const map = new Map<FailureClass, FailureOccurrence[]>();
87
+ for (const occ of occurrences) {
88
+ for (const cls of occ.classes) {
89
+ const list = map.get(cls) ?? [];
90
+ list.push(occ);
91
+ map.set(cls, list);
92
+ }
93
+ }
94
+
95
+ const aggregates: FailureClassAggregate[] = [];
96
+ for (const cls of FAILURE_CLASSES) {
97
+ const list = map.get(cls);
98
+ if (!list || list.length === 0) continue;
99
+ const byCommandMap = new Map<string, number>();
100
+ for (const occ of list) {
101
+ byCommandMap.set(occ.command, (byCommandMap.get(occ.command) ?? 0) + 1);
102
+ }
103
+ const byCommand = [...byCommandMap.entries()]
104
+ .map(([command, count]) => ({ command, count }))
105
+ .sort((a, b) => a.command.localeCompare(b.command));
106
+
107
+ aggregates.push({
108
+ class: cls,
109
+ description: describeFailureClass(cls),
110
+ count: list.length,
111
+ byCommand,
112
+ examples: list.slice(0, Math.max(0, exampleCount)),
113
+ });
114
+ }
115
+
116
+ return aggregates;
117
+ }
118
+
119
+ /**
120
+ * Summarize an arbitrary list of reliability records. Pure — no filesystem.
121
+ * Callers can combine records from multiple sources before summarizing.
122
+ */
123
+ export function summarizeFailures(
124
+ records: ReliabilityRecord[],
125
+ options: SummarizeOptions = {},
126
+ ): FailureSummary {
127
+ const exampleCount = options.exampleCount ?? 3;
128
+ const failures = records.filter(isFailureRecord).map(classifyRecord);
129
+
130
+ const classified = failures.filter((f) => f.classes.length > 0);
131
+ const unclassified = failures.filter((f) => f.classes.length === 0);
132
+
133
+ return {
134
+ totalFailures: failures.length,
135
+ aggregates: aggregate(classified, exampleCount),
136
+ unclassified,
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Convenience: load records from the per-cwd reliability store and
142
+ * summarize. Empty store produces an empty summary (no crashes).
143
+ */
144
+ export function summarizeLocalFailures(
145
+ paths: PlatformPaths,
146
+ cwd: string,
147
+ options: SummarizeOptions = {},
148
+ ): FailureSummary {
149
+ return summarizeFailures(readReliabilityRecords(paths, cwd), options);
150
+ }
151
+
152
+ /**
153
+ * Format a summary as readable lines. `[]` when there are no failures so
154
+ * callers can branch on length without a special case.
155
+ */
156
+ export function formatFailureSummary(summary: FailureSummary): string[] {
157
+ if (summary.totalFailures === 0) return [];
158
+
159
+ const lines: string[] = [`Failure summary: ${summary.totalFailures} non-ok record(s)`];
160
+ for (const agg of summary.aggregates) {
161
+ lines.push(` [${agg.class}] ${agg.description} \u2014 ${agg.count} occurrence(s)`);
162
+ for (const per of agg.byCommand) {
163
+ lines.push(` \u00b7 ${per.command}: ${per.count}`);
164
+ }
165
+ }
166
+ if (summary.unclassified.length > 0) {
167
+ lines.push(` [unclassified] ${summary.unclassified.length} record(s) did not match any taxonomy class`);
168
+ }
169
+ return lines;
170
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Workflow failure taxonomy.
3
+ *
4
+ * Small, explicit set of failure classes used by summarizer and eval-promotion
5
+ * to turn raw reliability records + session notes into actionable categories.
6
+ *
7
+ * Classification is pure: regex / string checks only, no dynamic evaluation,
8
+ * deterministic for the same input, never throws.
9
+ */
10
+
11
+ export const FAILURE_CLASSES = [
12
+ "premature-completion",
13
+ "wrong-tool-path",
14
+ "missing-artifact",
15
+ "verification-skipped",
16
+ "discovery-miss",
17
+ "unproductive-retry",
18
+ ] as const;
19
+
20
+ export type FailureClass = (typeof FAILURE_CLASSES)[number];
21
+
22
+ export interface FailureSignals {
23
+ /** Stored reliability record, optional. */
24
+ outcome?: "ok" | "blocked" | "retry-exhausted" | "fallback" | "agent-error";
25
+ /** Reason string from the reliability record or log, optional. */
26
+ reason?: string;
27
+ /** Tool call name if the failure involves a blocked/rerouted tool. */
28
+ toolName?: string;
29
+ /** Path of an artifact that was expected but not found. */
30
+ missingArtifactPath?: string;
31
+ /** Free-text description from debug traces or session notes. */
32
+ note?: string;
33
+ /** Attempt count from the reliability record, optional. */
34
+ attempts?: number;
35
+ }
36
+
37
+ // Tool names blocked by `routeToolCall` when context-mode is active.
38
+ // Keep in lock-step with `src/context-mode/routing.ts` — every native tool
39
+ // that the router redirects must classify as `wrong-tool-path` here.
40
+ const BLOCKED_TOOLS = new Set<string>([
41
+ "search",
42
+ "find",
43
+ "bash-grep",
44
+ "bash-find",
45
+ "curl",
46
+ "wget",
47
+ "fetch",
48
+ "web_fetch",
49
+ "WebFetch",
50
+ ]);
51
+
52
+ const DESCRIPTIONS: Record<FailureClass, string> = {
53
+ "premature-completion":
54
+ "Workflow claimed done before required artifact existed.",
55
+ "wrong-tool-path":
56
+ "Workflow reached for a blocked tool instead of the preferred ctx_* tool.",
57
+ "missing-artifact":
58
+ "Required output (plan file, session, findings.md) was never written.",
59
+ "verification-skipped":
60
+ "Agent skipped a mandatory verification step (test, typecheck, eval).",
61
+ "discovery-miss":
62
+ "Workflow wandered before finding the right entry point.",
63
+ "unproductive-retry":
64
+ "Retry loop spent attempts without making progress.",
65
+ };
66
+
67
+ /**
68
+ * Classify a failure based on signals. Returns one or more matching classes in
69
+ * priority order (matching `FAILURE_CLASSES` order); empty array when no class
70
+ * fires. Deterministic — never throws.
71
+ */
72
+ export function classifyFailure(signals: FailureSignals): FailureClass[] {
73
+ const reason = signals.reason ?? "";
74
+ const note = signals.note ?? "";
75
+ const matches: FailureClass[] = [];
76
+
77
+ // premature-completion
78
+ if (
79
+ (signals.outcome === "ok" &&
80
+ /incomplete|partial|unresolved/i.test(reason)) ||
81
+ (signals.outcome === "fallback" &&
82
+ /never produced valid artifact/i.test(reason))
83
+ ) {
84
+ matches.push("premature-completion");
85
+ }
86
+
87
+ // wrong-tool-path
88
+ if (
89
+ (signals.toolName && BLOCKED_TOOLS.has(signals.toolName)) ||
90
+ /ctx_/.test(reason)
91
+ ) {
92
+ matches.push("wrong-tool-path");
93
+ }
94
+
95
+ // missing-artifact
96
+ if (
97
+ signals.missingArtifactPath !== undefined ||
98
+ (/missing/i.test(reason) && /plan|findings|session/i.test(reason))
99
+ ) {
100
+ matches.push("missing-artifact");
101
+ }
102
+
103
+ // verification-skipped
104
+ if (
105
+ /without running (validator|tests|typecheck)/i.test(reason) ||
106
+ /skipped (verification|validation|test)/i.test(reason)
107
+ ) {
108
+ matches.push("verification-skipped");
109
+ }
110
+
111
+ // discovery-miss
112
+ if (
113
+ /wandered/i.test(reason) ||
114
+ /wrong file/i.test(reason) ||
115
+ /searched broadly/i.test(note)
116
+ ) {
117
+ matches.push("discovery-miss");
118
+ }
119
+
120
+ // unproductive-retry
121
+ if (signals.outcome === "retry-exhausted" && (signals.attempts ?? 0) >= 3) {
122
+ matches.push("unproductive-retry");
123
+ }
124
+
125
+ return matches;
126
+ }
127
+
128
+ /** Canonical human-friendly description per class. */
129
+ export function describeFailureClass(cls: FailureClass): string {
130
+ return DESCRIPTIONS[cls];
131
+ }
@@ -0,0 +1,125 @@
1
+ // src/discipline/workflow-invariants.ts
2
+ //
3
+ // Runtime invariants that AI-heavy workflows must satisfy before reporting
4
+ // completion. When an invariant fails, the workflow yields a truthful
5
+ // blocker instead of silently claiming success. Used by plan/review/qa/fix-pr
6
+ // completion paths and by Phase 0 evals that test workflow boundaries.
7
+ //
8
+ // Design notes:
9
+ // - Invariants are pure functions over a workflow-specific context object.
10
+ // - Invariants return either `{ state: "satisfied" }` or a blocker with a
11
+ // human-readable `reason`. Keep reasons short and actionable.
12
+ // - `checkInvariants` returns the FIRST blocker, not a list. Workflows
13
+ // surface one blocker at a time so the user (or the model) can fix it
14
+ // and proceed, rather than being handed a noisy report.
15
+ // - This module owns only the generic abstraction. Workflow-specific
16
+ // invariant builders live next to their workflow (e.g. plan's PlanSpec
17
+ // validation in src/planning/approval-flow.ts).
18
+
19
+ export type InvariantState =
20
+ | { state: "satisfied" }
21
+ | { state: "blocked"; reason: string };
22
+
23
+ export interface WorkflowInvariant<TContext> {
24
+ /** Stable identifier. Used for logging and test assertions. */
25
+ name: string;
26
+ /**
27
+ * Evaluate this invariant against the workflow context. Return a blocker
28
+ * with a truthful reason if the invariant fails.
29
+ */
30
+ check: (ctx: TContext) => InvariantState | Promise<InvariantState>;
31
+ }
32
+
33
+ export type InvariantCheckResult =
34
+ | { state: "satisfied" }
35
+ | { state: "blocked"; invariant: string; reason: string };
36
+
37
+ /**
38
+ * Run invariants in order against `ctx`. Stop at the first blocker and
39
+ * return it. Returns `{ state: "satisfied" }` only when every invariant
40
+ * reports satisfied.
41
+ */
42
+ export async function checkInvariants<TContext>(
43
+ invariants: readonly WorkflowInvariant<TContext>[],
44
+ ctx: TContext,
45
+ ): Promise<InvariantCheckResult> {
46
+ for (const invariant of invariants) {
47
+ const result = await invariant.check(ctx);
48
+ if (result.state === "blocked") {
49
+ return { state: "blocked", invariant: invariant.name, reason: result.reason };
50
+ }
51
+ }
52
+ return { state: "satisfied" };
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Invariant builders — the common shapes workflows compose from
57
+ // ---------------------------------------------------------------------------
58
+
59
+ /**
60
+ * Build an invariant that is satisfied only when the predicate returns true.
61
+ */
62
+ export function requireCondition<TContext>(
63
+ name: string,
64
+ predicate: (ctx: TContext) => boolean | Promise<boolean>,
65
+ reason: string,
66
+ ): WorkflowInvariant<TContext> {
67
+ return {
68
+ name,
69
+ async check(ctx) {
70
+ const satisfied = await predicate(ctx);
71
+ return satisfied ? { state: "satisfied" } : { state: "blocked", reason };
72
+ },
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Build an invariant that is satisfied only when the artifact exists. The
78
+ * caller supplies both the existence check and the artifact identifier used
79
+ * in the blocker reason.
80
+ */
81
+ export function requireArtifact<TContext>(
82
+ name: string,
83
+ exists: (ctx: TContext) => boolean | Promise<boolean>,
84
+ artifactLabel: string,
85
+ ): WorkflowInvariant<TContext> {
86
+ return requireCondition(
87
+ name,
88
+ exists,
89
+ `Required artifact is missing: ${artifactLabel}.`,
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Build an invariant that blocks when the workflow still has pending work.
95
+ * Typical uses: outstanding todos, unresolved review comments, undispatched
96
+ * follow-up steps.
97
+ */
98
+ export function requireNoPendingWork<TContext>(
99
+ name: string,
100
+ pending: (ctx: TContext) => number | Promise<number>,
101
+ workLabel: string,
102
+ ): WorkflowInvariant<TContext> {
103
+ return {
104
+ name,
105
+ async check(ctx) {
106
+ const count = await pending(ctx);
107
+ return count === 0
108
+ ? { state: "satisfied" }
109
+ : {
110
+ state: "blocked",
111
+ reason: `${count} ${workLabel} still pending \u2014 workflow cannot complete.`,
112
+ };
113
+ },
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Render a blocker result as a single line for notifications, logs, or
119
+ * prompts. `satisfied` states render as an empty string so callers can join
120
+ * multiple results without branching.
121
+ */
122
+ export function formatInvariantResult(result: InvariantCheckResult): string {
123
+ if (result.state === "satisfied") return "";
124
+ return `[${result.invariant}] ${result.reason}`;
125
+ }
@@ -0,0 +1,31 @@
1
+ // src/discovery/index.ts
2
+ //
3
+ // Deterministic repo-entry-point discovery. Given a workflow query (e.g.
4
+ // "fix the login bug", "review the latest commit") and context signals
5
+ // (changed files, workspace targets), rank likely-relevant files with a
6
+ // short rationale for each candidate.
7
+ //
8
+ // Used by /supi:review, /supi:plan, /supi:qa, and /supi:fix-pr to start
9
+ // from strong candidates rather than broad wandering.
10
+ //
11
+ // Non-goals:
12
+ // - Hosted search / vector database
13
+ // - Replacing native tools (grep/lsp). This layer *orchestrates* them.
14
+ //
15
+ // Phase 6 exit gate: fixture workspaces rank expected files first, every
16
+ // candidate carries rationale, behavior stays stable when inputs are empty.
17
+
18
+ export type {
19
+ DiscoveryCandidate,
20
+ DiscoveryInput,
21
+ DiscoveryResult,
22
+ DiscoverySource,
23
+ } from "./rank.js";
24
+
25
+ export { rankDiscoveryCandidates } from "./rank.js";
26
+ export { discoverFromSources } from "./sources.js";
27
+
28
+ export { rankWithLspAugmentation } from "./lsp.js";
29
+ export type { LspSymbolLocation, LspAugmentedResult } from "./lsp.js";
30
+ export { suggestCandidatesForWorkflow } from "./workflow.js";
31
+ export type { WorkflowDiscoveryInput, WorkflowDiscoveryResult } from "./workflow.js";
@@ -0,0 +1,87 @@
1
+ // src/discovery/lsp.ts
2
+ //
3
+ // LSP-assisted discovery: convert symbol search results into external
4
+ // signals the ranker consumes. When LSP is unavailable or the symbol
5
+ // lookup returns nothing, falls through cleanly — callers should never
6
+ // require LSP for discovery to work.
7
+ //
8
+ // This module deliberately does not talk to LSP directly. Callers pass in
9
+ // a `querySymbols` callback so the same function can be driven by:
10
+ // - the live platform LSP bridge in production
11
+ // - a deterministic fixture in tests
12
+
13
+ import type { DiscoveryCandidate } from "./rank.js";
14
+ import { rankDiscoveryCandidates, type DiscoveryInput } from "./rank.js";
15
+
16
+ export interface LspSymbolLocation {
17
+ /** Repo-relative path where the symbol is defined or referenced. */
18
+ path: string;
19
+ /** Short reason string attached to the candidate. */
20
+ reason: string;
21
+ /** Extra score beyond the baseline LSP boost. Optional. */
22
+ bonus?: number;
23
+ }
24
+
25
+ export interface LspDiscoveryInput extends DiscoveryInput {
26
+ /**
27
+ * Called with the workflow `query`. Must return a (possibly empty) list
28
+ * of symbol locations. Any thrown error is caught and treated as
29
+ * "LSP unavailable" — discovery still returns a ranked list from the
30
+ * remaining sources.
31
+ */
32
+ querySymbols: (query: string) => LspSymbolLocation[] | Promise<LspSymbolLocation[]>;
33
+ }
34
+
35
+ export interface LspAugmentedResult {
36
+ candidates: DiscoveryCandidate[];
37
+ lspAvailable: boolean;
38
+ lspHitCount: number;
39
+ }
40
+
41
+ const WEIGHT_LSP = 6;
42
+
43
+ /**
44
+ * Run LSP symbol discovery against the query, fold the hits into external
45
+ * signals, and rank the combined candidate pool. When `querySymbols` throws
46
+ * or returns [], the result is still valid — discovery degrades, not fails.
47
+ */
48
+ export async function rankWithLspAugmentation(
49
+ input: LspDiscoveryInput,
50
+ ): Promise<LspAugmentedResult> {
51
+ let lspAvailable = true;
52
+ let lspHits: LspSymbolLocation[] = [];
53
+
54
+ if (input.query && input.query.trim().length > 0) {
55
+ try {
56
+ lspHits = await input.querySymbols(input.query);
57
+ } catch {
58
+ lspAvailable = false;
59
+ lspHits = [];
60
+ }
61
+ }
62
+
63
+ const externalSignals: Record<string, { score: number; rationale: string }> = {
64
+ ...(input.externalSignals ?? {}),
65
+ };
66
+ for (const hit of lspHits) {
67
+ const prior = externalSignals[hit.path];
68
+ const score = WEIGHT_LSP + (hit.bonus ?? 0);
69
+ externalSignals[hit.path] = prior
70
+ ? {
71
+ score: prior.score + score,
72
+ rationale: `${prior.rationale}; ${hit.reason}`,
73
+ }
74
+ : { score, rationale: hit.reason };
75
+ }
76
+
77
+ const ranked = rankDiscoveryCandidates({
78
+ ...input,
79
+ externalSignals,
80
+ });
81
+
82
+ return {
83
+ candidates: ranked.candidates,
84
+ lspAvailable,
85
+ lspHitCount: lspHits.length,
86
+ };
87
+ }