@hover-dev/core 0.16.0 → 0.18.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 (181) hide show
  1. package/README.md +26 -55
  2. package/dist/agentDirectives.d.ts +55 -0
  3. package/dist/agentDirectives.d.ts.map +1 -0
  4. package/dist/agentDirectives.js +276 -0
  5. package/dist/engine.d.ts +28 -0
  6. package/dist/engine.d.ts.map +1 -0
  7. package/dist/engine.js +27 -0
  8. package/dist/memory/businessMemory.d.ts +29 -0
  9. package/dist/memory/businessMemory.d.ts.map +1 -0
  10. package/dist/memory/businessMemory.js +125 -0
  11. package/dist/playwright/launchChrome.d.ts +18 -0
  12. package/dist/playwright/launchChrome.d.ts.map +1 -1
  13. package/dist/playwright/launchChrome.js +46 -3
  14. package/dist/qa/candidates.d.ts +32 -0
  15. package/dist/qa/candidates.d.ts.map +1 -0
  16. package/dist/qa/candidates.js +20 -0
  17. package/dist/qa/intensity.d.ts +33 -0
  18. package/dist/qa/intensity.d.ts.map +1 -0
  19. package/dist/qa/intensity.js +25 -0
  20. package/dist/qa/qaReport.d.ts +19 -0
  21. package/dist/qa/qaReport.d.ts.map +1 -0
  22. package/dist/qa/qaReport.js +50 -0
  23. package/dist/sessions/sessions.d.ts +125 -0
  24. package/dist/sessions/sessions.d.ts.map +1 -0
  25. package/dist/sessions/sessions.js +175 -0
  26. package/dist/specs/authFixture.d.ts +30 -0
  27. package/dist/specs/authFixture.d.ts.map +1 -0
  28. package/dist/specs/authFixture.js +145 -0
  29. package/dist/specs/detectSharedFlows.d.ts +1 -1
  30. package/dist/specs/detectSharedFlows.d.ts.map +1 -1
  31. package/dist/specs/detectSharedFlows.js +20 -21
  32. package/dist/specs/generatePageObject.d.ts +1 -1
  33. package/dist/specs/generatePageObject.d.ts.map +1 -1
  34. package/dist/specs/healPrompt.d.ts +19 -0
  35. package/dist/specs/healPrompt.d.ts.map +1 -0
  36. package/dist/specs/healPrompt.js +48 -0
  37. package/dist/specs/humanSteps.d.ts +4 -8
  38. package/dist/specs/humanSteps.d.ts.map +1 -1
  39. package/dist/specs/humanSteps.js +6 -1
  40. package/dist/specs/optimizeSpec.d.ts +15 -8
  41. package/dist/specs/optimizeSpec.d.ts.map +1 -1
  42. package/dist/specs/optimizeSpec.js +71 -41
  43. package/dist/specs/pageObjectManifest.d.ts +3 -1
  44. package/dist/specs/pageObjectManifest.d.ts.map +1 -1
  45. package/dist/specs/pageObjectManifest.js +24 -19
  46. package/dist/specs/replayGrounded.d.ts +45 -0
  47. package/dist/specs/replayGrounded.d.ts.map +1 -0
  48. package/dist/specs/replayGrounded.js +155 -0
  49. package/dist/specs/runFailures.d.ts +34 -0
  50. package/dist/specs/runFailures.d.ts.map +1 -0
  51. package/dist/specs/runFailures.js +93 -0
  52. package/dist/specs/seeds.d.ts +16 -15
  53. package/dist/specs/seeds.d.ts.map +1 -1
  54. package/dist/specs/seeds.js +86 -54
  55. package/dist/specs/sidecar.d.ts +34 -6
  56. package/dist/specs/sidecar.d.ts.map +1 -1
  57. package/dist/specs/sidecar.js +79 -9
  58. package/dist/specs/specStep.d.ts +21 -0
  59. package/dist/specs/specStep.d.ts.map +1 -0
  60. package/dist/specs/specStep.js +1 -0
  61. package/dist/specs/text.d.ts +8 -6
  62. package/dist/specs/text.d.ts.map +1 -1
  63. package/dist/specs/text.js +10 -7
  64. package/dist/specs/writeSpec.d.ts +62 -1
  65. package/dist/specs/writeSpec.d.ts.map +1 -1
  66. package/dist/specs/writeSpec.js +596 -21
  67. package/package.json +9 -29
  68. package/dist/agents/aider.d.ts +0 -16
  69. package/dist/agents/aider.d.ts.map +0 -1
  70. package/dist/agents/aider.js +0 -161
  71. package/dist/agents/argv.d.ts +0 -11
  72. package/dist/agents/argv.d.ts.map +0 -1
  73. package/dist/agents/argv.js +0 -23
  74. package/dist/agents/claude.d.ts +0 -3
  75. package/dist/agents/claude.d.ts.map +0 -1
  76. package/dist/agents/claude.js +0 -195
  77. package/dist/agents/codex.d.ts +0 -19
  78. package/dist/agents/codex.d.ts.map +0 -1
  79. package/dist/agents/codex.js +0 -216
  80. package/dist/agents/cursor.d.ts +0 -18
  81. package/dist/agents/cursor.d.ts.map +0 -1
  82. package/dist/agents/cursor.js +0 -220
  83. package/dist/agents/detect.d.ts +0 -46
  84. package/dist/agents/detect.d.ts.map +0 -1
  85. package/dist/agents/detect.js +0 -80
  86. package/dist/agents/gemini.d.ts +0 -17
  87. package/dist/agents/gemini.d.ts.map +0 -1
  88. package/dist/agents/gemini.js +0 -186
  89. package/dist/agents/index.d.ts +0 -6
  90. package/dist/agents/index.d.ts.map +0 -1
  91. package/dist/agents/index.js +0 -5
  92. package/dist/agents/invoke.d.ts +0 -12
  93. package/dist/agents/invoke.d.ts.map +0 -1
  94. package/dist/agents/invoke.js +0 -96
  95. package/dist/agents/qwen.d.ts +0 -17
  96. package/dist/agents/qwen.d.ts.map +0 -1
  97. package/dist/agents/qwen.js +0 -172
  98. package/dist/agents/registry.d.ts +0 -19
  99. package/dist/agents/registry.d.ts.map +0 -1
  100. package/dist/agents/registry.js +0 -34
  101. package/dist/agents/shared.d.ts +0 -28
  102. package/dist/agents/shared.d.ts.map +0 -1
  103. package/dist/agents/shared.js +0 -35
  104. package/dist/agents/types.d.ts +0 -186
  105. package/dist/agents/types.d.ts.map +0 -1
  106. package/dist/agents/types.js +0 -23
  107. package/dist/index.d.ts +0 -3
  108. package/dist/index.d.ts.map +0 -1
  109. package/dist/index.js +0 -2
  110. package/dist/mcp/sourceFence.d.ts +0 -23
  111. package/dist/mcp/sourceFence.d.ts.map +0 -1
  112. package/dist/mcp/sourceFence.js +0 -75
  113. package/dist/mcp/sourceServer.d.ts +0 -3
  114. package/dist/mcp/sourceServer.d.ts.map +0 -1
  115. package/dist/mcp/sourceServer.js +0 -116
  116. package/dist/playwright/cdpStatus.d.ts +0 -29
  117. package/dist/playwright/cdpStatus.d.ts.map +0 -1
  118. package/dist/playwright/cdpStatus.js +0 -119
  119. package/dist/playwright/preflight.d.ts +0 -31
  120. package/dist/playwright/preflight.d.ts.map +0 -1
  121. package/dist/playwright/preflight.js +0 -82
  122. package/dist/playwright/preflightCache.d.ts +0 -27
  123. package/dist/playwright/preflightCache.d.ts.map +0 -1
  124. package/dist/playwright/preflightCache.js +0 -21
  125. package/dist/playwright/raiseWindow.d.ts +0 -10
  126. package/dist/playwright/raiseWindow.d.ts.map +0 -1
  127. package/dist/playwright/raiseWindow.js +0 -158
  128. package/dist/playwright/resolveMcpConfig.d.ts +0 -55
  129. package/dist/playwright/resolveMcpConfig.d.ts.map +0 -1
  130. package/dist/playwright/resolveMcpConfig.js +0 -66
  131. package/dist/plugin-api.d.ts +0 -235
  132. package/dist/plugin-api.d.ts.map +0 -1
  133. package/dist/plugin-api.js +0 -52
  134. package/dist/runSession.d.ts +0 -42
  135. package/dist/runSession.d.ts.map +0 -1
  136. package/dist/runSession.js +0 -81
  137. package/dist/scripts/bench-multi-tab.d.ts +0 -2
  138. package/dist/scripts/bench-multi-tab.d.ts.map +0 -1
  139. package/dist/scripts/bench-multi-tab.js +0 -192
  140. package/dist/scripts/bench-ttfb.d.ts +0 -2
  141. package/dist/scripts/bench-ttfb.d.ts.map +0 -1
  142. package/dist/scripts/bench-ttfb.js +0 -127
  143. package/dist/scripts/start-chrome.d.ts +0 -3
  144. package/dist/scripts/start-chrome.d.ts.map +0 -1
  145. package/dist/scripts/start-chrome.js +0 -23
  146. package/dist/service/cdpHandlers.d.ts +0 -44
  147. package/dist/service/cdpHandlers.d.ts.map +0 -1
  148. package/dist/service/cdpHandlers.js +0 -85
  149. package/dist/service/cdpHint.d.ts +0 -48
  150. package/dist/service/cdpHint.d.ts.map +0 -1
  151. package/dist/service/cdpHint.js +0 -216
  152. package/dist/service/conventions.d.ts +0 -8
  153. package/dist/service/conventions.d.ts.map +0 -1
  154. package/dist/service/conventions.js +0 -42
  155. package/dist/service/saveHandlers.d.ts +0 -52
  156. package/dist/service/saveHandlers.d.ts.map +0 -1
  157. package/dist/service/saveHandlers.js +0 -75
  158. package/dist/service/types.d.ts +0 -58
  159. package/dist/service/types.d.ts.map +0 -1
  160. package/dist/service/types.js +0 -26
  161. package/dist/service.d.ts +0 -50
  162. package/dist/service.d.ts.map +0 -1
  163. package/dist/service.js +0 -1065
  164. package/dist/skills/writeSkill.d.ts +0 -27
  165. package/dist/skills/writeSkill.d.ts.map +0 -1
  166. package/dist/skills/writeSkill.js +0 -13
  167. package/dist/specs/extractPageObjects.d.ts +0 -18
  168. package/dist/specs/extractPageObjects.d.ts.map +0 -1
  169. package/dist/specs/extractPageObjects.js +0 -98
  170. package/dist/specs/listSpecs.d.ts +0 -52
  171. package/dist/specs/listSpecs.d.ts.map +0 -1
  172. package/dist/specs/listSpecs.js +0 -139
  173. package/dist/specs/optimizationSuggestion.d.ts +0 -26
  174. package/dist/specs/optimizationSuggestion.d.ts.map +0 -1
  175. package/dist/specs/optimizationSuggestion.js +0 -28
  176. package/dist/specs/optimizeSpecWithAgent.d.ts +0 -11
  177. package/dist/specs/optimizeSpecWithAgent.d.ts.map +0 -1
  178. package/dist/specs/optimizeSpecWithAgent.js +0 -40
  179. package/dist/specs/writeCaseCsv.d.ts +0 -28
  180. package/dist/specs/writeCaseCsv.d.ts.map +0 -1
  181. package/dist/specs/writeCaseCsv.js +0 -134
