playwright-test-agent 0.1.4 → 0.1.6

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.
@@ -7,7 +7,7 @@ import { initializeProject } from '../playwright-test-agent/scripts/init-playwri
7
7
  const usage = `Usage: playwright-test-agent init [target-directory]
8
8
 
9
9
  Initialize Playwright Test agents, install the playwright-test-agent skill,
10
- and update AGENTS.md and CLAUDE.md in the target project.`;
10
+ configure the project MCP servers, and update AGENTS.md and CLAUDE.md.`;
11
11
 
12
12
  const [command, targetDirectory, ...extraArguments] = process.argv.slice(2);
13
13
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "playwright-test-agent",
3
- "version": "0.1.4",
4
- "description": "Initialize Playwright Test agents and install the playwright-test-agent skill.",
3
+ "version": "0.1.6",
4
+ "description": "Initialize Playwright Test, its agents, MCP configuration, and the playwright-test-agent skill.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "playwright-test-agent": "bin/playwright-test-agent.mjs"
@@ -1,113 +1,64 @@
1
1
  ---
2
2
  name: playwright-test-agent
3
- description: Use first when a user asks to test a website, browser workflow, HTTP API, or application feature, including planning, generating, running, debugging, healing, screenshots, and reusable test evidence.
3
+ description: Use first when a user asks to investigate or test a website, browser workflow, HTTP API, or application feature with Playwright, including test planning, generation, execution, debugging, healing, and evidence collection.
4
4
  ---
5
5
 
6
6
  # Playwright Test Agent
7
7
 
8
- Use Playwright Test for durable automation. The main agent orchestrates Playwright's official planner, generator, and healer subagents instead of replacing their work with ad hoc browser exploration. Pure HTTP API tests may be written directly with Playwright `APIRequestContext` without those subagents.
8
+ Use Playwright's Planner -> Generator -> Healer workflow for durable, auditable tests. `AGENTS.md` or `CLAUDE.md` defines how the active platform assumes or launches those roles; this Skill contains only their shared workflow.
9
9
 
10
- This file is the single source of truth for the testing workflow. Project-level `AGENTS.md` and `CLAUDE.md` only force this skill to be loaded and name the platform-specific subagents; do not duplicate or infer the detailed workflow from those routing files.
10
+ ## Prepare the objective
11
11
 
12
- ## Platform agent names
12
+ The main agent may inspect the local project for relevant requirements, routes, fixtures, and constraints. Do not require a setup audit or check for a seed, Playwright config, or generated files before beginning Planner.
13
13
 
14
- | Role | Codex | Claude Code |
15
- |---|---|---|
16
- | Planner | `playwright_test_planner` | `playwright-test-planner` |
17
- | Generator | `playwright_test_generator` | `playwright-test-generator` |
18
- | Healer | `playwright_test_healer` | `playwright-test-healer` |
14
+ Before planning, ask the user for information that could materially change the test purpose, scope, safety, or expected behavior, such as the deployed URL, intended role, success criteria, allowed data mutations, and required environment. Do not ask for UI details Planner can safely discover.
19
15
 
20
- Use the names for the active platform. The role names below refer to these mapped agents.
16
+ If the request is sufficient, begin Planner immediately. If investigation reveals that a URL, credential, role, prerequisite, expected result, or authorization boundary is missing, incorrect, or contradictory, pause Planner and ask for the specific correction. Resume Planner with the corrected information and blocking observation. Do not guess credentials, probe nearby environments, bypass access controls, or wander through unrelated pages.
21
17
 
22
- ## Agent responsibilities
18
+ Keep credentials and tokens in environment variables or ignored secret files. Never copy them into plans, tests, screenshots, evidence, or chat.
23
19
 
24
- - **Main agent:** Own the user conversation, inspect only the local project for context, gather the minimum non-discoverable information from the user, locate the default seed generated by `playwright init-agents`, start Planner, Generator, and Healer directly as leaf subagents, present the plan for confirmation, and report final results. It must not open, navigate, inspect, screenshot, or otherwise investigate the deployed website itself. It must not insert an intermediary or nested agent between itself and any Playwright Test agent.
25
- - **Planner:** Run the generated seed to establish page context, global setup, dependencies, fixtures, and hooks. Explore the deployed application for the requested scenarios and user flows, compare observations with the request and optional PRD, and save a precise human-readable Markdown plan under `specs/`. It does not generate the formal test files and must not spawn another agent. It must call its own injected `planner_setup_page` and `planner_save_plan` tools directly.
26
- - **Generator:** Read the complete user-confirmed Markdown plan and the same generated seed, execute each scenario to validate locators and assertions against the live application, and generate executable Playwright tests under the configured test directory. It must not invent unconfirmed scenarios or expected behavior, must not spawn another agent, and must call its own injected `generator_setup_page`, `generator_read_log`, and `generator_write_test` tools directly.
27
- - **Healer:** Run the failing test, replay its steps, inspect the current UI, propose and apply test-side patches, and rerun until the test passes or a guardrail stops the loop. It must not spawn another agent and must call its own injected `test_list`, `test_run`, `test_debug`, and relevant `browser_*` tools directly. It must report a likely broken product, environment/data problem, or plan conflict instead of weakening assertions merely to obtain a pass.
20
+ ## Planner
28
21
 
29
- For UI testing, keep these responsibilities and sequence:
22
+ Planner owns live application investigation and saves a human-readable Markdown plan under `specs/`. Start page setup immediately. Supply a seed only when the user or request explicitly identifies one; otherwise allow Playwright to use its initialized default. Do not perform a separate seed/config discovery pass.
30
23
 
31
- ```text
32
- request + deployed URL -> locate generated seed -> planner -> user confirms plan
33
- -> generator -> healer for failures -> final report
34
- ```
35
-
36
- Planner, Generator, and Healer are terminal/leaf agents in this flow. The main agent starts the platform-specific agent for each role directly and passes all required inputs in that invocation. Never delegate their work to `live_planner`, `live_generator`, `live_healer`, a same-role nested agent, or another intermediary. If a directly started agent lacks any required MCP tool, stop with an MCP-injection diagnosis; do not create another subagent level and do not replace that agent's work in the main agent.
37
-
38
- ## Initialize once per project
39
-
40
- Inspect the target project first. If Playwright Test, its config, or either Codex/Claude agent set is missing, tell the user initialization will modify the project, then run from the target project:
41
-
42
- ```bash
43
- npx playwright-test-agent init
44
- ```
45
-
46
- The command installs this skill for Codex and Claude, selects the ready-to-use Playwright defaults (TypeScript, `playwright-tests/`, no GitHub Actions, Chromium with browser installation), and runs both:
47
-
48
- ```text
49
- npx playwright init-agents --loop=codex
50
- npx playwright init-agents --loop=claude
51
- ```
52
-
53
- It uses the project-local Playwright command. Never install a global/additional Playwright CLI or run `playwright init-skills`.
54
-
55
- `playwright init-agents` also creates the project's default `seed.spec.ts`. Its directory follows the selected Playwright project's `testDir`, so it may be under `playwright-tests/`, `e2e/`, `tests/`, or another configured directory. Do not hardcode the path. Locate it from `playwright.config.*`, the generated agent definitions, or the filesystem before invoking planner or generator.
56
-
57
- ## Understand the request
24
+ The plan contains prerequisites, test data requirements, independent scenarios, steps, observable expected results, exclusions, and intended test outputs. Reconnaissance must not create durable/shared data or perform purchases, submissions, messages, deletion, or other consequential actions unless the user authorized them.
58
25
 
59
- Before testing, the main agent may briefly inspect the local target project for relevant source, routes/API clients, environment examples, existing tests, fixtures, the generated seed, and run instructions. The project may otherwise be empty or contain no useful application context; in that case, do not keep searching the filesystem or invent missing details.
26
+ Show the completed scenario list and exclusions to the user. Generator may start only after the user confirms the plan.
60
27
 
61
- This local project inspection is the maximum reconnaissance the main agent may perform. The main agent must not use browser tools, HTTP requests, screenshots, or other means to investigate the deployed application. All live website reconnaissance belongs exclusively to the Planner. The main agent may read and summarize Planner output, but it must not independently verify or extend that investigation.
28
+ ## Generator
62
29
 
63
- Before starting the Planner, the main agent may ask the user to clarify information that materially affects the purpose, scope, safety, or expected result of the test plan. Relevant questions may cover the business goal, in-scope and excluded flows, user roles or accounts, expected success and failure outcomes, allowed data mutations, test-data constraints, environment limitations, and required browsers or devices. Ask only questions whose answers could change the plan or authorization boundary; if a necessary answer is missing, wait for it before starting the Planner.
30
+ Generator converts the complete confirmed Markdown plan into executable Playwright tests and validates locators and assertions against the application. Generate only confirmed scenarios. Prefer independent tests, semantic locators, assertions for every expected result, and environment-based secrets.
64
31
 
65
- Do not ask the user for UI facts the Planner can safely discover from the deployed application, such as field labels, button locations, page structure, or ordinary navigation. Once the main agent has the test objective, deployed base URL, and any necessary non-discoverable constraints, invoke the Planner and let it perform all live investigation. For example, "test this project's login feature" plus its deployment URL is enough to investigate a public login page, unless the intended account role, authenticated outcome, or permitted side effects must first be clarified.
32
+ For API-only scenarios, Playwright `APIRequestContext` tests may be written directly. Assert status, headers, schema, stable business invariants, and safe mutation cleanup.
66
33
 
67
- Pass the planner the exact objective, deployed URL, generated seed path, known authentication/roles, allowed side effects, supplied test data, and relevant project or PRD context. The seed is produced by initialization, not supplied by the user. It may remain the default page-context seed or be customized for project fixtures, global setup, dependencies, or authenticated context. Credentials are required before attempting an authenticated-only flow, but not merely to investigate a publicly reachable login page.
34
+ ## Healer
68
35
 
69
- If no seed can be located after initialization, treat the Playwright agent setup as incomplete. Check the configured test directory and generated agent definitions, then rerun `npx playwright-test-agent init` when regeneration is needed. Do not ask the user to author a seed, invent an arbitrary path, or continue to planner without the initialized project context.
36
+ Use Healer only for failing generated tests. Diagnose one failing test at a time, replay the failure, inspect the current application state, apply a test-side correction when justified, and rerun within the role's guardrails.
70
37
 
71
- If planner reconnaissance shows that information is missing, invalid, stale, or contradictory—for example, the URL is unreachable, a page or control does not exist, credentials fail, the observed role differs, a required variable is absent, or application behavior conflicts with the request—stop that planning attempt. Report the exact mismatch without exposing secrets and ask the user to correct or complete it. Do not repeatedly try nearby URLs, guess credentials, wander through unrelated pages, or substitute the main agent's own exploration. Resume with the planner only after the user supplies enough corrected information.
38
+ Do not weaken assertions, add arbitrary sleeps, retry blindly, or skip a test merely to obtain a pass. If behavior conflicts with the confirmed plan, preserve the evidence and ask whether it is a regression or intended product change. Classify unresolved failures as an application defect, test defect, environment/data problem, or product decision.
72
39
 
73
- Never put credentials or tokens in plans, source, screenshots, reports, or chat output. Use environment variables or an ignored secret file.
40
+ ## Evidence
74
41
 
75
- ## Plan and confirm
42
+ Keep generated evidence under `.playwright-evidence/`:
76
43
 
77
- Always have the planner save its human-readable Markdown plan under `specs/` before invoking the generator. Include prerequisites, the generated seed path, test data, independent scenarios, steps or requests, expected observable results, and intended output test files.
78
-
79
- For UI work, invoke the Planner directly (as the first and only Planner-level subagent) once the objective, deployed base URL, and generated seed path are available and no known mismatch already blocks access. Explicitly name the seed in its prompt and provide an optional PRD only when one is relevant. The Planner runs the seed to perform global setup, dependencies, fixtures, and hooks, then uses its ready page context to inspect the live application. Reconnaissance must not create, delete, submit, purchase, message, or otherwise mutate durable/shared data without authorization.
80
-
81
- If the planner discovers a new missing or incorrect prerequisite, the main agent stops that planning attempt, summarizes what was observed, and asks the user for clarification. Continue or reinvoke the planner only after the prerequisite is resolved; do not let it keep exploring around missing information. If the Planner session itself lacks a required MCP tool, report that direct-session environment failure and wait for the environment to be repaired; do not create a nested fallback agent.
82
-
83
- Choose relevant positive, negative, empty, invalid, boundary, permission, persistence, and error scenarios. For login, normally consider valid credentials, empty username, empty password, both empty, wrong password, and unknown user; add MFA, lockout, recovery, or remember-me only when in scope.
84
-
85
- Show the complete scenario list and exclusions to the user. Ask whether it is complete. Revise until confirmed. Do not invoke the generator or formally execute tests before confirmation.
86
-
87
- ## Generate and run
88
-
89
- For confirmed UI scenarios, invoke the Generator directly once for the complete confirmed plan (or complete selected scenario set), explicitly naming the Markdown plan and the same generated seed; use the configured Playwright test directory for generated tests. The Generator executes scenarios against the live application to validate locators and assertions while generating. Its internal per-scenario setup does not mean starting a new Generator agent for every scenario, and it must not delegate execution to a nested Generator. If the direct Generator lacks `generator_setup_page`, `generator_read_log`, or `generator_write_test`, report an MCP-injection failure and stop. Require one independent test per file, semantic locators, an assertion for every expected result, and environment-based secrets.
90
-
91
- For confirmed API-only scenarios, write `APIRequestContext` tests directly under `playwright-tests/`. Assert status, headers, schema, and stable business invariants; define safe setup/cleanup for mutations.
44
+ ```text
45
+ .playwright-evidence/
46
+ |-- mcp/
47
+ |-- snapshots/
48
+ |-- test-results/
49
+ `-- report/
50
+ ```
92
51
 
