@yasserkhanorg/e2e-agents 1.7.1 → 1.7.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
@@ -56,7 +56,67 @@ npx e2e-ai-agents llm-health
56
56
 
57
57
  ## Route-Families Training
58
58
 
59
- Route-families map your source files to features, test directories, and user flows. They are the context that powers accurate impact analysis. The `train` command bootstraps and maintains this manifest.
59
+ ### What it produces
60
+
61
+ The `train` command builds a **knowledge map** of your codebase — a single JSON file (`route-families.json`) that maps source files to features, test directories, and user flows. This is not ML training; no model is trained. It's building a structured manifest like:
62
+
63
+ ```json
64
+ {
65
+ "id": "channels",
66
+ "routes": ["/{team}/channels/{channel}"],
67
+ "priority": "P0",
68
+ "webappPaths": ["src/components/channel_header/**"],
69
+ "serverPaths": ["server/channels/api4/channel*.go", "server/channels/app/channel*.go"],
70
+ "specDirs": ["specs/functional/channels/"],
71
+ "userFlows": ["Create channel", "Archive channel", "Search in channel"],
72
+ "components": ["ChannelHeader", "ChannelSidebar"]
73
+ }
74
+ ```
75
+
76
+ ### Why the tool needs this
77
+
78
+ When a PR changes `server/channels/app/channel.go`, the tool needs to answer: **"which E2E tests should I run?"** Without the manifest, it has no idea. With it:
79
+
80
+ ```
81
+ channel.go changed
82
+ → belongs to "channels" family
83
+ → specs are in specs/functional/channels/
84
+ → run those tests
85
+ → flag if coverage is missing for the affected user flows
86
+ ```
87
+
88
+ Every downstream command (`impact`, `plan`, `generate`, `heal`, `e2e-qa-agent`) reads this manifest to understand the codebase.
89
+
90
+ ### How scanning works
91
+
92
+ The scanner uses 4 strategies to build the `file → family` mapping:
93
+
94
+ 1. **Directory matching** — `src/channels/` + `tests/channels/` share a name → channels family
95
+ 2. **Test-derived** — `specs/functional/channels/drafts/` exists with spec files → drafts family (even if source code is scattered across components/actions/reducers)
96
+ 3. **Server-derived** — `api4/channel.go` + `app/channel.go` + `store/channel_store.go` span 3 backend tiers → channel family (related files like `channel_bookmark.go` are grouped under the parent)
97
+ 4. **Name-matched** — `src/utils/channels.ts` or `server/public/model/channel.go` basename matches → add to channels family's paths
98
+
99
+ ### What LLM enrichment adds
100
+
101
+ The scanner finds files. The LLM reads code samples and adds **semantic metadata** the scanner can't determine:
102
+ - Accurate URL routes (`/{team}/channels/{channel}` instead of guessed `/channels`)
103
+ - Priority classification (P0 critical user flow vs P2 nice-to-have)
104
+ - Human-readable user flows ("Create channel", "Search messages")
105
+ - React component and page object names
106
+
107
+ This metadata makes impact analysis smarter — it can prioritize P0 flows and suggest specific test scenarios.
108
+
109
+ ### What validation does
110
+
111
+ The `--validate` flag measures manifest accuracy against **real git history**. It's not training data — it's a quality check:
112
+
113
+ ```
114
+ 835 commits → 5105 changed files → 3223 bound to a family = 63% coverage
115
+ ```
116
+
117
+ This tells you the manifest is complete enough. If coverage were 30%, impact analysis would be blind to most code changes.
118
+
119
+ ### Usage
60
120
 
61
121
  ```bash
62
122
  # Scan your codebase + LLM enrichment (default)
@@ -72,18 +132,19 @@ npx e2e-ai-agents train --path /path/to/project --validate --since HEAD~50
72
132
  npx e2e-ai-agents train --path /path/to/project --validate --since HEAD~20
73
133
  ```
74
134
 
