codeloop-mcp-server 0.1.14 → 0.1.16

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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +75 -0
  3. package/dist/auth/local_mode.d.ts +32 -0
  4. package/dist/auth/local_mode.d.ts.map +1 -0
  5. package/dist/auth/local_mode.js +56 -0
  6. package/dist/auth/local_mode.js.map +1 -0
  7. package/dist/auth/usage_tracker.d.ts +11 -1
  8. package/dist/auth/usage_tracker.d.ts.map +1 -1
  9. package/dist/auth/usage_tracker.js +51 -4
  10. package/dist/auth/usage_tracker.js.map +1 -1
  11. package/dist/environment/presets.d.ts +46 -0
  12. package/dist/environment/presets.d.ts.map +1 -0
  13. package/dist/environment/presets.js +109 -0
  14. package/dist/environment/presets.js.map +1 -0
  15. package/dist/evidence/baseline_governance.d.ts +62 -0
  16. package/dist/evidence/baseline_governance.d.ts.map +1 -0
  17. package/dist/evidence/baseline_governance.js +113 -0
  18. package/dist/evidence/baseline_governance.js.map +1 -0
  19. package/dist/evidence/run_lineage.d.ts +66 -0
  20. package/dist/evidence/run_lineage.d.ts.map +1 -0
  21. package/dist/evidence/run_lineage.js +138 -0
  22. package/dist/evidence/run_lineage.js.map +1 -0
  23. package/dist/evidence/screenshot_diff.d.ts +6 -2
  24. package/dist/evidence/screenshot_diff.d.ts.map +1 -1
  25. package/dist/evidence/screenshot_diff.js +40 -3
  26. package/dist/evidence/screenshot_diff.js.map +1 -1
  27. package/dist/evidence/visual_attribution.d.ts +32 -0
  28. package/dist/evidence/visual_attribution.d.ts.map +1 -0
  29. package/dist/evidence/visual_attribution.js +88 -0
  30. package/dist/evidence/visual_attribution.js.map +1 -0
  31. package/dist/index.js +163 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/prompt_manager/index.d.ts +27 -0
  34. package/dist/prompt_manager/index.d.ts.map +1 -0
  35. package/dist/prompt_manager/index.js +28 -0
  36. package/dist/prompt_manager/index.js.map +1 -0
  37. package/dist/prompt_manager/state_machine.d.ts +55 -0
  38. package/dist/prompt_manager/state_machine.d.ts.map +1 -0
  39. package/dist/prompt_manager/state_machine.js +60 -0
  40. package/dist/prompt_manager/state_machine.js.map +1 -0
  41. package/dist/prompt_manager/templates.d.ts +43 -0
  42. package/dist/prompt_manager/templates.d.ts.map +1 -0
  43. package/dist/prompt_manager/templates.js +177 -0
  44. package/dist/prompt_manager/templates.js.map +1 -0
  45. package/dist/runners/base.d.ts +1 -1
  46. package/dist/runners/base.d.ts.map +1 -1
  47. package/dist/runners/base.js +2 -2
  48. package/dist/runners/base.js.map +1 -1
  49. package/dist/runners/figma_spec_generator.d.ts +54 -0
  50. package/dist/runners/figma_spec_generator.d.ts.map +1 -0
  51. package/dist/runners/figma_spec_generator.js +227 -0
  52. package/dist/runners/figma_spec_generator.js.map +1 -0
  53. package/dist/runners/generic.js +23 -20
  54. package/dist/runners/generic.js.map +1 -1
  55. package/dist/runners/playwright.d.ts +9 -1
  56. package/dist/runners/playwright.d.ts.map +1 -1
  57. package/dist/runners/playwright.js +18 -3
  58. package/dist/runners/playwright.js.map +1 -1
  59. package/dist/runners/plugin_sdk.d.ts +25 -0
  60. package/dist/runners/plugin_sdk.d.ts.map +1 -0
  61. package/dist/runners/plugin_sdk.js +86 -0
  62. package/dist/runners/plugin_sdk.js.map +1 -0
  63. package/dist/state/dependency_graph.d.ts +23 -0
  64. package/dist/state/dependency_graph.d.ts.map +1 -0
  65. package/dist/state/dependency_graph.js +127 -0
  66. package/dist/state/dependency_graph.js.map +1 -0
  67. package/dist/state/section_registry.d.ts.map +1 -1
  68. package/dist/state/section_registry.js +11 -5
  69. package/dist/state/section_registry.js.map +1 -1
  70. package/dist/tools/design_compare.js +2 -2
  71. package/dist/tools/design_compare.js.map +1 -1
  72. package/dist/tools/init-project.d.ts.map +1 -1
  73. package/dist/tools/init-project.js +40 -262
  74. package/dist/tools/init-project.js.map +1 -1
  75. package/dist/tools/update_baseline.d.ts +3 -0
  76. package/dist/tools/update_baseline.d.ts.map +1 -1
  77. package/dist/tools/update_baseline.js +18 -2
  78. package/dist/tools/update_baseline.js.map +1 -1
  79. package/dist/tools/verify.d.ts.map +1 -1
  80. package/dist/tools/verify.js +66 -4
  81. package/dist/tools/verify.js.map +1 -1
  82. package/dist/tools/visual_review.js +1 -1
  83. package/dist/tools/visual_review.js.map +1 -1
  84. package/package.json +28 -4
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Baseline governance — region masks, change logs, and approval tracking.
3
+ *
4
+ * Region masks let you exclude dynamic UI regions (timestamps, avatars,
5
+ * ads) from visual comparison by zeroing them out in both baseline and
6
+ * actual screenshots before pixelmatch runs.
7
+ *
8
+ * Change logs record every baseline update with the commit, timestamp,
9
+ * approval status, and which screens changed — enabling audit trails.
10
+ */
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, } from "fs";
12
+ import { join } from "path";
13
+ const MASK_FILE = ".codeloop/baseline_masks.json";
14
+ export function loadMasks(cwd) {
15
+ const p = join(cwd, MASK_FILE);
16
+ if (!existsSync(p))
17
+ return { masks: [] };
18
+ try {
19
+ const raw = JSON.parse(readFileSync(p, "utf-8"));
20
+ return Array.isArray(raw.masks) ? raw : { masks: [] };
21
+ }
22
+ catch {
23
+ return { masks: [] };
24
+ }
25
+ }
26
+ export function saveMasks(cwd, config) {
27
+ const p = join(cwd, MASK_FILE);
28
+ mkdirSync(join(cwd, ".codeloop"), { recursive: true });
29
+ writeFileSync(p, JSON.stringify(config, null, 2));
30
+ }
31
+ export function getMaskForScreen(config, screen) {
32
+ const entry = config.masks.find((m) => m.screen === screen || m.screen === screen.replace(/\.png$/i, ""));
33
+ return entry?.regions ?? [];
34
+ }
35
+ /**
36
+ * Zero out masked regions in an RGBA buffer. Modifies the buffer in-place.
37
+ * Used to neutralize dynamic regions before pixelmatch comparison.
38
+ *
39
+ * Coordinates are clamped to [0, width) x [0, height); negative origins and
40
+ * regions extending past the image bounds are silently truncated rather
41
+ * than corrupting buffer memory.
42
+ */
43
+ export function applyMaskToBuffer(data, width, height, regions) {
44
+ for (const r of regions) {
45
+ const minX = Math.max(0, Math.trunc(r.x));
46
+ const minY = Math.max(0, Math.trunc(r.y));
47
+ const maxX = Math.min(Math.trunc(r.x + r.width), width);
48
+ const maxY = Math.min(Math.trunc(r.y + r.height), height);
49
+ if (maxX <= minX || maxY <= minY)
50
+ continue;
51
+ for (let y = minY; y < maxY; y++) {
52
+ for (let x = minX; x < maxX; x++) {
53
+ const i = (y * width + x) * 4;
54
+ data[i] = 0;
55
+ data[i + 1] = 0;
56
+ data[i + 2] = 0;
57
+ data[i + 3] = 0;
58
+ }
59
+ }
60
+ }
61
+ }
62
+ const CHANGELOG_FILE = ".codeloop/baseline_changelog.json";
63
+ export function loadChangeLog(cwd) {
64
+ const p = join(cwd, CHANGELOG_FILE);
65
+ if (!existsSync(p))
66
+ return { version: 1, entries: [] };
67
+ try {
68
+ const raw = JSON.parse(readFileSync(p, "utf-8"));
69
+ return Array.isArray(raw.entries) ? raw : { version: 1, entries: [] };
70
+ }
71
+ catch {
72
+ return { version: 1, entries: [] };
73
+ }
74
+ }
75
+ export function saveChangeLog(cwd, log) {
76
+ const p = join(cwd, CHANGELOG_FILE);
77
+ mkdirSync(join(cwd, ".codeloop"), { recursive: true });
78
+ writeFileSync(p, JSON.stringify(log, null, 2));
79
+ }
80
+ export function recordBaselineChange(cwd, entry) {
81
+ const log = loadChangeLog(cwd);
82
+ const full = {
83
+ ...entry,
84
+ timestamp: new Date().toISOString(),
85
+ };
86
+ log.entries.push(full);
87
+ saveChangeLog(cwd, log);
88
+ return full;
89
+ }
90
+ /**
91
+ * Mark the most recent unapproved change as approved.
92
+ * Returns the approved entry or null if nothing to approve.
93
+ */
94
+ export function approveLatest(cwd, approver = "cli") {
95
+ const log = loadChangeLog(cwd);
96
+ const unapproved = log.entries.filter((e) => !e.approved);
97
+ if (unapproved.length === 0)
98
+ return null;
99
+ const target = unapproved[unapproved.length - 1];
100
+ target.approved = true;
101
+ target.approved_by = approver;
102
+ target.approved_at = new Date().toISOString();
103
+ saveChangeLog(cwd, log);
104
+ return target;
105
+ }
106
+ /**
107
+ * List pending (unapproved) baseline changes.
108
+ */
109
+ export function listPending(cwd) {
110
+ const log = loadChangeLog(cwd);
111
+ return log.entries.filter((e) => !e.approved);
112
+ }
113
+ //# sourceMappingURL=baseline_governance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline_governance.js","sourceRoot":"","sources":["../../src/evidence/baseline_governance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Run lineage — enriches every run with ancestry and Git context so that
3
+ * artifacts can be traced back to the commit, branch, and section that
4
+ * produced them.
5
+ *
6
+ * Also maintains `run_index.json` at the artifacts root for fast lookups
7
+ * without scanning every `meta.json` file.
8
+ */
9
+ export interface LineageFields {
10
+ parent_run_id?: string;
11
+ section_id?: string;
12
+ commit_sha?: string;
13
+ branch?: string;
14
+ trigger: "manual" | "verify" | "gate_check" | "integration_check" | "replan";
15
+ run_index: number;
16
+ }
17
+ export interface RunIndexEntry {
18
+ run_id: string;
19
+ started_at: string;
20
+ finished_at?: string;
21
+ trigger: string;
22
+ section_id?: string;
23
+ parent_run_id?: string;
24
+ commit_sha?: string;
25
+ branch?: string;
26
+ confidence?: number;
27
+ pass_count?: number;
28
+ fail_count?: number;
29
+ platform?: string;
30
+ }
31
+ export interface RunIndex {
32
+ version: 1;
33
+ entries: RunIndexEntry[];
34
+ updated_at: string;
35
+ }
36
+ export declare function captureGitContext(cwd: string): {
37
+ commit_sha?: string;
38
+ branch?: string;
39
+ };
40
+ export declare function loadRunIndex(artifactsBase: string): RunIndex;
41
+ export declare function saveRunIndex(artifactsBase: string, index: RunIndex): void;
42
+ export declare function appendToIndex(artifactsBase: string, entry: RunIndexEntry): RunIndex;
43
+ export declare function buildLineage(artifactsBase: string, opts: {
44
+ trigger: LineageFields["trigger"];
45
+ section_id?: string;
46
+ parent_run_id?: string;
47
+ cwd?: string;
48
+ }): LineageFields;
49
+ /**
50
+ * Rebuild `run_index.json` by scanning every `meta.json` in the artifacts base.
51
+ * Useful for migrating existing runs that pre-date the lineage feature.
52
+ */
53
+ export declare function rebuildIndex(artifactsBase: string): RunIndex;
54
+ export interface RunHistoryQuery {
55
+ section_id?: string;
56
+ branch?: string;
57
+ limit?: number;
58
+ since?: string;
59
+ }
60
+ export interface RunHistoryResult {
61
+ total: number;
62
+ returned: number;
63
+ entries: RunIndexEntry[];
64
+ }
65
+ export declare function queryRunHistory(artifactsBase: string, query?: RunHistoryQuery): RunHistoryResult;
66
+ //# sourceMappingURL=run_lineage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run_lineage.d.ts","sourceRoot":"","sources":["../../src/evidence/run_lineage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,WAAW,aAAa;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,mBAAmB,GAAG,QAAQ,CAAC;IAC7E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB;AAYD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAKvF;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAc5D;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAIzE;AAED,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,aAAa,GACnB,QAAQ,CAUV;AAED,wBAAgB,YAAY,CAC1B,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE;IACJ,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GACA,aAAa,CAWf;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAmC5D;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,eAAoB,GAC1B,gBAAgB,CA6BlB"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Run lineage — enriches every run with ancestry and Git context so that
3
+ * artifacts can be traced back to the commit, branch, and section that
4
+ * produced them.
5
+ *
6
+ * Also maintains `run_index.json` at the artifacts root for fast lookups
7
+ * without scanning every `meta.json` file.
8
+ */
9
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from "fs";
10
+ import { join } from "path";
11
+ import { execSync } from "child_process";
12
+ const INDEX_FILE = "run_index.json";
13
+ function gitField(cwd, cmd) {
14
+ try {
15
+ return execSync(cmd, { cwd, encoding: "utf-8", timeout: 3000 }).trim() || undefined;
16
+ }
17
+ catch {
18
+ return undefined;
19
+ }
20
+ }
21
+ export function captureGitContext(cwd) {
22
+ return {
23
+ commit_sha: gitField(cwd, "git rev-parse --short HEAD"),
24
+ branch: gitField(cwd, "git rev-parse --abbrev-ref HEAD"),
25
+ };
26
+ }
27
+ export function loadRunIndex(artifactsBase) {
28
+ const p = join(artifactsBase, INDEX_FILE);
29
+ if (!existsSync(p)) {
30
+ return { version: 1, entries: [], updated_at: new Date().toISOString() };
31
+ }
32
+ try {
33
+ const raw = JSON.parse(readFileSync(p, "utf-8"));
34
+ if (!Array.isArray(raw.entries)) {
35
+ return { version: 1, entries: [], updated_at: new Date().toISOString() };
36
+ }
37
+ return raw;
38
+ }
39
+ catch {
40
+ return { version: 1, entries: [], updated_at: new Date().toISOString() };
41
+ }
42
+ }
43
+ export function saveRunIndex(artifactsBase, index) {
44
+ mkdirSync(artifactsBase, { recursive: true });
45
+ index.updated_at = new Date().toISOString();
46
+ writeFileSync(join(artifactsBase, INDEX_FILE), JSON.stringify(index, null, 2));
47
+ }
48
+ export function appendToIndex(artifactsBase, entry) {
49
+ const index = loadRunIndex(artifactsBase);
50
+ const existing = index.entries.findIndex((e) => e.run_id === entry.run_id);
51
+ if (existing >= 0) {
52
+ index.entries[existing] = { ...index.entries[existing], ...entry };
53
+ }
54
+ else {
55
+ index.entries.push(entry);
56
+ }
57
+ saveRunIndex(artifactsBase, index);
58
+ return index;
59
+ }
60
+ export function buildLineage(artifactsBase, opts) {
61
+ const index = loadRunIndex(artifactsBase);
62
+ const git = captureGitContext(opts.cwd ?? process.cwd());
63
+ return {
64
+ parent_run_id: opts.parent_run_id,
65
+ section_id: opts.section_id,
66
+ commit_sha: git.commit_sha,
67
+ branch: git.branch,
68
+ trigger: opts.trigger,
69
+ run_index: index.entries.length + 1,
70
+ };
71
+ }
72
+ /**
73
+ * Rebuild `run_index.json` by scanning every `meta.json` in the artifacts base.
74
+ * Useful for migrating existing runs that pre-date the lineage feature.
75
+ */
76
+ export function rebuildIndex(artifactsBase) {
77
+ const entries = [];
78
+ if (!existsSync(artifactsBase)) {
79
+ return { version: 1, entries, updated_at: new Date().toISOString() };
80
+ }
81
+ const dirs = readdirSync(artifactsBase, { withFileTypes: true })
82
+ .filter((d) => d.isDirectory() && d.name.startsWith("run_"))
83
+ .map((d) => d.name)
84
+ .sort();
85
+ for (const dir of dirs) {
86
+ const metaPath = join(artifactsBase, dir, "meta.json");
87
+ if (!existsSync(metaPath))
88
+ continue;
89
+ try {
90
+ const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
91
+ entries.push({
92
+ run_id: meta.run_id ?? dir,
93
+ started_at: meta.started_at ?? "",
94
+ finished_at: meta.finished_at,
95
+ trigger: meta.lineage?.trigger ?? "manual",
96
+ section_id: meta.lineage?.section_id ?? meta.section_id,
97
+ parent_run_id: meta.lineage?.parent_run_id,
98
+ commit_sha: meta.lineage?.commit_sha,
99
+ branch: meta.lineage?.branch,
100
+ confidence: meta.confidence,
101
+ pass_count: meta.test_summary?.passed ?? meta.deterministic_results?.tests?.passed,
102
+ fail_count: meta.test_summary?.failed ?? meta.deterministic_results?.tests?.failed,
103
+ platform: meta.platform,
104
+ });
105
+ }
106
+ catch { /* skip corrupt metas */ }
107
+ }
108
+ const index = { version: 1, entries, updated_at: new Date().toISOString() };
109
+ saveRunIndex(artifactsBase, index);
110
+ return index;
111
+ }
112
+ export function queryRunHistory(artifactsBase, query = {}) {
113
+ const index = loadRunIndex(artifactsBase);
114
+ let filtered = index.entries;
115
+ if (query.section_id) {
116
+ filtered = filtered.filter((e) => e.section_id === query.section_id);
117
+ }
118
+ if (query.branch) {
119
+ filtered = filtered.filter((e) => e.branch === query.branch);
120
+ }
121
+ if (query.since) {
122
+ const since = new Date(query.since).getTime();
123
+ filtered = filtered.filter((e) => {
124
+ const t = new Date(e.started_at).getTime();
125
+ return !isNaN(t) && t >= since;
126
+ });
127
+ }
128
+ filtered.sort((a, b) => {
129
+ const ta = new Date(a.started_at).getTime();
130
+ const tb = new Date(b.started_at).getTime();
131
+ return tb - ta;
132
+ });
133
+ const limit = query.limit ?? 50;
134
+ const total = filtered.length;
135
+ const entries = filtered.slice(0, limit);
136
+ return { total, returned: entries.length, entries };
137
+ }
138
+ //# sourceMappingURL=run_lineage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run_lineage.js","sourceRoot":"","sources":["../../src/evidence/run_lineage.ts"],"names":[],"mappings":"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"}
@@ -15,13 +15,17 @@ export interface ScreenshotDiffSummary {
15
15
  /**
16
16
  * Compare two PNG images pixel-by-pixel.
17
17
  * Size mismatches are handled by padding both images to the larger width and height (white fill), then comparing.
18
+ *
19
+ * When `cwd` is provided, region masks defined in `.codeloop/baseline_masks.json`
20
+ * for the matching screen are applied to BOTH buffers before pixelmatch.
21
+ * This neutralises dynamic UI regions (timestamps, avatars, ads).
18
22
  */
19
- export declare function compareScreenshot(screenshotPath: string, baselinePath: string, diffOutputPath: string, threshold?: number): DiffResult;
23
+ export declare function compareScreenshot(screenshotPath: string, baselinePath: string, diffOutputPath: string, threshold?: number, cwd?: string): DiffResult;
20
24
  /**
21
25
  * Compare every PNG in `screenshotsDir` to the same filename under `baselinesDir`.
22
26
  * Writes diff images to `diffsDir` using the same base filename.
23
27
  */
24
- export declare function diffAllScreenshots(screenshotsDir: string, baselinesDir: string, diffsDir: string, threshold?: number): ScreenshotDiffSummary;
28
+ export declare function diffAllScreenshots(screenshotsDir: string, baselinesDir: string, diffsDir: string, threshold?: number, cwd?: string): ScreenshotDiffSummary;
25
29
  /**
26
30
  * Copy current screenshots into `baselinesDir` (creates the directory if needed).
27
31
  * If `screens` is set, only those screen names (without `.png`) are updated; otherwise all PNGs are copied.
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot_diff.d.ts","sourceRoot":"","sources":["../../src/evidence/screenshot_diff.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,OAAO,CAAC;IACxB,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAqDD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EACtB,SAAS,GAAE,MAA0B,GACpC,UAAU,CAmCZ;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAA0B,GACpC,qBAAqB,CA+CvB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,MAAM,EAAE,CAwBV"}
1
+ {"version":3,"file":"screenshot_diff.d.ts","sourceRoot":"","sources":["../../src/evidence/screenshot_diff.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,OAAO,CAAC;IACxB,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAqDD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EACtB,SAAS,GAAE,MAA0B,EACrC,GAAG,CAAC,EAAE,MAAM,GACX,UAAU,CAoEZ;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAA0B,EACrC,GAAG,CAAC,EAAE,MAAM,GACX,qBAAqB,CA+CvB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,MAAM,EAAE,CAwBV"}
@@ -2,6 +2,7 @@ import pixelmatch from "pixelmatch";
2
2
  import { PNG } from "pngjs";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, } from "fs";
4
4
  import { join, basename, dirname } from "path";
5
+ import { loadMasks, getMaskForScreen, applyMaskToBuffer, } from "./baseline_governance.js";
5
6
  const DEFAULT_THRESHOLD = 0.1;
6
7
  function screenNameFromPath(filePath) {
7
8
  const base = basename(filePath);
@@ -42,8 +43,12 @@ function prepareCompareBuffers(a, b) {
42
43
  /**
43
44
  * Compare two PNG images pixel-by-pixel.
44
45
  * Size mismatches are handled by padding both images to the larger width and height (white fill), then comparing.
46
+ *
47
+ * When `cwd` is provided, region masks defined in `.codeloop/baseline_masks.json`
48
+ * for the matching screen are applied to BOTH buffers before pixelmatch.
49
+ * This neutralises dynamic UI regions (timestamps, avatars, ads).
45
50
  */
46
- export function compareScreenshot(screenshotPath, baselinePath, diffOutputPath, threshold = DEFAULT_THRESHOLD) {
51
+ export function compareScreenshot(screenshotPath, baselinePath, diffOutputPath, threshold = DEFAULT_THRESHOLD, cwd) {
47
52
  const screen = screenNameFromPath(screenshotPath);
48
53
  if (!existsSync(screenshotPath) || !existsSync(baselinePath)) {
49
54
  throw new Error(`compareScreenshot: missing file(s) — screenshot: ${screenshotPath}, baseline: ${baselinePath}`);
@@ -53,6 +58,38 @@ export function compareScreenshot(screenshotPath, baselinePath, diffOutputPath,
53
58
  const { width, height, buf1, buf2 } = prepareCompareBuffers(img1, img2);
54
59
  const totalPixels = width * height;
55
60
  mkdirSync(dirname(diffOutputPath), { recursive: true });
61
+ // Apply region masks (if any) to both buffers before pixel matching.
62
+ // Both buffers MUST be masked identically so pixelmatch sees zeros on
63
+ // both sides of any dynamic region and never counts them as diffs.
64
+ if (cwd) {
65
+ const masks = loadMasks(cwd);
66
+ const regions = getMaskForScreen(masks, screen);
67
+ if (regions.length > 0) {
68
+ // Buffers from prepareCompareBuffers may share memory with the source
69
+ // PNG.data when no padding was needed; clone first to avoid mutating
70
+ // the original images for downstream consumers.
71
+ const safeBuf1 = Buffer.from(buf1);
72
+ const safeBuf2 = Buffer.from(buf2);
73
+ applyMaskToBuffer(safeBuf1, width, height, regions);
74
+ applyMaskToBuffer(safeBuf2, width, height, regions);
75
+ const diff = Buffer.alloc(totalPixels * 4);
76
+ const diffPixels = pixelmatch(safeBuf1, safeBuf2, diff, width, height, {
77
+ threshold,
78
+ });
79
+ const outPng = new PNG({ width, height });
80
+ diff.copy(outPng.data);
81
+ writeFileSync(diffOutputPath, PNG.sync.write(outPng));
82
+ const diffScore = totalPixels === 0 ? 0 : diffPixels / totalPixels;
83
+ return {
84
+ screen,
85
+ diffScore,
86
+ diffPixels,
87
+ totalPixels,
88
+ diffImagePath: diffOutputPath,
89
+ baselineExists: true,
90
+ };
91
+ }
92
+ }
56
93
  const diff = Buffer.alloc(totalPixels * 4);
57
94
  const diffPixels = pixelmatch(buf1, buf2, diff, width, height, {
58
95
  threshold,
@@ -74,7 +111,7 @@ export function compareScreenshot(screenshotPath, baselinePath, diffOutputPath,
74
111
  * Compare every PNG in `screenshotsDir` to the same filename under `baselinesDir`.
75
112
  * Writes diff images to `diffsDir` using the same base filename.
76
113
  */
77
- export function diffAllScreenshots(screenshotsDir, baselinesDir, diffsDir, threshold = DEFAULT_THRESHOLD) {
114
+ export function diffAllScreenshots(screenshotsDir, baselinesDir, diffsDir, threshold = DEFAULT_THRESHOLD, cwd) {
78
115
  const pngFiles = listPngFiles(screenshotsDir);
79
116
  const results = [];
80
117
  let allHaveBaselines = true;
@@ -104,7 +141,7 @@ export function diffAllScreenshots(screenshotsDir, baselinesDir, diffsDir, thres
104
141
  continue;
105
142
  }
106
143
  const diffOutputPath = join(diffsDir, file);
107
- const result = compareScreenshot(screenshotPath, baselinePath, diffOutputPath, threshold);
144
+ const result = compareScreenshot(screenshotPath, baselinePath, diffOutputPath, threshold, cwd);
108
145
  results.push(result);
109
146
  }
110
147
  const screensAboveThreshold = results
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot_diff.js","sourceRoot":"","sources":["../../src/evidence/screenshot_diff.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screenshot_diff.js","sourceRoot":"","sources":["../../src/evidence/screenshot_diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Visual change attribution — maps visual diffs to the commit, branch,
3
+ * and section that introduced them. Uses the run lineage index to
4
+ * correlate diff scores across consecutive runs.
5
+ */
6
+ export interface VisualAttribution {
7
+ screen: string;
8
+ diff_score: number;
9
+ introduced_by: {
10
+ run_id: string;
11
+ commit_sha?: string;
12
+ branch?: string;
13
+ section_id?: string;
14
+ started_at: string;
15
+ };
16
+ previous_run_id?: string;
17
+ previous_diff_score?: number;
18
+ }
19
+ export interface AttributionReport {
20
+ attributions: VisualAttribution[];
21
+ total_screens: number;
22
+ screens_with_changes: number;
23
+ }
24
+ /**
25
+ * Build a visual attribution report by comparing diff scores across
26
+ * consecutive runs in the index.
27
+ */
28
+ export declare function buildAttribution(artifactsBase: string, opts?: {
29
+ section_id?: string;
30
+ limit?: number;
31
+ }): AttributionReport;
32
+ //# sourceMappingURL=visual_attribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual_attribution.d.ts","sourceRoot":"","sources":["../../src/evidence/visual_attribution.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE;QACb,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAiCD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,IAAI,GAAE;IACJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,GACL,iBAAiB,CAuDnB"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Visual change attribution — maps visual diffs to the commit, branch,
3
+ * and section that introduced them. Uses the run lineage index to
4
+ * correlate diff scores across consecutive runs.
5
+ */
6
+ import { loadRunIndex } from "./run_lineage.js";
7
+ import { existsSync, readFileSync, readdirSync } from "fs";
8
+ import { join, basename } from "path";
9
+ function loadDiffScores(runDir) {
10
+ const scores = new Map();
11
+ const diffsDir = join(runDir, "diffs");
12
+ if (!existsSync(diffsDir))
13
+ return scores;
14
+ const summaryPath = join(runDir, "design_compare_summary.json");
15
+ if (existsSync(summaryPath)) {
16
+ try {
17
+ const summary = JSON.parse(readFileSync(summaryPath, "utf-8"));
18
+ if (Array.isArray(summary.per_screen)) {
19
+ for (const s of summary.per_screen) {
20
+ if (s.screen && typeof s.score === "number") {
21
+ scores.set(s.screen, 1 - s.score);
22
+ }
23
+ }
24
+ }
25
+ return scores;
26
+ }
27
+ catch { /* fall through to PNG scanning */ }
28
+ }
29
+ try {
30
+ const files = readdirSync(diffsDir).filter((f) => f.endsWith(".png"));
31
+ for (const f of files) {
32
+ const screen = basename(f, ".png");
33
+ scores.set(screen, -1);
34
+ }
35
+ }
36
+ catch { /* empty */ }
37
+ return scores;
38
+ }
39
+ /**
40
+ * Build a visual attribution report by comparing diff scores across
41
+ * consecutive runs in the index.
42
+ */
43
+ export function buildAttribution(artifactsBase, opts = {}) {
44
+ const index = loadRunIndex(artifactsBase);
45
+ let entries = [...index.entries].sort((a, b) => new Date(a.started_at).getTime() - new Date(b.started_at).getTime());
46
+ if (opts.section_id) {
47
+ entries = entries.filter((e) => e.section_id === opts.section_id);
48
+ }
49
+ const attributions = [];
50
+ const allScreens = new Set();
51
+ for (let i = 0; i < entries.length; i++) {
52
+ const entry = entries[i];
53
+ const runDir = join(artifactsBase, entry.run_id);
54
+ const current = loadDiffScores(runDir);
55
+ let previous = new Map();
56
+ if (i > 0) {
57
+ const prevDir = join(artifactsBase, entries[i - 1].run_id);
58
+ previous = loadDiffScores(prevDir);
59
+ }
60
+ for (const [screen, score] of current.entries()) {
61
+ allScreens.add(screen);
62
+ const prevScore = previous.get(screen);
63
+ if (score > 0 && (prevScore === undefined || prevScore === 0 || score > prevScore * 1.1)) {
64
+ attributions.push({
65
+ screen,
66
+ diff_score: score,
67
+ introduced_by: {
68
+ run_id: entry.run_id,
69
+ commit_sha: entry.commit_sha,
70
+ branch: entry.branch,
71
+ section_id: entry.section_id,
72
+ started_at: entry.started_at,
73
+ },
74
+ previous_run_id: i > 0 ? entries[i - 1].run_id : undefined,
75
+ previous_diff_score: prevScore,
76
+ });
77
+ }
78
+ }
79
+ }
80
+ const limit = opts.limit ?? 50;
81
+ attributions.sort((a, b) => b.diff_score - a.diff_score);
82
+ return {
83
+ attributions: attributions.slice(0, limit),
84
+ total_screens: allScreens.size,
85
+ screens_with_changes: new Set(attributions.map((a) => a.screen)).size,
86
+ };
87
+ }
88
+ //# sourceMappingURL=visual_attribution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual_attribution.js","sourceRoot":"","sources":["../../src/evidence/visual_attribution.ts"],"names":[],"mappings":"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"}