93
- After generation, invoke the Healer directly with one failing UI test name at a time. The Healer runs the test, replays the failing steps, inspects the current UI for the equivalent element or flow, proposes a patch such as a locator, synchronization, or test-data repair, and reruns until it passes or a guardrail stops the loop. It must not delegate diagnosis or repair to a nested Healer. If the direct Healer lacks `test_list`, `test_run`, `test_debug`, or the browser tools needed for diagnosis, report an MCP-injection failure and stop. API failures may be diagnosed directly.
52
+ Keep trace DOM snapshots, sources, network data, and attachments, but set trace `screenshots: false`. Trace screenshots are screencast frames and can create hundreds of JPEG resources. Explicit screenshots and failure evidence remain available when needed; avoid secrets and sensitive personal data.
94
53
 
95
- If observed product behavior conflicts with the confirmed plan, report the conflict and ask whether it is a regression or intended change. Do not weaken assertions, add arbitrary sleeps/`networkidle`, retry blindly, or skip/fixme tests merely to get green. If the healer concludes the feature is broken, preserve the failure evidence and report it as an application defect rather than treating a skipped test as success.
54
+ Report the plan path, generated test paths, pass/fail/flaky/skipped counts when tests ran, evidence paths, and failure classification. Never report a skipped or unresolved test as success.
96
55
 
