playwright-test-agent 0.1.7 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright-test-agent",
3
- "version": "0.1.7",
3
+ "version": "0.2.1",
4
4
  "description": "Initialize Playwright Test agents with a Playwright CLI-first browser workflow.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,21 +5,26 @@ description: Use first when a user asks to investigate or test a website, browse
5
5
 
6
6
  # Playwright Test Agent
7
7
 
8
- Use Planner -> Generator -> Healer for durable tests. `AGENTS.md` or `CLAUDE.md` defines the platform-specific role routing. Generated role definitions use `playwright-cli` as their primary browser interface, so MCP tool injection is not required.
8
+ Use Planner -> Generator -> Healer for durable tests. `AGENTS.md` or `CLAUDE.md` defines the platform-specific role routing. The role that owns the current phase uses `playwright-cli`; the parent does not perform another role's browser work.
9
9
 
10
- ## Prepare the objective
10
+ ## Required phase handoff
11
11
 
12
- The main agent may inspect the local project for relevant requirements, routes, fixtures, and constraints. Do not require a seed/config audit before planning.
12
+ 1. Planner investigates the application and saves a Markdown plan under `specs/`.
13
+ 2. The parent presents that plan and waits for user confirmation.
14
+ 3. Generator validates the confirmed plan and writes executable tests.
15
+ 4. Healer runs only for failing generated tests.
13
16
 
14
- Before Planner, ask for information that could materially change purpose, scope, safety, or expected behavior: deployed URL, intended role, success criteria, allowed data mutations, and environment constraints. Do not ask for UI details Planner can safely discover.
17
+ On Claude Code, the parent must start `playwright-test-planner`; only that Planner may open the website. The parent must not call `playwright-cli`, Chrome DevTools, browser MCP tools, or another browser integration. On Codex, the main agent does not spawn a Playwright subagent; it explicitly assumes one phase at a time, and may call CLI only while acting in that phase. Preserve the official Playwright Test MCP configuration and role tools; CLI is the preferred live-browser path and prevents missing MCP tools from blocking the workflow.
15
18
 
16
- If the request is sufficient, begin Planner immediately. If investigation finds a missing, incorrect, or contradictory URL, credential, role, prerequisite, expected result, or authorization boundary, pause and ask for the specific correction. Resume Planner with the correction and blocking observation. Do not guess credentials, probe nearby environments, bypass access controls, or wander through unrelated pages.
19
+ Before Planner, the parent may read relevant local project information to understand routes, requirements, fixtures, and constraints. It may ask for information that is necessary or could materially change the test purpose, scope, assertions, access, or safety, such as the deployed URL, account or role, expected behavior, target environment, allowed data mutations, and authorization boundary. Keep this preparation focused: do not locate a seed file or turn config, test-directory, agent-definition, fixture, or generated-file discovery into a prerequisite.
20
+
21
+ If the available context is sufficient, start/enter Planner without further confirmation. During exploration, pause when required information is missing, incorrect, or contradictory, including credentials, URL, role, prerequisites, expected behavior that changes assertions, or authorization for a consequential action. Ask the user for the specific correction, then resume or restart Planner with the answer and blocking observation. Do not guess credentials, probe nearby environments, bypass access controls, or wander through unrelated pages.
17
22
 
18
23
  Keep credentials and tokens in environment variables or ignored secret files. Never copy them into plans, tests, screenshots, evidence, or chat.
19
24
 
20
25
  ## Planner
21
26
 
22
- Planner investigates the live application with `playwright-cli` and saves a human-readable Markdown plan under `specs/`. Use `open`/`goto`, compact `snapshot` or `find`, and refs for interaction. Supply a seed only when explicitly identified; do not perform a separate seed discovery pass.
27
+ Planner—not the parent—opens the supplied URL with `playwright-cli open <deployed-url>`, investigates the application, and saves a human-readable Markdown plan under `specs/`. Once the parent has supplied the objective, URL, access information, relevant project context, and constraints, Planner's first browser action must be that CLI command. It then uses compact `snapshot` or `find` output and refs for interaction. A seed is not a prerequisite and must not be located before exploration.
23
28
 
24
29
  The plan contains prerequisites, test data, independent scenarios, steps, observable expected results, exclusions, and intended output files. Reconnaissance must not mutate durable/shared data or perform consequential actions unless authorized.
25
30
 
