openspec-playwright 0.3.8 → 0.3.11

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.
@@ -449,6 +449,65 @@ After writing `app-exploration.md`, extract **project-level shared knowledge** a
449
449
 
450
450
  Append only new/changed items — preserve existing content.
451
451
 
452
+ ### 4.5. Vision Check (Optional, VLM-powered)
453
+
454
+ If Ollama with a vision model is available, analyze screenshots for layout anomalies before proceeding to test generation. This catches UI issues early — before tests fail.
455
+
456
+ **Prerequisite**: Run `openspec-pw doctor` to check Vision Check availability:
457
+ ```
458
+ ─── Vision Check ───
459
+ ✓ ollama: http://localhost:11434 (qwen2.5-vl)
460
+ ```
461
+
462
+ If Vision Check shows `⚠ disabled` or `⚠ not reachable` → skip this step entirely. Vision Check is optional and should not block exploration.
463
+
464
+ **Configuration** (in order of priority):
465
+ 1. Environment variables: `OLLAMA_URL`, `OLLAMA_VISION_MODEL`
466
+ 2. `tests/playwright/app-knowledge.md` → **Vision Check Config** section
467
+
468
+ If no configuration is found, vision check is disabled.
469
+
470
+ **Run vision check** after exploration is complete:
471
+
472
+ ```bash
473
+ openspec-pw vision-check --screenshots "__screenshots__/*.png"
474
+ ```
475
+
476
+ **Flags**:
477
+ - `--screenshots <glob>` — Screenshot paths (required)
478
+ - `--parallel <n>` — Concurrent Ollama requests (default: 4)
479
+ - `--severity <levels>` — Filter by severity: `blocking,warning,minor`
480
+ - `--output <path>` — Write JSON results to file
481
+ - `--dry-run` — List screenshots without analyzing
482
+ - `--json` — Output JSON format
483
+
484
+ **Exit codes**:
485
+ - `0` = Check completed (with or without anomalies)
486
+ - `1` = Ollama not available → skip vision check, continue workflow
487
+ - `2` = Configuration disabled → skip vision check, continue workflow
488
+
489
+ **Anomaly types detected**:
490
+
491
+ | Type | Description | Example |
492
+ | --- | --- | --- |
493
+ | `obscured` | Interactive element covered by another element | Submit button hidden behind modal |
494
+ | `crowded` | Elements too close together, hard to distinguish | Form fields with <8px spacing |
495
+ | `overflowed` | Content clipped by container | Truncated text without ellipsis |
496
+
497
+ **Output handling**:
498
+
499
+ 1. **JSON output** (with `--json`): Parse anomalies array for programmatic use
500
+ 2. **Auto-append**: Without `--json`, anomalies are appended to `app-exploration.md` → **Visual Anomalies** table
501
+ 3. **Decision table**:
502
+
503
+ | Anomaly Severity | Action |
504
+ | --- | --- |
505
+ | `blocking` | Flag route with `⚠️ Layout`, consider skipping test generation until fixed |
506
+ | `warning` | Log to Visual Anomalies table, continue test generation |
507
+ | `minor` | Log for reference, no action required |
508
+
509
+ **Graceful degradation**: If Ollama is unavailable or times out → log warning, skip vision check, continue to Step 5. Vision check should never block the E2E workflow.
510
+
452
511
  #### 4.7. After exploration
453
512
 
454
513
  Pass `app-exploration.md` to:
package/README.md CHANGED
@@ -35,9 +35,11 @@ Claude Code — E2E workflow is driven by SKILL.md using /browse (exploration) +
35
35
  ```bash
36
36
  openspec-pw init # Initialize integration (one-time setup)
37
37
  openspec-pw update # Update CLI and commands to latest version
38
- openspec-pw doctor # Check prerequisites
38
+ openspec-pw doctor # Check prerequisites (including Vision Check)
39
39
  openspec-pw audit # Audit tests for orphaned specs and issues
40
40
  openspec-pw migrate # Migrate old test files to new structure
41
+ openspec-pw explore # Explore routes in parallel with Playwright
42
+ openspec-pw vision-check # Analyze screenshots for layout anomalies (Ollama VLM)
41
43
  openspec-pw uninstall # Remove integration from the project
42
44
  ```
43
45
 