97
- ## Preserve evidence
56
+ ## Initialization
98
57
 
99
- All browser-agent and test artifacts must stay under:
58
+ When this Skill or the Playwright Test tools are unavailable, run from the target project:
100
59
 
101
- ```text
102
- .playwright-evidence/
103
- ├── mcp/ # page YAML snapshots, screenshots, and agent logs
104
- ├── snapshots/ # reusable visual/ARIA baselines
105
- ├── test-results/ # failure screenshots, traces, videos, attachments
106
- └── report/ # HTML report
60
+ ```bash
61
+ npx playwright-test-agent init
107
62
  ```
108
63
 
109
- Never save snapshots or screenshots in the project root. Automatic failure evidence is enabled. For key business states, save an explicit screenshot with `testInfo.outputPath('screenshots', '<meaningful-name>.png')` so it remains in `test-results/`. Avoid secrets and sensitive personal data.
110
-
111
- Keep trace DOM snapshots, sources, network data, and attachments, but disable trace screenshots (`use.trace.screenshots: false`). Trace screenshots are screencast frames and can create hundreds of JPEG files under trace `resources/`; they are separate from explicit test screenshots and are not needed for ordinary DOM-based diagnosis.
112
-
113
- Finish by reporting the plan path, generated test paths, environment without secrets, pass/fail/flaky/skipped counts, and evidence paths. Classify failures as application defect, test defect, environment/data problem, or unresolved product decision.
64
+ The initializer installs the Skill, generates the official role definitions, and configures each platform. Restart the active agent session after initialization so newly configured tools are registered.
@@ -10,25 +10,24 @@ const BLOCK_END = '<!-- playwright-test-agent:end -->';
10
10
  const CODEX_INSTRUCTIONS = `${BLOCK_START}
