openspec-playwright 0.3.46 → 0.3.48
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 +3 -0
- package/dist/commands/audit.js +5 -3
- package/dist/commands/audit.js.map +1 -1
- package/dist/commands/coverage.d.ts +41 -0
- package/dist/commands/coverage.js +368 -0
- package/dist/commands/coverage.js.map +1 -0
- package/dist/commands/flake.d.ts +23 -0
- package/dist/commands/flake.js +311 -0
- package/dist/commands/flake.js.map +1 -0
- package/dist/index.js +32 -9
- package/dist/index.js.map +1 -1
- package/employee-standards.md +7 -0
- package/package.json +1 -1
- package/dist/commands/visionCheck.d.ts +0 -35
- package/dist/commands/visionCheck.js +0 -351
- package/dist/commands/visionCheck.js.map +0 -1
- package/dist/utils/ollama.d.ts +0 -73
- package/dist/utils/ollama.js +0 -619
- package/dist/utils/ollama.js.map +0 -1
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { SHARED_FILE_NAMES } from "../shared/index.js";
|
|
5
|
+
import { collectSpecFiles } from "./coverage.js";
|
|
6
|
+
// ─── Main Entry Point ─────────────────────────────────────────────────
|
|
7
|
+
export async function flake(changeName, options) {
|
|
8
|
+
const projectRoot = process.cwd();
|
|
9
|
+
const testsDir = join(projectRoot, "tests", "playwright");
|
|
10
|
+
if (!existsSync(testsDir)) {
|
|
11
|
+
console.log(chalk.yellow(" tests/playwright/ not found. Run `openspec-pw init` first.\n"));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
console.log(chalk.blue("\n🔍 OpenSpec Playwright: Flake Detection\n"));
|
|
15
|
+
// Scope filtering
|
|
16
|
+
let specFiles;
|
|
17
|
+
if (changeName) {
|
|
18
|
+
const changeTestDir = join(testsDir, "changes", changeName);
|
|
19
|
+
if (!existsSync(changeTestDir)) {
|
|
20
|
+
console.log(chalk.yellow(` No tests found for change "${changeName}".\n`));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
specFiles = collectSpecFiles(changeTestDir);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
specFiles = collectSpecFiles(testsDir);
|
|
27
|
+
}
|
|
28
|
+
// Filter out shared files
|
|
29
|
+
const SHARED_FILES = new Set([
|
|
30
|
+
...SHARED_FILE_NAMES,
|
|
31
|
+
"auth.setup.ts",
|
|
32
|
+
"global.teardown.ts",
|
|
33
|
+
"seed.spec.ts",
|
|
34
|
+
]);
|
|
35
|
+
specFiles = specFiles.filter((f) => {
|
|
36
|
+
const name = f.split("/").pop() ?? "";
|
|
37
|
+
return !SHARED_FILES.has(name);
|
|
38
|
+
});
|
|
39
|
+
if (specFiles.length === 0) {
|
|
40
|
+
console.log(chalk.yellow(" No spec files found to analyze.\n"));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// Parse config
|
|
44
|
+
const configHasStorageState = getConfigStorageState(projectRoot);
|
|
45
|
+
// Run all detectors
|
|
46
|
+
const findings = [];
|
|
47
|
+
for (const file of specFiles) {
|
|
48
|
+
const content = readFileSync(file, "utf-8");
|
|
49
|
+
const relPath = file.replace(testsDir + "/", "");
|
|
50
|
+
findings.push(...detectNetworkIdle(content, relPath));
|
|
51
|
+
findings.push(...detectRouteAfterGoto(content, relPath));
|
|
52
|
+
findings.push(...detectStorageLeakage(content, relPath, configHasStorageState));
|
|
53
|
+
findings.push(...detectTestUseScope(content, relPath, configHasStorageState));
|
|
54
|
+
}
|
|
55
|
+
// Build report
|
|
56
|
+
const report = buildReport(findings);
|
|
57
|
+
// Render
|
|
58
|
+
if (options?.json) {
|
|
59
|
+
console.log(JSON.stringify(report, null, 2));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
renderReport(report);
|
|
63
|
+
}
|
|
64
|
+
// Gate
|
|
65
|
+
if (options?.gate) {
|
|
66
|
+
const exitCode = computeGateExitCode(report, options.gate);
|
|
67
|
+
if (exitCode !== 0)
|
|
68
|
+
process.exit(exitCode);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// ─── Config Parsing ───────────────────────────────────────────────────
|
|
72
|
+
export function getConfigStorageState(projectRoot) {
|
|
73
|
+
for (const name of [
|
|
74
|
+
"playwright.config.ts",
|
|
75
|
+
"playwright.config.js",
|
|
76
|
+
"playwright.config.mjs",
|
|
77
|
+
]) {
|
|
78
|
+
const configPath = join(projectRoot, name);
|
|
79
|
+
if (!existsSync(configPath))
|
|
80
|
+
continue;
|
|
81
|
+
const content = readFileSync(configPath, "utf-8");
|
|
82
|
+
if (/storageState/.test(content) || /storageState\s*:/.test(content)) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
// ─── Pattern Detectors ────────────────────────────────────────────────
|
|
89
|
+
export function detectNetworkIdle(content, filePath) {
|
|
90
|
+
const findings = [];
|
|
91
|
+
const regex = /waitForLoadState\s*\(\s*['"]networkidle['"]/g;
|
|
92
|
+
let match;
|
|
93
|
+
while ((match = regex.exec(content)) !== null) {
|
|
94
|
+
const line = content.substring(0, match.index).split("\n").length;
|
|
95
|
+
findings.push({
|
|
96
|
+
pattern: "networkidle",
|
|
97
|
+
file: filePath,
|
|
98
|
+
line,
|
|
99
|
+
message: "waitForLoadState('networkidle') in SPA — use a specific response wait instead",
|
|
100
|
+
severity: "high",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return findings;
|
|
104
|
+
}
|
|
105
|
+
export function detectRouteAfterGoto(content, filePath) {
|
|
106
|
+
const findings = [];
|
|
107
|
+
// Find all test( positions to isolate test blocks
|
|
108
|
+
const testPositions = [];
|
|
109
|
+
const testRegex = /test\s*\(/g;
|
|
110
|
+
let tm;
|
|
111
|
+
while ((tm = testRegex.exec(content)) !== null) {
|
|
112
|
+
testPositions.push(tm.index);
|
|
113
|
+
}
|
|
114
|
+
for (let i = 0; i < testPositions.length; i++) {
|
|
115
|
+
const testStart = testPositions[i];
|
|
116
|
+
const testEnd = i + 1 < testPositions.length ? testPositions[i + 1] : content.length;
|
|
117
|
+
const block = content.substring(testStart, testEnd);
|
|
118
|
+
// Collect page.goto() line numbers
|
|
119
|
+
const gotoCalls = [];
|
|
120
|
+
const gotoRegex = /page\.goto\s*\(/g;
|
|
121
|
+
let gm;
|
|
122
|
+
while ((gm = gotoRegex.exec(block)) !== null) {
|
|
123
|
+
const line = content.substring(0, testStart + gm.index).split("\n").length;
|
|
124
|
+
gotoCalls.push(line);
|
|
125
|
+
}
|
|
126
|
+
// Collect page.route() line numbers (excluding page.context().route())
|
|
127
|
+
const routeCalls = [];
|
|
128
|
+
const routeRegex = /page\.route\s*\(/g;
|
|
129
|
+
let rm;
|
|
130
|
+
while ((rm = routeRegex.exec(block)) !== null) {
|
|
131
|
+
// Skip if this is page.context().route()
|
|
132
|
+
const before = block.substring(Math.max(0, rm.index - 20), rm.index);
|
|
133
|
+
if (before.includes(".context("))
|
|
134
|
+
continue;
|
|
135
|
+
const line = content.substring(0, testStart + rm.index).split("\n").length;
|
|
136
|
+
routeCalls.push(line);
|
|
137
|
+
}
|
|
138
|
+
// If any route call appears after any goto call → finding
|
|
139
|
+
if (routeCalls.length > 0 && gotoCalls.length > 0) {
|
|
140
|
+
const earliestGoto = Math.min(...gotoCalls);
|
|
141
|
+
for (const routeLine of routeCalls) {
|
|
142
|
+
if (routeLine > earliestGoto) {
|
|
143
|
+
findings.push({
|
|
144
|
+
pattern: "route-after-goto",
|
|
145
|
+
file: filePath,
|
|
146
|
+
line: routeLine,
|
|
147
|
+
message: "page.route() registered after page.goto() — move route() before goto()",
|
|
148
|
+
severity: "high",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return findings;
|
|
155
|
+
}
|
|
156
|
+
export function detectStorageLeakage(content, filePath, configHasStorageState) {
|
|
157
|
+
// Condition 1: file references storageState
|
|
158
|
+
const hasStorageStateRef = content.includes("storageState") || configHasStorageState;
|
|
159
|
+
// Condition 2: page.goto() targeting protected routes
|
|
160
|
+
const protectedRoutes = [
|
|
161
|
+
"/dashboard",
|
|
162
|
+
"/profile",
|
|
163
|
+
"/admin",
|
|
164
|
+
"/settings",
|
|
165
|
+
"/account",
|
|
166
|
+
];
|
|
167
|
+
const gotoRegex = /page\.goto\s*\(\s*['"`]([^'"`]*)['"`]/g;
|
|
168
|
+
let hasProtectedGoto = false;
|
|
169
|
+
let firstProtectedGotoLine = 0;
|
|
170
|
+
let gm;
|
|
171
|
+
while ((gm = gotoRegex.exec(content)) !== null) {
|
|
172
|
+
const url = gm[1];
|
|
173
|
+
if (protectedRoutes.some((route) => url.includes(route))) {
|
|
174
|
+
hasProtectedGoto = true;
|
|
175
|
+
firstProtectedGotoLine =
|
|
176
|
+
content.substring(0, gm.index).split("\n").length;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Condition 3: no browser.newContext()
|
|
181
|
+
const hasNewContext = content.includes("browser.newContext(");
|
|
182
|
+
if (hasStorageStateRef && hasProtectedGoto && !hasNewContext) {
|
|
183
|
+
return [
|
|
184
|
+
{
|
|
185
|
+
pattern: "storage-leakage",
|
|
186
|
+
file: filePath,
|
|
187
|
+
line: firstProtectedGotoLine,
|
|
188
|
+
message: "Potential storageState leakage — authenticated state may bleed into unauthenticated tests",
|
|
189
|
+
severity: "medium",
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
export function detectTestUseScope(content, filePath, configHasStorageState) {
|
|
196
|
+
const findings = [];
|
|
197
|
+
if (!configHasStorageState)
|
|
198
|
+
return findings;
|
|
199
|
+
// Find all test.describe( positions
|
|
200
|
+
const describePositions = [];
|
|
201
|
+
const descRegex = /test\.describe\s*\(/g;
|
|
202
|
+
let dm;
|
|
203
|
+
while ((dm = descRegex.exec(content)) !== null) {
|
|
204
|
+
describePositions.push(dm.index);
|
|
205
|
+
}
|
|
206
|
+
// Find all test.use({ with storageState
|
|
207
|
+
const useRegex = /test\.use\s*\(/g;
|
|
208
|
+
let um;
|
|
209
|
+
while ((um = useRegex.exec(content)) !== null) {
|
|
210
|
+
const usePos = um.index;
|
|
211
|
+
const line = content.substring(0, usePos).split("\n").length;
|
|
212
|
+
// Check if storageState appears nearby (within the test.use config block)
|
|
213
|
+
// Search from usePos to the next test( or test.describe( or end of content
|
|
214
|
+
let searchEnd = content.length;
|
|
215
|
+
const nextTest = content.indexOf("test(", usePos + 1);
|
|
216
|
+
const nextDescribe = content.indexOf("test.describe(", usePos + 1);
|
|
217
|
+
if (nextTest > 0)
|
|
218
|
+
searchEnd = Math.min(searchEnd, nextTest);
|
|
219
|
+
if (nextDescribe > 0)
|
|
220
|
+
searchEnd = Math.min(searchEnd, nextDescribe);
|
|
221
|
+
const context = content.substring(usePos, Math.min(usePos + 500, searchEnd));
|
|
222
|
+
if (!context.includes("storageState"))
|
|
223
|
+
continue;
|
|
224
|
+
// Determine if inside a describe block
|
|
225
|
+
const isInsideDescribe = describePositions.some((dp) => dp < usePos);
|
|
226
|
+
if (isInsideDescribe) {
|
|
227
|
+
// Sub-check 1: test.use({ storageState }) inside test.describe(
|
|
228
|
+
findings.push({
|
|
229
|
+
pattern: "test-use-scope",
|
|
230
|
+
file: filePath,
|
|
231
|
+
line,
|
|
232
|
+
message: "Conflicting test.use({ storageState }) scope — may silently break test isolation",
|
|
233
|
+
severity: "medium",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
// Sub-check 2: top-level test.use({ storageState }) with untagged tests
|
|
238
|
+
const testTagRegex = /test\(['"`]([^'"`]*)['"`]/g;
|
|
239
|
+
let hasUntaggedTest = false;
|
|
240
|
+
let ttm;
|
|
241
|
+
while ((ttm = testTagRegex.exec(content)) !== null) {
|
|
242
|
+
if (!/@unauthenticated|@public/i.test(ttm[1])) {
|
|
243
|
+
hasUntaggedTest = true;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (hasUntaggedTest) {
|
|
248
|
+
findings.push({
|
|
249
|
+
pattern: "test-use-scope",
|
|
250
|
+
file: filePath,
|
|
251
|
+
line,
|
|
252
|
+
message: "Conflicting test.use({ storageState }) scope — may silently break test isolation",
|
|
253
|
+
severity: "medium",
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return findings;
|
|
259
|
+
}
|
|
260
|
+
// ─── Report Helpers ───────────────────────────────────────────────────
|
|
261
|
+
export function buildReport(findings) {
|
|
262
|
+
const patternCounts = {};
|
|
263
|
+
for (const f of findings) {
|
|
264
|
+
patternCounts[f.pattern] = (patternCounts[f.pattern] || 0) + 1;
|
|
265
|
+
}
|
|
266
|
+
const totalPatterns = Object.keys(patternCounts).length;
|
|
267
|
+
return {
|
|
268
|
+
findings,
|
|
269
|
+
patternCounts,
|
|
270
|
+
summaryText: `${findings.length} finding(s) across ${totalPatterns} pattern(s)`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function renderReport(report) {
|
|
274
|
+
if (report.findings.length === 0) {
|
|
275
|
+
console.log(" ✅ No flake patterns detected.\n");
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
// Group findings by pattern
|
|
279
|
+
const grouped = {};
|
|
280
|
+
for (const f of report.findings) {
|
|
281
|
+
if (!grouped[f.pattern])
|
|
282
|
+
grouped[f.pattern] = [];
|
|
283
|
+
grouped[f.pattern].push(f);
|
|
284
|
+
}
|
|
285
|
+
for (const [pattern, patternFindings] of Object.entries(grouped)) {
|
|
286
|
+
console.log(`─── ${pattern} ───`);
|
|
287
|
+
for (const f of patternFindings) {
|
|
288
|
+
console.log(` ⚠ ${f.file}:${f.line}`);
|
|
289
|
+
console.log(` ${f.message}`);
|
|
290
|
+
}
|
|
291
|
+
console.log();
|
|
292
|
+
}
|
|
293
|
+
console.log(`Summary: ${report.summaryText}\n`);
|
|
294
|
+
}
|
|
295
|
+
export function computeGateExitCode(report, gate) {
|
|
296
|
+
const level = gate.toUpperCase();
|
|
297
|
+
if (level === "HIGH") {
|
|
298
|
+
return report.findings.some((f) => f.severity === "high") ? 1 : 0;
|
|
299
|
+
}
|
|
300
|
+
if (level === "MEDIUM") {
|
|
301
|
+
return report.findings.some((f) => f.severity === "high" || f.severity === "medium")
|
|
302
|
+
? 1
|
|
303
|
+
: 0;
|
|
304
|
+
}
|
|
305
|
+
if (level === "ALL") {
|
|
306
|
+
return report.findings.length > 0 ? 1 : 0;
|
|
307
|
+
}
|
|
308
|
+
// Default to HIGH
|
|
309
|
+
return report.findings.some((f) => f.severity === "high") ? 1 : 0;
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=flake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flake.js","sourceRoot":"","sources":["../../src/commands/flake.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -7,15 +7,6 @@ import { readFileSync } from "fs";
|
|
|
7
7
|
import { join, dirname } from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
import { Command } from "commander";
|
|
10
|
-
import { init } from "./commands/init.js";
|
|
11
|
-
import { update } from "./commands/update.js";
|
|
12
|
-
import { doctor } from "./commands/doctor.js";
|
|
13
|
-
import { run } from "./commands/run.js";
|
|
14
|
-
import { migrate } from "./commands/migrate.js";
|
|
15
|
-
import { uninstall } from "./commands/uninstall.js";
|
|
16
|
-
import { audit } from "./commands/audit.js";
|
|
17
|
-
import { explore } from "./commands/explore.js";
|
|
18
|
-
import { checkForUpdate } from "./shared/version-check.js";
|
|
19
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
11
|
const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
21
12
|
const program = new Command();
|
|
@@ -31,6 +22,8 @@ program
|
|
|
31
22
|
.option("--seed", "force regenerate seed.spec.ts (overwrite if exists)")
|
|
32
23
|
.option("--ci", "generate GitHub Actions CI workflow")
|
|
33
24
|
.action(async (opts) => {
|
|
25
|
+
const { init } = await import("./commands/init.js");
|
|
26
|
+
const { checkForUpdate } = await import("./shared/version-check.js");
|
|
34
27
|
await init(opts);
|
|
35
28
|
await checkForUpdate(pkg.version);
|
|
36
29
|
});
|
|
@@ -39,6 +32,8 @@ program
|
|
|
39
32
|
.description("Check if all prerequisites are installed")
|
|
40
33
|
.option("--json", "Output results as JSON")
|
|
41
34
|
.action(async (opts) => {
|
|
35
|
+
const { doctor } = await import("./commands/doctor.js");
|
|
36
|
+
const { checkForUpdate } = await import("./shared/version-check.js");
|
|
42
37
|
await doctor(opts);
|
|
43
38
|
await checkForUpdate(pkg.version);
|
|
44
39
|
});
|
|
@@ -48,6 +43,7 @@ program
|
|
|
48
43
|
.option("--no-cli", "skip CLI update")
|
|
49
44
|
.option("--no-skill", "skip command update")
|
|
50
45
|
.action(async (opts) => {
|
|
46
|
+
const { update } = await import("./commands/update.js");
|
|
51
47
|
await update(opts);
|
|
52
48
|
// No version check needed after update — user just updated
|
|
53
49
|
});
|
|
@@ -67,6 +63,8 @@ program
|
|
|
67
63
|
.option("--headed", "Show browser during test run (default: headless)")
|
|
68
64
|
.option("--update-snapshots", "Update screenshot baselines before running tests")
|
|
69
65
|
.action(async (changeName, opts) => {
|
|
66
|
+
const { run } = await import("./commands/run.js");
|
|
67
|
+
const { checkForUpdate } = await import("./shared/version-check.js");
|
|
70
68
|
await run(changeName, opts);
|
|
71
69
|
await checkForUpdate(pkg.version);
|
|
72
70
|
});
|
|
@@ -76,6 +74,8 @@ program
|
|
|
76
74
|
.option("-n, --dry-run", "Show what would be migrated without moving files")
|
|
77
75
|
.option("-f, --force", "Overwrite existing files at the new location")
|
|
78
76
|
.action(async (opts) => {
|
|
77
|
+
const { migrate } = await import("./commands/migrate.js");
|
|
78
|
+
const { checkForUpdate } = await import("./shared/version-check.js");
|
|
79
79
|
await migrate(opts);
|
|
80
80
|
await checkForUpdate(pkg.version);
|
|
81
81
|
});
|
|
@@ -83,6 +83,8 @@ program
|
|
|
83
83
|
.command("uninstall")
|
|
84
84
|
.description("Remove OpenSpec + Playwright E2E integration from the current project")
|
|
85
85
|
.action(async () => {
|
|
86
|
+
const { uninstall } = await import("./commands/uninstall.js");
|
|
87
|
+
const { checkForUpdate } = await import("./shared/version-check.js");
|
|
86
88
|
await uninstall();
|
|
87
89
|
await checkForUpdate(pkg.version);
|
|
88
90
|
});
|
|
@@ -90,15 +92,36 @@ program
|
|
|
90
92
|
.command("audit")
|
|
91
93
|
.description("Audit test files for orphaned specs, missing auth, sitemap issues")
|
|
92
94
|
.action(async () => {
|
|
95
|
+
const { audit } = await import("./commands/audit.js");
|
|
96
|
+
const { checkForUpdate } = await import("./shared/version-check.js");
|
|
93
97
|
await audit();
|
|
94
98
|
await checkForUpdate(pkg.version);
|
|
95
99
|
});
|
|
100
|
+
program
|
|
101
|
+
.command("coverage [change-name]")
|
|
102
|
+
.description("Analyze spec–test coverage for OpenSpec changes")
|
|
103
|
+
.option("--json", "Output results as JSON")
|
|
104
|
+
.action(async (changeName, opts) => {
|
|
105
|
+
const { coverage } = await import("./commands/coverage.js");
|
|
106
|
+
await coverage(changeName, opts);
|
|
107
|
+
});
|
|
108
|
+
program
|
|
109
|
+
.command("flake [change-name]")
|
|
110
|
+
.description("Detect static flake patterns in Playwright test files")
|
|
111
|
+
.option("--json", "Output results as JSON")
|
|
112
|
+
.option("--gate <severity>", "Exit non-zero if findings meet severity (HIGH|MEDIUM|ALL)")
|
|
113
|
+
.action(async (changeName, opts) => {
|
|
114
|
+
const { flake } = await import("./commands/flake.js");
|
|
115
|
+
await flake(changeName, opts);
|
|
116
|
+
});
|
|
96
117
|
program
|
|
97
118
|
.command("explore")
|
|
98
119
|
.description("Explore routes in parallel with Playwright")
|
|
99
120
|
.option("-p, --parallel <n>", "Number of parallel workers", (v) => parseInt(v, 10), 4)
|
|
100
121
|
.option("-n, --dry-run", "Show what would be explored without running")
|
|
101
122
|
.action(async (opts) => {
|
|
123
|
+
const { explore } = await import("./commands/explore.js");
|
|
124
|
+
const { checkForUpdate } = await import("./shared/version-check.js");
|
|
102
125
|
await explore(opts);
|
|
103
126
|
await checkForUpdate(pkg.version);
|
|
104
127
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,8FAA8F;AAC9F,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;IAC/B,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAC5C,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/employee-standards.md
CHANGED
|
@@ -91,3 +91,10 @@
|
|
|
91
91
|
4. 用户拒绝提供 → 用 stub / `throw` / `return null` 让代码显式失败,**禁止**静默编造值
|
|
92
92
|
|
|
93
93
|
**纯前端项目**:若存在 OpenAPI / 接口文档 / MCP 暴露的接口,必须查阅真实定义后引用,并标注来源(例如 `// 来源: docs/api/openapi.yaml#/paths/...`),禁止凭印象编造 endpoint / path / 字段名。
|
|
94
|
+
|
|
95
|
+
## 7. 临时文件管理
|
|
96
|
+
|
|
97
|
+
所有工具(Chrome DevTools MCP、截图、日志、heapdump 等)产生的非源码临时文件必须放项目根目录 `tmp/` 下:
|
|
98
|
+
- 平铺,不分子目录
|
|
99
|
+
- 文件名含时间戳(避免覆盖)
|
|
100
|
+
- `tmp/` 中超过 24 小时的文件可以在 commit 前删除
|
package/package.json
CHANGED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vision check command: Analyze screenshots for layout anomalies using Ollama VLM.
|
|
3
|
-
*
|
|
4
|
-
* Modes:
|
|
5
|
-
* --screenshots "..." Analyze existing files (single mode)
|
|
6
|
-
* --url ... --viewport ... Capture from URL at multiple viewports
|
|
7
|
-
* --baseline Save as baseline (with --screenshots or --url+viewport)
|
|
8
|
-
* --diff Compare against baseline
|
|
9
|
-
* --report <path> Generate HTML report
|
|
10
|
-
*
|
|
11
|
-
* Exit codes:
|
|
12
|
-
* 0 = Check completed (with or without anomalies)
|
|
13
|
-
* 1 = Ollama not available (skipped)
|
|
14
|
-
* 2 = Configuration missing or invalid (skipped)
|
|
15
|
-
*/
|
|
16
|
-
export interface VisionCheckOptions {
|
|
17
|
-
screenshots: string;
|
|
18
|
-
config?: string;
|
|
19
|
-
parallel?: number;
|
|
20
|
-
output?: string;
|
|
21
|
-
dryRun?: boolean;
|
|
22
|
-
severity?: string;
|
|
23
|
-
json?: boolean;
|
|
24
|
-
viewport?: string;
|
|
25
|
-
url?: string;
|
|
26
|
-
baseline?: boolean;
|
|
27
|
-
diff?: boolean;
|
|
28
|
-
report?: string;
|
|
29
|
-
threshold?: number;
|
|
30
|
-
noCache?: boolean;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Main vision check command.
|
|
34
|
-
*/
|
|
35
|
-
export declare function visionCheck(options: VisionCheckOptions): Promise<void>;
|