openspec-playwright 0.1.62 → 0.1.64

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 (55) hide show
  1. package/.claude/commands/CLAUDE.md +12 -0
  2. package/.claude/commands/opsx/CLAUDE.md +12 -0
  3. package/.claude/commands/opsx/e2e-body.md +5 -0
  4. package/.claude/commands/opsx/e2e.md +5 -1
  5. package/.claude/skills/CLAUDE.md +12 -0
  6. package/.claude/skills/openspec-e2e/CLAUDE.md +14 -0
  7. package/.claude/skills/openspec-e2e/SKILL.md +101 -77
  8. package/LICENSE +21 -0
  9. package/README.md +4 -3
  10. package/README.zh-CN.md +4 -3
  11. package/bin/CLAUDE.md +11 -0
  12. package/dist/CLAUDE.md +17 -0
  13. package/dist/commands/doctor.d.ts +4 -1
  14. package/dist/commands/doctor.js +110 -73
  15. package/dist/commands/doctor.js.map +1 -1
  16. package/dist/commands/editors.js +149 -95
  17. package/dist/commands/editors.js.map +1 -1
  18. package/dist/commands/init.js +105 -97
  19. package/dist/commands/init.js.map +1 -1
  20. package/dist/commands/mcpSync.js +46 -31
  21. package/dist/commands/mcpSync.js.map +1 -1
  22. package/dist/commands/run.d.ts +13 -0
  23. package/dist/commands/run.js +74 -51
  24. package/dist/commands/run.js.map +1 -1
  25. package/dist/commands/uninstall.d.ts +1 -0
  26. package/dist/commands/uninstall.js +133 -0
  27. package/dist/commands/uninstall.js.map +1 -0
  28. package/dist/commands/update.js +79 -68
  29. package/dist/commands/update.js.map +1 -1
  30. package/dist/index.js +33 -26
  31. package/dist/index.js.map +1 -1
  32. package/employee-standards.md +3 -3
  33. package/package.json +21 -1
  34. package/schemas/playwright-e2e/templates/playwright.config.ts +22 -22
  35. package/templates/CLAUDE.md +15 -0
  36. package/templates/seed.spec.ts +5 -3
  37. package/.github/workflows/release.yml +0 -81
  38. package/docs/plans/2026-03-26-openspec-playwright-design.md +0 -180
  39. package/openspec/schemas/playwright-e2e/schema.yaml +0 -56
  40. package/openspec/schemas/playwright-e2e/templates/e2e-test.ts +0 -55
  41. package/openspec/schemas/playwright-e2e/templates/playwright.config.ts +0 -52
  42. package/openspec/schemas/playwright-e2e/templates/report.md +0 -27
  43. package/openspec/schemas/playwright-e2e/templates/test-plan.md +0 -24
  44. package/openspec-playwright-0.1.62.tgz +0 -0
  45. package/release-notes.md +0 -5
  46. package/src/commands/doctor.ts +0 -115
  47. package/src/commands/editors.ts +0 -606
  48. package/src/commands/init.ts +0 -252
  49. package/src/commands/mcpSync.ts +0 -160
  50. package/src/commands/run.ts +0 -172
  51. package/src/commands/update.ts +0 -192
  52. package/src/index.ts +0 -47
  53. package/tests/editors.test.ts +0 -180
  54. package/tsconfig.json +0 -18
  55. package/vitest.config.ts +0 -9
@@ -0,0 +1,12 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ ### Mar 27, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ | ----- | -------- | --- | ---------------------------------------------------------- | ---- |
10
+ | #5419 | 10:01 AM | 🟣 | Added Claude Code command opsx-e2e.md for E2E verification | ~213 |
11
+
12
+ </claude-mem-context>
@@ -0,0 +1,12 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ ### Mar 27, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ | ----- | -------- | --- | ----------------------------------------------------- | ---- |
10
+ | #5434 | 10:15 AM | 🔄 | Organized OpenSpec command files under opsx namespace | ~145 |
11
+
12
+ </claude-mem-context>
@@ -3,9 +3,11 @@ Run Playwright E2E verification for an OpenSpec change.
3
3
  ## Workflow
4
4
 
5
5
  1. **Validate environment**: Run the seed test to confirm your app is reachable.
6
+
6
7
  ```bash
7
8
  npx playwright test tests/playwright/seed.spec.ts --project=chromium
8
9
  ```
10
+
9
11
  If it fails, fix your BASE_URL or start the dev server first.
10
12
 
11
13
  2. **Select the change**: If no change name is provided, run `openspec list --json` and pick one. Then announce: "Using change: `<name>`".
@@ -23,10 +25,13 @@ Run Playwright E2E verification for an OpenSpec change.
23
25
  - Use `browser.newContext()` for auth guard tests (fresh session, no cookies)
24
26
 
25
27
  7. **Run tests**:
28
+
26
29
  ```bash
27
30
  openspec-pw run <change-name>
28
31
  ```
32
+
29
33
  Or with role filtering:
34
+
30
35
  ```bash
31
36
  npx playwright test tests/playwright/<name>.spec.ts --grep "@<role>"
32
37
  ```
@@ -10,9 +10,11 @@ Run Playwright E2E verification for an OpenSpec change.
10
10
  ## Workflow
11
11
 
12
12
  1. **Validate environment**: Run the seed test to confirm your app is reachable.
13
+
13
14
  ```bash
14
15
  npx playwright test tests/playwright/seed.spec.ts --project=chromium
15
16
  ```
17
+
16
18
  If it fails, fix your BASE_URL or start the dev server first.
17
19
 
18
20
  2. **Select the change**: If no change name is provided, run `openspec list --json` and pick one. Then announce: "Using change: `<name>`".
@@ -30,10 +32,13 @@ Run Playwright E2E verification for an OpenSpec change.
30
32
  - Use `browser.newContext()` for auth guard tests (fresh session, no cookies)
31
33
 
32
34
  7. **Run tests**:
35
+
33
36
  ```bash
34
37
  openspec-pw run <change-name>
35
38
  ```
39
+
36
40
  Or with role filtering:
41
+
37
42
  ```bash
38
43
  npx playwright test tests/playwright/<name>.spec.ts --grep "@<role>"
39
44
  ```
@@ -44,4 +49,3 @@ Run Playwright E2E verification for an OpenSpec change.
44
49
  - Auto-heal up to 3 attempts
45
50
 
46
51
  9. **Report**: Results are saved to `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present the summary table to the user.
47
-
@@ -0,0 +1,12 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ ### Mar 27, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ | ----- | ------- | --- | -------------------------------------------------- | ---- |
10
+ | #5372 | 9:44 AM | ✅ | Created skill directory structure for openspec-e2e | ~124 |
11
+
12
+ </claude-mem-context>
@@ -0,0 +1,14 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ ### Mar 27, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ | ----- | -------- | --- | ----------------------------------------------------------------------- | ---- |
10
+ | #5511 | 11:22 AM | ✅ | Extended guardrails to protect auth files from overwriting | ~181 |
11
+ | #5510 | 11:21 AM | 🟣 | Enhanced SKILL.md with Playwright auth project configuration | ~228 |
12
+ | #5427 | 10:05 AM | ✅ | Committed style fixes aligning SKILL.md with OpenSpec template standard | ~281 |
13
+
14
+ </claude-mem-context>
@@ -25,20 +25,20 @@ metadata:
25
25
 
26
26
  Two modes, same pipeline:
27
27
 
28
- | Mode | Command | Route source | Output |
29
- |------|---------|--------------|--------|
30
- | Change | `/opsx:e2e <name>` | OpenSpec specs | `<name>.spec.ts` |
31
- | All | `/opsx:e2e all` | sitemap + homepage crawl | `app-all.spec.ts` |
28
+ | Mode | Command | Route source | Output |
29
+ | ------ | ------------------ | ------------------------ | ----------------- |
30
+ | Change | `/opsx:e2e <name>` | OpenSpec specs | `<name>.spec.ts` |
31
+ | All | `/opsx:e2e all` | sitemap + homepage crawl | `app-all.spec.ts` |
32
32
 
33
33
  Both modes update `app-knowledge.md` and `app-exploration.md`. All `.spec.ts` files run together as regression suite.
34
34
 
35
35
  **Role mapping** (Playwright Test Agents terminology):
36
36
 
37
- | Role | This SKILL | What it does |
38
- |------|-----------|--------------|
39
- | Planner | Step 4–5 | Explores app via Playwright MCP → produces test-plan.md |
40
- | Generator | Step 6 | Transforms test-plan.md → `.spec.ts` with verified selectors |
41
- | Healer | Step 9 | Executes tests, repairs failures via Playwright MCP |
37
+ | Role | This SKILL | What it does |
38
+ | --------- | ---------- | ------------------------------------------------------------ |
39
+ | Planner | Step 4–5 | Explores app via Playwright MCP → produces test-plan.md |
40
+ | Generator | Step 6 | Transforms test-plan.md → `.spec.ts` with verified selectors |
41
+ | Healer | Step 9 | Executes tests, repairs failures via Playwright MCP |
42
42
 
43
43
  ## Testing principles
44
44
 
@@ -49,11 +49,13 @@ Both modes update `app-knowledge.md` and `app-exploration.md`. All `.spec.ts` fi
49
49
  ```
50
50
 
51
51
  **API only as fallback** — Use `page.request` only when UI genuinely cannot cover the scenario:
52
+
52
53
  - Triggering HTTP 5xx/4xx error responses (hard to reach via UI)
53
54
  - Edge cases requiring pre-condition data that UI cannot set up