@@ -4,17 +4,17 @@
4
4
  * Reads a deterministic draft spec + its sidecar, asks an LLM (the codegen
5
5
  * mode — no browser, no MCP) to improve it (chiefly: add assertions for the
6
6
  * feedback the session observed), validates the result, and writes it as a
7
- * CANDIDATE at `.hover/optimized/<slug>.spec.ts.draft` — never overwriting the
7
+ * CANDIDATE at `.hover/cache/optimized/<slug>.spec.ts.draft` — never overwriting the
8
8
  * original (D10). A human promotes or discards it via diff.
9
9
  *
10
10
  * The LLM call is injected (`runCodegen`) so callers wire their own agent and
11
11
  * tests run deterministically without spawning anything.
12
12
  */
13
- import { readFile, mkdir, writeFile, rm } from 'node:fs/promises';
13
+ import { readFile, mkdir, writeFile, readdir } from 'node:fs/promises';
14
14
  import { join } from 'node:path';
15
15
  import { Project } from 'ts-morph';
16
- import { sidecarDir } from './sidecar.js';
17
- import { readSeeds, relevantSeeds } from './seeds.js';
16
+ import { readSidecar } from './sidecar.js';
17
+ import { BUILTIN_SEEDS, relevantSeeds } from './seeds.js';
18
18
  import { softBatch } from './softBatch.js';
