@slowcook-ai/cli 0.19.6 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +22 -2
  2. package/dist/cli.js +30 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/brand/index.d.ts.map +1 -1
  5. package/dist/commands/brand/index.js +6 -0
  6. package/dist/commands/brand/index.js.map +1 -1
  7. package/dist/commands/brand/logo-cmd.d.ts +2 -0
  8. package/dist/commands/brand/logo-cmd.d.ts.map +1 -0
  9. package/dist/commands/brand/logo-cmd.js +90 -0
  10. package/dist/commands/brand/logo-cmd.js.map +1 -0
  11. package/dist/commands/brand/logo.d.ts +23 -0
  12. package/dist/commands/brand/logo.d.ts.map +1 -0
  13. package/dist/commands/brand/logo.js +60 -0
  14. package/dist/commands/brand/logo.js.map +1 -0
  15. package/dist/commands/brew/fidelity-loop.d.ts +71 -0
  16. package/dist/commands/brew/fidelity-loop.d.ts.map +1 -0
  17. package/dist/commands/brew/fidelity-loop.js +108 -0
  18. package/dist/commands/brew/fidelity-loop.js.map +1 -0
  19. package/dist/commands/brew/fidelity-phase.d.ts +49 -0
  20. package/dist/commands/brew/fidelity-phase.d.ts.map +1 -0
  21. package/dist/commands/brew/fidelity-phase.js +75 -0
  22. package/dist/commands/brew/fidelity-phase.js.map +1 -0
  23. package/dist/commands/eye/index.d.ts +2 -0
  24. package/dist/commands/eye/index.d.ts.map +1 -0
  25. package/dist/commands/eye/index.js +83 -0
  26. package/dist/commands/eye/index.js.map +1 -0
  27. package/dist/commands/eye/plan.d.ts +69 -0
  28. package/dist/commands/eye/plan.d.ts.map +1 -0
  29. package/dist/commands/eye/plan.js +87 -0
  30. package/dist/commands/eye/plan.js.map +1 -0
  31. package/dist/commands/eye/run.d.ts +42 -0
  32. package/dist/commands/eye/run.d.ts.map +1 -0
  33. package/dist/commands/eye/run.js +116 -0
  34. package/dist/commands/eye/run.js.map +1 -0
  35. package/dist/commands/eye/spec-modes.d.ts +5 -0
  36. package/dist/commands/eye/spec-modes.d.ts.map +1 -0
  37. package/dist/commands/eye/spec-modes.js +36 -0
  38. package/dist/commands/eye/spec-modes.js.map +1 -0
  39. package/dist/commands/gate/github.d.ts +27 -0
  40. package/dist/commands/gate/github.d.ts.map +1 -0
  41. package/dist/commands/gate/github.js +46 -0
  42. package/dist/commands/gate/github.js.map +1 -0
  43. package/dist/commands/gate/index.d.ts +2 -0
  44. package/dist/commands/gate/index.d.ts.map +1 -0
  45. package/dist/commands/gate/index.js +68 -0
  46. package/dist/commands/gate/index.js.map +1 -0
  47. package/dist/commands/gate/model.d.ts +55 -0
  48. package/dist/commands/gate/model.d.ts.map +1 -0
  49. package/dist/commands/gate/model.js +64 -0
  50. package/dist/commands/gate/model.js.map +1 -0
  51. package/dist/commands/gate/reviewers.d.ts +24 -0
  52. package/dist/commands/gate/reviewers.d.ts.map +1 -0
  53. package/dist/commands/gate/reviewers.js +69 -0
  54. package/dist/commands/gate/reviewers.js.map +1 -0
  55. package/dist/commands/greenfield/index.d.ts +2 -0
  56. package/dist/commands/greenfield/index.d.ts.map +1 -0
  57. package/dist/commands/greenfield/index.js +80 -0
  58. package/dist/commands/greenfield/index.js.map +1 -0
  59. package/dist/commands/greenfield/status.d.ts +38 -0
  60. package/dist/commands/greenfield/status.d.ts.map +1 -0
  61. package/dist/commands/greenfield/status.js +52 -0
  62. package/dist/commands/greenfield/status.js.map +1 -0
  63. package/dist/commands/menu/assemble.d.ts +31 -0
  64. package/dist/commands/menu/assemble.d.ts.map +1 -0
  65. package/dist/commands/menu/assemble.js +37 -0
  66. package/dist/commands/menu/assemble.js.map +1 -0
  67. package/dist/commands/menu/index.d.ts +5 -0
  68. package/dist/commands/menu/index.d.ts.map +1 -0
  69. package/dist/commands/menu/index.js +111 -0
  70. package/dist/commands/menu/index.js.map +1 -0
  71. package/dist/commands/menu/prd.d.ts +33 -0
  72. package/dist/commands/menu/prd.d.ts.map +1 -0
  73. package/dist/commands/menu/prd.js +68 -0
  74. package/dist/commands/menu/prd.js.map +1 -0
  75. package/dist/commands/plate/index.d.ts.map +1 -1
  76. package/dist/commands/plate/index.js +5 -1
  77. package/dist/commands/plate/index.js.map +1 -1
  78. package/dist/commands/plate/route-hint.d.ts +18 -0
  79. package/dist/commands/plate/route-hint.d.ts.map +1 -0
  80. package/dist/commands/plate/route-hint.js +21 -0
  81. package/dist/commands/plate/route-hint.js.map +1 -0
  82. package/dist/commands/recon/shape-preserve.d.ts +38 -0
  83. package/dist/commands/recon/shape-preserve.d.ts.map +1 -1
  84. package/dist/commands/recon/shape-preserve.js +112 -1
  85. package/dist/commands/recon/shape-preserve.js.map +1 -1
  86. package/dist/commands/refine/spec-yaml.d.ts +26 -0
  87. package/dist/commands/refine/spec-yaml.d.ts.map +1 -1
  88. package/dist/commands/refine/spec-yaml.js +29 -0
  89. package/dist/commands/refine/spec-yaml.js.map +1 -1
  90. package/dist/commands/run-mock/index.d.ts.map +1 -1
  91. package/dist/commands/run-mock/index.js +69 -16
  92. package/dist/commands/run-mock/index.js.map +1 -1
  93. package/dist/commands/run-mock/reviewer-auth-server.d.ts +40 -0
  94. package/dist/commands/run-mock/reviewer-auth-server.d.ts.map +1 -0
  95. package/dist/commands/run-mock/reviewer-auth-server.js +124 -0
  96. package/dist/commands/run-mock/reviewer-auth-server.js.map +1 -0
  97. package/dist/commands/serve/config.d.ts +28 -9
  98. package/dist/commands/serve/config.d.ts.map +1 -1
  99. package/dist/commands/serve/config.js +43 -1
  100. package/dist/commands/serve/config.js.map +1 -1
  101. package/dist/commands/serve/dev.d.ts +14 -19
  102. package/dist/commands/serve/dev.d.ts.map +1 -1
  103. package/dist/commands/serve/dev.js +46 -50
  104. package/dist/commands/serve/dev.js.map +1 -1
  105. package/dist/commands/serve/index.d.ts.map +1 -1
  106. package/dist/commands/serve/index.js +30 -22
  107. package/dist/commands/serve/index.js.map +1 -1
  108. package/dist/commands/serve/mock.d.ts +5 -20
  109. package/dist/commands/serve/mock.d.ts.map +1 -1
  110. package/dist/commands/serve/mock.js +44 -65
  111. package/dist/commands/serve/mock.js.map +1 -1
  112. package/dist/commands/serve/runner.d.ts +52 -0
  113. package/dist/commands/serve/runner.d.ts.map +1 -0
  114. package/dist/commands/serve/runner.js +53 -0
  115. package/dist/commands/serve/runner.js.map +1 -0
  116. package/dist/commands/serve/staging.d.ts +8 -19
  117. package/dist/commands/serve/staging.d.ts.map +1 -1
  118. package/dist/commands/serve/staging.js +53 -91
  119. package/dist/commands/serve/staging.js.map +1 -1
  120. package/dist/commands/trace/check.d.ts +67 -0
  121. package/dist/commands/trace/check.d.ts.map +1 -0
  122. package/dist/commands/trace/check.js +82 -0
  123. package/dist/commands/trace/check.js.map +1 -0
  124. package/dist/commands/trace/index.d.ts +2 -0
  125. package/dist/commands/trace/index.d.ts.map +1 -0
  126. package/dist/commands/trace/index.js +86 -0
  127. package/dist/commands/trace/index.js.map +1 -0
  128. package/dist/commands/upsert-agent-docs.d.ts.map +1 -1
  129. package/dist/commands/upsert-agent-docs.js +12 -0
  130. package/dist/commands/upsert-agent-docs.js.map +1 -1
  131. package/dist/commands.manifest.d.ts.map +1 -1
  132. package/dist/commands.manifest.js +32 -2
  133. package/dist/commands.manifest.js.map +1 -1
  134. package/dist/lib/mock-shape.d.ts +6 -0
  135. package/dist/lib/mock-shape.d.ts.map +1 -1
  136. package/dist/lib/mock-shape.js +26 -0
  137. package/dist/lib/mock-shape.js.map +1 -1
  138. package/package.json +8 -6