11
11
  ## Playwright Test Agent
12
12
 
13
- For any website, browser workflow, HTTP API, or application-feature testing request, load and follow \`.agents/skills/playwright-test-agent/SKILL.md\` before planning or using browser tools. This skill has priority over ad hoc browser automation.
13
+ For website, browser workflow, HTTP API, or application-feature testing, first load and follow \`.agents/skills/playwright-test-agent/SKILL.md\`.
14
14
 
15
- For UI testing, the Codex subagents are \`playwright_test_planner\`, \`playwright_test_generator\`, and \`playwright_test_healer\`. The main agent may inspect only the local project; it must not open, navigate, inspect, screenshot, or otherwise investigate the deployed website itself. Before starting Planner, the main agent may ask for necessary information that materially affects test purpose, scope, expected outcomes, roles, allowed side effects, data, or environment constraints. It must not ask for UI details Planner can safely discover. Once the objective, deployed URL, and necessary non-discoverable constraints are available, start \`playwright_test_planner\` directly as the live-investigation leaf agent.
15
+ Codex must not start \`playwright_test_planner\`, \`playwright_test_generator\`, \`playwright_test_healer\`, or another Playwright subagent. The main agent performs Planner -> Generator -> Healer in the same session with the project-level \`playwright-test\` MCP tools.
16
16
 
17
- The main agent must start each Playwright Test agent directly. Planner, Generator, and Healer are leaf agents: do not ask them to spawn \`live_planner\`, \`live_generator\`, \`live_healer\`, or any other nested subagent. Each must use its own injected Playwright Test MCP tools directly. If a directly started agent does not expose its required tools, stop and report the Codex MCP-injection problem; do not retry by creating a second- or third-level agent.
17
+ For each phase, read the matching generated role definition and follow its \`developer_instructions\` directly:
18
+ - Planner: \`.codex/agents/playwright_test_planner.toml\`; call \`planner_setup_page\`, \`browser_*\`, then \`planner_save_plan\`.
19
+ - Generator: \`.codex/agents/playwright_test_generator.toml\`; call \`generator_setup_page\`, \`browser_*\`, \`generator_read_log\`, then \`generator_write_test\`.
20
+ - Healer: \`.codex/agents/playwright_test_healer.toml\`; call \`test_list\`, \`test_run\`, \`test_debug\`, and relevant \`browser_*\` tools.
18
21
 
19
- If Planner reports that required test information is missing, invalid, or inconsistent, stop that planning attempt and ask the user for the specific missing information. After the user responds, start \`playwright_test_planner\` again with the corrected information and the previous blocking observation. Do not continue guessing or exploring around the blocker.
22
+ These TOML files are role instructions to read, not agents to launch. If the project-level \`playwright-test\` tools are absent, ask the user to rerun \`npx playwright-test-agent init\` and restart Codex; do not create a subagent fallback. Ask for missing or contradictory test-purpose, access, role, data, or safety information before or during Planner, then resume the same phase after clarification. The Skill contains the shared testing rules.
20
23
  ${BLOCK_END}`;
