openspec-playwright 0.3.42 → 0.3.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,7 +20,9 @@ openspec-pw init # Install Playwright E2E integration
20
20
 
21
21
  ## Supported AI Coding Assistants
22
22
 
23
- Claude Code — E2E workflow is driven by `/opsx:e2e` command using a browser exploration tool (gstack `/browse`, Playwright MCP, or `openspec-pw explore`) + Playwright MCP (test execution).
23
+ **Claude Code** (Anthropic) — E2E workflow is driven by the `/opsx:e2e` command using a browser exploration tool (gstack `/browse`, Playwright MCP, or `openspec-pw explore`) + Playwright MCP (test execution).
24
+
25
+ **OpenCode** (SST) — E2E workflow is driven by the `/opsx-e2e` command (hyphenated per OpenSpec convention) using the same browser exploration + Playwright MCP stack. Playwright MCP is configured under `mcp.playwright` in `opencode.jsonc`.
24
26
 
25
27
  ## Usage
26
28
 
@@ -30,12 +32,20 @@ Claude Code — E2E workflow is driven by `/opsx:e2e` command using a browser ex
30
32
  /opsx:e2e <change-name>
31
33
  ```
32
34
 
35
+ ### In OpenCode
36
+
37
+ ```bash
38
+ /opsx-e2e <change-name>
39
+ ```
40
+
41
+ The command id is hyphenated per the OpenSpec convention; the body is rewritten from `/opsx:` to `/opsx-` during install and stored at `.opencode/commands/opsx-e2e.md`.
42
+
33
43
  ### CLI Commands
34
44
 
35
45
  ```bash
36
46
  openspec-pw init # Initialize integration (one-time setup; add --seed to overwrite existing seed.spec.ts)
37
47
  openspec-pw update # Update CLI and commands to latest version
38
- openspec-pw doctor # Check prerequisites
48
+ openspec-pw doctor # Check prerequisites + app server diagnostics
39
49
  openspec-pw audit # Audit tests for orphaned specs and issues
40
50
  openspec-pw migrate # Migrate old test files to new structure
41
51
  openspec-pw explore # Explore routes in parallel with Playwright
@@ -46,6 +56,7 @@ openspec-pw uninstall # Remove integration from the project
46
56
  ## How It Works
47
57
 