19
19
  export class OptimizeError extends Error {
20
20
  constructor(message) {
@@ -22,6 +22,32 @@ export class OptimizeError extends Error {
22
22
  this.name = 'OptimizeError';
23
23
  }
24
24
  }
25
+ /** Total Page-Object source budget injected into the prompt (chars). Keeps the
26
+ * refinement context bounded on a large suite; extra POMs are dropped (logged in
27
+ * the prompt) rather than blowing the window. */
28
+ const POM_CONTEXT_BUDGET = 16_000;
29
+ /** Best-effort gather of the suite context (conventions.md + __vibe_tests__/pages
30
+ * Page Objects). Missing files → empty; never throws. */
31
+ export async function gatherSuiteContext(devRoot) {
32
+ const conventions = (await readFile(join(devRoot, '.hover', 'conventions.md'), 'utf-8').catch(() => ''))
33
+ .trim() || undefined;
34
+ const pages = [];
35
+ let used = 0;
36
+ try {
37
+ const dir = join(devRoot, '__vibe_tests__', 'pages');
38
+ for (const f of (await readdir(dir)).sort()) {
39
+ if (!f.endsWith('.ts'))
40
+ continue;
41
+ const source = (await readFile(join(dir, f), 'utf-8').catch(() => '')).trim();
42
+ if (!source || used + source.length > POM_CONTEXT_BUDGET)
43
+ continue;
44
+ used += source.length;
45
+ pages.push({ name: f, source });
46
+ }
47
+ }
48
+ catch { /* no pages dir — plain spec, no POMs to reuse */ }
49
+ return { conventions, pages };
50
+ }
25
51
  export async function optimizeSpec(devRoot, slug, runCodegen) {
26
52
  const specPath = join(devRoot, '__vibe_tests__', `${slug}.spec.ts`);
27
53
  let draft;
@@ -31,18 +57,14 @@ export async function optimizeSpec(devRoot, slug, runCodegen) {
31
57
  catch {
32
58
  throw new OptimizeError(`spec not found: ${slug} (looked at ${specPath})`);
33
59
  }
34
- let sidecar = null;
35
- try {
36
- sidecar = JSON.parse(await readFile(join(sidecarDir(devRoot), `${slug}.json`), 'utf-8'));
37
- }
38
- catch {
39
- /* no sidecar — optimize from the draft alone */
40
- }
60
+ // Legacy-aware read; null → no sidecar, optimize from the draft alone.
61
+ const sidecar = await readSidecar(devRoot, slug);
41
62
  const specTools = new Set((sidecar?.steps ?? [])
42
63
  .filter(s => s.kind === 'step' && s.tool)
43
64
  .map(s => s.tool));
44
- const seeds = relevantSeeds(await readSeeds(devRoot), specTools);
45
- const raw = await runCodegen(buildOptimizePrompt(draft, sidecar, seeds));
65
+ const seeds = relevantSeeds(BUILTIN_SEEDS, specTools);
66
+ const suite = await gatherSuiteContext(devRoot);
67
+ const raw = await runCodegen(buildOptimizePrompt(draft, sidecar, seeds, suite));
46
68
  const llmCode = extractCode(raw);
47
69
  const check = validateSpecCode(llmCode);
48
70
  if (!check.ok) {
@@ -52,7 +74,9 @@ export async function optimizeSpec(devRoot, slug, runCodegen) {
52
74
  // applies the safe mechanical rewrite (trailing run of independent assertions
53
75
  // → expect.soft) surgically on its output. See softBatch.ts for the guard.
54
76
  const code = softBatch(llmCode).code;
55
- const dir = join(devRoot, '__vibe_tests__', '.hover', 'optimized');
77
+ // Candidates are disposable derived artifacts → `.hover/cache/` (always
78
+ // gitignored). Losing one costs a re-run of the optimization, nothing more.
79
+ const dir = join(devRoot, '.hover', 'cache', 'optimized');
56
80
  await mkdir(dir, { recursive: true });
57
81
  // `.spec.ts.draft`, never `*.spec.ts` — Playwright's glob must not collect a
58
82
  // candidate before a human reviews it.
@@ -65,7 +89,7 @@ export async function optimizeSpec(devRoot, slug, runCodegen) {
65
89
  * same rules the deterministic path enforces (semantic selectors, no XPath, no
66
90
  * waitForTimeout, keep the test.step shape).
67
91
  */
68
- export function buildOptimizePrompt(draft, sidecar, seeds = []) {
92
+ export function buildOptimizePrompt(draft, sidecar, seeds = [], suite = { pages: [] }) {
69
93
  const done = sidecar?.steps.find(s => s.kind === 'done');
70
94
  const stepsJson = sidecar
71
95
  ? JSON.stringify(sidecar.steps.filter(s => s.kind === 'step'), null, 2)
@@ -78,8 +102,29 @@ export function buildOptimizePrompt(draft, sidecar, seeds = []) {
78
102
  `Improve it WITHOUT changing what it tests:`,
79
103
  ` - Add assertions for the success/error feedback the session OBSERVED —`,
80
104
  ` e.g. await expect(page.getByText('Invalid email')).toBeVisible(), a`,
81
- ` success toast, a counter value. Use the captured steps + the outcome`,
105
+ ` success toast, a confirmation. Use the captured steps + the outcome`,
82
106
  ` summary below to know what to assert.`,
107
+ ` - ASSERT THE INVARIANT, NOT THIS RUN'S VALUE. When the asserted content`,
108
+ ` varies run-to-run (a generated id, an order number, a date, a counter, a`,
109
+ ` drawn word — and ANY step whose captured input has "dynamic": true),`,
110
+ ` assert the contract with a pattern, never the literal: expect(loc).not`,
111
+ ` .toHaveText('') or .toContainText(/…/), or .toHaveCount(n). Reserve an`,
112
+ ` exact-literal assertion for genuinely fixed text (a heading, a fixed`,
113
+ ` confirmation). Any captured assert_visible step already encodes this —`,
114
+ ` preserve its matcher/intent; don't tighten a dynamic one back to a literal.`,
115
+ ` - DE-LITERALIZE VOLATILE VALUES — even ones NOT pre-flagged. Scan every`,
116
+ ` selector and assertion in the spec for values that are this run's DATA, not`,
117
+ ` stable UI: a word/title/name/id/order number/date/price/count drawn from`,
118
+ ` app content. Judge from what this app IS (the conventions + Page Objects`,
119
+ ` below tell you) and the captured values. For each volatile one:`,
120
+ ` getByRole(role, { name: "<value>" }) -> a stable anchor (getByTestId, or`,
121
+ ` getByRole(role).first(), or a Page Object method); toHaveText("<value>") ->`,
122
+ ` .not.toHaveText('') or .toContainText(/.../). When unsure if a value is`,
123
+ ` volatile, PREFER the invariant — over-asserting a changing value is the`,
124
+ ` failure we are fixing. But NEVER newly hard-code a value that wasn't there.`,
125
+ ` - REUSE the project's Page Objects + conventions (below). If a step sequence`,
126
+ ` matches a Page Object method, CALL it instead of re-emitting raw locators,`,
127
+ ` and follow the naming / structure the existing suite uses.`,
83
128
  ` - Keep semantic selectors: getByRole / getByLabel / getByText. NEVER emit`,
84
129
  ` XPath or CSS-id selectors. NEVER use waitForTimeout (Playwright`,
85
130
  ` auto-waits).`,
@@ -104,6 +149,16 @@ export function buildOptimizePrompt(draft, sidecar, seeds = []) {
104
149
  ``,
105
150
  `=== CAPTURED STEPS ===`,
106
151
  stepsJson,
152
+ ...(suite.conventions
153
+ ? [``, `=== PROJECT CONVENTIONS (follow these) ===`, suite.conventions]
154
+ : []),
155
+ ...(suite.pages.length > 0
156
+ ? [
157
+ ``,
158
+ `=== REUSABLE PAGE OBJECTS (prefer calling these over raw locators) ===`,
159
+ ...suite.pages.map(p => `// ${p.name}\n${p.source}`),
160
+ ]
161
+ : []),
107
162
  ...(seeds.length > 0
108
163
  ? [
109
164
  ``,
@@ -161,28 +216,3 @@ function hasSyntaxError(code) {
161
216
  const sf = project.createSourceFile('__candidate.ts', code, { overwrite: true });
162
217
  return project.getProgram().getSyntacticDiagnostics(sf).length > 0;
163
218
  }
164
- function candidatePathFor(devRoot, slug) {
165
- return join(devRoot, '__vibe_tests__', '.hover', 'optimized', `${slug}.spec.ts.draft`);
166
- }
167
- /** Promote an optimization candidate to the real spec (overwriting it) and
168
- * remove the candidate. Returns the written spec path. The human's "Use
169
- * optimized" / `mv` action. */
170
- export async function promoteOptimized(devRoot, slug) {
171
- const candidate = candidatePathFor(devRoot, slug);
172
- const specPath = join(devRoot, '__vibe_tests__', `${slug}.spec.ts`);
173
- let code;
174
- try {
175
- code = await readFile(candidate, 'utf-8');
176
- }
177
- catch {
178
- throw new OptimizeError(`no optimization candidate to promote for "${slug}"`);
179
- }
180
- await writeFile(specPath, code, 'utf-8');
181
- await rm(candidate, { force: true });
182
- return specPath;
183
- }
184
- /** Discard an optimization candidate (delete the .draft, leave the spec). The
185
- * human's "Keep original". */
186
- export async function discardOptimized(devRoot, slug) {
187
- await rm(candidatePathFor(devRoot, slug), { force: true });
188
- }
@@ -15,6 +15,8 @@ export interface PageObjectManifest {
15
15
  pages: PageObjectEntry[];
16
16
  }
17
17
  export declare function writePageObjectManifest(devRoot: string, pages: PageObjectEntry[]): Promise<string>;
18
- /** Read the manifest, or null when none exists (no extraction has run). */
18
+ /** Read the manifest, or null when none exists (no extraction has run).
19
+ * Falls back to the legacy `__vibe_tests__/.hover/` home for manifests
20
+ * written before the `.hover/sidecars/` relocation. */
19
21
  export declare function readPageObjectManifest(devRoot: string): Promise<PageObjectManifest | null>;
20
22
  //# sourceMappingURL=pageObjectManifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pageObjectManifest.d.ts","sourceRoot":"","sources":["../../src/specs/pageObjectManifest.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAMD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,eAAe,EAAE,GACvB,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED,2EAA2E;AAC3E,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAQhG"}
1
+ {"version":3,"file":"pageObjectManifest.d.ts","sourceRoot":"","sources":["../../src/specs/pageObjectManifest.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAMD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,eAAe,EAAE,GACvB,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;wDAEwD;AACxD,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAUhG"}
@@ -1,19 +1,20 @@
1
1
  /**
2
- * Page Object manifest — the link between extraction (Stage 3b) and
3
- * consumption (Stage 3c).
2
+ * Page Object manifest — `.hover/page-objects.json` describing each emitted
3
+ * Page Object (class/method/fixture names + the signature prefix it replays).
4
+ * writeSpec reads it (`readPageObjectManifest`) to decide whether a freshly-saved
5
+ * spec's prefix matches an existing Page Object — if so it consumes
6
+ * `await loginPage.login(…)` and imports from `./fixtures` instead of re-emitting
7
+ * the steps inline.
4
8
  *
5
- * extractPageObjects writes `.hover/page-objects.json` describing each emitted
6
- * Page Object: its class/method/fixture names and the signature prefix it
7
- * replays. writeSpec reads it to decide whether a freshly-saved spec's prefix
8
- * matches a Page Object if so it consumes `await loginPage.login(…)` and
9
- * imports from `./fixtures` instead of re-emitting the steps inline.
10
- *
11
- * Kept separate from extractPageObjects so writeSpec can read the manifest
12
- * without importing the detection/generation chain.
9
+ * NOTE: cross-spec reuse is currently DORMANT — the manifest writer
10
+ * (`writePageObjectManifest`) has no caller since the standalone extraction stage
11
+ * was removed, so the read path returns null and writeSpec emits Page Objects
12
+ * inline. Kept as the seam for when cross-spec Page Object reuse is wired into
13
+ * the MCP-first flow.
13
14
  */
14
15
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
15
16
  import { join } from 'node:path';
16
- import { sidecarDir } from './sidecar.js';
17
+ import { sidecarDir, legacySidecarDir } from './sidecar.js';
17
18
  export const MANIFEST_VERSION = 1;
18
19
  function manifestPath(devRoot) {
19
20
  return join(sidecarDir(devRoot), 'page-objects.json');
@@ -26,15 +27,19 @@ export async function writePageObjectManifest(devRoot, pages) {
26
27
  await writeFile(path, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
27
28
  return path;
28
29
  }
29
- /** Read the manifest, or null when none exists (no extraction has run). */
30
+ /** Read the manifest, or null when none exists (no extraction has run).
31
+ * Falls back to the legacy `__vibe_tests__/.hover/` home for manifests
32
+ * written before the `.hover/sidecars/` relocation. */
30
33
  export async function readPageObjectManifest(devRoot) {
31
- try {
32
- const m = JSON.parse(await readFile(manifestPath(devRoot), 'utf-8'));
33
- if (Array.isArray(m.pages))
34
- return m;
35
- }
36
- catch {
37
- /* no manifest / malformed — treat as none */
34
+ for (const path of [manifestPath(devRoot), join(legacySidecarDir(devRoot), 'page-objects.json')]) {
35
+ try {
36
+ const m = JSON.parse(await readFile(path, 'utf-8'));
37
+ if (Array.isArray(m.pages))
38
+ return m;
39
+ }
40
+ catch {
41
+ /* no manifest / malformed — try next */
42
+ }
38
43
  }
39
44
  return null;
40
45
  }
@@ -0,0 +1,45 @@
1
+ import { type Locator, type Page } from 'playwright-core';
2
+ export interface GroundedTarget {
3
+ role?: string;
4
+ name?: string;
5
+ text?: string;
6
+ testId?: string;
7
+ within?: {
8
+ role?: string;
9
+ name?: string;
10
+ };
11
+ }
12
+ export interface ReplayStep {
13
+ kind?: string;
14
+ tool?: string;
15
+ input?: unknown;
16
+ }
17
+ export interface ReplayFailure {
18
+ index: number;
19
+ tool: string;
20
+ target: string;
21
+ error: string;
22
+ }
23
+ export interface ReplayResult {
24
+ ok: boolean;
25
+ /** Grounded actions that ran successfully. */
26
+ ran: number;
27
+ /** Total grounded actions in the flow. */
28
+ total: number;
29
+ failures: ReplayFailure[];
30
+ }
31
+ export declare function groundedLocate(page: Page, g: GroundedTarget): Locator | null;
32
+ /** Replay ONE grounded step on a page. Returns 'skipped' for non-actuation
33
+ * steps; throws on a failed locate / action (the caller records it). */
34
+ export declare function applyGroundedStep(page: Page, step: ReplayStep): Promise<'ok' | 'skipped'>;
35
+ /** Replay a flow's grounded steps on a given page (injected, so it's testable
36
+ * without a real browser). Navigates to `devUrl` first for a consistent start,
37
+ * then runs each grounded action, stopping at the first failure. */
38
+ export declare function replayOnPage(page: Page, devUrl: string, steps: ReplayStep[]): Promise<ReplayResult>;
39
+ /** Connect to the debug Chrome over CDP and replay a flow's grounded steps. */
40
+ export declare function replayGroundedSteps(opts: {
41
+ cdpUrl: string;
42
+ devUrl: string;
43
+ steps: ReplayStep[];
44
+ }): Promise<ReplayResult>;
45
+ //# sourceMappingURL=replayGrounded.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replayGrounded.d.ts","sourceRoot":"","sources":["../../src/specs/replayGrounded.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAiBlF,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAGD,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AACD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AA+BD,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,cAAc,GAAG,OAAO,GAAG,IAAI,CAS5E;AAED;yEACyE;AACzE,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CA4B/F;AAED;;qEAEqE;AACrE,wBAAsB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAqBzG;AAuBD,+EAA+E;AAC/E,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAoB9H"}
@@ -0,0 +1,155 @@
1
+ import { chromium } from 'playwright-core';
2
+ const ACTUATION_TOOLS = new Set(['click_control', 'fill_control', 'select_control', 'check_control', 'assert_visible']);
3
+ const ACTION_TIMEOUT = 8000;
4
+ function bare(tool) {
5
+ if (!tool)
6
+ return '';
7
+ const p = tool.split('__');
8
+ return p[0] === 'mcp' && p.length >= 3 ? p.slice(2).join('__') : tool;
9
+ }
10
+ function errLine(e) {
11
+ return e instanceof Error ? e.message.split('\n')[0] : String(e);
12
+ }
13
+ function originOf(u) {
14
+ try {
15
+ return new URL(u).origin;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ function describe(g) {
22
+ if (g.role && g.name)
23
+ return `${g.role} "${g.name}"`;
24
+ if (g.testId)
25
+ return `testId "${g.testId}"`;
26
+ if (g.text)
27
+ return `text "${g.text}"`;
28
+ return '(no target)';
29
+ }
30
+ // Mirrors mcp/actuateServer.ts `locate` — role+name → testId → text, optionally
31
+ // scoped to a `within` container. See file header. Exported so non-extension
32
+ // consumers (the replayer here, the standalone `hover-mcp`) share ONE grounded
33
+ // resolver without touching the load-bearing actuateServer.
34
+ export function groundedLocate(page, g) {
35
+ const base = g.within?.role && g.within?.name
36
+ ? page.getByRole(g.within.role, { name: g.within.name, exact: true })
37
+ : page;
38
+ if (g.role && g.name)
39
+ return base.getByRole(g.role, { name: g.name, exact: true });
40
+ if (g.testId)
41
+ return base.getByTestId(g.testId);
42
+ if (g.text)
43
+ return base.getByText(g.text).first();
44
+ return null;
45
+ }
46
+ /** Replay ONE grounded step on a page. Returns 'skipped' for non-actuation
47
+ * steps; throws on a failed locate / action (the caller records it). */
48
+ export async function applyGroundedStep(page, step) {
49
+ const tool = bare(step.tool);
50
+ if (!ACTUATION_TOOLS.has(tool))
51
+ return 'skipped';
52
+ const input = (step.input ?? {});
53
+ const loc = groundedLocate(page, input);
54
+ if (!loc)
55
+ throw new Error(`could not locate ${describe(input)}`);
56
+ switch (tool) {
57
+ case 'click_control':
58
+ await loc.click({ timeout: ACTION_TIMEOUT });
59
+ return 'ok';
60
+ case 'fill_control':
61
+ await loc.fill(String(input.value ?? ''), { timeout: ACTION_TIMEOUT });
62
+ return 'ok';
63
+ case 'select_control':
64
+ await loc.selectOption(String(input.value ?? ''), { timeout: ACTION_TIMEOUT });
65
+ return 'ok';
66
+ case 'check_control':
67
+ if (input.checked === false)
68
+ await loc.uncheck({ timeout: ACTION_TIMEOUT });
69
+ else
70
+ await loc.check({ timeout: ACTION_TIMEOUT });
71
+ return 'ok';
72
+ case 'assert_visible': {
73
+ const visible = await loc.first().isVisible();
74
+ if (!visible)
75
+ throw new Error(`${describe(input)} not visible`);
76
+ return 'ok';
77
+ }
78
+ default:
79
+ return 'skipped';
80
+ }
81
+ }
82
+ /** Replay a flow's grounded steps on a given page (injected, so it's testable
83
+ * without a real browser). Navigates to `devUrl` first for a consistent start,
84
+ * then runs each grounded action, stopping at the first failure. */
85
+ export async function replayOnPage(page, devUrl, steps) {
86
+ const failures = [];
87
+ let ran = 0;
88
+ const total = steps.filter((s) => ACTUATION_TOOLS.has(bare(s.tool))).length;
89
+ try {
90
+ await page.goto(devUrl, { waitUntil: 'domcontentloaded', timeout: 15000 });
91
+ }
92
+ catch {
93
+ // SPA / already on the origin — replay anyway.
94
+ }
95
+ for (let i = 0; i < steps.length; i++) {
96
+ if (!ACTUATION_TOOLS.has(bare(steps[i].tool)))
97
+ continue;
98
+ try {
99
+ if ((await applyGroundedStep(page, steps[i])) === 'ok')
100
+ ran++;
101
+ }
102
+ catch (e) {
103
+ failures.push({ index: i, tool: bare(steps[i].tool), target: describe((steps[i].input ?? {})), error: errLine(e) });
104
+ break; // a broken step breaks the flow — stop here
105
+ }
106
+ }
107
+ return { ok: failures.length === 0, ran, total, failures };
108
+ }
109
+ /** Pick the page on the dev origin, else the foreground page. */
110
+ async function pickPage(browser, devUrl) {
111
+ const wantOrigin = originOf(devUrl);
112
+ const pages = browser.contexts().flatMap((c) => c.pages());
113
+ if (pages.length === 0)
114
+ return null;
115
+ const matches = wantOrigin ? pages.filter((p) => originOf(p.url()) === wantOrigin) : [];
116
+ const candidates = matches.length ? matches : pages;
117
+ let chosen = candidates[candidates.length - 1];
118
+ for (const p of candidates) {
119
+ try {
120
+ if (await p.evaluate(() => document.visibilityState === 'visible')) {
121
+ chosen = p;
122
+ break;
123
+ }
124
+ }
125
+ catch {
126
+ /* busy/closed */
127
+ }
128
+ }
129
+ return chosen;
130
+ }
131
+ /** Connect to the debug Chrome over CDP and replay a flow's grounded steps. */
132
+ export async function replayGroundedSteps(opts) {
133
+ let browser;
134
+ try {
135
+ browser = await chromium.connectOverCDP(opts.cdpUrl, { timeout: 5000 });
136
+ }
137
+ catch (e) {
138
+ return { ok: false, ran: 0, total: 0, failures: [{ index: -1, tool: 'connect', target: opts.cdpUrl, error: errLine(e) }] };
139
+ }
140
+ try {
141
+ const page = await pickPage(browser, opts.devUrl);
142
+ if (!page) {
143
+ return { ok: false, ran: 0, total: 0, failures: [{ index: -1, tool: 'page', target: opts.devUrl, error: 'no page on the dev origin' }] };
144
+ }
145
+ return await replayOnPage(page, opts.devUrl, opts.steps);
146
+ }
147
+ finally {
148
+ try {
149
+ await browser.close();
150
+ }
151
+ catch {
152
+ /* disconnect only — never kill the user's debug Chrome */
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Self-heal Stage 1 — the failure → heal-hint bridge.
3
+ *
4
+ * Playwright's JSON reporter records pass/fail but not *which locator* failed.
5
+ * Self-healing needs that to know what to re-locate. The locator IS in the error
6
+ * message (e.g. `waiting for getByRole('button', { name: 'Submit' })`), so this
7
+ * module parses the run JSON's failed tests and pulls out, per failure, the
8
+ * `getBy…` expression + the action that failed — the hint the heal session
9
+ * (Stage 2) drives the agent with.
10
+ *
11
+ * Pure + total: bad/partial JSON yields []. No FS, no agent — just parsing.
12
+ */
13
+ export interface RunFailure {
14
+ /** Spec file as Playwright reports it (path or basename). */
15
+ specFile: string;
16
+ /** Test title. */
17
+ title: string;
18
+ /** First line of the error message (the human-readable failure). */
19
+ error: string;
20
+ /** The `getBy…` locator expression parsed from the error — the thing to
21
+ * re-locate — or undefined if the failure wasn't a locator miss. */
22
+ failingLocator?: string;
23
+ /** The Playwright action that failed: 'click' / 'fill' / 'assert' / … */
24
+ failingAction?: string;
25
+ }
26
+ /** The `getBy…` expression in an error message, if any. */
27
+ export declare function extractLocator(message: string): string | undefined;
28
+ /** The failing action (click / fill / assert / …) inferred from the message. */
29
+ export declare function extractAction(message: string): string | undefined;
30
+ /** Parse a Playwright JSON-reporter run into its failures (with the failing
31
+ * locator + action pulled from each error). Accepts the parsed object or a
32
+ * JSON string; anything malformed yields []. */
33
+ export declare function parseRunFailures(json: unknown): RunFailure[];
34
+ //# sourceMappingURL=runFailures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runFailures.d.ts","sourceRoot":"","sources":["../../src/specs/runFailures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd;yEACqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAgBD,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGlE;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOjE;AAoCD;;iDAEiD;AACjD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,EAAE,CAY5D"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Self-heal Stage 1 — the failure → heal-hint bridge.
3
+ *
4
+ * Playwright's JSON reporter records pass/fail but not *which locator* failed.
5
+ * Self-healing needs that to know what to re-locate. The locator IS in the error
6
+ * message (e.g. `waiting for getByRole('button', { name: 'Submit' })`), so this
7
+ * module parses the run JSON's failed tests and pulls out, per failure, the
8
+ * `getBy…` expression + the action that failed — the hint the heal session
9
+ * (Stage 2) drives the agent with.
10
+ *
11
+ * Pure + total: bad/partial JSON yields []. No FS, no agent — just parsing.
12
+ */
13
+ /** A `getBy…(...)` call with an optional `.first()/.last()/.nth()` tail. The
14
+ * inner `(?:[^()]|\([^()]*\))*` tolerates one level of nested parens so
15
+ * `getByRole('button', { name: 'x' })` matches whole. */
16
+ const LOCATOR_RE = /getBy\w+\((?:[^()]|\([^()]*\))*\)(?:\.(?:first|last|nth)\([^)]*\))?/;
17
+ /** A KNOWN Playwright interaction at the head of an error (`locator.click: …`),
18
+ * restricted to real actions so a generic `Error:` / `Some:` prefix is NOT
19
+ * mistaken for one. */
20
+ const ACTION_RE = /^(?:locator\.)?(click|dblclick|fill|type|press|check|uncheck|selectOption|setInputFiles|hover|tap|focus|clear)\b/;
21
+ const ASSERT_RE = /\b(?:toBeVisible|toHaveText|toContainText|toHaveCount|toHaveValue|toBeChecked|toBeEnabled)\b|^expect\(/;
22
+ function firstLine(s) {
23
+ return (s.split('\n').find(l => l.trim()) ?? '').trim();
24
+ }
25
+ /** The `getBy…` expression in an error message, if any. */
26
+ export function extractLocator(message) {
27
+ const m = message.match(LOCATOR_RE);
28
+ return m ? m[0] : undefined;
29
+ }
30
+ /** The failing action (click / fill / assert / …) inferred from the message. */
31
+ export function extractAction(message) {
32
+ const head = firstLine(message);
33
+ // Assert first — an `Error: expect(...)` line leads with "Error:", which a
34
+ // generic action match would wrongly read as the action.
35
+ if (ASSERT_RE.test(head) || ASSERT_RE.test(message))
36
+ return 'assert';
37
+ const m = head.match(ACTION_RE);
38
+ return m ? m[1].toLowerCase() : undefined;
39
+ }
40
+ /** The error message of the first failed/timed-out result on a spec, if any. */
41
+ function failedMessage(spec) {
42
+ for (const t of spec.tests ?? []) {
43
+ for (const r of t.results ?? []) {
44
+ if (r.status === 'failed' || r.status === 'timedOut' || r.status === 'interrupted') {
45
+ return r.error?.message || r.errors?.find(e => e.message)?.message || 'Test failed (no error message).';
46
+ }
47
+ }
48
+ }
49
+ return undefined;
50
+ }
51
+ function walk(suite, fileFallback, out) {
52
+ const file = suite.file || fileFallback;
53
+ for (const spec of suite.specs ?? []) {
54
+ if (spec.ok === true)
55
+ continue;
56
+ const message = failedMessage(spec);
57
+ if (!message)
58
+ continue; // not a failure (ok may be undefined but no failed result)
59
+ out.push({
60
+ specFile: spec.file || file,
61
+ title: spec.title || '(untitled)',
62
+ error: firstLine(message),
63
+ failingLocator: extractLocator(message),
64
+ failingAction: extractAction(message),
65
+ });
66
+ }
67
+ for (const child of suite.suites ?? [])
68
+ walk(child, file, out);
69
+ }
70
+ /** Parse a Playwright JSON-reporter run into its failures (with the failing
71
+ * locator + action pulled from each error). Accepts the parsed object or a
72
+ * JSON string; anything malformed yields []. */
73
+ export function parseRunFailures(json) {
74
+ let root;
75
+ if (typeof json === 'string') {
76
+ try {
77
+ root = JSON.parse(json);
78
+ }
79
+ catch {
80
+ return [];
81
+ }
82
+ }
83
+ else if (json && typeof json === 'object') {
84
+ root = json;
85
+ }
86
+ else {
87
+ return [];
88
+ }
89
+ const out = [];
90
+ for (const suite of root.suites ?? [])
91
+ walk(suite, suite.file || '', out);
92
+ return out;
93
+ }
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Translation seeds: human-written worked examples that teach the optimization
3
+ * pass (F7) a multi-step Playwright pattern by few-shot — NOT by deterministic
4
+ * match+template. A seed is a rough `signature` (tool names, used only to pick
5
+ * relevant seeds) + a concrete `example` (input steps → output code) the LLM
6
+ * generalizes from.
7
+ *
8
+ * These ship inlined as the `BUILTIN_SEEDS` constant below. They used to be
9
+ * JSON files under `packages/core/seeds/optimization/` plus a `.hover/rules/`
10
+ * "author your own seed" mechanism and a `.hover/seeds.json` opt-out — all
11
+ * removed: that user-facing surface added burden for a small curated catalogue
12
+ * that feeds an optional, manually-invoked pass. To add a pattern, append a
13
+ * `SeedRule` here.
14
+ */
1
15
  export interface SeedRule {
2
16
  /** Identifier, e.g. `download`. */
3
17
  name: string;
@@ -13,23 +27,10 @@ export interface SeedRule {
13
27
  };
14
28
  }
15
29
  /**
16
- * Built-in seeds ship with core and feed EVERY project's optimization pass, so
17
- * the bar is high: a pattern qualifies as built-in ONLY if it's a *highly
18
- * certain* optimization — a fixed, app-agnostic translation whose output is
19
- * deterministic and can't mislead (e.g. download → waitForEvent pairing).
20
- *
21
- * Deliberately NOT built-in:
22
- * - Semantic / judgement-based optimizations (e.g. WHICH feedback text to
23
- * assert) — those are already standing instructions in buildOptimizePrompt,
24
- * and a bad generalization would pollute every user's spec.
25
- * - Popup/new-tab — hardcoded in the translator (writeSpec), not a seed.
26
- * Project-specific or speculative patterns live in <root>/.hover/rules/, where
27
- * the bar is the user's own call.
30
+ * Built-in optimization seeds, inlined. They feed EVERY project's optimization
31
+ * pass (the prompt builder and the relevance filter consume this directly).
28
32
  */
29
33
  export declare const BUILTIN_SEEDS: SeedRule[];
30
- /** Built-in seeds + any in <projectRoot>/.hover/rules/*.json. Malformed files
31
- * are skipped rather than failing the whole read. */
32
- export declare function readSeeds(projectRoot: string): Promise<SeedRule[]>;
33
34
  /** Pick seeds whose signature's base tool appears in the spec — a cheap
34
35
  * relevance filter so the prompt only carries plausibly-applicable examples. */
35
36
  export declare function relevantSeeds(seeds: SeedRule[], specTools: Set<string>, cap?: number): SeedRule[];
@@ -1 +1 @@
1
- {"version":3,"file":"seeds.d.ts","sourceRoot":"","sources":["../../src/specs/seeds.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,QAAQ;IACvB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb;qEACiE;IACjE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,OAAO,EAAE;QAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,EAenC,CAAC;AAEF;sDACsD;AACtD,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAiBxE;AAED;iFACiF;AACjF,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,SAAI,GAAG,QAAQ,EAAE,CAG5F"}
1
+ {"version":3,"file":"seeds.d.ts","sourceRoot":"","sources":["../../src/specs/seeds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,QAAQ;IACvB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb;qEACiE;IACjE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,OAAO,EAAE;QAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,EAiFnC,CAAC;AAEF;iFACiF;AACjF,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,SAAI,GAAG,QAAQ,EAAE,CAG5F"}