75
- **Why LLM enrichment is on by default:** The manifest exists to give AI context for impact analysis, scenario suggestion, and bug detection. AI-generated context produces better AI reasoning downstream. Use `--no-enrich` for offline/free operation or to avoid sending code snippets to third-party LLM APIs.
135
+ **Why LLM enrichment is on by default:** The manifest gives AI context for impact analysis, scenario suggestion, and bug detection. AI-generated context produces better AI reasoning downstream. Use `--no-enrich` for offline/free operation or to avoid sending code snippets to third-party LLM APIs.
76
136
 
77
- **Training loop:** Run `train` → review the generated `route-families.json` → run `train --validate` to check coverage % → fix gaps → repeat until 95%+.
137
+ **Training loop:** Run `train` → review `route-families.json` → run `train --validate` to check coverage % → fix gaps → repeat.
78
138
 
79
- The `train` command:
80
- 1. **Scans** your project structure (frontend `src/`, backend `server/`, test dirs)
81
- 2. **Matches** source directories to test directories by name
82
- 3. **Enriches** with LLM (priority, user flows, routes, components)
83
- 4. **Merges** intelligently with any existing manifest (preserves human curation)
84
- 5. **Validates** against git history to measure accuracy
139
+ **Additional flags:**
140
+ - `--verbose` / `-v` DEBUG-level output with timing for each phase
141
+ - `--json` structured JSON log output (for CI pipelines)
142
+ - `--server-path` explicit path to backend server root
143
+ - `--budget-usd` max LLM spend (default: $0.50, max: $10)
85
144
 
86
- Output is written to `<testsRoot>/.e2e-ai-agents/route-families.json`.
145
+ **Output:**
146
+ - `<testsRoot>/.e2e-ai-agents/route-families.json` — the manifest
147
+ - `<testsRoot>/.e2e-ai-agents/train-report.json` — timing data, family counts, coverage stats, LLM metrics
87
148
 
88
149
  ## Configuration
89
150
 
