@momentiq/dark-factory-cli 3.0.1 → 3.1.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 (53) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +3 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/mode.d.ts.map +1 -1
  5. package/dist/commands/mode.js +120 -22
  6. package/dist/commands/mode.js.map +1 -1
  7. package/dist/commands/skills.d.ts.map +1 -1
  8. package/dist/commands/skills.js +11 -1
  9. package/dist/commands/skills.js.map +1 -1
  10. package/dist/commands/verify.d.ts.map +1 -1
  11. package/dist/commands/verify.js +113 -8
  12. package/dist/commands/verify.js.map +1 -1
  13. package/dist/mcp/tools/skills-install.d.ts.map +1 -1
  14. package/dist/mcp/tools/skills-install.js +8 -3
  15. package/dist/mcp/tools/skills-install.js.map +1 -1
  16. package/dist/mode/critic-registration.d.ts +30 -10
  17. package/dist/mode/critic-registration.d.ts.map +1 -1
  18. package/dist/mode/critic-registration.js +31 -10
  19. package/dist/mode/critic-registration.js.map +1 -1
  20. package/dist/mode/guard.d.ts.map +1 -1
  21. package/dist/mode/guard.js +30 -2
  22. package/dist/mode/guard.js.map +1 -1
  23. package/dist/mode/init.d.ts +7 -4
  24. package/dist/mode/init.d.ts.map +1 -1
  25. package/dist/mode/init.js +146 -12
  26. package/dist/mode/init.js.map +1 -1
  27. package/dist/mode/preview-capture.d.ts +3 -0
  28. package/dist/mode/preview-capture.d.ts.map +1 -0
  29. package/dist/mode/preview-capture.js +129 -0
  30. package/dist/mode/preview-capture.js.map +1 -0
  31. package/dist/mode/preview.d.ts +160 -0
  32. package/dist/mode/preview.d.ts.map +1 -0
  33. package/dist/mode/preview.js +522 -0
  34. package/dist/mode/preview.js.map +1 -0
  35. package/dist/onboard/seeders/design-md.d.ts.map +1 -1
  36. package/dist/onboard/seeders/design-md.js +512 -17
  37. package/dist/onboard/seeders/design-md.js.map +1 -1
  38. package/dist/skills/install.d.ts +25 -5
  39. package/dist/skills/install.d.ts.map +1 -1
  40. package/dist/skills/install.js +34 -17
  41. package/dist/skills/install.js.map +1 -1
  42. package/dist/skills/known-skills.d.ts +2 -0
  43. package/dist/skills/known-skills.d.ts.map +1 -0
  44. package/dist/skills/known-skills.js +25 -0
  45. package/dist/skills/known-skills.js.map +1 -0
  46. package/package.json +2 -2
  47. package/skills/designer-build/SKILL.md.tmpl +14 -0
  48. package/skills/designer-handoff/SKILL.md.tmpl +43 -3
  49. package/skills/designer-ship/SKILL.md.tmpl +17 -14
  50. package/skills/verify/producer/README.md +29 -4
  51. package/skills/verify/producer/playwright-route.sh +10 -0
  52. package/skills/verify/producer/surface-declaration.ts +373 -0
  53. package/skills/verify/producer/ui-route.producer.spec.ts +44 -11
package/dist/mode/init.js CHANGED
@@ -4,8 +4,10 @@
4
4
  // Design constraints:
5
5
  // - APPEND, never re-serialize the consumer's file: hand-maintained comments
6
6
  // and formatting survive. The appended block is generated YAML.
7
- // - Deterministic scope proposal: only directories that actually exist are
8
- // proposed (the engineer reviews the init PR — this is a seed, not policy).
7
+ // - Deterministic scope proposal: directories that actually exist are proposed
8
+ // (the engineer reviews the init PR — this is a seed, not policy), plus two
9
+ // always-on seeds independent of layout — `**/*.css` (a minimal editable
10
+ // surface) and `.darkfactory/**` (the mode's own workflow artifacts).
9
11
  // - Idempotent: an existing `modes.<name>` entry short-circuits to "exists".
10
12
  // - Fail closed: the candidate file content must round-trip through the
11
13
  // canonical parser (@momentiq/dark-factory-schemas) before it is written.
@@ -23,6 +25,12 @@ const DEFAULT_PROTECTED_PATHS = [
23
25
  "package.json",
24
26
  "package-lock.json",
25
27
  ];