@@ -27,13 +32,13 @@ Show the scenario list and exclusions to the user. Generator starts only after c
27
32
 
28
33
  ## Generator
29
34
 
30
- Generator converts the confirmed plan into executable tests. Use `playwright-cli` to validate flows, locators, and assertions against the live application, then write the tests directly. Generate only confirmed scenarios; prefer independent tests, semantic locators, explicit assertions, and environment-based secrets.
35
+ Generator starts only after confirmation and converts the confirmed plan into executable tests. Generator—not the parent—uses `playwright-cli` to validate flows, locators, and assertions, then writes the tests directly. Generate only confirmed scenarios; prefer independent tests, semantic locators, explicit assertions, and environment-based secrets.
31
36
 
32
37
  For API-only scenarios, use Playwright `APIRequestContext` directly and assert status, headers, schema, stable business invariants, and safe mutation cleanup.
33
38
 
34
39
  ## Healer
35
40
 
36
- Use Healer only for failures. Run the failing test with `npx playwright test`, reproduce and inspect the current UI with `playwright-cli`, apply justified test-side changes, and rerun within guardrails.
41
+ Use Healer only for failures. Healer—not the parent—runs the failing test with `npx playwright test`, reproduces and inspects the UI with `playwright-cli`, applies justified test-side changes, and reruns within guardrails.
37
42
 
38
43
  Do not weaken assertions, add arbitrary sleeps, retry blindly, or skip merely to pass. If behavior conflicts with the confirmed plan, preserve evidence and ask whether it is a regression or intended change. Classify unresolved failures as application defect, test defect, environment/data problem, or product decision.
39
44
 
@@ -13,24 +13,40 @@ const cliFirstInstructions = (role) => `${ROLE_BLOCK_START}
13
13
 
14
14
  ## Browser tool priority
15
15
 
16
- Use the installed \`playwright-cli\` command as the primary browser interface. Read the globally installed \`playwright-cli\` skill when available. Start or attach a session with \`playwright-cli open\`/\`attach\`, navigate with \`goto\`, inspect compact state with \`snapshot\`, and interact through refs from the latest snapshot. Prefer \`snapshot\` and \`find\` over screenshots. Keep credentials in environment variables or ignored files and never print them.
16
+ Use the installed \`playwright-cli\` command as the primary browser interface for this role. Read the globally installed \`playwright-cli\` skill when available. Start or attach a session with \`playwright-cli open\`/\`attach\`, navigate with \`goto\`, inspect compact state with \`snapshot\`, and interact through refs from the latest snapshot. Prefer \`snapshot\` and \`find\` over screenshots. Keep credentials in environment variables or ignored files and never print them.
17
17
 
18
- CLI replaces any conflicting requirement above to call Playwright Test MCP setup, browser, save, generation, or debug tools. Do not stop merely because \`planner_*\`, \`generator_*\`, \`test_*\`, or \`browser_*\` MCP tools are absent, and do not spawn a nested or same-role agent.
18
+ Keep the official Playwright Test MCP configuration and tools generated for this role; do not remove or disable them. Prefer CLI for live page interaction and use it when those MCP tools are unavailable, so missing \`planner_*\`, \`generator_*\`, \`test_*\`, or \`browser_*\` tools must not block the phase. Do not call Chrome DevTools or an unrelated browser integration, and do not spawn a nested or same-role agent.
19
19
 
20
- ${role === 'planner' ? 'Explore with CLI and save the completed Markdown test plan directly under `specs/` using filesystem tools.' : ''}${role === 'generator' ? 'Use CLI to validate the confirmed plan against the live UI, then write executable Playwright test files directly with filesystem tools.' : ''}${role === 'healer' ? 'Run failures with `npx playwright test`, use CLI to inspect and reproduce the current UI, patch the tests directly, and rerun within the healer guardrails.' : ''}
20
+ ${role === 'planner' ? 'FAST START: you are the Planner. The parent may pass relevant local-project context and answers gathered before this phase. When the request contains the test objective, deployed URL, and any information required to access the target, your first browser action must be `playwright-cli open <url>`; do not call `planner_setup_page` or any other browser/MCP action before this CLI open. After the session is open, use compact CLI `snapshot`/`find` output for exploration; MCP tools remain available as an optional supplement. Do not wait for the parent agent to open a browser, and do not first locate or read a seed file, Playwright config, test directory, agent definition, source tree, fixtures, or existing tests. Ask only when the supplied information is still missing, incorrect, contradictory, or exploration reaches a real blocker such as required credentials, ambiguous expected behavior that changes assertions, or authorization for a consequential action. Save the completed Markdown test plan directly under `specs/` using filesystem tools or the available planner save tool.' : ''}${role === 'generator' ? 'You are the Generator. Start only after the parent agent confirms the plan. Begin live validation with `playwright-cli open`/`attach` and use CLI snapshots/find to verify the confirmed plan; MCP setup and browser tools remain optional. Then write executable Playwright test files directly with filesystem tools or the available generator write tool.' : ''}${role === 'healer' ? 'You are the Healer. Run failures with `npx playwright test`, begin UI diagnosis with `playwright-cli open`/`attach`, and use CLI snapshots/find to inspect and reproduce the current UI; MCP tools remain optional. Patch the tests directly and rerun within the healer guardrails.' : ''}
21
21
 
22
22
  ${ROLE_BLOCK_END}`;
