openspec-playwright 0.1.61 ā 0.1.63
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/LICENSE +21 -0
- package/README.md +4 -3
- package/README.zh-CN.md +4 -3
- package/bin/CLAUDE.md +11 -0
- package/dist/CLAUDE.md +17 -0
- package/dist/commands/doctor.d.ts +4 -1
- package/dist/commands/doctor.js +110 -73
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/editors.js +149 -95
- package/dist/commands/editors.js.map +1 -1
- package/dist/commands/init.js +105 -97
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcpSync.js +46 -31
- package/dist/commands/mcpSync.js.map +1 -1
- package/dist/commands/run.d.ts +13 -0
- package/dist/commands/run.js +74 -51
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/uninstall.js +133 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +92 -55
- package/dist/commands/update.js.map +1 -1
- package/dist/index.js +33 -26
- package/dist/index.js.map +1 -1
- package/package.json +19 -1
- package/schemas/playwright-e2e/templates/playwright.config.ts +22 -22
- package/templates/CLAUDE.md +15 -0
- package/templates/seed.spec.ts +5 -3
- package/.claude/commands/opsx/e2e-body.md +0 -39
- package/.claude/commands/opsx/e2e.md +0 -47
- package/.claude/skills/openspec-e2e/SKILL.md +0 -444
- package/.github/workflows/release.yml +0 -81
- package/docs/plans/2026-03-26-openspec-playwright-design.md +0 -180
- package/employee-standards.md +0 -42
- package/openspec/schemas/playwright-e2e/schema.yaml +0 -56
- package/openspec/schemas/playwright-e2e/templates/e2e-test.ts +0 -55
- package/openspec/schemas/playwright-e2e/templates/playwright.config.ts +0 -52
- package/openspec/schemas/playwright-e2e/templates/report.md +0 -27
- package/openspec/schemas/playwright-e2e/templates/test-plan.md +0 -24
- package/openspec-playwright-0.1.61.tgz +0 -0
- package/release-notes.md +0 -5
- package/src/commands/doctor.ts +0 -115
- package/src/commands/editors.ts +0 -606
- package/src/commands/init.ts +0 -252
- package/src/commands/mcpSync.ts +0 -160
- package/src/commands/run.ts +0 -172
- package/src/commands/update.ts +0 -165
- package/src/index.ts +0 -47
- package/tests/editors.test.ts +0 -180
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -9
package/dist/commands/run.js
CHANGED
|
@@ -1,88 +1,111 @@
|
|
|
1
|
-
import { execSync } from
|
|
2
|
-
import { existsSync, mkdirSync, writeFileSync } from
|
|
3
|
-
import { join } from
|
|
4
|
-
import chalk from
|
|
5
|
-
const REPORTS_DIR =
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
const REPORTS_DIR = "openspec/reports";
|
|
6
6
|
export async function run(changeName, options) {
|
|
7
7
|
console.log(chalk.blue(`\nš OpenSpec Playwright E2E: ${changeName}\n`));
|
|
8
8
|
const projectRoot = process.cwd();
|
|
9
9
|
// 1. Verify test file exists
|
|
10
|
-
const testFile = join(projectRoot,
|
|
10
|
+
const testFile = join(projectRoot, "tests", "playwright", `${changeName}.spec.ts`);
|
|
11
11
|
if (!existsSync(testFile)) {
|
|
12
12
|
console.log(chalk.red(` ā Test file not found: tests/playwright/${changeName}.spec.ts`));
|
|
13
|
-
console.log(chalk.gray(
|
|
13
|
+
console.log(chalk.gray(" Run /opsx:e2e first to generate tests.\n"));
|
|
14
14
|
process.exit(1);
|
|
15
15
|
}
|
|
16
16
|
// 2. Setup reports dir
|
|
17
17
|
mkdirSync(join(projectRoot, REPORTS_DIR), { recursive: true });
|
|
18
18
|
// 3. Detect auth credentials
|
|
19
|
-
const credsPath = join(projectRoot,
|
|
19
|
+
const credsPath = join(projectRoot, "tests", "playwright", "credentials.yaml");
|
|
20
20
|
const hasCredentials = existsSync(credsPath);
|
|
21
21
|
if (!hasCredentials) {
|
|
22
|
-
console.log(chalk.yellow(
|
|
22
|
+
console.log(chalk.yellow(" ā No credentials.yaml found ā tests may fail if auth required"));
|
|
23
23
|
}
|
|
24
24
|
// 4. Run Playwright tests with output capture
|
|
25
|
-
console.log(chalk.blue(
|
|
26
|
-
const args = [
|
|
25
|
+
console.log(chalk.blue("āāā Running Tests āāā"));
|
|
26
|
+
const args = ["npx", "playwright", "test", testFile, "--reporter=list"];
|
|
27
27
|
if (options.project) {
|
|
28
|
-
args.push(
|
|
28
|
+
args.push("--project=" + options.project);
|
|
29
29
|
}
|
|
30
|
-
let testOutput =
|
|
31
|
-
let exitCode = 0;
|
|
30
|
+
let testOutput = "";
|
|
32
31
|
try {
|
|
33
32
|
// Capture stdout to detect port mismatch
|
|
34
|
-
const result = execSync(args.join(
|
|
33
|
+
const result = execSync(args.join(" "), {
|
|
35
34
|
cwd: projectRoot,
|
|
36
|
-
encoding:
|
|
37
|
-
stdio: [
|
|
35
|
+
encoding: "utf-8",
|
|
36
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
38
37
|
timeout: (options.timeout ?? 300) * 1000,
|
|
39
38
|
});
|
|
40
39
|
testOutput = result;
|
|
41
40
|
}
|
|
42
41
|
catch (err) {
|
|
43
|
-
exitCode = 1;
|
|
44
42
|
const error = err;
|
|
45
|
-
testOutput = (error.stdout ??
|
|
43
|
+
testOutput = (error.stdout ?? "") + (error.stderr ?? "");
|
|
46
44
|
}
|
|
47
45
|
// 5. Parse results from Playwright output
|
|
48
46
|
const results = parsePlaywrightOutput(testOutput);
|
|
49
47
|
// 6. Detect port mismatch
|
|
50
|
-
if (testOutput.includes(
|
|
51
|
-
testOutput.includes(
|
|
52
|
-
testOutput.includes(
|
|
53
|
-
console.log(chalk.yellow(
|
|
48
|
+
if (testOutput.includes("net::ERR_CONNECTION_REFUSED") ||
|
|
49
|
+
testOutput.includes("listen EADDRINUSE") ||
|
|
50
|
+
testOutput.includes("0.0.0.0:")) {
|
|
51
|
+
console.log(chalk.yellow(" ā Port mismatch detected. Check BASE_URL and webServer port."));
|
|
54
52
|
}
|
|
55
53
|
// 7. Generate markdown report
|
|
56
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g,
|
|
54
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
57
55
|
const reportPath = join(projectRoot, REPORTS_DIR, `playwright-e2e-${changeName}-${timestamp}.md`);
|
|
58
56
|
const reportContent = generateReport(changeName, timestamp, results);
|
|
59
57
|
writeFileSync(reportPath, reportContent);
|
|
60
58
|
// 8. Summary
|
|
61
|
-
|
|
59
|
+
if (options.json) {
|
|
60
|
+
const output = JSON.stringify({
|
|
61
|
+
change: changeName,
|
|
62
|
+
total: results.total,
|
|
63
|
+
passed: results.passed,
|
|
64
|
+
failed: results.failed,
|
|
65
|
+
duration: results.duration,
|
|
66
|
+
report: reportPath,
|
|
67
|
+
tests: results.tests,
|
|
68
|
+
ok: results.failed === 0,
|
|
69
|
+
}, null, 2);
|
|
70
|
+
console.log(output);
|
|
71
|
+
if (results.failed > 0)
|
|
72
|
+
process.exit(1);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(chalk.blue("\nāāā Results āāā"));
|
|
62
76
|
console.log(` Tests: ${results.total} ` +
|
|
63
|
-
chalk.green(`ā ${results.passed}`) +
|
|
64
|
-
|
|
77
|
+
chalk.green(`ā ${results.passed}`) +
|
|
78
|
+
" " +
|
|
79
|
+
(results.failed > 0
|
|
80
|
+
? chalk.red(`ā ${results.failed}`)
|
|
81
|
+
: chalk.gray(`ā ${results.failed}`)) +
|
|
65
82
|
` Duration: ${results.duration}`);
|
|
66
|
-
console.log(chalk.blue(
|
|
83
|
+
console.log(chalk.blue("\nāāā Report āāā"));
|
|
67
84
|
console.log(chalk.green(` ā ${reportPath}\n`));
|
|
68
85
|
if (results.failed > 0) {
|
|
69
86
|
console.log(chalk.red(`ā E2E verification FAILED (${results.failed} tests)`));
|
|
70
87
|
process.exit(1);
|
|
71
88
|
}
|
|
72
89
|
else {
|
|
73
|
-
console.log(chalk.green(
|
|
90
|
+
console.log(chalk.green("ā E2E verification PASSED"));
|
|
74
91
|
}
|
|
75
92
|
}
|
|
76
|
-
function parsePlaywrightOutput(output) {
|
|
77
|
-
const results = {
|
|
93
|
+
export function parsePlaywrightOutput(output) {
|
|
94
|
+
const results = {
|
|
95
|
+
total: 0,
|
|
96
|
+
passed: 0,
|
|
97
|
+
failed: 0,
|
|
98
|
+
duration: "0s",
|
|
99
|
+
tests: [],
|
|
100
|
+
};
|
|
78
101
|
// Parse: "ā my-test (1.2s)" or "ā my-test (0.5s)"
|
|
79
102
|
const testLineRegex = /([āāx]) (.+?) \((\d+(?:\.\d+)?[a-z]+)\)/g;
|
|
80
103
|
let match;
|
|
81
104
|
while ((match = testLineRegex.exec(output)) !== null) {
|
|
82
|
-
const status = match[1] ===
|
|
105
|
+
const status = match[1] === "ā" ? "passed" : "failed";
|
|
83
106
|
results.tests.push({ name: match[2], status });
|
|
84
107
|
results.total++;
|
|
85
|
-
if (status ===
|
|
108
|
+
if (status === "passed")
|
|
86
109
|
results.passed++;
|
|
87
110
|
else
|
|
88
111
|
results.failed++;
|
|
@@ -96,40 +119,40 @@ function parsePlaywrightOutput(output) {
|
|
|
96
119
|
function generateReport(changeName, timestamp, results) {
|
|
97
120
|
const lines = [
|
|
98
121
|
`# E2E Verify Report: ${changeName}`,
|
|
99
|
-
|
|
122
|
+
"",
|
|
100
123
|
`**Change**: \`${changeName}\``,
|
|
101
|
-
`**Generated**: ${timestamp.replace(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
124
|
+
`**Generated**: ${timestamp.replace("T", " ").slice(0, 16)} UTC`,
|
|
125
|
+
"",
|
|
126
|
+
"## Summary",
|
|
127
|
+
"",
|
|
128
|
+
"| Check | Status |",
|
|
129
|
+
"|-------|--------|",
|
|
107
130
|
`| Tests Run | ${results.total} |`,
|
|
108
131
|
`| Passed | ${results.passed} |`,
|
|
109
132
|
`| Failed | ${results.failed} |`,
|
|
110
133
|
`| Duration | ${results.duration} |`,
|
|
111
|
-
`| Final Status | ${results.failed === 0 ?
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
134
|
+
`| Final Status | ${results.failed === 0 ? "ā
PASS" : "ā FAIL"} |`,
|
|
135
|
+
"",
|
|
136
|
+
"## Test Results",
|
|
137
|
+
"",
|
|
115
138
|
];
|
|
116
139
|
if (results.tests.length === 0) {
|
|
117
|
-
lines.push(
|
|
140
|
+
lines.push("_(No test output captured ā check Playwright configuration)_", "");
|
|
118
141
|
}
|
|
119
142
|
else {
|
|
120
143
|
for (const test of results.tests) {
|
|
121
|
-
const icon = test.status ===
|
|
144
|
+
const icon = test.status === "passed" ? "ā
" : "ā";
|
|
122
145
|
lines.push(`- ${test.name}: ${icon} ${test.status}`);
|
|
123
146
|
}
|
|
124
|
-
lines.push(
|
|
147
|
+
lines.push("");
|
|
125
148
|
}
|
|
126
|
-
lines.push(
|
|
149
|
+
lines.push("## Recommendations", "");
|
|
127
150
|
if (results.failed > 0) {
|
|
128
|
-
lines.push(
|
|
151
|
+
lines.push("Review failed tests above. Common fixes:", "- Update selectors if UI changed (use `data-testid` attributes)", "- Adjust BASE_URL in seed.spec.ts if port differs", "- Set E2E_USERNAME/E2E_PASSWORD if auth is required", "- Check `npx playwright show-report` for screenshots", "");
|
|
129
152
|
}
|
|
130
153
|
else {
|
|
131
|
-
lines.push(
|
|
154
|
+
lines.push("All tests passed. No action needed.", "");
|
|
132
155
|
}
|
|
133
|
-
return lines.join(
|
|
156
|
+
return lines.join("\n");
|
|
134
157
|
}
|
|
135
158
|
//# sourceMappingURL=run.js.map
|
package/dist/commands/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uninstall(): Promise<void>;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, rmdirSync, } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { detectEditors, detectCodex } from "./editors.js";
|
|
6
|
+
export async function uninstall() {
|
|
7
|
+
console.log(chalk.blue("\nšļø Uninstalling OpenSpec + Playwright E2E\n"));
|
|
8
|
+
const projectRoot = process.cwd();
|
|
9
|
+
// 1. Remove Playwright MCP from .claude.json
|
|
10
|
+
console.log(chalk.blue("āāā Removing Playwright MCP āāā"));
|
|
11
|
+
removeMcpFromClaudeJson(homedir(), projectRoot);
|
|
12
|
+
// 2. Remove E2E commands for detected editors
|
|
13
|
+
console.log(chalk.blue("\nāāā Removing E2E Commands āāā"));
|
|
14
|
+
const detected = detectEditors(projectRoot);
|
|
15
|
+
const codex = detectCodex();
|
|
16
|
+
const adapters = codex ? [...detected, codex] : detected;
|
|
17
|
+
if (adapters.length > 0) {
|
|
18
|
+
for (const adapter of adapters) {
|
|
19
|
+
const relPath = adapter.getCommandPath("e2e");
|
|
20
|
+
const absPath = join(projectRoot, relPath);
|
|
21
|
+
if (existsSync(absPath)) {
|
|
22
|
+
rmSync(absPath);
|
|
23
|
+
// Remove empty parent directories
|
|
24
|
+
cleanupEmptyDirs(dirname(absPath), projectRoot);
|
|
25
|
+
console.log(chalk.green(` ā ${adapter.toolId}: removed ${relPath}`));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(chalk.gray(" - No editors detected, skipping"));
|
|
31
|
+
}
|
|
32
|
+
// 3. Remove SKILL.md
|
|
33
|
+
console.log(chalk.blue("\nāāā Removing Skill āāā"));
|
|
34
|
+
const skillDir = join(projectRoot, ".claude", "skills", "openspec-e2e");
|
|
35
|
+
if (existsSync(skillDir)) {
|
|
36
|
+
rmSync(skillDir, { recursive: true, force: true });
|
|
37
|
+
console.log(chalk.green(" ā Removed .claude/skills/openspec-e2e/"));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.log(chalk.gray(" - Skill not found, skipping"));
|
|
41
|
+
}
|
|
42
|
+
// 4. Remove schema
|
|
43
|
+
console.log(chalk.blue("\nāāā Removing Schema āāā"));
|
|
44
|
+
const schemaDir = join(projectRoot, "openspec", "schemas", "playwright-e2e");
|
|
45
|
+
if (existsSync(schemaDir)) {
|
|
46
|
+
rmSync(schemaDir, { recursive: true, force: true });
|
|
47
|
+
console.log(chalk.green(" ā Removed openspec/schemas/playwright-e2e/"));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
console.log(chalk.gray(" - Schema not found, skipping"));
|
|
51
|
+
}
|
|
52
|
+
// 5. Clean CLAUDE.md markers
|
|
53
|
+
console.log(chalk.blue("\nāāā Cleaning CLAUDE.md āāā"));
|
|
54
|
+
cleanClaudeMd(projectRoot);
|
|
55
|
+
// Summary
|
|
56
|
+
console.log(chalk.blue("\nāāā Summary āāā"));
|
|
57
|
+
console.log(chalk.green(" ā Uninstall complete!\n"));
|
|
58
|
+
console.log(chalk.gray(" Note: Run openspec-pw doctor to verify clean removal.\n"));
|
|
59
|
+
}
|
|
60
|
+
function removeMcpFromClaudeJson(homeDir, projectRoot) {
|
|
61
|
+
const claudeJsonPath = join(homeDir, ".claude.json");
|
|
62
|
+
if (!existsSync(claudeJsonPath)) {
|
|
63
|
+
console.log(chalk.gray(" - .claude.json not found, skipping"));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const content = readFileSync(claudeJsonPath, "utf-8");
|
|
68
|
+
const json = JSON.parse(content);
|
|
69
|
+
let changed = false;
|
|
70
|
+
// Remove from global mcpServers
|
|
71
|
+
if (json.mcpServers?.["playwright"]) {
|
|
72
|
+
delete json.mcpServers["playwright"];
|
|
73
|
+
changed = true;
|
|
74
|
+
}
|
|
75
|
+
// Remove from project-level mcpServers
|
|
76
|
+
if (json.projects?.[projectRoot]?.mcpServers?.["playwright"]) {
|
|
77
|
+
delete json.projects[projectRoot].mcpServers["playwright"];
|
|
78
|
+
changed = true;
|
|
79
|
+
}
|
|
80
|
+
if (changed) {
|
|
81
|
+
writeFileSync(claudeJsonPath, JSON.stringify(json, null, 2) + "\n");
|
|
82
|
+
console.log(chalk.green(" ā Removed playwright from .claude.json"));
|
|
83
|
+
console.log(chalk.gray(" Restart Claude Code to complete removal"));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.log(chalk.gray(" - Playwright MCP not found in .claude.json"));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
console.log(chalk.yellow(` ā Failed to update .claude.json: ${err instanceof Error ? err.message : String(err)}`));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function cleanupEmptyDirs(dir, stopAt) {
|
|
94
|
+
while (dir !== stopAt && dir.length > stopAt.length) {
|
|
95
|
+
try {
|
|
96
|
+
const entries = readdirSync(dir);
|
|
97
|
+
if (entries.length === 0) {
|
|
98
|
+
rmdirSync(dir);
|
|
99
|
+
dir = dirname(dir);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function cleanClaudeMd(projectRoot) {
|
|
111
|
+
const dest = join(projectRoot, "CLAUDE.md");
|
|
112
|
+
if (!existsSync(dest)) {
|
|
113
|
+
console.log(chalk.gray(" - CLAUDE.md not found, skipping"));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const existing = readFileSync(dest, "utf-8");
|
|
117
|
+
const markerStart = "<!-- OPENSPEC:START -->";
|
|
118
|
+
const markerEnd = "<!-- OPENSPEC:END -->";
|
|
119
|
+
if (!existing.includes(markerStart)) {
|
|
120
|
+
console.log(chalk.gray(" - No OpenSpec markers found in CLAUDE.md"));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const updated = existing
|
|
124
|
+
.split("\n")
|
|
125
|
+
.filter((line) => !line.trim().startsWith(markerStart) &&
|
|
126
|
+
!line.trim().startsWith(markerEnd))
|
|
127
|
+
.join("\n")
|
|
128
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
129
|
+
.trim() + "\n";
|
|
130
|
+
writeFileSync(dest, updated);
|
|
131
|
+
console.log(chalk.green(" ā Removed OpenSpec markers from CLAUDE.md"));
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/update.js
CHANGED
|
@@ -1,122 +1,159 @@
|
|
|
1
|
-
import { execSync, exec } from
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync, readdirSync, statSync, } from
|
|
3
|
-
import { join } from
|
|
4
|
-
import { tmpdir } from
|
|
5
|
-
import { promisify } from
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import { detectEditors, detectCodex, installForAllEditors, installSkill } from './editors.js';
|
|
11
|
-
const CMD_BODY_SRC = fileURLToPath(new URL('../../.claude/commands/opsx/e2e-body.md', import.meta.url));
|
|
12
|
-
const SCHEMA_DIR = fileURLToPath(new URL('../../schemas', import.meta.url));
|
|
1
|
+
import { execSync, exec } from "child_process";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync, readdirSync, statSync, } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { tmpdir, homedir } from "os";
|
|
5
|
+
import { promisify } from "util";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import * as tar from "tar";
|
|
8
|
+
import { syncMcpTools } from "./mcpSync.js";
|
|
9
|
+
import { detectEditors, detectCodex, installForAllEditors, installSkill, } from "./editors.js";
|
|
13
10
|
export async function update(options) {
|
|
14
|
-
console.log(chalk.blue(
|
|
11
|
+
console.log(chalk.blue("\nš Updating OpenSpec + Playwright E2E\n"));
|
|
15
12
|
const projectRoot = process.cwd();
|
|
16
13
|
// Check if init has been run
|
|
17
|
-
const hasSkill = existsSync(join(projectRoot,
|
|
18
|
-
const hasOpenSpec = existsSync(join(projectRoot,
|
|
14
|
+
const hasSkill = existsSync(join(projectRoot, ".claude", "skills", "openspec-e2e", "SKILL.md"));
|
|
15
|
+
const hasOpenSpec = existsSync(join(projectRoot, "openspec"));
|
|
19
16
|
if (!hasSkill && !hasOpenSpec) {
|
|
20
|
-
console.log(chalk.yellow(
|
|
17
|
+
console.log(chalk.yellow(" ā OpenSpec + Playwright E2E not initialized."));
|
|
21
18
|
console.log(chalk.gray(' Run "openspec-pw init" first to set up the integration.\n'));
|
|
22
19
|
return;
|
|
23
20
|
}
|
|
24
21
|
// 1. Update CLI tool from npm
|
|
25
22
|
if (options.cli !== false) {
|
|
26
|
-
console.log(chalk.blue(
|
|
23
|
+
console.log(chalk.blue("āāā Updating CLI āāā"));
|
|
27
24
|
try {
|
|
28
|
-
execSync(
|
|
29
|
-
|
|
25
|
+
execSync("npm install -g openspec-playwright", {
|
|
26
|
+
stdio: "inherit",
|
|
27
|
+
cwd: projectRoot,
|
|
28
|
+
});
|
|
29
|
+
console.log(chalk.green(" ā CLI updated via npm"));
|
|
30
30
|
}
|
|
31
31
|
catch {
|
|
32
|
-
console.log(chalk.yellow(
|
|
33
|
-
console.log(chalk.gray(
|
|
32
|
+
console.log(chalk.yellow(" ā Failed to update CLI via npm"));
|
|
33
|
+
console.log(chalk.gray(" Run manually: npm install -g openspec-playwright"));
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
// 2. Update commands for all detected editors + schema
|
|
37
37
|
if (options.skill !== false) {
|
|
38
|
-
console.log(chalk.blue(
|
|
38
|
+
console.log(chalk.blue("\nāāā Updating Commands & Schema āāā"));
|
|
39
39
|
try {
|
|
40
|
-
const tmpDir = join(tmpdir(),
|
|
40
|
+
const tmpDir = join(tmpdir(), "openspec-e2e-update");
|
|
41
41
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
42
42
|
mkdirSync(tmpDir, { recursive: true });
|
|
43
43
|
const execAsync = promisify(exec);
|
|
44
44
|
await execAsync(`npm pack openspec-playwright --pack-destination ${tmpDir}`, { timeout: 30000 });
|
|
45
45
|
// Find the latest tarball by mtime
|
|
46
46
|
const tgzFiles = readdirSync(tmpDir)
|
|
47
|
-
.filter(f => f.startsWith(
|
|
48
|
-
.map(f => ({ name: f, mtime: statSync(join(tmpDir, f)).mtimeMs }))
|
|
47
|
+
.filter((f) => f.startsWith("openspec-playwright-") && f.endsWith(".tgz"))
|
|
48
|
+
.map((f) => ({ name: f, mtime: statSync(join(tmpDir, f)).mtimeMs }))
|
|
49
49
|
.sort((a, b) => b.mtime - a.mtime);
|
|
50
50
|
if (tgzFiles.length === 0)
|
|
51
|
-
throw new Error(
|
|
51
|
+
throw new Error("No tarball found");
|
|
52
52
|
const tarballPath = join(tmpDir, tgzFiles[0].name);
|
|
53
53
|
// Extract tarball
|
|
54
54
|
await tar.extract({ file: tarballPath, cwd: tmpDir, strip: 1 });
|
|
55
|
-
const bodySrc = join(tmpDir,
|
|
56
|
-
const schemaSrc = join(tmpDir,
|
|
55
|
+
const bodySrc = join(tmpDir, ".claude", "commands", "opsx", "e2e-body.md");
|
|
56
|
+
const schemaSrc = join(tmpDir, "schemas", "playwright-e2e");
|
|
57
57
|
// Install commands for all detected editors
|
|
58
58
|
const detected = detectEditors(projectRoot);
|
|
59
59
|
const codex = detectCodex();
|
|
60
60
|
const adapters = codex ? [...detected, codex] : detected;
|
|
61
61
|
if (adapters.length > 0 && existsSync(bodySrc)) {
|
|
62
|
-
const body = readFileSync(bodySrc,
|
|
62
|
+
const body = readFileSync(bodySrc, "utf-8");
|
|
63
63
|
installForAllEditors(body, adapters, projectRoot);
|
|
64
64
|
}
|
|
65
65
|
// Install SKILL.md for Claude Code
|
|
66
|
-
const skillSrc = join(tmpDir,
|
|
67
|
-
if (existsSync(join(projectRoot,
|
|
68
|
-
const skillContent = readFileSync(skillSrc,
|
|
66
|
+
const skillSrc = join(tmpDir, ".claude", "skills", "openspec-e2e", "SKILL.md");
|
|
67
|
+
if (existsSync(join(projectRoot, ".claude")) && existsSync(skillSrc)) {
|
|
68
|
+
const skillContent = readFileSync(skillSrc, "utf-8");
|
|
69
69
|
installSkill(projectRoot, skillContent);
|
|
70
70
|
}
|
|
71
71
|
// Install schema
|
|
72
72
|
installSchemaFrom(schemaSrc, projectRoot);
|
|
73
73
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
74
|
-
console.log(chalk.green(
|
|
74
|
+
console.log(chalk.green(" ā Commands & schema updated to latest"));
|
|
75
75
|
}
|
|
76
76
|
catch (err) {
|
|
77
77
|
const msg = err instanceof Error ? err.message : String(err);
|
|
78
78
|
console.log(chalk.yellow(` ā Failed to update from npm: ${msg}`));
|
|
79
|
-
console.log(chalk.gray(
|
|
79
|
+
console.log(chalk.gray(" Trying npm install to pull latest version..."));
|
|
80
80
|
try {
|
|
81
|
-
execSync(
|
|
82
|
-
|
|
81
|
+
execSync("npm install -g openspec-playwright", {
|
|
82
|
+
stdio: "inherit",
|
|
83
|
+
cwd: projectRoot,
|
|
84
|
+
});
|
|
85
|
+
console.log(chalk.green(" ā Updated via npm install"));
|
|
83
86
|
}
|
|
84
87
|
catch {
|
|
85
|
-
console.log(chalk.red(
|
|
86
|
-
console.log(chalk.gray(
|
|
88
|
+
console.log(chalk.red(" ā Failed to update. Run manually:"));
|
|
89
|
+
console.log(chalk.gray(" npm install -g openspec-playwright"));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// 2b. Install Playwright MCP if not present (Claude Code only)
|
|
94
|
+
if (options.mcp !== false && existsSync(join(projectRoot, ".claude"))) {
|
|
95
|
+
console.log(chalk.blue("\nāāā Installing Playwright MCP āāā"));
|
|
96
|
+
const claudeJsonPath = join(homedir(), ".claude.json");
|
|
97
|
+
const claudeJson = existsSync(claudeJsonPath)
|
|
98
|
+
? JSON.parse(readFileSync(claudeJsonPath, "utf-8"))
|
|
99
|
+
: {};
|
|
100
|
+
const globalMcp = claudeJson?.mcpServers ?? {};
|
|
101
|
+
const localMcp = claudeJson?.projects?.[projectRoot]?.mcpServers ?? {};
|
|
102
|
+
if (globalMcp["playwright"] || localMcp["playwright"]) {
|
|
103
|
+
console.log(chalk.green(" ā Playwright MCP already installed"));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
try {
|
|
107
|
+
execSync("claude mcp add playwright npx @playwright/mcp@latest", {
|
|
108
|
+
cwd: projectRoot,
|
|
109
|
+
stdio: "inherit",
|
|
110
|
+
});
|
|
111
|
+
console.log(chalk.green(" ā Playwright MCP installed globally"));
|
|
112
|
+
console.log(chalk.gray(" (Restart Claude Code to activate)"));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
console.log(chalk.yellow(" ā Failed to install Playwright MCP"));
|
|
116
|
+
console.log(chalk.gray(" Run manually: claude mcp add playwright npx @playwright/mcp@latest"));
|
|
117
|
+
console.log(chalk.gray(" (Restart Claude Code to activate the MCP server)"));
|
|
87
118
|
}
|
|
88
119
|
}
|
|
89
120
|
}
|
|
90
121
|
// 3. Sync Healer tools (Claude Code only)
|
|
91
|
-
if (existsSync(join(projectRoot,
|
|
92
|
-
console.log(chalk.blue(
|
|
93
|
-
const skillDest = join(projectRoot,
|
|
122
|
+
if (existsSync(join(projectRoot, ".claude", "skills", "openspec-e2e", "SKILL.md"))) {
|
|
123
|
+
console.log(chalk.blue("\nāāā Syncing Healer Tools āāā"));
|
|
124
|
+
const skillDest = join(projectRoot, ".claude", "skills", "openspec-e2e", "SKILL.md");
|
|
94
125
|
await syncMcpTools(skillDest, true);
|
|
95
126
|
}
|
|
96
127
|
// Summary
|
|
97
|
-
console.log(chalk.blue(
|
|
98
|
-
console.log(chalk.green(
|
|
99
|
-
if (existsSync(join(projectRoot,
|
|
100
|
-
console.log(chalk.bold(
|
|
101
|
-
console.log(chalk.gray(
|
|
128
|
+
console.log(chalk.blue("\nāāā Summary āāā"));
|
|
129
|
+
console.log(chalk.green(" ā Update complete!\n"));
|
|
130
|
+
if (existsSync(join(projectRoot, ".claude"))) {
|
|
131
|
+
console.log(chalk.bold("Restart Claude Code to use the updated skill."));
|
|
132
|
+
console.log(chalk.gray(" Then run /opsx:e2e <change-name> to verify.\n"));
|
|
102
133
|
}
|
|
103
134
|
else {
|
|
104
|
-
console.log(chalk.bold(
|
|
105
|
-
console.log(chalk.gray(
|
|
135
|
+
console.log(chalk.bold("Restart your AI coding assistant to use the updated commands."));
|
|
136
|
+
console.log(chalk.gray(" Then run openspec-pw run <change-name> to verify.\n"));
|
|
106
137
|
}
|
|
107
138
|
}
|
|
108
139
|
function installSchemaFrom(schemaSrc, projectRoot) {
|
|
109
|
-
const schemaDest = join(projectRoot,
|
|
140
|
+
const schemaDest = join(projectRoot, "openspec", "schemas", "playwright-e2e");
|
|
110
141
|
mkdirSync(schemaDest, { recursive: true });
|
|
111
|
-
const schemaYamlSrc = join(schemaSrc,
|
|
142
|
+
const schemaYamlSrc = join(schemaSrc, "schema.yaml");
|
|
112
143
|
if (existsSync(schemaYamlSrc)) {
|
|
113
|
-
writeFileSync(join(schemaDest,
|
|
144
|
+
writeFileSync(join(schemaDest, "schema.yaml"), readFileSync(schemaYamlSrc));
|
|
114
145
|
}
|
|
115
|
-
const templatesSrc = join(schemaSrc,
|
|
116
|
-
const templatesDest = join(schemaDest,
|
|
146
|
+
const templatesSrc = join(schemaSrc, "templates");
|
|
147
|
+
const templatesDest = join(schemaDest, "templates");
|
|
117
148
|
if (existsSync(templatesSrc)) {
|
|
118
149
|
mkdirSync(templatesDest, { recursive: true });
|
|
119
|
-
const templateFiles = [
|
|
150
|
+
const templateFiles = [
|
|
151
|
+
"test-plan.md",
|
|
152
|
+
"report.md",
|
|
153
|
+
"e2e-test.ts",
|
|
154
|
+
"playwright.config.ts",
|
|
155
|
+
"app-knowledge.md",
|
|
156
|
+
];
|
|
120
157
|
for (const file of templateFiles) {
|
|
121
158
|
const src = join(templatesSrc, file);
|
|
122
159
|
const dest = join(templatesDest, file);
|
|
@@ -125,6 +162,6 @@ function installSchemaFrom(schemaSrc, projectRoot) {
|
|
|
125
162
|
}
|
|
126
163
|
}
|
|
127
164
|
}
|
|
128
|
-
console.log(chalk.green(
|
|
165
|
+
console.log(chalk.green(" ā Schema updated: openspec/schemas/playwright-e2e/"));
|
|
129
166
|
}
|
|
130
167
|
//# sourceMappingURL=update.js.map
|