forge-cc 1.0.0 → 1.0.1

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 (142) hide show
  1. package/package.json +3 -2
  2. package/dist/gates/codex-gate.d.ts +0 -51
  3. package/dist/gates/codex-gate.js +0 -121
  4. package/dist/gates/codex-gate.js.map +0 -1
  5. package/dist/gates/prd-gate.d.ts +0 -7
  6. package/dist/gates/prd-gate.js +0 -193
  7. package/dist/gates/prd-gate.js.map +0 -1
  8. package/dist/gates/remediation.d.ts +0 -46
  9. package/dist/gates/remediation.js +0 -423
  10. package/dist/gates/remediation.js.map +0 -1
  11. package/dist/gates/review-gate.d.ts +0 -16
  12. package/dist/gates/review-gate.js +0 -479
  13. package/dist/gates/review-gate.js.map +0 -1
  14. package/dist/gates/runtime-gate.d.ts +0 -5
  15. package/dist/gates/runtime-gate.js +0 -99
  16. package/dist/gates/runtime-gate.js.map +0 -1
  17. package/dist/gates/test-analysis.d.ts +0 -21
  18. package/dist/gates/test-analysis.js +0 -394
  19. package/dist/gates/test-analysis.js.map +0 -1
  20. package/dist/gates/visual-capture.d.ts +0 -24
  21. package/dist/gates/visual-capture.js +0 -144
  22. package/dist/gates/visual-capture.js.map +0 -1
  23. package/dist/gates/visual-gate.d.ts +0 -18
  24. package/dist/gates/visual-gate.js +0 -234
  25. package/dist/gates/visual-gate.js.map +0 -1
  26. package/dist/gates/visual-reviewer.d.ts +0 -11
  27. package/dist/gates/visual-reviewer.js +0 -211
  28. package/dist/gates/visual-reviewer.js.map +0 -1
  29. package/dist/go/auto-chain.d.ts +0 -136
  30. package/dist/go/auto-chain.js +0 -389
  31. package/dist/go/auto-chain.js.map +0 -1
  32. package/dist/go/executor.d.ts +0 -137
  33. package/dist/go/executor.js +0 -447
  34. package/dist/go/executor.js.map +0 -1
  35. package/dist/go/finalize.d.ts +0 -108
  36. package/dist/go/finalize.js +0 -331
  37. package/dist/go/finalize.js.map +0 -1
  38. package/dist/go/linear-sync-cli.d.ts +0 -55
  39. package/dist/go/linear-sync-cli.js +0 -192
  40. package/dist/go/linear-sync-cli.js.map +0 -1
  41. package/dist/go/linear-sync.d.ts +0 -112
  42. package/dist/go/linear-sync.js +0 -375
  43. package/dist/go/linear-sync.js.map +0 -1
  44. package/dist/go/prd-queue.d.ts +0 -43
  45. package/dist/go/prd-queue.js +0 -67
  46. package/dist/go/prd-queue.js.map +0 -1
  47. package/dist/go/prd-selector.d.ts +0 -57
  48. package/dist/go/prd-selector.js +0 -101
  49. package/dist/go/prd-selector.js.map +0 -1
  50. package/dist/go/verify-loop.d.ts +0 -64
  51. package/dist/go/verify-loop.js +0 -327
  52. package/dist/go/verify-loop.js.map +0 -1
  53. package/dist/hooks/pre-commit.d.ts +0 -5
  54. package/dist/hooks/pre-commit.js +0 -75
  55. package/dist/hooks/pre-commit.js.map +0 -1
  56. package/dist/linear/issues.d.ts +0 -22
  57. package/dist/linear/issues.js +0 -51
  58. package/dist/linear/issues.js.map +0 -1
  59. package/dist/linear/milestones.d.ts +0 -11
  60. package/dist/linear/milestones.js +0 -32
  61. package/dist/linear/milestones.js.map +0 -1
  62. package/dist/linear/projects.d.ts +0 -16
  63. package/dist/linear/projects.js +0 -51
  64. package/dist/linear/projects.js.map +0 -1
  65. package/dist/reporter/human.d.ts +0 -7
  66. package/dist/reporter/human.js +0 -93
  67. package/dist/reporter/human.js.map +0 -1
  68. package/dist/reporter/json.d.ts +0 -2
  69. package/dist/reporter/json.js +0 -4
  70. package/dist/reporter/json.js.map +0 -1
  71. package/dist/setup/structural-templates.d.ts +0 -12
  72. package/dist/setup/structural-templates.js +0 -288
  73. package/dist/setup/structural-templates.js.map +0 -1
  74. package/dist/setup/templates.d.ts +0 -17
  75. package/dist/setup/templates.js +0 -109
  76. package/dist/setup/templates.js.map +0 -1
  77. package/dist/setup/test-planner.d.ts +0 -38
  78. package/dist/setup/test-planner.js +0 -91
  79. package/dist/setup/test-planner.js.map +0 -1
  80. package/dist/setup/test-scaffold.d.ts +0 -31
  81. package/dist/setup/test-scaffold.js +0 -209
  82. package/dist/setup/test-scaffold.js.map +0 -1
  83. package/dist/setup/test-templates.d.ts +0 -37
  84. package/dist/setup/test-templates.js +0 -313
  85. package/dist/setup/test-templates.js.map +0 -1
  86. package/dist/spec/generator.d.ts +0 -34
  87. package/dist/spec/generator.js +0 -227
  88. package/dist/spec/generator.js.map +0 -1
  89. package/dist/spec/interview.d.ts +0 -142
  90. package/dist/spec/interview.js +0 -287
  91. package/dist/spec/interview.js.map +0 -1
  92. package/dist/spec/linear-sync.d.ts +0 -48
  93. package/dist/spec/linear-sync.js +0 -125
  94. package/dist/spec/linear-sync.js.map +0 -1
  95. package/dist/spec/scanner.d.ts +0 -79
  96. package/dist/spec/scanner.js +0 -566
  97. package/dist/spec/scanner.js.map +0 -1
  98. package/dist/spec/templates.d.ts +0 -375
  99. package/dist/spec/templates.js +0 -95
  100. package/dist/spec/templates.js.map +0 -1
  101. package/dist/state/prd-status.d.ts +0 -62
  102. package/dist/state/prd-status.js +0 -122
  103. package/dist/state/prd-status.js.map +0 -1
  104. package/dist/state/reader.d.ts +0 -7
  105. package/dist/state/reader.js +0 -43
  106. package/dist/state/reader.js.map +0 -1
  107. package/dist/state/writer.d.ts +0 -21
  108. package/dist/state/writer.js +0 -106
  109. package/dist/state/writer.js.map +0 -1
  110. package/dist/team/consensus.d.ts +0 -28
  111. package/dist/team/consensus.js +0 -130
  112. package/dist/team/consensus.js.map +0 -1
  113. package/dist/team/index.d.ts +0 -4
  114. package/dist/team/index.js +0 -5
  115. package/dist/team/index.js.map +0 -1
  116. package/dist/team/lifecycle.d.ts +0 -37
  117. package/dist/team/lifecycle.js +0 -92
  118. package/dist/team/lifecycle.js.map +0 -1
  119. package/dist/team/reviewer.d.ts +0 -10
  120. package/dist/team/reviewer.js +0 -345
  121. package/dist/team/reviewer.js.map +0 -1
  122. package/dist/team/types.d.ts +0 -269
  123. package/dist/team/types.js +0 -70
  124. package/dist/team/types.js.map +0 -1
  125. package/dist/utils/browser.d.ts +0 -10
  126. package/dist/utils/browser.js +0 -96
  127. package/dist/utils/browser.js.map +0 -1
  128. package/dist/utils/platform.d.ts +0 -29
  129. package/dist/utils/platform.js +0 -90
  130. package/dist/utils/platform.js.map +0 -1
  131. package/dist/worktree/identity.d.ts +0 -9
  132. package/dist/worktree/identity.js +0 -32
  133. package/dist/worktree/identity.js.map +0 -1
  134. package/dist/worktree/parallel.d.ts +0 -87
  135. package/dist/worktree/parallel.js +0 -328
  136. package/dist/worktree/parallel.js.map +0 -1
  137. package/dist/worktree/session.d.ts +0 -67
  138. package/dist/worktree/session.js +0 -194
  139. package/dist/worktree/session.js.map +0 -1
  140. package/dist/worktree/state-merge.d.ts +0 -43
  141. package/dist/worktree/state-merge.js +0 -162
  142. package/dist/worktree/state-merge.js.map +0 -1
