openspec-playwright 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.claude/commands/opsx/e2e.md +8 -0
  2. package/.claude/skills/openspec-e2e/SKILL.md +138 -0
  3. package/.github/workflows/release.yml +41 -0
  4. package/README.md +133 -0
  5. package/README.zh-CN.md +118 -0
  6. package/bin/openspec-pw +4 -0
  7. package/bin/openspec-pw.js +2 -0
  8. package/dist/commands/doctor.d.ts +1 -0
  9. package/dist/commands/doctor.js +110 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/init.d.ts +6 -0
  12. package/dist/commands/init.js +174 -0
  13. package/dist/commands/init.js.map +1 -0
  14. package/dist/commands/run.d.ts +5 -0
  15. package/dist/commands/run.js +135 -0
  16. package/dist/commands/run.js.map +1 -0
  17. package/dist/commands/update.d.ts +5 -0
  18. package/dist/commands/update.js +91 -0
  19. package/dist/commands/update.js.map +1 -0
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.js +40 -0
  22. package/dist/index.js.map +1 -0
  23. package/docs/plans/2026-03-26-openspec-playwright-design.md +180 -0
  24. package/package.json +39 -0
  25. package/schemas/playwright-e2e/schema.yaml +56 -0
  26. package/schemas/playwright-e2e/templates/e2e-test.ts +55 -0
  27. package/schemas/playwright-e2e/templates/playwright.config.ts +52 -0
  28. package/schemas/playwright-e2e/templates/report.md +27 -0
  29. package/schemas/playwright-e2e/templates/test-plan.md +24 -0
  30. package/src/commands/doctor.ts +114 -0
  31. package/src/commands/init.ts +209 -0
  32. package/src/commands/run.ts +172 -0
  33. package/src/commands/update.ts +130 -0
  34. package/src/index.ts +47 -0
  35. package/templates/auth.setup.ts +77 -0
  36. package/templates/credentials.yaml +33 -0
  37. package/templates/seed.spec.ts +63 -0
  38. package/tsconfig.json +18 -0
