frontend-precision 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,46 +1,40 @@
1
1
  # frontend-precision
2
2
 
3
- Cursor-first frontend precision toolkit: Playwright screenshots, pixel visual diff, configured test runs, and JSON/Markdown/HTML reports.
3
+ Cursor-first CLI for frontend visual QA: fixed-viewport screenshots, pixel diff against reference PNGs, configured test runs, and JSON / Markdown / HTML reports. The CLI is deterministic and does **not** call an LLM. Pair it with the Cursor Skill in this repo for the full PRD + Figma workflow.
4
4
 
5
5
  ## Install
6
6
 
7
- This package is not yet published to the npm registry. Install it from a
8
- local checkout or a generated tarball:
9
-
10
7
  ```bash
11
- pnpm add -D /absolute/path/to/frontend-precision/packages/cli
8
+ pnpm add -D frontend-precision
12
9
  pnpm exec frontend-precision init
13
10
  pnpm exec playwright install chromium
14
11
  ```
15
12
 
16
- After an npm release is verified, the supported registry command will be:
13
+ Requires Node.js 20+.
14
+
15
+ ## Verify loop
17
16
 
18
17
  ```bash
19
- pnpm add -D frontend-precision
18
+ pnpm exec frontend-precision capture --run-id <id>
19
+ pnpm exec frontend-precision visual --run-id <id>
20
+ pnpm exec frontend-precision test --run-id <id>
21
+ pnpm exec frontend-precision report --run-id <id>
20
22
  ```
21
23
 
24
+ `report` is overall `PASS` only when capture, visual, and tests all succeed. Missing stages → `SKIPPED`; failures → `FAIL`; too many fix attempts → `BLOCKED`.
25
+
22
26
  ## Commands
23
27
 
24
28
  | Command | Purpose |
25
29
  |---|---|
26
- | `init` | Create config, Cursor Skill, rules, and templates (does not overwrite by default) |
27
- | `doctor` | Check Node, pnpm, config, baseUrl (2xx), references, Playwright, Skill |
30
+ | `init` | Config, Cursor Skill, and rule templates |
31
+ | `doctor` | Environment and project checks |
28
32
  | `capture` | Screenshot configured routes |
29
- | `visual` | Compare screenshots to reference PNGs |
30
- | `test` | Run `testCommand` (and optional checks) and record results |
31
- | `report` | Aggregate capture/visual/test into `report.json` / `.md` / `.html` |
32
-
33
- Typical verify loop:
34
-
35
- ```bash
36
- frontend-precision capture --run-id <id>
37
- frontend-precision visual --run-id <id>
38
- frontend-precision test --run-id <id>
39
- frontend-precision report --run-id <id>
40
- ```
41
-
42
- Overall `report` status is `PASS` only when capture, visual, and tests all completed successfully. Missing stages yield `SKIPPED`; failures yield `FAIL`; `fixAttempts > maxFixAttempts` yields `BLOCKED`.
33
+ | `visual` | Pixel-diff against reference PNGs |
34
+ | `test` | Run configured `testCommand` |
35
+ | `report` | Aggregate JSON / Markdown / HTML |
43
36
 
44
- ## Docs
37
+ ## Links
45
38
 
46
- See the repository root README for Skill workflow, Figma MCP notes, and the Vite demo walkthrough.
39
+ - Package: https://www.npmjs.com/package/frontend-precision
40
+ - Full workflow (Chinese docs): https://github.com/spike-soc/frontend-precision
package/dist/index.js CHANGED
@@ -1128,9 +1128,7 @@ function evaluateAcceptanceEvidence(acceptance, options) {
1128
1128
  );
1129
1129
  continue;
1130
1130
  }
1131
- if (hints.needsUnitMention && !testBlob.includes(acToken) && !/tokenDramaPreviewPlaylistLogic|playlistLogic|\.test\.ts|pass/.test(
1132
- testBlob
1133
- )) {
1131
+ if (hints.needsUnitMention && !testBlob.includes(acToken) && !/\.test\.ts|pass/.test(testBlob)) {
1134
1132
  unverified.push(`acceptance_evidence_weak:${row.id}`);
1135
1133
  }
1136
1134
  }