@@ -1,234 +0,0 @@
1
- import { getBrowser, startDevServer, stopDevServer, waitForServer, } from "../utils/browser.js";
2
- import { captureVisual } from "./visual-capture.js";
3
- import { reviewVisual } from "./visual-reviewer.js";
4
- import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
5
- import { join } from "node:path";
6
- // ---------------------------------------------------------------------------
7
- // Before snapshot disk persistence
8
- // ---------------------------------------------------------------------------
9
- /** Sanitize a page path to a safe filename key (e.g. "/" -> "index", "/dashboard" -> "dashboard") */
10
- function snapshotKey(pagePath) {
11
- const sanitized = pagePath.replace(/\//g, "_").replace(/^_/, "");
12
- return sanitized || "index";
13
- }
14
- /** Save a before snapshot to disk so it survives across CLI invocations. */
15
- function saveBeforeSnapshot(screenshotDir, pagePath, result) {
16
- const dir = join(screenshotDir, "before");
17
- mkdirSync(dir, { recursive: true });
18
- const filePath = join(dir, `${snapshotKey(pagePath)}.snapshot.json`);
19
- writeFileSync(filePath, JSON.stringify(result, null, 2));
20
- }
21
- /** Load a before snapshot from disk. Returns null if none exists. */
22
- function loadBeforeSnapshot(screenshotDir, pagePath) {
23
- const filePath = join(screenshotDir, "before", `${snapshotKey(pagePath)}.snapshot.json`);
24
- if (!existsSync(filePath))
25
- return null;
26
- try {
27
- return JSON.parse(readFileSync(filePath, "utf-8"));
28
- }
29
- catch {
30
- return null;
31
- }
32
- }
33
- /**
34
- * Capture and store "before" snapshots for the given pages.
35
- * Snapshots are persisted to disk (.forge/screenshots/before/*.snapshot.json)
36
- * so they survive across separate CLI invocations.
37
- */
38
- export async function captureBeforeSnapshots(projectDir, pages, options) {
39
- const resolvedPages = pages.length > 0 ? pages : ["/"];
40
- const port = options?.devServerPort ?? 3000;
41
- const screenshotDir = options?.screenshotDir ?? join(projectDir, ".forge", "screenshots");
42
- const beforeDir = join(screenshotDir, "before");
43
- mkdirSync(beforeDir, { recursive: true });
44
- try {
45
- await startDevServer(projectDir, options?.devServerCommand, port);
46
- const ready = await waitForServer(port);
47
- if (!ready) {
48
- return; // Cannot capture baseline — verifyVisual will still work without it
49
- }
50
- const browser = await getBrowser();
51
- const context = await browser.newContext();
52
- try {
53
- for (const pagePath of resolvedPages) {
54
- const page = await context.newPage();
55
- try {
56
- await page.goto(`http://localhost:${port}${pagePath}`, {
57
- waitUntil: "networkidle",
58
- timeout: 30_000,
59
- });
60
- const result = await captureVisual(page, {
61
- pagePath,
62
- screenshotDir: beforeDir,
63
- });
64
- saveBeforeSnapshot(screenshotDir, pagePath, result);
65
- }
66
- catch {
67
- // Failed to capture baseline for this page — skip it
68
- }
69
- finally {
70
- await page.close();
71
- }
72
- }
73
- }
74
- finally {
75
- await context.close();
76
- }
77
- }
78
- finally {
79
- await stopDevServer();
80
- }
81
- }
82
- /** Clear all stored "before" snapshots from disk (e.g. between milestones). */
83
- export function clearBeforeSnapshots(projectDir) {
84
- const dir = join(projectDir, ".forge", "screenshots", "before");
85
- let files;
86
- try {
87
- files = readdirSync(dir);
88
- }
89
- catch {
90
- return; // Directory doesn't exist — nothing to clear
91
- }
92
- for (const file of files) {
93
- if (file.endsWith(".snapshot.json")) {
94
- try {
95
- unlinkSync(join(dir, file));
96
- }
97
- catch { /* ignore */ }
98
- }
99
- }
100
- }
101
- // ---------------------------------------------------------------------------
102
- // Main visual gate
103
- // ---------------------------------------------------------------------------
104
- export async function verifyVisual(projectDir, pages, options) {
105
- const start = Date.now();
106
- const resolvedPages = pages.length > 0 ? pages : ["/"];
107
- const port = options?.devServerPort ?? 3000;
108
- const screenshotDir = options?.screenshotDir ?? join(projectDir, ".forge", "screenshots");
109
- const afterDir = join(screenshotDir, "after");
110
- const consoleErrors = [];
111
- const warnings = [];
112
- const screenshots = [];
113
- mkdirSync(afterDir, { recursive: true });
114
- try {
115
- // Start dev server
116
- try {
117
- await startDevServer(projectDir, options?.devServerCommand, port);
118
- }
119
- catch (err) {
120
- const message = err instanceof Error ? err.message : "Unknown dev server error";
121
- return {
122
- gate: "visual",
123
- passed: false,
124
- errors: [{ message: `Dev server failed to start: ${message}` }],
125
- warnings,
126
- duration_ms: Date.now() - start,
127
- screenshots,
128
- consoleErrors,
129
- };
130
- }
131
- // Ensure server is reachable
132
- const ready = await waitForServer(port);
133
- if (!ready) {
134
- return {
135
- gate: "visual",
136
- passed: false,
137
- errors: [{ message: `Dev server not reachable on port ${port}` }],
138
- warnings,
139
- duration_ms: Date.now() - start,
140
- screenshots,
141
- consoleErrors,
142
- };
143
- }
144
- // Launch browser and create context
145
- const browser = await getBrowser();
146
- const context = await browser.newContext();
147
- const reviewerErrors = [];
148
- try {
149
- for (const pagePath of resolvedPages) {
150
- const page = await context.newPage();
151
- // Collect console errors
152
- page.on("console", (msg) => {
153
- if (msg.type() === "error") {
154
- consoleErrors.push(msg.text());
155
- }
156
- });
157
- page.on("pageerror", (err) => {
158
- consoleErrors.push(err.message);
159
- });
160
- // Navigate to the page
161
- try {
162
- await page.goto(`http://localhost:${port}${pagePath}`, {
163
- waitUntil: "networkidle",
164
- timeout: 30_000,
165
- });
166
- }
167
- catch (err) {
168
- const message = err instanceof Error ? err.message : "Navigation failed";
169
- consoleErrors.push(`Navigation error for ${pagePath}: ${message}`);
170
- await page.close();
171
- continue;
172
- }
173
- // Capture "after" snapshot using the M1 capture module
174
- try {
175
- const afterResult = await captureVisual(page, {
176
- pagePath,
177
- screenshotDir: afterDir,
178
- });
179
- // Map multi-viewport screenshots to the flat { page, path } format
180
- for (const shot of afterResult.screenshots) {
181
- screenshots.push({ page: shot.page, path: shot.path });
182
- }
183
- // Compare with "before" snapshot if one exists (loaded from disk)
184
- const beforeResult = loadBeforeSnapshot(screenshotDir, pagePath);
185
- if (beforeResult) {
186
- const findings = reviewVisual(beforeResult, afterResult);
187
- reviewerErrors.push(...findings);
188
- }
189
- else {
190
- warnings.push(`No baseline snapshot for ${pagePath} — before/after comparison skipped. Call captureBeforeSnapshots() before verifyVisual() to enable.`);
191
- }
192
- }
193
- catch (err) {
194
- const message = err instanceof Error ? err.message : "Visual capture failed";
195
- warnings.push(`Visual capture failed for ${pagePath}: ${message}`);
196
- }
197
- await page.close();
198
- }
199
- }
200
- finally {
201
- await context.close();
202
- }
203
- // Combine console errors and reviewer errors into a single GateError list
204
- const errors = [
205
- ...consoleErrors.map((msg) => ({ message: msg })),
206
- ...reviewerErrors,
207
- ];
208
- return {
209
- gate: "visual",
210
- passed: errors.length === 0,
211
- errors,
212
- warnings,
213
- duration_ms: Date.now() - start,
214
- screenshots,
215
- consoleErrors,
216
- };
217
- }
218
- catch (err) {
219
- const message = err instanceof Error ? err.message : "Unknown error in verifyVisual";
220
- return {
221
- gate: "visual",
222
- passed: false,
223
- errors: [{ message }],
224
- warnings,
225
- duration_ms: Date.now() - start,
226
- screenshots,
227
- consoleErrors,
228
- };
229
- }
230
- finally {
231
- await stopDevServer();
232
- }
233
- }
234
- //# sourceMappingURL=visual-gate.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"visual-gate.js","sourceRoot":"","sources":["../../src/gates/visual-gate.ts"],"names":[],"mappings":"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"}
@@ -1,11 +0,0 @@
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[];
@@ -1,211 +0,0 @@
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
@@ -1 +0,0 @@
1
- {"version":3,"file":"visual-reviewer.js","sourceRoot":"","sources":["../../src/gates/visual-reviewer.ts"],"names":[],"mappings":"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"}
@@ -1,136 +0,0 @@
1
- /**
2
- * Auto-Chain — Multi-Milestone Execution Orchestrator
3
- *
4
- * After a milestone completes, spawns a fresh agent with clean context for the
5
- * next milestone. Fresh agent reads CLAUDE.md + next milestone section only.
6
- * Loops until all milestones done or a failure stops the chain.
7
- *
8
- * This module is the data/logic layer. It does NOT spawn agents — that is
9
- * the skill file's job (via Claude Code's Task tool). Auto-chain prepares
10
- * the context and tracks results across milestones.
11
- */
12
- import { findNextPendingMilestone, countPendingMilestones } from "../state/prd-status.js";
13
- import type { CommitResult } from "../state/writer.js";
14
- import type { ForgeConfig } from "../types.js";
15
- import type { SchedulerResult } from "../worktree/parallel.js";
16
- export interface AutoChainOptions {
17
- projectDir: string;
18
- prdPath: string;
19
- config: ForgeConfig;
20
- branch: string;
21
- /** Project name (e.g., "forge-cc") */
22
- project: string;
23
- /** PRD slug for per-PRD status tracking */
24
- prdSlug: string;
25
- /** Relative PRD path for state docs */
26
- activePrd: string;
27
- /** Developer name for session memory */
28
- developer: string;
29
- /** Repository root (for worktree operations). Defaults to projectDir. */
30
- repoRoot?: string;
31
- /** If not provided, detect from per-PRD status */
32
- startMilestone?: number;
33
- /** Called when a milestone begins execution */
34
- onMilestoneStart?: (milestoneNumber: number, name: string) => void;
35
- /** Called when a milestone finishes (success or failure) */
36
- onMilestoneComplete?: (milestoneNumber: number, result: MilestoneResult) => void;
37
- /** Called when the entire chain finishes */
38
- onChainComplete?: (results: MilestoneResult[]) => void;
39
- }
40
- export interface MilestoneResult {
41
- milestoneNumber: number;
42
- milestoneName: string;
43
- success: boolean;
44
- commitSha?: string;
45
- isLast: boolean;
46
- /** Fresh-context prompt for spawning this milestone's agent */
47
- freshPrompt: string;
48
- /** Path to the worktree used for execution */
49
- worktreePath?: string;
50
- errors: string[];
51
- }
52
- export interface AutoChainResult {
53
- completed: MilestoneResult[];
54
- /** true if the chain stopped due to a milestone failure */
55
- stopped: boolean;
56
- /** Milestone number where the chain stopped (on failure) */
57
- stoppedAt?: number;
58
- /** true if every milestone in the roadmap is complete */
59
- allComplete: boolean;
60
- }
61
- /**
62
- * Build the fresh-context prompt for a milestone agent (Ralph Loop pattern).
63
- *
64
- * Reads CLAUDE.md (abbreviated to Quick Context) and the current milestone
65
- * section from the PRD. The total is kept as small as possible (~200-300
66
- * lines) while giving the agent enough context to work.
67
- */
68
- export declare function buildFreshSessionPrompt(projectDir: string, prdPath: string, milestoneNumber: number): Promise<string>;
69
- export { findNextPendingMilestone, countPendingMilestones };
70
- /**
71
- * Auto-chain orchestrator: manages multi-milestone execution with context resets.
72
- *
73
- * For each pending milestone:
74
- * 1. Determines the starting milestone (from options or per-PRD status)
75
- * 2. Creates a git worktree for isolated execution
76
- * 3. Registers a session in the session registry
77
- * 4. Builds a fresh-context prompt for the milestone agent
78
- * 5. Calls the milestone context builder for structured data
79
- * 6. Returns results so the calling skill can spawn agents and drive execution
80
- * 7. On completion or failure, deregisters the session and cleans up the worktree
81
- *
82
- * The worktree is created ONCE per /forge:go session, not per milestone.
83
- * All milestones in the chain execute in the same worktree.
84
- *
85
- * The chain stops on the first milestone failure, or when all milestones
86
- * are complete. The caller is responsible for actually executing each
87
- * milestone (spawning agents, running waves) — this function provides the
88
- * orchestration loop and context management.
89
- */
90
- export declare function runAutoChain(options: AutoChainOptions): Promise<AutoChainResult>;
91
- /**
92
- * Called after a milestone's agent finishes execution.
93
- *
94
- * Handles:
95
- * 1. Updating milestone status in per-PRD status JSON
96
- * 2. Committing milestone work to git (in the worktree if one was used)
97
- * 3. Merging worktree branch into the feature branch (if worktree was used)
98
- * 4. Returning the commit SHA for the milestone result
99
- *
100
- * The caller should update the MilestoneResult with the returned commit info.
101
- */
102
- export declare function completeMilestone(options: {
103
- projectDir: string;
104
- project: string;
105
- prdSlug: string;
106
- milestoneNumber: number;
107
- milestoneName: string;
108
- branch: string;
109
- activePrd: string;
110
- developer: string;
111
- filesToStage: string[];
112
- push?: boolean;
113
- /** Path to the worktree used for execution. If set, commit happens in the worktree. */
114
- worktreePath?: string;
115
- /** Repository root, required when worktreePath is provided (for merge operations). */
116
- repoRoot?: string;
117
- }): Promise<{
118
- commitResult: CommitResult;
119
- isLast: boolean;
120
- }>;
121
- /**
122
- * Build a parallel execution plan for all milestones in a PRD.
123
- *
124
- * Parses the PRD markdown for milestone definitions and `dependsOn` fields,
125
- * builds a dependency DAG, and computes parallel execution waves showing
126
- * which milestones can run simultaneously.
127
- *
128
- * If no `dependsOn` fields are found in the PRD, all milestones are treated
129
- * as roots (no dependencies) and placed in a single wave. This is backward
130
- * compatible — the caller can process them sequentially by milestone number.
131
- *
132
- * @param prdPath - Absolute path to the PRD markdown file
133
- * @returns The wave schedule showing which milestones can run simultaneously
134
- * @throws If a dependency cycle is detected or a referenced dependency doesn't exist
135
- */
136
- export declare function buildParallelPlan(prdPath: string): Promise<SchedulerResult>;