48
58
  ```
59
+ # Triggered by /opsx:e2e <change-name> (Claude Code) or /opsx-e2e <change-name> (OpenCode)
49
60
  /opsx:e2e <change-name>
50
61
 
51
62
  ├── 1. Select change → read openspec/changes/<name>/specs/
@@ -81,9 +92,11 @@ openspec-pw uninstall # Remove integration from the project
81
92
  **Required:**
82
93
 
83
94
  1. **Node.js >= 20**
84
- 2. **Claude Code** with `.claude/` directory
95
+ 2. **Claude Code** (with `.claude/` directory) and/or **OpenCode** (with `.opencode/` directory)
85
96
  3. **OpenSpec** initialized: `npm install -g @fission-ai/openspec@latest && openspec init`
86
- 4. **Playwright MCP** (for test execution + Healer): `claude mcp add playwright npx @playwright/mcp@latest`
97
+ 4. **Playwright MCP** (for test execution + Healer) — installed automatically by `openspec-pw init` for the detected editor:
98
+ - **Claude Code**: `claude mcp add playwright npx @playwright/mcp@latest`
99
+ - **OpenCode**: merged into `opencode.jsonc` under `mcp.playwright = { type: "local", command: ["npx", "@playwright/mcp@latest"] }`
87
100
 
88
101
  **Optional** — `openspec-pw explore` and Playwright MCP can do browser exploration out of the box; install gstack only if you want the `/browse` slash command:
89
102
 
@@ -91,9 +104,10 @@ openspec-pw uninstall # Remove integration from the project
91
104
 
92
105
  ## What `openspec-pw init` Does
93
106
 
94
- 1. Detects Claude Code in the project
95
- 2. Installs E2E command (`/opsx:e2e`) for Claude Code
107
+ 1. Detects supported editors in the project (Claude Code and/or OpenCode)
108
+ 2. Installs the E2E command for each detected editor (`/opsx:e2e` for Claude Code, `/opsx-e2e` for OpenCode)
96
109
  3. Generates `tests/playwright/seed.spec.ts`, `auth.setup.ts`, `credentials.yaml`, `app-knowledge.md`
110
+ 4. Generates `playwright.config.ts` with automatic dev script and port detection (Vite/Next/Nuxt/Astro, `.env`, and `--port`)
97
111
 
98
112
  > **Note**: After running `openspec-pw init`, manually install Playwright browsers: `npx playwright install --with-deps`
99
113
 
@@ -106,12 +120,12 @@ Run through these steps in order when using the E2E workflow for the first time:
106
120
  | 1. Install CLI | `npm install -g openspec-playwright@latest` | Check Node.js version `node -v` (needs >= 20) |
107
121
  | 2. Install OpenSpec | `npm install -g @fission-ai/openspec@latest && openspec init` | `npm cache clean -f && npm install -g @fission-ai/openspec@latest` |
108
122
  | 3. Initialize E2E | `openspec-pw init` | Run `openspec-pw doctor` to see what's missing |
109
- | 4. Install Playwright MCP | `claude mcp add playwright npx @playwright/mcp@latest` | `claude mcp list` to confirm installation |
123
+ | 4. Install Playwright MCP | `claude mcp add playwright npx @playwright/mcp@latest` (Claude), or add `mcp.playwright` to `opencode.jsonc` (OpenCode) | `claude mcp list` (Claude) / `cat opencode.jsonc` (OpenCode) to confirm |
110
124
  | 5. Install browsers | `npx playwright install --with-deps` | macOS may need `xcode-select --install` first |
111
125
  | 6. Start dev server | `npm run dev` (in a separate terminal) | Confirm port, set `BASE_URL` if non-standard |
112
126
  | 7. Validate env | `npx playwright test tests/playwright/seed.spec.ts` | Check `webServer` in `playwright.config.ts` |
113
127
  | 8. Configure auth (if needed) | See "Authentication" below | Debug with `npx playwright test --project=setup` |
114
- | 9. Run first E2E | `/opsx:e2e <change-name>` | Check `openspec/reports/` for the report |
128
+ | 9. Run first E2E | `/opsx:e2e <change-name>` (Claude) or `/opsx-e2e <change-name>` (OpenCode) | Check `openspec/reports/` for the report |
115
129
 
116
130
  **Optional — only if you want the single-browser `/browse` workflow:**
117
131
 
@@ -119,6 +133,28 @@ Run through these steps in order when using the E2E workflow for the first time:
119
133
  |------|---------|-------------|
120
134
  | A. Install gstack | `git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup` | Requires Bun: `curl -fsSL https://bun.sh/install \| bash` |
121
135
 
136
+ ## App Server Detection
137
+
138
+ Generated `playwright.config.ts` automatically detects the app URL in this order:
139
+
140
+ 1. `BASE_URL` environment variable
141
+ 2. port flags in `package.json` scripts, e.g. `vite --port 5125`
142
+ 3. `vite.config.*` `server.port`
143
+ 4. `.env.local`, `.env.development`, `.env` (`PLAYWRIGHT_PORT`, `E2E_PORT`, `VITE_PORT`, `PORT`)
144
+ 5. framework defaults: Vite `5173`, Astro `4321`, Next/Nuxt `3000`
145
+ 6. fallback: `http://localhost:3000`
146
+
147
+ Run `openspec-pw doctor` to see the detected dev script and base URL:
148
+
149
+ ```text
150
+ ─── App Server ───
151
+ ✓ dev-script: npm run dev:all
152
+ ✓ base-url: http://localhost:5125 (vite.config.ts)
153
+ ⚠ reachable: fetch failed (diagnostic only; Playwright webServer may start it)
154
+ ```
155
+
156
+ If your project already has `playwright.config.ts`, `openspec-pw init` will not overwrite it. It prints patch hints for missing `webServer`, `testDir`, `storageState`, and setup-project wiring.
157
+
122
158
  ## Authentication
123
159
 
124
160
  If your app requires login, set up credentials once, then all tests run authenticated automatically.
@@ -127,7 +163,9 @@ If your app requires login, set up credentials once, then all tests run authenti
127
163
  # 1. Edit credentials