23
23
  const CODEX_INSTRUCTIONS = `${BLOCK_START}
24
24
  ## Playwright Test Agent
25
25
 
26
- For website, browser workflow, HTTP API, or application-feature testing, load \`.agents/skills/playwright-test-agent/SKILL.md\` first. Codex must not start Playwright subagents; the main agent performs Planner -> Generator -> Healer in this session. Use \`playwright-cli\` as the primary browser interface and read generated TOML role definitions only for responsibilities. Ask for missing or contradictory purpose, access, role, data, or safety information before or during planning; do not guess or create nested agents.
26
+ Codex routing is mandatory for website, browser workflow, HTTP API, or application-feature testing:
27
+
28
+ 1. Load \`.agents/skills/playwright-test-agent/SKILL.md\`.
29
+ 2. Before Planner, the main agent may read relevant local project information and ask for information that materially affects the test, such as the deployed URL, account or role, expected behavior, environment, and authorization boundary. Do not perform a seed/config/generated-file audit or open the website during preparation.
30
+ 3. Do not start Playwright subagents. Once the required context is available, the main agent explicitly assumes the Planner role in this session.
31
+ 4. Planner's first live browser action is \`playwright-cli open <url>\`. Prefer CLI while preserving the generated Playwright Test MCP configuration and tools. Do not call Chrome DevTools or an unrelated browser integration.
32
+ 5. After Planner saves the plan, show it to the user and wait for confirmation. Then assume Generator; use Healer only for failing generated tests.
33
+
34
+ Ask only for information that is necessary or could materially change scope, assertions, access, or safety. Do not guess or create nested agents.
27
35
 
28
36
  ${BLOCK_END}`;
29
37
 
30
38
  const CLAUDE_INSTRUCTIONS = `${BLOCK_START}
31
39
  ## Playwright Test Agent
32
40
 
33
- For website, browser workflow, HTTP API, or application-feature testing, load \`.claude/skills/playwright-test-agent/SKILL.md\` first. Claude Code starts the official \`playwright-test-planner\`, \`playwright-test-generator\`, and \`playwright-test-healer\` directly. These roles use \`playwright-cli\` as their primary browser interface and must not create nested agents. Ask for missing or contradictory purpose, access, role, data, or safety information before or during planning; do not guess.
41
+ Claude Code routing is mandatory for website, browser workflow, HTTP API, or application-feature testing:
42
+
43
+ 1. Load \`.claude/skills/playwright-test-agent/SKILL.md\`.
44
+ 2. Before starting Planner, the main agent may read relevant local project information and ask for information that materially affects the test, such as the deployed URL, account or role, expected behavior, environment, and authorization boundary. Do not perform a seed/config/generated-file audit.
45
+ 3. Once the required context is available, directly start \`playwright-test-planner\` with the objective, deployed URL, supplied access information, relevant project context, and constraints. The main agent must not open or inspect the website itself and must not call \`playwright-cli\`, Chrome DevTools, browser MCP tools, or another browser integration.
46
+ 4. Planner opens and investigates the website with \`playwright-cli\` and saves the Markdown plan. After Planner returns, show the plan to the user and wait for confirmation.
47
+ 5. Only after confirmation start \`playwright-test-generator\`. Start \`playwright-test-healer\` only for failing generated tests.
48
+
49
+ Start each role directly and never create a nested or same-role intermediary. Do not locate a seed file or make setup-file discovery a prerequisite to Planner. If exploration finds missing, incorrect, or contradictory required information, let Planner pause and return the precise question; after the user answers, start or resume Planner with that answer and the blocking observation. Do not guess.
34
50
 
35
51
  ${BLOCK_END}`;