54
55
  - Cases where Step 4 exploration confirmed no UI element exists
55
56
 
56
57
  **Decision rule**:
58
+
57
59
  ```
58
60
  Can this be tested through the UI?
59
61
  → Yes → page.getByRole/ByLabel/ByText + click/fill/type + assert UI
@@ -67,12 +69,14 @@ Can this be tested through the UI?
67
69
  ### 1. Select the change or mode
68
70
 
69
71
  **Change mode** (`/opsx:e2e <name>`):
72
+
70
73
  - Use provided name, or infer from context, or auto-select if only one exists
71
74
  - If ambiguous → `openspec list --json` + AskUserQuestion
72
75
  - Verify specs exist: `openspec status --change "<name>" --json`
73
76
  - If specs empty → **STOP: E2E requires specs.** Use "all" mode instead.
74
77
 
75
78
  **"all" mode** (`/opsx:e2e all` — no OpenSpec needed):
79
+
76
80
  - Announce: "Mode: full app exploration"
77
81
  - Discover routes via:
78
82
  1. Navigate to `${BASE_URL}/sitemap.xml` (if exists)
@@ -95,6 +99,7 @@ Can this be tested through the UI?
95
99
  **Exclude false positives**: HTTP header examples (`Authorization: Bearer ...`) and code snippets do not count.
96
100
 
97
101
  **Confidence**:
102
+
98
103
  - High (auto-proceed): Multiple markers AND context indicators
99
104
  - Medium (proceed with note): Single marker, context unclear
100
105
  - Low (skip auth): No markers found
@@ -102,6 +107,7 @@ Can this be tested through the UI?
102
107
  ### 3. Validate environment
103
108
 
104
109
  Run the seed test before generating tests:
110
+
105
111
  ```bash
106
112
  npx playwright test tests/playwright/seed.spec.ts --project=chromium
107
113
  ```
@@ -132,20 +138,22 @@ browser_navigate → browser_console_messages → browser_snapshot → browser_t
132
138
 
133
139
  **After navigating, check for app-level errors**:
134
140
 
135
- | Signal | Meaning | Action |
136
- |--------|---------|--------|
137
- | HTTP 5xx or unreachable | Backend/server error | **STOP** — tell user: "App has a backend error (HTTP <code>). Fix it, then re-run /opsx:e2e." |
138
- | JS error in console | App runtime error | **STOP** — tell user: "Page has JS errors. Fix them, then re-run /opsx:e2e." |
139
- | HTTP 404 | Route not in app (metadata issue) | Continue — mark `⚠️ route not found` in app-exploration.md |
140
- | Auth required, no credentials | Missing auth setup | Continue — skip protected routes, explore login page |
141
+ | Signal | Meaning | Action |
142
+ | ----------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------- |
143
+ | HTTP 5xx or unreachable | Backend/server error | **STOP** — tell user: "App has a backend error (HTTP <code>). Fix it, then re-run /opsx:e2e." |
144
+ | JS error in console | App runtime error | **STOP** — tell user: "Page has JS errors. Fix them, then re-run /opsx:e2e." |
145
+ | HTTP 404 | Route not in app (metadata issue) | Continue — mark `⚠️ route not found` in app-exploration.md |
146
+ | Auth required, no credentials | Missing auth setup | Continue — skip protected routes, explore login page |
141
147
 
142
148
  **For guest routes** (no auth):
149
+
143
150
  ```javascript
144
151
  // Navigate directly
145
- await browser_navigate(`${BASE_URL}/<route>`)
152
+ await browser_navigate(`${BASE_URL}/<route>`);
146
153
  ```
147
154
 
148
155
  **For protected routes** (auth required):
156
+
149
157
  ```javascript
150
158
  // Option A: use existing storageState (recommended)
151
159
  // Option B: navigate to /login first, fill form, then navigate to target
