@neonwatty/limner 0.1.0 → 0.1.2

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 (54) hide show
  1. package/README.md +53 -39
  2. package/dist/cli.js +3 -3
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/compare-image-reference.d.ts +3 -0
  5. package/dist/commands/compare-image-reference.js +26 -3
  6. package/dist/commands/compare-image-reference.js.map +1 -1
  7. package/dist/commands/compare.d.ts +4 -1
  8. package/dist/commands/compare.js +72 -45
  9. package/dist/commands/compare.js.map +1 -1
  10. package/dist/core/agent-comparison-fingerprint.d.ts +12 -0
  11. package/dist/core/agent-comparison-fingerprint.js +27 -0
  12. package/dist/core/agent-comparison-fingerprint.js.map +1 -0
  13. package/dist/core/agent-comparison-pack.d.ts +30 -0
  14. package/dist/core/agent-comparison-pack.js +148 -0
  15. package/dist/core/agent-comparison-pack.js.map +1 -0
  16. package/dist/core/agent-comparison-profiles.d.ts +9 -0
  17. package/dist/core/agent-comparison-profiles.js +19 -0
  18. package/dist/core/agent-comparison-profiles.js.map +1 -0
  19. package/dist/core/agent-comparison-prompts.d.ts +22 -0
  20. package/dist/core/agent-comparison-prompts.js +84 -0
  21. package/dist/core/agent-comparison-prompts.js.map +1 -0
  22. package/dist/core/agent-comparison-report.d.ts +3 -0
  23. package/dist/core/agent-comparison-report.js +56 -0
  24. package/dist/core/agent-comparison-report.js.map +1 -0
  25. package/dist/core/agent-comparison-validation.d.ts +2 -0
  26. package/dist/core/agent-comparison-validation.js +8 -0
  27. package/dist/core/agent-comparison-validation.js.map +1 -0
  28. package/dist/core/playwright-capture.js +1 -1
  29. package/dist/core/playwright-capture.js.map +1 -1
  30. package/dist/core/report-writer.d.ts +7 -11
  31. package/dist/core/report-writer.js +21 -43
  32. package/dist/core/report-writer.js.map +1 -1
  33. package/dist/core/visual-spec-prompts.js +2 -2
  34. package/dist/core/visual-spec-prompts.js.map +1 -1
  35. package/dist/core/workspace.d.ts +1 -0
  36. package/dist/core/workspace.js +13 -2
  37. package/dist/core/workspace.js.map +1 -1
  38. package/dist/index.d.ts +4 -2
  39. package/dist/index.js +3 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/schemas/comparison.d.ts +349 -0
  42. package/dist/schemas/comparison.js +211 -0
  43. package/dist/schemas/comparison.js.map +1 -0
  44. package/dist/schemas/visual-spec.d.ts +8 -8
  45. package/dist/schemas/visual-spec.js +2 -2
  46. package/dist/schemas/visual-spec.js.map +1 -1
  47. package/docs/agent-workflow.md +20 -33
  48. package/docs/superpowers/plans/2026-06-12-agent-comparison-scores.md +209 -0
  49. package/package.json +2 -2
  50. package/skills/limner/SKILL.md +18 -21
  51. package/templates/target/AGENT_GUIDE.md +13 -9
  52. package/dist/commands/compare-image-app.d.ts +0 -12
  53. package/dist/commands/compare-image-app.js +0 -45
  54. package/dist/commands/compare-image-app.js.map +0 -1