36
52
 
@@ -126,13 +142,31 @@ function patchCodexRole(source, role) {
126
142
 
127
143
  function patchClaudeRole(source, role) {
128
144
  let updated = source;
129
- if (!/^\s*-\s+Bash\(playwright-cli:\*\)\s*$/m.test(updated)) {
130
- const tools = updated.match(/^tools:\s*\r?\n/m);
131
- if (!tools) throw new Error('tools list was not found in a Claude role definition');
132
- const insertion = ' - Bash(playwright-cli:*)\n - Bash(npx:*)\n';
133
- updated = updated.slice(0, tools.index + tools[0].length) + insertion +
134
- updated.slice(tools.index + tools[0].length);
145
+ const frontmatter = updated.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
146
+ if (!frontmatter) throw new Error('YAML frontmatter was not found in a Claude role definition');
147
+
148
+ let header = frontmatter[1];
149
+ const inlineTools = header.match(/^tools:[ \t]*(\S.*?)[ \t]*$/m);
150
+ const blockTools = header.match(/^tools:[ \t]*$/m);
151
+ if (inlineTools) {
152
+ const tools = inlineTools[1].split(',').map((tool) => tool.trim());
153
+ if (!tools.includes('Bash')) tools.push('Bash');
154
+ header = header.replace(inlineTools[0], `tools: ${tools.join(', ')}`);
155
+ } else if (blockTools) {
156
+ const blockStart = blockTools.index + blockTools[0].length;
157
+ const remainder = header.slice(blockStart);
158
+ const nextKey = remainder.search(/\r?\n(?=[A-Za-z0-9_-]+:\s*)/);
159
+ const blockEnd = nextKey === -1 ? header.length : blockStart + nextKey;
160
+ let toolsBlock = header.slice(blockStart, blockEnd);
161
+ if (!/^\s*-\s+Bash\s*$/m.test(toolsBlock)) {
162
+ toolsBlock = `\n - Bash${toolsBlock}`;
163
+ }
164
+ header = header.slice(0, blockStart) + toolsBlock + header.slice(blockEnd);
165
+ } else {
166
+ header = `${header.trimEnd()}\ntools: Bash`;
135
167
  }
168
+ updated = updated.slice(0, frontmatter.index) + `---\n${header}\n---` +
169
+ updated.slice(frontmatter.index + frontmatter[0].length);
136
170
  return withRoleBlock(updated, role);
137
171
  }
138
172
 
@@ -153,6 +187,61 @@ async function configureCliFirstRoles(projectDir) {
153
187
  }
154
188
  }
155
189
 
190
+ function escapeRegExp(value) {
191
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
192
+ }
193
+
194
+ function upsertTomlSection(source, section, settings) {
195
+ const headerPattern = new RegExp(`^\\[${escapeRegExp(section)}\\][ \\t]*$`, 'm');
196
+ const header = source.match(headerPattern);
197
+
198
+ if (!header) {
199
+ const body = Object.entries(settings).map(([key, value]) => `${key} = ${value}`).join('\n');
200
+ const separator = source.length === 0 ? '' : source.endsWith('\n') ? '\n' : '\n\n';
201
+ return `${source}${separator}[${section}]\n${body}\n`;
202
+ }
203
+
204
+ const bodyStart = header.index + header[0].length;
205
+ const remainder = source.slice(bodyStart);
206
+ const nextHeader = remainder.search(/\r?\n(?=\[[^\]]+\][ \\t]*(?:\r?\n|$))/);
207
+ const bodyEnd = nextHeader === -1 ? source.length : bodyStart + nextHeader;
208
+ let body = source.slice(bodyStart, bodyEnd);
209
+
210
+ for (const [key, value] of Object.entries(settings)) {
211
+ const settingPattern = new RegExp(`(^|\\n)${escapeRegExp(key)}[ \\t]*=[^\\r\\n]*`);
212
+ if (settingPattern.test(body)) {
213
+ body = body.replace(settingPattern, `$1${key} = ${value}`);
214
+ } else {
215
+ body = `${body.trimEnd()}\n${key} = ${value}\n`;
216
+ }
217
+ }
218
+
219
+ return source.slice(0, bodyStart) + body + source.slice(bodyEnd);
220
+ }
221
+
222
+ async function configureCodexMcp(projectDir) {
223
+ const codexDir = path.join(projectDir, '.codex');
224
+ const file = path.join(codexDir, 'config.toml');
225
+ await mkdir(codexDir, { recursive: true });
226
+ let source = await exists(file) ? await readFile(file, 'utf8') : '';
227
+ const command = process.platform === 'win32' ? '"cmd"' : '"npx"';
228
+ const args = process.platform === 'win32'
229
+ ? '["/d", "/s", "/c", "npx", "--no-install", "playwright", "run-test-mcp-server"]'
230
+ : '["--no-install", "playwright", "run-test-mcp-server"]';
231
+
232
+ source = upsertTomlSection(source, 'mcp_servers.playwright-test', {
233
+ command,
234
+ args,
235
+ cwd: '"."',
236
+ enabled: 'true',
237
+ default_tools_approval_mode: '"approve"',
238
+ });
239
+ source = upsertTomlSection(source, 'mcp_servers.playwright-test.env', {
240
+ PLAYWRIGHT_MCP_OUTPUT_DIR: '".playwright-evidence/mcp"',
241
+ });
242
+ await writeFile(file, source, 'utf8');
243
+ }
244
+
156
245
  async function installPlaywrightCli(projectDir, run) {
157
246
  await run(projectDir, 'npm', ['install', '-g', '@playwright/cli@latest']);
158
247
  await run(projectDir, 'playwright-cli', ['install', '--skills=agents', '--global']);
@@ -204,33 +293,7 @@ async function disableTraceScreenshots(projectDir) {
204
293
  }
205
294
  }