@@ -153,11 +161,13 @@ await browser_navigate(`${BASE_URL}/<route>`)
153
161
  ```
154
162
 
155
163
  **If credentials are not yet available**:
164
+
156
165
  1. Skip protected routes — mark `⚠️ auth needed — explore after auth.setup.ts`
157
166
  2. Explore the login page itself (guest route) — extract form selectors
158
167
  3. After auth.setup.ts runs, re-run exploration for protected routes
159
168
 
160
169
  Wait for page stability:
170
+
161
171
  - Prefer `browser_wait_for` with text or selector
162
172
  - Avoid `networkidle` / `load` — too slow or unreliable
163
173
  - Ready signal: heading, spinner disappears, or URL change
@@ -166,14 +176,14 @@ Wait for page stability:
166
176
 
167
177
  From `browser_snapshot` output, extract **interactive elements** for each route:
168
178
 
169
- | Element type | What to capture | Selector priority |
170
- |---|---|---|
171
- | **Buttons** | text, selector | `[data-testid]` > `getByRole` > `getByLabel` > `getByText` |
172
- | **Form fields** | name, type, label, selector | `[data-testid]` > `name` > `label` |
173
- | **Navigation links** | text, href, selector | `text` > `href` |
174
- | **Headings** | text content, selector | for assertions |
175
- | **Error messages** | text patterns, selector | for error path testing |
176
- | **Dynamic content** | structure — row counts, card layouts | for data-driven tests |
179
+ | Element type | What to capture | Selector priority |
180
+ | -------------------- | ------------------------------------ | ---------------------------------------------------------- |
181
+ | **Buttons** | text, selector | `[data-testid]` > `getByRole` > `getByLabel` > `getByText` |
182
+ | **Form fields** | name, type, label, selector | `[data-testid]` > `name` > `label` |
183
+ | **Navigation links** | text, href, selector | `text` > `href` |
184
+ | **Headings** | text content, selector | for assertions |
185
+ | **Error messages** | text patterns, selector | for error path testing |
186
+ | **Dynamic content** | structure — row counts, card layouts | for data-driven tests |
177
187
 
178
188
  #### 4.4. Write app-exploration.md
179
189
 
@@ -182,6 +192,7 @@ Output: `openspec/changes/<name>/specs/playwright/app-exploration.md`
182
192
  Use template: `openspec/schemas/playwright-e2e/templates/app-exploration.md`
183
193
 
184
194
  Key fields per route:
195
+
185
196
  - **URL**: `${BASE_URL}<path>`
186
197
  - **Auth**: none / required (storageState: `<path>`)
187
198
  - **Ready signal**: how to know the page is loaded
@@ -192,11 +203,11 @@ After exploration, add route-level notes (redirects, dynamic content → see 4.5
192
203
 
193
204
  #### 4.5. Exploration behavior notes
194
205
 
195
- | Situation | Action |
196
- |-----------|--------|
206
+ | Situation | Action |
207
+ | ------------------------------------------------- | ---------------------------------------------------------------- |
197
208
  | SPA routing (URL changes but page doesn't reload) | Explore via navigation clicks from known routes, not direct URLs |
198
- | Page loads but no interactive elements | Wait longer for SPA hydration |
199
- | Dynamic content (user-specific) | Record structure — use `toContainText`, not `toHaveText` |
209
+ | Page loads but no interactive elements | Wait longer for SPA hydration |
210
+ | Dynamic content (user-specific) | Record structure — use `toContainText`, not `toHaveText` |
200
211
 
201
212
  **Idempotency**: If `app-exploration.md` already exists → read it, verify routes still match specs, update only new routes or changed pages.
202
213
 
@@ -204,20 +215,21 @@ After exploration, add route-level notes (redirects, dynamic content → see 4.5
204
215
 
205
216
  After writing `app-exploration.md`, extract **project-level shared knowledge** and append to `tests/playwright/app-knowledge.md`:
206
217
 
207
- | Section | What to extract |
208
- |---------|----------------|
209
- | Architecture | Monolith or separated? Backend port? Restart command? |
210
- | Credential Format | Login endpoint, username format (email vs username) |
211
- | Common Selector Patterns | New patterns discovered that apply across routes |
212
- | SPA Routing | SPA framework, routing behavior |
213
- | Project Conventions | BASE_URL, auth method, multi-user roles |
214
- | Selector Fixes | Healed selectors (see Step 9) — route, old selector, new selector, reason |
218
+ | Section | What to extract |
219
+ | ------------------------ | ------------------------------------------------------------------------- |
220
+ | Architecture | Monolith or separated? Backend port? Restart command? |
221
+ | Credential Format | Login endpoint, username format (email vs username) |
222
+ | Common Selector Patterns | New patterns discovered that apply across routes |
223
+ | SPA Routing | SPA framework, routing behavior |
224
+ | Project Conventions | BASE_URL, auth method, multi-user roles |
225
+ | Selector Fixes | Healed selectors (see Step 9) — route, old selector, new selector, reason |
215
226
 
216
227
  Append only new/changed items — preserve existing content.
217
228
 
218
229
  #### 4.7. After exploration
219
230
 
220
231
  Pass `app-exploration.md` to:
232
+
221
233
  - **Step 5 (Planner)**: reference real routes, auth states, and elements in test-plan.md
222
234
  - **Step 6 (Generator)**: use verified selectors instead of inferring
223
235
 
@@ -242,15 +254,18 @@ Template: `openspec/schemas/playwright-e2e/templates/test-plan.md`
242
254
  ### 6. Generate test file
243
255
 
244
256
  **"all" mode** → `tests/playwright/app-all.spec.ts` (smoke regression):
257
+
245
258
  - For each discovered route: navigate → assert HTTP 200 → assert ready signal visible
246
259
  - No detailed assertions — just "this page loads without crashing"
247
260
  - This is a regression baseline — catches when existing pages break
248
261
 
249
262
  **Change mode** → `tests/playwright/<name>.spec.ts` (functional):
263
+
250
264
  - Read: test-plan.md, app-exploration.md, app-knowledge.md, seed.spec.ts
251
265
  - For each test case: verify selectors in real browser, then write Playwright code
252
266
 
253
267
  **Selector verification (change mode)**:
268
+
254
269
  1. Navigate to route with correct auth state
255
270
  2. browser_snapshot to confirm page loaded
256
271
  3. For each selector: verify from current snapshot (see 4.3 table for priority)
@@ -260,6 +275,7 @@ Template: `openspec/schemas/playwright-e2e/templates/test-plan.md`
260
275
  **Test coverage — empty states**: For list/detail pages, explore the empty state. If the app shows a "no data" UI when the list is empty, generate a test to verify it. Empty states are often missing from specs but are real user paths.
261
276
 
262
277
  **Output format**:
278
+
263
279
  - Follow `seed.spec.ts` structure
264
280
  - Use `test.describe(...)` for grouping
265
281
  - Each test: `test('描述性名称', async ({ page }) => { ... })`
@@ -270,16 +286,16 @@ Template: `openspec/schemas/playwright-e2e/templates/test-plan.md`
270
286
  ```typescript
