e2e-ai 1.4.0 → 1.4.3

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
@@ -598,6 +598,16 @@ If e2e-ai is installed globally or as a project dependency, you can use the bina
598
598
 
599
599
  ### Available Tools
600
600
 
601
+ #### Orchestration (workflow automation)
602
+
603
+ | Tool | Description | Input |
604
+ |------|-------------|-------|
605
+ | `e2e_ai_plan_workflow` | Plan an automation workflow — returns an ordered todo list of steps | `goal`, `key?`, `from?`, `skip?`, `voice?`, `trace?`, `scanDir?` |
606
+ | `e2e_ai_execute_step` | Execute a single pipeline step | `step`, `key?`, `voice?`, `trace?`, `scanDir?`, `output?`, `extraArgs?` |
607
+ | `e2e_ai_get_workflow_guide` | Get the full workflow guide explaining how the pipeline works | (none) |
608
+
609
+ #### Project setup
610
+
601
611
  | Tool | Description | Input |
602
612
  |------|-------------|-------|
603
613
  | `e2e_ai_scan_codebase` | Scan project for test files, configs, fixtures, path aliases, and sample test content | `projectRoot?` (defaults to cwd) |
@@ -605,16 +615,35 @@ If e2e-ai is installed globally or as a project dependency, you can use the bina
605
615
  | `e2e_ai_read_agent` | Load an agent prompt by name — returns system prompt + config | `agentName` (e.g. `scenario-agent`) |
606
616
  | `e2e_ai_get_example` | Get the example context markdown template | (none) |
607
617
 
608
- ### Usage with AI Assistants
609
-
610
- Once configured, an AI assistant can:
611
-
612
- 1. **Scan your project** to understand its test structure, fixtures, and conventions
613
- 2. **Read agent prompts** to understand how each pipeline step works
614
- 3. **Validate context files** to ensure they have the right format before running commands
615
- 4. **Get the example template** as a starting point for writing `e2e-ai.context.md`
616
-
617
- This enables AI assistants to help you set up e2e-ai, debug pipeline issues, and generate better project context files.
618
+ ### How AI Orchestration Works
619
+
620
+ The MCP server includes built-in orchestration instructions that teach AI assistants (Claude Code, Cursor, etc.) how to run e2e-ai workflows autonomously. The protocol is:
621
+
622
+ 1. **Plan** The AI calls `e2e_ai_plan_workflow` with your goal. It returns an ordered step list.
623
+ 2. **Approve** The AI presents the plan to you for review. You can adjust steps before proceeding.
624
+ 3. **Execute** The AI runs each step one at a time via `e2e_ai_execute_step`, reporting results between steps. If a step fails, it stops and asks you how to proceed.
625
+
626
+ Each step is executed as a separate job (ideally a subagent) to keep context clean. The AI never runs multiple pipeline steps at once.
627
+
628
+ **Example interaction:**
629
+
630
+ > **You:** "Run the full test pipeline for PROJ-101"
631
+ >
632
+ > **AI:** *Calls `e2e_ai_plan_workflow`*, then presents:
633
+ > 1. `record` — Launch browser codegen + voice recording
634
+ > 2. `transcribe` — Transcribe voice via Whisper
635
+ > 3. `scenario` — Generate YAML test scenario
636
+ > 4. `generate` — Generate Playwright test
637
+ > 5. `refine` — Refactor test with AI
638
+ > 6. `test` — Run Playwright test
639
+ > 7. `heal` — Self-heal if failing (can skip if test passes)
640
+ > 8. `qa` — Generate QA documentation
641
+ >
642
+ > "Does this look right? Ready to start?"
643
+ >
644
+ > **You:** "Skip voice, go ahead"
645
+ >
646
+ > **AI:** *Removes transcribe, executes each step sequentially*
618
647
 
619
648
  ## Library API
620
649
 
package/dist/cli.js CHANGED
@@ -9075,6 +9075,7 @@ function registerInit(program2) {
9075
9075
  success(`Config written: ${configPath}`);
9076
9076
  }
9077
9077
  await copyAgentsToLocal(projectRoot, !!cmdOpts?.nonInteractive);
9078
+ copyWorkflowGuide(projectRoot);
9078
9079
  console.log("");
9079
9080
  success(`Initialization complete!
9080
9081
  `);
@@ -9215,6 +9216,18 @@ async function copyAgentsToLocal(projectRoot, nonInteractive) {
9215
9216
  success(`Agents copied to .e2e-ai/agents/ (${agentFiles.length} files)`);
9216
9217
  return agentFiles.length;
9217
9218
  }
9219
+ function copyWorkflowGuide(projectRoot) {
9220
+ const packageRoot = getPackageRoot();
9221
+ const source = join13(packageRoot, "templates", "workflow.md");
9222
+ const target = join13(projectRoot, ".e2e-ai", "workflow.md");
9223
+ if (!existsSync2(source))
9224
+ return;
9225
+ if (existsSync2(target))
9226
+ return;
9227
+ const content = readFileSync2(source, "utf-8");
9228
+ writeFile(target, content);
9229
+ success("Workflow guide written to .e2e-ai/workflow.md");
9230
+ }
9218
9231
 
