@hover-dev/core 0.17.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 (106) hide show
  1. package/dist/engine.d.ts +14 -39
  2. package/dist/engine.d.ts.map +1 -1
  3. package/dist/engine.js +16 -67
  4. package/dist/specs/pageObjectManifest.d.ts.map +1 -1
  5. package/dist/specs/pageObjectManifest.js +11 -10
  6. package/dist/specs/replayGrounded.d.ts.map +1 -1
  7. package/package.json +5 -22
  8. package/dist/agents/argv.d.ts +0 -11
  9. package/dist/agents/argv.d.ts.map +0 -1
  10. package/dist/agents/argv.js +0 -23
  11. package/dist/agents/claude.d.ts +0 -3
  12. package/dist/agents/claude.d.ts.map +0 -1
  13. package/dist/agents/claude.js +0 -220
  14. package/dist/agents/codex.d.ts +0 -19
  15. package/dist/agents/codex.d.ts.map +0 -1
  16. package/dist/agents/codex.js +0 -231
  17. package/dist/agents/detect.d.ts +0 -46
  18. package/dist/agents/detect.d.ts.map +0 -1
  19. package/dist/agents/detect.js +0 -80
  20. package/dist/agents/gemini.d.ts +0 -17
  21. package/dist/agents/gemini.d.ts.map +0 -1
  22. package/dist/agents/gemini.js +0 -186
  23. package/dist/agents/index.d.ts +0 -6
  24. package/dist/agents/index.d.ts.map +0 -1
  25. package/dist/agents/index.js +0 -5
  26. package/dist/agents/invoke.d.ts +0 -12
  27. package/dist/agents/invoke.d.ts.map +0 -1
  28. package/dist/agents/invoke.js +0 -93
  29. package/dist/agents/qwen.d.ts +0 -17
  30. package/dist/agents/qwen.d.ts.map +0 -1
  31. package/dist/agents/qwen.js +0 -172
  32. package/dist/agents/registry.d.ts +0 -19
  33. package/dist/agents/registry.d.ts.map +0 -1
  34. package/dist/agents/registry.js +0 -30
  35. package/dist/agents/shared.d.ts +0 -28
  36. package/dist/agents/shared.d.ts.map +0 -1
  37. package/dist/agents/shared.js +0 -35
  38. package/dist/agents/types.d.ts +0 -194
  39. package/dist/agents/types.d.ts.map +0 -1
  40. package/dist/agents/types.js +0 -23
  41. package/dist/index.d.ts +0 -3
  42. package/dist/index.d.ts.map +0 -1
  43. package/dist/index.js +0 -2
  44. package/dist/mcp/actuateServer.d.ts +0 -3
  45. package/dist/mcp/actuateServer.d.ts.map +0 -1
  46. package/dist/mcp/actuateServer.js +0 -594
  47. package/dist/mcp/sourceFence.d.ts +0 -23
  48. package/dist/mcp/sourceFence.d.ts.map +0 -1
  49. package/dist/mcp/sourceFence.js +0 -79
  50. package/dist/mcp/sourceServer.d.ts +0 -3
  51. package/dist/mcp/sourceServer.d.ts.map +0 -1
  52. package/dist/mcp/sourceServer.js +0 -191
  53. package/dist/modes.d.ts +0 -39
  54. package/dist/modes.d.ts.map +0 -1
  55. package/dist/modes.js +0 -34
  56. package/dist/playwright/cdpStatus.d.ts +0 -14
  57. package/dist/playwright/cdpStatus.d.ts.map +0 -1
  58. package/dist/playwright/cdpStatus.js +0 -52
  59. package/dist/playwright/preflight.d.ts +0 -31
  60. package/dist/playwright/preflight.d.ts.map +0 -1
  61. package/dist/playwright/preflight.js +0 -82
  62. package/dist/playwright/preflightCache.d.ts +0 -27
  63. package/dist/playwright/preflightCache.d.ts.map +0 -1
  64. package/dist/playwright/preflightCache.js +0 -21
  65. package/dist/playwright/resolveMcpConfig.d.ts +0 -61
  66. package/dist/playwright/resolveMcpConfig.d.ts.map +0 -1
  67. package/dist/playwright/resolveMcpConfig.js +0 -84
  68. package/dist/plugin-api.d.ts +0 -237
  69. package/dist/plugin-api.d.ts.map +0 -1
  70. package/dist/plugin-api.js +0 -52
  71. package/dist/qa/classify.d.ts +0 -38
  72. package/dist/qa/classify.d.ts.map +0 -1
  73. package/dist/qa/classify.js +0 -138
  74. package/dist/runSession.d.ts +0 -53
  75. package/dist/runSession.d.ts.map +0 -1
  76. package/dist/runSession.js +0 -96
  77. package/dist/service/cdpHandlers.d.ts +0 -24
  78. package/dist/service/cdpHandlers.d.ts.map +0 -1
  79. package/dist/service/cdpHandlers.js +0 -50
  80. package/dist/service/cdpHint.d.ts +0 -41
  81. package/dist/service/cdpHint.d.ts.map +0 -1
  82. package/dist/service/cdpHint.js +0 -158
  83. package/dist/service/conventions.d.ts +0 -8
  84. package/dist/service/conventions.d.ts.map +0 -1
  85. package/dist/service/conventions.js +0 -42
  86. package/dist/service/relayHandlers.d.ts +0 -28
  87. package/dist/service/relayHandlers.d.ts.map +0 -1
  88. package/dist/service/relayHandlers.js +0 -105
  89. package/dist/service/saveHandlers.d.ts +0 -50
  90. package/dist/service/saveHandlers.d.ts.map +0 -1
  91. package/dist/service/saveHandlers.js +0 -77
  92. package/dist/service/types.d.ts +0 -158
  93. package/dist/service/types.d.ts.map +0 -1
  94. package/dist/service/types.js +0 -26
  95. package/dist/service.d.ts +0 -54
  96. package/dist/service.d.ts.map +0 -1
  97. package/dist/service.js +0 -1772
  98. package/dist/specs/businessMap.d.ts +0 -29
  99. package/dist/specs/businessMap.d.ts.map +0 -1
  100. package/dist/specs/businessMap.js +0 -95
  101. package/dist/specs/extractPageObjects.d.ts +0 -18
  102. package/dist/specs/extractPageObjects.d.ts.map +0 -1
  103. package/dist/specs/extractPageObjects.js +0 -98
  104. package/dist/specs/optimizeSpecWithAgent.d.ts +0 -9
  105. package/dist/specs/optimizeSpecWithAgent.d.ts.map +0 -1
  106. package/dist/specs/optimizeSpecWithAgent.js +0 -39