271
287
  // ✅ UI 测试 — 用户在界面上的真实操作
272
288
  await page.goto(`${BASE_URL}/orders`);
273
- await page.getByRole('button', { name: '新建订单' }).click();
274
- await page.getByLabel('订单名称').fill('Test Order');
275
- await page.getByRole('button', { name: '提交' }).click();
276
- await expect(page.getByText('订单创建成功')).toBeVisible();
289
+ await page.getByRole("button", { name: "新建订单" }).click();
290
+ await page.getByLabel("订单名称").fill("Test Order");
291
+ await page.getByRole("button", { name: "提交" }).click();
292
+ await expect(page.getByText("订单创建成功")).toBeVisible();
277
293
 
278
294
  // ✅ Error path — 通过 UI 触发错误
279
295
  await page.goto(`${BASE_URL}/orders`);
280
- await page.getByRole('button', { name: '新建订单' }).click();
281
- await page.getByRole('button', { name: '提交' }).click();
282
- await expect(page.getByRole('alert')).toContainText('名称不能为空');
296
+ await page.getByRole("button", { name: "新建订单" }).click();
297
+ await page.getByRole("button", { name: "提交" }).click();
298
+ await expect(page.getByRole("alert")).toContainText("名称不能为空");
283
299
 
284
300
  // ✅ API fallback — 仅在 UI 无法触发时使用
285
301
  const res = await page.request.get(`${BASE_URL}/api/orders/99999`);
@@ -306,13 +322,13 @@ await expect(page).toHaveURL(/dashboard/);
306
322
 
307
323
  ```typescript
308
324
  // ✅ Fresh browser context for auth guard
309
- test('unauthenticated user redirected to login', async ({ browser }) => {
325
+ test("unauthenticated user redirected to login", async ({ browser }) => {
310
326
  const freshPage = await browser.newContext().newPage();
311
327
  await freshPage.goto(`${BASE_URL}/dashboard`);
312
328
  await expect(freshPage).toHaveURL(/login|auth/);
313
329
  });
314
330
  // ✅ Session — logout clears protected state
315
- await page.getByRole('button', { name: '退出登录' }).click();
331
+ await page.getByRole("button", { name: "退出登录" }).click();
316
332
  await expect(page).toHaveURL(/login|auth/);
317
333
  const freshPage2 = await browser.newContext().newPage();
318
334
  await freshPage2.goto(`${BASE_URL}/dashboard`);
@@ -320,7 +336,7 @@ await expect(freshPage2).toHaveURL(/login|auth/); // session revoked
320
336
 
321
337
  // ✅ Browser history — SPA back/forward navigation
322
338
  await page.goto(`${BASE_URL}/list`);
323
- await page.getByRole('link', { name: '详情' }).first().click();
339
+ await page.getByRole("link", { name: "详情" }).first().click();
324
340
  await expect(page).toHaveURL(/detail/);
325
341
  await page.goBack();
326
342
  await expect(page).toHaveURL(/list/);
@@ -328,7 +344,7 @@ await page.goForward();
328
344
  await expect(page).toHaveURL(/detail/);
329
345
 
330
346
  // ✅ File uploads — UI 操作
331
- await page.locator('input[type="file"]').setInputFiles('/path/to/file.pdf');
347
+ await page.locator('input[type="file"]').setInputFiles("/path/to/file.pdf");
332
348
  ```
333
349
 
334
350
  Always include error path tests: UI validation messages, network failure, invalid input. Use `page.request` only for scenarios confirmed unreachable via UI.