21
24
 
22
25
  const CLAUDE_INSTRUCTIONS = `${BLOCK_START}
23
26
  ## Playwright Test Agent
24
27
 
25
- For any website, browser workflow, HTTP API, or application-feature testing request, load and follow \`.claude/skills/playwright-test-agent/SKILL.md\` before planning or using browser tools. This skill has priority over ad hoc browser automation.
28
+ For website, browser workflow, HTTP API, or application-feature testing, first load and follow \`.claude/skills/playwright-test-agent/SKILL.md\`.
26
29
 
27
- For UI testing, the Claude Code subagents are \`playwright-test-planner\`, \`playwright-test-generator\`, and \`playwright-test-healer\`. The main agent may inspect only the local project; it must not open, navigate, inspect, screenshot, or otherwise investigate the deployed website itself. Before starting Planner, the main agent may ask for necessary information that materially affects test purpose, scope, expected outcomes, roles, allowed side effects, data, or environment constraints. It must not ask for UI details Planner can safely discover. Once the objective, deployed URL, and necessary non-discoverable constraints are available, start \`playwright-test-planner\` directly as the live-investigation leaf agent.
28
-
29
- The main agent must start each Playwright Test agent directly. Planner, Generator, and Healer are leaf agents: do not ask them to spawn \`live_planner\`, \`live_generator\`, \`live_healer\`, or any other nested subagent. Each must use its own injected Playwright Test MCP tools directly. If a directly started agent does not expose its required tools, stop and report the MCP-injection problem; do not retry by creating a second- or third-level agent.
30
-
31
- If Planner reports that required test information is missing, invalid, or inconsistent, stop that planning attempt and ask the user for the specific missing information. After the user responds, start \`playwright-test-planner\` again with the corrected information and the previous blocking observation. Do not continue guessing or exploring around the blocker.
30
+ Claude Code uses the official \`playwright-test-planner\`, \`playwright-test-generator\`, and \`playwright-test-healer\` subagents. Start each required role directly and never insert a nested or same-role intermediary. Use Planner for all live website investigation, Generator only after the user confirms the saved plan, and Healer for failing generated tests. Ask for missing or contradictory test-purpose, access, role, data, or safety information before or during Planner, then resume the same phase after clarification. The Skill contains the shared testing rules.
32
31
  ${BLOCK_END}`;