@@ -0,0 +1,174 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, } from 'fs';
3
+ import { join } from 'path';
4
+ import chalk from 'chalk';
5
+ import { readFile } from 'fs/promises';
6
+ const TEMPLATE_DIR = new URL('../../templates', import.meta.url).pathname;
7
+ const SCHEMA_DIR = new URL('../../schemas', import.meta.url).pathname;
8
+ const SKILL_SRC = new URL('../../.claude/skills/openspec-e2e', import.meta.url).pathname;
9
+ const CMD_SRC = new URL('../../.claude/commands/opsx/e2e.md', import.meta.url).pathname;
10
+ export async function init(options) {
11
+ console.log(chalk.blue('\nšŸ”§ OpenSpec + Playwright E2E Setup\n'));
12
+ const projectRoot = process.cwd();
13
+ // 1. Check prerequisites
14
+ console.log(chalk.blue('─── Prerequisites ───'));
15
+ const hasNode = execCmd('node --version', 'Node.js', true);
16
+ const hasNpm = execCmd('npm --version', 'npm', true);
17
+ const hasOpenspec = execCmd('npx openspec --version 2>/dev/null || echo "not found"', 'OpenSpec', true);
18
+ if (!hasNode || !hasNpm) {
19
+ console.log(chalk.red(' āœ— Node.js/npm is required'));
20
+ process.exit(1);
21
+ }
22
+ console.log(chalk.green(' āœ“ Node.js and npm found'));
23
+ // 2. Check OpenSpec
24
+ if (!existsSync(join(projectRoot, 'openspec'))) {
25
+ console.log(chalk.yellow('\n⚠ OpenSpec not initialized. Run these commands first:'));
26
+ console.log(chalk.gray(' npm install -g @fission-ai/openspec'));
27
+ console.log(chalk.gray(' openspec init'));
28
+ console.log(chalk.gray(' openspec config profile core'));
29
+ console.log(chalk.gray(' openspec update\n'));
30
+ console.log(chalk.gray(' Then run openspec-pw init again.\n'));
31
+ return;
32
+ }
33
+ console.log(chalk.green(' āœ“ OpenSpec initialized'));
34
+ // 3. Install Playwright MCP (global)
35
+ if (options.mcp !== false) {
36
+ console.log(chalk.blue('\n─── Installing Playwright MCP ───'));
37
+ // Check if playwright MCP already exists in global config
38
+ const claudeJsonPath = join(process.env.HOME ?? '', '.claude.json');
39
+ const claudeJson = existsSync(claudeJsonPath) ? JSON.parse(readFileSync(claudeJsonPath, 'utf-8')) : {};
40
+ const globalMcp = claudeJson?.mcpServers ?? {};
41
+ const localMcp = claudeJson?.projects?.[projectRoot]?.mcpServers ?? {};
42
+ if (globalMcp['playwright'] || localMcp['playwright']) {
43
+ console.log(chalk.green(' āœ“ Playwright MCP already installed'));
44
+ }
45
+ else {
46
+ try {
47
+ execSync('claude mcp add playwright npx @playwright/mcp@latest', {
48
+ cwd: projectRoot,
49
+ stdio: 'inherit',
50
+ });
51
+ console.log(chalk.green(' āœ“ Playwright MCP installed globally'));
52
+ console.log(chalk.gray(' (Restart Claude Code to activate)'));
53
+ }
54
+ catch {
55
+ console.log(chalk.yellow(' ⚠ Failed to run claude mcp add'));
56
+ console.log(chalk.gray(' Run manually: claude mcp add playwright npx @playwright/mcp@latest'));
57
+ console.log(chalk.gray(' (Restart Claude Code to activate the MCP server)'));
58
+ }
59
+ }
60
+ }
61
+ // 4. Copy skill files
62
+ console.log(chalk.blue('\n─── Installing Claude Code Skill ───'));
63
+ await installSkill(projectRoot);
64
+ // 5. Install OpenSpec schema
65
+ console.log(chalk.blue('\n─── Installing OpenSpec Schema ───'));
66
+ await installSchema(projectRoot);
67
+ // 6. Generate seed test
68
+ if (options.seed !== false) {
69
+ console.log(chalk.blue('\n─── Generating Seed Test ───'));
70
+ await generateSeedTest(projectRoot);
71
+ }
72
+ // 7. Summary
73
+ console.log(chalk.blue('\n─── Summary ───'));
74
+ console.log(chalk.green(' āœ“ Setup complete!\n'));
75
+ console.log(chalk.bold('Next steps:'));
76
+ console.log(chalk.gray(' 1. Install Playwright browsers: npx playwright install --with-deps'));
77
+ console.log(chalk.gray(' 2. Customize tests/playwright/credentials.yaml with your test user'));
78
+ console.log(chalk.gray(' 3. Set credentials: export E2E_USERNAME=xxx E2E_PASSWORD=yyy'));
79
+ console.log(chalk.gray(' 4. Run auth setup: npx playwright test --project=setup'));
80
+ console.log(chalk.gray(' 5. In Claude Code, run: /opsx:e2e <change-name>'));
81
+ console.log(chalk.gray(' 6. Or: openspec-pw doctor to verify setup\n'));
82
+ console.log(chalk.bold('How it works:'));
83
+ console.log(chalk.gray(' /opsx:e2e reads your OpenSpec specs and runs Playwright'));
84
+ console.log(chalk.gray(' E2E tests through a three-agent pipeline:'));
85
+ console.log(chalk.gray(' Planner → Generator → Healer\n'));
86
+ }
87
+ async function installSkill(projectRoot) {
88
+ const skillsDir = join(projectRoot, '.claude', 'skills');
89
+ const skillDir = join(skillsDir, 'openspec-e2e');
90
+ const cmdDir = join(projectRoot, '.claude', 'commands');
91
+ // Copy skill
92
+ mkdirSync(skillDir, { recursive: true });
93
+ const skillContent = await readFile(SKILL_SRC + '/SKILL.md', 'utf-8');
94
+ writeFileSync(join(skillDir, 'SKILL.md'), skillContent);
95
+ console.log(chalk.green(` āœ“ Skill installed: /openspec-e2e`));
96
+ // Copy command
97
+ mkdirSync(join(cmdDir, 'opsx'), { recursive: true });
98
+ const cmdContent = await readFile(CMD_SRC, 'utf-8');
99
+ writeFileSync(join(cmdDir, 'opsx', 'e2e.md'), cmdContent);
100
+ console.log(chalk.green(` āœ“ Command installed: /opsx:e2e`));
101
+ }
102
+ async function generateSeedTest(projectRoot) {
103
+ const testsDir = join(projectRoot, 'tests', 'playwright');
104
+ mkdirSync(testsDir, { recursive: true });
105
+ const seedPath = join(testsDir, 'seed.spec.ts');
106
+ if (existsSync(seedPath)) {
107
+ console.log(chalk.gray(' - seed.spec.ts already exists, skipping'));
108
+ }
109
+ else {
110
+ const seedContent = await readFile(TEMPLATE_DIR + '/seed.spec.ts', 'utf-8');
111
+ writeFileSync(seedPath, seedContent);
112
+ console.log(chalk.green(' āœ“ Generated: tests/playwright/seed.spec.ts'));
113
+ }
114
+ // Generate auth.setup.ts
115
+ const authSetupPath = join(testsDir, 'auth.setup.ts');
116
+ if (existsSync(authSetupPath)) {
117
+ console.log(chalk.gray(' - auth.setup.ts already exists, skipping'));
118
+ }
119
+ else {
120
+ const authContent = await readFile(TEMPLATE_DIR + '/auth.setup.ts', 'utf-8');
121
+ writeFileSync(authSetupPath, authContent);
122
+ console.log(chalk.green(' āœ“ Generated: tests/playwright/auth.setup.ts'));
123
+ }
124
+ // Generate credentials.yaml
125
+ const credsPath = join(testsDir, 'credentials.yaml');
126
+ if (existsSync(credsPath)) {
127
+ console.log(chalk.gray(' - credentials.yaml already exists, skipping'));
128
+ }
129
+ else {
130
+ const credsContent = await readFile(TEMPLATE_DIR + '/credentials.yaml', 'utf-8');
131
+ writeFileSync(credsPath, credsContent);
132
+ console.log(chalk.green(' āœ“ Generated: tests/playwright/credentials.yaml'));
133
+ }
134
+ console.log(chalk.gray(' (Customize BASE_URL and credentials for your app)'));
135
+ }
136
+ async function installSchema(projectRoot) {
137
+ const schemaSrc = SCHEMA_DIR + '/playwright-e2e';
138
+ const schemaDest = join(projectRoot, 'openspec', 'schemas', 'playwright-e2e');
139
+ const schemaFiles = ['schema.yaml'];
140
+ for (const file of schemaFiles) {
141
+ const src = join(schemaSrc, file);
142
+ const dest = join(schemaDest, file);
143
+ if (existsSync(src)) {
144
+ writeFileSync(dest, readFileSync(src));
145
+ }
146
+ }
147
+ // Copy templates
148
+ const templatesSrc = join(schemaSrc, 'templates');
149
+ const templatesDest = join(schemaDest, 'templates');
150
+ mkdirSync(templatesDest, { recursive: true });
151
+ const templateFiles = ['test-plan.md', 'report.md', 'e2e-test.ts', 'playwright.config.ts'];
152
+ for (const file of templateFiles) {
153
+ const src = join(templatesSrc, file);
154
+ const dest = join(templatesDest, file);
155
+ if (existsSync(src)) {
156
+ writeFileSync(dest, readFileSync(src));
157
+ }
158
+ }
159
+ console.log(chalk.green(' āœ“ Schema installed: openspec/schemas/playwright-e2e/'));
160
+ }
161
+ function execCmd(cmd, name, silent = false) {
162
+ try {
163
+ execSync(cmd, { stdio: 'pipe' });
164
+ if (!silent)
165
+ console.log(chalk.green(` āœ“ ${name} found`));
166
+ return true;
167
+ }
168
+ catch {
169
+ if (!silent)
170
+ console.log(chalk.yellow(` ⚠ ${name} not found`));
171
+ return false;
172
+ }
173
+ }
174
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export interface RunOptions {
2
+ project?: string;
3
+ timeout?: number;
4
+ }
5
+ export declare function run(changeName: string, options: RunOptions): Promise<void>;
@@ -0,0 +1,135 @@
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
+ export async function run(changeName, options) {
7
+ console.log(chalk.blue(`\nšŸ” OpenSpec Playwright E2E: ${changeName}\n`));
8
+ const projectRoot = process.cwd();
9
+ // 1. Verify test file exists
10
+ const testFile = join(projectRoot, 'tests', 'playwright', `${changeName}.spec.ts`);
11
+ if (!existsSync(testFile)) {
12
+ console.log(chalk.red(` āœ— Test file not found: tests/playwright/${changeName}.spec.ts`));
13
+ console.log(chalk.gray(' Run /opsx:e2e first to generate tests.\n'));
14
+ process.exit(1);
15
+ }
16
+ // 2. Setup reports dir
17
+ mkdirSync(join(projectRoot, REPORTS_DIR), { recursive: true });
18
+ // 3. Detect auth credentials
19
+ const credsPath = join(projectRoot, 'tests', 'playwright', 'credentials.yaml');
20
+ const hasCredentials = existsSync(credsPath);
21
+ if (!hasCredentials) {
22
+ console.log(chalk.yellow(' ⚠ No credentials.yaml found — tests may fail if auth required'));
23
+ }
24
+ // 4. Run Playwright tests with output capture
25
+ console.log(chalk.blue('─── Running Tests ───'));
26
+ const args = ['npx', 'playwright', 'test', testFile, '--reporter=list'];
27
+ if (options.project) {
28
+ args.push('--project=' + options.project);
29
+ }
30
+ let testOutput = '';
31
+ let exitCode = 0;
32
+ try {
33
+ // Capture stdout to detect port mismatch
34
+ const result = execSync(args.join(' '), {
35
+ cwd: projectRoot,
36
+ encoding: 'utf-8',
37
+ stdio: ['pipe', 'pipe', 'pipe'],
38
+ timeout: (options.timeout ?? 300) * 1000,
39
+ });
40
+ testOutput = result;
41
+ }
42
+ catch (err) {
43
+ exitCode = 1;
44
+ const error = err;
45
+ testOutput = (error.stdout ?? '') + (error.stderr ?? '');
46
+ }
47
+ // 5. Parse results from Playwright output
48
+ const results = parsePlaywrightOutput(testOutput);
49
+ // 6. Detect port mismatch
50
+ if (testOutput.includes('net::ERR_CONNECTION_REFUSED') ||
51
+ testOutput.includes('listen EADDRINUSE') ||
52
+ testOutput.includes('0.0.0.0:')) {
53
+ console.log(chalk.yellow(' ⚠ Port mismatch detected. Check BASE_URL and webServer port.'));
54
+ }
55
+ // 7. Generate markdown report
56
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
57
+ const reportPath = join(projectRoot, REPORTS_DIR, `playwright-e2e-${changeName}-${timestamp}.md`);
58
+ const reportContent = generateReport(changeName, timestamp, results);
59
+ writeFileSync(reportPath, reportContent);
60
+ // 8. Summary
61
+ console.log(chalk.blue('\n─── Results ───'));
62
+ console.log(` Tests: ${results.total} ` +
63
+ chalk.green(`āœ“ ${results.passed}`) + ' ' +
64
+ (results.failed > 0 ? chalk.red(`āœ— ${results.failed}`) : chalk.gray(`āœ— ${results.failed}`)) +
65
+ ` Duration: ${results.duration}`);
66
+ console.log(chalk.blue('\n─── Report ───'));
67
+ console.log(chalk.green(` āœ“ ${reportPath}\n`));
68
+ if (results.failed > 0) {
69
+ console.log(chalk.red(`āœ— E2E verification FAILED (${results.failed} tests)`));
70
+ process.exit(1);
71
+ }
72
+ else {
73
+ console.log(chalk.green('āœ“ E2E verification PASSED'));
74
+ }
75
+ }
76
+ function parsePlaywrightOutput(output) {
77
+ const results = { total: 0, passed: 0, failed: 0, duration: '0s', tests: [] };
78
+ // Parse: "āœ“ my-test (1.2s)" or "āœ— my-test (0.5s)"
79
+ const testLineRegex = /([āœ“āœ—x]) (.+?) \((\d+(?:\.\d+)?[a-z]+)\)/g;
80
+ let match;
81
+ while ((match = testLineRegex.exec(output)) !== null) {
82
+ const status = match[1] === 'āœ“' ? 'passed' : 'failed';
83
+ results.tests.push({ name: match[2], status });
84
+ results.total++;
85
+ if (status === 'passed')
86
+ results.passed++;
87
+ else
88
+ results.failed++;
89
+ }
90
+ // Parse duration: "N tests ran (1m 30s)" or "1 test ran (5s)"
91
+ const durationMatch = output.match(/\d+ tests? ran \((\d+(?:m\s*\d+)?s?)\)/);
92
+ if (durationMatch)
93
+ results.duration = durationMatch[1];
94
+ return results;
95
+ }
96
+ function generateReport(changeName, timestamp, results) {
97
+ const lines = [
98
+ `# E2E Verify Report: ${changeName}`,
99
+ '',
100
+ `**Change**: \`${changeName}\``,
101
+ `**Generated**: ${timestamp.replace('T', ' ').slice(0, 16)} UTC`,
102
+ '',
103
+ '## Summary',
104
+ '',
105
+ '| Check | Status |',
106
+ '|-------|--------|',
107
+ `| Tests Run | ${results.total} |`,
108
+ `| Passed | ${results.passed} |`,
109
+ `| Failed | ${results.failed} |`,
110
+ `| Duration | ${results.duration} |`,
111
+ `| Final Status | ${results.failed === 0 ? 'āœ… PASS' : 'āŒ FAIL'} |`,
112
+ '',
113
+ '## Test Results',
114
+ '',
115
+ ];
116
+ if (results.tests.length === 0) {
117
+ lines.push('_(No test output captured — check Playwright configuration)_', '');
118
+ }
119
+ else {
120
+ for (const test of results.tests) {
121
+ const icon = test.status === 'passed' ? 'āœ…' : 'āŒ';
122
+ lines.push(`- ${test.name}: ${icon} ${test.status}`);
123
+ }
124
+ lines.push('');
125
+ }
126
+ lines.push('## Recommendations', '');
127
+ if (results.failed > 0) {
128
+ 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
+ }
130
+ else {
131
+ lines.push('All tests passed. No action needed.', '');
132
+ }
133
+ return lines.join('\n');
134
+ }
135
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export interface UpdateOptions {
2
+ cli?: boolean;
3
+ skill?: boolean;
4
+ }
5
+ export declare function update(options: UpdateOptions): Promise<void>;
@@ -0,0 +1,91 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, } from 'fs';
3
+ import { join } from 'path';
4
+ import chalk from 'chalk';
5
+ const SKILL_SRC = new URL('../../.claude/skills/openspec-e2e', import.meta.url).pathname;
6
+ const CMD_SRC = new URL('../../.claude/commands/opsx/e2e.md', import.meta.url).pathname;
7
+ const SCHEMA_DIR = new URL('../../schemas', import.meta.url).pathname;
8
+ export async function update(options) {
9
+ console.log(chalk.blue('\nšŸ”„ Updating OpenSpec + Playwright E2E\n'));
10
+ const projectRoot = process.cwd();
11
+ // 1. Update CLI tool (from git latest, not npm)
12
+ if (options.cli !== false) {
13
+ console.log(chalk.blue('─── Updating CLI ───'));
14
+ try {
15
+ execSync('npm install -g https://github.com/wxhou/openspec-playwright/archive/refs/heads/main.tar.gz', { stdio: 'inherit' });
16
+ console.log(chalk.green(' āœ“ CLI updated to latest commit'));
17
+ }
18
+ catch {
19
+ console.log(chalk.yellow(' ⚠ Failed to update CLI'));
20
+ console.log(chalk.gray(' Run manually: npm install -g https://github.com/wxhou/openspec-playwright/archive/refs/heads/main.tar.gz'));
21
+ }
22
+ }
23
+ // 2. Update skill and command from git tarball (latest commit, not npm package)
24
+ if (options.skill !== false) {
25
+ console.log(chalk.blue('\n─── Updating Skill & Command ───'));
26
+ try {
27
+ // Download tarball to temp file and extract
28
+ const tmpSkill = '/tmp/openspec-e2e-skill.tar.gz';
29
+ const tmpDir = '/tmp/openspec-e2e-update';
30
+ execSync(`curl -sL https://github.com/wxhou/openspec-playwright/archive/refs/heads/main.tar.gz -o ${tmpSkill}`, { stdio: 'pipe' });
31
+ // Clean and re-extract to avoid stale files from previous runs
32
+ execSync(`rm -rf ${tmpDir} && mkdir -p ${tmpDir} && tar -xzf ${tmpSkill} -C ${tmpDir} --strip-components=1`, { stdio: 'pipe' });
33
+ const skillSrc = `${tmpDir}/.claude/skills/openspec-e2e/SKILL.md`;
34
+ const cmdSrc = `${tmpDir}/.claude/commands/opsx/e2e.md`;
35
+ const schemaSrc = `${tmpDir}/schemas/playwright-e2e`;
36
+ installSkillFrom(skillSrc, cmdSrc, schemaSrc, projectRoot);
37
+ console.log(chalk.green(' āœ“ Skill & command updated to latest'));
38
+ }
39
+ catch {
40
+ console.log(chalk.yellow(' ⚠ Failed to update skill/command from git'));
41
+ console.log(chalk.gray(' Running from npm package instead...'));
42
+ installSkill(projectRoot);
43
+ }
44
+ }
45
+ // Summary
46
+ console.log(chalk.blue('\n─── Summary ───'));
47
+ console.log(chalk.green(' āœ“ Update complete!\n'));
48
+ console.log(chalk.bold('Restart Claude Code to use the updated skill.'));
49
+ console.log(chalk.gray(' Then run /opsx:e2e <change-name> to verify.\n'));
50
+ }
51
+ function installSkill(projectRoot) {
52
+ installSkillFrom(SKILL_SRC, CMD_SRC, SCHEMA_DIR + '/playwright-e2e', projectRoot);
53
+ }
54
+ function installSkillFrom(skillSrc, cmdSrc, schemaSrc, projectRoot) {
55
+ const skillDir = join(projectRoot, '.claude', 'skills', 'openspec-e2e');
56
+ const cmdDir = join(projectRoot, '.claude', 'commands');
57
+ mkdirSync(skillDir, { recursive: true });
58
+ const skillContent = readFileSync(skillSrc, 'utf-8');
59
+ writeFileSync(join(skillDir, 'SKILL.md'), skillContent);
60
+ console.log(chalk.green(` āœ“ Skill updated: /openspec-e2e`));
61
+ mkdirSync(join(cmdDir, 'opsx'), { recursive: true });
62
+ const cmdContent = readFileSync(cmdSrc, 'utf-8');
63
+ writeFileSync(join(cmdDir, 'opsx', 'e2e.md'), cmdContent);
64
+ console.log(chalk.green(` āœ“ Command updated: /opsx:e2e`));
65
+ installSchemaFrom(schemaSrc, projectRoot);
66
+ }
67
+ function installSchemaFrom(schemaSrc, projectRoot) {
68
+ const schemaDest = join(projectRoot, 'openspec', 'schemas', 'playwright-e2e');
69
+ mkdirSync(schemaDest, { recursive: true });
70
+ // Copy schema.yaml
71
+ const schemaYamlSrc = join(schemaSrc, 'schema.yaml');
72
+ if (existsSync(schemaYamlSrc)) {
73
+ writeFileSync(join(schemaDest, 'schema.yaml'), readFileSync(schemaYamlSrc));
74
+ }
75
+ // Copy templates
76
+ const templatesSrc = join(schemaSrc, 'templates');
77
+ const templatesDest = join(schemaDest, 'templates');
78
+ if (existsSync(templatesSrc)) {
79
+ mkdirSync(templatesDest, { recursive: true });
80
+ const templateFiles = ['test-plan.md', 'report.md', 'e2e-test.ts', 'playwright.config.ts'];
81
+ for (const file of templateFiles) {
82
+ const src = join(templatesSrc, file);
83
+ const dest = join(templatesDest, file);
84
+ if (existsSync(src)) {
85
+ writeFileSync(dest, readFileSync(src));
86
+ }
87
+ }
88
+ }
89
+ console.log(chalk.green(' āœ“ Schema updated: openspec/schemas/playwright-e2e/'));
90
+ }
91
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,UAAU,EACV,YAAY,EACZ,aAAa,EACb,SAAS,GACV,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,mCAAmC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;AACzF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,oCAAoC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;AACxF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;AAOtE,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,OAAsB;IACjD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC,CAAC;IAErE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAElC,gDAAgD;IAChD,IAAI,OAAO,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC;QAChD,IAAI,CAAC;YACH,QAAQ,CACN,4FAA4F,EAC5F,EAAE,KAAK,EAAE,SAAS,EAAE,CACrB,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,4GAA4G,CAAC,CACzH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,gFAAgF;IAChF,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,4CAA4C;YAC5C,MAAM,QAAQ,GAAG,gCAAgC,CAAC;YAClD,MAAM,MAAM,GAAG,0BAA0B,CAAC;YAE1C,QAAQ,CACN,2FAA2F,QAAQ,EAAE,EACrG,EAAE,KAAK,EAAE,MAAM,EAAE,CAClB,CAAC;YACF,+DAA+D;YAC/D,QAAQ,CAAC,UAAU,MAAM,gBAAgB,MAAM,gBAAgB,QAAQ,OAAO,MAAM,uBAAuB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAEhI,MAAM,QAAQ,GAAG,GAAG,MAAM,uCAAuC,CAAC;YAClE,MAAM,MAAM,GAAG,GAAG,MAAM,+BAA+B,CAAC;YACxD,MAAM,SAAS,GAAG,GAAG,MAAM,yBAAyB,CAAC;YAErD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;YAC3D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,6CAA6C,CAAC,CAAC,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC,CAAC;YACjE,YAAY,CAAC,WAAW,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,UAAU;IACV,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAEnD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,YAAY,CAAC,WAAmB;IACvC,gBAAgB,CACd,SAAS,EACT,OAAO,EACP,UAAU,GAAG,iBAAiB,EAC9B,WAAW,CACZ,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAgB,EAAE,MAAc,EAAE,SAAiB,EAAE,WAAmB;IAChG,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAExD,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACrD,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC;IAE7D,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;IAE3D,iBAAiB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAiB,EAAE,WAAmB;IAC/D,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;IAE9E,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,mBAAmB;IACnB,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IACrD,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED,iBAAiB;IACjB,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IACpD,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,aAAa,GAAG,CAAC,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,sBAAsB,CAAC,CAAC;QAC3F,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;YACrC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;YACvC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpB,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,40 @@
1
+ import { readFileSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { Command } from 'commander';
5
+ import { init } from './commands/init.js';
6
+ import { update } from './commands/update.js';
7
+ import { doctor } from './commands/doctor.js';
8
+ import { run } from './commands/run.js';
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
11
+ const program = new Command();
12
+ program
13
+ .name('openspec-pw')
14
+ .description('OpenSpec + Playwright E2E verification setup tool')
15
+ .version(pkg.version);
16
+ program
17
+ .command('init')
18
+ .description('Initialize OpenSpec + Playwright E2E integration in the current project')
19
+ .option('-c, --change <name>', 'default change name', 'default')
20
+ .option('--no-mcp', 'skip Playwright MCP configuration')
21
+ .option('--no-seed', 'skip seed test generation')
22
+ .action(init);
23
+ program
24
+ .command('doctor')
25
+ .description('Check if all prerequisites are installed')
26
+ .action(doctor);
27
+ program
28
+ .command('update')
29
+ .description('Update the CLI tool and skill to the latest version')
30
+ .option('--no-cli', 'skip CLI update')
31
+ .option('--no-skill', 'skip skill/command update')
32
+ .action(update);
33
+ program
34
+ .command('run <change-name>')
35
+ .description('Run Playwright E2E tests for an OpenSpec change')
36
+ .option('-p, --project <name>', 'Playwright project to run (e.g., user, admin)')
37
+ .option('-t, --timeout <seconds>', 'Test timeout in seconds', '300')
38
+ .action(run);
39
+ program.parse();
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,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;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAExC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAElF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,yEAAyE,CAAC;KACtF,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,SAAS,CAAC;KAC/D,MAAM,CAAC,UAAU,EAAE,mCAAmC,CAAC;KACvD,MAAM,CAAC,WAAW,EAAE,2BAA2B,CAAC;KAChD,MAAM,CAAC,IAAI,CAAC,CAAC;AAEhB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,MAAM,CAAC,CAAC;AAElB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,qDAAqD,CAAC;KAClE,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC;KACrC,MAAM,CAAC,YAAY,EAAE,2BAA2B,CAAC;KACjD,MAAM,CAAC,MAAM,CAAC,CAAC;AAElB,OAAO;KACJ,OAAO,CAAC,mBAAmB,CAAC;KAC5B,WAAW,CAAC,iDAAiD,CAAC;KAC9D,MAAM,CAAC,sBAAsB,EAAE,+CAA+C,CAAC;KAC/E,MAAM,CAAC,yBAAyB,EAAE,yBAAyB,EAAE,KAAK,CAAC;KACnE,MAAM,CAAC,GAAG,CAAC,CAAC;AAEf,OAAO,CAAC,KAAK,EAAE,CAAC"}