@@ -1,29 +0,0 @@
1
- export type MapNodeKind = 'app' | 'area' | 'line' | 'spec';
2
- export type CoverageStatus = 'covered' | 'uncovered';
3
- export interface MapNode {
4
- id: string;
5
- label: string;
6
- kind: MapNodeKind;
7
- /** Coverage of a business line (only on `line` nodes). */
8
- status?: CoverageStatus;
9
- /** Entry route of a business line, if given. */
10
- route?: string;
11
- /** Spec filename a line is covered by (on `line` and `spec` nodes). */
12
- spec?: string;
13
- }
14
- export interface MapEdge {
15
- source: string;
16
- target: string;
17
- }
18
- export interface BusinessMapGraph {
19
- app: string;
20
- nodes: MapNode[];
21
- edges: MapEdge[];
22
- stats: {
23
- lines: number;
24
- covered: number;
25
- areas: number;
26
- };
27
- }
28
- export declare function parseBusinessMap(md: string, fallbackApp?: string): BusinessMapGraph;
29
- //# sourceMappingURL=businessMap.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"businessMap.d.ts","sourceRoot":"","sources":["../../src/specs/businessMap.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAC3D,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,WAAW,CAAC;AAErD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;IAClB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1D;AA8BD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,SAAQ,GAAG,gBAAgB,CAwDlF"}
@@ -1,95 +0,0 @@
1
- /*
2
- * Business-map parser: turn the `.hover/hover-map.md` wiki the agent maintains
3
- * into a graph model the cockpit renders (areas → business lines → specs, with
4
- * coverage). The map is a human-curated markdown checklist:
5
- *
6
- * # Business map — myapp
7
- * ## Auth
8
- * - [ ] Log in — /login
9
- * - [x] Checkout — /checkout — checkout.spec.ts
10
- *
11
- * Pure + total: malformed lines are skipped, never thrown. The graph is
12
- * hierarchical (app → area → line → spec); richer relationship edges
13
- * (depends-on / shares-state / navigates-to) are a later format extension.
14
- */
15
- function slug(s) {
16
- return (s
17
- .toLowerCase()
18
- .replace(/[^a-z0-9]+/g, '-')
19
- .replace(/^-+|-+$/g, '') || 'x');
20
- }
21
- const SPEC_RE = /\.spec\.tsx?$/;
22
- /** Split a business-line item on " — " / " – " / " - " (em/en/hyphen, spaced). */
23
- function splitItem(rest) {
24
- const parts = rest
25
- .split(/\s+[—–-]\s+/)
26
- .map((p) => p.trim())
27
- .filter(Boolean);
28
- const name = parts.shift() ?? rest.trim();
29
- let route;
30
- let spec;
31
- for (const p of parts) {
32
- if (SPEC_RE.test(p))
33
- spec = p;
34
- else if (p.startsWith('/'))
35
- route = p;
36
- else if (!spec && SPEC_RE.test(p))
37
- spec = p;
38
- }
39
- return { name, route, spec };
40
- }
41
- export function parseBusinessMap(md, fallbackApp = 'app') {
42
- const nodes = [];
43
- const edges = [];
44
- const seen = new Set();
45
- const add = (n) => {
46
- if (seen.has(n.id))
47
- return;
48
- seen.add(n.id);
49
- nodes.push(n);
50
- };
51
- let app = fallbackApp;
52
- // Title: `# Business map — <app>` (or any `# <title>`).
53
- const title = md.match(/^#\s+(.+)$/m);
54
- if (title) {
55
- const t = title[1].trim();
56
- const m = t.match(/business\s*map\s*[—–-]\s*(.+)$/i);
57
- app = (m ? m[1] : t).trim() || fallbackApp;
58
- }
59
- add({ id: 'app', label: app, kind: 'app' });
60
- let area = null;
61
- let covered = 0;
62
- let lineCount = 0;
63
- let areaCount = 0;
64
- for (const raw of md.split('\n')) {
65
- const line = raw.trimEnd();
66
- const areaM = line.match(/^##\s+(.+)$/);
67
- if (areaM) {
68
- const label = areaM[1].trim();
69
- const id = `area:${slug(label)}`;
70
- area = { id };
71
- add({ id, label, kind: 'area' });
72
- edges.push({ source: 'app', target: id });
73
- areaCount++;
74
- continue;
75
- }
76
- const itemM = line.match(/^\s*-\s*\[([ xX])\]\s+(.+)$/);
77
- if (itemM) {
78
- const status = itemM[1].toLowerCase() === 'x' ? 'covered' : 'uncovered';
79
- const { name, route, spec } = splitItem(itemM[2]);
80
- const parentId = area?.id ?? 'app';
81
- const lineId = `line:${slug(area ? area.id.slice(5) : 'top')}/${slug(name)}`;
82
- add({ id: lineId, label: name, kind: 'line', status, route, spec });
83
- edges.push({ source: parentId, target: lineId });
84
- lineCount++;
85
- if (status === 'covered')
86
- covered++;
87
- if (spec) {
88
- const specId = `spec:${spec}`;
89
- add({ id: specId, label: spec, kind: 'spec', spec });
90
- edges.push({ source: lineId, target: specId });
91
- }
92
- }
93
- }
94
- return { app, nodes, edges, stats: { lines: lineCount, covered, areas: areaCount } };
95
- }
@@ -1,18 +0,0 @@
1
- export interface ExtractedPage {
2
- className: string;
3
- methodName: string;
4
- fileName: string;
5
- /** Absolute path written. */
6
- path: string;
7
- /** Slugs of the specs that share this flow. */
8
- specs: string[];
9
- }
10
- export interface ExtractResult {
11
- pages: ExtractedPage[];
12
- /** Absolute path of the written fixtures.ts, or null when nothing extracted. */
13
- fixturesPath: string | null;
14
- }
15
- export declare function extractPageObjects(devRoot: string, opts?: {
16
- minSpecs?: number;
17
- }): Promise<ExtractResult>;
18
- //# sourceMappingURL=extractPageObjects.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extractPageObjects.d.ts","sourceRoot":"","sources":["../../src/specs/extractPageObjects.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,gFAAgF;IAChF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,aAAa,CAAC,CA4CxB"}
@@ -1,98 +0,0 @@
1
- /**
2
- * Stage 3 (F4): extract Page Objects + a fixtures entry point from flows shared
3
- * across saved specs.
4
- *
5
- * Reads detectSharedFlows (>= 3 specs sharing an entry prefix — the scaffold's
6
- * 3-use threshold), generates a `pages/<Name>.ts` per flow, and (re)writes a
7
- * single `fixtures.ts` that registers each Page Object via `base.extend`. New
8
- * specs `import { test, expect } from './fixtures'` and consume e.g.
9
- * `async ({ page, loginPage }) => …`.
10
- *
11
- * Manual trigger (Stage 3b): invoked by a CLI command, not on every save, and
12
- * it never rewrites already-committed specs (D6) — it only emits the shared
13
- * pages/ + fixtures.ts going forward.
14
- */
15
- import { mkdir, writeFile } from 'node:fs/promises';
16
- import { join } from 'node:path';
17
- import { detectSharedFlows } from './detectSharedFlows.js';
18
- import { generatePageObject } from './generatePageObject.js';
19
- import { writePageObjectManifest } from './pageObjectManifest.js';
20
- export async function extractPageObjects(devRoot, opts = {}) {
21
- // 3-use threshold for extraction; lower thresholds only *report* (Stage 2).
22
- const flows = await detectSharedFlows(devRoot, { minSpecs: opts.minSpecs ?? 3 });
23
- if (flows.length === 0)
24
- return { pages: [], fixturesPath: null };
25
- const testsDir = join(devRoot, '__vibe_tests__');
26
- const pagesDir = join(testsDir, 'pages');
27
- await mkdir(pagesDir, { recursive: true });
28
- const pages = [];
29
- const entries = [];
30
- const usedNames = new Set();
31
- for (const flow of flows) {
32
- const probe = generatePageObject(flow.prefixSteps);
33
- const className = uniqueName(probe.className, usedNames);
34
- const po = className === probe.className
35
- ? probe
36
- : generatePageObject(flow.prefixSteps, { className });
37
- const path = join(pagesDir, po.fileName);
38
- await writeFile(path, po.source, 'utf-8');
39
- pages.push({
40
- className: po.className,
41
- methodName: po.methodName,
42
- fileName: po.fileName,
43
- path,
44
- specs: flow.specs,
45
- });
46
- entries.push({
47
- className: po.className,
48
- methodName: po.methodName,
49
- fixtureName: fixtureName(po.className),
50
- fileName: po.fileName,
51
- signatures: flow.signatures,
52
- specs: flow.specs,
53
- });
54
- }
55
- const fixturesPath = join(testsDir, 'fixtures.ts');
56
- await writeFile(fixturesPath, renderFixtures(pages), 'utf-8');
57
- // Manifest lets writeSpec match a new spec's prefix to a Page Object and
58
- // consume it (Stage 3c) without re-running detection.
59
- await writePageObjectManifest(devRoot, entries);
60
- return { pages, fixturesPath };
61
- }
62
- function renderFixtures(pages) {
63
- const lines = [];
64
- lines.push(`import { test as base } from '@playwright/test';`);
65
- for (const p of pages) {
66
- lines.push(`import { ${p.className} } from './pages/${p.className}';`);
67
- }
68
- lines.push('');
69
- lines.push('/**');
70
- lines.push(' * Generated by Hover — Page Object fixtures lifted from flows shared');
71
- lines.push(" * across specs. In a new spec: `import { test, expect } from './fixtures';`");
72
- lines.push(' * then consume e.g. `async ({ page, loginPage }) => …`.');
73
- lines.push(' */');
74
- const typeMembers = pages.map(p => `${fixtureName(p.className)}: ${p.className}`).join('; ');
75
- lines.push(`export const test = base.extend<{ ${typeMembers} }>({`);
76
- for (const p of pages) {
77
- lines.push(` ${fixtureName(p.className)}: async ({ page }, use) => {`);
78
- lines.push(` await use(new ${p.className}(page));`);
79
- lines.push(` },`);
80
- }
81
- lines.push(`});`);
82
- lines.push('');
83
- lines.push(`export { expect } from '@playwright/test';`);
84
- lines.push('');
85
- return lines.join('\n');
86
- }
87
- /** Class name -> fixture key: LoginPage -> loginPage. */
88
- function fixtureName(className) {
89
- return className.charAt(0).toLowerCase() + className.slice(1);
90
- }
91
- function uniqueName(base, used) {
92
- let name = base;
93
- let n = 2;
94
- while (used.has(name))
95
- name = `${base}${n++}`;
96
- used.add(name);
97
- return name;
98
- }
@@ -1,9 +0,0 @@
1
- import { type OptimizeResult } from './optimizeSpec.js';
2
- export interface OptimizeAgentOptions {
3
- agentId: string;
4
- model?: string;
5
- maxBudgetUsd?: number;
6
- signal?: AbortSignal;
7
- }
8
- export declare function optimizeSpecWithAgent(devRoot: string, slug: string, opts: OptimizeAgentOptions): Promise<OptimizeResult>;
9
- //# sourceMappingURL=optimizeSpecWithAgent.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"optimizeSpecWithAgent.d.ts","sourceRoot":"","sources":["../../src/specs/optimizeSpecWithAgent.ts"],"names":[],"mappings":"AAUA,OAAO,EAAgB,KAAK,cAAc,EAAmB,MAAM,mBAAmB,CAAC;AAEvF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,cAAc,CAAC,CA2BzB"}
@@ -1,39 +0,0 @@
1
- /**
2
- * Wires optimizeSpec's injected codegen call to a real agent via invokeAgent,
3
- * in "codegen mode": no MCP, no browser tools, the agent's own built-in tools
4
- * disallowed — it just reads the prompt and emits the improved spec as text.
5
- *
6
- * Kept separate from optimizeSpec.ts so the core (prompt / extract / validate /
7
- * write) stays a pure, spawn-free module that tests import directly.
8
- */
9
- import { invokeAgent } from '../agents/invoke.js';
10
- import { getAgent } from '../agents/registry.js';
11
- import { optimizeSpec } from './optimizeSpec.js';
12
- export async function optimizeSpecWithAgent(devRoot, slug, opts) {
13
- const descriptor = getAgent(opts.agentId);
14
- // Codegen mode: deny the agent's built-in tools so it answers with text only;
15
- // pass no mcpConfig / allowedTools so it never reaches a browser.
16
- const disallowedTools = descriptor?.defaultDisallowedTools
17
- ? [...descriptor.defaultDisallowedTools]
18
- : undefined;
19
- const runCodegen = async (prompt) => {
20
- let streamed = '';
21
- let summary = '';
22
- for await (const ev of invokeAgent({
23
- agentId: opts.agentId,
24
- prompt,
25
- model: opts.model,
26
- maxBudgetUsd: opts.maxBudgetUsd,
27
- signal: opts.signal,
28
- disallowedTools,
29
- })) {
30
- if (ev.kind === 'text' && ev.text)
31
- streamed += `${ev.text}\n`;
32
- else if (ev.kind === 'session_end' && ev.summary)
33
- summary = ev.summary;
34
- }
35
- // Prefer the final result summary; fall back to streamed text blocks.
36
- return summary || streamed;
37
- };
38
- return optimizeSpec(devRoot, slug, runCodegen);
39
- }