@@ -227,6 +288,8 @@ Schemas: [schemas/traceability-input.schema.json](schemas/traceability-input.sch
227
288
 
228
289
  | File | Written by | Purpose |
229
290
  |------|-----------|---------|
291
+ | `route-families.json` | `train` | Route family manifest |
292
+ | `train-report.json` | `train` | Training timings, coverage, LLM metrics |
230
293
  | `plan.json` | `plan` | Coverage plan with gaps, decisions, metrics |
231
294
  | `ci-summary.md` | `plan` | Markdown for PR comments |
232
295
  | `metrics.jsonl` | `plan` | Append-only run metrics |
@@ -1 +1 @@
1
- {"version":3,"file":"plan_builder.d.ts","sourceRoot":"","sources":["../../src/engine/plan_builder.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAC,YAAY,EAAkB,MAAM,oBAAoB,CAAC;AAEtE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAG7D,OAAO,KAAK,EACR,UAAU,EACV,SAAS,EACT,kBAAkB,EAIrB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAC,UAAU,EAAE,SAAS,EAAE,kBAAkB,EAAC,CAAC;AAoPxD,wBAAgB,mBAAmB,CAC/B,MAAM,EAAE,YAAY,EACpB,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,EACtC,YAAY,CAAC,EAAE,kBAAkB,EACjC,kBAAkB,CAAC,EAAE,kBAAkB,GACxC,UAAU,CAsJZ;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAMzE;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAwHhE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,SAAiC,GAAG,MAAM,CAMvH"}
1
+ {"version":3,"file":"plan_builder.d.ts","sourceRoot":"","sources":["../../src/engine/plan_builder.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAC,YAAY,EAAkB,MAAM,oBAAoB,CAAC;AAEtE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAG7D,OAAO,KAAK,EACR,UAAU,EACV,SAAS,EACT,kBAAkB,EAIrB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAC,UAAU,EAAE,SAAS,EAAE,kBAAkB,EAAC,CAAC;AAoPxD,wBAAgB,mBAAmB,CAC/B,MAAM,EAAE,YAAY,EACpB,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,EACtC,YAAY,CAAC,EAAE,kBAAkB,EACjC,kBAAkB,CAAC,EAAE,kBAAkB,GACxC,UAAU,CAyKZ;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAMzE;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAwHhE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,SAAiC,GAAG,MAAM,CAMvH"}
@@ -251,8 +251,19 @@ function buildPlanFromImpact(impact, policyOverride, aiEnrichment, adaptiveThres
251
251
  ? (aiFeatureByFeatureId.get(f.featureId) ?? aiFeatureByFamilyId.get(f.familyId))
252
252
  : aiFeatureByFamilyId.get(f.familyId);
253
253
  const baseReasons = [`No E2E tests found for ${label}`];
254
- const reasons = aiFeature && aiFeature.aiReasons.length > 0
255
- ? [...baseReasons, ...aiFeature.aiReasons.slice(0, 2)]
254
+ let aiReasonsList = [];
255
+ if (aiFeature) {
256
+ if (aiFeature.aiReasons.length > 0) {
257
+ aiReasonsList = aiFeature.aiReasons.slice(0, 2);
258
+ }
259
+ else {
260
+ // Fallback: LLM returned scenarios but no reasons — synthesize a description
261
+ const fileHint = f.changedFiles.slice(0, 3).map((p) => p.split('/').pop()).join(', ');
262
+ aiReasonsList = [`Changes to ${fileHint} affect the ${label} feature, which currently lacks E2E coverage.`];
263
+ }
264
+ }
265
+ const reasons = aiReasonsList.length > 0
266
+ ? [...baseReasons, ...aiReasonsList]
256
267
  : baseReasons;
257
268
  const missingScenarios = aiFeature && aiFeature.aiMissingScenarios.length > 0
258
269
  ? aiFeature.aiMissingScenarios
@@ -274,8 +285,18 @@ function buildPlanFromImpact(impact, policyOverride, aiEnrichment, adaptiveThres
274
285
  ? (aiFeatureByFeatureId.get(f.featureId) ?? aiFeatureByFamilyId.get(f.familyId))
275
286
  : aiFeatureByFamilyId.get(f.familyId);
276
287
  const baseReasons = [`${label} is covered by Cypress only — consider adding Playwright tests`];
277
- const reasons = aiFeature && aiFeature.aiReasons.length > 0
278
- ? [...baseReasons, ...aiFeature.aiReasons.slice(0, 2)]
288
+ let partialAiReasons = [];
289
+ if (aiFeature) {
290
+ if (aiFeature.aiReasons.length > 0) {
291
+ partialAiReasons = aiFeature.aiReasons.slice(0, 2);
292
+ }
293
+ else {
294
+ const fileHint = f.changedFiles.slice(0, 3).map((p) => p.split('/').pop()).join(', ');
295
+ partialAiReasons = [`Changes to ${fileHint} affect the ${label} feature, which has Cypress but no Playwright coverage.`];
296
+ }
297
+ }
298
+ const reasons = partialAiReasons.length > 0
299
+ ? [...baseReasons, ...partialAiReasons]
279
300
  : baseReasons;
280
301
  gapDetails.push({
281
302
  id: label,
@@ -245,8 +245,19 @@ export function buildPlanFromImpact(impact, policyOverride, aiEnrichment, adapti
245
245
  ? (aiFeatureByFeatureId.get(f.featureId) ?? aiFeatureByFamilyId.get(f.familyId))
246
246
  : aiFeatureByFamilyId.get(f.familyId);
247
247
  const baseReasons = [`No E2E tests found for ${label}`];
248
- const reasons = aiFeature && aiFeature.aiReasons.length > 0
249
- ? [...baseReasons, ...aiFeature.aiReasons.slice(0, 2)]
248
+ let aiReasonsList = [];
249
+ if (aiFeature) {
250
+ if (aiFeature.aiReasons.length > 0) {
251
+ aiReasonsList = aiFeature.aiReasons.slice(0, 2);
252
+ }
253
+ else {
254
+ // Fallback: LLM returned scenarios but no reasons — synthesize a description
255
+ const fileHint = f.changedFiles.slice(0, 3).map((p) => p.split('/').pop()).join(', ');
256
+ aiReasonsList = [`Changes to ${fileHint} affect the ${label} feature, which currently lacks E2E coverage.`];
257
+ }
258
+ }
259
+ const reasons = aiReasonsList.length > 0
260
+ ? [...baseReasons, ...aiReasonsList]
250
261
  : baseReasons;
251
262
  const missingScenarios = aiFeature && aiFeature.aiMissingScenarios.length > 0
252
263
  ? aiFeature.aiMissingScenarios
@@ -268,8 +279,18 @@ export function buildPlanFromImpact(impact, policyOverride, aiEnrichment, adapti
268
279
  ? (aiFeatureByFeatureId.get(f.featureId) ?? aiFeatureByFamilyId.get(f.familyId))
269
280
  : aiFeatureByFamilyId.get(f.familyId);
270
281
  const baseReasons = [`${label} is covered by Cypress only — consider adding Playwright tests`];
271
- const reasons = aiFeature && aiFeature.aiReasons.length > 0
272
- ? [...baseReasons, ...aiFeature.aiReasons.slice(0, 2)]
282
+ let partialAiReasons = [];
283
+ if (aiFeature) {
284
+ if (aiFeature.aiReasons.length > 0) {
285
+ partialAiReasons = aiFeature.aiReasons.slice(0, 2);
286
+ }
287
+ else {
288
+ const fileHint = f.changedFiles.slice(0, 3).map((p) => p.split('/').pop()).join(', ');
289
+ partialAiReasons = [`Changes to ${fileHint} affect the ${label} feature, which has Cypress but no Playwright coverage.`];
290
+ }
291
+ }
292
+ const reasons = partialAiReasons.length > 0
293
+ ? [...baseReasons, ...partialAiReasons]
273
294
  : baseReasons;
274
295
  gapDetails.push({
275
296
  id: label,
@@ -15,6 +15,9 @@ const INFRA_GLOBS = [
15
15
  '**/.github/*', '**/.ci/*', '**/scripts/*',
16
16
  '**/docker-compose*',
17
17
  '**/__fixtures__/*', '**/test_templates/*',
18
+ '**/__snapshots__/*', '**/retrylayer/*', '**/timerlayer/*',
19
+ '**/cmd/mmctl/*',
20
+ '**/mattermost-redux/src/reducers/*',
18
21
  'playwright.config.ts', 'global-setup.ts',
19
22
  ];
20
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/training/validator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAExE,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,YAAY,CAAC;AAiBnE;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CA6BrD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAC,CAAC,CA6BhG;AAED,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAC,CAAC,CAgB1H;AA+CD,wBAAgB,cAAc,CAC1B,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM,EAAE,GACxB,gBAAgB,CA+BlB;AAED,wBAAgB,qBAAqB,CACjC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,QAAQ,EAAE,mBAAmB,GAC9B,gBAAgB,CAkDlB;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAgCvE"}
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/training/validator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAExE,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,YAAY,CAAC;AAoBnE;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CA6BrD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAC,CAAC,CA6BhG;AAED,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAC,CAAC,CAgB1H;AA+CD,wBAAgB,cAAc,CAC1B,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM,EAAE,GACxB,gBAAgB,CA+BlB;AAED,wBAAgB,qBAAqB,CACjC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,QAAQ,EAAE,mBAAmB,GAC9B,gBAAgB,CAkDlB;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAgCvE"}
@@ -23,6 +23,9 @@ const INFRA_GLOBS = [
23
23
  '**/.github/*', '**/.ci/*', '**/scripts/*',
24
24
  '**/docker-compose*',
25
25
  '**/__fixtures__/*', '**/test_templates/*',
26
+ '**/__snapshots__/*', '**/retrylayer/*', '**/timerlayer/*',
27
+ '**/cmd/mmctl/*',
28
+ '**/mattermost-redux/src/reducers/*',
26
29
  'playwright.config.ts', 'global-setup.ts',
27
30
  ];
28
31
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yasserkhanorg/e2e-agents",
3
- "version": "1.7.1",
3
+ "version": "1.7.3",
4
4
  "description": "AI-powered E2E test impact analysis, generation, and healing. Analyzes code changes to identify affected Playwright tests, detects coverage gaps, and generates or repairs specs using pluggable LLM providers (Claude, OpenAI, Ollama). Includes MCP server, traceability, and CI/CD integration.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",