28
+ // Non-monorepo frontend roots (Next/Vite/CRA/Remix): the app lives in a named
29
+ // top-level dir rather than at the repo root or under `apps/*`. Each root is
30
+ // probed for the same conventional subdirs, mirrored one level deep — so a
31
+ // designer on a `web/`-rooted repo gets real component scopes, not css-only (#354).
32
+ const FRONTEND_ROOT_DIRS = ["web", "frontend", "client"];
33
+ const FRONTEND_ROOT_SUBDIRS = ["components", "app", "pages", "src", "lib", "styles"];
26
34
  const ROOT_SCOPE_DIR_CANDIDATES = [
27
35
  "src/components",
28
36
  "src/app",
@@ -33,6 +41,7 @@ const ROOT_SCOPE_DIR_CANDIDATES = [
33
41
  "pages",
34
42
  "styles",
35
43
  "public",
44
+ ...FRONTEND_ROOT_DIRS.flatMap((root) => FRONTEND_ROOT_SUBDIRS.map((sub) => `${root}/${sub}`)),
36
45
  ];
37
46
  const APP_SCOPE_SUBDIR_CANDIDATES = ["src/components", "src/app", "src/pages", "public"];
38
47
  function isDir(path) {
@@ -44,10 +53,13 @@ function isDir(path) {
44
53
  }
45
54
  }
46
55
  /**
47
- * Propose write scopes from directories that exist. Monorepo `apps/<name>`
48
- * workspaces are scanned one level deep. Styling files are always in scope —
49
- * a designer mode with no reachable surface still gets `**\/*.css` so the
50
- * engineer sees an editable (if minimal) seed to correct in the init PR.
56
+ * Propose write scopes from directories that exist. Root-level frontend roots
57
+ * (`web/`, `frontend/`, `client/`) and the Next `src/` conventions are probed,
58
+ * plus monorepo `apps/<name>` workspaces one level deep. Two always-on seeds
59
+ * are appended regardless of layout: `**\/*.css` (a designer with no reachable
60
+ * component surface still gets a minimal editable seed to correct in the init
61
+ * PR) and `.darkfactory/**` (the mode's own workflow — `designer-brief` and
62
+ * `df objectives derive --apply` — writes there, so its outputs stay in scope).
51
63
  */
52
64
  export function detectWriteScopes(repoRoot) {
53
65
  const scopes = [];
@@ -55,6 +67,17 @@ export function detectWriteScopes(repoRoot) {
55
67
  if (isDir(join(repoRoot, candidate)))
56
68
  scopes.push(`${candidate}/**`);
57
69
  }
70
+ // Bare top-level `src/` fallback (#354): repos that keep the whole frontend
71
+ // under a single `src/` (no Next `src/<area>` split) match none of the
72
+ // `src/<subdir>` candidates above and would otherwise fall through to
73
+ // css-only. Propose the broad `src/**` only when `src/` exists AND no
74
+ // more-specific `src/<subdir>` scope already matched, so the seed never lists
75
+ // `src/**` beside a scope it already contains. (The web/frontend/client roots
76
+ // need no such guard — their subdirs are siblings, not nested under a broader
77
+ // candidate.)
78
+ if (isDir(join(repoRoot, "src")) && !scopes.some((s) => s.startsWith("src/"))) {
79
+ scopes.push("src/**");
80
+ }
58
81
  const appsRoot = join(repoRoot, "apps");
59
82
  if (isDir(appsRoot)) {
60
83
  for (const entry of readdirSync(appsRoot).sort()) {
@@ -65,24 +88,135 @@ export function detectWriteScopes(repoRoot) {
65
88
  }
66
89
  }
67
90
  }
68
- scopes.push("**/*.css");
91
+ // Always-on seeds, independent of repo layout: `**\/*.css` (a designer with no
92
+ // reachable component surface still gets a minimal editable seed) and
93
+ // `.darkfactory/**` — the mode's OWN workflow writes there (`designer-brief`
94
+ // → .darkfactory/briefs/…, `df objectives derive --apply` →
95
+ // .darkfactory/objectives.yaml), so seeding it keeps those outputs in scope
96
+ // and the mode doesn't fail its own `mode-scope-critic` gate (#361).
97
+ scopes.push("**/*.css", ".darkfactory/**");
69
98
  return scopes;
70
99
  }
71
- function detectDevServer(repoRoot) {
100
+ // Best-effort default preview URL. The seed is reviewed in the init PR; the
101
+ // engineer corrects the port if the detected server binds elsewhere.
102
+ const DEFAULT_DEV_URL = "http://localhost:3000";
103
+ // GNU make reads the first of these that exists (GNUmakefile → makefile →
104
+ // Makefile); we mirror that precedence.
105
+ const MAKEFILE_NAMES = ["GNUmakefile", "makefile", "Makefile"];
106
+ // foreman/honcho read `Procfile.dev` for development when present, else `Procfile`.
107
+ const PROCFILE_NAMES = ["Procfile.dev", "Procfile"];
108
+ // Docker Compose v2 file precedence — the highest-precedence file that exists
109
+ // is authoritative; the rest are ignored by the tool, so we don't read them.
110
+ const COMPOSE_FILE_NAMES = [
111
+ "compose.yaml",
112
+ "compose.yml",
113
+ "docker-compose.yaml",
114
+ "docker-compose.yml",
115
+ ];
116
+ // Service names that plausibly ARE the dev server, in priority order.
117
+ const COMPOSE_DEV_SERVICE_NAMES = ["dev", "web", "app", "frontend", "client"];
118
+ function detectPackageDevServer(repoRoot) {
72
119
  const pkgPath = join(repoRoot, "package.json");
73
120
  if (!existsSync(pkgPath))
74
121
  return undefined;
75
122
  try {
76
123
  const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
77
- if (pkg.scripts?.["dev"]) {
78
- return { command: "npm run dev", url: "http://localhost:3000" };
79
- }
124
+ if (pkg.scripts?.["dev"])
125
+ return { command: "npm run dev", url: DEFAULT_DEV_URL };
80
126
  }
81
127
  catch {
82
- // Malformed package.json → no dev-server proposal; init still succeeds.
128
+ // Malformed package.json → no proposal from this source; init still succeeds.
129
+ }
130
+ return undefined;
131
+ }
132
+ function detectMakefileDevServer(repoRoot) {
133
+ for (const name of MAKEFILE_NAMES) {
134
+ const filePath = join(repoRoot, name);
135
+ if (!existsSync(filePath))
136
+ continue;
137
+ let content;
138
+ try {
139
+ content = readFileSync(filePath, "utf8");
140
+ }
141
+ catch {
142
+ continue;
143
+ }
144
+ for (const line of content.split("\n")) {
145
+ // A `dev` target is defined at column 0 as `dev:` / `dev: prereqs` /
146
+ // `dev::` (double-colon rule). Inspect the TARGET's own colon only: a
147
+ // `dev :=` / `dev ::=` / `dev :::=` variable assignment has `=`
148
+ // immediately after the colon run, while a real target does not. Keying
149
+ // on the target colon (not a `:=` substring anywhere on the line) keeps a
150
+ // valid target with a target-specific variable (`dev: FOO := bar`)
151
+ // correctly detected.
152
+ if (/^dev[ \t]*:{1,2}(?![:=])/.test(line)) {
153
+ return { command: "make dev", url: DEFAULT_DEV_URL };
154
+ }
155
+ }
156
+ }
157
+ return undefined;
158
+ }
159
+ function detectProcfileDevServer(repoRoot) {
160
+ for (const name of PROCFILE_NAMES) {
161
+ const filePath = join(repoRoot, name);
162
+ if (!existsSync(filePath))
163
+ continue;
164
+ let content;
165
+ try {
166
+ content = readFileSync(filePath, "utf8");
167
+ }
168
+ catch {
169
+ continue;
170
+ }
171
+ // Procfile lines are `<process>: <command>`; the `web` process is the HTTP
172
+ // server a designer previews. Propose its command verbatim.
173
+ const match = content.match(/^web:\s*(.+)$/m);
174
+ const command = (match?.[1] ?? "").trim();
175
+ if (command)
176
+ return { command, url: DEFAULT_DEV_URL };
177
+ }
178
+ return undefined;
179
+ }
180
+ function detectComposeDevServer(repoRoot) {
181
+ for (const name of COMPOSE_FILE_NAMES) {
182
+ const filePath = join(repoRoot, name);
183
+ if (!existsSync(filePath))
184
+ continue;
185
+ // The highest-precedence compose file that exists is authoritative; Compose
186
+ // ignores the lower-precedence files, so we don't fall through to them.
187
+ try {
188
+ const doc = parseYaml(readFileSync(filePath, "utf8"));
189
+ const services = doc?.services;
190
+ if (services && typeof services === "object") {
191
+ for (const svc of COMPOSE_DEV_SERVICE_NAMES) {
192
+ if (Object.prototype.hasOwnProperty.call(services, svc)) {
193
+ return { command: `docker compose up ${svc}`, url: DEFAULT_DEV_URL };
194
+ }
195
+ }
196
+ }
197
+ }
198
+ catch {
199
+ // Malformed compose file → no proposal; init still succeeds.
200
+ }
201
+ return undefined;
83
202
  }
84
203
  return undefined;
85
204
  }
205
+ /**
206
+ * Propose a dev-server command for the mode's mandatory visual loop. Priority:
207
+ * an explicit npm `dev` script first, then the container-first / make / Procfile
208
+ * layouts that repos without a `scripts.dev` use — so a designer on a
209
+ * make-driven or compose-driven repo gets a real preview command instead of
210
+ * nothing (#357; the component-isolation preview harness is a separate PR).
211
+ * Only a source whose file/target actually exists yields a proposal; undefined
212
+ * when none match.
213
+ */
214
+ function detectDevServer(repoRoot) {
215
+ return (detectPackageDevServer(repoRoot) ??
216
+ detectMakefileDevServer(repoRoot) ??
217
+ detectProcfileDevServer(repoRoot) ??
218
+ detectComposeDevServer(repoRoot));
219
+ }
86
220
  export function buildDefaultModeEntry(repoRoot, modeName) {
87
221
  const writeScopes = detectWriteScopes(repoRoot);
88
222
  const devServer = detectDevServer(repoRoot);
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/mode/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/mode/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type CaptureDeps } from "./preview.js";
2
+ export declare const defaultCaptureDeps: CaptureDeps;
3
+ //# sourceMappingURL=preview-capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-capture.d.ts","sourceRoot":"","sources":["../../src/mode/preview-capture.ts"],"names":[],"mappings":"AAiBA,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AA2BzE,eAAO,MAAM,kBAAkB,EAAE,WA8DhC,CAAC"}
@@ -0,0 +1,129 @@
1
+ // Default `CaptureDeps` for `df mode preview` — the real Vite-server + Playwright
2
+ // wiring. Kept in its own file (never imported by tests, which inject fakes) so
3
+ // the pure harness/detection core in `preview.ts` stays browser- and
4
+ // bundler-free.
5
+ //
6
+ // RESOLUTION CONTEXT (load-bearing). Vite and Playwright are NOT dependencies of
7
+ // this CLI — like the `ui-visual` route, they are resolved from the CONSUMER's
8
+ // repo. A bare `import("vite")` would resolve against the CLI's own node_modules
9
+ // (wrong copy, or absent). So we resolve every consumer module against `cwd`
10
+ // explicitly and dynamic-import the resolved path. When a module or the browser
11
+ // is missing, we THROW — `runPreview` catches it and degrades with an actionable
12
+ // message (never a crash, never fabricated evidence).
13
+ import { createRequire } from "node:module";
14
+ import { join } from "node:path";
15
+ import { pathToFileURL } from "node:url";
16
+ import { BrowserUnavailableError } from "./preview.js";
17
+ /** Dynamic-import a module resolved from the consumer repo (`cwd`), NOT the CLI's
18
+ * own node_modules. Throws (with the module name) when it is not installed. */
19
+ async function importFromCwd(mod, cwd) {
20
+ // The anchor file need not exist — node uses its dirname as the resolution base.
21
+ const req = createRequire(join(cwd, "package.json"));
22
+ let resolved;
23
+ try {
24
+ resolved = req.resolve(mod);
25
+ }
26
+ catch {
27
+ throw new Error(`cannot resolve "${mod}" from ${cwd} — is it installed here?`);
28
+ }
29
+ return (await import(pathToFileURL(resolved).href));
30
+ }
31
+ /** CJS/ESM interop: a CJS module imported via dynamic `import()` exposes its
32
+ * `module.exports` under `.default`; a real ESM module exposes named exports
33
+ * directly. Vite's and Playwright's resolved (CJS) builds land on `.default`,
34
+ * so read the member from whichever object actually has it. */
35
+ function member(mod, name) {
36
+ const direct = mod[name];
37
+ if (direct !== undefined)
38
+ return direct;
39
+ const def = mod["default"];
40
+ return def?.[name];
41
+ }
42
+ export const defaultCaptureDeps = {
43
+ async startServer({ cwd, harnessDir, port }) {
44
+ // Vite transforms the component's TSX/JSX (+ CSS, path aliases) on the fly —
45
+ // the "detected framework's isolated render" the harness relies on.
46
+ const viteMod = await importFromCwd("vite", cwd);
47
+ const createServer = member(viteMod, "createServer");
48
+ if (typeof createServer !== "function") {
49
+ throw new Error("resolved `vite` but it exposes no createServer() — unexpected build shape");
50
+ }
51
+ const reactPluginMod = await importFromCwd("@vitejs/plugin-react", cwd);
52
+ const reactPlugin = (typeof reactPluginMod["default"] === "function" ? reactPluginMod["default"] : reactPluginMod);
53
+ // root = harnessDir (inside cwd), so its index.html is served at "/", bare
54
+ // `react` imports resolve up into cwd/node_modules, and `fs.allow` lets the
55
+ // entry import the component that lives elsewhere under the repo root.
56
+ const server = await createServer({
57
+ root: harnessDir,
58
+ configFile: false,
59
+ logLevel: "silent",
60
+ plugins: [reactPlugin()],
61
+ server: {
62
+ port,
63
+ strictPort: false,
64
+ host: "127.0.0.1",
65
+ fs: { allow: [cwd, harnessDir] },
66
+ },
67
+ });
68
+ await server.listen();
69
+ const url = server.resolvedUrls?.local?.[0] ?? `http://127.0.0.1:${port}/`;
70
+ return {
71
+ url,
72
+ close: async () => {
73
+ await server.close();
74
+ },
75
+ };
76
+ },
77
+ async capture({ url, cwd }) {
78
+ // Resolve Playwright from the SAME consumer repo (`cwd`) as the server —
79
+ // accept either `playwright` or `@playwright/test` (the ui-visual route uses
80
+ // the latter). Resolution + launch failures are a genuinely-unavailable
81
+ // browser → BrowserUnavailableError (soft-degrade). Anything AFTER launch
82
+ // (navigation, the render, the screenshot) is a REAL failure and propagates
83
+ // as a plain Error so runPreview surfaces it loudly.
84
+ const browser = await launchBrowser(cwd);
85
+ try {
86
+ const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
87
+ await page.goto(url, { waitUntil: "networkidle", timeout: 30_000 });
88
+ // Structural render floor — the harness wraps the component in <main>.
89
+ await page.locator("main").first().waitFor({ state: "visible", timeout: 15_000 });
90
+ const aria = await page.locator("body").ariaSnapshot();
91
+ const png = await page.screenshot({ fullPage: true });
92
+ return { aria, png };
93
+ }
94
+ finally {
95
+ await browser.close();
96
+ }
97
+ },
98
+ };
99
+ /** Resolve + launch Chromium from the consumer repo. Every failure here is a
100
+ * browser-unavailable condition (not installed / no binary / missing system
101
+ * deps), so normalize it to `BrowserUnavailableError` — the ONLY capture failure
102
+ * runPreview soft-degrades. */
103
+ async function launchBrowser(cwd) {
104
+ let pwMod;
105
+ try {
106
+ pwMod = await importFromCwd("playwright", cwd);
107
+ }
108
+ catch {
109
+ try {
110
+ pwMod = await importFromCwd("@playwright/test", cwd);
111
+ }
112
+ catch (err) {
113
+ throw new BrowserUnavailableError(`Playwright is not installed in ${cwd} (${err instanceof Error ? err.message : String(err)}). ` +
114
+ `Install with \`npm i -D playwright\`.`);
115
+ }
116
+ }
117
+ const chromium = member(pwMod, "chromium");
118
+ if (!chromium || typeof chromium.launch !== "function") {
119
+ throw new BrowserUnavailableError("resolved Playwright but it exposes no chromium.launch() — unexpected shape");
120
+ }
121
+ try {
122
+ return await chromium.launch();
123
+ }
124
+ catch (err) {
125
+ // A launch failure is a missing/unusable browser binary or system deps.
126
+ throw new BrowserUnavailableError(err instanceof Error ? err.message : String(err));
127
+ }
128
+ }
129
+ //# sourceMappingURL=preview-capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-capture.js","sourceRoot":"","sources":["../../src/mode/preview-capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,160 @@
1
+ /** One file the harness writes into its temp dir. */
2
+ export interface HarnessFile {
3
+ /** Path relative to the harness dir (e.g. "index.html", ENTRY_BASENAME). */
4
+ path: string;
5
+ contents: string;
6
+ }
7
+ export declare const ENTRY_BASENAME = "df-preview-entry.tsx";
8
+ export declare const INDEX_HTML_BASENAME = "index.html";
9
+ export interface GenerateHarnessInput {
10
+ /** Absolute path to the component file being previewed. */
11
+ componentAbsPath: string;
12
+ /** Absolute path to the (temp) harness dir the files are written into. The
13
+ * entry imports the component by a path relative to here. */
14
+ harnessDir: string;
15
+ /** Human component name (from the filename) — used in the title + wrapper. */
16
+ componentName: string;
17
+ /** Named export to mount. When omitted the harness imports the DEFAULT export. */
18
+ exportName?: string;
19
+ /** React major (18/19 → `createRoot`; <18 → `ReactDOM.render`). */
20
+ reactMajor: number;
21
+ }
22
+ /**
23
+ * Generate the minimal isolated-render harness for a React component: an entry
24
+ * that mounts JUST that component (placeholder/default props) inside a `<main>`
25
+ * landmark, plus an `index.html`. The `<main>` wrapper guarantees the same
26
+ * structural render floor the `ui-visual` producer asserts (`getByRole("main")`),
27
+ * regardless of what the component itself renders. PURE — returns file contents;
28
+ * the caller writes them.
29
+ */
30
+ export declare function generateHarness(input: GenerateHarnessInput): HarnessFile[];
31
+ export type PreviewBlockerCode = "not-a-component-file" | "no-react" | "no-vite" | "server-component" | "no-export";
32
+ export interface DetectInput {
33
+ /** Repo root (consumer cwd). */
34
+ cwd: string;
35
+ /** Absolute path to the component file. */
36
+ componentAbsPath: string;
37
+ /** The component file's source. */
38
+ source: string;
39
+ /** Parsed consumer package.json (deps ∪ devDeps ∪ peerDeps merged), or null. */
40
+ packageDeps: Record<string, string> | null;
41
+ }
42
+ export interface DetectResult {
43
+ /** `vite-harness` = renderable via the generated harness; `unsupported` = a
44
+ * blocker prevents an honest render (see `blocker`). */
45
+ engine: "vite-harness" | "unsupported";
46
+ componentName: string;
47
+ reactMajor: number;
48
+ /** Resolved export to mount (undefined = default export). */
49
+ exportName?: string;
50
+ /** An isolation tool is present — reported (v1 still renders via the generated
51
+ * harness; native story capture is deferred, see the PR body). */
52
+ storybook: boolean;
53
+ ladle: boolean;
54
+ /** Non-fatal advisories surfaced to the operator. */
55
+ warnings: string[];
56
+ /** Non-null when the component genuinely cannot be rendered — `runPreview`
57
+ * degrades (exit 2) with this message rather than a wrong render. */
58
+ blocker?: {
59
+ code: PreviewBlockerCode;
60
+ message: string;
61
+ };
62
+ }
63
+ /** Best-effort PascalCase component name from a filename (`user-card.tsx` →
64
+ * `UserCard`, `Button.tsx` → `Button`). */
65
+ export declare function componentNameFromPath(componentAbsPath: string): string;
66
+ /**
67
+ * Resolve which export the harness should mount. Preference order:
68
+ * 1. `export default` present → default import (undefined exportName).
69
+ * 2. a named export matching the PascalCase filename.
70
+ * 3. the first `export function|const <PascalCaseName>`.
71
+ * Returns `{ kind: "none" }` when no export is found (a blocker).
72
+ */
73
+ export declare function resolveExport(source: string, componentName: string): {
74
+ kind: "default";
75
+ } | {
76
+ kind: "named";
77
+ name: string;
78
+ } | {
79
+ kind: "none";
80
+ };
81
+ /**
82
+ * Decide whether the component can be rendered via the generated Vite harness,
83
+ * and gather advisories. PURE — no fs, no network.
84
+ */
85
+ export declare function detectStrategy(input: DetectInput): DetectResult;
86
+ /** Injected boundary so the Vite-server + Playwright wiring is never imported by
87
+ * tests. The default implementation lives in `preview-capture.ts`. */
88
+ export interface CaptureDeps {
89
+ /** Start an isolated dev server rooted at the harness dir; resolve to its base
90
+ * URL + a close(). Throws when the transform tool (Vite) is unavailable. */
91
+ startServer(input: {
92
+ cwd: string;
93
+ harnessDir: string;
94
+ port: number;
95
+ }): Promise<{
96
+ url: string;
97
+ close: () => Promise<void>;
98
+ }>;
99
+ /** Drive a browser: navigate, wait for the `main` landmark, return the ARIA
100
+ * snapshot + a full-page PNG. `cwd` is threaded so the browser is resolved
101
+ * from the SAME consumer repo as the server (not `process.cwd()`). Throws a
102
+ * `BrowserUnavailableError` when the browser genuinely can't launch; any OTHER
103
+ * throw is treated as a real render/transform failure (surfaced loudly). */
104
+ capture(input: {
105
+ url: string;
106
+ cwd: string;
107
+ }): Promise<{
108
+ aria: string;
109
+ png: Buffer;
110
+ }>;
111
+ }
112
+ /** Thrown by a `CaptureDeps.capture` when the browser cannot be resolved/launched
113
+ * (Playwright not installed, Chromium binary absent, missing system deps). This
114
+ * is the ONLY capture failure that degrades softly (exit 2, "reached the capture
115
+ * step") — a Vite transform error, a component runtime exception, missing props,
116
+ * or an `fs.allow`-blocked import is a REAL failure and surfaces loudly (exit 1). */
117
+ export declare class BrowserUnavailableError extends Error {
118
+ constructor(message: string);
119
+ }
120
+ /** Secondary heuristic (belt-and-suspenders alongside `BrowserUnavailableError`):
121
+ * recognize a genuine missing-browser / launch / install failure from its message
122
+ * so a raw Playwright error is still classified as degrade-soft, while a render or
123
+ * transform error is NOT. Kept deliberately narrow. */
124
+ export declare function isBrowserUnavailable(err: unknown): boolean;
125
+ export interface RunPreviewOptions {
126
+ /** Component path as given by the operator (relative to cwd or absolute). */
127
+ componentPath: string;
128
+ cwd: string;
129
+ /** Local port for the isolated harness server (default 5199). */
130
+ port?: number;
131
+ /** Override the evidence slug (default: sanitized component filename). */
132
+ slug?: string;
133
+ }
134
+ export type PreviewStatus = "captured" | "degraded" | "error";
135
+ export interface RunPreviewResult {
136
+ status: PreviewStatus;
137
+ /** 0 captured · 2 degraded (tool/browser/unsupported) · 1 hard usage error. */
138
+ exitCode: 0 | 1 | 2;
139
+ slug: string;
140
+ message: string;
141
+ warnings: string[];
142
+ /** Absolute evidence dir when `captured`. */
143
+ evidenceDir?: string;
144
+ }
145
+ /**
146
+ * A safe evidence slug is a SINGLE path segment: filesystem-safe chars only, and
147
+ * never `.` / `..` / anything with a separator. The slug is interpolated into the
148
+ * evidence path (`agent-reviews/quality-gates/ui/<sha>/<slug>/…`), so an operator
149
+ * `--slug ../../..` would otherwise traverse OUT of the evidence tree and clobber
150
+ * arbitrary files. The auto-derived slug is already safe (`sanitizeSlug`); this
151
+ * fail-closes an explicit `--slug`. Exported for the traversal regression test.
152
+ */
153
+ export declare function isSafeSlug(slug: string): boolean;
154
+ /**
155
+ * Render one component in isolation and write `ui-visual`-shaped evidence.
156
+ * Never throws for an expected failure — returns a `degraded`/`error` result
157
+ * with an actionable message and the right exit code.
158
+ */
159
+ export declare function runPreview(opts: RunPreviewOptions, deps: CaptureDeps): Promise<RunPreviewResult>;
160
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/mode/preview.ts"],"names":[],"mappings":"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"}