openspec-playwright 0.1.27 → 0.1.29
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/.claude/skills/openspec-e2e/SKILL.md +103 -18
- package/dist/commands/init.js +8 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcpSync.d.ts +27 -0
- package/dist/commands/mcpSync.js +124 -0
- package/dist/commands/mcpSync.js.map +1 -0
- package/dist/commands/update.js +5 -4
- package/dist/commands/update.js.map +1 -1
- package/openspec-playwright-0.1.29.tgz +0 -0
- package/package.json +1 -1
- package/release-notes.md +2 -2
- package/src/commands/init.ts +9 -3
- package/src/commands/mcpSync.ts +133 -0
- package/src/commands/update.ts +6 -4
- package/openspec-playwright-0.1.27.tgz +0 -0
|
@@ -7,12 +7,33 @@ compatibility: Requires openspec CLI, Playwright (with browsers installed), and
|
|
|
7
7
|
**Architecture**: Uses CLI + SKILLs (not `init-agents`). This follows Playwright's recommended approach for coding agents — CLI is more token-efficient than loading MCP tool schemas into context. MCP is used only for Healer (UI inspection on failure).
|
|
8
8
|
metadata:
|
|
9
9
|
author: openspec-playwright
|
|
10
|
-
version: "2.
|
|
10
|
+
version: "2.4"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Input
|
|
14
14
|
|
|
15
|
-
**
|
|
15
|
+
- **Change name**: `/opsx:e2e <name>` or auto-detected from context
|
|
16
|
+
- **Specs**: `openspec/changes/<name>/specs/*.md`
|
|
17
|
+
- **Credentials**: `E2E_USERNAME` + `E2E_PASSWORD` env vars
|
|
18
|
+
|
|
19
|
+
## Output
|
|
20
|
+
|
|
21
|
+
- **Test file**: `tests/playwright/<name>.spec.ts`
|
|
22
|
+
- **Auth setup**: `tests/playwright/auth.setup.ts` (if auth required)
|
|
23
|
+
- **Report**: `openspec/reports/playwright-e2e-<name>-<timestamp>.md`
|
|
24
|
+
- **Test plan**: `openspec/changes/<name>/specs/playwright/test-plan.md`
|
|
25
|
+
|
|
26
|
+
## Architecture
|
|
27
|
+
|
|
28
|
+
This skill implements the Playwright Test Agents pipeline:
|
|
29
|
+
|
|
30
|
+
- **🎭 Planner** (Step 4): Consumes OpenSpec specs (`specs/*.md`) and produces `test-plan.md` — combines OpenSpec's structured requirements with LLM编排.
|
|
31
|
+
- **🎭 Generator** (Step 5): Transforms the Markdown test-plan into real Playwright `.spec.ts` files using LLM code generation.
|
|
32
|
+
- **🎭 Healer** (Step 8): Executes the test suite and automatically repairs failing selectors via Playwright MCP tools.
|
|
33
|
+
|
|
34
|
+
Uses CLI + SKILLs (not `init-agents`). This follows Playwright's recommended approach for coding agents — CLI is more token-efficient than loading MCP tool schemas into context. MCP is used only for Healer (UI inspection on failure).
|
|
35
|
+
|
|
36
|
+
**Schema owns templates. CLI handles execution. Skill handles cognitive work.**
|
|
16
37
|
|
|
17
38
|
## Steps
|
|
18
39
|
|
|
@@ -48,7 +69,24 @@ Detect if auth is required. Mark as **auth required** only when BOTH conditions
|
|
|
48
69
|
- Medium (proceed with note): Single explicit marker, context unclear
|
|
49
70
|
- Low (skip auth): No explicit markers found
|
|
50
71
|
|
|
51
|
-
### 3.
|
|
72
|
+
### 3. Validate environment
|
|
73
|
+
|
|
74
|
+
Before generating tests, verify the environment is ready by running the seed test:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx playwright test tests/playwright/seed.spec.ts --project=chromium
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**What this validates**:
|
|
81
|
+
- App server is reachable (BASE_URL accessible)
|
|
82
|
+
- Auth fixtures (`storageState`) are initialized if auth is required
|
|
83
|
+
- Playwright browser and config are working
|
|
84
|
+
|
|
85
|
+
**If seed test fails**: Stop and report the failure. User must fix the environment before proceeding.
|
|
86
|
+
|
|
87
|
+
**If seed test passes or no auth required**: Proceed to Step 4.
|
|
88
|
+
|
|
89
|
+
### 4. Generate test plan
|
|
52
90
|
|
|
53
91
|
Create `openspec/changes/<name>/specs/playwright/test-plan.md` by:
|
|
54
92
|
- Listing each functional requirement as a test case
|
|
@@ -58,7 +96,7 @@ Create `openspec/changes/<name>/specs/playwright/test-plan.md` by:
|
|
|
58
96
|
|
|
59
97
|
**Idempotency**: If test-plan.md already exists → read it, use it, do NOT regenerate unless user explicitly asks.
|
|
60
98
|
|
|
61
|
-
###
|
|
99
|
+
### 5. Generate test file (LLM-driven)
|
|
62
100
|
|
|
63
101
|
Use your file writing capability to create `tests/playwright/<name>.spec.ts`.
|
|
64
102
|
|
|
@@ -100,7 +138,7 @@ test.describe('Feature Name', () => {
|
|
|
100
138
|
|
|
101
139
|
**Selector guidance**: If no `data-testid` exists in the app, prefer `getByRole`, `getByLabel`, `getByText` over fragile selectors like CSS paths.
|
|
102
140
|
|
|
103
|
-
###
|
|
141
|
+
### 6. Configure auth (if required)
|
|
104
142
|
|
|
105
143
|
If auth is required:
|
|
106
144
|
|
|
@@ -122,7 +160,7 @@ Auth required. To set up credentials:
|
|
|
122
160
|
|
|
123
161
|
**Idempotency**: If `auth.setup.ts` already exists → verify format, update only if stale.
|
|
124
162
|
|
|
125
|
-
###
|
|
163
|
+
### 7. Configure playwright.config.ts (non-destructive)
|
|
126
164
|
|
|
127
165
|
If `playwright.config.ts` does not exist → generate it from the schema template at `openspec/schemas/playwright-e2e/templates/playwright.config.ts`. The template auto-detects:
|
|
128
166
|
- **BASE_URL**: from `process.env.BASE_URL`, falling back to `tests/playwright/seed.spec.ts` → `BASE_URL` value, then `http://localhost:3000`
|
|
@@ -130,7 +168,7 @@ If `playwright.config.ts` does not exist → generate it from the schema templat
|
|
|
130
168
|
|
|
131
169
|
If `playwright.config.ts` exists → READ it first. Extract existing `webServer`, `use.baseURL`, and `projects`. Preserve ALL existing fields. Add `webServer` block if missing. Do NOT replace existing config values.
|
|
132
170
|
|
|
133
|
-
###
|
|
171
|
+
### 8. Execute tests via CLI
|
|
134
172
|
|
|
135
173
|
```bash
|
|
136
174
|
openspec-pw run <name> --project=<role>
|
|
@@ -145,19 +183,20 @@ The CLI handles:
|
|
|
145
183
|
If tests fail → analyze failures, use **Playwright MCP tools** to inspect UI state, fix selectors in the test file, and re-run.
|
|
146
184
|
|
|
147
185
|
**Healer MCP tools** (in order of use):
|
|
186
|
+
<!-- MCP_VERSION: 0.0.68 -->
|
|
148
187
|
|
|
149
188
|
| Tool | Purpose |
|
|
150
189
|
|------|---------|
|
|
151
|
-
| `
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
190
|
+
| `browser_navigate` | Go to the failing test's page |
|
|
191
|
+
| `browser_snapshot` | Get page structure to find equivalent selectors |
|
|
192
|
+
| `browser_console_messages` | Diagnose JS errors that may cause failures |
|
|
193
|
+
| `browser_take_screenshot` | Visually compare before/after fixes |
|
|
194
|
+
| `browser_run_code` | Execute custom fix logic (optional) |
|
|
156
195
|
|
|
157
196
|
**Healer workflow**:
|
|
158
197
|
1. Read the failing test file — identify the broken selector or assertion
|
|
159
|
-
2. Navigate to the target page with `
|
|
160
|
-
3. Take a `
|
|
198
|
+
2. Navigate to the target page with `browser_navigate`
|
|
199
|
+
3. Take a `browser_snapshot` — find an equivalent stable selector
|
|
161
200
|
4. Fix the selector in the test file using the Edit tool
|
|
162
201
|
5. Re-run: `openspec-pw run <name>`
|
|
163
202
|
6. Repeat until pass or 3 attempts reached
|
|
@@ -166,7 +205,7 @@ If tests fail → analyze failures, use **Playwright MCP tools** to inspect UI s
|
|
|
166
205
|
|
|
167
206
|
**Cap auto-heal attempts at 3** to prevent infinite loops.
|
|
168
207
|
|
|
169
|
-
###
|
|
208
|
+
### 9. Report results
|
|
170
209
|
|
|
171
210
|
Read the report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`.
|
|
172
211
|
|
|
@@ -181,9 +220,55 @@ Read the report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`.
|
|
|
181
220
|
- Append a verification note: e.g. `✅ Verified via Playwright E2E (<timestamp>)`
|
|
182
221
|
- Write the updated content back using the Edit tool
|
|
183
222
|
|
|
184
|
-
|
|
223
|
+
## Output Format
|
|
185
224
|
|
|
186
|
-
|
|
225
|
+
### Report Structure (`openspec/reports/playwright-e2e-<name>-<timestamp>.md`)
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
# Playwright E2E Report — <name>
|
|
229
|
+
|
|
230
|
+
## Summary
|
|
231
|
+
| Tests | Passed | Failed | Duration | Status |
|
|
232
|
+
|-------|--------|--------|----------|--------|
|
|
233
|
+
| N | N | N | Xm Xs | ✅/❌ |
|
|
234
|
+
|
|
235
|
+
## Results
|
|
236
|
+
|
|
237
|
+
### Passed
|
|
238
|
+
| Test | Duration | Notes |
|
|
239
|
+
|------|----------|-------|
|
|
240
|
+
| ... | ... | ... |
|
|
241
|
+
|
|
242
|
+
### Failed
|
|
243
|
+
| Test | Error | Recommendation |
|
|
244
|
+
|------|-------|----------------|
|
|
245
|
+
| ... | ... | file:line — fix |
|
|
246
|
+
|
|
247
|
+
## Auto-Heal Log
|
|
248
|
+
- Attempt N: selector fix → result
|
|
249
|
+
|
|
250
|
+
## Coverage
|
|
251
|
+
- [x] Requirement 1
|
|
252
|
+
- [ ] Requirement 2 (unverified)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Updated tasks.md
|
|
256
|
+
```
|
|
257
|
+
- [x] Implement feature X ✅ Verified via Playwright E2E (2026-03-28)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Graceful Degradation
|
|
261
|
+
|
|
262
|
+
| Scenario | Behavior |
|
|
263
|
+
|----------|----------|
|
|
264
|
+
| No specs found | Stop with info message — E2E requires specs |
|
|
265
|
+
| Seed test fails | Stop with failure — fix environment before proceeding |
|
|
266
|
+
| No auth required | Skip auth setup entirely |
|
|
267
|
+
| test-plan.md exists | Read and use it — never regenerate |
|
|
268
|
+
| auth.setup.ts exists | Verify format — update only if stale |
|
|
269
|
+
| playwright.config.ts exists | Read and preserve all fields — add only missing |
|
|
270
|
+
| Test fails (selector) | Healer: snapshot → fix → re-run, cap at 3 attempts |
|
|
271
|
+
| Test fails (app bug) | Report as failed with recommendation |
|
|
187
272
|
|
|
188
273
|
## Verification Heuristics
|
|
189
274
|
|
package/dist/commands/init.js
CHANGED
|
@@ -3,6 +3,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, } from 'fs';
|
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { readFile } from 'fs/promises';
|
|
6
|
+
import { syncMcpTools } from './mcpSync.js';
|
|
6
7
|
const TEMPLATE_DIR = new URL('../../templates', import.meta.url).pathname;
|
|
7
8
|
const SCHEMA_DIR = new URL('../../schemas', import.meta.url).pathname;
|
|
8
9
|
const SKILL_SRC = new URL('../../.claude/skills/openspec-e2e', import.meta.url).pathname;
|
|
@@ -61,15 +62,19 @@ export async function init(options) {
|
|
|
61
62
|
// 4. Copy skill files
|
|
62
63
|
console.log(chalk.blue('\n─── Installing Claude Code Skill ───'));
|
|
63
64
|
await installSkill(projectRoot);
|
|
64
|
-
// 5.
|
|
65
|
+
// 5. Sync Healer tools with latest @playwright/mcp
|
|
66
|
+
console.log(chalk.blue('\n─── Syncing Healer Tools ───'));
|
|
67
|
+
const skillDest = join(projectRoot, '.claude', 'skills', 'openspec-e2e', 'SKILL.md');
|
|
68
|
+
await syncMcpTools(skillDest, true);
|
|
69
|
+
// 6. Install OpenSpec schema
|
|
65
70
|
console.log(chalk.blue('\n─── Installing OpenSpec Schema ───'));
|
|
66
71
|
await installSchema(projectRoot);
|
|
67
|
-
//
|
|
72
|
+
// 7. Generate seed test
|
|
68
73
|
if (options.seed !== false) {
|
|
69
74
|
console.log(chalk.blue('\n─── Generating Seed Test ───'));
|
|
70
75
|
await generateSeedTest(projectRoot);
|
|
71
76
|
}
|
|
72
|
-
//
|
|
77
|
+
// 8. Summary
|
|
73
78
|
console.log(chalk.blue('\n─── Summary ───'));
|
|
74
79
|
console.log(chalk.green(' ✓ Setup complete!\n'));
|
|
75
80
|
console.log(chalk.bold('Next steps:'));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.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;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const MCP_VERSION_MARKER = "<!-- MCP_VERSION:";
|
|
2
|
+
export declare const DEFAULT_HEALER_TOOLS: {
|
|
3
|
+
name: string;
|
|
4
|
+
purpose: string;
|
|
5
|
+
}[];
|
|
6
|
+
/** Extract MCP version from SKILL.md marker */
|
|
7
|
+
export declare function getStoredMcpVersion(skillContent: string): string | null;
|
|
8
|
+
/** Replace the Healer tools table in SKILL.md */
|
|
9
|
+
export declare function updateHealerTable(skillContent: string, version: string, tools: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
purpose: string;
|
|
12
|
+
}>): string;
|
|
13
|
+
/** Fetch latest @playwright/mcp version from npm registry */
|
|
14
|
+
export declare function getLatestMcpVersion(): Promise<string | null>;
|
|
15
|
+
/**
|
|
16
|
+
* Fetch @playwright/mcp tools from npm package.
|
|
17
|
+
* Downloads the tarball, extracts README, parses tool names.
|
|
18
|
+
*/
|
|
19
|
+
export declare function fetchMcpTools(version: string): Promise<Array<{
|
|
20
|
+
name: string;
|
|
21
|
+
purpose: string;
|
|
22
|
+
}>>;
|
|
23
|
+
/**
|
|
24
|
+
* Sync Healer tools table in SKILL.md with latest @playwright/mcp.
|
|
25
|
+
* Returns true if updated, false if already current or failed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function syncMcpTools(skillDest: string, verbose?: boolean): Promise<boolean>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
export const MCP_VERSION_MARKER = '<!-- MCP_VERSION:';
|
|
5
|
+
export const DEFAULT_HEALER_TOOLS = [
|
|
6
|
+
{ name: 'browser_navigate', purpose: "Go to the failing test's page" },
|
|
7
|
+
{ name: 'browser_snapshot', purpose: 'Get page structure to find equivalent selectors' },
|
|
8
|
+
{ name: 'browser_console_messages', purpose: 'Diagnose JS errors that may cause failures' },
|
|
9
|
+
{ name: 'browser_take_screenshot', purpose: 'Visually compare before/after fixes' },
|
|
10
|
+
{ name: 'browser_run_code', purpose: 'Execute custom fix logic (optional)' },
|
|
11
|
+
];
|
|
12
|
+
/** Extract MCP version from SKILL.md marker */
|
|
13
|
+
export function getStoredMcpVersion(skillContent) {
|
|
14
|
+
const idx = skillContent.indexOf(MCP_VERSION_MARKER);
|
|
15
|
+
if (idx === -1)
|
|
16
|
+
return null;
|
|
17
|
+
const end = skillContent.indexOf(' -->', idx);
|
|
18
|
+
return skillContent.slice(idx + MCP_VERSION_MARKER.length, end).trim();
|
|
19
|
+
}
|
|
20
|
+
/** Build the Healer tools table markdown */
|
|
21
|
+
function buildHealerTable(version, tools) {
|
|
22
|
+
const rows = tools.map(t => `| \`${t.name}\` | ${t.purpose} |`).join('\n');
|
|
23
|
+
return `${MCP_VERSION_MARKER} ${version} -->\n\n| Tool | Purpose |\n|------|---------|\n${rows}`;
|
|
24
|
+
}
|
|
25
|
+
/** Replace the Healer tools table in SKILL.md */
|
|
26
|
+
export function updateHealerTable(skillContent, version, tools) {
|
|
27
|
+
const start = skillContent.indexOf('| Tool | Purpose |');
|
|
28
|
+
if (start === -1)
|
|
29
|
+
return skillContent;
|
|
30
|
+
let end = skillContent.indexOf('\n\n', start);
|
|
31
|
+
if (end === -1)
|
|
32
|
+
end = skillContent.length;
|
|
33
|
+
const before = skillContent.slice(0, start);
|
|
34
|
+
const after = skillContent.slice(end);
|
|
35
|
+
return before + buildHealerTable(version, tools) + after;
|
|
36
|
+
}
|
|
37
|
+
/** Fetch latest @playwright/mcp version from npm registry */
|
|
38
|
+
export function getLatestMcpVersion() {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
exec('npm show @playwright/mcp version --json', { timeout: 15000 }, (err, stdout) => {
|
|
41
|
+
if (err) {
|
|
42
|
+
resolve(null);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
resolve(JSON.parse(stdout.trim()));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
resolve(null);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Parse README markdown to extract browser_* tool entries */
|
|
55
|
+
function parseMcpReadme(content) {
|
|
56
|
+
const tools = [];
|
|
57
|
+
const re = /-\s+\*\*`?([^`*\n]+)`?\*\*\s*-\s*Title:\s*([^\n]+)/g;
|
|
58
|
+
let m;
|
|
59
|
+
while ((m = re.exec(content)) !== null) {
|
|
60
|
+
const name = m[1].trim();
|
|
61
|
+
if (name.startsWith('browser_')) {
|
|
62
|
+
const purpose = m[2].trim().replace(/\.$/, '');
|
|
63
|
+
tools.push({ name, purpose });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return tools;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Fetch @playwright/mcp tools from npm package.
|
|
70
|
+
* Downloads the tarball, extracts README, parses tool names.
|
|
71
|
+
*/
|
|
72
|
+
export function fetchMcpTools(version) {
|
|
73
|
+
return new Promise((resolve) => {
|
|
74
|
+
const tmpDir = `/tmp/openspec-pw-mcp-${version}`;
|
|
75
|
+
exec(`rm -rf ${tmpDir} && mkdir -p ${tmpDir} && npm pack @playwright/mcp@${version} --pack-destination ${tmpDir} 2>/dev/null && tar -xzf ${tmpDir}/playwright-mcp-${version}.tgz -C ${tmpDir} --strip-components=1 && cat ${tmpDir}/package/README.md`, { timeout: 30000 }, (err, stdout) => {
|
|
76
|
+
if (err) {
|
|
77
|
+
resolve([]);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const tools = parseMcpReadme(stdout);
|
|
81
|
+
resolve(tools);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Sync Healer tools table in SKILL.md with latest @playwright/mcp.
|
|
87
|
+
* Returns true if updated, false if already current or failed.
|
|
88
|
+
*/
|
|
89
|
+
export async function syncMcpTools(skillDest, verbose = false) {
|
|
90
|
+
const latestVersion = await getLatestMcpVersion();
|
|
91
|
+
if (!latestVersion) {
|
|
92
|
+
if (verbose)
|
|
93
|
+
console.log(chalk.yellow(' ⚠ Could not fetch latest @playwright/mcp version'));
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
if (!existsSync(skillDest)) {
|
|
97
|
+
if (verbose)
|
|
98
|
+
console.log(chalk.gray(' - SKILL.md not found, skipping MCP sync'));
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const skillContent = readFileSync(skillDest, 'utf-8');
|
|
102
|
+
const storedVersion = getStoredMcpVersion(skillContent);
|
|
103
|
+
if (storedVersion === latestVersion) {
|
|
104
|
+
if (verbose)
|
|
105
|
+
console.log(chalk.gray(` - Healer tools current (${latestVersion})`));
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (verbose)
|
|
109
|
+
console.log(chalk.blue(` - Updating from ${storedVersion ?? 'unknown'} → ${latestVersion}`));
|
|
110
|
+
const tools = await fetchMcpTools(latestVersion);
|
|
111
|
+
const toolSet = tools.length > 0 ? tools : DEFAULT_HEALER_TOOLS;
|
|
112
|
+
const updated = updateHealerTable(skillContent, latestVersion, toolSet);
|
|
113
|
+
writeFileSync(skillDest, updated);
|
|
114
|
+
if (verbose) {
|
|
115
|
+
if (tools.length > 0) {
|
|
116
|
+
console.log(chalk.green(` ✓ Healer tools synced to ${latestVersion} (${tools.length} tools)`));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
console.log(chalk.green(` ✓ Healer tools synced to ${latestVersion} (default set)`));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=mcpSync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpSync.js","sourceRoot":"","sources":["../../src/commands/mcpSync.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/update.js
CHANGED
|
@@ -2,6 +2,7 @@ import { execSync } from 'child_process';
|
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
+
import { syncMcpTools } from './mcpSync.js';
|
|
5
6
|
const SKILL_SRC = new URL('../../.claude/skills/openspec-e2e', import.meta.url).pathname;
|
|
6
7
|
const CMD_SRC = new URL('../../.claude/commands/opsx', import.meta.url).pathname;
|
|
7
8
|
const SCHEMA_DIR = new URL('../../schemas', import.meta.url).pathname;
|
|
@@ -24,12 +25,10 @@ export async function update(options) {
|
|
|
24
25
|
if (options.skill !== false) {
|
|
25
26
|
console.log(chalk.blue('\n─── Updating Skill & Command ───'));
|
|
26
27
|
try {
|
|
27
|
-
// Download npm tarball and extract
|
|
28
28
|
const tmpDir = '/tmp/openspec-e2e-update';
|
|
29
29
|
execSync(`rm -rf ${tmpDir} && mkdir -p ${tmpDir}`, { stdio: 'pipe', cwd: projectRoot });
|
|
30
30
|
execSync(`npm pack openspec-playwright --pack-destination ${tmpDir}`, { stdio: 'pipe', cwd: projectRoot });
|
|
31
31
|
const tarball = execSync(`ls -t ${tmpDir}/openspec-playwright-*.tgz | head -1`, { encoding: 'utf-8', cwd: projectRoot }).trim();
|
|
32
|
-
// Move tarball out before extracting to avoid "overwrite archive" error
|
|
33
32
|
const tmpTarball = `${tmpDir}/package.tgz`;
|
|
34
33
|
execSync(`mv "${tarball}" "${tmpTarball}"`, { stdio: 'pipe', cwd: projectRoot });
|
|
35
34
|
execSync(`tar -xzf "${tmpTarball}" -C ${tmpDir} --strip-components=1`, { stdio: 'pipe', cwd: projectRoot });
|
|
@@ -45,6 +44,10 @@ export async function update(options) {
|
|
|
45
44
|
installSkill(projectRoot);
|
|
46
45
|
}
|
|
47
46
|
}
|
|
47
|
+
// 3. Sync Healer tools with latest @playwright/mcp
|
|
48
|
+
console.log(chalk.blue('\n─── Syncing Healer Tools ───'));
|
|
49
|
+
const skillDest = join(projectRoot, '.claude', 'skills', 'openspec-e2e', 'SKILL.md');
|
|
50
|
+
await syncMcpTools(skillDest, true);
|
|
48
51
|
// Summary
|
|
49
52
|
console.log(chalk.blue('\n─── Summary ───'));
|
|
50
53
|
console.log(chalk.green(' ✓ Update complete!\n'));
|
|
@@ -70,12 +73,10 @@ function installSkillFrom(skillSrc, cmdSrc, schemaSrc, projectRoot) {
|
|
|
70
73
|
function installSchemaFrom(schemaSrc, projectRoot) {
|
|
71
74
|
const schemaDest = join(projectRoot, 'openspec', 'schemas', 'playwright-e2e');
|
|
72
75
|
mkdirSync(schemaDest, { recursive: true });
|
|
73
|
-
// Copy schema.yaml
|
|
74
76
|
const schemaYamlSrc = join(schemaSrc, 'schema.yaml');
|
|
75
77
|
if (existsSync(schemaYamlSrc)) {
|
|
76
78
|
writeFileSync(join(schemaDest, 'schema.yaml'), readFileSync(schemaYamlSrc));
|
|
77
79
|
}
|
|
78
|
-
// Copy templates
|
|
79
80
|
const templatesSrc = join(schemaSrc, 'templates');
|
|
80
81
|
const templatesDest = join(schemaDest, 'templates');
|
|
81
82
|
if (existsSync(templatesSrc)) {
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
|
Binary file
|
package/package.json
CHANGED
package/release-notes.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
## What's Changed
|
|
2
2
|
|
|
3
|
-
- feat
|
|
3
|
+
- feat: add MCP version sync for Healer tools (init + update)
|
|
4
4
|
|
|
5
|
-
**Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.
|
|
5
|
+
**Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.29
|
package/src/commands/init.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
import { readFile } from 'fs/promises';
|
|
11
|
+
import { syncMcpTools } from './mcpSync.js';
|
|
11
12
|
|
|
12
13
|
const TEMPLATE_DIR = new URL('../../templates', import.meta.url).pathname;
|
|
13
14
|
const SCHEMA_DIR = new URL('../../schemas', import.meta.url).pathname;
|
|
@@ -82,17 +83,22 @@ export async function init(options: InitOptions) {
|
|
|
82
83
|
console.log(chalk.blue('\n─── Installing Claude Code Skill ───'));
|
|
83
84
|
await installSkill(projectRoot);
|
|
84
85
|
|
|
85
|
-
// 5.
|
|
86
|
+
// 5. Sync Healer tools with latest @playwright/mcp
|
|
87
|
+
console.log(chalk.blue('\n─── Syncing Healer Tools ───'));
|
|
88
|
+
const skillDest = join(projectRoot, '.claude', 'skills', 'openspec-e2e', 'SKILL.md');
|
|
89
|
+
await syncMcpTools(skillDest, true);
|
|
90
|
+
|
|
91
|
+
// 6. Install OpenSpec schema
|
|
86
92
|
console.log(chalk.blue('\n─── Installing OpenSpec Schema ───'));
|
|
87
93
|
await installSchema(projectRoot);
|
|
88
94
|
|
|
89
|
-
//
|
|
95
|
+
// 7. Generate seed test
|
|
90
96
|
if (options.seed !== false) {
|
|
91
97
|
console.log(chalk.blue('\n─── Generating Seed Test ───'));
|
|
92
98
|
await generateSeedTest(projectRoot);
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
//
|
|
101
|
+
// 8. Summary
|
|
96
102
|
console.log(chalk.blue('\n─── Summary ───'));
|
|
97
103
|
console.log(chalk.green(' ✓ Setup complete!\n'));
|
|
98
104
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
export const MCP_VERSION_MARKER = '<!-- MCP_VERSION:';
|
|
7
|
+
|
|
8
|
+
export const DEFAULT_HEALER_TOOLS = [
|
|
9
|
+
{ name: 'browser_navigate', purpose: "Go to the failing test's page" },
|
|
10
|
+
{ name: 'browser_snapshot', purpose: 'Get page structure to find equivalent selectors' },
|
|
11
|
+
{ name: 'browser_console_messages', purpose: 'Diagnose JS errors that may cause failures' },
|
|
12
|
+
{ name: 'browser_take_screenshot', purpose: 'Visually compare before/after fixes' },
|
|
13
|
+
{ name: 'browser_run_code', purpose: 'Execute custom fix logic (optional)' },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/** Extract MCP version from SKILL.md marker */
|
|
17
|
+
export function getStoredMcpVersion(skillContent: string): string | null {
|
|
18
|
+
const idx = skillContent.indexOf(MCP_VERSION_MARKER);
|
|
19
|
+
if (idx === -1) return null;
|
|
20
|
+
const end = skillContent.indexOf(' -->', idx);
|
|
21
|
+
return skillContent.slice(idx + MCP_VERSION_MARKER.length, end).trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Build the Healer tools table markdown */
|
|
25
|
+
function buildHealerTable(version: string, tools: Array<{ name: string; purpose: string }>): string {
|
|
26
|
+
const rows = tools.map(t => `| \`${t.name}\` | ${t.purpose} |`).join('\n');
|
|
27
|
+
return `${MCP_VERSION_MARKER} ${version} -->\n\n| Tool | Purpose |\n|------|---------|\n${rows}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Replace the Healer tools table in SKILL.md */
|
|
31
|
+
export function updateHealerTable(
|
|
32
|
+
skillContent: string,
|
|
33
|
+
version: string,
|
|
34
|
+
tools: Array<{ name: string; purpose: string }>
|
|
35
|
+
): string {
|
|
36
|
+
const start = skillContent.indexOf('| Tool | Purpose |');
|
|
37
|
+
if (start === -1) return skillContent;
|
|
38
|
+
let end = skillContent.indexOf('\n\n', start);
|
|
39
|
+
if (end === -1) end = skillContent.length;
|
|
40
|
+
|
|
41
|
+
const before = skillContent.slice(0, start);
|
|
42
|
+
const after = skillContent.slice(end);
|
|
43
|
+
return before + buildHealerTable(version, tools) + after;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Fetch latest @playwright/mcp version from npm registry */
|
|
47
|
+
export function getLatestMcpVersion(): Promise<string | null> {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
exec('npm show @playwright/mcp version --json', { timeout: 15000 }, (err, stdout) => {
|
|
50
|
+
if (err) { resolve(null); return; }
|
|
51
|
+
try { resolve(JSON.parse(stdout.trim())); } catch { resolve(null); }
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Parse README markdown to extract browser_* tool entries */
|
|
57
|
+
function parseMcpReadme(content: string): Array<{ name: string; purpose: string }> {
|
|
58
|
+
const tools: Array<{ name: string; purpose: string }> = [];
|
|
59
|
+
const re = /-\s+\*\*`?([^`*\n]+)`?\*\*\s*-\s*Title:\s*([^\n]+)/g;
|
|
60
|
+
let m;
|
|
61
|
+
while ((m = re.exec(content)) !== null) {
|
|
62
|
+
const name = m[1].trim();
|
|
63
|
+
if (name.startsWith('browser_')) {
|
|
64
|
+
const purpose = m[2].trim().replace(/\.$/, '');
|
|
65
|
+
tools.push({ name, purpose });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return tools;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Fetch @playwright/mcp tools from npm package.
|
|
73
|
+
* Downloads the tarball, extracts README, parses tool names.
|
|
74
|
+
*/
|
|
75
|
+
export function fetchMcpTools(version: string): Promise<Array<{ name: string; purpose: string }>> {
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
const tmpDir = `/tmp/openspec-pw-mcp-${version}`;
|
|
78
|
+
exec(
|
|
79
|
+
`rm -rf ${tmpDir} && mkdir -p ${tmpDir} && npm pack @playwright/mcp@${version} --pack-destination ${tmpDir} 2>/dev/null && tar -xzf ${tmpDir}/playwright-mcp-${version}.tgz -C ${tmpDir} --strip-components=1 && cat ${tmpDir}/package/README.md`,
|
|
80
|
+
{ timeout: 30000 },
|
|
81
|
+
(err, stdout) => {
|
|
82
|
+
if (err) { resolve([]); return; }
|
|
83
|
+
const tools = parseMcpReadme(stdout);
|
|
84
|
+
resolve(tools);
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Sync Healer tools table in SKILL.md with latest @playwright/mcp.
|
|
92
|
+
* Returns true if updated, false if already current or failed.
|
|
93
|
+
*/
|
|
94
|
+
export async function syncMcpTools(
|
|
95
|
+
skillDest: string,
|
|
96
|
+
verbose = false
|
|
97
|
+
): Promise<boolean> {
|
|
98
|
+
const latestVersion = await getLatestMcpVersion();
|
|
99
|
+
if (!latestVersion) {
|
|
100
|
+
if (verbose) console.log(chalk.yellow(' ⚠ Could not fetch latest @playwright/mcp version'));
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!existsSync(skillDest)) {
|
|
105
|
+
if (verbose) console.log(chalk.gray(' - SKILL.md not found, skipping MCP sync'));
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const skillContent = readFileSync(skillDest, 'utf-8');
|
|
110
|
+
const storedVersion = getStoredMcpVersion(skillContent);
|
|
111
|
+
|
|
112
|
+
if (storedVersion === latestVersion) {
|
|
113
|
+
if (verbose) console.log(chalk.gray(` - Healer tools current (${latestVersion})`));
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (verbose) console.log(chalk.blue(` - Updating from ${storedVersion ?? 'unknown'} → ${latestVersion}`));
|
|
118
|
+
|
|
119
|
+
const tools = await fetchMcpTools(latestVersion);
|
|
120
|
+
const toolSet = tools.length > 0 ? tools : DEFAULT_HEALER_TOOLS;
|
|
121
|
+
|
|
122
|
+
const updated = updateHealerTable(skillContent, latestVersion, toolSet);
|
|
123
|
+
writeFileSync(skillDest, updated);
|
|
124
|
+
|
|
125
|
+
if (verbose) {
|
|
126
|
+
if (tools.length > 0) {
|
|
127
|
+
console.log(chalk.green(` ✓ Healer tools synced to ${latestVersion} (${tools.length} tools)`));
|
|
128
|
+
} else {
|
|
129
|
+
console.log(chalk.green(` ✓ Healer tools synced to ${latestVersion} (default set)`));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
}
|
package/src/commands/update.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from 'fs';
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
+
import { syncMcpTools } from './mcpSync.js';
|
|
10
11
|
|
|
11
12
|
const SKILL_SRC = new URL('../../.claude/skills/openspec-e2e', import.meta.url).pathname;
|
|
12
13
|
const CMD_SRC = new URL('../../.claude/commands/opsx', import.meta.url).pathname;
|
|
@@ -43,7 +44,6 @@ export async function update(options: UpdateOptions) {
|
|
|
43
44
|
if (options.skill !== false) {
|
|
44
45
|
console.log(chalk.blue('\n─── Updating Skill & Command ───'));
|
|
45
46
|
try {
|
|
46
|
-
// Download npm tarball and extract
|
|
47
47
|
const tmpDir = '/tmp/openspec-e2e-update';
|
|
48
48
|
execSync(`rm -rf ${tmpDir} && mkdir -p ${tmpDir}`, { stdio: 'pipe', cwd: projectRoot });
|
|
49
49
|
execSync(
|
|
@@ -54,7 +54,6 @@ export async function update(options: UpdateOptions) {
|
|
|
54
54
|
`ls -t ${tmpDir}/openspec-playwright-*.tgz | head -1`,
|
|
55
55
|
{ encoding: 'utf-8', cwd: projectRoot }
|
|
56
56
|
).trim();
|
|
57
|
-
// Move tarball out before extracting to avoid "overwrite archive" error
|
|
58
57
|
const tmpTarball = `${tmpDir}/package.tgz`;
|
|
59
58
|
execSync(`mv "${tarball}" "${tmpTarball}"`, { stdio: 'pipe', cwd: projectRoot });
|
|
60
59
|
execSync(`tar -xzf "${tmpTarball}" -C ${tmpDir} --strip-components=1`, { stdio: 'pipe', cwd: projectRoot });
|
|
@@ -72,6 +71,11 @@ export async function update(options: UpdateOptions) {
|
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
73
|
|
|
74
|
+
// 3. Sync Healer tools with latest @playwright/mcp
|
|
75
|
+
console.log(chalk.blue('\n─── Syncing Healer Tools ───'));
|
|
76
|
+
const skillDest = join(projectRoot, '.claude', 'skills', 'openspec-e2e', 'SKILL.md');
|
|
77
|
+
await syncMcpTools(skillDest, true);
|
|
78
|
+
|
|
75
79
|
// Summary
|
|
76
80
|
console.log(chalk.blue('\n─── Summary ───'));
|
|
77
81
|
console.log(chalk.green(' ✓ Update complete!\n'));
|
|
@@ -110,13 +114,11 @@ function installSchemaFrom(schemaSrc: string, projectRoot: string) {
|
|
|
110
114
|
const schemaDest = join(projectRoot, 'openspec', 'schemas', 'playwright-e2e');
|
|
111
115
|
|
|
112
116
|
mkdirSync(schemaDest, { recursive: true });
|
|
113
|
-
// Copy schema.yaml
|
|
114
117
|
const schemaYamlSrc = join(schemaSrc, 'schema.yaml');
|
|
115
118
|
if (existsSync(schemaYamlSrc)) {
|
|
116
119
|
writeFileSync(join(schemaDest, 'schema.yaml'), readFileSync(schemaYamlSrc));
|
|
117
120
|
}
|
|
118
121
|
|
|
119
|
-
// Copy templates
|
|
120
122
|
const templatesSrc = join(schemaSrc, 'templates');
|
|
121
123
|
const templatesDest = join(schemaDest, 'templates');
|
|
122
124
|
if (existsSync(templatesSrc)) {
|
|
Binary file
|