forge-cc 0.1.7 → 0.1.9

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.
@@ -0,0 +1,144 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ /** Default viewports for multi-viewport capture: desktop, tablet, mobile */
4
+ export const DEFAULT_VIEWPORTS = [
5
+ { name: "desktop", width: 1280, height: 800 },
6
+ { name: "tablet", width: 768, height: 1024 },
7
+ { name: "mobile", width: 390, height: 844 },
8
+ ];
9
+ /**
10
+ * Sanitize a page path into a safe filename component.
11
+ * Replaces slashes with underscores and strips leading underscores.
12
+ * Falls back to "index" for root path.
13
+ */
14
+ function sanitizePageName(pagePath) {
15
+ const sanitized = pagePath.replace(/\//g, "_").replace(/^_/, "");
16
+ return sanitized || "index";
17
+ }
18
+ /**
19
+ * Extract DOM snapshot from the page via page.evaluate().
20
+ * The entire extraction logic runs inside the browser context so it must be
21
+ * self-contained -- no references to Node.js modules or TypeScript types.
22
+ */
23
+ async function extractDOM(page) {
24
+ return await page.evaluate(() => {
25
+ function walkElement(el, isTopLevel) {
26
+ const style = window.getComputedStyle(el);
27
+ const visible = style.display !== "none" &&
28
+ style.visibility !== "hidden" &&
29
+ style.opacity !== "0";
30
+ const node = {
31
+ tag: el.tagName.toLowerCase(),
32
+ visible,
33
+ children: [],
34
+ };
35
+ if (el.id) {
36
+ node.id = el.id;
37
+ }
38
+ if (el.className &&
39
+ typeof el.className === "string" &&
40
+ el.className.trim()) {
41
+ node.className = el.className.trim();
42
+ }
43
+ // Only capture bounding rect for top-level elements to limit payload size
44
+ if (isTopLevel) {
45
+ const rect = el.getBoundingClientRect();
46
+ node.rect = {
47
+ x: Math.round(rect.x),
48
+ y: Math.round(rect.y),
49
+ width: Math.round(rect.width),
50
+ height: Math.round(rect.height),
51
+ };
52
+ }
53
+ const childElements = el.children;
54
+ for (let i = 0; i < childElements.length; i++) {
55
+ node.children.push(walkElement(childElements[i], false));
56
+ }
57
+ return node;
58
+ }
59
+ const body = document.body;
60
+ if (!body) {
61
+ return { tag: "body", visible: true, children: [] };
62
+ }
63
+ const snapshot = {
64
+ tag: "body",
65
+ visible: true,
66
+ children: [],
67
+ };
68
+ if (body.id) {
69
+ snapshot.id = body.id;
70
+ }
71
+ if (body.className &&
72
+ typeof body.className === "string" &&
73
+ body.className.trim()) {
74
+ snapshot.className = body.className.trim();
75
+ }
76
+ const childElements = body.children;
77
+ for (let i = 0; i < childElements.length; i++) {
78
+ snapshot.children.push(walkElement(childElements[i], true));
79
+ }
80
+ return snapshot;
81
+ });
82
+ }
83
+ /**
84
+ * Capture multi-viewport screenshots and extract DOM structure from a Playwright page.
85
+ *
86
+ * For each viewport in the configuration:
87
+ * 1. Resizes the viewport via page.setViewportSize()
88
+ * 2. Waits for layout to settle (500ms)
89
+ * 3. Takes a full-page screenshot saved as {safeName}_{viewportName}.png
90
+ * 4. Extracts the serialized DOM tree via page.evaluate()
91
+ *
92
+ * @param page - Playwright Page instance (already navigated to the target URL)
93
+ * @param options - Capture configuration including page path, screenshot dir, and optional viewports
94
+ * @returns VisualCaptureResult with screenshots array, DOM snapshots per viewport, and metadata
95
+ */
96
+ export async function captureVisual(page, options) {
97
+ const start = Date.now();
98
+ const viewports = options.viewports ?? DEFAULT_VIEWPORTS;
99
+ const safeName = sanitizePageName(options.pagePath);
100
+ mkdirSync(options.screenshotDir, { recursive: true });
101
+ const screenshots = [];
102
+ const domSnapshots = {};
103
+ for (const viewport of viewports) {
104
+ try {
105
+ // Resize viewport
106
+ await page.setViewportSize({
107
+ width: viewport.width,
108
+ height: viewport.height,
109
+ });
110
+ // Wait for layout to settle after resize
111
+ await page.waitForTimeout(500);
112
+ // Take full-page screenshot
113
+ const screenshotPath = join(options.screenshotDir, `${safeName}_${viewport.name}.png`);
114
+ try {
115
+ await page.screenshot({ path: screenshotPath, fullPage: true });
116
+ screenshots.push({
117
+ page: options.pagePath,
118
+ viewport: viewport.name,
119
+ path: screenshotPath,
120
+ });
121
+ }
122
+ catch {
123
+ // Screenshot failed for this viewport — skip it but continue
124
+ }
125
+ // Extract DOM snapshot
126
+ const domSnapshot = await extractDOM(page);
127
+ domSnapshots[viewport.name] = domSnapshot;
128
+ }
129
+ catch {
130
+ // Viewport resize or other operation failed — skip this viewport entirely
131
+ }
132
+ }
133
+ return {
134
+ screenshots,
135
+ domSnapshots,
136
+ metadata: {
137
+ viewports,
138
+ pagePath: options.pagePath,
139
+ capturedAt: new Date().toISOString(),
140
+ durationMs: Date.now() - start,
141
+ },
142
+ };
143
+ }
144
+ //# sourceMappingURL=visual-capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-capture.js","sourceRoot":"","sources":["../../src/gates/visual-capture.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,16 @@
1
1
  import type { VisualResult } from "../types.js";
2
+ /**
3
+ * Capture and store "before" snapshots for the given pages.
4
+ * Called at milestone start by the orchestrator so we have a baseline
5
+ * to compare against when `verifyVisual` runs later.
6
+ */
7
+ export declare function captureBeforeSnapshots(projectDir: string, pages: string[], options?: {
8
+ devServerCommand?: string;
9
+ devServerPort?: number;
10
+ screenshotDir?: string;
11
+ }): Promise<void>;
12
+ /** Clear all stored "before" snapshots (e.g. between milestones). */
13
+ export declare function clearBeforeSnapshots(): void;
2
14
  export declare function verifyVisual(projectDir: string, pages: string[], options?: {
3
15
  devServerCommand?: string;
4
16
  devServerPort?: number;
@@ -1,15 +1,79 @@
1
1
  import { getBrowser, startDevServer, stopDevServer, waitForServer, } from "../utils/browser.js";
2
+ import { captureVisual } from "./visual-capture.js";
3
+ import { reviewVisual } from "./visual-reviewer.js";
2
4
  import { mkdirSync } from "node:fs";
3
5
  import { join } from "node:path";
6
+ // ---------------------------------------------------------------------------
7
+ // Before/after snapshot storage
8
+ // ---------------------------------------------------------------------------
9
+ /** Module-level map storing "before" snapshots keyed by page path */
10
+ const beforeSnapshots = new Map();
11
+ /**
12
+ * Capture and store "before" snapshots for the given pages.
13
+ * Called at milestone start by the orchestrator so we have a baseline
14
+ * to compare against when `verifyVisual` runs later.
15
+ */
16
+ export async function captureBeforeSnapshots(projectDir, pages, options) {
17
+ const resolvedPages = pages.length > 0 ? pages : ["/"];
18
+ const port = options?.devServerPort ?? 3000;
19
+ const screenshotDir = options?.screenshotDir ?? join(projectDir, ".forge", "screenshots");
20
+ const beforeDir = join(screenshotDir, "before");
21
+ mkdirSync(beforeDir, { recursive: true });
22
+ try {
23
+ await startDevServer(projectDir, options?.devServerCommand, port);
24
+ const ready = await waitForServer(port);
25
+ if (!ready) {
26
+ return; // Cannot capture baseline — verifyVisual will still work without it
27
+ }
28
+ const browser = await getBrowser();
29
+ const context = await browser.newContext();
30
+ try {
31
+ for (const pagePath of resolvedPages) {
32
+ const page = await context.newPage();
33
+ try {
34
+ await page.goto(`http://localhost:${port}${pagePath}`, {
35
+ waitUntil: "networkidle",
36
+ timeout: 30_000,
37
+ });
38
+ const result = await captureVisual(page, {
39
+ pagePath,
40
+ screenshotDir: beforeDir,
41
+ });
42
+ beforeSnapshots.set(pagePath, result);
43
+ }
44
+ catch {
45
+ // Failed to capture baseline for this page — skip it
46
+ }
47
+ finally {
48
+ await page.close();
49
+ }
50
+ }
51
+ }
52
+ finally {
53
+ await context.close();
54
+ }
55
+ }
56
+ finally {
57
+ await stopDevServer();
58
+ }
59
+ }
60
+ /** Clear all stored "before" snapshots (e.g. between milestones). */
61
+ export function clearBeforeSnapshots() {
62
+ beforeSnapshots.clear();
63
+ }
64
+ // ---------------------------------------------------------------------------
65
+ // Main visual gate
66
+ // ---------------------------------------------------------------------------
4
67
  export async function verifyVisual(projectDir, pages, options) {
5
68
  const start = Date.now();
6
69
  const resolvedPages = pages.length > 0 ? pages : ["/"];
7
70
  const port = options?.devServerPort ?? 3000;
8
71
  const screenshotDir = options?.screenshotDir ?? join(projectDir, ".forge", "screenshots");
72
+ const afterDir = join(screenshotDir, "after");
9
73
  const consoleErrors = [];
10
74
  const warnings = [];
11
75
  const screenshots = [];
12
- mkdirSync(screenshotDir, { recursive: true });
76
+ mkdirSync(afterDir, { recursive: true });
13
77
  try {
14
78
  // Start dev server
15
79
  try {
@@ -43,6 +107,7 @@ export async function verifyVisual(projectDir, pages, options) {
43
107
  // Launch browser and create context
44
108
  const browser = await getBrowser();
45
109
  const context = await browser.newContext();
110
+ const reviewerErrors = [];
46
111
  try {
47
112
  for (const pagePath of resolvedPages) {
48
113
  const page = await context.newPage();
@@ -68,16 +133,29 @@ export async function verifyVisual(projectDir, pages, options) {
68
133
  await page.close();
69
134
  continue;
70
135
  }
71
- // Take screenshot
136
+ // Capture "after" snapshot using the M1 capture module
72
137
  try {
73
- const safeName = pagePath.replace(/\//g, "_").replace(/^_/, "") || "index";
74
- const screenshotPath = join(screenshotDir, `${safeName}.png`);
75
- await page.screenshot({ path: screenshotPath, fullPage: true });
76
- screenshots.push({ page: pagePath, path: screenshotPath });
138
+ const afterResult = await captureVisual(page, {
139
+ pagePath,
140
+ screenshotDir: afterDir,
141
+ });
142
+ // Map multi-viewport screenshots to the flat { page, path } format
143
+ for (const shot of afterResult.screenshots) {
144
+ screenshots.push({ page: shot.page, path: shot.path });
145
+ }
146
+ // Compare with "before" snapshot if one exists
147
+ const beforeResult = beforeSnapshots.get(pagePath);
148
+ if (beforeResult) {
149
+ const findings = reviewVisual(beforeResult, afterResult);
150
+ reviewerErrors.push(...findings);
151
+ }
152
+ else {
153
+ warnings.push(`No baseline snapshot for ${pagePath} — before/after comparison skipped. Call captureBeforeSnapshots() before verifyVisual() to enable.`);
154
+ }
77
155
  }
78
156
  catch (err) {
79
- const message = err instanceof Error ? err.message : "Screenshot failed";
80
- warnings.push(`Screenshot failed for ${pagePath}: ${message}`);
157
+ const message = err instanceof Error ? err.message : "Visual capture failed";
158
+ warnings.push(`Visual capture failed for ${pagePath}: ${message}`);
81
159
  }
82
160
  await page.close();
83
161
  }
@@ -85,13 +163,14 @@ export async function verifyVisual(projectDir, pages, options) {
85
163
  finally {
86
164
  await context.close();
87
165
  }
88
- // Convert console errors to GateError objects
89
- const errors = consoleErrors.map((msg) => ({
90
- message: msg,
91
- }));
166
+ // Combine console errors and reviewer errors into a single GateError list
167
+ const errors = [
168
+ ...consoleErrors.map((msg) => ({ message: msg })),
169
+ ...reviewerErrors,
170
+ ];
92
171
  return {
93
172
  gate: "visual",
94
- passed: consoleErrors.length === 0,
173
+ passed: errors.length === 0,
95
174
  errors,
96
175
  warnings,
97
176
  duration_ms: Date.now() - start,
@@ -1 +1 @@
1
- {"version":3,"file":"visual-gate.js","sourceRoot":"","sources":["../../src/gates/visual-gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"visual-gate.js","sourceRoot":"","sources":["../../src/gates/visual-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { GateError, VisualCaptureResult } from "../types.js";
2
+ /**
3
+ * Compare before and after visual capture results using DOM structural
4
+ * analysis. Returns a list of `GateError` entries describing detected
5
+ * differences per viewport.
6
+ *
7
+ * This does NOT perform pixel-level comparison — it analyses the serialized
8
+ * DOM trees for element count changes, missing/added elements, visibility
9
+ * changes, and significant layout dimension shifts.
10
+ */
11
+ export declare function reviewVisual(before: VisualCaptureResult, after: VisualCaptureResult): GateError[];
@@ -0,0 +1,211 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Helpers
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Build a human-readable DOM path string from root to the given node.
6
+ * Example: "body > div#main > nav.sidebar"
7
+ */
8
+ function domPath(node, ancestors) {
9
+ const parts = [];
10
+ for (const a of ancestors) {
11
+ parts.push(nodeLabel(a));
12
+ }
13
+ parts.push(nodeLabel(node));
14
+ return parts.join(" > ");
15
+ }
16
+ /** Produce a concise label for a single DOM node: tag#id.className */
17
+ function nodeLabel(node) {
18
+ let label = node.tag;
19
+ if (node.id) {
20
+ label += `#${node.id}`;
21
+ }
22
+ if (node.className) {
23
+ // Collapse whitespace and join with dots
24
+ label += "." + node.className.split(/\s+/).join(".");
25
+ }
26
+ return label;
27
+ }
28
+ /**
29
+ * Build an identity key for a node so we can match elements across
30
+ * before/after snapshots. Nodes with an `id` get that as their key;
31
+ * otherwise we fall back to tag+className which is less unique but still
32
+ * useful.
33
+ */
34
+ function nodeKey(node) {
35
+ if (node.id) {
36
+ return `id:${node.id}`;
37
+ }
38
+ return `tag:${node.tag}|class:${node.className ?? ""}`;
39
+ }
40
+ /**
41
+ * Recursively flatten a DOM tree into a list of FlatNode entries, each
42
+ * carrying its identity key and human-readable path.
43
+ */
44
+ function flattenTree(root, ancestors = []) {
45
+ const result = [];
46
+ const currentPath = domPath(root, ancestors);
47
+ result.push({ node: root, path: currentPath, key: nodeKey(root) });
48
+ const nextAncestors = [...ancestors, root];
49
+ for (const child of root.children) {
50
+ result.push(...flattenTree(child, nextAncestors));
51
+ }
52
+ return result;
53
+ }
54
+ /** Count the total number of nodes in a DOM tree (including the root). */
55
+ function countNodes(root) {
56
+ let count = 1;
57
+ for (const child of root.children) {
58
+ count += countNodes(child);
59
+ }
60
+ return count;
61
+ }
62
+ // ---------------------------------------------------------------------------
63
+ // Comparison thresholds
64
+ // ---------------------------------------------------------------------------
65
+ /** If total element count changes by more than this ratio, flag it. */
66
+ const ELEMENT_COUNT_CHANGE_THRESHOLD = 0.20; // 20%
67
+ /** Bounding-rect shift (in px) above which we flag a layout change. */
68
+ const LAYOUT_SHIFT_PX = 50;
69
+ // ---------------------------------------------------------------------------
70
+ // Per-viewport comparison
71
+ // ---------------------------------------------------------------------------
72
+ function compareSnapshots(viewport, before, after) {
73
+ const errors = [];
74
+ // --- Element count change ------------------------------------------------
75
+ const beforeCount = countNodes(before);
76
+ const afterCount = countNodes(after);
77
+ if (beforeCount > 0) {
78
+ const ratio = Math.abs(afterCount - beforeCount) / beforeCount;
79
+ if (ratio > ELEMENT_COUNT_CHANGE_THRESHOLD) {
80
+ const direction = afterCount > beforeCount ? "increase" : "decrease";
81
+ errors.push({
82
+ message: `[${viewport}] Significant element count ${direction}: ${beforeCount} -> ${afterCount} (${Math.round(ratio * 100)}% change)`,
83
+ remediation: `Review the DOM on the "${viewport}" viewport — a large element count ${direction} may indicate unintended additions or removals.`,
84
+ });
85
+ }
86
+ }
87
+ // --- Flatten both trees and index by key ---------------------------------
88
+ const beforeFlat = flattenTree(before);
89
+ const afterFlat = flattenTree(after);
90
+ const beforeByKey = new Map();
91
+ for (const entry of beforeFlat) {
92
+ const existing = beforeByKey.get(entry.key);
93
+ if (existing) {
94
+ existing.push(entry);
95
+ }
96
+ else {
97
+ beforeByKey.set(entry.key, [entry]);
98
+ }
99
+ }
100
+ const afterByKey = new Map();
101
+ for (const entry of afterFlat) {
102
+ const existing = afterByKey.get(entry.key);
103
+ if (existing) {
104
+ existing.push(entry);
105
+ }
106
+ else {
107
+ afterByKey.set(entry.key, [entry]);
108
+ }
109
+ }
110
+ // --- Missing elements (in before, not in after) --------------------------
111
+ for (const [key, beforeEntries] of beforeByKey) {
112
+ if (!afterByKey.has(key)) {
113
+ // All instances of this key are missing in `after`
114
+ for (const entry of beforeEntries) {
115
+ errors.push({
116
+ message: `[${viewport}] Missing element: ${entry.path}`,
117
+ remediation: `Element ${nodeLabel(entry.node)} missing on "${viewport}" viewport — check responsive CSS rules and conditional rendering logic.`,
118
+ });
119
+ }
120
+ }
121
+ }
122
+ // --- Added elements (in after, not in before) — informational ------------
123
+ for (const [key, afterEntries] of afterByKey) {
124
+ if (!beforeByKey.has(key)) {
125
+ for (const entry of afterEntries) {
126
+ errors.push({
127
+ message: `[${viewport}] Added element: ${entry.path}`,
128
+ remediation: `New element ${nodeLabel(entry.node)} detected on "${viewport}" viewport — verify this addition is intentional.`,
129
+ });
130
+ }
131
+ }
132
+ }
133
+ // --- Visibility changes and layout shifts --------------------------------
134
+ // For elements present in both snapshots, compare visibility and rect.
135
+ for (const [key, beforeEntries] of beforeByKey) {
136
+ const afterEntries = afterByKey.get(key);
137
+ if (!afterEntries)
138
+ continue;
139
+ // Compare pairwise (first-to-first, etc.) up to the shorter list length
140
+ const pairCount = Math.min(beforeEntries.length, afterEntries.length);
141
+ for (let i = 0; i < pairCount; i++) {
142
+ const bEntry = beforeEntries[i];
143
+ const aEntry = afterEntries[i];
144
+ // Visibility change
145
+ if (bEntry.node.visible && !aEntry.node.visible) {
146
+ errors.push({
147
+ message: `[${viewport}] Element became hidden: ${bEntry.path}`,
148
+ remediation: `Element ${nodeLabel(bEntry.node)} changed from visible to hidden on "${viewport}" viewport — check CSS display/visibility/opacity rules.`,
149
+ });
150
+ }
151
+ else if (!bEntry.node.visible && aEntry.node.visible) {
152
+ errors.push({
153
+ message: `[${viewport}] Element became visible: ${bEntry.path}`,
154
+ remediation: `Element ${nodeLabel(bEntry.node)} changed from hidden to visible on "${viewport}" viewport — verify this visibility change is intentional.`,
155
+ });
156
+ }
157
+ // Layout dimension shift (only when both have rect data)
158
+ if (bEntry.node.rect && aEntry.node.rect) {
159
+ const bRect = bEntry.node.rect;
160
+ const aRect = aEntry.node.rect;
161
+ const dx = Math.abs(aRect.x - bRect.x);
162
+ const dy = Math.abs(aRect.y - bRect.y);
163
+ const dw = Math.abs(aRect.width - bRect.width);
164
+ const dh = Math.abs(aRect.height - bRect.height);
165
+ const shifts = [];
166
+ if (dx > LAYOUT_SHIFT_PX)
167
+ shifts.push(`x shifted by ${dx}px`);
168
+ if (dy > LAYOUT_SHIFT_PX)
169
+ shifts.push(`y shifted by ${dy}px`);
170
+ if (dw > LAYOUT_SHIFT_PX)
171
+ shifts.push(`width changed by ${dw}px`);
172
+ if (dh > LAYOUT_SHIFT_PX)
173
+ shifts.push(`height changed by ${dh}px`);
174
+ if (shifts.length > 0) {
175
+ errors.push({
176
+ message: `[${viewport}] Layout shift on ${bEntry.path}: ${shifts.join(", ")}`,
177
+ remediation: `Element ${nodeLabel(bEntry.node)} dimensions shifted by >${LAYOUT_SHIFT_PX}px on "${viewport}" viewport — review layout changes.`,
178
+ });
179
+ }
180
+ }
181
+ }
182
+ }
183
+ return errors;
184
+ }
185
+ // ---------------------------------------------------------------------------
186
+ // Public API
187
+ // ---------------------------------------------------------------------------
188
+ /**
189
+ * Compare before and after visual capture results using DOM structural
190
+ * analysis. Returns a list of `GateError` entries describing detected
191
+ * differences per viewport.
192
+ *
193
+ * This does NOT perform pixel-level comparison — it analyses the serialized
194
+ * DOM trees for element count changes, missing/added elements, visibility
195
+ * changes, and significant layout dimension shifts.
196
+ */
197
+ export function reviewVisual(before, after) {
198
+ const errors = [];
199
+ // Only compare viewports present in both snapshots
200
+ const beforeViewports = new Set(Object.keys(before.domSnapshots));
201
+ const afterViewports = new Set(Object.keys(after.domSnapshots));
202
+ for (const viewport of beforeViewports) {
203
+ if (!afterViewports.has(viewport))
204
+ continue;
205
+ const beforeSnap = before.domSnapshots[viewport];
206
+ const afterSnap = after.domSnapshots[viewport];
207
+ errors.push(...compareSnapshots(viewport, beforeSnap, afterSnap));
208
+ }
209
+ return errors;
210
+ }
211
+ //# sourceMappingURL=visual-reviewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-reviewer.js","sourceRoot":"","sources":["../../src/gates/visual-reviewer.ts"],"names":[],"mappings":"AAMA,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;GAGG;AACH,SAAS,OAAO,CAAC,IAAiB,EAAE,SAAwB;IAC1D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,sEAAsE;AACtE,SAAS,SAAS,CAAC,IAAiB;IAClC,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC;IACrB,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;QACZ,KAAK,IAAI,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;IACzB,CAAC;IACD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,yCAAyC;QACzC,KAAK,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAS,OAAO,CAAC,IAAiB;IAChC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;QACZ,OAAO,MAAM,IAAI,CAAC,EAAE,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,OAAO,IAAI,CAAC,GAAG,UAAU,IAAI,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;AACzD,CAAC;AAYD;;;GAGG;AACH,SAAS,WAAW,CAClB,IAAiB,EACjB,YAA2B,EAAE;IAE7B,MAAM,MAAM,GAAe,EAAE,CAAC;IAE9B,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC7C,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEnE,MAAM,aAAa,GAAG,CAAC,GAAG,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,0EAA0E;AAC1E,SAAS,UAAU,CAAC,IAAiB;IACnC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,uEAAuE;AACvE,MAAM,8BAA8B,GAAG,IAAI,CAAC,CAAC,MAAM;AAEnD,uEAAuE;AACvE,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,8EAA8E;AAC9E,0BAA0B;AAC1B,8EAA8E;AAE9E,SAAS,gBAAgB,CACvB,QAAgB,EAChB,MAAmB,EACnB,KAAkB;IAElB,MAAM,MAAM,GAAgB,EAAE,CAAC;IAE/B,4EAA4E;IAC5E,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAErC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC;QAC/D,IAAI,KAAK,GAAG,8BAA8B,EAAE,CAAC;YAC3C,MAAM,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;YACrE,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,IAAI,QAAQ,+BAA+B,SAAS,KAAK,WAAW,OAAO,UAAU,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,WAAW;gBACrI,WAAW,EAAE,0BAA0B,QAAQ,sCAAsC,SAAS,iDAAiD;aAChJ,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAErC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;IAClD,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;IACjD,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,KAAK,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,WAAW,EAAE,CAAC;QAC/C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,mDAAmD;YACnD,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;gBAClC,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,IAAI,QAAQ,sBAAsB,KAAK,CAAC,IAAI,EAAE;oBACvD,WAAW,EAAE,WAAW,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,QAAQ,0EAA0E;iBAChJ,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,KAAK,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,IAAI,UAAU,EAAE,CAAC;QAC7C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;gBACjC,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,IAAI,QAAQ,oBAAoB,KAAK,CAAC,IAAI,EAAE;oBACrD,WAAW,EAAE,eAAe,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,QAAQ,mDAAmD;iBAC9H,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,uEAAuE;IACvE,KAAK,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,WAAW,EAAE,CAAC;QAC/C,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,CAAC,YAAY;YAAE,SAAS;QAE5B,wEAAwE;QACxE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;QACtE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YAE/B,oBAAoB;YACpB,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAChD,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,IAAI,QAAQ,4BAA4B,MAAM,CAAC,IAAI,EAAE;oBAC9D,WAAW,EAAE,WAAW,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,uCAAuC,QAAQ,0DAA0D;iBACxJ,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACvD,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,IAAI,QAAQ,6BAA6B,MAAM,CAAC,IAAI,EAAE;oBAC/D,WAAW,EAAE,WAAW,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,uCAAuC,QAAQ,4DAA4D;iBAC1J,CAAC,CAAC;YACL,CAAC;YAED,yDAAyD;YACzD,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACzC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;gBAE/B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACvC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACvC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;gBAEjD,MAAM,MAAM,GAAa,EAAE,CAAC;gBAC5B,IAAI,EAAE,GAAG,eAAe;oBAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;gBAC9D,IAAI,EAAE,GAAG,eAAe;oBAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;gBAC9D,IAAI,EAAE,GAAG,eAAe;oBAAE,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;gBAClE,IAAI,EAAE,GAAG,eAAe;oBAAE,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;gBAEnE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,CAAC,IAAI,CAAC;wBACV,OAAO,EAAE,IAAI,QAAQ,qBAAqB,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;wBAC7E,WAAW,EAAE,WAAW,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,eAAe,UAAU,QAAQ,qCAAqC;qBAChJ,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAC1B,MAA2B,EAC3B,KAA0B;IAE1B,MAAM,MAAM,GAAgB,EAAE,CAAC;IAE/B,mDAAmD;IACnD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;IAClE,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;IAEhE,KAAK,MAAM,QAAQ,IAAI,eAAe,EAAE,CAAC;QACvC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS;QAE5C,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAE/C,MAAM,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -150,7 +150,8 @@ export function formatErrorsForAgent(result) {
150
150
  }
151
151
  lines.push(`**Error:** ${err.message}`);
152
152
  if (err.remediation) {
153
- lines.push(`**Suggested fix:** ${err.remediation}`);
153
+ lines.push("");
154
+ lines.push(`> **Remediation:** ${err.remediation}`);
154
155
  }
155
156
  lines.push("");
156
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"verify-loop.js","sourceRoot":"","sources":["../../src/go/verify-loop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"verify-loop.js","sourceRoot":"","sources":["../../src/go/verify-loop.ts"],"names":[],"mappings":"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"}