9219
9232
  // src/commands/scan.ts
9220
9233
  import { join as join15 } from "node:path";
package/dist/mcp.js CHANGED
@@ -14856,8 +14856,9 @@ class StdioServerTransport {
14856
14856
  }
14857
14857
 
14858
14858
  // src/mcp.ts
14859
- import { readFileSync as readFileSync2 } from "node:fs";
14859
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
14860
14860
  import { join as join2 } from "node:path";
14861
+ import { execSync } from "node:child_process";
14861
14862
 
14862
14863
  // src/utils/scan.ts
14863
14864
  import { readdirSync, existsSync, readFileSync } from "node:fs";
@@ -14957,13 +14958,281 @@ function validateContext(content) {
14957
14958
  }
14958
14959
 
14959
14960
  // src/mcp.ts
14960
- var server = new McpServer({
14961
- name: "e2e-ai",
14962
- version: "1.1.2"
14963
- });
14961
+ var SERVER_INSTRUCTIONS = `
14962
+ # e2e-ai — Orchestration Guide
14963
+
14964
+ You have access to e2e-ai, an AI-powered E2E test automation tool. Follow this protocol when the user asks you to perform any e2e-ai automation.
14965
+
14966
+ ## Core Principle: Plan → Approve → Execute Step-by-Step
14967
+
14968
+ NEVER run multiple pipeline steps at once. Each step is a separate job with its own context.
14969
+
14970
+ ## Protocol
14971
+
14972
+ 1. **Plan first.** Call \`e2e_ai_plan_workflow\` with the user's goal. This returns a structured todo list of steps.
14973
+ 2. **Present the plan.** Show the user the ordered step list with descriptions. Ask for confirmation or adjustments before proceeding.
14974
+ 3. **Execute one step at a time.** For each step in the approved plan:
14975
+ a. Tell the user which step you're about to run and why.
14976
+ b. Call \`e2e_ai_execute_step\` with the step name and parameters.
14977
+ c. Report the result to the user (success, key output, any warnings).
14978
+ d. If the step fails, stop and discuss with the user before continuing.
14979
+ e. Move to the next step only after the current one succeeds.
14980
+ 4. **Use subagents when available.** If your AI platform supports subagents (e.g., Claude Code Agent tool), dispatch each step as a dedicated subagent to preserve context. Each subagent should:
14981
+ - Receive only the context it needs (step name, key, relevant file paths)
14982
+ - Call \`e2e_ai_execute_step\` to do its work
14983
+ - Return the result to the orchestrator
14984
+
14985
+ ## Step Dependencies
14986
+
14987
+ Steps produce artifacts that feed into later steps. The pipeline handles this automatically — each step picks up where the previous one left off. Do not skip steps unless the plan says a step can be skipped.
14988
+
14989
+ ## Interactive Steps
14990
+
14991
+ The \`record\` step opens a browser and requires user interaction. When the plan includes \`record\`:
14992
+ - Tell the user they need to interact with the browser window
14993
+ - The step will block until they close the codegen window
14994
+ - After recording completes, proceed with the next step
14995
+
14996
+ ## When Things Fail
14997
+
14998
+ - If \`test\` fails and \`heal\` is in the plan, that's expected — heal will attempt to fix it
14999
+ - If \`heal\` exhausts all retries, stop and show the user the last error output
15000
+ - For any other failure, stop and ask the user how to proceed
15001
+
15002
+ ## Available Workflows
15003
+
15004
+ - **Full test pipeline**: record → transcribe → scenario → generate → refine → test → heal → qa
15005
+ - **From existing recording**: transcribe → scenario → generate → refine → test → heal → qa
15006
+ - **AI-only (no recording)**: scenario → generate → refine → test → heal → qa
15007
+ - **Generate from scenario**: generate → refine → test → heal → qa
15008
+ - **Test + heal loop**: test → heal
15009
+ - **Scanner pipeline**: scan → analyze → push
15010
+ - **Single step**: any individual command
15011
+
15012
+ Always use \`e2e_ai_plan_workflow\` to determine the right steps — don't guess.
15013
+ `.trim();
15014
+ var TEST_PIPELINE_STEPS = [
15015
+ {
15016
+ name: "record",
15017
+ description: "Launch Playwright codegen in the browser. Optionally records voice narration for richer test scenarios.",
15018
+ produces: "codegen .ts file + optional .wav voice recording",
15019
+ requires: "none",
15020
+ interactive: true
15021
+ },
15022
+ {
15023
+ name: "transcribe",
15024
+ description: "Transcribe the voice recording via OpenAI Whisper. Merges timestamped voice comments into the codegen file.",
15025
+ produces: "transcript JSON + annotated codegen file",
15026
+ requires: "voice recording from record step",
15027
+ interactive: false,
15028
+ canSkip: "No voice recording exists or voice is disabled"
15029
+ },
15030
+ {
15031
+ name: "scenario",
15032
+ description: "AI analyzes the codegen + transcript and generates a structured YAML test scenario with semantic steps and expected results.",
15033
+ produces: "YAML test scenario file",
15034
+ requires: "codegen file (+ optional transcript)",
15035
+ interactive: false
15036
+ },
15037
+ {
15038
+ name: "generate",
15039
+ description: "AI converts the YAML scenario into a complete Playwright .test.ts file using project conventions from context.md.",
15040
+ produces: "Playwright .test.ts file",
15041
+ requires: "YAML scenario file",
15042
+ interactive: false
15043
+ },
15044
+ {
15045
+ name: "refine",
15046
+ description: "AI refactors the test: replaces raw selectors with semantic alternatives, adds proper timeouts, uses project helpers.",
15047
+ produces: "improved .test.ts file (in-place)",
15048
+ requires: "Playwright .test.ts file",
15049
+ interactive: false
15050
+ },
15051
+ {
15052
+ name: "test",
15053
+ description: "Run the Playwright test with trace/video/screenshot capture. Reports pass/fail status.",
15054
+ produces: "test results + trace files",
15055
+ requires: "Playwright .test.ts file",
15056
+ interactive: false
15057
+ },
15058
+ {
15059
+ name: "heal",
15060
+ description: "If the test failed, AI diagnoses the failure and patches the test. Retries up to 3 times with different strategies.",
15061
+ produces: "patched .test.ts file (if test was failing)",
15062
+ requires: "failing test + error output",
15063
+ interactive: false,
15064
+ canSkip: "Test already passes"
15065
+ },
15066
+ {
15067
+ name: "qa",
15068
+ description: "Generate formal QA documentation: markdown test case with preconditions, steps table, and optional Zephyr XML export.",
15069
+ produces: "QA markdown + optional Zephyr XML",
15070
+ requires: "Playwright .test.ts file + scenario",
15071
+ interactive: false
15072
+ }
15073
+ ];
15074
+ var SCANNER_PIPELINE_STEPS = [
15075
+ {
15076
+ name: "scan",
15077
+ description: "Scan the codebase AST: extract routes, components, hooks, imports, and dependency graph.",
15078
+ produces: "ast-scan.json with full codebase structure",
15079
+ requires: "none",
15080
+ interactive: false
15081
+ },
15082
+ {
15083
+ name: "analyze",
15084
+ description: "AI analyzes the AST scan to identify features, workflows, components, and generate test scenarios.",
15085
+ produces: "qa-map.json with features, workflows, scenarios",
15086
+ requires: "ast-scan.json from scan step",
15087
+ interactive: false
15088
+ },
15089
+ {
15090
+ name: "push",
15091
+ description: "Push the QA map to a remote API endpoint for integration with external tools.",
15092
+ produces: "push confirmation with version info",
15093
+ requires: "qa-map.json from analyze step + API config",
15094
+ interactive: false
15095
+ }
15096
+ ];
15097
+ var ALL_STEPS = [...TEST_PIPELINE_STEPS, ...SCANNER_PIPELINE_STEPS];
15098
+ function planWorkflow(goal, options) {
15099
+ const goalLower = goal.toLowerCase();
15100
+ const notes = [];
15101
+ const isScannerGoal = /\b(scan|analyze|qa.?map|feature.?analy|push.?qa|codebase.?scan)\b/.test(goalLower);
15102
+ const isSingleStep = ALL_STEPS.some((s) => goalLower === s.name || goalLower === `run ${s.name}`);
15103
+ let stepDefs;
15104
+ if (isScannerGoal && !isSingleStep) {
15105
+ stepDefs = [...SCANNER_PIPELINE_STEPS];
15106
+ if (!/\bpush\b/.test(goalLower)) {
15107
+ stepDefs = stepDefs.filter((s) => s.name !== "push");
15108
+ notes.push("Push step excluded — add it if you want to upload the QA map to a remote API.");
15109
+ }
15110
+ } else if (isSingleStep) {
15111
+ const stepName = ALL_STEPS.find((s) => goalLower.includes(s.name)).name;
15112
+ stepDefs = ALL_STEPS.filter((s) => s.name === stepName);
15113
+ } else {
15114
+ stepDefs = [...TEST_PIPELINE_STEPS];
15115
+ if (options.from) {
15116
+ const fromIdx = stepDefs.findIndex((s) => s.name === options.from);
15117
+ if (fromIdx > 0) {
15118
+ const skipped = stepDefs.slice(0, fromIdx).map((s) => s.name);
15119
+ stepDefs = stepDefs.slice(fromIdx);
15120
+ notes.push(`Starting from "${options.from}" — skipping: ${skipped.join(", ")}`);
15121
+ }
15122
+ } else {
15123
+ if (/\b(from recording|existing recording|already recorded)\b/.test(goalLower)) {
15124
+ stepDefs = stepDefs.filter((s) => s.name !== "record");
15125
+ notes.push("Skipping record — using existing recording files.");
15126
+ }
15127
+ if (/\b(from scenario|existing scenario|manual scenario|yaml)\b/.test(goalLower)) {
15128
+ stepDefs = stepDefs.filter((s) => !["record", "transcribe", "scenario"].includes(s.name));
15129
+ notes.push("Starting from generate — using existing scenario YAML.");
15130
+ }
15131
+ if (/\b(generate.?only|just.?generate|no.?record)\b/.test(goalLower)) {
15132
+ stepDefs = stepDefs.filter((s) => !["record", "transcribe"].includes(s.name));
15133
+ }
15134
+ if (/\b(test.?and.?heal|test.?heal|heal.?loop|fix.?test|self.?heal)\b/.test(goalLower)) {
15135
+ stepDefs = stepDefs.filter((s) => ["test", "heal"].includes(s.name));
15136
+ }
15137
+ if (/\b(refine|refactor)\b/.test(goalLower) && !/\brun\b/.test(goalLower)) {
15138
+ stepDefs = stepDefs.filter((s) => s.name === "refine");
15139
+ }
15140
+ if (/\bqa\b/.test(goalLower) && /\b(doc|only|generate)\b/.test(goalLower)) {
15141
+ stepDefs = stepDefs.filter((s) => s.name === "qa");
15142
+ }
15143
+ }
15144
+ }
15145
+ if (options.skip?.length) {
15146
+ stepDefs = stepDefs.filter((s) => !options.skip.includes(s.name));
15147
+ notes.push(`Skipping: ${options.skip.join(", ")}`);
15148
+ }
15149
+ if (options.voice === false) {
15150
+ stepDefs = stepDefs.filter((s) => s.name !== "transcribe");
15151
+ notes.push("Voice disabled — transcribe step removed.");
15152
+ }
15153
+ const cliBase = "e2e-ai";
15154
+ const steps = stepDefs.map((s, i) => {
15155
+ const args = [s.name];
15156
+ if (options.key && !["scan", "analyze", "push"].includes(s.name)) {
15157
+ args.push("--key", options.key);
15158
+ }
15159
+ if (s.name === "record") {
15160
+ if (options.voice === false)
15161
+ args.push("--no-voice");
15162
+ if (options.trace === false)
15163
+ args.push("--no-trace");
15164
+ }
15165
+ if (s.name === "scan" && options.scanDir) {
15166
+ args.push("--scan-dir", options.scanDir);
15167
+ }
15168
+ return {
15169
+ order: i + 1,
15170
+ name: s.name,
15171
+ description: s.description,
15172
+ command: `${cliBase} ${args.join(" ")}`,
15173
+ produces: s.produces,
15174
+ interactive: s.interactive,
15175
+ canSkip: s.canSkip
15176
+ };
15177
+ });
15178
+ const pipeline2 = isScannerGoal ? "scanner" : isSingleStep ? "single" : "test";
15179
+ if (!options.key && pipeline2 === "test" && steps.length > 1) {
15180
+ notes.push("No --key provided. Use --key <ISSUE-KEY> to organize files by issue.");
15181
+ }
15182
+ return { goal, pipeline: pipeline2, steps, notes };
15183
+ }
15184
+ function executeStep(stepName, options) {
15185
+ const args = [stepName];
15186
+ if (options.key && !["scan", "analyze", "push"].includes(stepName)) {
15187
+ args.push("--key", options.key);
15188
+ }
15189
+ if (stepName === "record") {
15190
+ if (options.voice === false)
15191
+ args.push("--no-voice");
15192
+ if (options.trace === false)
15193
+ args.push("--no-trace");
15194
+ }
15195
+ if (stepName === "scan" && options.scanDir) {
15196
+ args.push("--scan-dir", options.scanDir);
15197
+ }
15198
+ if (options.output) {
15199
+ args.push("--output", options.output);
15200
+ }
15201
+ if (options.extraArgs?.length) {
15202
+ args.push(...options.extraArgs);
15203
+ }
15204
+ const pkgRoot = getPackageRoot();
15205
+ const cliBin = join2(pkgRoot, "dist", "cli.js");
15206
+ const command = `node ${cliBin} ${args.join(" ")}`;
15207
+ try {
15208
+ const stdout = execSync(command, {
15209
+ cwd: process.cwd(),
15210
+ encoding: "utf-8",
15211
+ timeout: 300000,
15212
+ env: { ...process.env },
15213
+ stdio: ["pipe", "pipe", "pipe"]
15214
+ });
15215
+ return { success: true, output: stdout, command };
15216
+ } catch (err) {
15217
+ const stderr = err.stderr?.toString() ?? "";
15218
+ const stdout = err.stdout?.toString() ?? "";
15219
+ return {
15220
+ success: false,
15221
+ output: `EXIT CODE: ${err.status ?? "unknown"}
15222
+
15223
+ STDOUT:
15224
+ ${stdout}
15225
+
15226
+ STDERR:
15227
+ ${stderr}`,
15228
+ command
15229
+ };
15230
+ }
15231
+ }
15232
+ var server = new McpServer({ name: "e2e-ai", version: "1.2.0" }, { instructions: SERVER_INSTRUCTIONS });
14964
15233
  server.registerTool("e2e_ai_scan_codebase", {
14965
15234
  title: "Scan Codebase",
14966
- description: "Scan a project directory for test files, configs, fixtures, path aliases, and sample test content",
15235
+ description: "Scan a project directory for test files, configs, fixtures, path aliases, and sample test content. Use this during project setup or to understand test infrastructure.",
14967
15236
  inputSchema: exports_external.object({
14968
15237
  projectRoot: exports_external.string().optional().describe("Project root directory (defaults to cwd)")
14969
15238
  })
@@ -14976,7 +15245,7 @@ server.registerTool("e2e_ai_scan_codebase", {
14976
15245
  });
14977
15246
  server.registerTool("e2e_ai_validate_context", {
14978
15247
  title: "Validate Context",
14979
- description: "Validate that a context markdown file contains all required sections",
15248
+ description: "Validate that a context markdown file contains all required sections (Application, Test Infrastructure, Feature Methods, Import Conventions, Selector Conventions, Test Structure Template, Utility Patterns).",
14980
15249
  inputSchema: exports_external.object({
14981
15250
  content: exports_external.string().describe("The markdown content of the context file to validate")
14982
15251
  })
@@ -14988,7 +15257,7 @@ server.registerTool("e2e_ai_validate_context", {
14988
15257
  });
14989
15258
  server.registerTool("e2e_ai_read_agent", {
14990
15259
  title: "Read Agent",
14991
- description: "Read an agent prompt definition by name. Returns the agent name, system prompt, and config (model, max_tokens, temperature).",
15260
+ description: "Read an agent prompt definition by name. Returns the agent system prompt and config. Agents: transcript-agent, scenario-agent, playwright-generator-agent, refactor-agent, self-healing-agent, qa-testcase-agent, feature-analyzer-agent, scenario-planner-agent, init-agent.",
14992
15261
  inputSchema: exports_external.object({
14993
15262
  agentName: exports_external.string().describe("Agent name (e.g. scenario-agent, playwright-generator-agent)")
14994
15263
  })
@@ -15014,7 +15283,7 @@ server.registerTool("e2e_ai_read_agent", {
15014
15283
  });
15015
15284
  server.registerTool("e2e_ai_get_example", {
15016
15285
  title: "Get Example Context",
15017
- description: "Returns the full example context markdown file that shows the expected format for .e2e-ai/context.md",
15286
+ description: "Returns the full example context markdown file that shows the expected format for .e2e-ai/context.md.",
15018
15287
  inputSchema: exports_external.object({})
15019
15288
  }, async () => {
15020
15289
  try {
@@ -15030,6 +15299,87 @@ server.registerTool("e2e_ai_get_example", {
15030
15299
  };
15031
15300
  }
15032
15301
  });
15302
+ server.registerTool("e2e_ai_plan_workflow", {
15303
+ title: "Plan Workflow",
15304
+ description: "Plan an e2e-ai automation workflow. Call this FIRST when the user asks to run any automation. " + "Returns an ordered list of steps (todo list) that should be executed one at a time. " + "Present the plan to the user for approval before executing any step.",
15305
+ inputSchema: exports_external.object({
15306
+ goal: exports_external.string().describe('What the user wants to achieve. Examples: "run full pipeline for PROJ-101", ' + '"generate test from existing recording", "scan codebase and analyze features", ' + '"heal failing test PROJ-101", "refactor test PROJ-101"'),
15307
+ key: exports_external.string().optional().describe("Issue key (e.g. PROJ-101, LIN-42)"),
15308
+ from: exports_external.string().optional().describe("Start from a specific step (skip all prior steps)"),
15309
+ skip: exports_external.array(exports_external.string()).optional().describe('Steps to skip (e.g. ["transcribe", "heal"])'),
15310
+ voice: exports_external.boolean().optional().describe("Enable voice recording (default: true)"),
15311
+ trace: exports_external.boolean().optional().describe("Enable trace capture (default: true)"),
15312
+ scanDir: exports_external.string().optional().describe("Directory to scan (for scanner pipeline)")
15313
+ })
15314
+ }, async ({ goal, key, from, skip, voice, trace, scanDir }) => {
15315
+ const plan = planWorkflow(goal, { key, from, skip, voice, trace, scanDir });
15316
+ return {
15317
+ content: [{
15318
+ type: "text",
15319
+ text: JSON.stringify(plan, null, 2)
15320
+ }]
15321
+ };
15322
+ });
15323
+ server.registerTool("e2e_ai_execute_step", {
15324
+ title: "Execute Pipeline Step",
15325
+ description: "Execute a single e2e-ai pipeline step. Call this ONE STEP AT A TIME from an approved plan. " + "Each step produces artifacts consumed by later steps. " + "If your AI platform supports subagents, run each step in a dedicated subagent to preserve context. " + 'The "record" step is interactive and will open a browser window — the user must interact with it.',
15326
+ inputSchema: exports_external.object({
15327
+ step: exports_external.string().describe("Step name: record, transcribe, scenario, generate, refine, test, heal, qa, scan, analyze, push"),
15328
+ key: exports_external.string().optional().describe("Issue key (e.g. PROJ-101)"),
15329
+ voice: exports_external.boolean().optional().describe("Enable voice recording (record step only)"),
15330
+ trace: exports_external.boolean().optional().describe("Enable trace capture (record step only)"),
15331
+ scanDir: exports_external.string().optional().describe("Directory to scan (scan step only)"),
15332
+ output: exports_external.string().optional().describe("Custom output path (scan/analyze steps)"),
15333
+ extraArgs: exports_external.array(exports_external.string()).optional().describe("Additional CLI arguments")
15334
+ })
15335
+ }, async ({ step, key, voice, trace, scanDir, output, extraArgs }) => {
15336
+ const validSteps = ALL_STEPS.map((s) => s.name);
15337
+ if (!validSteps.includes(step)) {
15338
+ return {
15339
+ content: [{
15340
+ type: "text",
15341
+ text: `Error: Unknown step "${step}". Valid steps: ${validSteps.join(", ")}`
15342
+ }],
15343
+ isError: true
15344
+ };
15345
+ }
15346
+ const result = executeStep(step, { key, voice, trace, scanDir, output, extraArgs });
15347
+ return {
15348
+ content: [{
15349
+ type: "text",
15350
+ text: JSON.stringify({
15351
+ step,
15352
+ success: result.success,
15353
+ command: result.command,
15354
+ output: result.output
15355
+ }, null, 2)
15356
+ }]
15357
+ };
15358
+ });
15359
+ server.registerTool("e2e_ai_get_workflow_guide", {
15360
+ title: "Get Workflow Guide",
15361
+ description: "Returns the e2e-ai workflow guide explaining how the pipeline works, step by step. Useful for understanding what each step does and how they connect.",
15362
+ inputSchema: exports_external.object({})
15363
+ }, async () => {
15364
+ try {
15365
+ const guidePath = join2(getPackageRoot(), "templates", "workflow.md");
15366
+ if (!existsSync2(guidePath)) {
15367
+ return {
15368
+ content: [{ type: "text", text: "Error: workflow.md not found in templates" }],
15369
+ isError: true
15370
+ };
15371
+ }
15372
+ const content = readFileSync2(guidePath, "utf-8");
15373
+ return {
15374
+ content: [{ type: "text", text: content }]
15375
+ };
15376
+ } catch (err) {
15377
+ return {
15378
+ content: [{ type: "text", text: `Error: ${err.message}` }],
15379
+ isError: true
15380
+ };
15381
+ }
15382
+ });
15033
15383
  async function main() {
15034
15384
  const transport = new StdioServerTransport;
15035
15385
  await server.connect(transport);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "e2e-ai",
3
3
  "description": "AI-powered test automation pipeline — record, transcribe, generate, heal and ship Playwright tests from a single CLI",
4
- "version": "1.4.0",
4
+ "version": "1.4.3",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "bin": {
@@ -225,7 +225,11 @@ if (!existsSync(storageStatePath)) {
225
225
  }
226
226
 
227
227
  // Spawn codegen with --load-storage (skip login) and --save-storage (update cache)
228
+ const harPath = traceEnabled ? resolve(issueDir, `har-${timestamp}.har`) : null;
228
229
  const codegenArgs = ['playwright', 'codegen', '--output', outputPath];
230
+ if (traceEnabled && harPath) {
231
+ codegenArgs.push('--save-har', harPath);
232
+ }
229
233
  if (existsSync(storageStatePath)) {
230
234
  codegenArgs.push('--load-storage', storageStatePath);
231
235
  codegenArgs.push('--save-storage', storageStatePath);
@@ -336,7 +340,7 @@ child.on('exit', async (code) => {
336
340
  }
337
341
  }
338
342
 
339
- // --- Trace: inject test.use and replay ---
343
+ // --- Trace: inject test.use({ trace: 'on' }) and run replay to generate trace ---
340
344
  if (existsSync(outputPath)) {
341
345
  const codegenSrc = readFileSync(outputPath, 'utf-8');
342
346
  if (!codegenSrc.includes("test.use({ trace: 'on' })")) {
@@ -365,5 +369,9 @@ child.on('exit', async (code) => {
365
369
  }
366
370
  }
367
371
 
372
+ if (harPath && existsSync(harPath)) {
373
+ console.error(`HAR saved: ${relative(root, harPath)}`);
374
+ }
375
+
368
376
  process.exit(code ?? 0);
369
377
  });
@@ -0,0 +1,280 @@
1
+ # e2e-ai Workflow Guide
2
+
3
+ This file explains how e2e-ai works and how to use it. Keep it as a reference in your `.e2e-ai/` folder.
4
+
5
+ ---
6
+
7
+ ## How It Works
8
+
9
+ e2e-ai converts manual browser recordings into stable, documented Playwright tests. An AI pipeline processes your recording through multiple stages, each producing an artifact that feeds the next.
10
+
11
+ ```
12
+ record → transcribe → scenario → generate → refine → test → heal → qa
13
+ ```
14
+
15
+ **In short:** You record yourself testing in the browser (optionally narrating what you're doing), and e2e-ai turns that into a production-ready Playwright test with QA documentation.
16
+
17
+ **Two ways to run it:**
18
+ - **CLI**: Run commands directly (`e2e-ai run --key PROJ-101`)
19
+ - **AI assistant**: Ask your AI tool (Claude Code, Cursor, etc.) — the MCP server guides it through the pipeline step by step, asking for your approval before starting
20
+
21
+ ---
22
+
23
+ ## Setup
24
+
25
+ After running `e2e-ai init`, you need a **context file** (`.e2e-ai/context.md`) that teaches the AI about your project's test conventions — fixtures, helpers, selectors, login flows, etc.
26
+
27
+ **How to create it:** Use the `init-agent` in your AI tool (Claude Code, Cursor, etc.). If you have the MCP server configured, the AI can scan your codebase automatically with `e2e_ai_scan_codebase`.
28
+
29
+ ---
30
+
31
+ ## The Standard Workflow
32
+
33
+ ### 1. Record (`record`)
34
+
35
+ Opens Playwright codegen in your browser. You interact with your app while codegen captures every action.
36
+
37
+ ```bash
38
+ e2e-ai record --key PROJ-101
39
+ ```
40
+
41
+ **With voice** (default): Records your microphone while you narrate what you're testing. Press `R` to pause/resume audio. Your voice comments become test documentation.
42
+
43
+ **Without voice:**
44
+ ```bash
45
+ e2e-ai record --key PROJ-101 --no-voice
46
+ ```
47
+
48
+ **Output:** `codegen-<timestamp>.ts` + `voice-<timestamp>.wav` (if voice enabled)
49
+
50
+ ### 2. Transcribe (`transcribe`)
51
+
52
+ Sends the voice recording to OpenAI Whisper. Gets back timestamped text segments and injects them as comments into the codegen file:
53
+
54
+ ```typescript
55
+ // [Voice 00:12 - 00:15] "Now I'm checking the item list loads correctly"
56
+ await page.getByRole('button', { name: 'Items' }).click();
57
+ ```
58
+
59
+ **Skipped automatically** if no voice recording exists.
60
+
61
+ ### 3. Scenario (`scenario`)
62
+
63
+ Two AI agents process the codegen + transcript:
64
+
65
+ 1. **transcript-agent** — Maps your voice comments to codegen actions, translates non-English speech, classifies what's test-relevant vs. noise
66
+ 2. **scenario-agent** — Converts everything into a structured YAML test scenario with semantic steps and expected results
67
+
68
+ ```yaml
69
+ name: "Items list: verify weekly view headers"
70
+ steps:
71
+ - number: 1
72
+ action: "Log in with valid credentials"
73
+ expectedResult: "User is redirected to dashboard"
74
+ - number: 2
75
+ action: "Navigate to Items section"
76
+ selector: "getByRole('button', { name: 'Items' })"
77
+ expectedResult: "Items list is displayed"
78
+ ```
79
+
80
+ **Without voice:** The scenario is generated from codegen actions alone (the AI infers intent from selectors and page structure).
81
+
82
+ ### 4. Generate (`generate`)
83
+
84
+ The **playwright-generator-agent** takes the YAML scenario + your project context (`.e2e-ai/context.md`) and writes a complete `.test.ts` file using your project's fixtures, helpers, and conventions.
85
+
86
+ ### 5. Refine (`refine`)
87
+
88
+ The **refactor-agent** improves the generated test:
89
+ - Replaces raw CSS selectors with semantic alternatives (`getByRole`, `getByText`)
90
+ - Uses your project's helper methods where available
91
+ - Adds proper timeouts to assertions
92
+ - Replaces `waitForTimeout()` with proper waits
93
+
94
+ ### 6. Test (`test`)
95
+
96
+ Runs the test with Playwright, capturing traces, video, and screenshots.
97
+
98
+ - **If it passes** → moves to QA documentation
99
+ - **If it fails** → moves to self-healing
100
+
101
+ ### 7. Heal (`heal`)
102
+
103
+ The **self-healing-agent** diagnoses the failure and patches the test. Up to 3 attempts, each trying a different fix strategy:
104
+
105
+ | Failure Type | Fix Strategy |
106
+ |---|---|
107
+ | Selector changed | Try semantic selectors, stable attributes |
108
+ | Timing issue | Add waits, increase timeouts |
109
+ | Element not interactable | Wait for enabled state, scroll into view |
110
+ | Assertion mismatch | Update expected values |
111
+ | Navigation failure | Add `waitForURL`, `waitForLoadState` |
112
+
113
+ Never removes assertions. Never changes test structure. Adds `// HEALED: <reason>` comments.
114
+
115
+ **Skipped automatically** if the test passes.
116
+
117
+ ### 8. QA (`qa`)
118
+
119
+ The **qa-testcase-agent** generates formal QA documentation:
120
+ - Markdown test case (ID, preconditions, steps table, postconditions)
121
+ - Zephyr XML (optional, if configured)
122
+
123
+ ---
124
+
125
+ ## Running the Full Pipeline
126
+
127
+ ```bash
128
+ # Everything in one command
129
+ e2e-ai run --key PROJ-101
130
+
131
+ # Without voice recording
132
+ e2e-ai run --key PROJ-101 --no-voice
133
+
134
+ # Start from a specific step (skip prior steps)
135
+ e2e-ai run --key PROJ-101 --from scenario
136
+
137
+ # Skip specific steps
138
+ e2e-ai run --key PROJ-101 --skip heal
139
+
140
+ # Common: generate from existing recording data
141
+ e2e-ai run --key PROJ-101 --from generate
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Workflow Variations
147
+
148
+ ### With Issue Tracker (Jira / Linear)
149
+
150
+ Set `inputSource: 'jira'` or `'linear'` in config. The scenario step will fetch issue context (summary, acceptance criteria, labels) and use it to align the test scenario with the ticket.
151
+
152
+ ```bash
153
+ e2e-ai run --key PROJ-101 # fetches Jira/Linear issue automatically
154
+ ```
155
+
156
+ ### Without Issue Tracker
157
+
158
+ Set `inputSource: 'none'` (default). Use any identifier as the key, or omit it entirely:
159
+
160
+ ```bash
161
+ e2e-ai run --key login-flow
162
+ e2e-ai run my-session
163
+ ```
164
+
165
+ ### AI-Only (No Recording)
166
+
167
+ Write the YAML scenario manually or have it generated from an existing codegen file, then run the AI pipeline:
168
+
169
+ ```bash
170
+ e2e-ai generate --key PROJ-101
171
+ e2e-ai test --key PROJ-101
172
+ e2e-ai heal --key PROJ-101
173
+ e2e-ai qa --key PROJ-101
174
+ ```
175
+
176
+ ### Existing Test Improvement
177
+
178
+ Refactor an existing test to follow project conventions:
179
+
180
+ ```bash
181
+ e2e-ai refine --key PROJ-101
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Scanner Pipeline (Separate Workflow)
187
+
188
+ Scans your codebase to build a QA map of features, workflows, and test scenarios:
189
+
190
+ ```bash
191
+ # 1. Extract AST (routes, components, hooks)
192
+ e2e-ai scan
193
+
194
+ # 2. AI analysis → features, workflows, scenarios
195
+ e2e-ai analyze
196
+
197
+ # 3. Push QA map to remote API (optional)
198
+ e2e-ai push
199
+ ```
200
+
201
+ This is independent from the test pipeline — use it to get an overview of your app's testable surface.
202
+
203
+ ---
204
+
205
+ ## AI-Assisted Workflow (MCP)
206
+
207
+ If you have the e2e-ai MCP server configured, you can ask your AI assistant to run the pipeline for you. The MCP server teaches the AI how to orchestrate the workflow:
208
+
209
+ 1. **You say:** "Run the full test pipeline for PROJ-101" (or any variation)
210
+ 2. **AI plans:** Calls `e2e_ai_plan_workflow` → gets an ordered step list
211
+ 3. **AI shows plan:** Presents the steps and asks for your approval
212
+ 4. **You adjust:** "Skip voice" / "Start from generate" / "Looks good, go"
213
+ 5. **AI executes:** Runs each step one at a time via `e2e_ai_execute_step`, reporting results between steps
214
+
215
+ Each step runs as a separate subagent (when supported by the AI platform) to keep context clean and focused. If a step fails, the AI stops and asks you what to do.
216
+
217
+ **Example prompts you can give your AI assistant:**
218
+ - "Run the full pipeline for PROJ-101"
219
+ - "Generate a test from the existing recording for PROJ-101, skip voice"
220
+ - "Just run test and heal for PROJ-101"
221
+ - "Scan the codebase and analyze features"
222
+ - "Refactor the test for PROJ-101"
223
+
224
+ ---
225
+
226
+ ## File Structure
227
+
228
+ After running the pipeline for `PROJ-101`:
229
+
230
+ ```
231
+ .e2e-ai/
232
+ config.ts ← your configuration
233
+ context.md ← project context (teach AI your conventions)
234
+ workflow.md ← this file
235
+ agents/ ← AI agent prompts (customizable)
236
+ PROJ-101/ ← working files (codegen, recordings)
237
+
238
+ e2e/
239
+ tests/PROJ-101/
240
+ PROJ-101.yaml ← generated scenario
241
+ PROJ-101.test.ts ← generated Playwright test
242
+
243
+ qa/
244
+ PROJ-101.md ← QA documentation
245
+ ```
246
+
247
+ ---
248
+
249
+ ## Environment Variables
250
+
251
+ ```bash
252
+ # Required
253
+ OPENAI_API_KEY=sk-... # For LLM calls + Whisper transcription
254
+
255
+ # Optional
256
+ AI_PROVIDER=openai # openai | anthropic
257
+ AI_MODEL=gpt-4o # Model override
258
+ ANTHROPIC_API_KEY=sk-ant-... # If using Anthropic
259
+ BASE_URL=https://your-app.com # Your application URL
260
+ ```
261
+
262
+ ---
263
+
264
+ ## Quick Reference
265
+
266
+ | Command | What it does |
267
+ |---|---|
268
+ | `e2e-ai init` | Create config + copy agents |
269
+ | `e2e-ai record --key X` | Record browser session |
270
+ | `e2e-ai transcribe --key X` | Transcribe voice recording |
271
+ | `e2e-ai scenario --key X` | Generate YAML test scenario |
272
+ | `e2e-ai generate --key X` | Generate Playwright test |
273
+ | `e2e-ai refine --key X` | Refactor test with AI |
274
+ | `e2e-ai test --key X` | Run Playwright test |
275
+ | `e2e-ai heal --key X` | Auto-fix failing test |
276
+ | `e2e-ai qa --key X` | Generate QA documentation |
277
+ | `e2e-ai run --key X` | Run full pipeline |
278
+ | `e2e-ai scan` | Scan codebase AST |
279
+ | `e2e-ai analyze` | AI feature/scenario analysis |
280
+ | `e2e-ai push` | Push QA map to API |