@@ -342,12 +358,14 @@ If the file exists → diff against test-plan, add only missing test cases.
342
358
  - **Multi-user**: Separate `storageState` paths per role
343
359
 
344
360
  **Credential format guidance**:
361
+
345
362
  - If the app uses **email** for login → use `CHANGE_ME@example.com`
346
363
  - If the app uses **username** (alphanumeric + underscore) → use `test_user_001` (more universal)
347
364
  - Check existing test files or login page to determine the format
348
365
  - Always set credentials via environment variables — never hardcode
349
366
 
350
367
  **Prompt user**:
368
+
351
369
  ```
352
370
  Auth required. To set up:
353
371
  1. Customize tests/playwright/credentials.yaml
@@ -363,6 +381,7 @@ Auth required. To set up:
363
381
  If missing → generate from `openspec/schemas/playwright-e2e/templates/playwright.config.ts`.
364
382
 
365
383
  **Auto-detect BASE_URL** (in priority order):
384
+
366
385
  1. `process.env.BASE_URL` if already set
367
386
  2. `tests/playwright/seed.spec.ts` → extract `BASE_URL` value
368
387
  3. Read `vite.config.ts` (or `vite.config.js`) → extract `server.port` + infer protocol (`https` if `server.https`, else `http`)
@@ -370,6 +389,7 @@ If missing → generate from `openspec/schemas/playwright-e2e/templates/playwrig
370
389
  5. Fallback: `http://localhost:3000`
371
390
 
372
391
  **Auto-detect dev command**:
392
+
373
393
  1. `package.json` → scripts in order: `dev` → `start` → `serve` → `preview` → `npm run dev`
374
394
 
375
395
  If playwright.config.ts exists → READ first, preserve ALL existing fields, add only missing `webServer` block.
@@ -385,27 +405,29 @@ The CLI handles: server lifecycle, port mismatch, report generation.
385
405
  If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
386
406
 
387
407
  **Healer MCP tools** (in order of use):
408
+
388
409
  <!-- MCP_VERSION: 0.0.70 -->
389
410
 
390
- | Tool | Purpose |
391
- |------|---------|
392
- | `browser_navigate` | Go to the failing test's page |
393
- | `browser_snapshot` | Get page structure to find equivalent selectors |
394
- | `browser_console_messages` | Diagnose JS errors that may cause failures |
395
- | `browser_take_screenshot` | Visually compare before/after fixes |
396
- | `browser_run_code` | Execute custom fix logic (optional) |
411
+ | Tool | Purpose |
412
+ | -------------------------- | ----------------------------------------------- |
413
+ | `browser_navigate` | Go to the failing test's page |
414
+ | `browser_snapshot` | Get page structure to find equivalent selectors |
415
+ | `browser_console_messages` | Diagnose JS errors that may cause failures |
416
+ | `browser_take_screenshot` | Visually compare before/after fixes |
417
+ | `browser_run_code` | Execute custom fix logic (optional) |
397
418
 
398
419
  **Healer workflow**:
420
+
399
421
  1. Read the failing test → identify failure type
400
422
  2. Classify:
401
423
 
402
- | Failure type | Signal | Action |
403
- |-------------|--------|--------|
404
- | **Network/backend** | `fetch failed`, `net::ERR`, 5xx | `browser_console_messages` → `test.skip()` |
405
- | **Selector changed** | Element not found | `browser_snapshot` → fix selector → re-run |
406
- | **Assertion mismatch** | Wrong content/value | `browser_snapshot` → compare → fix assertion → re-run |
407
- | **Timing issue** | `waitFor`/`page.evaluate` timeout | Switch to `request` API or add `waitFor` → re-run |
408
- | **page.evaluate with fetch** | `fetch` in browser context, CORS errors | Switch to `page.request` API → re-run |
424
+ | Failure type | Signal | Action |
425
+ | ---------------------------- | --------------------------------------- | ----------------------------------------------------- |
426
+ | **Network/backend** | `fetch failed`, `net::ERR`, 5xx | `browser_console_messages` → `test.skip()` |
427
+ | **Selector changed** | Element not found | `browser_snapshot` → fix selector → re-run |
428
+ | **Assertion mismatch** | Wrong content/value | `browser_snapshot` → compare → fix assertion → re-run |
429
+ | **Timing issue** | `waitFor`/`page.evaluate` timeout | Switch to `request` API or add `waitFor` → re-run |
430
+ | **page.evaluate with fetch** | `fetch` in browser context, CORS errors | Switch to `page.request` API → re-run |
409
431
 
410
432
  3. **Heal** (≤3 attempts): snapshot → fix → re-run. If healed successfully → append to `app-knowledge.md` → **Selector Fixes** table: route, old selector → new selector, reason.
411
433
  4. **After 3 failures**: collect evidence checklist → `test.skip()` if app bug, report recommendation if test bug