33
32
 
34
33
  const exists = async (file) => {
@@ -147,17 +146,128 @@ async function configureClaudeMcp(projectDir) {
147
146
  await writeFile(file, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
148
147
  }
149
148
 
150
- async function configureCodexAgents(projectDir) {
151
- for (const name of ['planner', 'generator', 'healer']) {
152
- const file = path.join(projectDir, '.codex', 'agents', `playwright_test_${name}.toml`);
153
- if (!await exists(file)) continue;
154
- let source = await readFile(file, 'utf8');
155
- if (!source.includes('[mcp_servers.playwright-test.env]')) {
156
- source = `${source.trimEnd()}\n\n[mcp_servers.playwright-test.env]\n` +
157
- 'PLAYWRIGHT_MCP_OUTPUT_DIR = ".playwright-evidence/mcp"\n';
158
- await writeFile(file, source, 'utf8');
149
+ function normalizeTomlTableName(name) {
150
+ return name
151
+ .split('.')
152
+ .map((part) => part.trim().replace(/^(?:"([^"]+)"|'([^']+)')$/, '$1$2'))
153
+ .join('.');
154
+ }
155
+
156
+ function findTomlTables(lines, tableName) {
157
+ const matches = [];
158
+ for (let index = 0; index < lines.length; index += 1) {
159
+ const match = lines[index].match(/^\s*\[([^\]]+)\]\s*(?:#.*)?$/);
160
+ if (match && normalizeTomlTableName(match[1]) === tableName) matches.push(index);
161
+ }
162
+ return matches;
163
+ }
164
+
165
+ function listTomlTableNames(lines) {
166
+ return lines
167
+ .map((line) => line.match(/^\s*\[([^\]]+)\]\s*(?:#.*)?$/))
168
+ .filter(Boolean)
169
+ .map((match) => normalizeTomlTableName(match[1]));
170
+ }
171
+
172
+ function assignmentEnd(lines, start, limit) {
173
+ let square = 0;
174
+ let curly = 0;
175
+ let quote = null;
176
+ let escaped = false;
177
+
178
+ for (let index = start; index < limit; index += 1) {
179
+ for (const character of lines[index]) {
180
+ if (escaped) {
181
+ escaped = false;
182
+ } else if (quote === '"' && character === '\\') {
183
+ escaped = true;
184
+ } else if (quote) {
185
+ if (character === quote) quote = null;
186
+ } else if (character === '"' || character === "'") {
187
+ quote = character;
188
+ } else if (character === '#') {
189
+ break;
190
+ } else if (character === '[') {
191
+ square += 1;
192
+ } else if (character === ']') {
193
+ square -= 1;
194
+ } else if (character === '{') {
195
+ curly += 1;
196
+ } else if (character === '}') {
197
+ curly -= 1;
198
+ }
159
199
  }
200
+ if (square <= 0 && curly <= 0 && !quote) return index + 1;
201
+ }
202
+ return limit;
203
+ }
204
+
205
+ function replaceTomlTable(lines, tableName, assignments, removedKeys = []) {
206
+ const matches = findTomlTables(lines, tableName);
207
+ if (matches.length > 1) {
208
+ throw new Error(`duplicate [${tableName}] tables in .codex/config.toml`);
160
209
  }
210
+
211
+ const body = [`[${tableName}]`, ...Object.entries(assignments).map(([key, value]) => `${key} = ${value}`)];
212
+ if (matches.length === 0) {
213
+ while (lines.at(-1) === '') lines.pop();
214
+ if (lines.length > 0) lines.push('');
215
+ lines.push(...body, '');
216
+ return;
217
+ }
218
+
219
+ const start = matches[0];
220
+ let end = start + 1;
221
+ while (end < lines.length && !/^\s*\[\[?[^\]]+\]\]?\s*(?:#.*)?$/.test(lines[end])) {
222
+ end += 1;
223
+ }
224
+
225
+ const replacedKeys = new Set([...Object.keys(assignments), ...removedKeys]);
226
+ const preserved = [];
227
+ for (let index = start + 1; index < end;) {
228
+ const match = lines[index].match(/^\s*([A-Za-z0-9_-]+)\s*=/);
229
+ if (match && replacedKeys.has(match[1])) {
230
+ index = assignmentEnd(lines, index, end);
231
+ } else {
232
+ preserved.push(lines[index]);
233
+ index += 1;
234
+ }
235
+ }
236
+ while (preserved[0] === '') preserved.shift();
237
+ while (preserved.at(-1) === '') preserved.pop();
238
+ lines.splice(start, end - start, ...body, ...(preserved.length ? ['', ...preserved] : []), '');
239
+ }
240
+
241
+ async function configureCodexMainMcp(projectDir) {
242
+ const codexDir = path.join(projectDir, '.codex');
243
+ const file = path.join(codexDir, 'config.toml');
244
+ await mkdir(codexDir, { recursive: true });
245
+
246
+ const source = await exists(file) ? await readFile(file, 'utf8') : '';
247
+ const lines = source.replace(/\r\n?/g, '\n').split('\n');
248
+ const command = process.platform === 'win32' ? 'cmd' : 'npx';
249
+ const args = process.platform === 'win32'
250
+ ? '["/d", "/s", "/c", "npx", "--no-install", "playwright", "run-test-mcp-server"]'
251
+ : '["--no-install", "playwright", "run-test-mcp-server"]';
252
+
253
+ replaceTomlTable(lines, 'mcp_servers.playwright-test', {
254
+ command: `"${command}"`,
255
+ args,
256
+ cwd: '"."',
257
+ enabled: 'true',
258
+ default_tools_approval_mode: '"approve"',
259
+ }, ['enabled_tools', 'disabled_tools']);
260
+ replaceTomlTable(lines, 'mcp_servers.playwright-test.env', {
261
+ PLAYWRIGHT_MCP_OUTPUT_DIR: '".playwright-evidence/mcp"',
262
+ });
263
+ for (const tableName of new Set(listTomlTableNames(lines))) {
264
+ if (tableName.startsWith('mcp_servers.playwright-test.tools.')) {
265
+ replaceTomlTable(lines, tableName, { approval_mode: '"approve"' });
266
+ }
267
+ }
268
+
269
+ while (lines.at(-1) === '') lines.pop();
270
+ await writeFile(file, `${lines.join('\n')}\n`, 'utf8');
161
271
  }
162
272
 
163
273
  async function initializePlaywright(projectDir, run) {
@@ -187,7 +297,7 @@ async function initializePlaywright(projectDir, run) {
187
297
  await run(projectDir, 'npx', ['--no-install', 'playwright', 'init-agents', '--loop=claude']);
188
298
  await mkdir(path.join(projectDir, '.playwright-evidence', 'mcp'), { recursive: true });
189
299
  await configureClaudeMcp(projectDir);
190
- await configureCodexAgents(projectDir);
300
+ await configureCodexMainMcp(projectDir);
191
301
  }
192
302
 
193
303
  async function runStage(name, action) {
@@ -213,7 +323,8 @@ export async function initializeProject({
213
323
 
214
324
  process.stdout.write(
215
325
  'Playwright Test Agent ready: skills installed for Codex and Claude, ' +
216
- 'tests in playwright-tests/, evidence in .playwright-evidence/.\n',
326
+ 'tests in playwright-tests/, evidence in .playwright-evidence/. ' +
327
+ 'Restart Codex so it loads the project-level playwright-test MCP server.\n',
217
328
  );
218
329
  }
219
330