@@ -0,0 +1,49 @@
1
+ /**
2
+ * design #8 — the post-green fidelity phase. After brew's behavioural/structural
3
+ * tests pass, render the brewed app vs the mock across the spec-declared modes
4
+ * and either (a) gate: drift → escalate to the #9 designer/QA gate, or (b) drive:
5
+ * feed the hard per-element violations back to the brew agent over a hot-reload
6
+ * candidate, bounded, until convergence or escalation.
7
+ *
8
+ * The eye measurement + the brew-fix application are INJECTED — the orchestration
9
+ * (matrix from spec, loop, escalation mapping) is pure-flow + unit-tested; the
10
+ * Playwright render (../eye/run.ts) and the live brew-agent fix supply the seams.
11
+ */
12
+ import type { FidelityViolation } from "@slowcook-ai/gates";
13
+ import { type EyeContext } from "../eye/plan.js";
14
+ /**
15
+ * Render violations into a concise, mock-faithful fix instruction for the brew
16
+ * agent. Grouped by viewport/scheme; scoped to presentation-only edits. Pure.
17
+ */
18
+ export declare function formatViolationsForBrew(violations: FidelityViolation[]): string;
19
+ export interface FidelityPhaseDeps {
20
+ /** Story id — used to resolve the spec's fidelity.modes. */
21
+ story?: string;
22
+ /** Repo root for the spec lookup. */
23
+ cwd: string;
24
+ /** Render + grade the matrix → violations (real: a runEyeMatrix wrapper). */
25
+ measure: (matrix: EyeContext[]) => Promise<FidelityViolation[]>;
26
+ /**
27
+ * Apply fixes via the brew agent against a hot-reload candidate, then the
28
+ * caller's HMR re-renders. OMIT for gate-only mode (drift → escalate, no fix).
29
+ */
30
+ applyFix?: (violations: FidelityViolation[], iteration: number) => Promise<void>;
31
+ maxIters?: number;
32
+ log?: (msg: string) => void;
33
+ }
34
+ export interface FidelityPhaseResult {
35
+ converged: boolean;
36
+ escalated: boolean;
37
+ iterations: number;
38
+ remaining: FidelityViolation[];
39
+ reason: string;
40
+ /** The #9 gate action: pass advances; blocked-on-designer halts for review. */
41
+ gateAction: "pass" | "blocked-on-designer";
42
+ matrixCells: number;
43
+ }
44
+ /**
45
+ * Run the fidelity phase. Matrix comes from the spec's fidelity.modes (the
46
+ * contract) or the full default. Gate-only when `applyFix` is omitted.
47
+ */
48
+ export declare function runFidelityPhase(deps: FidelityPhaseDeps): Promise<FidelityPhaseResult>;
49
+ //# sourceMappingURL=fidelity-phase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fidelity-phase.d.ts","sourceRoot":"","sources":["../../../src/commands/brew/fidelity-phase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG5D,OAAO,EAAmC,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAElF;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAuB/E;AAED,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA6E;IAC7E,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAChE;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,iBAAiB,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,UAAU,EAAE,MAAM,GAAG,qBAAqB,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC;CACrB;AAKD;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuC5F"}
@@ -0,0 +1,75 @@
1
+ import { runFidelityLoop } from "./fidelity-loop.js";
2
+ import { loadFidelityModes } from "../eye/spec-modes.js";
3
+ import { matrixFromModes, DEFAULT_MATRIX } from "../eye/plan.js";
4
+ /**
5
+ * Render violations into a concise, mock-faithful fix instruction for the brew
6
+ * agent. Grouped by viewport/scheme; scoped to presentation-only edits. Pure.
7
+ */
8
+ export function formatViolationsForBrew(violations) {
9
+ if (violations.length === 0)
10
+ return "No fidelity violations.";
11
+ const byCtx = new Map();
12
+ for (const v of violations) {
13
+ const k = `${v.context.viewport}/${v.context.scheme}`;
14
+ const arr = byCtx.get(k) ?? [];
15
+ arr.push(v);
16
+ byCtx.set(k, arr);
17
+ }
18
+ const lines = [
19
+ "Visual fidelity drift vs the approved mock. Edit ONLY the presentation of the",
20
+ "affected @slowcook-port-from components to restore the mock's rendering — do not",
21
+ "change behaviour, data-wiring, or markup structure, only the diverged styling:",
22
+ "",
23
+ ];
24
+ for (const [ctx, vs] of byCtx) {
25
+ lines.push(`## ${ctx}`);
26
+ for (const v of vs) {
27
+ lines.push(`- \`${v.selector}\`: ${v.property} should be ${v.expected} (currently ${v.actual}) [${v.axis}]`);
28
+ }
29
+ lines.push("");
30
+ }
31
+ return lines.join("\n").trimEnd() + "\n";
32
+ }
33
+ const keyOf = (v) => `${v.context.viewport}/${v.context.scheme}|${v.selector}|${v.property}`;
34
+ /**
35
+ * Run the fidelity phase. Matrix comes from the spec's fidelity.modes (the
36
+ * contract) or the full default. Gate-only when `applyFix` is omitted.
37
+ */
38
+ export async function runFidelityPhase(deps) {
39
+ const modes = deps.story ? loadFidelityModes(deps.cwd, deps.story) : null;
40
+ const matrix = modes && modes.length ? matrixFromModes(modes) : DEFAULT_MATRIX;
41
+ deps.log?.(`fidelity phase: ${matrix.length} cell(s)${deps.applyFix ? " (driving)" : " (gate-only)"}`);
42
+ const measure = () => deps.measure(matrix);
43
+ // Gate-only: one measurement; drift escalates to the designer/QA gate.
44
+ if (!deps.applyFix) {
45
+ const violations = await measure();
46
+ const converged = violations.length === 0;
47
+ return {
48
+ converged,
49
+ escalated: !converged,
50
+ iterations: 1,
51
+ remaining: violations,
52
+ reason: converged ? "converged" : "drift (gate-only — escalating)",
53
+ gateAction: converged ? "pass" : "blocked-on-designer",
54
+ matrixCells: matrix.length,
55
+ };
56
+ }
57
+ // Eye-driven correction loop (bounded; escalates on stall/exhaustion).
58
+ const loop = await runFidelityLoop({
59
+ measure,
60
+ applyFix: deps.applyFix,
61
+ keyOf,
62
+ maxIters: deps.maxIters,
63
+ onIteration: deps.log ? (i) => deps.log(` iter ${i.iteration}: ${i.count} violation(s)`) : undefined,
64
+ });
65
+ return {
66
+ converged: loop.converged,
67
+ escalated: loop.escalated,
68
+ iterations: loop.iterations,
69
+ remaining: loop.remaining,
70
+ reason: loop.reason,
71
+ gateAction: loop.converged ? "pass" : "blocked-on-designer",
72
+ matrixCells: matrix.length,
73
+ };
74
+ }
75
+ //# sourceMappingURL=fidelity-phase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fidelity-phase.js","sourceRoot":"","sources":["../../../src/commands/brew/fidelity-phase.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function eye(args: string[], _version: string): Promise<void>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/eye/index.ts"],"names":[],"mappings":"AAmBA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA+DzE"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * design #8 — `slowcook eye`. Renders a reference (mock) URL and a candidate
3
+ * (brewed) URL across the (viewport × scheme) matrix in headless Chromium,
4
+ * grades candidate-vs-reference with the gates fidelity engine, writes
5
+ * screenshots + a JSON report, and exits 1 when the gate fails. This is the
6
+ * runnable eye behind the brew-loop + the #9 designer/QA gate; it consumes
7
+ * `references.visual` as the reference URL and the spec's fidelity.modes for
8
+ * the matrix.
9
+ *
10
+ * slowcook eye --reference http://localhost:33010 --candidate http://localhost:3001 \
11
+ * [--story 020] [--cwd .] [--out .brewing/eye] [--viewport mobile|desktop] \
12
+ * [--scheme light|dark] [--max-violations N] [--fail-on color,box,computed-style,missing]
13
+ */
14
+ import { writeFileSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ import { parseEyeArgs, matrixFromModes, narrowMatrix } from "./plan.js";
17
+ import { loadFidelityModes } from "./spec-modes.js";
18
+ import { runEyeMatrix, runEyeWatch } from "./run.js";
19
+ export async function eye(args, _version) {
20
+ if (args[0] === "--help" || args[0] === "-h") {
21
+ console.log("usage: slowcook eye --reference <url> --candidate <url> [--story <id>] [--cwd <dir>] [--out dir] [--viewport m] [--scheme s] [--max-violations N] [--fail-on a,b] [--watch [--interval ms] [--until-converged] [--max-passes N]]");
22
+ return;
23
+ }
24
+ let opts;
25
+ try {
26
+ opts = parseEyeArgs(args);
27
+ }
28
+ catch (e) {
29
+ console.error(String(e instanceof Error ? e.message : e));
30
+ process.exit(64);
31
+ }
32
+ // Spec-declared modes (the contract) override the default matrix; explicit
33
+ // --viewport/--scheme flags still narrow on top.
34
+ if (opts.story) {
35
+ const modes = loadFidelityModes(opts.cwd, opts.story);
36
+ if (modes && modes.length) {
37
+ opts.matrix = narrowMatrix(matrixFromModes(modes), { viewport: opts.viewport, scheme: opts.scheme });
38
+ console.log(`eye: matrix from story-${opts.story} fidelity.modes [${modes.join(", ")}] → ${opts.matrix.length} cell(s)`);
39
+ }
40
+ else {
41
+ console.log(`eye: story-${opts.story} declares no fidelity.modes; using ${opts.matrix.length}-cell default matrix`);
42
+ }
43
+ }
44
+ // sc#189 — warm-browser HMR re-eye loop for fast visual convergence.
45
+ if (opts.watch) {
46
+ console.log(`eye --watch: ${opts.matrix.length} cell(s) · interval ${opts.intervalMs}ms · max ${opts.maxPasses} passes${opts.untilConverged ? " · until-converged" : ""}`);
47
+ const { result, passes, converged } = await runEyeWatch({
48
+ referenceUrl: opts.referenceUrl,
49
+ candidateUrl: opts.candidateUrl,
50
+ matrix: opts.matrix,
51
+ outDir: opts.outDir,
52
+ gate: opts.gate,
53
+ intervalMs: opts.intervalMs,
54
+ untilConverged: opts.untilConverged,
55
+ maxPasses: opts.maxPasses,
56
+ });
57
+ writeFileSync(join(opts.outDir, "eye-report.json"), JSON.stringify({ ...result, passes, converged }, null, 2));
58
+ console.log(`\nslowcook eye --watch — ${result.passed ? "PASS ✓" : "FAIL ✗"} after ${passes} pass(es)`);
59
+ if (opts.untilConverged && !result.passed)
60
+ process.exit(1);
61
+ return;
62
+ }
63
+ const { result, screenshots } = await runEyeMatrix({
64
+ referenceUrl: opts.referenceUrl,
65
+ candidateUrl: opts.candidateUrl,
66
+ matrix: opts.matrix,
67
+ outDir: opts.outDir,
68
+ gate: opts.gate,
69
+ });
70
+ writeFileSync(join(opts.outDir, "eye-report.json"), JSON.stringify({ ...result, screenshots }, null, 2));
71
+ console.log(`\nslowcook eye — ${result.passed ? "PASS ✓" : "FAIL ✗"} (${result.violations.length} violation(s) across ${opts.matrix.length} render(s))`);
72
+ for (const ctx of result.byContext) {
73
+ if (ctx.violations.length === 0)
74
+ continue;
75
+ console.log(` [${ctx.context.viewport}/${ctx.context.scheme}] ${ctx.violations.length}: ${JSON.stringify(ctx.summary.byAxis)}`);
76
+ for (const v of ctx.violations.slice(0, 5))
77
+ console.log(` ${v.axis}: ${v.evidence}`);
78
+ }
79
+ console.log(` screenshots + report → ${opts.outDir}/`);
80
+ if (!result.passed)
81
+ process.exit(1);
82
+ }
83
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/eye/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * design #8 — `slowcook eye` planning (pure). Expands the CLI flags + the
3
+ * spec's declared fidelity modes into the (viewport × scheme) capture matrix +
4
+ * gate thresholds the runner executes. Kept pure + unit-tested; the Playwright
5
+ * execution + spec IO live in ./index.ts (matrix source) and ./spec-modes.ts.
6
+ *
7
+ * Which modes are in fidelity scope is a CONTRACT declared by refine in the
8
+ * spec (`fidelity.modes`), not chosen by brew. The eye reads + enforces it;
9
+ * brew is measured against it. CLI flags can only NARROW (never widen).
10
+ */
11
+ import type { FidelityGateOptions } from "@slowcook-ai/gates";
12
+ /** A capture context plus the pixel viewport to emulate. */
13
+ export interface EyeContext {
14
+ viewport: string;
15
+ scheme: "light" | "dark";
16
+ width: number;
17
+ height: number;
18
+ }
19
+ export interface EyeOptions {
20
+ referenceUrl: string;
21
+ candidateUrl: string;
22
+ outDir: string;
23
+ matrix: EyeContext[];
24
+ gate: FidelityGateOptions;
25
+ /** When set, the runner derives the matrix from this story's `fidelity.modes`. */
26
+ story?: string;
27
+ /** Repo root for spec lookup (default "."). */
28
+ cwd: string;
29
+ /** Raw narrowing flags, re-applied after a spec-derived matrix is built. */
30
+ viewport?: string;
31
+ scheme?: string;
32
+ /** design #8 / sc#189 — warm-browser HMR re-eye loop. */
33
+ watch: boolean;
34
+ /** Poll interval between watch passes (ms). Default 2000. */
35
+ intervalMs: number;
36
+ /** In watch mode, exit 0 as soon as the gate passes. */
37
+ untilConverged: boolean;
38
+ /** Safety cap on watch passes. Default 60. */
39
+ maxPasses: number;
40
+ }
41
+ export declare const VIEWPORTS: Record<string, {
42
+ width: number;
43
+ height: number;
44
+ }>;
45
+ /** Full default matrix: {mobile,desktop} × {light,dark}. */
46
+ export declare const DEFAULT_MATRIX: EyeContext[];
47
+ /**
48
+ * Build the capture matrix from a spec's declared `fidelity.modes` (pure).
49
+ * Tokens are dimension VALUES (`light`/`dark`/`mobile`/`desktop`), expanded to
50
+ * the product: a dimension with no declared value defaults to its full set
51
+ * (so `[dark]` → dark × {mobile,desktop}; `[mobile]` → {light,dark} × mobile).
52
+ * Unrecognised tokens are ignored; if NONE are recognised, the full default
53
+ * matrix is returned (fail-open — a typo never silently checks nothing).
54
+ */
55
+ export declare function matrixFromModes(modes: string[]): EyeContext[];
56
+ /** Narrow a matrix by explicit --viewport / --scheme flags (pure). Validates. */
57
+ export declare function narrowMatrix(base: EyeContext[], opts: {
58
+ viewport?: string;
59
+ scheme?: string;
60
+ }): EyeContext[];
61
+ /**
62
+ * Parse `slowcook eye` flags. Required: --reference <url>, --candidate <url>.
63
+ * Optional: --story <id> (derive the matrix from the spec's fidelity.modes),
64
+ * --cwd <dir>, --out <dir>, --viewport <name> + --scheme <light|dark> (narrow),
65
+ * --max-violations <n>, --fail-on <a,b>. The returned `matrix` is the flag-only
66
+ * default; when `story` is set the runner rebuilds it from the spec.
67
+ */
68
+ export declare function parseEyeArgs(args: string[]): EyeOptions;
69
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/commands/eye/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAgB,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE5E,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,cAAc,EAAE,OAAO,CAAC;IACxB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAGvE,CAAC;AAIF,4DAA4D;AAC5D,eAAO,MAAM,cAAc,EAAE,UAAU,EAEtC,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE,CAQ7D;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,EAAE,CAa3G;AAOD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAmCvD"}
@@ -0,0 +1,87 @@
1
+ export const VIEWPORTS = {
2
+ mobile: { width: 390, height: 844 },
3
+ desktop: { width: 1280, height: 800 },
4
+ };
5
+ const SCHEMES = ["light", "dark"];
6
+ /** Full default matrix: {mobile,desktop} × {light,dark}. */
7
+ export const DEFAULT_MATRIX = Object.entries(VIEWPORTS).flatMap(([viewport, dim]) => SCHEMES.map((scheme) => ({ viewport, scheme, ...dim })));
8
+ /**
9
+ * Build the capture matrix from a spec's declared `fidelity.modes` (pure).
10
+ * Tokens are dimension VALUES (`light`/`dark`/`mobile`/`desktop`), expanded to
11
+ * the product: a dimension with no declared value defaults to its full set
12
+ * (so `[dark]` → dark × {mobile,desktop}; `[mobile]` → {light,dark} × mobile).
13
+ * Unrecognised tokens are ignored; if NONE are recognised, the full default
14
+ * matrix is returned (fail-open — a typo never silently checks nothing).
15
+ */
16
+ export function matrixFromModes(modes) {
17
+ const wanted = new Set(modes.map((m) => String(m).toLowerCase().trim()));
18
+ const viewports = Object.keys(VIEWPORTS).filter((v) => wanted.has(v));
19
+ const schemes = SCHEMES.filter((s) => wanted.has(s));
20
+ if (!viewports.length && !schemes.length)
21
+ return DEFAULT_MATRIX;
22
+ const vps = viewports.length ? viewports : Object.keys(VIEWPORTS);
23
+ const schs = schemes.length ? schemes : SCHEMES;
24
+ return vps.flatMap((viewport) => schs.map((scheme) => ({ viewport, scheme, ...VIEWPORTS[viewport] })));
25
+ }
26
+ /** Narrow a matrix by explicit --viewport / --scheme flags (pure). Validates. */
27
+ export function narrowMatrix(base, opts) {
28
+ let m = base;
29
+ if (opts.viewport) {
30
+ if (!VIEWPORTS[opts.viewport]) {
31
+ throw new Error(`eye: unknown --viewport '${opts.viewport}' (have: ${Object.keys(VIEWPORTS).join(", ")})`);
32
+ }
33
+ m = m.filter((c) => c.viewport === opts.viewport);
34
+ }
35
+ if (opts.scheme) {
36
+ if (opts.scheme !== "light" && opts.scheme !== "dark")
37
+ throw new Error("eye: --scheme must be light|dark");
38
+ m = m.filter((c) => c.scheme === opts.scheme);
39
+ }
40
+ return m;
41
+ }
42
+ function val(args, flag) {
43
+ const i = args.indexOf(flag);
44
+ return i >= 0 ? args[i + 1] : undefined;
45
+ }
46
+ /**
47
+ * Parse `slowcook eye` flags. Required: --reference <url>, --candidate <url>.
48
+ * Optional: --story <id> (derive the matrix from the spec's fidelity.modes),
49
+ * --cwd <dir>, --out <dir>, --viewport <name> + --scheme <light|dark> (narrow),
50
+ * --max-violations <n>, --fail-on <a,b>. The returned `matrix` is the flag-only
51
+ * default; when `story` is set the runner rebuilds it from the spec.
52
+ */
53
+ export function parseEyeArgs(args) {
54
+ const referenceUrl = val(args, "--reference");
55
+ const candidateUrl = val(args, "--candidate");
56
+ if (!referenceUrl || !candidateUrl) {
57
+ throw new Error("eye: --reference <url> and --candidate <url> are both required");
58
+ }
59
+ const viewport = val(args, "--viewport");
60
+ const scheme = val(args, "--scheme");
61
+ const matrix = narrowMatrix(DEFAULT_MATRIX, { viewport, scheme });
62
+ const maxV = val(args, "--max-violations");
63
+ const failOn = val(args, "--fail-on");
64
+ const gate = {};
65
+ if (maxV !== undefined)
66
+ gate.maxViolations = Number.parseInt(maxV, 10);
67
+ if (failOn)
68
+ gate.failOnAxes = failOn.split(",").map((s) => s.trim());
69
+ const interval = val(args, "--interval");
70
+ const maxPasses = val(args, "--max-passes");
71
+ return {
72
+ referenceUrl,
73
+ candidateUrl,
74
+ outDir: val(args, "--out") ?? ".brewing/eye",
75
+ matrix,
76
+ gate,
77
+ story: val(args, "--story"),
78
+ cwd: val(args, "--cwd") ?? ".",
79
+ viewport,
80
+ scheme,
81
+ watch: args.includes("--watch"),
82
+ intervalMs: interval !== undefined ? Number.parseInt(interval, 10) : 2000,
83
+ untilConverged: args.includes("--until-converged"),
84
+ maxPasses: maxPasses !== undefined ? Number.parseInt(maxPasses, 10) : 60,
85
+ };
86
+ }
87
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/commands/eye/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { type FidelityGateOptions, type FidelityGateResult } from "@slowcook-ai/gates";
2
+ import type { EyeContext } from "./plan.js";
3
+ export interface RunEyeOptions {
4
+ referenceUrl: string;
5
+ candidateUrl: string;
6
+ matrix: EyeContext[];
7
+ /** Directory for screenshots. Files named `<label>-<viewport>-<scheme>.png`. */
8
+ outDir: string;
9
+ gate?: FidelityGateOptions;
10
+ /** Screenshot filename prefix (e.g. `eye-pr-123`); default none. */
11
+ shotPrefix?: string;
12
+ }
13
+ export interface RunEyeResult {
14
+ result: FidelityGateResult;
15
+ screenshots: string[];
16
+ }
17
+ /** Render + grade the full matrix. Launches one browser, a fresh context per cell. */
18
+ export declare function runEyeMatrix(opts: RunEyeOptions): Promise<RunEyeResult>;
19
+ /** Pure: one-line pass summary for the watch loop (`violations: N → M (Δ)`). */
20
+ export declare function formatPassLine(pass: number, prevTotal: number | null, result: FidelityGateResult): string;
21
+ export interface WatchEyeOptions extends RunEyeOptions {
22
+ intervalMs: number;
23
+ untilConverged: boolean;
24
+ maxPasses: number;
25
+ /** Injectable for tests/logging; defaults to console.log. */
26
+ log?: (msg: string) => void;
27
+ }
28
+ export interface WatchEyeResult {
29
+ result: FidelityGateResult;
30
+ passes: number;
31
+ converged: boolean;
32
+ }
33
+ /**
34
+ * sc#189 — warm-browser HMR re-eye loop. Captures the reference (static mock)
35
+ * ONCE per cell, keeps a warm candidate page per cell, and re-captures ONLY the
36
+ * candidate each pass (a `reload()` picks up HMR edits with no rebuild). Prints
37
+ * the per-axis violation delta each pass; with `untilConverged`, exits as soon
38
+ * as the gate passes. Bounded by `maxPasses`. The warm browser + reference-once
39
+ * capture is the win over re-running one-shot `eye` per iteration.
40
+ */
41
+ export declare function runEyeWatch(opts: WatchEyeOptions): Promise<WatchEyeResult>;
42
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/eye/run.ts"],"names":[],"mappings":"AAUA,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,sFAAsF;AACtF,wBAAsB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAmC7E;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAIzG;AAED,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAqDhF"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * design #8 — reusable eye runner. Renders a reference (mock) URL + a candidate
3
+ * (brewed) URL across a capture matrix in headless Chromium, screenshots each
4
+ * cell, and grades candidate-vs-reference with the gates fidelity engine.
5
+ * Shared by the `slowcook eye` command (./index.ts) and the eye-driven brew
6
+ * fidelity phase (../brew/fidelity-phase.ts) so both measure identically.
7
+ */
8
+ import { mkdirSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { chromium } from "@playwright/test";
11
+ import { captureSnapshot, gradeFidelity, } from "@slowcook-ai/gates";
12
+ /** Render + grade the full matrix. Launches one browser, a fresh context per cell. */
13
+ export async function runEyeMatrix(opts) {
14
+ mkdirSync(opts.outDir, { recursive: true });
15
+ const prefix = opts.shotPrefix ? `${opts.shotPrefix}-` : "";
16
+ const browser = await chromium.launch();
17
+ const screenshots = [];
18
+ const captureUrl = async (url, label, ctx) => {
19
+ const c = await browser.newContext({
20
+ colorScheme: ctx.scheme,
21
+ viewport: { width: ctx.width, height: ctx.height },
22
+ deviceScaleFactor: 2,
23
+ });
24
+ const page = await c.newPage();
25
+ await page.goto(url, { waitUntil: "networkidle" });
26
+ await page.waitForTimeout(400); // let lazy / client styles settle
27
+ const shot = join(opts.outDir, `${prefix}${label}-${ctx.viewport}-${ctx.scheme}.png`);
28
+ await page.screenshot({ path: shot, fullPage: true });
29
+ screenshots.push(shot);
30
+ const snap = await captureSnapshot(page, { viewport: ctx.viewport, scheme: ctx.scheme });
31
+ await c.close();
32
+ return snap;
33
+ };
34
+ const pairs = [];
35
+ try {
36
+ for (const ctx of opts.matrix) {
37
+ const reference = await captureUrl(opts.referenceUrl, "reference", ctx);
38
+ const candidate = await captureUrl(opts.candidateUrl, "candidate", ctx);
39
+ pairs.push({ reference, candidate });
40
+ }
41
+ }
42
+ finally {
43
+ await browser.close();
44
+ }
45
+ return { result: gradeFidelity(pairs, opts.gate), screenshots };
46
+ }
47
+ /** Pure: one-line pass summary for the watch loop (`violations: N → M (Δ)`). */
48
+ export function formatPassLine(pass, prevTotal, result) {
49
+ const c = result.violations.length;
50
+ const delta = prevTotal === null ? "" : ` (Δ${c - prevTotal >= 0 ? "+" : ""}${c - prevTotal})`;
51
+ return `pass ${pass}: ${c} violation(s)${delta} ${result.passed ? "PASS" : "FAIL"} ${JSON.stringify(result.summary.byAxis)}`;
52
+ }
53
+ /**
54
+ * sc#189 — warm-browser HMR re-eye loop. Captures the reference (static mock)
55
+ * ONCE per cell, keeps a warm candidate page per cell, and re-captures ONLY the
56
+ * candidate each pass (a `reload()` picks up HMR edits with no rebuild). Prints
57
+ * the per-axis violation delta each pass; with `untilConverged`, exits as soon
58
+ * as the gate passes. Bounded by `maxPasses`. The warm browser + reference-once
59
+ * capture is the win over re-running one-shot `eye` per iteration.
60
+ */
61
+ export async function runEyeWatch(opts) {
62
+ const log = opts.log ?? ((m) => console.log(m));
63
+ mkdirSync(opts.outDir, { recursive: true });
64
+ const browser = await chromium.launch();
65
+ const cells = [];
66
+ try {
67
+ const newCell = async (url, ctx) => {
68
+ const c = await browser.newContext({
69
+ colorScheme: ctx.scheme,
70
+ viewport: { width: ctx.width, height: ctx.height },
71
+ deviceScaleFactor: 2,
72
+ });
73
+ const p = await c.newPage();
74
+ await p.goto(url, { waitUntil: "networkidle" });
75
+ await p.waitForTimeout(400);
76
+ return { c, p };
77
+ };
78
+ for (const ctx of opts.matrix) {
79
+ const ref = await newCell(opts.referenceUrl, ctx);
80
+ const reference = await captureSnapshot(ref.p, { viewport: ctx.viewport, scheme: ctx.scheme });
81
+ await ref.c.close(); // reference is static — capture once, release it
82
+ const cand = await newCell(opts.candidateUrl, ctx);
83
+ cells.push({ ctx, reference, page: cand.p, close: () => cand.c.close() });
84
+ }
85
+ let prevTotal = null;
86
+ let result;
87
+ let converged = false;
88
+ let pass = 1;
89
+ for (; pass <= opts.maxPasses; pass++) {
90
+ const pairs = [];
91
+ for (const cell of cells) {
92
+ await cell.page.reload({ waitUntil: "networkidle" });
93
+ await cell.page.waitForTimeout(300);
94
+ const candidate = await captureSnapshot(cell.page, { viewport: cell.ctx.viewport, scheme: cell.ctx.scheme });
95
+ pairs.push({ reference: cell.reference, candidate });
96
+ }
97
+ result = gradeFidelity(pairs, opts.gate);
98
+ log(formatPassLine(pass, prevTotal, result));
99
+ prevTotal = result.violations.length;
100
+ if (opts.untilConverged && result.passed) {
101
+ converged = true;
102
+ log("converged ✓");
103
+ break;
104
+ }
105
+ if (pass < opts.maxPasses)
106
+ await new Promise((r) => setTimeout(r, opts.intervalMs));
107
+ }
108
+ return { result, passes: Math.min(pass, opts.maxPasses), converged };
109
+ }
110
+ finally {
111
+ for (const cell of cells)
112
+ await cell.close().catch(() => { });
113
+ await browser.close();
114
+ }
115
+ }
116
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/eye/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Pure: extract `fidelity.modes` from a spec YAML string, or null if absent. */
2
+ export declare function extractFidelityModes(specYaml: string): string[] | null;
3
+ /** Load `fidelity.modes` for a story from `<repoRoot>/specs/story-<id>.yaml`. */
4
+ export declare function loadFidelityModes(repoRoot: string, story: string): string[] | null;
5
+ //# sourceMappingURL=spec-modes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-modes.d.ts","sourceRoot":"","sources":["../../../src/commands/eye/spec-modes.ts"],"names":[],"mappings":"AAeA,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAUtE;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAIlF"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * design #8 — read a story spec's declared fidelity modes. refine writes
3
+ * `fidelity.modes` on the spec (the contract for which viewport/scheme cells
4
+ * matter); the eye reads it here to build its matrix. Decoupled from the
5
+ * (not-yet-built) #7 `references` field — when that lands, `references.visual[].modes`
6
+ * becomes the per-source override and `fidelity.modes` the spec-level default.
7
+ *
8
+ * # specs/story-020.yaml
9
+ * fidelity:
10
+ * modes: [light, dark, mobile, desktop]
11
+ */
12
+ import { existsSync, readFileSync } from "node:fs";
13
+ import { join } from "node:path";
14
+ import YAML from "yaml";
15
+ /** Pure: extract `fidelity.modes` from a spec YAML string, or null if absent. */
16
+ export function extractFidelityModes(specYaml) {
17
+ let doc;
18
+ try {
19
+ doc = YAML.parse(specYaml);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ const modes = doc?.fidelity?.modes;
25
+ if (Array.isArray(modes))
26
+ return modes.map((m) => String(m));
27
+ return null;
28
+ }
29
+ /** Load `fidelity.modes` for a story from `<repoRoot>/specs/story-<id>.yaml`. */
30
+ export function loadFidelityModes(repoRoot, story) {
31
+ const p = join(repoRoot, "specs", `story-${story}.yaml`);
32
+ if (!existsSync(p))
33
+ return null;
34
+ return extractFidelityModes(readFileSync(p, "utf8"));
35
+ }
36
+ //# sourceMappingURL=spec-modes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-modes.js","sourceRoot":"","sources":["../../../src/commands/eye/spec-modes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,iFAAiF;AACjF,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAI,GAAiD,EAAE,QAAQ,EAAE,KAAK,CAAC;IAClF,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,KAAa;IAC/D,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC;IACzD,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,oBAAoB,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AACvD,CAAC"}