@@ -413,6 +435,7 @@ If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
413
435
  ### 10. False Pass Detection
414
436
 
415
437
  Run after test suite completes (even if all pass). Common patterns (see Step 6 Anti-Pattern Warnings for fixes):
438
+
416
439
  - **Conditional visibility**: `if (locator.isVisible().catch(() => false))` — if test passes, locator may not exist
417
440
  - **Too fast**: < 200ms for a complex flow is suspicious
418
441
  - **No fresh auth context**: Protected routes without `browser.newContext()`
@@ -422,6 +445,7 @@ Report any gaps in a **⚠️ Coverage Gap** section.
422
445
  ### 11. Report results
423
446
 
424
447
  Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
448
+
425
449
  - Summary table (tests, passed, failed, duration, status)
426
450
  - Auto-heal notes
427
451
  - Recommendations with `file:line` references
@@ -436,22 +460,22 @@ Reference: `openspec/schemas/playwright-e2e/templates/report.md`
436
460
 
437
461
  ## Graceful Degradation
438
462
 
439
- | Scenario | Behavior |
440
- |----------|----------|
441
- | No specs (change mode) | Stop — E2E requires specs. Use "all" mode instead. |
442
- | Sitemap discovery fails ("all" mode) | Continue — use homepage links + common paths fallback |
443
- | App has JS errors or HTTP 5xx during exploration | **STOP** — see app-knowledge.md → Architecture for restart instructions |
444
- | app-all.spec.ts exists | Read and use (never regenerate — regression baseline) |
445
- | app-exploration.md missing (change mode) | **STOP** — Step 4 exploration is mandatory. Explore before generating tests. |
446
- | app-exploration.md exists | Read and use (verify routes still match specs — re-explore if page structure changed) |
447
- | app-knowledge.md exists | Read and use (append new patterns only) |
448
- | test-plan.md exists (change mode) | Read and use (never regenerate) |
449
- | auth.setup.ts exists | Verify format (update only if stale) |
450
- | playwright.config.ts exists | Preserve all fields (add only missing) |
451
- | Test fails (backend) | `test.skip()` + report |
452
- | Test fails (selector/assertion) | Healer: snapshot → fix → re-run (≤3) |
453
- | 3 heals failed | Evidence checklist → app bug: `test.skip()`; unclear: report |
454
- | False pass detected | Add "⚠️ Coverage Gap" to report |
463
+ | Scenario | Behavior |
464
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------- |
465
+ | No specs (change mode) | Stop — E2E requires specs. Use "all" mode instead. |
466
+ | Sitemap discovery fails ("all" mode) | Continue — use homepage links + common paths fallback |
467
+ | App has JS errors or HTTP 5xx during exploration | **STOP** — see app-knowledge.md → Architecture for restart instructions |
468
+ | app-all.spec.ts exists | Read and use (never regenerate — regression baseline) |
469
+ | app-exploration.md missing (change mode) | **STOP** — Step 4 exploration is mandatory. Explore before generating tests. |
470
+ | app-exploration.md exists | Read and use (verify routes still match specs — re-explore if page structure changed) |
471
+ | app-knowledge.md exists | Read and use (append new patterns only) |
472
+ | test-plan.md exists (change mode) | Read and use (never regenerate) |
473
+ | auth.setup.ts exists | Verify format (update only if stale) |
474
+ | playwright.config.ts exists | Preserve all fields (add only missing) |
475
+ | Test fails (backend) | `test.skip()` + report |
476
+ | Test fails (selector/assertion) | Healer: snapshot → fix → re-run (≤3) |
477
+ | 3 heals failed | Evidence checklist → app bug: `test.skip()`; unclear: report |
478
+ | False pass detected | Add "⚠️ Coverage Gap" to report |
455
479
 
456
480
  ## Guardrails
457
481
 
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present wxhou
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -20,7 +20,7 @@ openspec-pw init # Install Playwright E2E integration
20
20
 
21
21
  ## Supported AI Coding Assistants
22
22
 
23
- Auto-detects and installs commands for all 24 editors OpenSpec supports:
23
+ Auto-detects and installs commands for all 23 editors OpenSpec supports:
24
24
 
25
25
  | Editor | Path | Editor | Path |
26
26
  |--------|------|--------|------|
@@ -54,6 +54,7 @@ Auto-detects and installs commands for all 24 editors OpenSpec supports:
54
54
  openspec-pw init # Initialize integration (one-time setup)
55
55
  openspec-pw update # Update CLI and commands to latest version
56
56
  openspec-pw doctor # Check prerequisites