package/README.zh-CN.md CHANGED
@@ -45,9 +45,11 @@ Claude Code — E2E 工作流由 SKILL.md 驱动,使用 /browse(探索)+ P
45
45
  ```bash
46
46
  openspec-pw init # 初始化集成(一次性设置)
47
47
  openspec-pw update # 更新 CLI 和命令到最新版本
48
- openspec-pw doctor # 检查前置条件
48
+ openspec-pw doctor # 检查前置条件(含 Vision Check)
49
49
  openspec-pw audit # 检查测试文件是否有孤儿文件和配置问题
50
50
  openspec-pw migrate # 迁移旧测试文件到新目录结构
51
+ openspec-pw explore # 并行探索路由
52
+ openspec-pw vision-check # 使用 Ollama VLM 检测布局问题
51
53
  openspec-pw uninstall # 移除项目中的集成
52
54
  ```
53
55
 
@@ -2,6 +2,7 @@ import { existsSync } from "fs";
2
2
  import { join } from "path";
3
3
  import { execSync } from "child_process";
4
4
  import chalk from "chalk";
5
+ import { loadOllamaConfig, checkOllamaHealth } from "../utils/ollama.js";
5
6
  export async function doctor(options = {}) {
6
7
  const checks = [];
7
8
  const projectRoot = process.cwd();
@@ -105,7 +106,22 @@ export async function doctor(options = {}) {
105
106
  ok: hasSeed,
106
107
  message: hasSeed ? "found" : "not found (optional)",
107
108
  });