128
164
  vim tests/playwright/credentials.yaml
129
165
 
130
- # 2. Set environment variables
166
+ # 2. Enable auth and set environment variables
167
+ export E2E_AUTH_REQUIRED=true
168
+ export E2E_AUTH_METHOD=api # or ui
131
169
  export E2E_USERNAME=your-email@example.com
132
170
  export E2E_PASSWORD=your-password
133
171
 
@@ -138,7 +176,7 @@ npx playwright test --project=setup
138
176
  /opsx:e2e my-feature
139
177
  ```
140
178
 
141
- Supports **API login** (preferred) and **UI login** (fallback). For multi-user tests (admin vs user), add multiple users in `credentials.yaml` and run `/opsx:e2e` — it auto-detects roles from specs.
179
+ Supports **API login** (preferred) and **UI login** (fallback). For multi-user tests (admin vs user), add multiple users in `credentials.yaml` and run `/opsx:e2e` (or `/opsx-e2e` in OpenCode) — it auto-detects roles from specs.
142
180
 
143
181
  ## Customization
144
182
 
@@ -172,9 +210,15 @@ CLI (openspec-pw)
172
210
  ├── explore → Explores routes in parallel with Playwright
173
211
  └── uninstall → Removes integration from the project
174
212
 
175
- Claude Code (/opsx:e2e)
176
- ├── .claude/commands/opsx/e2e.md → Command file (installed from templates/e2e-command.md)
177
- └── @playwright/mcp Healer Agent tools
213
+ Editors (auto-detected by openspec-pw init)
214
+ ├── Claude Code (/opsx:e2e)
215
+ │ ├── .claude/commands/opsx/e2e.md Command file
216
+ │ ├── @playwright/mcp → Healer Agent tools (via `claude mcp add playwright …`)
217
+ │ └── CLAUDE.md → Employee-grade standards
218
+ └── OpenCode (/opsx-e2e)
219
+ ├── .opencode/commands/opsx-e2e.md → Command file (body rewritten from /opsx: → /opsx-)
220
+ ├── opencode.jsonc → Playwright MCP (mcp.playwright) + instructions routing
221
+ └── CLAUDE.md or AGENTS.md → Employee-grade standards (routed by detected editors)
178
222
 
179
223
  Test Assets (tests/playwright/)
180
224
  ├── seed.spec.ts → Env validation
@@ -3,7 +3,8 @@ import { createRequire } from "node:module";
3
3
  import { join } from "path";
4
4
  import { execFileSync } from "child_process";
5
5
  import chalk from "chalk";
6
- import { isPlaywrightMcpInstalled, needsShell } from "../shared/index.js";
6
+ import { detectAdapters } from "../commands/editors.js";
7
+ import { detectAppServer, isPlaywrightMcpInstalled, needsShell } from "../shared/index.js";
7
8
  export async function doctor(options = {}) {
8
9
  const checks = [];
9
10
  const projectRoot = process.cwd();
@@ -105,14 +106,27 @@ export async function doctor(options = {}) {
105
106
  ok: hasPwTest,
106
107
  message: pwTestMsg,
107
108
  });
108
- // Playwright MCP — use shared utility
109
- const mcpInstalled = isPlaywrightMcpInstalled();
110
- checks.push({
111
- category: "Playwright MCP",
112
- name: "playwright-mcp",
113
- ok: mcpInstalled,
114
- message: mcpInstalled ? "installed" : "not configured",
115
- });
109
+ // Playwright MCP — check each detected editor adapter
110
+ const adapters = detectAdapters(projectRoot);
111
+ if (adapters.length === 0) {
112
+ checks.push({
113
+ category: "Playwright MCP",
114
+ name: "playwright-mcp",
115
+ ok: false,
116
+ message: "no editors detected (configure .claude/ or .opencode/)",
117
+ });
118
+ }
119
+ else {
120
+ for (const adapter of adapters) {
121
+ const installed = isPlaywrightMcpInstalled(adapter);
122
+ checks.push({
123
+ category: "Playwright MCP",
124
+ name: `playwright-mcp-${adapter.label}`,
125
+ ok: installed,
126
+ message: installed ? "installed" : `not configured for ${adapter.label}`,
127
+ });
128
+ }
129
+ }
116
130
  // Seed test
117
131
  const hasSeed = existsSync(join(projectRoot, "tests", "playwright", "seed.spec.ts"));
118
132
  checks.push({
@@ -121,7 +135,32 @@ export async function doctor(options = {}) {
121
135
  ok: hasSeed,
122
136
  message: hasSeed ? "found" : "not found (optional)",
123
137
  });
124
- const allOk = checks.filter((c) => !c.ok && c.category !== "Seed Test").length === 0;
138
+ // App server detection (diagnostic only not a prerequisite)
139
+ const app = detectAppServer(projectRoot);
140
+ checks.push({
141
+ category: "App Server",
142
+ name: "dev-script",
143
+ ok: Boolean(app.devCommand),
144
+ message: app.devCommand
145
+ ? `${app.devCommand}${app.scriptCommand ? ` (${app.scriptCommand})` : ""}`
146
+ : "not detected (configure webServer manually)",
147
+ });
148
+ checks.push({
149
+ category: "App Server",
150
+ name: "base-url",
151
+ ok: true,
152
+ message: `${app.baseUrl} (${app.baseUrlSource})`,
153
+ });
154
+ const reachable = await checkUrl(app.baseUrl);
155
+ checks.push({
156
+ category: "App Server",
157
+ name: "reachable",
158
+ ok: reachable.ok,
159
+ message: reachable.ok
160
+ ? `responded ${reachable.status ?? "ok"}`
161
+ : `${reachable.message} (diagnostic only; Playwright webServer may start it)`,
162
+ });
163
+ const allOk = checks.filter((c) => !c.ok && c.category !== "Seed Test" && c.category !== "App Server").length === 0;
125
164
  if (options.json) {
126
165
  console.log(JSON.stringify({ ok: allOk, checks }, null, 2));
127
166
  if (!allOk)
@@ -139,7 +178,7 @@ export async function doctor(options = {}) {
139
178
  if (check.ok) {
140
179
  console.log(chalk.green(` ✓ ${check.name}: ${check.message}`));
141
180
  }
142
- else if (check.category === "Seed Test" || check.category === "Vision Check") {
181
+ else if (check.category === "Seed Test" || check.category === "App Server" || check.category === "Vision Check") {
143
182
  console.log(chalk.yellow(` ⚠ ${check.name}: ${check.message}`));
144
183
  }
145
184
  else {
@@ -157,4 +196,23 @@ export async function doctor(options = {}) {
157
196
  process.exit(1);
158
197
  }
159
198
  }
199
+ async function checkUrl(url) {
200
+ try {
201
+ const res = await fetch(url, {
202
+ method: "GET",
203
+ signal: AbortSignal.timeout(2000),
204
+ });
205
+ return {
206
+ ok: res.status < 500,
207
+ status: res.status,
208
+ message: `responded ${res.status}`,
209
+ };
210
+ }
211
+ catch (err) {
212
+ return {
213
+ ok: false,
214
+ message: err instanceof Error ? err.message : "not reachable",
215
+ };
216
+ }
217
+ }
160
218
  //# sourceMappingURL=doctor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,13 @@
1
- /** Shared YAML escape matches OpenSpec's escape logic */
1
+ /** Escape a value for safe inclusion in a YAML frontmatter scalar. */
2
2
  export declare function escapeYamlValue(value: string): string;
3
- /** Format tags as YAML inline array */
3
+ /** Format tags as a YAML inline array. */
4
4
  export declare function formatTagsArray(tags: string[]): string;
5
- /** Command metadata shared across editors */
5
+ /**
6
+ * OpenCode slash-command names are hyphenated (`/opsx-e2e`), Claude's are
7
+ * colon-prefixed (`/opsx:e2e`). Rewrite all `/opsx:` references in a
8
+ * command body for OpenCode installation.
9
+ */
10
+ export declare function transformToHyphenCommands(text: string): string;
6
11
  export interface CommandMeta {
7
12
  id: string;
8
13
  name: string;
@@ -11,16 +16,60 @@ export interface CommandMeta {
11
16
  tags: string[];
12
17
  body: string;
13
18
  }
14
- /** Claude Code command file: .claude/commands/opsx/<id>.md */
19
+ /** Build the command metadata for the /opsx:e2e command. */
20
+ export declare function buildCommandMeta(body: string): CommandMeta;
21
+ export interface EditorAdapter {
22
+ id: "claude" | "opencode";
23
+ /** Short label used in log messages. */
24
+ label: string;
25
+ /** True if this editor's config dir is present in the project. */
26
+ detect(projectRoot: string): boolean;
27
+ /** Relative path of the command file inside the project. */
28
+ commandFilePath(id: string): string;
29
+ /** Format command file contents (frontmatter + body). */
30
+ formatCommand(meta: CommandMeta): string;
31
+ /** Absolute path of the project rules file. */
32
+ projectRulesPath(projectRoot: string): string;
33
+ /** True if MCP server `serverName` is already configured. */
34
+ isMcpInstalled(projectRoot: string, serverName: string): boolean;
35
+ /** Install MCP server config in this editor. */
36
+ installMcp(projectRoot: string, serverName: string, command: string[]): void;
37
+ /** Remove MCP server config from this editor. */
38
+ removeMcp(projectRoot: string, serverName: string): void;
39
+ /** Optional: register project rules file path in editor config. */
40
+ registerInstructions?(projectRoot: string, instructions: string[]): void;
41
+ }
15
42
  export declare function formatClaudeCommand(meta: CommandMeta): string;
16
43
  export declare function getClaudeCommandPath(id: string): string;
17
- /** Build the command metadata for Claude Code */
18
- export declare function buildCommandMeta(body: string): CommandMeta;
19
- /** Detect if Claude Code is installed */
20
44
  export declare function hasClaudeCode(projectRoot: string): boolean;
21
- /** Install command files for Claude Code */
22
- export declare function installForClaudeCode(body: string, projectRoot: string): void;
23
- /** Install project-level CLAUDE.md with employee-grade standards + OpenSpec context */
24
- export declare function installProjectClaudeMd(projectRoot: string, standardsContent: string): void;
25
- /** Read the employee-grade standards from a source file */
45
+ export declare function formatOpenCodeCommand(meta: CommandMeta): string;
46
+ export declare function getOpenCodeCommandPath(id: string): string;
47
+ export declare function hasOpenCode(projectRoot: string): boolean;
48
+ export declare function getAdapter(id: "claude" | "opencode"): EditorAdapter | undefined;
49
+ export declare function detectAdapters(projectRoot: string): EditorAdapter[];
50
+ /** Install the command file for one adapter. */
51
+ export declare function installCommand(adapter: EditorAdapter, meta: CommandMeta, projectRoot: string): void;
52
+ /**
53
+ * Install employee-grade standards into the editor's rules file
54
+ * (CLAUDE.md for Claude, AGENTS.md for OpenCode). Wraps content in
55
+ * `<!-- OPENSPEC:START -->` / `<!-- OPENSPEC:END -->` markers so future
56
+ * updates can replace the block without touching the rest of the file.
57
+ */
58
+ export declare function installProjectClaudeMd(projectRoot: string, standardsContent: string, adapter?: EditorAdapter): void;
59
+ /**
60
+ * Route the employee-grade standards into the right rules file(s) for the
61
+ * detected editors:
62
+ *
63
+ * - 1 editor detected → write to that editor's rules file
64
+ * (CLAUDE.md for Claude, AGENTS.md for OpenCode)
65
+ * - 2 editors detected → write CLAUDE.md (OpenCode reads it natively)
66
+ * and register `CLAUDE.md` in `opencode.json[c].instructions`
67
+ * so OpenCode treats it as a project rule.
68
+ */
69
+ export declare function installProjectRules(projectRoot: string, standardsContent: string, detected: EditorAdapter[]): void;
70
+ /** Remove the OPENSPEC markers block from a rules file (CLAUDE.md / AGENTS.md). */
71
+ export declare function cleanProjectRules(adapter: EditorAdapter, projectRoot: string): void;
72
+ /** Read the employee-grade standards source file (empty string if missing). */
26
73
  export declare function readEmployeeStandards(srcPath: string): string;
74
+ export declare const claudeAdapter: EditorAdapter;
75
+ export declare const opencodeAdapter: EditorAdapter;