57
+ openspec-pw uninstall # Remove integration from the project
57
58
  ```
58
59
 
59
60
  ## How It Works
@@ -100,12 +101,12 @@ openspec-pw doctor # Check prerequisites
100
101
 
101
102
  1. **Node.js >= 20**
102
103
  2. **OpenSpec** initialized: `npm install -g @fission-ai/openspec && openspec init`
103
- 3. **One of 24 editors**: Claude Code, Cursor, Windsurf, Cline, Continue, Amazon Q, Gemini CLI, GitHub Copilot, Kiro, Kilo Code, iFlow, CoStrict, OpenCode, Auggie, Factory, CodeBuddy, Codex, Pi, Qoder, Qwen Code, RooCode, Crush, Antigravity (auto-detected)
104
+ 3. **One of 23 editors**: Claude Code, Cursor, Windsurf, Cline, Continue, Amazon Q, Gemini CLI, GitHub Copilot, Kiro, Kilo Code, iFlow, CoStrict, OpenCode, Auggie, Factory, CodeBuddy, Codex, Pi, Qoder, Qwen Code, RooCode, Crush, Antigravity (auto-detected)
104
105
  4. **Claude Code only**: Playwright MCP — `claude mcp add playwright npx @playwright/mcp@latest`
105
106
 
106
107
  ## What `openspec-pw init` Does
107
108
 
108
- 1. Detects installed AI coding assistants (all 24 supported editors)
109
+ 1. Detects installed AI coding assistants (all 23 supported editors)
109
110
  2. Installs E2E command/workflow files for each detected editor
110
111
  3. Installs `/openspec-e2e` skill for Claude Code
111
112
  4. Installs Playwright MCP globally for Claude Code (via `claude mcp add`)
package/README.zh-CN.md CHANGED
@@ -20,7 +20,7 @@ openspec-pw init # 安装 Playwright E2E 集成
20
20
 
21
21
  ## 支持的 AI 编码助手
22
22
 
23
- 自动检测并安装 OpenSpec 支持的全部 24 个编辑器的命令文件:
23
+ 自动检测并安装 OpenSpec 支持的全部 23 个编辑器的命令文件:
24
24
 
25
25
  | 编辑器 | 路径 | 编辑器 | 路径 |
26
26
  |--------|------|--------|------|
@@ -54,6 +54,7 @@ openspec-pw init # 安装 Playwright E2E 集成
54
54
  openspec-pw init # 初始化集成(一次性设置)
55
55
  openspec-pw update # 更新 CLI 和命令到最新版本
56
56
  openspec-pw doctor # 检查前置条件
57
+ openspec-pw uninstall # 移除项目中的集成
57
58
  ```
58
59
 
59
60
  ## 工作原理
@@ -101,12 +102,12 @@ openspec-pw doctor # 检查前置条件
101
102
 
102
103
  1. **Node.js >= 20**
103
104
  2. **OpenSpec** 已初始化: `npm install -g @fission-ai/openspec && openspec init`
104
- 3. **任一 24 编辑器**: Claude Code、Cursor、Windsurf、Cline、Continue 等(自动检测)
105
+ 3. **任一 23 编辑器**: Claude Code、Cursor、Windsurf、Cline、Continue 等(自动检测)
105
106
  4. **仅 Claude Code**: Playwright MCP — `claude mcp add playwright npx @playwright/mcp@latest`
106
107
 
107
108
  ## `openspec-pw init` 做了什么
108
109
 
109
- 1. 检测已安装的 AI 编码助手(支持全部 24 个编辑器)
110
+ 1. 检测已安装的 AI 编码助手(支持全部 23 个编辑器)
110
111
  2. 为每个检测到的编辑器安装 E2E 命令/工作流文件
111
112
  3. 为 Claude Code 安装 `/openspec-e2e` skill
112
113
  4. 为 Claude Code 全局安装 Playwright MCP(通过 `claude mcp add`)
package/bin/CLAUDE.md ADDED
@@ -0,0 +1,11 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ ### Mar 26, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ |----|------|---|-------|------|
10
+ | #5347 | 6:14 PM | 🔄 | Removed complex wrapper script bin/openspec-pw.js | ~119 |
11
+ </claude-mem-context>
package/dist/CLAUDE.md ADDED
@@ -0,0 +1,17 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ ### Mar 26, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ |----|------|---|-------|------|
10
+ | #5344 | 6:14 PM | 🟣 | Openspec-playwright CLI tool with 4 commands implemented | ~294 |
11
+
12
+ ### Mar 27, 2026
13
+
14
+ | ID | Time | T | Title | Read |
15
+ |----|------|---|-------|------|
16
+ | #5381 | 9:46 AM | 🟣 | Built openspec-pw CLI tool with init and doctor commands | ~206 |
17
+ </claude-mem-context>
@@ -1 +1,4 @@
1
- export declare function doctor(): Promise<void>;
1
+ export interface DoctorOptions {
2
+ json?: boolean;
3
+ }
4
+ export declare function doctor(options?: DoctorOptions): Promise<void>;