108
- const allOk = checks.filter((c) => !c.ok && c.category !== "Seed Test").length === 0;
109
+ // Vision Check (Ollama)
110
+ const ollamaConfig = loadOllamaConfig(projectRoot);
111
+ let visionOk = false;
112
+ let visionMessage = "disabled";
113
+ if (ollamaConfig.enabled) {
114
+ const health = await checkOllamaHealth(ollamaConfig);
115
+ visionOk = health.ok;
116
+ visionMessage = health.message;
117
+ }
118
+ checks.push({
119
+ category: "Vision Check",
120
+ name: "ollama",
121
+ ok: visionOk,
122
+ message: visionMessage,
123
+ });
124
+ const allOk = checks.filter((c) => !c.ok && c.category !== "Seed Test" && c.category !== "Vision Check").length === 0;
109
125
  if (options.json) {
110
126
  console.log(JSON.stringify({ ok: allOk, checks }, null, 2));
111
127
  if (!allOk)
@@ -123,7 +139,7 @@ export async function doctor(options = {}) {
123
139
  if (check.ok) {
124
140
  console.log(chalk.green(` ✓ ${check.name}: ${check.message}`));
125
141
  }
126
- else if (check.category === "Seed Test") {
142
+ else if (check.category === "Seed Test" || check.category === "Vision Check") {
127
143
  console.log(chalk.yellow(` ⚠ ${check.name}: ${check.message}`));
128
144
  }
129
145
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Vision check command: Analyze screenshots for layout anomalies using Ollama VLM.
3
+ *
4
+ * Usage:
5
+ * openspec-pw vision-check --screenshots "__screenshots__/*.png"
6
+ * openspec-pw vision-check --screenshots "shot1.png,shot2.png" --parallel 2
7
+ *
8
+ * Exit codes:
9
+ * 0 = Check completed (with or without anomalies)
10
+ * 1 = Ollama not available (skipped)
11
+ * 2 = Configuration missing or invalid (skipped)
12
+ */
13
+ export interface VisionCheckOptions {
14
+ screenshots: string;
15
+ config?: string;
16
+ parallel?: number;
17
+ output?: string;
18
+ dryRun?: boolean;
19
+ severity?: string;
20
+ json?: boolean;
21
+ }
22
+ /**
23
+ * Main vision check command.
24
+ */
25
+ export declare function visionCheck(options: VisionCheckOptions): Promise<void>;
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Vision check command: Analyze screenshots for layout anomalies using Ollama VLM.
3
+ *
4
+ * Usage:
5
+ * openspec-pw vision-check --screenshots "__screenshots__/*.png"
6
+ * openspec-pw vision-check --screenshots "shot1.png,shot2.png" --parallel 2
7
+ *
8
+ * Exit codes:
9
+ * 0 = Check completed (with or without anomalies)
10
+ * 1 = Ollama not available (skipped)
11
+ * 2 = Configuration missing or invalid (skipped)
12
+ */
13
+ import { existsSync, readFileSync, writeFileSync } from "fs";
14
+ import { join, basename } from "path";
15
+ import { glob } from "glob";
16
+ import chalk from "chalk";
17
+ import { loadOllamaConfig, checkOllamaHealth, batchAnalyzeScreenshots, } from "../utils/ollama.js";
18
+ /**
19
+ * Extract route name from screenshot path.
20
+ * e.g., "__screenshots__/dashboard.png" → "/dashboard"
21
+ * "__screenshots__/settings-profile.png" → "/settings/profile"
22
+ */
23
+ function extractRouteFromPath(screenshotPath) {
24
+ const name = basename(screenshotPath, ".png");
25
+ // Convert dashes back to slashes, handle root
26
+ if (name === "index" || name === "home" || name === "root") {
27
+ return "/";
28
+ }
29
+ return "/" + name.replace(/-/g, "/");
30
+ }
31
+ /**
32
+ * Resolve screenshot paths from glob or comma-separated list.
33
+ */
34
+ async function resolveScreenshots(pattern) {
35
+ // Check if comma-separated list
36
+ if (pattern.includes(",") && !pattern.includes("*")) {
37
+ return pattern.split(",").map((p) => ({
38
+ path: p.trim(),
39
+ route: extractRouteFromPath(p.trim()),
40
+ }));
41
+ }
42
+ // Glob pattern
43
+ const paths = await glob(pattern, { absolute: true });
44
+ return paths.map((p) => ({
45
+ path: p,
46
+ route: extractRouteFromPath(p),
47
+ }));
48
+ }
49
+ /**
50
+ * Filter anomalies by severity level.
51
+ */
52
+ function filterBySeverity(anomalies, severityFilter) {
53
+ const levels = severityFilter.toLowerCase().split(",").map((s) => s.trim());
54
+ return anomalies.filter((a) => levels.includes(a.severity));
55
+ }
56
+ /**
57
+ * Append anomalies to app-exploration.md Visual Anomalies section.
58
+ */
59
+ function appendToExploration(projectRoot, anomalies) {
60
+ const explorationPath = join(projectRoot, "tests", "playwright", "app-exploration.md");
61
+ if (!existsSync(explorationPath)) {
62
+ console.error(chalk.yellow(`Warning: ${explorationPath} not found, skipping write`));
63
+ return;
64
+ }
65
+ let content = readFileSync(explorationPath, "utf-8");
66
+ // Find or create Visual Anomalies section
67
+ const sectionHeader = "## Visual Anomalies";
68
+ const tableHeader = `| Route | Element | Type | Position | Severity | Description |
69
+ | --- | --- | --- | --- | --- | --- |`;
70
+ if (!content.includes(sectionHeader)) {
71
+ // Add section before "## Next Steps" or at the end
72
+ const nextStepsIndex = content.indexOf("## Next Steps");
73
+ if (nextStepsIndex !== -1) {
74
+ content =
75
+ content.slice(0, nextStepsIndex) +
76
+ `${sectionHeader}\n\n${tableHeader}\n\n` +
77
+ content.slice(nextStepsIndex);
78
+ }
79
+ else {
80
+ content += `\n\n${sectionHeader}\n\n${tableHeader}\n`;
81
+ }
82
+ }
83
+ // Append new anomalies (de-duplicate by route + element + type)
84
+ const existingRows = content.match(/\| [^|]+ \| [^|]+ \| [^|]+ \| [^|]+ \| [^|]+ \| [^|]+ \|/g) || [];
85
+ const existingKeys = new Set(existingRows.map((row) => {
86
+ const cells = row.split("|").map((c) => c.trim()).filter(Boolean);
87
+ return `${cells[0]}:${cells[1]}:${cells[2]}`;
88
+ }));
89
+ const newRows = [];
90
+ for (const a of anomalies) {
91
+ const key = `${a.route}:${a.element}:${a.type}`;
92
+ if (!existingKeys.has(key)) {
93
+ newRows.push(`| ${a.route} | ${a.element} | ${a.type} | ${a.position} | ${a.severity} | ${a.description} |`);
94
+ existingKeys.add(key);
95
+ }
96
+ }
97
+ if (newRows.length > 0) {
98
+ // Insert after table header
99
+ const headerIndex = content.indexOf(tableHeader);
100
+ if (headerIndex !== -1) {
101
+ const insertPoint = headerIndex + tableHeader.length;
102
+ content =
103
+ content.slice(0, insertPoint) +
104
+ "\n" +
105
+ newRows.join("\n") +
106
+ content.slice(insertPoint);
107
+ }
108
+ writeFileSync(explorationPath, content, "utf-8");
109
+ console.log(chalk.green(` ✓ Appended ${newRows.length} anomalies to app-exploration.md`));
110
+ }
111
+ }
112
+ /**
113
+ * Main vision check command.
114
+ */
115
+ export async function visionCheck(options) {
116
+ const projectRoot = process.cwd();
117
+ // 1. Load configuration
118
+ const config = loadOllamaConfig(projectRoot);
119
+ if (!config.enabled) {
120
+ const result = {
121
+ processed: 0,
122
+ anomalies: [],
123
+ skipped: [],
124
+ skipReason: "disabled",
125
+ ollamaUrl: config.url,
126
+ model: config.model,
127
+ };
128
+ if (options.json) {
129
+ console.log(JSON.stringify(result, null, 2));
130
+ }
131
+ else {
132
+ console.log(chalk.yellow("Vision check disabled in config"));
133
+ }
134
+ process.exit(2);
135
+ }
136
+ // 2. Dry run: list files only
137
+ if (options.dryRun) {
138
+ const screenshots = await resolveScreenshots(options.screenshots);
139
+ console.log(chalk.blue("\n📷 Screenshots to analyze (dry run):\n"));
140
+ for (const s of screenshots) {
141
+ console.log(` ${s.route} → ${s.path}`);
142
+ }
143
+ console.log(chalk.gray(`\nTotal: ${screenshots.length} files`));
144
+ console.log(chalk.gray(`Ollama: ${config.url} (${config.model})`));
145
+ return;
146
+ }
147
+ // 3. Health check
148
+ const health = await checkOllamaHealth(config);
149
+ if (!health.ok) {
150
+ const result = {
151
+ processed: 0,
152
+ anomalies: [],
153
+ skipped: [],
154
+ skipReason: health.message,
155
+ ollamaUrl: config.url,
156
+ model: config.model,
157
+ };
158
+ if (options.json) {
159
+ console.log(JSON.stringify(result, null, 2));
160
+ }
161
+ else {
162
+ console.log(chalk.yellow(`Vision check skipped: ${health.message}`));
163
+ }
164
+ process.exit(1);
165
+ }
166
+ // 4. Resolve screenshots
167
+ const screenshots = await resolveScreenshots(options.screenshots);
168
+ if (screenshots.length === 0) {
169
+ console.log(chalk.yellow("No screenshots found matching pattern"));
170
+ process.exit(0);
171
+ }
172
+ if (!options.json) {
173
+ console.log(chalk.blue(`\n🔍 Analyzing ${screenshots.length} screenshots...\n`));
174
+ }
175
+ // 5. Batch analyze
176
+ const concurrency = options.parallel || 4;
177
+ const result = await batchAnalyzeScreenshots(config, screenshots, concurrency);
178
+ // 6. Filter by severity if specified
179
+ if (options.severity) {
180
+ result.anomalies = filterBySeverity(result.anomalies, options.severity);
181
+ }
182
+ // 7. Output results
183
+ if (options.json) {
184
+ console.log(JSON.stringify(result, null, 2));
185
+ }
186
+ else {
187
+ // Text output
188
+ console.log(chalk.blue("─── Vision Check Results ───\n"));
189
+ console.log(` Processed: ${result.processed}/${screenshots.length}`);
190
+ console.log(` Anomalies: ${result.anomalies.length}`);
191
+ if (result.skipped.length > 0) {
192
+ console.log(chalk.yellow(` Skipped: ${result.skipped.length}`));
193
+ }
194
+ if (result.anomalies.length > 0) {
195
+ console.log(chalk.blue("\n─── Anomalies ───\n"));
196
+ for (const a of result.anomalies) {
197
+ const severityColor = a.severity === "blocking"
198
+ ? chalk.red
199
+ : a.severity === "warning"
200
+ ? chalk.yellow
201
+ : chalk.gray;
202
+ console.log(` ${severityColor(`[${a.severity}]`)} ${a.route}`);
203
+ console.log(` Element: ${a.element}`);
204
+ console.log(` Type: ${a.type} (${a.position})`);
205
+ console.log(` ${a.description}\n`);
206
+ }
207
+ }
208
+ else {
209
+ console.log(chalk.green("\n ✓ No layout anomalies detected\n"));
210
+ }
211
+ }
212
+ // 8. Append to app-exploration.md if anomalies found
213
+ if (result.anomalies.length > 0 && !options.json) {
214
+ appendToExploration(projectRoot, result.anomalies);
215
+ }
216
+ // 9. Write to output file if specified
217
+ if (options.output) {
218
+ writeFileSync(options.output, JSON.stringify(result, null, 2), "utf-8");
219
+ if (!options.json) {
220
+ console.log(chalk.green(` ✓ Results written to ${options.output}`));
221
+ }
222
+ }
223
+ }
224
+ //# sourceMappingURL=visionCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visionCheck.js","sourceRoot":"","sources":["../../src/commands/visionCheck.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { migrate } from "./commands/migrate.js";
10
10
  import { uninstall } from "./commands/uninstall.js";
11
11
  import { audit } from "./commands/audit.js";
12
12
  import { explore } from "./commands/explore.js";
13
+ import { visionCheck } from "./commands/visionCheck.js";
13
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
15
  const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
15
16
  const program = new Command();
@@ -74,5 +75,18 @@ program
74
75
  .action(async (opts) => {
75
76
  await explore(opts);
76
77
  });
78
+ program
79
+ .command("vision-check")
80
+ .description("Analyze screenshots for layout anomalies using Ollama VLM")
81
+ .requiredOption("-s, --screenshots <pattern>", "Screenshot path(s): glob pattern or comma-separated list")
82
+ .option("-c, --config <path>", "Config file path (default: app-knowledge.md)")
83
+ .option("-p, --parallel <n>", "Number of parallel requests to Ollama", (v) => parseInt(v, 10), 4)
84
+ .option("-o, --output <path>", "Write JSON results to file")
85
+ .option("--severity <levels>", "Filter by severity: blocking,warning,minor")
86
+ .option("-n, --dry-run", "List screenshots without analyzing")
87
+ .option("--json", "Output results as JSON")
88
+ .action(async (opts) => {
89
+ await visionCheck(opts);
90
+ });
77
91
  program.parse();
78
92
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Ollama API utilities for vision model integration.
3
+ *
4
+ * Configuration priority (highest to lowest):
5
+ * 1. Function parameters
6
+ * 2. Environment variables (OLLAMA_URL, OLLAMA_VISION_MODEL)
7
+ * 3. app-knowledge.md Vision Check Config section
8
+ *
9
+ * If no configuration is found, vision check is disabled.
10
+ */
11
+ export interface OllamaConfig {
12
+ url: string;
13
+ model: string;
14
+ enabled: boolean;
15
+ }
16
+ export interface VisionAnomaly {
17
+ route: string;
18
+ screenshot: string;
19
+ element: string;
20
+ type: "obscured" | "crowded" | "overflowed";
21
+ position: string;
22
+ severity: "blocking" | "warning" | "minor";
23
+ description: string;
24
+ }
25
+ export interface VisionCheckResult {
26
+ processed: number;
27
+ anomalies: VisionAnomaly[];
28
+ skipped: string[];
29
+ skipReason?: string;
30
+ ollamaUrl: string;
31
+ model: string;
32
+ }
33
+ export interface OllamaGenerateResponse {
34
+ response: string;
35
+ done: boolean;
36
+ }
37
+ /**
38
+ * Load Ollama config with priority:
39
+ * 1. Environment variables (OLLAMA_URL, OLLAMA_VISION_MODEL)
40
+ * 2. app-knowledge.md Vision Check Config section
41
+ *
42
+ * If no configuration is found, returns enabled=false.
43
+ */
44
+ export declare function loadOllamaConfig(projectRoot?: string): OllamaConfig;
45
+ /**
46
+ * Check if Ollama is running and the vision model is available.
47
+ * Returns { ok, message } for doctor output.
48
+ */
49
+ export declare function checkOllamaHealth(config: OllamaConfig): Promise<{
50
+ ok: boolean;
51
+ message: string;
52
+ }>;
53
+ /**
54
+ * Analyze a single screenshot with the vision model.
55
+ */
56
+ export declare function analyzeScreenshot(config: OllamaConfig, screenshotPath: string, route: string): Promise<VisionAnomaly[]>;
57
+ /**
58
+ * Batch analyze multiple screenshots with concurrency control.
59
+ */
60
+ export declare function batchAnalyzeScreenshots(config: OllamaConfig, screenshots: Array<{
61
+ path: string;
62
+ route: string;
63
+ }>, concurrency?: number): Promise<VisionCheckResult>;
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Ollama API utilities for vision model integration.
3
+ *
4
+ * Configuration priority (highest to lowest):
5
+ * 1. Function parameters
6
+ * 2. Environment variables (OLLAMA_URL, OLLAMA_VISION_MODEL)
7
+ * 3. app-knowledge.md Vision Check Config section
8
+ *
9
+ * If no configuration is found, vision check is disabled.
10
+ */
11
+ import { existsSync, readFileSync } from "fs";
12
+ import { join } from "path";
13
+ // ── Config loading ────────────────────────────────────────────────────────────
14
+ /**
15
+ * Load Ollama config from app-knowledge.md.
16
+ * Parses the "## Vision Check Config" section.
17
+ */
18
+ function loadFromAppKnowledge(projectRoot) {
19
+ const appKnowledgePath = join(projectRoot, "tests", "playwright", "app-knowledge.md");
20
+ if (!existsSync(appKnowledgePath))
21
+ return {};
22
+ try {
23
+ const content = readFileSync(appKnowledgePath, "utf-8");
24
+ const match = content.match(/## Vision Check Config\s*\n([\s\S]*?)(?=\n##|\n---|\z)/);
25
+ if (!match)
26
+ return {};
27
+ const section = match[1];
28
+ const config = {};
29
+ const urlMatch = section.match(/ollama_url:\s*(.+)/);
30
+ if (urlMatch)
31
+ config.url = urlMatch[1].trim();
32
+ const modelMatch = section.match(/vision_model:\s*(.+)/);
33
+ if (modelMatch)
34
+ config.model = modelMatch[1].trim();
35
+ const enabledMatch = section.match(/vision_enabled:\s*(.+)/);
36
+ if (enabledMatch)
37
+ config.enabled = enabledMatch[1].trim() === "true";
38
+ return config;
39
+ }
40
+ catch {
41
+ return {};
42
+ }
43
+ }
44
+ /**
45
+ * Load Ollama config with priority:
46
+ * 1. Environment variables (OLLAMA_URL, OLLAMA_VISION_MODEL)
47
+ * 2. app-knowledge.md Vision Check Config section
48
+ *
49
+ * If no configuration is found, returns enabled=false.
50
+ */
51
+ export function loadOllamaConfig(projectRoot = process.cwd()) {
52
+ const appKnowledge = loadFromAppKnowledge(projectRoot);
53
+ // Environment variables override app-knowledge.md
54
+ const url = process.env.OLLAMA_URL || appKnowledge.url;
55
+ const model = process.env.OLLAMA_VISION_MODEL || appKnowledge.model;
56
+ // If no url or model configured, disable vision check
57
+ if (!url || !model) {
58
+ return { url: "", model: "", enabled: false };
59
+ }
60
+ // Enabled: default true unless explicitly disabled
61
+ let enabled = true;
62
+ if (process.env.OLLAMA_VISION_ENABLED !== undefined) {
63
+ enabled = process.env.OLLAMA_VISION_ENABLED === "true";
64
+ }
65
+ else if (appKnowledge.enabled !== undefined) {
66
+ enabled = appKnowledge.enabled;
67
+ }
68
+ return { url, model, enabled };
69
+ }
70
+ // ── Health check ──────────────────────────────────────────────────────────────
71
+ /**
72
+ * Check if Ollama is running and the vision model is available.
73
+ * Returns { ok, message } for doctor output.
74
+ */
75
+ export async function checkOllamaHealth(config) {
76
+ if (!config.enabled) {
77
+ return { ok: false, message: "disabled in config" };
78
+ }
79
+ try {
80
+ // Check Ollama server
81
+ const controller = new AbortController();
82
+ const timeout = setTimeout(() => controller.abort(), 5000);
83
+ const response = await fetch(`${config.url}/api/tags`, {
84
+ signal: controller.signal,
85
+ });
86
+ clearTimeout(timeout);
87
+ if (!response.ok) {
88
+ return { ok: false, message: `server error: ${response.status}` };
89
+ }
90
+ const data = (await response.json());
91
+ const models = data.models || [];
92
+ const modelNames = models.map((m) => m.name);
93
+ // Check if vision model exists (exact match or prefix match)
94
+ const hasModel = modelNames.some((name) => name === config.model ||
95
+ name.startsWith(`${config.model}:`) ||
96
+ name === `${config.model}:latest`);
97
+ if (!hasModel) {
98
+ return {
99
+ ok: false,
100
+ message: `model "${config.model}" not found (available: ${modelNames.slice(0, 3).join(", ")}...)`,
101
+ };
102
+ }
103
+ return { ok: true, message: `${config.url} (${config.model})` };
104
+ }
105
+ catch (err) {
106
+ const error = err;
107
+ if (error.name === "AbortError") {
108
+ return { ok: false, message: "connection timeout" };
109
+ }
110
+ return { ok: false, message: `not reachable: ${error.message}` };
111
+ }
112
+ }
113
+ // ── Vision analysis ───────────────────────────────────────────────────────────
114
+ const VISION_PROMPT = `你是一个专业的网页UI测试员。请分析这张截图,检测以下问题:
115
+
116
+ 1. 遮挡:可交互元素(按钮、表单、链接)是否被其他元素遮挡、覆盖或压住,导致无法正常点击?
117
+ 2. 拥挤:多个元素是否间距过窄、文字重叠、边界模糊,导致难以区分或误操作?
118
+ 3. 溢出:内容是否被容器裁剪,导致关键信息不可见?
119
+
120
+ 对于每个问题,请返回:
121
+ - element: 元素名称/描述
122
+ - type: 问题类型(obscured / crowded / overflowed)
123
+ - position: 位置(用屏幕区域描述:左上/右上/中/左下/右下)
124
+ - severity: 严重程度(blocking / warning / minor)
125
+ - description: 问题描述
126
+
127
+ 如果一切正常,返回空的 anomalies 数组。
128
+
129
+ 请严格以JSON格式回答,不要包含任何其他文字:
130
+ {
131
+ "anomalies": []
132
+ }`;
133
+ /**
134
+ * Analyze a single screenshot with the vision model.
135
+ */
136
+ export async function analyzeScreenshot(config, screenshotPath, route) {
137
+ if (!existsSync(screenshotPath)) {
138
+ throw new Error(`Screenshot not found: ${screenshotPath}`);
139
+ }
140
+ const imageBase64 = readFileSync(screenshotPath).toString("base64");
141
+ const response = await fetch(`${config.url}/api/generate`, {
142
+ method: "POST",
143
+ headers: { "Content-Type": "application/json" },
144
+ body: JSON.stringify({
145
+ model: config.model,
146
+ prompt: VISION_PROMPT,
147
+ images: [imageBase64],
148
+ stream: false,
149
+ }),
150
+ });
151
+ if (!response.ok) {
152
+ throw new Error(`Ollama API error: ${response.status}`);
153
+ }
154
+ const data = (await response.json());
155
+ const responseText = data.response || "";
156
+ // Parse JSON from response (may have markdown code block)
157
+ let jsonStr = responseText;
158
+ const jsonMatch = responseText.match(/```(?:json)?\s*([\s\S]*?)```/);
159
+ if (jsonMatch) {
160
+ jsonStr = jsonMatch[1];
161
+ }
162
+ try {
163
+ const parsed = JSON.parse(jsonStr.trim());
164
+ const anomalies = parsed.anomalies || [];
165
+ // Enrich with route and screenshot path
166
+ return anomalies.map((a) => ({
167
+ ...a,
168
+ route,
169
+ screenshot: screenshotPath,
170
+ }));
171
+ }
172
+ catch {
173
+ // If JSON parse fails, return empty (graceful degradation)
174
+ console.error(`Warning: Could not parse vision response for ${route}`);
175
+ return [];
176
+ }
177
+ }
178
+ /**
179
+ * Batch analyze multiple screenshots with concurrency control.
180
+ */
181
+ export async function batchAnalyzeScreenshots(config, screenshots, concurrency = 4) {
182
+ const result = {
183
+ processed: 0,
184
+ anomalies: [],
185
+ skipped: [],
186
+ ollamaUrl: config.url,
187
+ model: config.model,
188
+ };
189
+ // Process in batches
190
+ for (let i = 0; i < screenshots.length; i += concurrency) {
191
+ const batch = screenshots.slice(i, i + concurrency);
192
+ const promises = batch.map(async ({ path, route }) => {
193
+ try {
194
+ const anomalies = await analyzeScreenshot(config, path, route);
195
+ result.processed++;
196
+ result.anomalies.push(...anomalies);
197
+ }
198
+ catch (err) {
199
+ const error = err;
200
+ console.error(`Warning: Skipped ${route}: ${error.message}`);
201
+ result.skipped.push(path);
202
+ }
203
+ });
204
+ await Promise.allSettled(promises);
205
+ }
206
+ return result;
207
+ }
208
+ //# sourceMappingURL=ollama.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/utils/ollama.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "openspec-playwright",
3
- "version": "0.3.8",
3
+ "version": "0.3.11",
4
4
  "description": "OpenSpec + Playwright E2E verification setup tool for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
7
- "openspec-pw": "./bin/openspec-pw.js"
7
+ "openspec-pw": "./bin/openspec-pw"
8
8
  },
9
9
  "scripts": {
10
10
  "build": "tsc",
@@ -19,9 +19,11 @@
19
19
  "release": "npm version patch && node scripts/bump-docs.js && npm run build && git add docs/index.html && git push && git push --tags && npm publish"
20
20
  },
21
21
  "dependencies": {
22
+ "@types/glob": "^8.1.0",
22
23
  "@types/tar": "^6.1.13",
23
24
  "chalk": "^5.3.0",
24
25
  "commander": "^12.1.0",
26
+ "glob": "^13.0.6",
25
27
  "playwright": "^1.50.0",
26
28
  "tar": "^7.5.13"
27
29
  },
@@ -58,6 +58,18 @@ BASE_URL: <from env or seed.spec.ts>
58
58
  |-------|-------|-------|
59
59
  | | | |
60
60
 
61
+ ## Visual Anomalies
62
+
63
+ Auto-populated by `openspec-pw vision-check`. Layout issues detected via Ollama VLM.
64
+
65
+ | Route | Element | Type | Position | Severity | Description |
66
+ | --- | --- | --- | --- | --- | --- |
67
+ | | | | | | |
68
+
69
+ > **Type legend**: `obscured` (element covered) | `crowded` (spacing too narrow) | `overflowed` (content clipped)
70
+ >
71
+ > **Severity**: `blocking` (cannot interact) | `warning` (usability issue) | `minor` (cosmetic)
72
+
61
73
  ## Next Steps
62
74
 
63
75
  After exploration, pass this file to Step 5 (Planner) and Step 6 (Generator).
@@ -101,6 +101,22 @@ Persists assertion repairs (typos, spec drift) across sessions.
101
101
 
102
102
  ---
103
103
 
104
+ ## Vision Check Config
105
+
106
+ Ollama VLM configuration for layout anomaly detection. See `openspec-pw doctor` for status.
107
+
108
+ ```
109
+ ollama_url:
110
+ vision_model:
111
+ vision_enabled: false
112
+ ```
113
+
114
+ > **Note**: Vision check is disabled by default. Configure `ollama_url` and `vision_model` to enable.
115
+ > Environment variables (`OLLAMA_URL`, `OLLAMA_VISION_MODEL`) override these values.
116
+ > Run `openspec-pw vision-check --dry-run` to verify configuration.
117
+
118
+ ---
119
+
104
120
  ## Changelog
105
121
 
106
122
  | Date | Change | By |
@@ -1,13 +0,0 @@
1
- <claude-mem-context>
2
- # Recent Activity
3
-
4
- <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
-
6
- ### Apr 7, 2026
7
-
8
- | ID | Time | T | Title | Read |
9
- |----|------|---|-------|------|
10
- | #5756 | 11:10 AM | ✅ | BasePage Input Methods Enhanced with Framework Sync | ~226 |
11
- | #5747 | 10:58 AM | ⚖️ | Input Reactivity Fix Strategy | ~265 |
12
- | #5744 | 10:57 AM | 🔵 | BasePage Pattern for Playwright Testing | ~253 |
13
- </claude-mem-context>