206
295
 
207
- async function configureClaudeMcp(projectDir) {
208
- const file = path.join(projectDir, '.mcp.json');
209
- if (!await exists(file)) return;
210
- const config = JSON.parse(await readFile(file, 'utf8'));
211
- const server = config.mcpServers?.['playwright-test'];
212
- if (!server) return;
213
- server.env = {
214
- ...(server.env ?? {}),
215
- PLAYWRIGHT_MCP_OUTPUT_DIR: '.playwright-evidence/mcp',
216
- };
217
- await writeFile(file, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
218
- }
219
-
220
- async function configureCodexAgents(projectDir) {
221
- for (const name of ['planner', 'generator', 'healer']) {
222
- const file = path.join(projectDir, '.codex', 'agents', `playwright_test_${name}.toml`);
223
- if (!await exists(file)) continue;
224
- let source = await readFile(file, 'utf8');
225
- if (!source.includes('[mcp_servers.playwright-test.env]')) {
226
- source = `${source.trimEnd()}\n\n[mcp_servers.playwright-test.env]\n` +
227
- 'PLAYWRIGHT_MCP_OUTPUT_DIR = ".playwright-evidence/mcp"\n';
228
- await writeFile(file, source, 'utf8');
229
- }
230
- }
231
- }
232
-
233
- async function initializePlaywright(projectDir, run) {
296
+ async function initializePlaywright(projectDir, run) {
234
297
  const configPath = path.join(projectDir, 'playwright.config.ts');
235
298
  if (!await exists(configPath)) {
236
299
  await run(projectDir, 'npm', [
@@ -254,12 +317,11 @@ async function initializePlaywright(projectDir, run) {
254
317
  await disableTraceScreenshots(projectDir);
255
318
 
256
319
  await run(projectDir, 'npx', ['--no-install', 'playwright', 'init-agents', '--loop=codex']);
257
- await run(projectDir, 'npx', ['--no-install', 'playwright', 'init-agents', '--loop=claude']);
258
- await mkdir(path.join(projectDir, '.playwright-evidence', 'mcp'), { recursive: true });
259
- await configureClaudeMcp(projectDir);
260
- await configureCodexAgents(projectDir);
320
+ await run(projectDir, 'npx', ['--no-install', 'playwright', 'init-agents', '--loop=claude']);
321
+ await mkdir(path.join(projectDir, '.playwright-evidence', 'mcp'), { recursive: true });
322
+ await configureCodexMcp(projectDir);
261
323
  await configureCliFirstRoles(projectDir);
262
- }
324
+ }
263
325
 
264
326
  async function runStage(name, action) {
265
327
  process.stdout.write(`[playwright-test-agent] ${name}...\n`);