@@ -1581,7 +1579,7 @@ ${report.unverified.map((u) => ` - ${u}`).join("\n")}` : ""
1581
1579
  var program = new Command();
1582
1580
  program.name("frontend-precision").description(
1583
1581
  "Cursor-first frontend precision toolkit (screenshot, visual diff, tests, reports)"
1584
- ).version("0.1.0");
1582
+ ).version("0.1.1");
1585
1583
  program.command("init").description("Initialize config, Cursor Skill, and rule templates").option("--force", "Overwrite existing files", false).action((opts) => {
1586
1584
  const result = runInit({ force: opts.force });
1587
1585
  console.log(result.messages.join("\n"));
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/config/schema.ts","../src/lib/safe-slug.ts","../src/lib/templates.ts","../src/commands/doctor.ts","../src/config/load.ts","../src/lib/http-status.ts","../src/commands/capture.ts","../src/lib/screenshot.ts","../src/lib/paths.ts","../src/lib/run-id.ts","../src/lib/routes.ts","../src/commands/visual.ts","../src/lib/visual-diff.ts","../src/lib/test-runner.ts","../src/commands/test.ts","../src/commands/report.ts","../src/lib/report-builder.ts","../src/lib/acceptance.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { runInit } from './commands/init.js';\nimport { runDoctor, formatDoctor } from './commands/doctor.js';\nimport { runCapture } from './commands/capture.js';\nimport { runVisual } from './commands/visual.js';\nimport { runTestCommand } from './commands/test.js';\nimport { runReport } from './commands/report.js';\nimport { ConfigError } from './config/load.js';\n\nconst program = new Command();\n\nprogram\n .name('frontend-precision')\n .description(\n 'Cursor-first frontend precision toolkit (screenshot, visual diff, tests, reports)',\n )\n .version('0.1.0');\n\nprogram\n .command('init')\n .description('Initialize config, Cursor Skill, and rule templates')\n .option('--force', 'Overwrite existing files', false)\n .action((opts: { force?: boolean }) => {\n const result = runInit({ force: opts.force });\n console.log(result.messages.join('\\n'));\n });\n\nprogram\n .command('doctor')\n .description('Diagnose environment and project readiness')\n .action(async () => {\n const { checks, ok } = await runDoctor();\n console.log(formatDoctor(checks));\n process.exitCode = ok ? 0 : 1;\n });\n\nprogram\n .command('capture')\n .description('Screenshot configured routes with Playwright')\n .option('--run-id <id>', 'Reuse or set run id')\n .option('--route <name>', 'Only capture named route (repeatable)', (v, acc: string[]) => {\n acc.push(v);\n return acc;\n }, [])\n .action(async (opts: { runId?: string; route: string[] }) => {\n try {\n const result = await runCapture({\n runId: opts.runId,\n routes: opts.route.length ? opts.route : undefined,\n });\n console.log(result.message);\n process.exitCode = result.ok ? 0 : 1;\n } catch (err) {\n handleError(err);\n }\n });\n\nprogram\n .command('visual')\n .description('Compare screenshots against reference PNGs')\n .requiredOption('--run-id <id>', 'Run id from capture')\n .option('--route <name>', 'Only compare named route (repeatable)', (v, acc: string[]) => {\n acc.push(v);\n return acc;\n }, [])\n .action(async (opts: { runId: string; route: string[] }) => {\n try {\n const result = await runVisual({\n runId: opts.runId,\n routes: opts.route.length ? opts.route : undefined,\n });\n console.log(result.message);\n process.exitCode = result.ok ? 0 : 1;\n } catch (err) {\n handleError(err);\n }\n });\n\nprogram\n .command('test')\n .description('Run configured test commands and record results')\n .option('--run-id <id>', 'Attach results to an existing run id')\n .action(async (opts: { runId?: string }) => {\n try {\n const result = await runTestCommand({ runId: opts.runId });\n console.log(result.message);\n process.exitCode = result.ok ? 0 : 1;\n } catch (err) {\n handleError(err);\n }\n });\n\nprogram\n .command('report')\n .description('Aggregate capture/visual/test into JSON/Markdown/HTML reports')\n .requiredOption('--run-id <id>', 'Run id to report')\n .option('--task-id <id>', 'Task folder under .frontend-precision/tasks/')\n .option('--fix-attempts <n>', 'Current auto-fix attempt count', (v) =>\n Number(v),\n )\n .option('--blocked <reason>', 'Mark report as BLOCKED with reason')\n .action(\n (opts: {\n runId: string;\n taskId?: string;\n fixAttempts?: number;\n blocked?: string;\n }) => {\n try {\n const result = runReport({\n runId: opts.runId,\n taskId: opts.taskId,\n fixAttempts: opts.fixAttempts,\n blockedReason: opts.blocked,\n });\n console.log(result.message);\n process.exitCode = result.ok ? 0 : 1;\n } catch (err) {\n handleError(err);\n }\n },\n );\n\nfunction handleError(err: unknown): never {\n if (err instanceof ConfigError) {\n console.error(err.message);\n process.exit(1);\n }\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n}\n\nprogram.parseAsync(process.argv).catch((err) => {\n handleError(err);\n});\n","import {\n cpSync,\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n appendFileSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { CONFIG_FILENAME } from '../config/schema.js';\nimport { getTemplatesRoot } from '../lib/templates.js';\n\nexport type InitOptions = {\n cwd?: string;\n force?: boolean;\n};\n\nfunction copyIfNeeded(\n src: string,\n dest: string,\n force: boolean,\n): 'created' | 'skipped' | 'overwritten' {\n if (existsSync(dest) && !force) return 'skipped';\n mkdirSync(dirname(dest), { recursive: true });\n cpSync(src, dest, { recursive: true });\n return existsSync(dest) && force ? 'overwritten' : 'created';\n}\n\nexport function runInit(options: InitOptions = {}): {\n messages: string[];\n configPath: string;\n} {\n const cwd = options.cwd ?? process.cwd();\n const force = options.force ?? false;\n const templates = getTemplatesRoot();\n const messages: string[] = [];\n\n const defaultConfigPath = join(templates, 'config.default.json');\n const configPath = join(cwd, CONFIG_FILENAME);\n\n if (existsSync(configPath) && !force) {\n messages.push(`Skipped ${CONFIG_FILENAME} (exists). Use --force to overwrite.`);\n } else {\n writeFileSync(configPath, readFileSync(defaultConfigPath));\n messages.push(\n existsSync(configPath) && force\n ? `Overwrote ${CONFIG_FILENAME}`\n : `Created ${CONFIG_FILENAME}`,\n );\n }\n\n const skillSrc = join(templates, 'skill', 'frontend-precision');\n const skillDest = join(cwd, '.cursor', 'skills', 'frontend-precision');\n if (existsSync(skillDest) && !force) {\n messages.push(\n 'Skipped .cursor/skills/frontend-precision (exists). Use --force to overwrite.',\n );\n } else {\n mkdirSync(join(cwd, '.cursor', 'skills'), { recursive: true });\n cpSync(skillSrc, skillDest, { recursive: true });\n messages.push(\n force\n ? 'Overwrote .cursor/skills/frontend-precision'\n : 'Installed .cursor/skills/frontend-precision',\n );\n }\n\n const ruleSrc = join(templates, 'rules', 'frontend-precision.mdc');\n const ruleDest = join(cwd, '.cursor', 'rules', 'frontend-precision.mdc');\n const ruleStatus = copyIfNeeded(ruleSrc, ruleDest, force);\n messages.push(\n ruleStatus === 'skipped'\n ? 'Skipped .cursor/rules/frontend-precision.mdc (exists).'\n : `${ruleStatus === 'overwritten' ? 'Overwrote' : 'Installed'} .cursor/rules/frontend-precision.mdc`,\n );\n\n const taskTplSrc = join(templates, 'task-templates');\n const taskTplDest = join(\n cwd,\n '.frontend-precision',\n 'templates',\n );\n if (!existsSync(taskTplDest) || force) {\n mkdirSync(taskTplDest, { recursive: true });\n cpSync(taskTplSrc, taskTplDest, { recursive: true });\n messages.push('Installed .frontend-precision/templates (preflight/plan/acceptance)');\n }\n\n const gitignorePath = join(cwd, '.gitignore');\n const ignoreLine = '.frontend-precision/artifacts/';\n if (existsSync(gitignorePath)) {\n const content = readFileSync(gitignorePath, 'utf8');\n if (!content.includes('.frontend-precision')) {\n appendFileSync(gitignorePath, `\\n# frontend-precision\\n.frontend-precision/artifacts/\\n`);\n messages.push('Appended .frontend-precision/artifacts/ to .gitignore');\n } else {\n messages.push(`Skipped .gitignore (already mentions frontend-precision)`);\n }\n } else {\n writeFileSync(\n gitignorePath,\n `# frontend-precision\\n${ignoreLine}\\n`,\n );\n messages.push('Created .gitignore with .frontend-precision/artifacts/');\n }\n\n messages.push('');\n messages.push('Next steps:');\n messages.push(' 1. Edit frontend-precision.config.json (baseUrl, routes, references)');\n messages.push(' 2. Install Playwright browsers: pnpm exec playwright install chromium');\n messages.push(' 3. In Cursor: /frontend-precision with your PRD + Figma link');\n messages.push(' 4. Run: frontend-precision doctor');\n\n return { messages, configPath };\n}\n","import { z } from 'zod';\nimport { isSafeSlug } from '../lib/safe-slug.js';\n\nconst ViewportSchema = z.object({\n width: z.number().int().positive(),\n height: z.number().int().positive(),\n});\n\nexport const RouteSchema = z.object({\n name: z\n .string()\n .min(1)\n .refine(isSafeSlug, {\n message:\n \"Invalid route name. Use a slug of letters, digits, '.', '_' or '-' with no path separators or '..'.\",\n }),\n path: z.string().min(1),\n reference: z.string().optional(),\n /**\n * figma: 设计稿导出基线(可宣称视觉验收)\n * regression: 实现自截图回归基线(不得当作 Figma PASS)\n */\n referenceKind: z.enum(['figma', 'regression']).default('figma'),\n viewport: ViewportSchema.optional(),\n waitFor: z.string().optional(),\n hide: z.array(z.string()).optional(),\n});\n\nconst RoutesSchema = z\n .array(RouteSchema)\n .min(1)\n .superRefine((routes, ctx) => {\n const seen = new Set<string>();\n routes.forEach((route, index) => {\n if (seen.has(route.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [index, 'name'],\n message: `Duplicate route name: ${route.name}`,\n });\n }\n seen.add(route.name);\n });\n });\n\nexport const ConfigSchema = z.object({\n baseUrl: z.string().url(),\n viewport: ViewportSchema,\n routes: RoutesSchema,\n visual: z\n .object({\n pixelThreshold: z.number().min(0).max(1).default(0.1),\n maxDiffRatio: z.number().min(0).max(1).default(0.03),\n })\n .default({ pixelThreshold: 0.1, maxDiffRatio: 0.03 }),\n testCommand: z.string().min(1),\n maxFixAttempts: z.number().int().min(0).max(10).default(3),\n browser: z.enum(['chromium', 'firefox', 'webkit']).default('chromium'),\n settleMs: z.number().int().nonnegative().default(300),\n /** 绑定任务目录名,report 自动读取 acceptance.md */\n taskId: z.string().min(1).optional(),\n gates: z\n .object({\n /** 报告必须加载到至少 1 条 AC,否则 FAIL */\n requireAcceptance: z.boolean().default(true),\n /** 任一 AC 为 PENDING/PARTIAL/FAIL 则整体不能 PASS */\n requireAcceptanceAllPass: z.boolean().default(true),\n /** 存在 referenceKind=regression 的路由时,不得宣称 Figma 视觉通过 */\n requireFigmaVisual: z.boolean().default(false),\n /** reference 与 actual 字节完全相同 → FAIL(防自截图冒充) */\n rejectIdenticalBaseline: z.boolean().default(true),\n })\n .default({\n requireAcceptance: true,\n requireAcceptanceAllPass: true,\n requireFigmaVisual: false,\n rejectIdenticalBaseline: true,\n }),\n checks: z\n .object({\n typecheck: z.string().optional(),\n lint: z.string().optional(),\n unit: z.string().optional(),\n e2e: z.string().optional(),\n })\n .optional(),\n});\n\nexport type FrontendPrecisionConfig = z.infer<typeof ConfigSchema>;\nexport type RouteConfig = z.infer<typeof RouteSchema>;\n\nexport const CONFIG_FILENAME = 'frontend-precision.config.json';\n","const SAFE_SLUG_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\n\nexport function isSafeSlug(value: unknown): value is string {\n return (\n typeof value === 'string' &&\n !value.includes('..') &&\n !value.includes('/') &&\n !value.includes('\\\\') &&\n SAFE_SLUG_PATTERN.test(value)\n );\n}\n\nexport function assertSafeSlug(value: unknown, label: string): string {\n if (!isSafeSlug(value)) {\n throw new Error(\n `Invalid ${label} \"${String(value)}\". Use a slug of letters, digits, '.', '_' or '-' (no path separators or '..').`,\n );\n }\n return value;\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Resolve packaged templates directory (dist/templates after build,\n * or src/templates when running from source via vitest/tsx).\n */\nexport function getTemplatesRoot(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n join(here, 'templates'),\n join(here, '..', 'templates'),\n join(here, '..', '..', 'src', 'templates'),\n join(here, '..', '..', 'dist', 'templates'),\n ];\n\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n\n throw new Error(\n 'Cannot locate templates directory. Rebuild the CLI (`pnpm build`).',\n );\n}\n","import { existsSync } from 'node:fs';\nimport { resolve, join } from 'node:path';\nimport { spawnSync, type SpawnSyncReturns } from 'node:child_process';\nimport { loadConfig, ConfigError } from '../config/load.js';\nimport { CONFIG_FILENAME, type FrontendPrecisionConfig } from '../config/schema.js';\nimport { isHealthyHttpStatus } from '../lib/http-status.js';\n\nexport type DoctorCheck = {\n name: string;\n ok: boolean;\n detail: string;\n fix?: string;\n};\n\nexport type DoctorDeps = {\n spawnSync: typeof spawnSync;\n fetch: typeof fetch;\n};\n\nconst defaultDeps: DoctorDeps = {\n spawnSync,\n fetch: globalThis.fetch.bind(globalThis),\n};\n\nfunction spawnOk(\n result: SpawnSyncReturns<string>,\n): boolean {\n return result.status === 0;\n}\n\nexport async function runDoctor(\n cwd = process.cwd(),\n deps: DoctorDeps = defaultDeps,\n): Promise<{\n checks: DoctorCheck[];\n ok: boolean;\n}> {\n const checks: DoctorCheck[] = [];\n\n const nodeMajor = Number(process.versions.node.split('.')[0]);\n checks.push({\n name: 'Node.js >= 20',\n ok: nodeMajor >= 20,\n detail: `Detected v${process.versions.node}`,\n fix: 'Install Node.js 20+ from https://nodejs.org/',\n });\n\n const pnpm = deps.spawnSync('pnpm', ['--version'], { encoding: 'utf8' });\n checks.push({\n name: 'pnpm',\n ok: spawnOk(pnpm),\n detail: spawnOk(pnpm) ? `pnpm ${pnpm.stdout.trim()}` : 'pnpm not found',\n fix: 'corepack enable && corepack prepare pnpm@latest --activate',\n });\n\n let config: FrontendPrecisionConfig | null = null;\n let configDetail = `Missing ${CONFIG_FILENAME}`;\n let configFix = 'Run `frontend-precision init`';\n try {\n config = loadConfig(cwd);\n configDetail = `Valid config at ${join(cwd, CONFIG_FILENAME)}`;\n } catch (err) {\n if (err instanceof ConfigError) {\n configDetail = err.message;\n configFix = err.message.includes('validation failed')\n ? 'Fix the invalid fields listed above in frontend-precision.config.json'\n : 'Run `frontend-precision init` (use --force only if you intend to replace the file)';\n } else {\n configDetail = (err as Error).message;\n }\n }\n checks.push({\n name: CONFIG_FILENAME,\n ok: Boolean(config),\n detail: configDetail,\n fix: configFix,\n });\n\n if (config) {\n let reachable = false;\n let reachDetail = '';\n try {\n const res = await deps.fetch(config.baseUrl, {\n signal: AbortSignal.timeout(5000),\n });\n reachable = isHealthyHttpStatus(res.status);\n reachDetail = `HTTP ${res.status} from ${config.baseUrl}`;\n if (!reachable) {\n reachDetail += ' (expected 2xx)';\n }\n } catch (err) {\n reachDetail = `Unreachable: ${(err as Error).message}`;\n }\n checks.push({\n name: 'baseUrl reachable',\n ok: reachable,\n detail: reachDetail,\n fix: 'Start your dev server, or update baseUrl in config',\n });\n\n for (const route of config.routes) {\n if (!route.reference) {\n checks.push({\n name: `reference:${route.name}`,\n ok: false,\n detail: 'No reference path configured',\n fix: 'Set routes[].reference to a PNG baseline (from Figma export or Agent)',\n });\n continue;\n }\n const refPath = resolve(cwd, route.reference);\n checks.push({\n name: `reference:${route.name}`,\n ok: existsSync(refPath),\n detail: existsSync(refPath)\n ? `Found ${refPath}`\n : `Missing ${refPath}`,\n fix: 'Export baseline PNG or ask Agent to place Figma screenshot',\n });\n }\n\n const testCmd = config.testCommand.trim();\n checks.push({\n name: 'testCommand non-empty',\n ok: testCmd.length > 0,\n detail: testCmd || '(empty)',\n fix: 'Set testCommand e.g. \"pnpm test:e2e\"',\n });\n }\n\n const pwInstall = deps.spawnSync(\n process.platform === 'win32' ? 'npx.cmd' : 'npx',\n ['playwright', '--version'],\n { cwd, encoding: 'utf8' },\n );\n checks.push({\n name: 'Playwright CLI',\n ok: spawnOk(pwInstall),\n detail: spawnOk(pwInstall)\n ? pwInstall.stdout.trim() || 'playwright available'\n : 'Playwright not available via npx',\n fix: 'pnpm add -D playwright && pnpm exec playwright install chromium',\n });\n\n const skillPath = join(\n cwd,\n '.cursor',\n 'skills',\n 'frontend-precision',\n 'SKILL.md',\n );\n checks.push({\n name: 'Cursor Skill installed',\n ok: existsSync(skillPath),\n detail: existsSync(skillPath) ? skillPath : 'Skill not found',\n fix: 'Run `frontend-precision init`',\n });\n\n return { checks, ok: checks.every((c) => c.ok) };\n}\n\nexport function formatDoctor(checks: DoctorCheck[]): string {\n const lines = ['frontend-precision doctor', ''];\n for (const c of checks) {\n lines.push(`${c.ok ? '✓' : '✗'} ${c.name}`);\n lines.push(` ${c.detail}`);\n if (!c.ok && c.fix) lines.push(` Fix: ${c.fix}`);\n }\n lines.push('');\n lines.push(\n checks.every((c) => c.ok)\n ? 'All checks passed.'\n : 'Some checks failed. Fix the items above before capture/visual/test.',\n );\n return lines.join('\\n');\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport {\n CONFIG_FILENAME,\n ConfigSchema,\n type FrontendPrecisionConfig,\n} from './schema.js';\n\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nexport function loadConfig(cwd = process.cwd()): FrontendPrecisionConfig {\n const path = resolve(cwd, CONFIG_FILENAME);\n if (!existsSync(path)) {\n throw new ConfigError(\n `Missing ${CONFIG_FILENAME}. Run \\`frontend-precision init\\` first.`,\n );\n }\n\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(path, 'utf8'));\n } catch (err) {\n throw new ConfigError(\n `Invalid JSON in ${CONFIG_FILENAME}: ${(err as Error).message}`,\n );\n }\n\n const parsed = ConfigSchema.safeParse(raw);\n if (!parsed.success) {\n const details = parsed.error.issues\n .map((i) => ` - ${i.path.join('.') || '(root)'}: ${i.message}`)\n .join('\\n');\n throw new ConfigError(`${CONFIG_FILENAME} validation failed:\\n${details}`);\n }\n\n return parsed.data;\n}\n\nexport function tryLoadConfig(\n cwd = process.cwd(),\n): FrontendPrecisionConfig | null {\n try {\n return loadConfig(cwd);\n } catch {\n return null;\n }\n}\n","/** Treat only 2xx as healthy for doctor/capture readiness. */\nexport function isHealthyHttpStatus(status: number): boolean {\n return Number.isInteger(status) && status >= 200 && status < 300;\n}\n","import { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { loadConfig } from '../config/load.js';\nimport { captureRoutes } from '../lib/screenshot.js';\nimport { createRunId } from '../lib/run-id.js';\nimport { artifactsDir, ensureDir } from '../lib/paths.js';\n\nexport async function runCapture(options: {\n cwd?: string;\n runId?: string;\n routes?: string[];\n}): Promise<{ runId: string; ok: boolean; message: string }> {\n const cwd = options.cwd ?? process.cwd();\n const config = loadConfig(cwd);\n const runId = options.runId ?? createRunId();\n ensureDir(artifactsDir(runId, cwd));\n\n const result = await captureRoutes({\n config,\n runId,\n cwd,\n routeFilter: options.routes,\n });\n\n writeFileSync(\n join(artifactsDir(runId, cwd), 'run-id.txt'),\n runId,\n 'utf8',\n );\n\n const failed = result.screenshots.filter((s) => !s.ok);\n const ok = failed.length === 0;\n const message = [\n `Capture run: ${runId}`,\n ...result.screenshots.map(\n (s) =>\n ` ${s.ok ? '✓' : '✗'} ${s.name} → ${s.path}${s.error ? ` (${s.error})` : ''}`,\n ),\n `Meta: ${result.metaPath}`,\n ].join('\\n');\n\n return { runId, ok, message };\n}\n","import { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { chromium, firefox, webkit, type BrowserType } from 'playwright';\nimport type { FrontendPrecisionConfig, RouteConfig } from '../config/schema.js';\nimport { ensureDir, screenshotPath, screenshotsDir } from './paths.js';\nimport { isHealthyHttpStatus } from './http-status.js';\nimport { selectRoutes } from './routes.js';\n\nfunction browserType(name: FrontendPrecisionConfig['browser']): BrowserType {\n switch (name) {\n case 'firefox':\n return firefox;\n case 'webkit':\n return webkit;\n default:\n return chromium;\n }\n}\n\nexport type CaptureResult = {\n runId: string;\n screenshots: Array<{\n name: string;\n path: string;\n url: string;\n ok: boolean;\n error?: string;\n }>;\n metaPath: string;\n};\n\nexport async function captureRoutes(options: {\n config: FrontendPrecisionConfig;\n runId: string;\n cwd?: string;\n routeFilter?: string[];\n}): Promise<CaptureResult> {\n const { config, runId, cwd, routeFilter } = options;\n const outDir = ensureDir(screenshotsDir(runId, cwd));\n const routes = selectRoutes(config.routes, routeFilter);\n\n const browser = await browserType(config.browser).launch({ headless: true });\n const context = await browser.newContext({\n viewport: config.viewport,\n deviceScaleFactor: 1,\n });\n const page = await context.newPage();\n const screenshots: CaptureResult['screenshots'] = [];\n const routeViewports: Record<string, { width: number; height: number }> = {};\n\n try {\n for (const route of routes) {\n const url = new URL(route.path, config.baseUrl).toString();\n const filePath = screenshotPath(runId, route.name, cwd);\n const viewport = route.viewport ?? config.viewport;\n routeViewports[route.name] = viewport;\n try {\n await page.setViewportSize(viewport);\n // Prefer load over networkidle so 4xx/5xx fail fast instead of hanging.\n const response = await page.goto(url, {\n waitUntil: 'load',\n timeout: 60_000,\n });\n const status = response?.status() ?? 0;\n const finalUrl = page.url();\n if (!response || !isHealthyHttpStatus(status)) {\n throw new Error(\n `HTTP ${status} for ${url} (final: ${finalUrl}); expected 2xx`,\n );\n }\n await page.waitForLoadState('networkidle', { timeout: 30_000 }).catch(\n () => undefined,\n );\n await page.evaluate(async () => {\n const fonts = (\n globalThis as unknown as {\n document?: { fonts?: { ready?: Promise<unknown> } };\n }\n ).document?.fonts;\n if (fonts?.ready) await fonts.ready;\n });\n if (route.waitFor) {\n await page.waitForSelector(route.waitFor, { timeout: 30_000 });\n }\n if (route.hide?.length) {\n await page.addStyleTag({\n content: route.hide\n .map((sel) => `${sel}{visibility:hidden !important;}`)\n .join('\\n'),\n });\n }\n if (config.settleMs > 0) {\n await page.waitForTimeout(config.settleMs);\n }\n await page.screenshot({ path: filePath, fullPage: false });\n screenshots.push({ name: route.name, path: filePath, url: finalUrl, ok: true });\n } catch (err) {\n screenshots.push({\n name: route.name,\n path: filePath,\n url,\n ok: false,\n error: (err as Error).message,\n });\n }\n }\n } finally {\n await context.close();\n await browser.close();\n }\n\n const meta = {\n runId,\n capturedAt: new Date().toISOString(),\n viewport: config.viewport,\n routeViewports,\n browser: config.browser,\n baseUrl: config.baseUrl,\n screenshots,\n };\n const metaPath = join(outDir, 'capture-meta.json');\n writeFileSync(metaPath, JSON.stringify(meta, null, 2));\n\n return { runId, screenshots, metaPath };\n}\n\nexport function describeRoute(route: RouteConfig): string {\n return `${route.name} (${route.path})`;\n}\n","import { mkdirSync } from 'node:fs';\nimport { join, resolve, sep } from 'node:path';\nimport { assertSafeRunId } from './run-id.js';\nimport { assertSafeSlug } from './safe-slug.js';\n\nexport const ARTIFACTS_ROOT = '.frontend-precision';\nexport const TASKS_DIR = 'tasks';\nexport const ARTIFACTS_DIR = 'artifacts';\n\nexport function resolveCwd(cwd?: string): string {\n return resolve(cwd ?? process.cwd());\n}\n\nexport function fpRoot(cwd?: string): string {\n return join(resolveCwd(cwd), ARTIFACTS_ROOT);\n}\n\nexport function artifactsDir(runId: string, cwd?: string): string {\n const safeId = assertSafeRunId(runId);\n const root = resolve(join(fpRoot(cwd), ARTIFACTS_DIR));\n const resolved = resolve(root, safeId);\n const prefix = root.endsWith(sep) ? root : root + sep;\n if (resolved !== root && !resolved.startsWith(prefix)) {\n throw new Error(\n `Invalid run id \"${runId}\": resolved path escapes artifacts directory.`,\n );\n }\n return resolved;\n}\n\nexport function screenshotsDir(runId: string, cwd?: string): string {\n return join(artifactsDir(runId, cwd), 'screenshots');\n}\n\nfunction assertContainedPath(\n root: string,\n candidate: string,\n label: string,\n): string {\n const resolvedRoot = resolve(root);\n const resolvedCandidate = resolve(candidate);\n const prefix = resolvedRoot.endsWith(sep)\n ? resolvedRoot\n : resolvedRoot + sep;\n if (\n resolvedCandidate !== resolvedRoot &&\n !resolvedCandidate.startsWith(prefix)\n ) {\n throw new Error(`${label} escapes its output directory.`);\n }\n return resolvedCandidate;\n}\n\nexport function screenshotPath(\n runId: string,\n routeName: string,\n cwd?: string,\n): string {\n const safeName = assertSafeSlug(routeName, 'route name');\n const root = screenshotsDir(runId, cwd);\n return assertContainedPath(\n root,\n resolve(root, `${safeName}.png`),\n 'Route screenshot path',\n );\n}\n\nexport function visualDir(\n runId: string,\n routeName: string,\n cwd?: string,\n): string {\n const safeName = assertSafeSlug(routeName, 'route name');\n const root = resolve(artifactsDir(runId, cwd), 'visual');\n return assertContainedPath(\n root,\n resolve(root, safeName),\n 'Route visual path',\n );\n}\n\nexport function ensureDir(path: string): string {\n mkdirSync(path, { recursive: true });\n return path;\n}\n\nexport function taskDir(taskId: string, cwd?: string): string {\n return join(fpRoot(cwd), TASKS_DIR, taskId);\n}\n","import { assertSafeSlug } from './safe-slug.js';\n\nexport function createRunId(date = new Date()): string {\n const iso = date.toISOString().replace(/[:.]/g, '-');\n const rand = Math.random().toString(36).slice(2, 8);\n return `${iso}_${rand}`;\n}\n\nexport function assertSafeRunId(runId: string): string {\n return assertSafeSlug(runId, 'run id');\n}\n","import type { RouteConfig } from '../config/schema.js';\n\nexport function selectRoutes(\n routes: RouteConfig[],\n filter?: string[],\n): RouteConfig[] {\n if (!filter?.length) return routes;\n\n const missing = filter.filter(\n (name) => !routes.some((route) => route.name === name),\n );\n if (missing.length > 0) {\n const valid = routes.map((r) => r.name).join(', ') || '(none)';\n throw new Error(\n `Unknown route(s): ${missing.join(', ')}. Valid routes: ${valid}`,\n );\n }\n\n return routes.filter((route) => filter.includes(route.name));\n}\n","import { resolve } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { loadConfig } from '../config/load.js';\nimport { compareImages } from '../lib/visual-diff.js';\nimport { createRunId } from '../lib/run-id.js';\nimport { selectRoutes } from '../lib/routes.js';\nimport {\n artifactsDir,\n ensureDir,\n screenshotPath,\n visualDir,\n} from '../lib/paths.js';\n\nexport async function runVisual(options: {\n cwd?: string;\n runId?: string;\n routes?: string[];\n}): Promise<{ runId: string; ok: boolean; message: string }> {\n const cwd = options.cwd ?? process.cwd();\n const config = loadConfig(cwd);\n let runId = options.runId;\n\n if (!runId) {\n runId = createRunId();\n }\n\n ensureDir(artifactsDir(runId, cwd));\n\n const routes = selectRoutes(config.routes, options.routes);\n\n const lines: string[] = [`Visual run: ${runId}`];\n let ok = true;\n\n for (const route of routes) {\n const actualPath = screenshotPath(runId, route.name, cwd);\n const referencePath = route.reference\n ? resolve(cwd, route.reference)\n : '';\n const outDir = visualDir(runId, route.name, cwd);\n\n if (!route.reference) {\n ok = false;\n lines.push(` ✗ ${route.name}: no reference configured`);\n compareImages({\n name: route.name,\n referencePath: '',\n actualPath,\n outputDir: outDir,\n pixelThreshold: config.visual.pixelThreshold,\n maxDiffRatio: config.visual.maxDiffRatio,\n referenceKind: route.referenceKind,\n rejectIdenticalBaseline: config.gates.rejectIdenticalBaseline,\n });\n continue;\n }\n\n if (!existsSync(actualPath)) {\n ok = false;\n lines.push(\n ` ✗ ${route.name}: missing actual screenshot (run capture first)`,\n );\n }\n\n const result = compareImages({\n name: route.name,\n referencePath,\n actualPath,\n outputDir: outDir,\n pixelThreshold: config.visual.pixelThreshold,\n maxDiffRatio: config.visual.maxDiffRatio,\n referenceKind: route.referenceKind,\n rejectIdenticalBaseline: config.gates.rejectIdenticalBaseline,\n });\n\n if (!result.passed) ok = false;\n const kindTag = `kind=${result.referenceKind}`;\n const identicalTag = result.identicalBytes ? ' identical' : '';\n lines.push(\n ` ${result.passed ? '✓' : '✗'} ${route.name}: diffRatio=${(result.diffRatio * 100).toFixed(3)}% (${kindTag}${identicalTag})${result.reason ? ` — ${result.reason}` : ''}`,\n );\n lines.push(` → ${result.paths.resultJson}`);\n }\n\n return { runId, ok, message: lines.join('\\n') };\n}\n","import {\n existsSync,\n readFileSync,\n writeFileSync,\n mkdirSync,\n copyFileSync,\n} from 'node:fs';\nimport { createHash } from 'node:crypto';\nimport { dirname, join } from 'node:path';\nimport { PNG } from 'pngjs';\nimport pixelmatch from 'pixelmatch';\n\nexport type VisualDiffInput = {\n name: string;\n referencePath: string;\n actualPath: string;\n outputDir: string;\n pixelThreshold: number;\n maxDiffRatio: number;\n /** figma | regression — regression 不得当作设计验收 */\n referenceKind?: 'figma' | 'regression';\n /** 为 true 时,reference 与 actual 字节相同则判定失败 */\n rejectIdenticalBaseline?: boolean;\n};\n\nexport type VisualDiffResult = {\n name: string;\n width: number;\n height: number;\n diffRatio: number;\n diffPixels: number;\n totalPixels: number;\n pixelThreshold: number;\n maxDiffRatio: number;\n passed: boolean;\n reason: string | null;\n referenceKind: 'figma' | 'regression';\n identicalBytes: boolean;\n paths: {\n reference: string;\n actual: string;\n diff: string | null;\n resultJson: string;\n };\n};\n\nfunction sha256File(path: string): string {\n return createHash('sha256').update(readFileSync(path)).digest('hex');\n}\n\nfunction ensureParent(filePath: string): void {\n mkdirSync(dirname(filePath), { recursive: true });\n}\n\nexport function compareImages(input: VisualDiffInput): VisualDiffResult {\n const {\n name,\n referencePath,\n actualPath,\n outputDir,\n pixelThreshold,\n maxDiffRatio,\n referenceKind = 'figma',\n rejectIdenticalBaseline = true,\n } = input;\n\n mkdirSync(outputDir, { recursive: true });\n const outRef = join(outputDir, 'reference.png');\n const outActual = join(outputDir, 'actual.png');\n const outDiff = join(outputDir, 'diff.png');\n const outJson = join(outputDir, 'visual-result.json');\n\n const base = {\n name,\n pixelThreshold,\n maxDiffRatio,\n referenceKind,\n identicalBytes: false,\n paths: {\n reference: outRef,\n actual: outActual,\n diff: null as string | null,\n resultJson: outJson,\n },\n };\n\n if (!existsSync(referencePath)) {\n const result: VisualDiffResult = {\n ...base,\n width: 0,\n height: 0,\n diffRatio: 1,\n diffPixels: 0,\n totalPixels: 0,\n passed: false,\n reason: `missing_reference: ${referencePath}`,\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n if (!existsSync(actualPath)) {\n copyFileSync(referencePath, outRef);\n const result: VisualDiffResult = {\n ...base,\n width: 0,\n height: 0,\n diffRatio: 1,\n diffPixels: 0,\n totalPixels: 0,\n passed: false,\n reason: `missing_actual: ${actualPath}`,\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n const identicalBytes =\n sha256File(referencePath) === sha256File(actualPath);\n\n copyFileSync(referencePath, outRef);\n copyFileSync(actualPath, outActual);\n\n // Figma 导出与浏览器截图像素几乎不可能字节级相同;相同通常意味着把 capture 当成了「设计基线」。\n // regression 基线在无 UI 变化时允许 identical(回归通过),但报告层不得宣称 Figma 验收。\n const rejectIdentical =\n identicalBytes &&\n rejectIdenticalBaseline &&\n referenceKind === 'figma';\n\n if (rejectIdentical) {\n const result: VisualDiffResult = {\n ...base,\n identicalBytes: true,\n width: 0,\n height: 0,\n diffRatio: 0,\n diffPixels: 0,\n totalPixels: 0,\n passed: false,\n reason:\n 'self_baseline_identical: reference SHA-256 equals capture while referenceKind=figma; export a real Figma PNG or set referenceKind=regression',\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n let refPng: PNG;\n let actPng: PNG;\n try {\n refPng = PNG.sync.read(readFileSync(referencePath));\n actPng = PNG.sync.read(readFileSync(actualPath));\n } catch (err) {\n const result: VisualDiffResult = {\n ...base,\n identicalBytes,\n width: 0,\n height: 0,\n diffRatio: 1,\n diffPixels: 0,\n totalPixels: 0,\n passed: false,\n reason: `image_read_error: ${(err as Error).message}`,\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n if (refPng.width !== actPng.width || refPng.height !== actPng.height) {\n const result: VisualDiffResult = {\n ...base,\n identicalBytes,\n width: actPng.width,\n height: actPng.height,\n diffRatio: 1,\n diffPixels: 0,\n totalPixels: refPng.width * refPng.height,\n passed: false,\n reason: `dimension_mismatch: reference=${refPng.width}x${refPng.height} actual=${actPng.width}x${actPng.height}`,\n paths: { ...base.paths, diff: null },\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n const { width, height } = refPng;\n const diff = new PNG({ width, height });\n const diffPixels = pixelmatch(\n refPng.data,\n actPng.data,\n diff.data,\n width,\n height,\n { threshold: pixelThreshold },\n );\n const totalPixels = width * height;\n const diffRatio = totalPixels === 0 ? 1 : diffPixels / totalPixels;\n const passed = diffRatio <= maxDiffRatio;\n\n ensureParent(outDiff);\n writeFileSync(outDiff, PNG.sync.write(diff));\n\n const result: VisualDiffResult = {\n ...base,\n identicalBytes,\n width,\n height,\n diffRatio,\n diffPixels,\n totalPixels,\n passed,\n reason: passed\n ? identicalBytes\n ? 'identical_bytes_allowed'\n : null\n : `diff_ratio_exceeded: ${(diffRatio * 100).toFixed(3)}% > ${(maxDiffRatio * 100).toFixed(3)}%`,\n paths: { ...base.paths, diff: outDiff },\n };\n\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n}\n","import { spawn } from 'node:child_process';\nimport { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { FrontendPrecisionConfig } from '../config/schema.js';\nimport { artifactsDir, ensureDir } from './paths.js';\n\nexport type TestRunStatus = 'PASS' | 'FAIL' | 'SKIPPED';\n\nexport type SingleTestResult = {\n name: string;\n command: string;\n status: TestRunStatus;\n exitCode: number | null;\n durationMs: number;\n stdout: string;\n stderr: string;\n reason?: string;\n};\n\nexport type TestSuiteResult = {\n runId: string;\n results: SingleTestResult[];\n passed: boolean;\n startedAt: string;\n finishedAt: string;\n};\n\nfunction runShell(\n command: string,\n cwd: string,\n): Promise<{\n exitCode: number | null;\n stdout: string;\n stderr: string;\n durationMs: number;\n}> {\n return new Promise((resolve) => {\n const started = Date.now();\n const child = spawn(command, {\n cwd,\n shell: true,\n env: process.env,\n });\n let stdout = '';\n let stderr = '';\n child.stdout.on('data', (d: Buffer) => {\n stdout += d.toString();\n });\n child.stderr.on('data', (d: Buffer) => {\n stderr += d.toString();\n });\n child.on('error', (err) => {\n stderr += err.message;\n resolve({\n exitCode: 127,\n stdout,\n stderr,\n durationMs: Date.now() - started,\n });\n });\n child.on('close', (code) => {\n resolve({\n exitCode: code,\n stdout,\n stderr,\n durationMs: Date.now() - started,\n });\n });\n });\n}\n\nexport async function runConfiguredTests(options: {\n config: FrontendPrecisionConfig;\n runId: string;\n cwd?: string;\n}): Promise<TestSuiteResult> {\n const cwd = options.cwd ?? process.cwd();\n const startedAt = new Date().toISOString();\n const commands: Array<{ name: string; command: string }> = [];\n\n if (options.config.checks?.typecheck) {\n commands.push({ name: 'typecheck', command: options.config.checks.typecheck });\n }\n if (options.config.checks?.lint) {\n commands.push({ name: 'lint', command: options.config.checks.lint });\n }\n if (options.config.checks?.unit) {\n commands.push({ name: 'unit', command: options.config.checks.unit });\n }\n if (options.config.checks?.e2e) {\n commands.push({ name: 'e2e', command: options.config.checks.e2e });\n }\n\n // Always include primary testCommand\n commands.push({ name: 'testCommand', command: options.config.testCommand });\n\n // Dedupe identical commands\n const seen = new Set<string>();\n const unique = commands.filter((c) => {\n if (seen.has(c.command)) return false;\n seen.add(c.command);\n return true;\n });\n\n const results: SingleTestResult[] = [];\n\n for (const item of unique) {\n if (!item.command.trim()) {\n results.push({\n name: item.name,\n command: item.command,\n status: 'FAIL',\n exitCode: null,\n durationMs: 0,\n stdout: '',\n stderr: '',\n reason: 'empty_command',\n });\n continue;\n }\n\n const run = await runShell(item.command, cwd);\n const status: TestRunStatus = run.exitCode === 0 ? 'PASS' : 'FAIL';\n results.push({\n name: item.name,\n command: item.command,\n status,\n exitCode: run.exitCode,\n durationMs: run.durationMs,\n stdout: run.stdout,\n stderr: run.stderr,\n reason:\n status === 'FAIL'\n ? `exit_code_${run.exitCode ?? 'null'}`\n : undefined,\n });\n }\n\n const finishedAt = new Date().toISOString();\n const suite: TestSuiteResult = {\n runId: options.runId,\n results,\n passed: results.every((r) => r.status === 'PASS'),\n startedAt,\n finishedAt,\n };\n\n const dir = ensureDir(artifactsDir(options.runId, cwd));\n writeFileSync(join(dir, 'test-result.json'), JSON.stringify(suite, null, 2));\n return suite;\n}\n","import { loadConfig } from '../config/load.js';\nimport { runConfiguredTests } from '../lib/test-runner.js';\nimport { createRunId } from '../lib/run-id.js';\nimport { artifactsDir, ensureDir } from '../lib/paths.js';\nimport { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport async function runTestCommand(options: {\n cwd?: string;\n runId?: string;\n}): Promise<{ runId: string; ok: boolean; message: string }> {\n const cwd = options.cwd ?? process.cwd();\n const config = loadConfig(cwd);\n const runId = options.runId ?? createRunId();\n ensureDir(artifactsDir(runId, cwd));\n writeFileSync(join(artifactsDir(runId, cwd), 'run-id.txt'), runId);\n\n const suite = await runConfiguredTests({ config, runId, cwd });\n const lines = [\n `Test run: ${runId}`,\n ...suite.results.map(\n (r) =>\n ` ${r.status === 'PASS' ? '✓' : '✗'} ${r.name} (${r.command}) exit=${r.exitCode} ${r.durationMs}ms`,\n ),\n `Overall: ${suite.passed ? 'PASS' : 'FAIL'}`,\n ];\n\n return { runId, ok: suite.passed, message: lines.join('\\n') };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { loadConfig } from '../config/load.js';\nimport { buildReport } from '../lib/report-builder.js';\nimport { artifactsDir } from '../lib/paths.js';\nimport { createRunId } from '../lib/run-id.js';\n\nexport function runReport(options: {\n cwd?: string;\n runId?: string;\n taskId?: string;\n codeChangeSummary?: string;\n fixAttempts?: number;\n blockedReason?: string;\n}): { runId: string; ok: boolean; message: string; reportPath: string } {\n const cwd = options.cwd ?? process.cwd();\n const config = loadConfig(cwd);\n\n let runId = options.runId;\n if (!runId) {\n runId = createRunId();\n }\n\n const runDir = artifactsDir(runId, cwd);\n if (!existsSync(runDir)) {\n return {\n runId,\n ok: false,\n message: `Artifacts not found for run ${runId}. Run capture/visual/test first.`,\n reportPath: '',\n };\n }\n\n let codeChangeSummary = options.codeChangeSummary ?? null;\n const summaryFile = join(runDir, 'code-change-summary.md');\n if (!codeChangeSummary && existsSync(summaryFile)) {\n codeChangeSummary = readFileSync(summaryFile, 'utf8');\n }\n\n const report = buildReport({\n runId,\n cwd,\n taskId: options.taskId ?? config.taskId,\n codeChangeSummary,\n fixAttempts: options.fixAttempts,\n maxFixAttempts: config.maxFixAttempts,\n blockedReason: options.blockedReason ?? null,\n gates: config.gates,\n });\n\n const reportPath = join(runDir, 'report.json');\n const message = [\n `Report: ${report.status}`,\n ` JSON: ${reportPath}`,\n ` MD: ${join(runDir, 'report.md')}`,\n ` HTML: ${join(runDir, 'report.html')}`,\n report.acceptance.length\n ? ` Acceptance: ${report.acceptance.length} rows (${report.acceptance.filter((a) => a.status === 'PASS').length} PASS / ${report.acceptance.filter((a) => a.status === 'PENDING').length} PENDING)`\n : ' Acceptance: (none loaded)',\n report.failures.length\n ? `Failures:\\n${report.failures.map((f) => ` - ${f}`).join('\\n')}`\n : 'No failures recorded.',\n report.unverified.length\n ? `Unverified:\\n${report.unverified.map((u) => ` - ${u}`).join('\\n')}`\n : '',\n ]\n .filter(Boolean)\n .join('\\n');\n\n return {\n runId,\n ok: report.status === 'PASS' && report.failures.length === 0,\n message,\n reportPath,\n };\n}\n","import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { artifactsDir, ensureDir } from './paths.js';\nimport type { VisualDiffResult } from './visual-diff.js';\nimport type { TestSuiteResult } from './test-runner.js';\nimport {\n evaluateAcceptanceEvidence,\n evaluateAcceptanceGate,\n loadAcceptanceFromTask,\n type AcceptanceRow,\n type AcceptanceStatus,\n type RequirementRow,\n} from './acceptance.js';\n\nexport type ReportStatus = 'PASS' | 'FAIL' | 'SKIPPED' | 'BLOCKED';\n\n/** 报告总状态 + AC/REQ 行状态(含 PENDING/PARTIAL) */\nexport type CriterionStatus = ReportStatus | AcceptanceStatus;\n\nexport type CaptureMeta = {\n screenshots: Array<{\n name: string;\n path: string;\n url: string;\n ok: boolean;\n error?: string;\n }>;\n};\n\nexport type PrecisionReport = {\n runId: string;\n generatedAt: string;\n status: ReportStatus;\n summary: {\n visualPassed: number;\n visualFailed: number;\n visualSkipped: number;\n testsPassed: number;\n testsFailed: number;\n capturePassed: number;\n captureFailed: number;\n };\n requirements: Array<{ id: string; title: string; status: CriterionStatus }>;\n acceptance: Array<{\n id: string;\n requirementId?: string;\n title: string;\n status: CriterionStatus;\n }>;\n capture: CaptureMeta | null;\n visual: VisualDiffResult[];\n tests: TestSuiteResult | null;\n failures: string[];\n unverified: string[];\n screenshots: string[];\n codeChangeSummary: string | null;\n manualReview: string[];\n fixAttempts?: number;\n maxFixAttempts?: number;\n};\n\nfunction readJsonIfExists<T>(path: string): T | null {\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as T;\n } catch {\n return null;\n }\n}\n\nfunction collectVisualResults(runDir: string): VisualDiffResult[] {\n const visualRoot = join(runDir, 'visual');\n if (!existsSync(visualRoot)) return [];\n const results: VisualDiffResult[] = [];\n for (const name of readdirSync(visualRoot)) {\n const jsonPath = join(visualRoot, name, 'visual-result.json');\n const data = readJsonIfExists<VisualDiffResult>(jsonPath);\n if (data) results.push(data);\n }\n return results;\n}\n\nfunction collectScreenshots(runDir: string): string[] {\n const shotDir = join(runDir, 'screenshots');\n if (!existsSync(shotDir)) return [];\n return readdirSync(shotDir)\n .filter((f) => f.endsWith('.png'))\n .map((f) => join(shotDir, f));\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nfunction truncateLog(text: string, max = 4000): string {\n if (text.length <= max) return text;\n return `${text.slice(0, max)}\\n…(truncated)`;\n}\n\nexport type ReportGates = {\n requireAcceptance: boolean;\n requireAcceptanceAllPass: boolean;\n requireFigmaVisual: boolean;\n rejectIdenticalBaseline: boolean;\n};\n\nexport function buildReport(options: {\n runId: string;\n cwd?: string;\n taskId?: string;\n requirements?: PrecisionReport['requirements'];\n acceptance?: PrecisionReport['acceptance'];\n codeChangeSummary?: string | null;\n fixAttempts?: number;\n maxFixAttempts?: number;\n blockedReason?: string | null;\n gates?: Partial<ReportGates>;\n}): PrecisionReport {\n const cwd = options.cwd ?? process.cwd();\n const runDir = artifactsDir(options.runId, cwd);\n const capture = readJsonIfExists<CaptureMeta>(\n join(runDir, 'screenshots', 'capture-meta.json'),\n );\n const visual = collectVisualResults(runDir);\n const tests = readJsonIfExists<TestSuiteResult>(\n join(runDir, 'test-result.json'),\n );\n const screenshots = collectScreenshots(runDir);\n\n const gates: ReportGates = {\n requireAcceptance: options.gates?.requireAcceptance ?? true,\n requireAcceptanceAllPass: options.gates?.requireAcceptanceAllPass ?? true,\n requireFigmaVisual: options.gates?.requireFigmaVisual ?? false,\n rejectIdenticalBaseline: options.gates?.rejectIdenticalBaseline ?? true,\n };\n\n const fromTask = loadAcceptanceFromTask({\n cwd,\n taskId: options.taskId,\n runId: options.runId,\n });\n\n function toAcceptanceStatus(status: CriterionStatus): AcceptanceStatus {\n if (status === 'BLOCKED') return 'FAIL';\n if (\n status === 'PASS' ||\n status === 'FAIL' ||\n status === 'PENDING' ||\n status === 'SKIPPED' ||\n status === 'PARTIAL'\n ) {\n return status;\n }\n return 'PENDING';\n }\n\n const acceptanceRows: AcceptanceRow[] =\n options.acceptance && options.acceptance.length > 0\n ? options.acceptance.map((a) => ({\n id: a.id,\n title: a.title,\n requirementId: a.requirementId,\n status: toAcceptanceStatus(a.status),\n }))\n : fromTask.acceptance;\n\n const requirementRows: RequirementRow[] =\n options.requirements && options.requirements.length > 0\n ? options.requirements.map((r) => ({\n id: r.id,\n title: r.title,\n status: toAcceptanceStatus(r.status),\n }))\n : fromTask.requirements;\n\n const codeChangeSummary =\n options.codeChangeSummary ?? fromTask.codeChangeSummary ?? null;\n\n const failures: string[] = [];\n const unverified: string[] = [];\n\n if (capture) {\n for (const shot of capture.screenshots) {\n if (!shot.ok) {\n failures.push(\n `capture:${shot.name}: ${shot.error ?? 'failed'}${shot.url ? ` (${shot.url})` : ''}`,\n );\n }\n }\n } else {\n unverified.push('capture_not_run');\n }\n\n for (const v of visual) {\n if (!v.passed) failures.push(`visual:${v.name}: ${v.reason ?? 'failed'}`);\n if (v.referenceKind === 'regression') {\n unverified.push(`figma_visual_not_proven:${v.name}`);\n }\n if (v.identicalBytes && v.referenceKind === 'regression') {\n unverified.push(`regression_identical:${v.name}`);\n }\n }\n if (visual.length === 0) {\n unverified.push('visual_not_run');\n }\n\n const hasFigmaVisualPass = visual.some(\n (v) =>\n v.passed &&\n (v.referenceKind ?? 'figma') === 'figma' &&\n !v.identicalBytes,\n );\n\n if (gates.requireFigmaVisual && !hasFigmaVisualPass) {\n failures.push(\n 'figma_visual_required: no passing referenceKind=figma visual (regression/self-baseline does not count)',\n );\n }\n\n if (tests) {\n for (const t of tests.results) {\n if (t.status === 'FAIL') {\n failures.push(`test:${t.name}: ${t.reason ?? `exit ${t.exitCode}`}`);\n }\n }\n } else {\n unverified.push('tests_not_run');\n }\n\n const acGate = evaluateAcceptanceGate(acceptanceRows, {\n requireAcceptance: gates.requireAcceptance,\n requireAcceptanceAllPass: gates.requireAcceptanceAllPass,\n });\n failures.push(...acGate.failures);\n unverified.push(...acGate.unverified);\n\n if (gates.requireAcceptance) {\n const evidence = evaluateAcceptanceEvidence(acceptanceRows, {\n tests,\n visualPassedNames: visual.filter((v) => v.passed).map((v) => v.name),\n hasFigmaVisualPass,\n });\n failures.push(...evidence.failures);\n unverified.push(...evidence.unverified);\n }\n\n let status: ReportStatus = 'PASS';\n const fixAttempts = options.fixAttempts;\n const maxFixAttempts = options.maxFixAttempts;\n let blockedReason = options.blockedReason ?? null;\n\n if (\n fixAttempts !== undefined &&\n maxFixAttempts !== undefined &&\n fixAttempts > maxFixAttempts\n ) {\n blockedReason =\n blockedReason ??\n `fix_attempts_exceeded: ${fixAttempts} > maxFixAttempts=${maxFixAttempts}`;\n }\n\n if (blockedReason) {\n status = 'BLOCKED';\n if (!failures.includes(blockedReason)) failures.push(blockedReason);\n } else if (failures.length > 0) {\n status = 'FAIL';\n } else if (unverified.length > 0) {\n // regression-only visual notes should not alone block PASS when AC all pass\n // and gates.requireFigmaVisual is false — strip soft unverified for status?\n // Codex: CLI PASS without AC is wrong. Soft unverified like figma_visual_not_proven\n // should keep status from being a \"full acceptance PASS\".\n const hardUnverified = unverified.filter(\n (u) =>\n u === 'capture_not_run' ||\n u === 'visual_not_run' ||\n u === 'tests_not_run' ||\n u.startsWith('acceptance_pending:') ||\n u.startsWith('acceptance_partial:'),\n );\n if (hardUnverified.length > 0) {\n status = 'SKIPPED';\n } else if (unverified.some((u) => u.startsWith('figma_visual_not_proven'))) {\n // Pipeline ok but not design-acceptance complete\n status = 'PASS';\n // Keep unverified listed; Skill must not mark DONE when figma_visual_not_proven present\n } else {\n status = 'PASS';\n }\n }\n\n const report: PrecisionReport = {\n runId: options.runId,\n generatedAt: new Date().toISOString(),\n status,\n summary: {\n visualPassed: visual.filter((v) => v.passed).length,\n visualFailed: visual.filter((v) => !v.passed).length,\n visualSkipped: 0,\n testsPassed: tests?.results.filter((r) => r.status === 'PASS').length ?? 0,\n testsFailed: tests?.results.filter((r) => r.status === 'FAIL').length ?? 0,\n capturePassed: capture?.screenshots.filter((s) => s.ok).length ?? 0,\n captureFailed: capture?.screenshots.filter((s) => !s.ok).length ?? 0,\n },\n requirements: requirementRows.map((r) => ({\n id: r.id,\n title: r.title,\n status: r.status,\n })),\n acceptance: acceptanceRows.map((a) => ({\n id: a.id,\n requirementId: a.requirementId,\n title: a.title,\n status: a.status,\n })),\n capture,\n visual,\n tests,\n failures,\n unverified,\n screenshots,\n codeChangeSummary,\n manualReview: [\n 'DONE 仅当:report.status===PASS 且 acceptance 无 PENDING/PARTIAL/FAIL 且无 figma_visual_not_proven(或已明确只要 regression)',\n 'Review Figma vs screenshot for typography and spacing (pixel diff is not full design QA)',\n 'Confirm Loading / Error / Empty states if not covered by e2e',\n 'Confirm no secrets were committed',\n ],\n fixAttempts: options.fixAttempts,\n maxFixAttempts: options.maxFixAttempts,\n };\n\n const outDir = ensureDir(runDir);\n writeFileSync(join(outDir, 'report.json'), JSON.stringify(report, null, 2));\n writeFileSync(join(outDir, 'report.md'), renderMarkdown(report));\n writeFileSync(join(outDir, 'report.html'), renderHtml(report));\n return report;\n}\n\nexport function renderMarkdown(report: PrecisionReport): string {\n const lines: string[] = [\n `# Frontend Precision Report`,\n '',\n `- Run ID: \\`${report.runId}\\``,\n `- Status: **${report.status}**`,\n `- Generated: ${report.generatedAt}`,\n '',\n `## Summary`,\n '',\n `| Area | Passed | Failed |`,\n `|---|---:|---:|`,\n `| Capture | ${report.summary.capturePassed} | ${report.summary.captureFailed} |`,\n `| Visual | ${report.summary.visualPassed} | ${report.summary.visualFailed} |`,\n `| Tests | ${report.summary.testsPassed} | ${report.summary.testsFailed} |`,\n '',\n ];\n\n if (report.failures.length) {\n lines.push('## Failures', '');\n for (const f of report.failures) lines.push(`- ${f}`);\n lines.push('');\n }\n\n if (report.unverified.length) {\n lines.push('## Unverified', '');\n for (const u of report.unverified) lines.push(`- ${u}`);\n lines.push('');\n } else {\n lines.push('## Unverified', '', '- (none)', '');\n }\n\n if (report.visual.length) {\n lines.push('## Visual', '');\n for (const v of report.visual) {\n lines.push(\n `- **${v.name}**: ${v.passed ? 'PASS' : 'FAIL'} (diffRatio=${(v.diffRatio * 100).toFixed(3)}%)${v.reason ? ` — ${v.reason}` : ''}`,\n );\n if (v.paths.diff) lines.push(` - Diff: \\`${v.paths.diff}\\``);\n lines.push(` - Reference: \\`${v.paths.reference}\\``);\n lines.push(` - Actual: \\`${v.paths.actual}\\``);\n }\n lines.push('');\n }\n\n if (report.tests) {\n lines.push('## Tests', '');\n for (const t of report.tests.results) {\n lines.push(\n `- **${t.name}**: ${t.status} (exit=${t.exitCode}, ${t.durationMs}ms)`,\n );\n lines.push(` - Command: \\`${t.command}\\``);\n if (t.stdout?.trim()) {\n lines.push(' - stdout:', '', '```', truncateLog(t.stdout).trimEnd(), '```', '');\n }\n if (t.stderr?.trim()) {\n lines.push(' - stderr:', '', '```', truncateLog(t.stderr).trimEnd(), '```', '');\n }\n }\n lines.push('');\n }\n\n if (report.requirements.length) {\n lines.push('## Requirements', '');\n for (const r of report.requirements) {\n lines.push(`- ${r.id}: ${r.title} — ${r.status}`);\n }\n lines.push('');\n }\n\n if (report.acceptance.length) {\n lines.push('## Acceptance Criteria', '');\n for (const a of report.acceptance) {\n lines.push(\n `- ${a.id}${a.requirementId ? ` (${a.requirementId})` : ''}: ${a.title} — ${a.status}`,\n );\n }\n lines.push('');\n }\n\n if (report.codeChangeSummary) {\n lines.push('## Code Change Summary', '', report.codeChangeSummary, '');\n }\n\n lines.push('## Manual Review', '');\n for (const m of report.manualReview) lines.push(`- ${m}`);\n lines.push('');\n\n lines.push('## Screenshots', '');\n if (report.screenshots.length) {\n for (const s of report.screenshots) lines.push(`- \\`${s}\\``);\n } else {\n lines.push('- (none)');\n }\n lines.push('');\n\n return lines.join('\\n');\n}\n\nexport function renderHtml(report: PrecisionReport): string {\n const statusColor =\n report.status === 'PASS'\n ? '#067647'\n : report.status === 'BLOCKED'\n ? '#b54708'\n : report.status === 'SKIPPED'\n ? '#667085'\n : '#b42318';\n\n const failureLis = report.failures\n .map((f) => `<li>${escapeHtml(f)}</li>`)\n .join('');\n const unverifiedLis = report.unverified.length\n ? report.unverified.map((u) => `<li>${escapeHtml(u)}</li>`).join('')\n : '<li>(none)</li>';\n const screenshotLis = report.screenshots.length\n ? report.screenshots\n .map((s) => `<li><code>${escapeHtml(s)}</code></li>`)\n .join('')\n : '<li>(none)</li>';\n const visualRows = report.visual\n .map(\n (v) =>\n `<tr><td>${escapeHtml(v.name)}</td><td>${v.passed ? 'PASS' : 'FAIL'}</td><td>${(v.diffRatio * 100).toFixed(3)}%</td><td>${escapeHtml(v.reason ?? '')}</td><td><code>${escapeHtml(v.paths.diff ?? '')}</code></td></tr>`,\n )\n .join('');\n const testRows =\n report.tests?.results\n .map((t) => {\n const logs = [\n t.stdout?.trim()\n ? `<details><summary>stdout</summary><pre>${escapeHtml(truncateLog(t.stdout))}</pre></details>`\n : '',\n t.stderr?.trim()\n ? `<details><summary>stderr</summary><pre>${escapeHtml(truncateLog(t.stderr))}</pre></details>`\n : '',\n ]\n .filter(Boolean)\n .join('');\n return `<tr><td>${escapeHtml(t.name)}</td><td>${t.status}</td><td>${t.exitCode}</td><td>${t.durationMs}ms</td><td>${logs || '—'}</td></tr>`;\n })\n .join('') ?? '';\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Frontend Precision Report — ${escapeHtml(report.runId)}</title>\n <style>\n body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;color:#101828;background:#fff}\n h1{font-size:1.5rem} table{border-collapse:collapse;width:100%;margin:1rem 0}\n th,td{border:1px solid #e4e7ec;padding:.5rem .75rem;text-align:left;font-size:.875rem;vertical-align:top}\n th{background:#f9fafb} .badge{display:inline-block;padding:.25rem .6rem;border-radius:999px;color:#fff;background:${statusColor};font-weight:600}\n code{background:#f2f4f7;padding:.1rem .35rem;border-radius:4px}\n pre{white-space:pre-wrap;background:#f2f4f7;padding:.75rem;border-radius:6px;max-height:240px;overflow:auto}\n </style>\n</head>\n<body>\n <h1>Frontend Precision Report</h1>\n <p>Run: <code>${escapeHtml(report.runId)}</code></p>\n <p>Status: <span class=\"badge\">${escapeHtml(report.status)}</span></p>\n <p>Generated: ${escapeHtml(report.generatedAt)}</p>\n <h2>Failures</h2>\n ${failureLis ? `<ul>${failureLis}</ul>` : '<p>None</p>'}\n <h2>Unverified</h2>\n <ul>${unverifiedLis}</ul>\n <h2>Screenshots</h2>\n <ul>${screenshotLis}</ul>\n <h2>Visual</h2>\n <table><thead><tr><th>Name</th><th>Status</th><th>Diff ratio</th><th>Reason</th><th>Diff</th></tr></thead><tbody>${visualRows || '<tr><td colspan=\"5\">No visual results</td></tr>'}</tbody></table>\n <h2>Tests</h2>\n <table><thead><tr><th>Name</th><th>Status</th><th>Exit</th><th>Duration</th><th>Logs</th></tr></thead><tbody>${testRows || '<tr><td colspan=\"5\">Tests not run</td></tr>'}</tbody></table>\n <h2>Manual review</h2>\n <ul>${report.manualReview.map((m) => `<li>${escapeHtml(m)}</li>`).join('')}</ul>\n</body>\n</html>`;\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { join, resolve, sep } from 'node:path';\nimport { isSafeSlug } from './safe-slug.js';\nimport type { TestSuiteResult } from './test-runner.js';\n\nexport type AcceptanceStatus =\n | 'PASS'\n | 'FAIL'\n | 'PENDING'\n | 'SKIPPED'\n | 'PARTIAL';\n\nexport type AcceptanceRow = {\n id: string;\n title: string;\n requirementId?: string;\n /** 验证方式列原文,用于证据启发(unit / Playwright / visual) */\n verification?: string;\n status: AcceptanceStatus;\n};\n\nexport type RequirementRow = {\n id: string;\n title: string;\n status: AcceptanceStatus;\n};\n\nconst STATUS_SET = new Set<AcceptanceStatus>([\n 'PASS',\n 'FAIL',\n 'PENDING',\n 'SKIPPED',\n 'PARTIAL',\n]);\n\nfunction normalizeStatus(raw: string): AcceptanceStatus {\n const value = raw.trim().toUpperCase();\n if (STATUS_SET.has(value as AcceptanceStatus)) {\n return value as AcceptanceStatus;\n }\n return 'PENDING';\n}\n\nfunction normalizeAcId(raw: string): string {\n const match = raw.trim().match(/AC-(\\d+)/i);\n const digits = match?.[1];\n if (!digits) return raw.trim().toUpperCase();\n return `AC-${digits.padStart(3, '0')}`;\n}\n\nfunction normalizeReqId(digits: string): string {\n return `REQ-${digits.padStart(3, '0')}`;\n}\n\n/**\n * Parse acceptance.md table rows:\n * | AC-001 | desc | REQ-001 | Playwright | PENDING |\n */\nexport function parseAcceptanceMarkdown(markdown: string): AcceptanceRow[] {\n const rows: AcceptanceRow[] = [];\n for (const line of markdown.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed.startsWith('|')) continue;\n if (/^\\|\\s*-+/.test(trimmed)) continue;\n if (/AC\\s*ID/i.test(trimmed) && /描述|Description/i.test(trimmed)) continue;\n\n const parts = trimmed\n .replace(/^\\|/, '')\n .replace(/\\|$/, '')\n .split('|')\n .map((c) => c.trim());\n\n if (parts.length < 2) continue;\n const idRaw = parts[0];\n if (!idRaw || !/^AC-\\d+/i.test(idRaw)) continue;\n\n const title = parts[1] ?? '';\n const reqCell = parts[2] ?? '';\n const verification = parts.length >= 5 ? parts[3] : undefined;\n const reqMatches = [...reqCell.matchAll(/REQ-(\\d+)/gi)].flatMap((m) => {\n const digits = m[1];\n return digits ? [normalizeReqId(digits)] : [];\n });\n const requirementId =\n reqMatches.length > 0 ? [...new Set(reqMatches)].join(', ') : undefined;\n const statusCell = parts[parts.length - 1] ?? 'PENDING';\n const status = normalizeStatus(statusCell);\n\n rows.push({\n id: normalizeAcId(idRaw),\n title,\n ...(requirementId ? { requirementId } : {}),\n ...(verification ? { verification } : {}),\n status,\n });\n }\n\n return rows;\n}\n\nexport function deriveRequirements(\n acceptance: AcceptanceRow[],\n): RequirementRow[] {\n const map = new Map<string, RequirementRow>();\n\n for (const row of acceptance) {\n const reqIds = (row.requirementId ?? '')\n .split(/[,,\\s]+/)\n .map((s) => s.trim())\n .filter((s) => /^REQ-\\d+/i.test(s));\n\n for (const raw of reqIds) {\n const match = raw.match(/REQ-(\\d+)/i);\n const digits = match?.[1];\n if (!digits) continue;\n const id = normalizeReqId(digits);\n const existing = map.get(id);\n if (!existing) {\n map.set(id, { id, title: id, status: row.status });\n continue;\n }\n existing.status = mergeStatus(existing.status, row.status);\n }\n }\n\n return [...map.values()];\n}\n\nfunction mergeStatus(\n a: AcceptanceStatus,\n b: AcceptanceStatus,\n): AcceptanceStatus {\n if (a === 'FAIL' || b === 'FAIL') return 'FAIL';\n if (a === 'PENDING' || b === 'PENDING') return 'PENDING';\n if (a === 'PARTIAL' || b === 'PARTIAL') return 'PARTIAL';\n if (a === 'SKIPPED' || b === 'SKIPPED') return 'SKIPPED';\n return 'PASS';\n}\n\nfunction isPathInside(parent: string, child: string): boolean {\n const root = resolve(parent);\n const target = resolve(child);\n return target === root || target.startsWith(root + sep);\n}\n\nexport function findTaskDir(options: {\n cwd: string;\n taskId?: string;\n /** 若提供,优先选 `.run-id` 内容匹配的任务目录 */\n runId?: string;\n}): string | null {\n const tasksRoot = join(options.cwd, '.frontend-precision', 'tasks');\n if (!existsSync(tasksRoot)) return null;\n\n if (options.taskId) {\n if (!isSafeSlug(options.taskId)) {\n return null;\n }\n const dir = join(tasksRoot, options.taskId);\n if (!existsSync(dir) || !isPathInside(tasksRoot, dir)) {\n return null;\n }\n return dir;\n }\n\n const entries = readdirSync(tasksRoot)\n .filter((name) => isSafeSlug(name))\n .map((name) => {\n const dir = join(tasksRoot, name);\n try {\n const st = statSync(dir);\n if (!st.isDirectory()) return null;\n if (!isPathInside(tasksRoot, dir)) return null;\n return { name, dir, mtime: st.mtimeMs };\n } catch {\n return null;\n }\n })\n .filter((e): e is { name: string; dir: string; mtime: number } => !!e)\n .sort((a, b) => b.mtime - a.mtime);\n\n if (options.runId) {\n for (const entry of entries) {\n const runIdPath = join(entry.dir, '.run-id');\n if (!existsSync(runIdPath)) continue;\n const saved = readFileSync(runIdPath, 'utf8').trim();\n if (saved === options.runId && existsSync(join(entry.dir, 'acceptance.md'))) {\n return entry.dir;\n }\n }\n }\n\n for (const entry of entries) {\n if (existsSync(join(entry.dir, 'acceptance.md'))) {\n return entry.dir;\n }\n }\n\n return null;\n}\n\nexport function loadAcceptanceFromTask(options: {\n cwd: string;\n taskId?: string;\n runId?: string;\n}): {\n taskDir: string | null;\n acceptance: AcceptanceRow[];\n requirements: RequirementRow[];\n codeChangeSummary: string | null;\n} {\n const taskDir = findTaskDir(options);\n if (!taskDir) {\n return {\n taskDir: null,\n acceptance: [],\n requirements: [],\n codeChangeSummary: null,\n };\n }\n\n const acceptancePath = join(taskDir, 'acceptance.md');\n const acceptance = existsSync(acceptancePath)\n ? parseAcceptanceMarkdown(readFileSync(acceptancePath, 'utf8'))\n : [];\n\n const summaryPath = join(taskDir, 'code-change-summary.md');\n const codeChangeSummary = existsSync(summaryPath)\n ? readFileSync(summaryPath, 'utf8')\n : null;\n\n return {\n taskDir,\n acceptance,\n requirements: deriveRequirements(acceptance),\n codeChangeSummary,\n };\n}\n\nfunction evidenceHintsForRow(row: AcceptanceRow): {\n needsTestPass: boolean;\n needsVisual: boolean;\n needsUnitMention: boolean;\n} {\n const v = `${row.verification ?? ''} ${row.title}`.toLowerCase();\n const needsVisual = /visual|figma|像素|视觉/.test(v);\n const needsUnitMention = /\\bunit\\b|单测|logic\\.test/.test(v);\n const needsTestPass =\n /playwright|e2e|frontend-precision test|typecheck|unit|单测|tsx --test/.test(\n v,\n ) || needsUnitMention;\n return { needsTestPass, needsVisual, needsUnitMention };\n}\n\n/**\n * 对标记为 PASS 的 AC 做弱证据校验,降低「只改 Markdown」冒充通过的概率。\n * 不替代真实 Playwright 绑定;只检查本轮 artifacts 是否具备对应产物。\n */\nexport function evaluateAcceptanceEvidence(\n acceptance: AcceptanceRow[],\n options: {\n tests: TestSuiteResult | null;\n visualPassedNames: string[];\n hasFigmaVisualPass: boolean;\n },\n): { failures: string[]; unverified: string[] } {\n const failures: string[] = [];\n const unverified: string[] = [];\n const testsPassed = Boolean(options.tests?.passed);\n const testBlob = (options.tests?.results ?? [])\n .map((r) => `${r.name} ${r.command} ${r.stdout} ${r.stderr}`)\n .join('\\n')\n .toLowerCase();\n\n for (const row of acceptance) {\n if (row.status !== 'PASS') continue;\n\n const hints = evidenceHintsForRow(row);\n const acToken = row.id.toLowerCase();\n\n if (hints.needsTestPass) {\n if (!options.tests) {\n failures.push(\n `acceptance:${row.id}: PASS without test evidence (tests_not_run)`,\n );\n continue;\n }\n if (!testsPassed) {\n failures.push(\n `acceptance:${row.id}: PASS but testCommand failed`,\n );\n continue;\n }\n // 若验证列点名 AC id / 单测文件,尽量在 stdout 或 command 中看到痕迹\n if (\n hints.needsUnitMention &&\n !testBlob.includes(acToken) &&\n !/tokenDramaPreviewPlaylistLogic|playlistLogic|\\.test\\.ts|pass/.test(\n testBlob,\n )\n ) {\n unverified.push(`acceptance_evidence_weak:${row.id}`);\n }\n }\n\n if (hints.needsVisual) {\n if (options.visualPassedNames.length === 0) {\n failures.push(\n `acceptance:${row.id}: PASS without visual evidence`,\n );\n } else if (!options.hasFigmaVisualPass) {\n // regression 通过不算 Figma 验收证据\n failures.push(\n `acceptance:${row.id}: PASS claims visual/Figma but no referenceKind=figma pass`,\n );\n }\n }\n }\n\n return { failures, unverified };\n}\n\nexport function evaluateAcceptanceGate(\n acceptance: AcceptanceRow[],\n options: {\n requireAcceptance: boolean;\n requireAcceptanceAllPass: boolean;\n },\n): { failures: string[]; unverified: string[] } {\n const failures: string[] = [];\n const unverified: string[] = [];\n\n if (!options.requireAcceptance) {\n return { failures, unverified };\n }\n\n if (acceptance.length === 0) {\n failures.push(\n 'acceptance_missing: no AC rows found (pass --task-id or ensure .frontend-precision/tasks/*/acceptance.md)',\n );\n return { failures, unverified };\n }\n\n const pending = acceptance.filter((a) => a.status === 'PENDING');\n const failed = acceptance.filter((a) => a.status === 'FAIL');\n const partial = acceptance.filter((a) => a.status === 'PARTIAL');\n\n for (const row of failed) {\n failures.push(`acceptance:${row.id}: FAIL — ${row.title}`);\n }\n\n if (options.requireAcceptanceAllPass) {\n for (const row of pending) {\n failures.push(`acceptance:${row.id}: PENDING — ${row.title}`);\n }\n for (const row of partial) {\n failures.push(\n `acceptance:${row.id}: PARTIAL — ${row.title} (requireAcceptanceAllPass)`,\n );\n }\n } else {\n for (const row of pending) {\n unverified.push(`acceptance_pending:${row.id}`);\n }\n for (const row of partial) {\n unverified.push(`acceptance_partial:${row.id}`);\n }\n }\n\n return { failures, unverified };\n}\n"],"mappings":"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","names":["existsSync","dirname","join","existsSync","dirname","join","existsSync","resolve","join","readFileSync","existsSync","existsSync","readFileSync","join","resolve","existsSync","writeFileSync","join","writeFileSync","join","mkdirSync","join","resolve","resolve","join","mkdirSync","join","writeFileSync","writeFileSync","join","resolve","existsSync","existsSync","readFileSync","writeFileSync","mkdirSync","dirname","join","result","resolve","existsSync","writeFileSync","join","resolve","writeFileSync","join","writeFileSync","join","existsSync","readFileSync","join","existsSync","readFileSync","readdirSync","writeFileSync","join","existsSync","readFileSync","join","resolve","sep","resolve","sep","join","existsSync","readFileSync","existsSync","readFileSync","join","readdirSync","status","writeFileSync","existsSync","join","readFileSync"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/config/schema.ts","../src/lib/safe-slug.ts","../src/lib/templates.ts","../src/commands/doctor.ts","../src/config/load.ts","../src/lib/http-status.ts","../src/commands/capture.ts","../src/lib/screenshot.ts","../src/lib/paths.ts","../src/lib/run-id.ts","../src/lib/routes.ts","../src/commands/visual.ts","../src/lib/visual-diff.ts","../src/lib/test-runner.ts","../src/commands/test.ts","../src/commands/report.ts","../src/lib/report-builder.ts","../src/lib/acceptance.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { runInit } from './commands/init.js';\nimport { runDoctor, formatDoctor } from './commands/doctor.js';\nimport { runCapture } from './commands/capture.js';\nimport { runVisual } from './commands/visual.js';\nimport { runTestCommand } from './commands/test.js';\nimport { runReport } from './commands/report.js';\nimport { ConfigError } from './config/load.js';\n\nconst program = new Command();\n\nprogram\n .name('frontend-precision')\n .description(\n 'Cursor-first frontend precision toolkit (screenshot, visual diff, tests, reports)',\n )\n .version('0.1.1');\n\nprogram\n .command('init')\n .description('Initialize config, Cursor Skill, and rule templates')\n .option('--force', 'Overwrite existing files', false)\n .action((opts: { force?: boolean }) => {\n const result = runInit({ force: opts.force });\n console.log(result.messages.join('\\n'));\n });\n\nprogram\n .command('doctor')\n .description('Diagnose environment and project readiness')\n .action(async () => {\n const { checks, ok } = await runDoctor();\n console.log(formatDoctor(checks));\n process.exitCode = ok ? 0 : 1;\n });\n\nprogram\n .command('capture')\n .description('Screenshot configured routes with Playwright')\n .option('--run-id <id>', 'Reuse or set run id')\n .option('--route <name>', 'Only capture named route (repeatable)', (v, acc: string[]) => {\n acc.push(v);\n return acc;\n }, [])\n .action(async (opts: { runId?: string; route: string[] }) => {\n try {\n const result = await runCapture({\n runId: opts.runId,\n routes: opts.route.length ? opts.route : undefined,\n });\n console.log(result.message);\n process.exitCode = result.ok ? 0 : 1;\n } catch (err) {\n handleError(err);\n }\n });\n\nprogram\n .command('visual')\n .description('Compare screenshots against reference PNGs')\n .requiredOption('--run-id <id>', 'Run id from capture')\n .option('--route <name>', 'Only compare named route (repeatable)', (v, acc: string[]) => {\n acc.push(v);\n return acc;\n }, [])\n .action(async (opts: { runId: string; route: string[] }) => {\n try {\n const result = await runVisual({\n runId: opts.runId,\n routes: opts.route.length ? opts.route : undefined,\n });\n console.log(result.message);\n process.exitCode = result.ok ? 0 : 1;\n } catch (err) {\n handleError(err);\n }\n });\n\nprogram\n .command('test')\n .description('Run configured test commands and record results')\n .option('--run-id <id>', 'Attach results to an existing run id')\n .action(async (opts: { runId?: string }) => {\n try {\n const result = await runTestCommand({ runId: opts.runId });\n console.log(result.message);\n process.exitCode = result.ok ? 0 : 1;\n } catch (err) {\n handleError(err);\n }\n });\n\nprogram\n .command('report')\n .description('Aggregate capture/visual/test into JSON/Markdown/HTML reports')\n .requiredOption('--run-id <id>', 'Run id to report')\n .option('--task-id <id>', 'Task folder under .frontend-precision/tasks/')\n .option('--fix-attempts <n>', 'Current auto-fix attempt count', (v) =>\n Number(v),\n )\n .option('--blocked <reason>', 'Mark report as BLOCKED with reason')\n .action(\n (opts: {\n runId: string;\n taskId?: string;\n fixAttempts?: number;\n blocked?: string;\n }) => {\n try {\n const result = runReport({\n runId: opts.runId,\n taskId: opts.taskId,\n fixAttempts: opts.fixAttempts,\n blockedReason: opts.blocked,\n });\n console.log(result.message);\n process.exitCode = result.ok ? 0 : 1;\n } catch (err) {\n handleError(err);\n }\n },\n );\n\nfunction handleError(err: unknown): never {\n if (err instanceof ConfigError) {\n console.error(err.message);\n process.exit(1);\n }\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n}\n\nprogram.parseAsync(process.argv).catch((err) => {\n handleError(err);\n});\n","import {\n cpSync,\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n appendFileSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { CONFIG_FILENAME } from '../config/schema.js';\nimport { getTemplatesRoot } from '../lib/templates.js';\n\nexport type InitOptions = {\n cwd?: string;\n force?: boolean;\n};\n\nfunction copyIfNeeded(\n src: string,\n dest: string,\n force: boolean,\n): 'created' | 'skipped' | 'overwritten' {\n if (existsSync(dest) && !force) return 'skipped';\n mkdirSync(dirname(dest), { recursive: true });\n cpSync(src, dest, { recursive: true });\n return existsSync(dest) && force ? 'overwritten' : 'created';\n}\n\nexport function runInit(options: InitOptions = {}): {\n messages: string[];\n configPath: string;\n} {\n const cwd = options.cwd ?? process.cwd();\n const force = options.force ?? false;\n const templates = getTemplatesRoot();\n const messages: string[] = [];\n\n const defaultConfigPath = join(templates, 'config.default.json');\n const configPath = join(cwd, CONFIG_FILENAME);\n\n if (existsSync(configPath) && !force) {\n messages.push(`Skipped ${CONFIG_FILENAME} (exists). Use --force to overwrite.`);\n } else {\n writeFileSync(configPath, readFileSync(defaultConfigPath));\n messages.push(\n existsSync(configPath) && force\n ? `Overwrote ${CONFIG_FILENAME}`\n : `Created ${CONFIG_FILENAME}`,\n );\n }\n\n const skillSrc = join(templates, 'skill', 'frontend-precision');\n const skillDest = join(cwd, '.cursor', 'skills', 'frontend-precision');\n if (existsSync(skillDest) && !force) {\n messages.push(\n 'Skipped .cursor/skills/frontend-precision (exists). Use --force to overwrite.',\n );\n } else {\n mkdirSync(join(cwd, '.cursor', 'skills'), { recursive: true });\n cpSync(skillSrc, skillDest, { recursive: true });\n messages.push(\n force\n ? 'Overwrote .cursor/skills/frontend-precision'\n : 'Installed .cursor/skills/frontend-precision',\n );\n }\n\n const ruleSrc = join(templates, 'rules', 'frontend-precision.mdc');\n const ruleDest = join(cwd, '.cursor', 'rules', 'frontend-precision.mdc');\n const ruleStatus = copyIfNeeded(ruleSrc, ruleDest, force);\n messages.push(\n ruleStatus === 'skipped'\n ? 'Skipped .cursor/rules/frontend-precision.mdc (exists).'\n : `${ruleStatus === 'overwritten' ? 'Overwrote' : 'Installed'} .cursor/rules/frontend-precision.mdc`,\n );\n\n const taskTplSrc = join(templates, 'task-templates');\n const taskTplDest = join(\n cwd,\n '.frontend-precision',\n 'templates',\n );\n if (!existsSync(taskTplDest) || force) {\n mkdirSync(taskTplDest, { recursive: true });\n cpSync(taskTplSrc, taskTplDest, { recursive: true });\n messages.push('Installed .frontend-precision/templates (preflight/plan/acceptance)');\n }\n\n const gitignorePath = join(cwd, '.gitignore');\n const ignoreLine = '.frontend-precision/artifacts/';\n if (existsSync(gitignorePath)) {\n const content = readFileSync(gitignorePath, 'utf8');\n if (!content.includes('.frontend-precision')) {\n appendFileSync(gitignorePath, `\\n# frontend-precision\\n.frontend-precision/artifacts/\\n`);\n messages.push('Appended .frontend-precision/artifacts/ to .gitignore');\n } else {\n messages.push(`Skipped .gitignore (already mentions frontend-precision)`);\n }\n } else {\n writeFileSync(\n gitignorePath,\n `# frontend-precision\\n${ignoreLine}\\n`,\n );\n messages.push('Created .gitignore with .frontend-precision/artifacts/');\n }\n\n messages.push('');\n messages.push('Next steps:');\n messages.push(' 1. Edit frontend-precision.config.json (baseUrl, routes, references)');\n messages.push(' 2. Install Playwright browsers: pnpm exec playwright install chromium');\n messages.push(' 3. In Cursor: /frontend-precision with your PRD + Figma link');\n messages.push(' 4. Run: frontend-precision doctor');\n\n return { messages, configPath };\n}\n","import { z } from 'zod';\nimport { isSafeSlug } from '../lib/safe-slug.js';\n\nconst ViewportSchema = z.object({\n width: z.number().int().positive(),\n height: z.number().int().positive(),\n});\n\nexport const RouteSchema = z.object({\n name: z\n .string()\n .min(1)\n .refine(isSafeSlug, {\n message:\n \"Invalid route name. Use a slug of letters, digits, '.', '_' or '-' with no path separators or '..'.\",\n }),\n path: z.string().min(1),\n reference: z.string().optional(),\n /**\n * figma: 设计稿导出基线(可宣称视觉验收)\n * regression: 实现自截图回归基线(不得当作 Figma PASS)\n */\n referenceKind: z.enum(['figma', 'regression']).default('figma'),\n viewport: ViewportSchema.optional(),\n waitFor: z.string().optional(),\n hide: z.array(z.string()).optional(),\n});\n\nconst RoutesSchema = z\n .array(RouteSchema)\n .min(1)\n .superRefine((routes, ctx) => {\n const seen = new Set<string>();\n routes.forEach((route, index) => {\n if (seen.has(route.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [index, 'name'],\n message: `Duplicate route name: ${route.name}`,\n });\n }\n seen.add(route.name);\n });\n });\n\nexport const ConfigSchema = z.object({\n baseUrl: z.string().url(),\n viewport: ViewportSchema,\n routes: RoutesSchema,\n visual: z\n .object({\n pixelThreshold: z.number().min(0).max(1).default(0.1),\n maxDiffRatio: z.number().min(0).max(1).default(0.03),\n })\n .default({ pixelThreshold: 0.1, maxDiffRatio: 0.03 }),\n testCommand: z.string().min(1),\n maxFixAttempts: z.number().int().min(0).max(10).default(3),\n browser: z.enum(['chromium', 'firefox', 'webkit']).default('chromium'),\n settleMs: z.number().int().nonnegative().default(300),\n /** 绑定任务目录名,report 自动读取 acceptance.md */\n taskId: z.string().min(1).optional(),\n gates: z\n .object({\n /** 报告必须加载到至少 1 条 AC,否则 FAIL */\n requireAcceptance: z.boolean().default(true),\n /** 任一 AC 为 PENDING/PARTIAL/FAIL 则整体不能 PASS */\n requireAcceptanceAllPass: z.boolean().default(true),\n /** 存在 referenceKind=regression 的路由时,不得宣称 Figma 视觉通过 */\n requireFigmaVisual: z.boolean().default(false),\n /** reference 与 actual 字节完全相同 → FAIL(防自截图冒充) */\n rejectIdenticalBaseline: z.boolean().default(true),\n })\n .default({\n requireAcceptance: true,\n requireAcceptanceAllPass: true,\n requireFigmaVisual: false,\n rejectIdenticalBaseline: true,\n }),\n checks: z\n .object({\n typecheck: z.string().optional(),\n lint: z.string().optional(),\n unit: z.string().optional(),\n e2e: z.string().optional(),\n })\n .optional(),\n});\n\nexport type FrontendPrecisionConfig = z.infer<typeof ConfigSchema>;\nexport type RouteConfig = z.infer<typeof RouteSchema>;\n\nexport const CONFIG_FILENAME = 'frontend-precision.config.json';\n","const SAFE_SLUG_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\n\nexport function isSafeSlug(value: unknown): value is string {\n return (\n typeof value === 'string' &&\n !value.includes('..') &&\n !value.includes('/') &&\n !value.includes('\\\\') &&\n SAFE_SLUG_PATTERN.test(value)\n );\n}\n\nexport function assertSafeSlug(value: unknown, label: string): string {\n if (!isSafeSlug(value)) {\n throw new Error(\n `Invalid ${label} \"${String(value)}\". Use a slug of letters, digits, '.', '_' or '-' (no path separators or '..').`,\n );\n }\n return value;\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Resolve packaged templates directory (dist/templates after build,\n * or src/templates when running from source via vitest/tsx).\n */\nexport function getTemplatesRoot(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n join(here, 'templates'),\n join(here, '..', 'templates'),\n join(here, '..', '..', 'src', 'templates'),\n join(here, '..', '..', 'dist', 'templates'),\n ];\n\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n\n throw new Error(\n 'Cannot locate templates directory. Rebuild the CLI (`pnpm build`).',\n );\n}\n","import { existsSync } from 'node:fs';\nimport { resolve, join } from 'node:path';\nimport { spawnSync, type SpawnSyncReturns } from 'node:child_process';\nimport { loadConfig, ConfigError } from '../config/load.js';\nimport { CONFIG_FILENAME, type FrontendPrecisionConfig } from '../config/schema.js';\nimport { isHealthyHttpStatus } from '../lib/http-status.js';\n\nexport type DoctorCheck = {\n name: string;\n ok: boolean;\n detail: string;\n fix?: string;\n};\n\nexport type DoctorDeps = {\n spawnSync: typeof spawnSync;\n fetch: typeof fetch;\n};\n\nconst defaultDeps: DoctorDeps = {\n spawnSync,\n fetch: globalThis.fetch.bind(globalThis),\n};\n\nfunction spawnOk(\n result: SpawnSyncReturns<string>,\n): boolean {\n return result.status === 0;\n}\n\nexport async function runDoctor(\n cwd = process.cwd(),\n deps: DoctorDeps = defaultDeps,\n): Promise<{\n checks: DoctorCheck[];\n ok: boolean;\n}> {\n const checks: DoctorCheck[] = [];\n\n const nodeMajor = Number(process.versions.node.split('.')[0]);\n checks.push({\n name: 'Node.js >= 20',\n ok: nodeMajor >= 20,\n detail: `Detected v${process.versions.node}`,\n fix: 'Install Node.js 20+ from https://nodejs.org/',\n });\n\n const pnpm = deps.spawnSync('pnpm', ['--version'], { encoding: 'utf8' });\n checks.push({\n name: 'pnpm',\n ok: spawnOk(pnpm),\n detail: spawnOk(pnpm) ? `pnpm ${pnpm.stdout.trim()}` : 'pnpm not found',\n fix: 'corepack enable && corepack prepare pnpm@latest --activate',\n });\n\n let config: FrontendPrecisionConfig | null = null;\n let configDetail = `Missing ${CONFIG_FILENAME}`;\n let configFix = 'Run `frontend-precision init`';\n try {\n config = loadConfig(cwd);\n configDetail = `Valid config at ${join(cwd, CONFIG_FILENAME)}`;\n } catch (err) {\n if (err instanceof ConfigError) {\n configDetail = err.message;\n configFix = err.message.includes('validation failed')\n ? 'Fix the invalid fields listed above in frontend-precision.config.json'\n : 'Run `frontend-precision init` (use --force only if you intend to replace the file)';\n } else {\n configDetail = (err as Error).message;\n }\n }\n checks.push({\n name: CONFIG_FILENAME,\n ok: Boolean(config),\n detail: configDetail,\n fix: configFix,\n });\n\n if (config) {\n let reachable = false;\n let reachDetail = '';\n try {\n const res = await deps.fetch(config.baseUrl, {\n signal: AbortSignal.timeout(5000),\n });\n reachable = isHealthyHttpStatus(res.status);\n reachDetail = `HTTP ${res.status} from ${config.baseUrl}`;\n if (!reachable) {\n reachDetail += ' (expected 2xx)';\n }\n } catch (err) {\n reachDetail = `Unreachable: ${(err as Error).message}`;\n }\n checks.push({\n name: 'baseUrl reachable',\n ok: reachable,\n detail: reachDetail,\n fix: 'Start your dev server, or update baseUrl in config',\n });\n\n for (const route of config.routes) {\n if (!route.reference) {\n checks.push({\n name: `reference:${route.name}`,\n ok: false,\n detail: 'No reference path configured',\n fix: 'Set routes[].reference to a PNG baseline (from Figma export or Agent)',\n });\n continue;\n }\n const refPath = resolve(cwd, route.reference);\n checks.push({\n name: `reference:${route.name}`,\n ok: existsSync(refPath),\n detail: existsSync(refPath)\n ? `Found ${refPath}`\n : `Missing ${refPath}`,\n fix: 'Export baseline PNG or ask Agent to place Figma screenshot',\n });\n }\n\n const testCmd = config.testCommand.trim();\n checks.push({\n name: 'testCommand non-empty',\n ok: testCmd.length > 0,\n detail: testCmd || '(empty)',\n fix: 'Set testCommand e.g. \"pnpm test:e2e\"',\n });\n }\n\n const pwInstall = deps.spawnSync(\n process.platform === 'win32' ? 'npx.cmd' : 'npx',\n ['playwright', '--version'],\n { cwd, encoding: 'utf8' },\n );\n checks.push({\n name: 'Playwright CLI',\n ok: spawnOk(pwInstall),\n detail: spawnOk(pwInstall)\n ? pwInstall.stdout.trim() || 'playwright available'\n : 'Playwright not available via npx',\n fix: 'pnpm add -D playwright && pnpm exec playwright install chromium',\n });\n\n const skillPath = join(\n cwd,\n '.cursor',\n 'skills',\n 'frontend-precision',\n 'SKILL.md',\n );\n checks.push({\n name: 'Cursor Skill installed',\n ok: existsSync(skillPath),\n detail: existsSync(skillPath) ? skillPath : 'Skill not found',\n fix: 'Run `frontend-precision init`',\n });\n\n return { checks, ok: checks.every((c) => c.ok) };\n}\n\nexport function formatDoctor(checks: DoctorCheck[]): string {\n const lines = ['frontend-precision doctor', ''];\n for (const c of checks) {\n lines.push(`${c.ok ? '✓' : '✗'} ${c.name}`);\n lines.push(` ${c.detail}`);\n if (!c.ok && c.fix) lines.push(` Fix: ${c.fix}`);\n }\n lines.push('');\n lines.push(\n checks.every((c) => c.ok)\n ? 'All checks passed.'\n : 'Some checks failed. Fix the items above before capture/visual/test.',\n );\n return lines.join('\\n');\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport {\n CONFIG_FILENAME,\n ConfigSchema,\n type FrontendPrecisionConfig,\n} from './schema.js';\n\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nexport function loadConfig(cwd = process.cwd()): FrontendPrecisionConfig {\n const path = resolve(cwd, CONFIG_FILENAME);\n if (!existsSync(path)) {\n throw new ConfigError(\n `Missing ${CONFIG_FILENAME}. Run \\`frontend-precision init\\` first.`,\n );\n }\n\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(path, 'utf8'));\n } catch (err) {\n throw new ConfigError(\n `Invalid JSON in ${CONFIG_FILENAME}: ${(err as Error).message}`,\n );\n }\n\n const parsed = ConfigSchema.safeParse(raw);\n if (!parsed.success) {\n const details = parsed.error.issues\n .map((i) => ` - ${i.path.join('.') || '(root)'}: ${i.message}`)\n .join('\\n');\n throw new ConfigError(`${CONFIG_FILENAME} validation failed:\\n${details}`);\n }\n\n return parsed.data;\n}\n\nexport function tryLoadConfig(\n cwd = process.cwd(),\n): FrontendPrecisionConfig | null {\n try {\n return loadConfig(cwd);\n } catch {\n return null;\n }\n}\n","/** Treat only 2xx as healthy for doctor/capture readiness. */\nexport function isHealthyHttpStatus(status: number): boolean {\n return Number.isInteger(status) && status >= 200 && status < 300;\n}\n","import { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { loadConfig } from '../config/load.js';\nimport { captureRoutes } from '../lib/screenshot.js';\nimport { createRunId } from '../lib/run-id.js';\nimport { artifactsDir, ensureDir } from '../lib/paths.js';\n\nexport async function runCapture(options: {\n cwd?: string;\n runId?: string;\n routes?: string[];\n}): Promise<{ runId: string; ok: boolean; message: string }> {\n const cwd = options.cwd ?? process.cwd();\n const config = loadConfig(cwd);\n const runId = options.runId ?? createRunId();\n ensureDir(artifactsDir(runId, cwd));\n\n const result = await captureRoutes({\n config,\n runId,\n cwd,\n routeFilter: options.routes,\n });\n\n writeFileSync(\n join(artifactsDir(runId, cwd), 'run-id.txt'),\n runId,\n 'utf8',\n );\n\n const failed = result.screenshots.filter((s) => !s.ok);\n const ok = failed.length === 0;\n const message = [\n `Capture run: ${runId}`,\n ...result.screenshots.map(\n (s) =>\n ` ${s.ok ? '✓' : '✗'} ${s.name} → ${s.path}${s.error ? ` (${s.error})` : ''}`,\n ),\n `Meta: ${result.metaPath}`,\n ].join('\\n');\n\n return { runId, ok, message };\n}\n","import { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { chromium, firefox, webkit, type BrowserType } from 'playwright';\nimport type { FrontendPrecisionConfig, RouteConfig } from '../config/schema.js';\nimport { ensureDir, screenshotPath, screenshotsDir } from './paths.js';\nimport { isHealthyHttpStatus } from './http-status.js';\nimport { selectRoutes } from './routes.js';\n\nfunction browserType(name: FrontendPrecisionConfig['browser']): BrowserType {\n switch (name) {\n case 'firefox':\n return firefox;\n case 'webkit':\n return webkit;\n default:\n return chromium;\n }\n}\n\nexport type CaptureResult = {\n runId: string;\n screenshots: Array<{\n name: string;\n path: string;\n url: string;\n ok: boolean;\n error?: string;\n }>;\n metaPath: string;\n};\n\nexport async function captureRoutes(options: {\n config: FrontendPrecisionConfig;\n runId: string;\n cwd?: string;\n routeFilter?: string[];\n}): Promise<CaptureResult> {\n const { config, runId, cwd, routeFilter } = options;\n const outDir = ensureDir(screenshotsDir(runId, cwd));\n const routes = selectRoutes(config.routes, routeFilter);\n\n const browser = await browserType(config.browser).launch({ headless: true });\n const context = await browser.newContext({\n viewport: config.viewport,\n deviceScaleFactor: 1,\n });\n const page = await context.newPage();\n const screenshots: CaptureResult['screenshots'] = [];\n const routeViewports: Record<string, { width: number; height: number }> = {};\n\n try {\n for (const route of routes) {\n const url = new URL(route.path, config.baseUrl).toString();\n const filePath = screenshotPath(runId, route.name, cwd);\n const viewport = route.viewport ?? config.viewport;\n routeViewports[route.name] = viewport;\n try {\n await page.setViewportSize(viewport);\n // Prefer load over networkidle so 4xx/5xx fail fast instead of hanging.\n const response = await page.goto(url, {\n waitUntil: 'load',\n timeout: 60_000,\n });\n const status = response?.status() ?? 0;\n const finalUrl = page.url();\n if (!response || !isHealthyHttpStatus(status)) {\n throw new Error(\n `HTTP ${status} for ${url} (final: ${finalUrl}); expected 2xx`,\n );\n }\n await page.waitForLoadState('networkidle', { timeout: 30_000 }).catch(\n () => undefined,\n );\n await page.evaluate(async () => {\n const fonts = (\n globalThis as unknown as {\n document?: { fonts?: { ready?: Promise<unknown> } };\n }\n ).document?.fonts;\n if (fonts?.ready) await fonts.ready;\n });\n if (route.waitFor) {\n await page.waitForSelector(route.waitFor, { timeout: 30_000 });\n }\n if (route.hide?.length) {\n await page.addStyleTag({\n content: route.hide\n .map((sel) => `${sel}{visibility:hidden !important;}`)\n .join('\\n'),\n });\n }\n if (config.settleMs > 0) {\n await page.waitForTimeout(config.settleMs);\n }\n await page.screenshot({ path: filePath, fullPage: false });\n screenshots.push({ name: route.name, path: filePath, url: finalUrl, ok: true });\n } catch (err) {\n screenshots.push({\n name: route.name,\n path: filePath,\n url,\n ok: false,\n error: (err as Error).message,\n });\n }\n }\n } finally {\n await context.close();\n await browser.close();\n }\n\n const meta = {\n runId,\n capturedAt: new Date().toISOString(),\n viewport: config.viewport,\n routeViewports,\n browser: config.browser,\n baseUrl: config.baseUrl,\n screenshots,\n };\n const metaPath = join(outDir, 'capture-meta.json');\n writeFileSync(metaPath, JSON.stringify(meta, null, 2));\n\n return { runId, screenshots, metaPath };\n}\n\nexport function describeRoute(route: RouteConfig): string {\n return `${route.name} (${route.path})`;\n}\n","import { mkdirSync } from 'node:fs';\nimport { join, resolve, sep } from 'node:path';\nimport { assertSafeRunId } from './run-id.js';\nimport { assertSafeSlug } from './safe-slug.js';\n\nexport const ARTIFACTS_ROOT = '.frontend-precision';\nexport const TASKS_DIR = 'tasks';\nexport const ARTIFACTS_DIR = 'artifacts';\n\nexport function resolveCwd(cwd?: string): string {\n return resolve(cwd ?? process.cwd());\n}\n\nexport function fpRoot(cwd?: string): string {\n return join(resolveCwd(cwd), ARTIFACTS_ROOT);\n}\n\nexport function artifactsDir(runId: string, cwd?: string): string {\n const safeId = assertSafeRunId(runId);\n const root = resolve(join(fpRoot(cwd), ARTIFACTS_DIR));\n const resolved = resolve(root, safeId);\n const prefix = root.endsWith(sep) ? root : root + sep;\n if (resolved !== root && !resolved.startsWith(prefix)) {\n throw new Error(\n `Invalid run id \"${runId}\": resolved path escapes artifacts directory.`,\n );\n }\n return resolved;\n}\n\nexport function screenshotsDir(runId: string, cwd?: string): string {\n return join(artifactsDir(runId, cwd), 'screenshots');\n}\n\nfunction assertContainedPath(\n root: string,\n candidate: string,\n label: string,\n): string {\n const resolvedRoot = resolve(root);\n const resolvedCandidate = resolve(candidate);\n const prefix = resolvedRoot.endsWith(sep)\n ? resolvedRoot\n : resolvedRoot + sep;\n if (\n resolvedCandidate !== resolvedRoot &&\n !resolvedCandidate.startsWith(prefix)\n ) {\n throw new Error(`${label} escapes its output directory.`);\n }\n return resolvedCandidate;\n}\n\nexport function screenshotPath(\n runId: string,\n routeName: string,\n cwd?: string,\n): string {\n const safeName = assertSafeSlug(routeName, 'route name');\n const root = screenshotsDir(runId, cwd);\n return assertContainedPath(\n root,\n resolve(root, `${safeName}.png`),\n 'Route screenshot path',\n );\n}\n\nexport function visualDir(\n runId: string,\n routeName: string,\n cwd?: string,\n): string {\n const safeName = assertSafeSlug(routeName, 'route name');\n const root = resolve(artifactsDir(runId, cwd), 'visual');\n return assertContainedPath(\n root,\n resolve(root, safeName),\n 'Route visual path',\n );\n}\n\nexport function ensureDir(path: string): string {\n mkdirSync(path, { recursive: true });\n return path;\n}\n\nexport function taskDir(taskId: string, cwd?: string): string {\n return join(fpRoot(cwd), TASKS_DIR, taskId);\n}\n","import { assertSafeSlug } from './safe-slug.js';\n\nexport function createRunId(date = new Date()): string {\n const iso = date.toISOString().replace(/[:.]/g, '-');\n const rand = Math.random().toString(36).slice(2, 8);\n return `${iso}_${rand}`;\n}\n\nexport function assertSafeRunId(runId: string): string {\n return assertSafeSlug(runId, 'run id');\n}\n","import type { RouteConfig } from '../config/schema.js';\n\nexport function selectRoutes(\n routes: RouteConfig[],\n filter?: string[],\n): RouteConfig[] {\n if (!filter?.length) return routes;\n\n const missing = filter.filter(\n (name) => !routes.some((route) => route.name === name),\n );\n if (missing.length > 0) {\n const valid = routes.map((r) => r.name).join(', ') || '(none)';\n throw new Error(\n `Unknown route(s): ${missing.join(', ')}. Valid routes: ${valid}`,\n );\n }\n\n return routes.filter((route) => filter.includes(route.name));\n}\n","import { resolve } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { loadConfig } from '../config/load.js';\nimport { compareImages } from '../lib/visual-diff.js';\nimport { createRunId } from '../lib/run-id.js';\nimport { selectRoutes } from '../lib/routes.js';\nimport {\n artifactsDir,\n ensureDir,\n screenshotPath,\n visualDir,\n} from '../lib/paths.js';\n\nexport async function runVisual(options: {\n cwd?: string;\n runId?: string;\n routes?: string[];\n}): Promise<{ runId: string; ok: boolean; message: string }> {\n const cwd = options.cwd ?? process.cwd();\n const config = loadConfig(cwd);\n let runId = options.runId;\n\n if (!runId) {\n runId = createRunId();\n }\n\n ensureDir(artifactsDir(runId, cwd));\n\n const routes = selectRoutes(config.routes, options.routes);\n\n const lines: string[] = [`Visual run: ${runId}`];\n let ok = true;\n\n for (const route of routes) {\n const actualPath = screenshotPath(runId, route.name, cwd);\n const referencePath = route.reference\n ? resolve(cwd, route.reference)\n : '';\n const outDir = visualDir(runId, route.name, cwd);\n\n if (!route.reference) {\n ok = false;\n lines.push(` ✗ ${route.name}: no reference configured`);\n compareImages({\n name: route.name,\n referencePath: '',\n actualPath,\n outputDir: outDir,\n pixelThreshold: config.visual.pixelThreshold,\n maxDiffRatio: config.visual.maxDiffRatio,\n referenceKind: route.referenceKind,\n rejectIdenticalBaseline: config.gates.rejectIdenticalBaseline,\n });\n continue;\n }\n\n if (!existsSync(actualPath)) {\n ok = false;\n lines.push(\n ` ✗ ${route.name}: missing actual screenshot (run capture first)`,\n );\n }\n\n const result = compareImages({\n name: route.name,\n referencePath,\n actualPath,\n outputDir: outDir,\n pixelThreshold: config.visual.pixelThreshold,\n maxDiffRatio: config.visual.maxDiffRatio,\n referenceKind: route.referenceKind,\n rejectIdenticalBaseline: config.gates.rejectIdenticalBaseline,\n });\n\n if (!result.passed) ok = false;\n const kindTag = `kind=${result.referenceKind}`;\n const identicalTag = result.identicalBytes ? ' identical' : '';\n lines.push(\n ` ${result.passed ? '✓' : '✗'} ${route.name}: diffRatio=${(result.diffRatio * 100).toFixed(3)}% (${kindTag}${identicalTag})${result.reason ? ` — ${result.reason}` : ''}`,\n );\n lines.push(` → ${result.paths.resultJson}`);\n }\n\n return { runId, ok, message: lines.join('\\n') };\n}\n","import {\n existsSync,\n readFileSync,\n writeFileSync,\n mkdirSync,\n copyFileSync,\n} from 'node:fs';\nimport { createHash } from 'node:crypto';\nimport { dirname, join } from 'node:path';\nimport { PNG } from 'pngjs';\nimport pixelmatch from 'pixelmatch';\n\nexport type VisualDiffInput = {\n name: string;\n referencePath: string;\n actualPath: string;\n outputDir: string;\n pixelThreshold: number;\n maxDiffRatio: number;\n /** figma | regression — regression 不得当作设计验收 */\n referenceKind?: 'figma' | 'regression';\n /** 为 true 时,reference 与 actual 字节相同则判定失败 */\n rejectIdenticalBaseline?: boolean;\n};\n\nexport type VisualDiffResult = {\n name: string;\n width: number;\n height: number;\n diffRatio: number;\n diffPixels: number;\n totalPixels: number;\n pixelThreshold: number;\n maxDiffRatio: number;\n passed: boolean;\n reason: string | null;\n referenceKind: 'figma' | 'regression';\n identicalBytes: boolean;\n paths: {\n reference: string;\n actual: string;\n diff: string | null;\n resultJson: string;\n };\n};\n\nfunction sha256File(path: string): string {\n return createHash('sha256').update(readFileSync(path)).digest('hex');\n}\n\nfunction ensureParent(filePath: string): void {\n mkdirSync(dirname(filePath), { recursive: true });\n}\n\nexport function compareImages(input: VisualDiffInput): VisualDiffResult {\n const {\n name,\n referencePath,\n actualPath,\n outputDir,\n pixelThreshold,\n maxDiffRatio,\n referenceKind = 'figma',\n rejectIdenticalBaseline = true,\n } = input;\n\n mkdirSync(outputDir, { recursive: true });\n const outRef = join(outputDir, 'reference.png');\n const outActual = join(outputDir, 'actual.png');\n const outDiff = join(outputDir, 'diff.png');\n const outJson = join(outputDir, 'visual-result.json');\n\n const base = {\n name,\n pixelThreshold,\n maxDiffRatio,\n referenceKind,\n identicalBytes: false,\n paths: {\n reference: outRef,\n actual: outActual,\n diff: null as string | null,\n resultJson: outJson,\n },\n };\n\n if (!existsSync(referencePath)) {\n const result: VisualDiffResult = {\n ...base,\n width: 0,\n height: 0,\n diffRatio: 1,\n diffPixels: 0,\n totalPixels: 0,\n passed: false,\n reason: `missing_reference: ${referencePath}`,\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n if (!existsSync(actualPath)) {\n copyFileSync(referencePath, outRef);\n const result: VisualDiffResult = {\n ...base,\n width: 0,\n height: 0,\n diffRatio: 1,\n diffPixels: 0,\n totalPixels: 0,\n passed: false,\n reason: `missing_actual: ${actualPath}`,\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n const identicalBytes =\n sha256File(referencePath) === sha256File(actualPath);\n\n copyFileSync(referencePath, outRef);\n copyFileSync(actualPath, outActual);\n\n // Figma 导出与浏览器截图像素几乎不可能字节级相同;相同通常意味着把 capture 当成了「设计基线」。\n // regression 基线在无 UI 变化时允许 identical(回归通过),但报告层不得宣称 Figma 验收。\n const rejectIdentical =\n identicalBytes &&\n rejectIdenticalBaseline &&\n referenceKind === 'figma';\n\n if (rejectIdentical) {\n const result: VisualDiffResult = {\n ...base,\n identicalBytes: true,\n width: 0,\n height: 0,\n diffRatio: 0,\n diffPixels: 0,\n totalPixels: 0,\n passed: false,\n reason:\n 'self_baseline_identical: reference SHA-256 equals capture while referenceKind=figma; export a real Figma PNG or set referenceKind=regression',\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n let refPng: PNG;\n let actPng: PNG;\n try {\n refPng = PNG.sync.read(readFileSync(referencePath));\n actPng = PNG.sync.read(readFileSync(actualPath));\n } catch (err) {\n const result: VisualDiffResult = {\n ...base,\n identicalBytes,\n width: 0,\n height: 0,\n diffRatio: 1,\n diffPixels: 0,\n totalPixels: 0,\n passed: false,\n reason: `image_read_error: ${(err as Error).message}`,\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n if (refPng.width !== actPng.width || refPng.height !== actPng.height) {\n const result: VisualDiffResult = {\n ...base,\n identicalBytes,\n width: actPng.width,\n height: actPng.height,\n diffRatio: 1,\n diffPixels: 0,\n totalPixels: refPng.width * refPng.height,\n passed: false,\n reason: `dimension_mismatch: reference=${refPng.width}x${refPng.height} actual=${actPng.width}x${actPng.height}`,\n paths: { ...base.paths, diff: null },\n };\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n }\n\n const { width, height } = refPng;\n const diff = new PNG({ width, height });\n const diffPixels = pixelmatch(\n refPng.data,\n actPng.data,\n diff.data,\n width,\n height,\n { threshold: pixelThreshold },\n );\n const totalPixels = width * height;\n const diffRatio = totalPixels === 0 ? 1 : diffPixels / totalPixels;\n const passed = diffRatio <= maxDiffRatio;\n\n ensureParent(outDiff);\n writeFileSync(outDiff, PNG.sync.write(diff));\n\n const result: VisualDiffResult = {\n ...base,\n identicalBytes,\n width,\n height,\n diffRatio,\n diffPixels,\n totalPixels,\n passed,\n reason: passed\n ? identicalBytes\n ? 'identical_bytes_allowed'\n : null\n : `diff_ratio_exceeded: ${(diffRatio * 100).toFixed(3)}% > ${(maxDiffRatio * 100).toFixed(3)}%`,\n paths: { ...base.paths, diff: outDiff },\n };\n\n writeFileSync(outJson, JSON.stringify(result, null, 2));\n return result;\n}\n","import { spawn } from 'node:child_process';\nimport { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { FrontendPrecisionConfig } from '../config/schema.js';\nimport { artifactsDir, ensureDir } from './paths.js';\n\nexport type TestRunStatus = 'PASS' | 'FAIL' | 'SKIPPED';\n\nexport type SingleTestResult = {\n name: string;\n command: string;\n status: TestRunStatus;\n exitCode: number | null;\n durationMs: number;\n stdout: string;\n stderr: string;\n reason?: string;\n};\n\nexport type TestSuiteResult = {\n runId: string;\n results: SingleTestResult[];\n passed: boolean;\n startedAt: string;\n finishedAt: string;\n};\n\nfunction runShell(\n command: string,\n cwd: string,\n): Promise<{\n exitCode: number | null;\n stdout: string;\n stderr: string;\n durationMs: number;\n}> {\n return new Promise((resolve) => {\n const started = Date.now();\n const child = spawn(command, {\n cwd,\n shell: true,\n env: process.env,\n });\n let stdout = '';\n let stderr = '';\n child.stdout.on('data', (d: Buffer) => {\n stdout += d.toString();\n });\n child.stderr.on('data', (d: Buffer) => {\n stderr += d.toString();\n });\n child.on('error', (err) => {\n stderr += err.message;\n resolve({\n exitCode: 127,\n stdout,\n stderr,\n durationMs: Date.now() - started,\n });\n });\n child.on('close', (code) => {\n resolve({\n exitCode: code,\n stdout,\n stderr,\n durationMs: Date.now() - started,\n });\n });\n });\n}\n\nexport async function runConfiguredTests(options: {\n config: FrontendPrecisionConfig;\n runId: string;\n cwd?: string;\n}): Promise<TestSuiteResult> {\n const cwd = options.cwd ?? process.cwd();\n const startedAt = new Date().toISOString();\n const commands: Array<{ name: string; command: string }> = [];\n\n if (options.config.checks?.typecheck) {\n commands.push({ name: 'typecheck', command: options.config.checks.typecheck });\n }\n if (options.config.checks?.lint) {\n commands.push({ name: 'lint', command: options.config.checks.lint });\n }\n if (options.config.checks?.unit) {\n commands.push({ name: 'unit', command: options.config.checks.unit });\n }\n if (options.config.checks?.e2e) {\n commands.push({ name: 'e2e', command: options.config.checks.e2e });\n }\n\n // Always include primary testCommand\n commands.push({ name: 'testCommand', command: options.config.testCommand });\n\n // Dedupe identical commands\n const seen = new Set<string>();\n const unique = commands.filter((c) => {\n if (seen.has(c.command)) return false;\n seen.add(c.command);\n return true;\n });\n\n const results: SingleTestResult[] = [];\n\n for (const item of unique) {\n if (!item.command.trim()) {\n results.push({\n name: item.name,\n command: item.command,\n status: 'FAIL',\n exitCode: null,\n durationMs: 0,\n stdout: '',\n stderr: '',\n reason: 'empty_command',\n });\n continue;\n }\n\n const run = await runShell(item.command, cwd);\n const status: TestRunStatus = run.exitCode === 0 ? 'PASS' : 'FAIL';\n results.push({\n name: item.name,\n command: item.command,\n status,\n exitCode: run.exitCode,\n durationMs: run.durationMs,\n stdout: run.stdout,\n stderr: run.stderr,\n reason:\n status === 'FAIL'\n ? `exit_code_${run.exitCode ?? 'null'}`\n : undefined,\n });\n }\n\n const finishedAt = new Date().toISOString();\n const suite: TestSuiteResult = {\n runId: options.runId,\n results,\n passed: results.every((r) => r.status === 'PASS'),\n startedAt,\n finishedAt,\n };\n\n const dir = ensureDir(artifactsDir(options.runId, cwd));\n writeFileSync(join(dir, 'test-result.json'), JSON.stringify(suite, null, 2));\n return suite;\n}\n","import { loadConfig } from '../config/load.js';\nimport { runConfiguredTests } from '../lib/test-runner.js';\nimport { createRunId } from '../lib/run-id.js';\nimport { artifactsDir, ensureDir } from '../lib/paths.js';\nimport { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport async function runTestCommand(options: {\n cwd?: string;\n runId?: string;\n}): Promise<{ runId: string; ok: boolean; message: string }> {\n const cwd = options.cwd ?? process.cwd();\n const config = loadConfig(cwd);\n const runId = options.runId ?? createRunId();\n ensureDir(artifactsDir(runId, cwd));\n writeFileSync(join(artifactsDir(runId, cwd), 'run-id.txt'), runId);\n\n const suite = await runConfiguredTests({ config, runId, cwd });\n const lines = [\n `Test run: ${runId}`,\n ...suite.results.map(\n (r) =>\n ` ${r.status === 'PASS' ? '✓' : '✗'} ${r.name} (${r.command}) exit=${r.exitCode} ${r.durationMs}ms`,\n ),\n `Overall: ${suite.passed ? 'PASS' : 'FAIL'}`,\n ];\n\n return { runId, ok: suite.passed, message: lines.join('\\n') };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { loadConfig } from '../config/load.js';\nimport { buildReport } from '../lib/report-builder.js';\nimport { artifactsDir } from '../lib/paths.js';\nimport { createRunId } from '../lib/run-id.js';\n\nexport function runReport(options: {\n cwd?: string;\n runId?: string;\n taskId?: string;\n codeChangeSummary?: string;\n fixAttempts?: number;\n blockedReason?: string;\n}): { runId: string; ok: boolean; message: string; reportPath: string } {\n const cwd = options.cwd ?? process.cwd();\n const config = loadConfig(cwd);\n\n let runId = options.runId;\n if (!runId) {\n runId = createRunId();\n }\n\n const runDir = artifactsDir(runId, cwd);\n if (!existsSync(runDir)) {\n return {\n runId,\n ok: false,\n message: `Artifacts not found for run ${runId}. Run capture/visual/test first.`,\n reportPath: '',\n };\n }\n\n let codeChangeSummary = options.codeChangeSummary ?? null;\n const summaryFile = join(runDir, 'code-change-summary.md');\n if (!codeChangeSummary && existsSync(summaryFile)) {\n codeChangeSummary = readFileSync(summaryFile, 'utf8');\n }\n\n const report = buildReport({\n runId,\n cwd,\n taskId: options.taskId ?? config.taskId,\n codeChangeSummary,\n fixAttempts: options.fixAttempts,\n maxFixAttempts: config.maxFixAttempts,\n blockedReason: options.blockedReason ?? null,\n gates: config.gates,\n });\n\n const reportPath = join(runDir, 'report.json');\n const message = [\n `Report: ${report.status}`,\n ` JSON: ${reportPath}`,\n ` MD: ${join(runDir, 'report.md')}`,\n ` HTML: ${join(runDir, 'report.html')}`,\n report.acceptance.length\n ? ` Acceptance: ${report.acceptance.length} rows (${report.acceptance.filter((a) => a.status === 'PASS').length} PASS / ${report.acceptance.filter((a) => a.status === 'PENDING').length} PENDING)`\n : ' Acceptance: (none loaded)',\n report.failures.length\n ? `Failures:\\n${report.failures.map((f) => ` - ${f}`).join('\\n')}`\n : 'No failures recorded.',\n report.unverified.length\n ? `Unverified:\\n${report.unverified.map((u) => ` - ${u}`).join('\\n')}`\n : '',\n ]\n .filter(Boolean)\n .join('\\n');\n\n return {\n runId,\n ok: report.status === 'PASS' && report.failures.length === 0,\n message,\n reportPath,\n };\n}\n","import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { artifactsDir, ensureDir } from './paths.js';\nimport type { VisualDiffResult } from './visual-diff.js';\nimport type { TestSuiteResult } from './test-runner.js';\nimport {\n evaluateAcceptanceEvidence,\n evaluateAcceptanceGate,\n loadAcceptanceFromTask,\n type AcceptanceRow,\n type AcceptanceStatus,\n type RequirementRow,\n} from './acceptance.js';\n\nexport type ReportStatus = 'PASS' | 'FAIL' | 'SKIPPED' | 'BLOCKED';\n\n/** 报告总状态 + AC/REQ 行状态(含 PENDING/PARTIAL) */\nexport type CriterionStatus = ReportStatus | AcceptanceStatus;\n\nexport type CaptureMeta = {\n screenshots: Array<{\n name: string;\n path: string;\n url: string;\n ok: boolean;\n error?: string;\n }>;\n};\n\nexport type PrecisionReport = {\n runId: string;\n generatedAt: string;\n status: ReportStatus;\n summary: {\n visualPassed: number;\n visualFailed: number;\n visualSkipped: number;\n testsPassed: number;\n testsFailed: number;\n capturePassed: number;\n captureFailed: number;\n };\n requirements: Array<{ id: string; title: string; status: CriterionStatus }>;\n acceptance: Array<{\n id: string;\n requirementId?: string;\n title: string;\n status: CriterionStatus;\n }>;\n capture: CaptureMeta | null;\n visual: VisualDiffResult[];\n tests: TestSuiteResult | null;\n failures: string[];\n unverified: string[];\n screenshots: string[];\n codeChangeSummary: string | null;\n manualReview: string[];\n fixAttempts?: number;\n maxFixAttempts?: number;\n};\n\nfunction readJsonIfExists<T>(path: string): T | null {\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as T;\n } catch {\n return null;\n }\n}\n\nfunction collectVisualResults(runDir: string): VisualDiffResult[] {\n const visualRoot = join(runDir, 'visual');\n if (!existsSync(visualRoot)) return [];\n const results: VisualDiffResult[] = [];\n for (const name of readdirSync(visualRoot)) {\n const jsonPath = join(visualRoot, name, 'visual-result.json');\n const data = readJsonIfExists<VisualDiffResult>(jsonPath);\n if (data) results.push(data);\n }\n return results;\n}\n\nfunction collectScreenshots(runDir: string): string[] {\n const shotDir = join(runDir, 'screenshots');\n if (!existsSync(shotDir)) return [];\n return readdirSync(shotDir)\n .filter((f) => f.endsWith('.png'))\n .map((f) => join(shotDir, f));\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nfunction truncateLog(text: string, max = 4000): string {\n if (text.length <= max) return text;\n return `${text.slice(0, max)}\\n…(truncated)`;\n}\n\nexport type ReportGates = {\n requireAcceptance: boolean;\n requireAcceptanceAllPass: boolean;\n requireFigmaVisual: boolean;\n rejectIdenticalBaseline: boolean;\n};\n\nexport function buildReport(options: {\n runId: string;\n cwd?: string;\n taskId?: string;\n requirements?: PrecisionReport['requirements'];\n acceptance?: PrecisionReport['acceptance'];\n codeChangeSummary?: string | null;\n fixAttempts?: number;\n maxFixAttempts?: number;\n blockedReason?: string | null;\n gates?: Partial<ReportGates>;\n}): PrecisionReport {\n const cwd = options.cwd ?? process.cwd();\n const runDir = artifactsDir(options.runId, cwd);\n const capture = readJsonIfExists<CaptureMeta>(\n join(runDir, 'screenshots', 'capture-meta.json'),\n );\n const visual = collectVisualResults(runDir);\n const tests = readJsonIfExists<TestSuiteResult>(\n join(runDir, 'test-result.json'),\n );\n const screenshots = collectScreenshots(runDir);\n\n const gates: ReportGates = {\n requireAcceptance: options.gates?.requireAcceptance ?? true,\n requireAcceptanceAllPass: options.gates?.requireAcceptanceAllPass ?? true,\n requireFigmaVisual: options.gates?.requireFigmaVisual ?? false,\n rejectIdenticalBaseline: options.gates?.rejectIdenticalBaseline ?? true,\n };\n\n const fromTask = loadAcceptanceFromTask({\n cwd,\n taskId: options.taskId,\n runId: options.runId,\n });\n\n function toAcceptanceStatus(status: CriterionStatus): AcceptanceStatus {\n if (status === 'BLOCKED') return 'FAIL';\n if (\n status === 'PASS' ||\n status === 'FAIL' ||\n status === 'PENDING' ||\n status === 'SKIPPED' ||\n status === 'PARTIAL'\n ) {\n return status;\n }\n return 'PENDING';\n }\n\n const acceptanceRows: AcceptanceRow[] =\n options.acceptance && options.acceptance.length > 0\n ? options.acceptance.map((a) => ({\n id: a.id,\n title: a.title,\n requirementId: a.requirementId,\n status: toAcceptanceStatus(a.status),\n }))\n : fromTask.acceptance;\n\n const requirementRows: RequirementRow[] =\n options.requirements && options.requirements.length > 0\n ? options.requirements.map((r) => ({\n id: r.id,\n title: r.title,\n status: toAcceptanceStatus(r.status),\n }))\n : fromTask.requirements;\n\n const codeChangeSummary =\n options.codeChangeSummary ?? fromTask.codeChangeSummary ?? null;\n\n const failures: string[] = [];\n const unverified: string[] = [];\n\n if (capture) {\n for (const shot of capture.screenshots) {\n if (!shot.ok) {\n failures.push(\n `capture:${shot.name}: ${shot.error ?? 'failed'}${shot.url ? ` (${shot.url})` : ''}`,\n );\n }\n }\n } else {\n unverified.push('capture_not_run');\n }\n\n for (const v of visual) {\n if (!v.passed) failures.push(`visual:${v.name}: ${v.reason ?? 'failed'}`);\n if (v.referenceKind === 'regression') {\n unverified.push(`figma_visual_not_proven:${v.name}`);\n }\n if (v.identicalBytes && v.referenceKind === 'regression') {\n unverified.push(`regression_identical:${v.name}`);\n }\n }\n if (visual.length === 0) {\n unverified.push('visual_not_run');\n }\n\n const hasFigmaVisualPass = visual.some(\n (v) =>\n v.passed &&\n (v.referenceKind ?? 'figma') === 'figma' &&\n !v.identicalBytes,\n );\n\n if (gates.requireFigmaVisual && !hasFigmaVisualPass) {\n failures.push(\n 'figma_visual_required: no passing referenceKind=figma visual (regression/self-baseline does not count)',\n );\n }\n\n if (tests) {\n for (const t of tests.results) {\n if (t.status === 'FAIL') {\n failures.push(`test:${t.name}: ${t.reason ?? `exit ${t.exitCode}`}`);\n }\n }\n } else {\n unverified.push('tests_not_run');\n }\n\n const acGate = evaluateAcceptanceGate(acceptanceRows, {\n requireAcceptance: gates.requireAcceptance,\n requireAcceptanceAllPass: gates.requireAcceptanceAllPass,\n });\n failures.push(...acGate.failures);\n unverified.push(...acGate.unverified);\n\n if (gates.requireAcceptance) {\n const evidence = evaluateAcceptanceEvidence(acceptanceRows, {\n tests,\n visualPassedNames: visual.filter((v) => v.passed).map((v) => v.name),\n hasFigmaVisualPass,\n });\n failures.push(...evidence.failures);\n unverified.push(...evidence.unverified);\n }\n\n let status: ReportStatus = 'PASS';\n const fixAttempts = options.fixAttempts;\n const maxFixAttempts = options.maxFixAttempts;\n let blockedReason = options.blockedReason ?? null;\n\n if (\n fixAttempts !== undefined &&\n maxFixAttempts !== undefined &&\n fixAttempts > maxFixAttempts\n ) {\n blockedReason =\n blockedReason ??\n `fix_attempts_exceeded: ${fixAttempts} > maxFixAttempts=${maxFixAttempts}`;\n }\n\n if (blockedReason) {\n status = 'BLOCKED';\n if (!failures.includes(blockedReason)) failures.push(blockedReason);\n } else if (failures.length > 0) {\n status = 'FAIL';\n } else if (unverified.length > 0) {\n // regression-only visual notes should not alone block PASS when AC all pass\n // and gates.requireFigmaVisual is false — strip soft unverified for status?\n // Codex: CLI PASS without AC is wrong. Soft unverified like figma_visual_not_proven\n // should keep status from being a \"full acceptance PASS\".\n const hardUnverified = unverified.filter(\n (u) =>\n u === 'capture_not_run' ||\n u === 'visual_not_run' ||\n u === 'tests_not_run' ||\n u.startsWith('acceptance_pending:') ||\n u.startsWith('acceptance_partial:'),\n );\n if (hardUnverified.length > 0) {\n status = 'SKIPPED';\n } else if (unverified.some((u) => u.startsWith('figma_visual_not_proven'))) {\n // Pipeline ok but not design-acceptance complete\n status = 'PASS';\n // Keep unverified listed; Skill must not mark DONE when figma_visual_not_proven present\n } else {\n status = 'PASS';\n }\n }\n\n const report: PrecisionReport = {\n runId: options.runId,\n generatedAt: new Date().toISOString(),\n status,\n summary: {\n visualPassed: visual.filter((v) => v.passed).length,\n visualFailed: visual.filter((v) => !v.passed).length,\n visualSkipped: 0,\n testsPassed: tests?.results.filter((r) => r.status === 'PASS').length ?? 0,\n testsFailed: tests?.results.filter((r) => r.status === 'FAIL').length ?? 0,\n capturePassed: capture?.screenshots.filter((s) => s.ok).length ?? 0,\n captureFailed: capture?.screenshots.filter((s) => !s.ok).length ?? 0,\n },\n requirements: requirementRows.map((r) => ({\n id: r.id,\n title: r.title,\n status: r.status,\n })),\n acceptance: acceptanceRows.map((a) => ({\n id: a.id,\n requirementId: a.requirementId,\n title: a.title,\n status: a.status,\n })),\n capture,\n visual,\n tests,\n failures,\n unverified,\n screenshots,\n codeChangeSummary,\n manualReview: [\n 'DONE 仅当:report.status===PASS 且 acceptance 无 PENDING/PARTIAL/FAIL 且无 figma_visual_not_proven(或已明确只要 regression)',\n 'Review Figma vs screenshot for typography and spacing (pixel diff is not full design QA)',\n 'Confirm Loading / Error / Empty states if not covered by e2e',\n 'Confirm no secrets were committed',\n ],\n fixAttempts: options.fixAttempts,\n maxFixAttempts: options.maxFixAttempts,\n };\n\n const outDir = ensureDir(runDir);\n writeFileSync(join(outDir, 'report.json'), JSON.stringify(report, null, 2));\n writeFileSync(join(outDir, 'report.md'), renderMarkdown(report));\n writeFileSync(join(outDir, 'report.html'), renderHtml(report));\n return report;\n}\n\nexport function renderMarkdown(report: PrecisionReport): string {\n const lines: string[] = [\n `# Frontend Precision Report`,\n '',\n `- Run ID: \\`${report.runId}\\``,\n `- Status: **${report.status}**`,\n `- Generated: ${report.generatedAt}`,\n '',\n `## Summary`,\n '',\n `| Area | Passed | Failed |`,\n `|---|---:|---:|`,\n `| Capture | ${report.summary.capturePassed} | ${report.summary.captureFailed} |`,\n `| Visual | ${report.summary.visualPassed} | ${report.summary.visualFailed} |`,\n `| Tests | ${report.summary.testsPassed} | ${report.summary.testsFailed} |`,\n '',\n ];\n\n if (report.failures.length) {\n lines.push('## Failures', '');\n for (const f of report.failures) lines.push(`- ${f}`);\n lines.push('');\n }\n\n if (report.unverified.length) {\n lines.push('## Unverified', '');\n for (const u of report.unverified) lines.push(`- ${u}`);\n lines.push('');\n } else {\n lines.push('## Unverified', '', '- (none)', '');\n }\n\n if (report.visual.length) {\n lines.push('## Visual', '');\n for (const v of report.visual) {\n lines.push(\n `- **${v.name}**: ${v.passed ? 'PASS' : 'FAIL'} (diffRatio=${(v.diffRatio * 100).toFixed(3)}%)${v.reason ? ` — ${v.reason}` : ''}`,\n );\n if (v.paths.diff) lines.push(` - Diff: \\`${v.paths.diff}\\``);\n lines.push(` - Reference: \\`${v.paths.reference}\\``);\n lines.push(` - Actual: \\`${v.paths.actual}\\``);\n }\n lines.push('');\n }\n\n if (report.tests) {\n lines.push('## Tests', '');\n for (const t of report.tests.results) {\n lines.push(\n `- **${t.name}**: ${t.status} (exit=${t.exitCode}, ${t.durationMs}ms)`,\n );\n lines.push(` - Command: \\`${t.command}\\``);\n if (t.stdout?.trim()) {\n lines.push(' - stdout:', '', '```', truncateLog(t.stdout).trimEnd(), '```', '');\n }\n if (t.stderr?.trim()) {\n lines.push(' - stderr:', '', '```', truncateLog(t.stderr).trimEnd(), '```', '');\n }\n }\n lines.push('');\n }\n\n if (report.requirements.length) {\n lines.push('## Requirements', '');\n for (const r of report.requirements) {\n lines.push(`- ${r.id}: ${r.title} — ${r.status}`);\n }\n lines.push('');\n }\n\n if (report.acceptance.length) {\n lines.push('## Acceptance Criteria', '');\n for (const a of report.acceptance) {\n lines.push(\n `- ${a.id}${a.requirementId ? ` (${a.requirementId})` : ''}: ${a.title} — ${a.status}`,\n );\n }\n lines.push('');\n }\n\n if (report.codeChangeSummary) {\n lines.push('## Code Change Summary', '', report.codeChangeSummary, '');\n }\n\n lines.push('## Manual Review', '');\n for (const m of report.manualReview) lines.push(`- ${m}`);\n lines.push('');\n\n lines.push('## Screenshots', '');\n if (report.screenshots.length) {\n for (const s of report.screenshots) lines.push(`- \\`${s}\\``);\n } else {\n lines.push('- (none)');\n }\n lines.push('');\n\n return lines.join('\\n');\n}\n\nexport function renderHtml(report: PrecisionReport): string {\n const statusColor =\n report.status === 'PASS'\n ? '#067647'\n : report.status === 'BLOCKED'\n ? '#b54708'\n : report.status === 'SKIPPED'\n ? '#667085'\n : '#b42318';\n\n const failureLis = report.failures\n .map((f) => `<li>${escapeHtml(f)}</li>`)\n .join('');\n const unverifiedLis = report.unverified.length\n ? report.unverified.map((u) => `<li>${escapeHtml(u)}</li>`).join('')\n : '<li>(none)</li>';\n const screenshotLis = report.screenshots.length\n ? report.screenshots\n .map((s) => `<li><code>${escapeHtml(s)}</code></li>`)\n .join('')\n : '<li>(none)</li>';\n const visualRows = report.visual\n .map(\n (v) =>\n `<tr><td>${escapeHtml(v.name)}</td><td>${v.passed ? 'PASS' : 'FAIL'}</td><td>${(v.diffRatio * 100).toFixed(3)}%</td><td>${escapeHtml(v.reason ?? '')}</td><td><code>${escapeHtml(v.paths.diff ?? '')}</code></td></tr>`,\n )\n .join('');\n const testRows =\n report.tests?.results\n .map((t) => {\n const logs = [\n t.stdout?.trim()\n ? `<details><summary>stdout</summary><pre>${escapeHtml(truncateLog(t.stdout))}</pre></details>`\n : '',\n t.stderr?.trim()\n ? `<details><summary>stderr</summary><pre>${escapeHtml(truncateLog(t.stderr))}</pre></details>`\n : '',\n ]\n .filter(Boolean)\n .join('');\n return `<tr><td>${escapeHtml(t.name)}</td><td>${t.status}</td><td>${t.exitCode}</td><td>${t.durationMs}ms</td><td>${logs || '—'}</td></tr>`;\n })\n .join('') ?? '';\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Frontend Precision Report — ${escapeHtml(report.runId)}</title>\n <style>\n body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;color:#101828;background:#fff}\n h1{font-size:1.5rem} table{border-collapse:collapse;width:100%;margin:1rem 0}\n th,td{border:1px solid #e4e7ec;padding:.5rem .75rem;text-align:left;font-size:.875rem;vertical-align:top}\n th{background:#f9fafb} .badge{display:inline-block;padding:.25rem .6rem;border-radius:999px;color:#fff;background:${statusColor};font-weight:600}\n code{background:#f2f4f7;padding:.1rem .35rem;border-radius:4px}\n pre{white-space:pre-wrap;background:#f2f4f7;padding:.75rem;border-radius:6px;max-height:240px;overflow:auto}\n </style>\n</head>\n<body>\n <h1>Frontend Precision Report</h1>\n <p>Run: <code>${escapeHtml(report.runId)}</code></p>\n <p>Status: <span class=\"badge\">${escapeHtml(report.status)}</span></p>\n <p>Generated: ${escapeHtml(report.generatedAt)}</p>\n <h2>Failures</h2>\n ${failureLis ? `<ul>${failureLis}</ul>` : '<p>None</p>'}\n <h2>Unverified</h2>\n <ul>${unverifiedLis}</ul>\n <h2>Screenshots</h2>\n <ul>${screenshotLis}</ul>\n <h2>Visual</h2>\n <table><thead><tr><th>Name</th><th>Status</th><th>Diff ratio</th><th>Reason</th><th>Diff</th></tr></thead><tbody>${visualRows || '<tr><td colspan=\"5\">No visual results</td></tr>'}</tbody></table>\n <h2>Tests</h2>\n <table><thead><tr><th>Name</th><th>Status</th><th>Exit</th><th>Duration</th><th>Logs</th></tr></thead><tbody>${testRows || '<tr><td colspan=\"5\">Tests not run</td></tr>'}</tbody></table>\n <h2>Manual review</h2>\n <ul>${report.manualReview.map((m) => `<li>${escapeHtml(m)}</li>`).join('')}</ul>\n</body>\n</html>`;\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { join, resolve, sep } from 'node:path';\nimport { isSafeSlug } from './safe-slug.js';\nimport type { TestSuiteResult } from './test-runner.js';\n\nexport type AcceptanceStatus =\n | 'PASS'\n | 'FAIL'\n | 'PENDING'\n | 'SKIPPED'\n | 'PARTIAL';\n\nexport type AcceptanceRow = {\n id: string;\n title: string;\n requirementId?: string;\n /** 验证方式列原文,用于证据启发(unit / Playwright / visual) */\n verification?: string;\n status: AcceptanceStatus;\n};\n\nexport type RequirementRow = {\n id: string;\n title: string;\n status: AcceptanceStatus;\n};\n\nconst STATUS_SET = new Set<AcceptanceStatus>([\n 'PASS',\n 'FAIL',\n 'PENDING',\n 'SKIPPED',\n 'PARTIAL',\n]);\n\nfunction normalizeStatus(raw: string): AcceptanceStatus {\n const value = raw.trim().toUpperCase();\n if (STATUS_SET.has(value as AcceptanceStatus)) {\n return value as AcceptanceStatus;\n }\n return 'PENDING';\n}\n\nfunction normalizeAcId(raw: string): string {\n const match = raw.trim().match(/AC-(\\d+)/i);\n const digits = match?.[1];\n if (!digits) return raw.trim().toUpperCase();\n return `AC-${digits.padStart(3, '0')}`;\n}\n\nfunction normalizeReqId(digits: string): string {\n return `REQ-${digits.padStart(3, '0')}`;\n}\n\n/**\n * Parse acceptance.md table rows:\n * | AC-001 | desc | REQ-001 | Playwright | PENDING |\n */\nexport function parseAcceptanceMarkdown(markdown: string): AcceptanceRow[] {\n const rows: AcceptanceRow[] = [];\n for (const line of markdown.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed.startsWith('|')) continue;\n if (/^\\|\\s*-+/.test(trimmed)) continue;\n if (/AC\\s*ID/i.test(trimmed) && /描述|Description/i.test(trimmed)) continue;\n\n const parts = trimmed\n .replace(/^\\|/, '')\n .replace(/\\|$/, '')\n .split('|')\n .map((c) => c.trim());\n\n if (parts.length < 2) continue;\n const idRaw = parts[0];\n if (!idRaw || !/^AC-\\d+/i.test(idRaw)) continue;\n\n const title = parts[1] ?? '';\n const reqCell = parts[2] ?? '';\n const verification = parts.length >= 5 ? parts[3] : undefined;\n const reqMatches = [...reqCell.matchAll(/REQ-(\\d+)/gi)].flatMap((m) => {\n const digits = m[1];\n return digits ? [normalizeReqId(digits)] : [];\n });\n const requirementId =\n reqMatches.length > 0 ? [...new Set(reqMatches)].join(', ') : undefined;\n const statusCell = parts[parts.length - 1] ?? 'PENDING';\n const status = normalizeStatus(statusCell);\n\n rows.push({\n id: normalizeAcId(idRaw),\n title,\n ...(requirementId ? { requirementId } : {}),\n ...(verification ? { verification } : {}),\n status,\n });\n }\n\n return rows;\n}\n\nexport function deriveRequirements(\n acceptance: AcceptanceRow[],\n): RequirementRow[] {\n const map = new Map<string, RequirementRow>();\n\n for (const row of acceptance) {\n const reqIds = (row.requirementId ?? '')\n .split(/[,,\\s]+/)\n .map((s) => s.trim())\n .filter((s) => /^REQ-\\d+/i.test(s));\n\n for (const raw of reqIds) {\n const match = raw.match(/REQ-(\\d+)/i);\n const digits = match?.[1];\n if (!digits) continue;\n const id = normalizeReqId(digits);\n const existing = map.get(id);\n if (!existing) {\n map.set(id, { id, title: id, status: row.status });\n continue;\n }\n existing.status = mergeStatus(existing.status, row.status);\n }\n }\n\n return [...map.values()];\n}\n\nfunction mergeStatus(\n a: AcceptanceStatus,\n b: AcceptanceStatus,\n): AcceptanceStatus {\n if (a === 'FAIL' || b === 'FAIL') return 'FAIL';\n if (a === 'PENDING' || b === 'PENDING') return 'PENDING';\n if (a === 'PARTIAL' || b === 'PARTIAL') return 'PARTIAL';\n if (a === 'SKIPPED' || b === 'SKIPPED') return 'SKIPPED';\n return 'PASS';\n}\n\nfunction isPathInside(parent: string, child: string): boolean {\n const root = resolve(parent);\n const target = resolve(child);\n return target === root || target.startsWith(root + sep);\n}\n\nexport function findTaskDir(options: {\n cwd: string;\n taskId?: string;\n /** 若提供,优先选 `.run-id` 内容匹配的任务目录 */\n runId?: string;\n}): string | null {\n const tasksRoot = join(options.cwd, '.frontend-precision', 'tasks');\n if (!existsSync(tasksRoot)) return null;\n\n if (options.taskId) {\n if (!isSafeSlug(options.taskId)) {\n return null;\n }\n const dir = join(tasksRoot, options.taskId);\n if (!existsSync(dir) || !isPathInside(tasksRoot, dir)) {\n return null;\n }\n return dir;\n }\n\n const entries = readdirSync(tasksRoot)\n .filter((name) => isSafeSlug(name))\n .map((name) => {\n const dir = join(tasksRoot, name);\n try {\n const st = statSync(dir);\n if (!st.isDirectory()) return null;\n if (!isPathInside(tasksRoot, dir)) return null;\n return { name, dir, mtime: st.mtimeMs };\n } catch {\n return null;\n }\n })\n .filter((e): e is { name: string; dir: string; mtime: number } => !!e)\n .sort((a, b) => b.mtime - a.mtime);\n\n if (options.runId) {\n for (const entry of entries) {\n const runIdPath = join(entry.dir, '.run-id');\n if (!existsSync(runIdPath)) continue;\n const saved = readFileSync(runIdPath, 'utf8').trim();\n if (saved === options.runId && existsSync(join(entry.dir, 'acceptance.md'))) {\n return entry.dir;\n }\n }\n }\n\n for (const entry of entries) {\n if (existsSync(join(entry.dir, 'acceptance.md'))) {\n return entry.dir;\n }\n }\n\n return null;\n}\n\nexport function loadAcceptanceFromTask(options: {\n cwd: string;\n taskId?: string;\n runId?: string;\n}): {\n taskDir: string | null;\n acceptance: AcceptanceRow[];\n requirements: RequirementRow[];\n codeChangeSummary: string | null;\n} {\n const taskDir = findTaskDir(options);\n if (!taskDir) {\n return {\n taskDir: null,\n acceptance: [],\n requirements: [],\n codeChangeSummary: null,\n };\n }\n\n const acceptancePath = join(taskDir, 'acceptance.md');\n const acceptance = existsSync(acceptancePath)\n ? parseAcceptanceMarkdown(readFileSync(acceptancePath, 'utf8'))\n : [];\n\n const summaryPath = join(taskDir, 'code-change-summary.md');\n const codeChangeSummary = existsSync(summaryPath)\n ? readFileSync(summaryPath, 'utf8')\n : null;\n\n return {\n taskDir,\n acceptance,\n requirements: deriveRequirements(acceptance),\n codeChangeSummary,\n };\n}\n\nfunction evidenceHintsForRow(row: AcceptanceRow): {\n needsTestPass: boolean;\n needsVisual: boolean;\n needsUnitMention: boolean;\n} {\n const v = `${row.verification ?? ''} ${row.title}`.toLowerCase();\n const needsVisual = /visual|figma|像素|视觉/.test(v);\n const needsUnitMention = /\\bunit\\b|单测|logic\\.test/.test(v);\n const needsTestPass =\n /playwright|e2e|frontend-precision test|typecheck|unit|单测|tsx --test/.test(\n v,\n ) || needsUnitMention;\n return { needsTestPass, needsVisual, needsUnitMention };\n}\n\n/**\n * 对标记为 PASS 的 AC 做弱证据校验,降低「只改 Markdown」冒充通过的概率。\n * 不替代真实 Playwright 绑定;只检查本轮 artifacts 是否具备对应产物。\n */\nexport function evaluateAcceptanceEvidence(\n acceptance: AcceptanceRow[],\n options: {\n tests: TestSuiteResult | null;\n visualPassedNames: string[];\n hasFigmaVisualPass: boolean;\n },\n): { failures: string[]; unverified: string[] } {\n const failures: string[] = [];\n const unverified: string[] = [];\n const testsPassed = Boolean(options.tests?.passed);\n const testBlob = (options.tests?.results ?? [])\n .map((r) => `${r.name} ${r.command} ${r.stdout} ${r.stderr}`)\n .join('\\n')\n .toLowerCase();\n\n for (const row of acceptance) {\n if (row.status !== 'PASS') continue;\n\n const hints = evidenceHintsForRow(row);\n const acToken = row.id.toLowerCase();\n\n if (hints.needsTestPass) {\n if (!options.tests) {\n failures.push(\n `acceptance:${row.id}: PASS without test evidence (tests_not_run)`,\n );\n continue;\n }\n if (!testsPassed) {\n failures.push(\n `acceptance:${row.id}: PASS but testCommand failed`,\n );\n continue;\n }\n // 若验证列点名 AC id / 单测文件,尽量在 stdout 或 command 中看到痕迹\n if (\n hints.needsUnitMention &&\n !testBlob.includes(acToken) &&\n !/\\.test\\.ts|pass/.test(testBlob)\n ) {\n unverified.push(`acceptance_evidence_weak:${row.id}`);\n }\n }\n\n if (hints.needsVisual) {\n if (options.visualPassedNames.length === 0) {\n failures.push(\n `acceptance:${row.id}: PASS without visual evidence`,\n );\n } else if (!options.hasFigmaVisualPass) {\n // regression 通过不算 Figma 验收证据\n failures.push(\n `acceptance:${row.id}: PASS claims visual/Figma but no referenceKind=figma pass`,\n );\n }\n }\n }\n\n return { failures, unverified };\n}\n\nexport function evaluateAcceptanceGate(\n acceptance: AcceptanceRow[],\n options: {\n requireAcceptance: boolean;\n requireAcceptanceAllPass: boolean;\n },\n): { failures: string[]; unverified: string[] } {\n const failures: string[] = [];\n const unverified: string[] = [];\n\n if (!options.requireAcceptance) {\n return { failures, unverified };\n }\n\n if (acceptance.length === 0) {\n failures.push(\n 'acceptance_missing: no AC rows found (pass --task-id or ensure .frontend-precision/tasks/*/acceptance.md)',\n );\n return { failures, unverified };\n }\n\n const pending = acceptance.filter((a) => a.status === 'PENDING');\n const failed = acceptance.filter((a) => a.status === 'FAIL');\n const partial = acceptance.filter((a) => a.status === 'PARTIAL');\n\n for (const row of failed) {\n failures.push(`acceptance:${row.id}: FAIL — ${row.title}`);\n }\n\n if (options.requireAcceptanceAllPass) {\n for (const row of pending) {\n failures.push(`acceptance:${row.id}: PENDING — ${row.title}`);\n }\n for (const row of partial) {\n failures.push(\n `acceptance:${row.id}: PARTIAL — ${row.title} (requireAcceptanceAllPass)`,\n );\n }\n } else {\n for (const row of pending) {\n unverified.push(`acceptance_pending:${row.id}`);\n }\n for (const row of partial) {\n unverified.push(`acceptance_partial:${row.id}`);\n }\n }\n\n return { failures, unverified };\n}\n"],"mappings":"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","names":["existsSync","dirname","join","existsSync","dirname","join","existsSync","resolve","join","readFileSync","existsSync","existsSync","readFileSync","join","resolve","existsSync","writeFileSync","join","writeFileSync","join","mkdirSync","join","resolve","resolve","join","mkdirSync","join","writeFileSync","writeFileSync","join","resolve","existsSync","existsSync","readFileSync","writeFileSync","mkdirSync","dirname","join","result","resolve","existsSync","writeFileSync","join","resolve","writeFileSync","join","writeFileSync","join","existsSync","readFileSync","join","existsSync","readFileSync","readdirSync","writeFileSync","join","existsSync","readFileSync","join","resolve","sep","resolve","sep","join","existsSync","readFileSync","existsSync","readFileSync","join","readdirSync","status","writeFileSync","existsSync","join","readFileSync"]}
package/package.json CHANGED
@@ -1,9 +1,26 @@
1
1
  {
2
2
  "name": "frontend-precision",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Cursor-first frontend precision toolkit: screenshot, visual diff, test runner, and reports",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/spike-soc/frontend-precision.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "homepage": "https://github.com/spike-soc/frontend-precision#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/spike-soc/frontend-precision/issues"
15
+ },
16
+ "keywords": [
17
+ "cursor",
18
+ "playwright",
19
+ "visual-diff",
20
+ "frontend",
21
+ "cli",
22
+ "screenshot"
23
+ ],
7
24
  "publishConfig": {
8
25
  "access": "public"
9
26
  },