@@ -0,0 +1,30 @@
1
+ import type { AgentComparisonProfileName } from './agent-comparison-profiles.js';
2
+ export type AgentComparisonPackResult = {
3
+ promptPath: string;
4
+ codexPromptPath: string;
5
+ claudePromptPath: string;
6
+ schemaPath: string;
7
+ examplePath: string;
8
+ responsePath: string;
9
+ status: 'awaiting-agent' | 'validated' | 'invalid';
10
+ validationErrors?: string[];
11
+ imageComparisonPath?: string;
12
+ structureComparisonPath?: string;
13
+ summaryPath?: string;
14
+ scoreHighlights?: string[];
15
+ };
16
+ export declare function writeAgentComparisonPack(input: {
17
+ workspaceRoot: string;
18
+ captureDir: string;
19
+ mode: 'image-reference' | 'reference-implementation';
20
+ profile: AgentComparisonProfileName;
21
+ imageInputs: {
22
+ expectedImagePath: string;
23
+ actualImagePath: string;
24
+ };
25
+ structureInputs?: {
26
+ expectedStructurePath: string;
27
+ actualStructurePath: string;
28
+ structureKind: 'visual-spec' | 'dom-metrics' | 'html' | 'json-description';
29
+ };
30
+ }): Promise<AgentComparisonPackResult>;
@@ -0,0 +1,148 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { agentComparisonBundleSchema, createAgentComparisonBundleExample, createAgentComparisonJsonSchema, } from '../schemas/comparison.js';
4
+ import { clearAgentComparisonOutputs, createInputFingerprint } from './agent-comparison-fingerprint.js';
5
+ import { buildAgentComparisonPrompt, buildClaudeAgentComparisonPrompt, buildCodexAgentComparisonPrompt, } from './agent-comparison-prompts.js';
6
+ import { validateFixTargets } from './agent-comparison-validation.js';
7
+ export async function writeAgentComparisonPack(input) {
8
+ const packDir = path.join(input.captureDir, 'agent-comparison');
9
+ const promptPath = path.join(packDir, 'agent-prompt.md');
10
+ const codexPromptPath = path.join(packDir, 'agent-prompt.codex.md');
11
+ const claudePromptPath = path.join(packDir, 'agent-prompt.claude.md');
12
+ const schemaPath = path.join(packDir, 'agent-response.schema.json');
13
+ const examplePath = path.join(packDir, 'agent-response.example.json');
14
+ const responsePath = path.join(packDir, 'agent-response.json');
15
+ const inputFingerprint = await createInputFingerprint(input);
16
+ const promptInput = { ...input, schemaPath, responsePath, inputFingerprint };
17
+ await mkdir(packDir, { recursive: true });
18
+ await clearAgentComparisonOutputs(input.captureDir);
19
+ await Promise.all([
20
+ writeJson(schemaPath, createAgentComparisonJsonSchema(input.structureInputs ? 'image-and-structure' : 'image-only')),
21
+ writeJson(examplePath, createExample(input, inputFingerprint)),
22
+ writeFile(promptPath, buildAgentComparisonPrompt(promptInput)),
23
+ writeFile(codexPromptPath, buildCodexAgentComparisonPrompt(promptInput)),
24
+ writeFile(claudePromptPath, buildClaudeAgentComparisonPrompt(promptInput)),
25
+ ]);
26
+ const response = await readBundle(responsePath);
27
+ const base = { promptPath, codexPromptPath, claudePromptPath, schemaPath, examplePath, responsePath };
28
+ if (response.status === 'missing') {
29
+ return { ...base, status: 'awaiting-agent' };
30
+ }
31
+ if (response.status === 'invalid-json') {
32
+ return { ...base, status: 'invalid', validationErrors: ['agent-response.json: Invalid JSON.'] };
33
+ }
34
+ const parsed = agentComparisonBundleSchema.safeParse(response.value);
35
+ if (!parsed.success) {
36
+ return { ...base, status: 'invalid', validationErrors: parsed.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`) };
37
+ }
38
+ const activeErrors = validateActiveComparison({ ...input, inputFingerprint }, parsed.data);
39
+ if (activeErrors.length > 0) {
40
+ return { ...base, status: 'invalid', validationErrors: activeErrors };
41
+ }
42
+ const imageComparisonPath = path.join(input.captureDir, 'image-comparison.json');
43
+ const structureComparisonPath = parsed.data.structureComparison ? path.join(input.captureDir, 'structure-comparison.json') : undefined;
44
+ const summaryPath = path.join(input.captureDir, 'comparison-summary.json');
45
+ await writeJson(imageComparisonPath, parsed.data.imageComparison);
46
+ if (parsed.data.structureComparison && structureComparisonPath) {
47
+ await writeJson(structureComparisonPath, parsed.data.structureComparison);
48
+ }
49
+ await writeJson(summaryPath, parsed.data);
50
+ return {
51
+ ...base,
52
+ status: 'validated',
53
+ imageComparisonPath,
54
+ structureComparisonPath,
55
+ summaryPath,
56
+ scoreHighlights: [
57
+ `Agent image score: ${parsed.data.imageComparison.score.overall}`,
58
+ ...(parsed.data.structureComparison ? [`Agent structure score: ${parsed.data.structureComparison.score.overall}`] : []),
59
+ ],
60
+ };
61
+ }
62
+ async function readBundle(filePath) {
63
+ try {
64
+ return { status: 'ok', value: JSON.parse(await readFile(filePath, 'utf8')) };
65
+ }
66
+ catch {
67
+ try {
68
+ await readFile(filePath, 'utf8');
69
+ return { status: 'invalid-json' };
70
+ }
71
+ catch {
72
+ return { status: 'missing' };
73
+ }
74
+ }
75
+ }
76
+ function createExample(input, inputFingerprint) {
77
+ const example = createAgentComparisonBundleExample(input.structureInputs ? 'image-and-structure' : 'image-only', inputFingerprint);
78
+ example.profile = input.profile;
79
+ example.imageComparison.inputs = {
80
+ expectedImagePath: relative(input.workspaceRoot, input.imageInputs.expectedImagePath),
81
+ actualImagePath: relative(input.workspaceRoot, input.imageInputs.actualImagePath),
82
+ };
83
+ if (input.structureInputs && example.structureComparison) {
84
+ example.structureComparison.inputs = {
85
+ expectedStructurePath: relative(input.workspaceRoot, input.structureInputs.expectedStructurePath),
86
+ actualStructurePath: relative(input.workspaceRoot, input.structureInputs.actualStructurePath),
87
+ structureKind: input.structureInputs.structureKind,
88
+ };
89
+ }
90
+ return example;
91
+ }
92
+ function validateActiveComparison(input, bundle) {
93
+ const errors = [];
94
+ if (bundle.profile !== input.profile)
95
+ errors.push(`profile: Expected ${input.profile}.`);
96
+ const expectedSurface = input.profile === 'ideal-to-mockup' ? 'mockup' : 'implementation';
97
+ if (!matchesPath(input.workspaceRoot, input.imageInputs.expectedImagePath, bundle.imageComparison.inputs.expectedImagePath)) {
98
+ errors.push('imageComparison.inputs.expectedImagePath: Does not match the active expected image.');
99
+ }
100
+ if (!matchesPath(input.workspaceRoot, input.imageInputs.actualImagePath, bundle.imageComparison.inputs.actualImagePath)) {
101
+ errors.push('imageComparison.inputs.actualImagePath: Does not match the active actual image.');
102
+ }
103
+ if (bundle.inputFingerprint.expectedImageSha256 !== input.inputFingerprint.expectedImageSha256) {
104
+ errors.push('inputFingerprint.expectedImageSha256: Does not match the active expected image.');
105
+ }
106
+ if (bundle.inputFingerprint.actualImageSha256 !== input.inputFingerprint.actualImageSha256) {
107
+ errors.push('inputFingerprint.actualImageSha256: Does not match the active actual image.');
108
+ }
109
+ validateFixTargets(errors, 'imageComparison', bundle.imageComparison.majorDiffAreas, expectedSurface);
110
+ if (!input.structureInputs && bundle.structureComparison) {
111
+ errors.push('structureComparison: Not expected for this comparison.');
112
+ }
113
+ if (!input.structureInputs && (bundle.inputFingerprint.expectedStructureSha256 || bundle.inputFingerprint.actualStructureSha256)) {
114
+ errors.push('inputFingerprint: Structure hashes are not expected for this comparison.');
115
+ }
116
+ if (input.structureInputs && !bundle.structureComparison) {
117
+ errors.push('structureComparison: Required for this comparison.');
118
+ }
119
+ if (input.structureInputs && bundle.structureComparison) {
120
+ validateFixTargets(errors, 'structureComparison', bundle.structureComparison.majorDiffAreas, expectedSurface);
121
+ if (bundle.inputFingerprint.expectedStructureSha256 !== input.inputFingerprint.expectedStructureSha256) {
122
+ errors.push('inputFingerprint.expectedStructureSha256: Does not match the active expected structure.');
123
+ }
124
+ if (bundle.inputFingerprint.actualStructureSha256 !== input.inputFingerprint.actualStructureSha256) {
125
+ errors.push('inputFingerprint.actualStructureSha256: Does not match the active actual structure.');
126
+ }
127
+ if (!matchesPath(input.workspaceRoot, input.structureInputs.expectedStructurePath, bundle.structureComparison.inputs.expectedStructurePath)) {
128
+ errors.push('structureComparison.inputs.expectedStructurePath: Does not match the active expected structure.');
129
+ }
130
+ if (!matchesPath(input.workspaceRoot, input.structureInputs.actualStructurePath, bundle.structureComparison.inputs.actualStructurePath)) {
131
+ errors.push('structureComparison.inputs.actualStructurePath: Does not match the active actual structure.');
132
+ }
133
+ if (bundle.structureComparison.inputs.structureKind !== input.structureInputs.structureKind) {
134
+ errors.push(`structureComparison.inputs.structureKind: Expected ${input.structureInputs.structureKind}.`);
135
+ }
136
+ }
137
+ return errors;
138
+ }
139
+ function matchesPath(root, expectedAbsolute, actual) {
140
+ return actual === expectedAbsolute || actual === relative(root, expectedAbsolute);
141
+ }
142
+ function relative(root, filePath) {
143
+ return path.relative(root, filePath);
144
+ }
145
+ async function writeJson(filePath, value) {
146
+ await writeFile(filePath, JSON.stringify(value, null, 2) + '\n');
147
+ }
148
+ //# sourceMappingURL=agent-comparison-pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-comparison-pack.js","sourceRoot":"","sources":["../../src/core/agent-comparison-pack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type AgentComparisonProfileName = 'ideal-to-mockup' | 'mockup-to-implementation';
2
+ export type AgentComparisonProfile = {
3
+ name: AgentComparisonProfileName;
4
+ expectedRole: string;
5
+ actualRole: string;
6
+ editableSurface: 'mockup' | 'implementation';
7
+ instruction: string;
8
+ };
9
+ export declare function getAgentComparisonProfile(name: AgentComparisonProfileName): AgentComparisonProfile;
@@ -0,0 +1,19 @@
1
+ export function getAgentComparisonProfile(name) {
2
+ if (name === 'ideal-to-mockup') {
3
+ return {
4
+ name,
5
+ expectedRole: 'ideal image',
6
+ actualRole: 'HTML mockup screenshot',
7
+ editableSurface: 'mockup',
8
+ instruction: 'Focus on changes to the HTML/CSS mockup that will make the next screenshot closer to the ideal.',
9
+ };
10
+ }
11
+ return {
12
+ name,
13
+ expectedRole: 'approved HTML mockup screenshot',
14
+ actualRole: 'implementation screenshot',
15
+ editableSurface: 'implementation',
16
+ instruction: 'Treat the mockup as canonical and focus on implementation changes that will match it.',
17
+ };
18
+ }
19
+ //# sourceMappingURL=agent-comparison-profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-comparison-profiles.js","sourceRoot":"","sources":["../../src/core/agent-comparison-profiles.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,yBAAyB,CAAC,IAAgC;IACxE,IAAI,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC/B,OAAO;YACL,IAAI;YACJ,YAAY,EAAE,aAAa;YAC3B,UAAU,EAAE,wBAAwB;YACpC,eAAe,EAAE,QAAQ;YACzB,WAAW,EAAE,iGAAiG;SAC/G,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI;QACJ,YAAY,EAAE,iCAAiC;QAC/C,UAAU,EAAE,2BAA2B;QACvC,eAAe,EAAE,gBAAgB;QACjC,WAAW,EAAE,uFAAuF;KACrG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { AgentComparisonInputFingerprint } from '../schemas/comparison.js';
2
+ import { type AgentComparisonProfileName } from './agent-comparison-profiles.js';
3
+ type PromptInput = {
4
+ workspaceRoot: string;
5
+ profile: AgentComparisonProfileName;
6
+ responsePath: string;
7
+ schemaPath: string;
8
+ inputFingerprint: AgentComparisonInputFingerprint;
9
+ imageInputs: {
10
+ expectedImagePath: string;
11
+ actualImagePath: string;
12
+ };
13
+ structureInputs?: {
14
+ expectedStructurePath: string;
15
+ actualStructurePath: string;
16
+ structureKind: 'visual-spec' | 'dom-metrics' | 'html' | 'json-description';
17
+ };
18
+ };
19
+ export declare function buildAgentComparisonPrompt(input: PromptInput): string;
20
+ export declare function buildCodexAgentComparisonPrompt(input: PromptInput): string;
21
+ export declare function buildClaudeAgentComparisonPrompt(input: PromptInput): string;
22
+ export {};
@@ -0,0 +1,84 @@
1
+ import path from 'node:path';
2
+ import { getAgentComparisonProfile } from './agent-comparison-profiles.js';
3
+ export function buildAgentComparisonPrompt(input) {
4
+ const profile = getAgentComparisonProfile(input.profile);
5
+ return `# Limner Agent UX Comparison
6
+
7
+ Profile: \`${profile.name}\`
8
+ Expected role: ${profile.expectedRole}
9
+ Actual role: ${profile.actualRole}
10
+ Editable surface: ${profile.editableSurface}
11
+
12
+ ${profile.instruction}
13
+
14
+ ## Image Inputs
15
+
16
+ - Expected image: \`${rel(input, input.imageInputs.expectedImagePath)}\`
17
+ - Actual image: \`${rel(input, input.imageInputs.actualImagePath)}\`
18
+
19
+ ## Input Fingerprint
20
+
21
+ Copy these values exactly into \`inputFingerprint\`. Limner uses them to reject stale responses after screenshots or structure files change.
22
+
23
+ - \`expectedImageSha256\`: \`${input.inputFingerprint.expectedImageSha256}\`
24
+ - \`actualImageSha256\`: \`${input.inputFingerprint.actualImageSha256}\`
25
+ ${formatStructureFingerprint(input.inputFingerprint)}
26
+
27
+ Inspect the expected and actual images separately before using side-by-side context.
28
+ Focus on the next implementation iteration, not exhaustive critique.
29
+ Name only high-signal UX diffs: layout, hierarchy, typography, color, spacing, component anatomy, copy/state, and attention flow.
30
+ ${formatStructureSection(input)}
31
+ Use this JSON schema:
32
+
33
+ - \`${rel(input, input.schemaPath)}\`
34
+
35
+ Write valid JSON only to:
36
+
37
+ - \`${rel(input, input.responsePath)}\`
38
+
39
+ Rules:
40
+
41
+ - Scores are 0..1 estimates, not pass/fail judgments.
42
+ - Do not invent diffs when the artifacts match; empty \`majorDiffAreas\` and empty next steps are valid.
43
+ - Every diff must include evidence, impact, and a recommended change for the editable surface.
44
+ - Use \`systemFeedback\` only for issues with Limner inputs, missing context, or prompt/tooling quality.
45
+ `;
46
+ }
47
+ export function buildCodexAgentComparisonPrompt(input) {
48
+ return `${buildAgentComparisonPrompt(input)}
49
+ ## Codex Instructions
50
+
51
+ Read the local files directly. Create or overwrite \`${rel(input, input.responsePath)}\` with JSON matching the schema.
52
+ `;
53
+ }
54
+ export function buildClaudeAgentComparisonPrompt(input) {
55
+ return `${buildAgentComparisonPrompt(input)}
56
+ ## Claude Instructions
57
+
58
+ Attach the referenced images, inspect them separately, and return JSON matching the schema. Copy that JSON into \`${rel(input, input.responsePath)}\`.
59
+ `;
60
+ }
61
+ function formatStructureFingerprint(fingerprint) {
62
+ if (!fingerprint.expectedStructureSha256 || !fingerprint.actualStructureSha256)
63
+ return '';
64
+ return `- \`expectedStructureSha256\`: \`${fingerprint.expectedStructureSha256}\`
65
+ - \`actualStructureSha256\`: \`${fingerprint.actualStructureSha256}\``;
66
+ }
67
+ function formatStructureSection(input) {
68
+ if (!input.structureInputs) {
69
+ return '';
70
+ }
71
+ return `
72
+ ## Structure Inputs
73
+
74
+ - Expected structure: \`${rel(input, input.structureInputs.expectedStructurePath)}\`
75
+ - Actual structure: \`${rel(input, input.structureInputs.actualStructurePath)}\`
76
+ - Structure kind: \`${input.structureInputs.structureKind}\`
77
+
78
+ Use structure evidence to support image findings and to produce a structure-vs-structure comparison.
79
+ `;
80
+ }
81
+ function rel(input, filePath) {
82
+ return path.relative(input.workspaceRoot, filePath);
83
+ }
84
+ //# sourceMappingURL=agent-comparison-prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-comparison-prompts.js","sourceRoot":"","sources":["../../src/core/agent-comparison-prompts.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,yBAAyB,EAAmC,MAAM,gCAAgC,CAAC;AAmB5G,MAAM,UAAU,0BAA0B,CAAC,KAAkB;IAC3D,MAAM,OAAO,GAAG,yBAAyB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzD,OAAO;;aAEI,OAAO,CAAC,IAAI;iBACR,OAAO,CAAC,YAAY;eACtB,OAAO,CAAC,UAAU;oBACb,OAAO,CAAC,eAAe;;EAEzC,OAAO,CAAC,WAAW;;;;sBAIC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,iBAAiB,CAAC;oBACjD,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC;;;;;;+BAMlC,KAAK,CAAC,gBAAgB,CAAC,mBAAmB;6BAC5C,KAAK,CAAC,gBAAgB,CAAC,iBAAiB;EACnE,0BAA0B,CAAC,KAAK,CAAC,gBAAgB,CAAC;;;;;EAKlD,sBAAsB,CAAC,KAAK,CAAC;;;MAGzB,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC;;;;MAI5B,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC;;;;;;;;CAQnC,CAAC;AACF,CAAC;AAED,MAAM,UAAU,+BAA+B,CAAC,KAAkB;IAChE,OAAO,GAAG,0BAA0B,CAAC,KAAK,CAAC;;;uDAGU,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC;CACpF,CAAC;AACF,CAAC;AAED,MAAM,UAAU,gCAAgC,CAAC,KAAkB;IACjE,OAAO,GAAG,0BAA0B,CAAC,KAAK,CAAC;;;oHAGuE,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC;CACjJ,CAAC;AACF,CAAC;AAED,SAAS,0BAA0B,CAAC,WAA4C;IAC9E,IAAI,CAAC,WAAW,CAAC,uBAAuB,IAAI,CAAC,WAAW,CAAC,qBAAqB;QAAE,OAAO,EAAE,CAAC;IAC1F,OAAO,oCAAoC,WAAW,CAAC,uBAAuB;iCAC/C,WAAW,CAAC,qBAAqB,IAAI,CAAC;AACvE,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAkB;IAChD,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO;;;0BAGiB,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,eAAe,CAAC,qBAAqB,CAAC;wBACzD,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,eAAe,CAAC,mBAAmB,CAAC;sBACvD,KAAK,CAAC,eAAe,CAAC,aAAa;;;CAGxD,CAAC;AACF,CAAC;AAED,SAAS,GAAG,CAAC,KAAkB,EAAE,QAAgB;IAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { AgentComparisonPackResult } from './agent-comparison-pack.js';
2
+ export declare function formatAgentComparisonArtifacts(root: string, agentComparison?: AgentComparisonPackResult): string;
3
+ export declare function formatAgentComparisonStatus(agentComparison?: AgentComparisonPackResult): string;
@@ -0,0 +1,56 @@
1
+ import path from 'node:path';
2
+ export function formatAgentComparisonArtifacts(root, agentComparison) {
3
+ if (!agentComparison) {
4
+ return '';
5
+ }
6
+ const lines = [
7
+ `- Agent comparison prompt: \`${relative(root, agentComparison.promptPath)}\``,
8
+ `- Codex comparison prompt: \`${relative(root, agentComparison.codexPromptPath)}\``,
9
+ `- Claude comparison prompt: \`${relative(root, agentComparison.claudePromptPath)}\``,
10
+ `- Agent comparison schema: \`${relative(root, agentComparison.schemaPath)}\``,
11
+ `- Agent comparison response: \`${relative(root, agentComparison.responsePath)}\``,
12
+ ];
13
+ if (agentComparison.imageComparisonPath) {
14
+ lines.push(`- Image comparison: \`${relative(root, agentComparison.imageComparisonPath)}\``);
15
+ }
16
+ if (agentComparison.structureComparisonPath) {
17
+ lines.push(`- Structure comparison: \`${relative(root, agentComparison.structureComparisonPath)}\``);
18
+ }
19
+ if (agentComparison.summaryPath) {
20
+ lines.push(`- Comparison summary: \`${relative(root, agentComparison.summaryPath)}\``);
21
+ }
22
+ return `\n${lines.join('\n')}`;
23
+ }
24
+ export function formatAgentComparisonStatus(agentComparison) {
25
+ if (!agentComparison) {
26
+ return '';
27
+ }
28
+ if (agentComparison.status === 'awaiting-agent') {
29
+ return `
30
+
31
+ ## Agent Comparison
32
+
33
+ - Status: awaiting agent response.
34
+ - Next step: have an agent inspect the comparison prompt, write \`${path.basename(agentComparison.responsePath)}\`, then rerun this compare command.`;
35
+ }
36
+ if (agentComparison.status === 'invalid') {
37
+ const errors = agentComparison.validationErrors?.map((error) => `- ${error}`).join('\n') ?? '- Unknown validation error.';
38
+ return `
39
+
40
+ ## Agent Comparison
41
+
42
+ - Status: invalid agent response.
43
+
44
+ ${errors}`;
45
+ }
46
+ return `
47
+
48
+ ## Agent Comparison
49
+
50
+ - Status: validated.
51
+ ${agentComparison.scoreHighlights?.map((highlight) => `- ${highlight}`).join('\n') ?? '- Agent comparison artifacts validated.'}`;
52
+ }
53
+ function relative(root, filePath) {
54
+ return path.relative(root, filePath);
55
+ }
56
+ //# sourceMappingURL=agent-comparison-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-comparison-report.js","sourceRoot":"","sources":["../../src/core/agent-comparison-report.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,MAAM,UAAU,8BAA8B,CAAC,IAAY,EAAE,eAA2C;IACtG,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,KAAK,GAAG;QACZ,gCAAgC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,UAAU,CAAC,IAAI;QAC9E,gCAAgC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,eAAe,CAAC,IAAI;QACnF,iCAAiC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,gBAAgB,CAAC,IAAI;QACrF,gCAAgC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,UAAU,CAAC,IAAI;QAC9E,kCAAkC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,YAAY,CAAC,IAAI;KACnF,CAAC;IACF,IAAI,eAAe,CAAC,mBAAmB,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,yBAAyB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC/F,CAAC;IACD,IAAI,eAAe,CAAC,uBAAuB,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,6BAA6B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC;IACvG,CAAC;IACD,IAAI,eAAe,CAAC,WAAW,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,2BAA2B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,eAA2C;IACrF,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,eAAe,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;QAChD,OAAO;;;;;oEAKyD,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,sCAAsC,CAAC;IACpJ,CAAC;IACD,IAAI,eAAe,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,eAAe,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,6BAA6B,CAAC;QAC1H,OAAO;;;;;;EAMT,MAAM,EAAE,CAAC;IACT,CAAC;IACD,OAAO;;;;;EAKP,eAAe,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,KAAK,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,yCAAyC,EAAE,CAAC;AAClI,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { AgentComparisonBundle } from '../schemas/comparison.js';
2
+ export declare function validateFixTargets(errors: string[], comparisonPath: 'imageComparison' | 'structureComparison', diffAreas: AgentComparisonBundle['imageComparison']['majorDiffAreas'], expectedSurface: 'mockup' | 'implementation'): void;
@@ -0,0 +1,8 @@
1
+ export function validateFixTargets(errors, comparisonPath, diffAreas, expectedSurface) {
2
+ diffAreas.forEach((diff, index) => {
3
+ if (diff.fixTarget && diff.fixTarget.surface !== expectedSurface && diff.fixTarget.surface !== 'unknown') {
4
+ errors.push(`${comparisonPath}.majorDiffAreas.${index}.fixTarget.surface: Expected ${expectedSurface} or unknown.`);
5
+ }
6
+ });
7
+ }
8
+ //# sourceMappingURL=agent-comparison-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-comparison-validation.js","sourceRoot":"","sources":["../../src/core/agent-comparison-validation.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAChC,MAAgB,EAChB,cAAyD,EACzD,SAAqE,EACrE,eAA4C;IAE5C,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAChC,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,KAAK,eAAe,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACzG,MAAM,CAAC,IAAI,CAAC,GAAG,cAAc,mBAAmB,KAAK,gCAAgC,eAAe,cAAc,CAAC,CAAC;QACtH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -34,7 +34,7 @@ export async function capturePage(options) {
34
34
  bodyText,
35
35
  };
36
36
  await writeFile(`${options.outputPath}.console.json`, JSON.stringify(consoleMessages, null, 2) + '\n');
37
- if (metrics.length > 0) {
37
+ if (options.contract) {
38
38
  await writeFile(`${options.outputPath}.metrics.json`, JSON.stringify(metrics, null, 2) + '\n');
39
39
  }
40
40
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"playwright-capture.js","sourceRoot":"","sources":["../../src/core/playwright-capture.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"playwright-capture.js","sourceRoot":"","sources":["../../src/core/playwright-capture.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { AgentComparisonPackResult } from './agent-comparison-pack.js';
1
2
  import type { RegionMetrics } from './dom-metrics.js';
2
3
  import type { TargetPaths } from './workspace.js';
3
4
  export declare function writeImageReferenceReport(input: {
@@ -23,21 +24,16 @@ export declare function writeImageReferenceReport(input: {
23
24
  status: 'awaiting-agent' | 'validated' | 'invalid';
24
25
  validationErrors?: string[];
25
26
  };
27
+ agentComparison?: AgentComparisonPackResult;
26
28
  }): Promise<string>;
27
- export declare function writeReferenceAppReport(input: {
29
+ export declare function writeReferenceImplementationReport(input: {
28
30
  target: TargetPaths;
29
31
  referencePath: string;
30
- appPath: string;
32
+ implementationPath: string;
31
33
  sideBySidePath: string;
32
34
  referenceMetrics: RegionMetrics[];
33
- appMetrics: RegionMetrics[];
34
- appUrl: string;
35
- }): Promise<string>;
36
- export declare function writeImageAppReport(input: {
37
- target: TargetPaths;
38
- idealPath: string;
39
- appPath: string;
40
- sideBySidePath: string;
41
- appUrl: string;
35
+ implementationMetrics: RegionMetrics[];
36
+ implementationUrl: string;
37
+ agentComparison?: AgentComparisonPackResult;
42
38
  }): Promise<string>;
43
39
  export declare function writeTargetSummary(target: TargetPaths): Promise<string>;
@@ -1,5 +1,6 @@
1
1
  import { mkdir, readdir, stat, writeFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
+ import { formatAgentComparisonArtifacts, formatAgentComparisonStatus } from './agent-comparison-report.js';
3
4
  export async function writeImageReferenceReport(input) {
4
5
  const reportPath = path.join(input.target.reportsDir, 'image-reference.md');
5
6
  await mkdir(path.dirname(reportPath), { recursive: true });
@@ -13,6 +14,7 @@ Target: \`${input.target.name}\`
13
14
  - Reference screenshot: \`${relative(input.target.workspace.root, input.referencePath)}\`
14
15
  - Side-by-side: \`${relative(input.target.workspace.root, input.sideBySidePath)}\`
15
16
  ${formatSpecArtifacts(input.target.workspace.root, input.specPaths, input.agentSpec)}
17
+ ${formatAgentComparisonArtifacts(input.target.workspace.root, input.agentComparison)}
16
18
 
17
19
  ## Agent Critique
18
20
 
@@ -28,64 +30,40 @@ ${formatSpecArtifacts(input.target.workspace.root, input.specPaths, input.agentS
28
30
  - Record the highest-value scoped fix here.
29
31
  ${formatAgentSpecStatus(input.agentSpec)}
30
32
  ${formatSpecHighlights(input.specHighlights)}
33
+ ${formatAgentComparisonStatus(input.agentComparison)}
31
34
  `);
32
35
  return reportPath;
33
36
  }
34
- export async function writeReferenceAppReport(input) {
35
- const reportPath = path.join(input.target.reportsDir, 'reference-app.md');
37
+ export async function writeReferenceImplementationReport(input) {
38
+ const reportPath = path.join(input.target.reportsDir, 'reference-implementation.md');
36
39
  await mkdir(path.dirname(reportPath), { recursive: true });
37
- await writeFile(reportPath, `# Reference To App Report
40
+ await writeFile(reportPath, `# Reference To Implementation Report
38
41
 
39
42
  Target: \`${input.target.name}\`
40
43
 
41
- App URL: ${input.appUrl}
44
+ Implementation URL: ${input.implementationUrl}
42
45
 
43
46
  ## Artifacts
44
47
 
45
48
  - Reference screenshot: \`${relative(input.target.workspace.root, input.referencePath)}\`
46
- - App screenshot: \`${relative(input.target.workspace.root, input.appPath)}\`
49
+ - Implementation screenshot: \`${relative(input.target.workspace.root, input.implementationPath)}\`
47
50
  - Side-by-side: \`${relative(input.target.workspace.root, input.sideBySidePath)}\`
51
+ ${formatAgentComparisonArtifacts(input.target.workspace.root, input.agentComparison)}
48
52
 
49
53
  ## Region Metrics
50
54
 
51
- ${formatMetrics(input.referenceMetrics, input.appMetrics)}
55
+ ${formatMetrics(input.referenceMetrics, input.implementationMetrics)}
52
56
 
53
- ## Agent Findings
54
-
55
- - Highest mismatch:
56
- - Evidence:
57
- - Suggested app fix:
58
- - Intentional deviations:
59
- `);
60
- return reportPath;
61
- }
62
- export async function writeImageAppReport(input) {
63
- const reportPath = path.join(input.target.reportsDir, 'image-app.md');
64
- await mkdir(path.dirname(reportPath), { recursive: true });
65
- await writeFile(reportPath, `# Image To App Report
66
-
67
- Target: \`${input.target.name}\`
68
-
69
- App URL: ${input.appUrl}
70
-
71
- ## Artifacts
72
-
73
- - Ideal image: \`${relative(input.target.workspace.root, input.idealPath)}\`
74
- - App screenshot: \`${relative(input.target.workspace.root, input.appPath)}\`
75
- - Side-by-side: \`${relative(input.target.workspace.root, input.sideBySidePath)}\`
76
-
77
- ## Agent Findings
57
+ ## Next Fix
78
58
 
79
- - Highest visual mismatch:
80
- - Evidence:
81
- - Suggested app fix:
82
- - Intentional deviations:
59
+ - Use the validated agent comparison to choose the highest-value implementation fix.
60
+ ${formatAgentComparisonStatus(input.agentComparison)}
83
61
  `);
84
62
  return reportPath;
85
63
  }
86
64
  export async function writeTargetSummary(target) {
87
65
  const lines = [`# Limner Target Summary`, '', `Target: \`${target.name}\``, ''];
88
- for (const dir of ['captures/image-reference', 'captures/reference-app', 'captures/image-app', 'reports']) {
66
+ for (const dir of ['captures/image-reference', 'captures/reference-implementation', 'reports']) {
89
67
  const absoluteDir = path.join(target.root, dir);
90
68
  lines.push(`## ${dir}`, '');
91
69
  try {
@@ -103,16 +81,16 @@ export async function writeTargetSummary(target) {
103
81
  }
104
82
  return lines.join('\n');
105
83
  }
106
- function formatMetrics(referenceMetrics, appMetrics) {
107
- const appById = new Map(appMetrics.map((metric) => [metric.id, metric]));
84
+ function formatMetrics(referenceMetrics, implementationMetrics) {
85
+ const implementationById = new Map(implementationMetrics.map((metric) => [metric.id, metric]));
108
86
  const rows = referenceMetrics.map((reference) => {
109
- const app = appById.get(reference.id);
87
+ const implementation = implementationById.get(reference.id);
110
88
  const referenceBounds = reference.bounds ? `${reference.bounds.width}x${reference.bounds.height}` : 'missing';
111
- const appBounds = app?.bounds ? `${app.bounds.width}x${app.bounds.height}` : 'missing';
112
- return `| ${reference.id} | ${reference.found ? 'yes' : 'no'} | ${app?.found ? 'yes' : 'no'} | ${referenceBounds} | ${appBounds} |`;
89
+ const implementationBounds = implementation?.bounds ? `${implementation.bounds.width}x${implementation.bounds.height}` : 'missing';
90
+ return `| ${reference.id} | ${reference.found ? 'yes' : 'no'} | ${implementation?.found ? 'yes' : 'no'} | ${referenceBounds} | ${implementationBounds} |`;
113
91
  });
114
92
  return [
115
- '| Region | Reference Found | App Found | Reference Bounds | App Bounds |',
93
+ '| Region | Reference Found | Implementation Found | Reference Bounds | Implementation Bounds |',
116
94
  '| --- | --- | --- | --- | --- |',
117
95
  ...rows,
118
96
  ].join('\n');
@@ -150,7 +128,7 @@ function formatAgentSpecStatus(agentSpec) {
150
128
 
151
129
  - Agent-required spec pack generated.
152
130
  - Next step: have an agent inspect the ideal image and reference artifacts, then write \`captures/image-reference/spec/agent-response.json\`.
153
- - Rerun \`limn compare image-reference --target <target> --spec\` after the response file exists.`;
131
+ - Rerun \`limner compare image-reference --target <target> --spec\` after the response file exists.`;
154
132
  }
155
133
  if (agentSpec.status === 'invalid') {
156
134
  const errors = agentSpec.validationErrors?.map((error) => `- ${error}`).join('\n') ?? '- Unknown validation error.';