@yasserkhanorg/e2e-agents 0.5.16 → 0.7.0

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.
Files changed (113) hide show
  1. package/dist/agent/pipeline.d.ts +1 -1
  2. package/dist/agent/pipeline.d.ts.map +1 -1
  3. package/dist/agent/plan.d.ts +2 -13
  4. package/dist/agent/plan.d.ts.map +1 -1
  5. package/dist/agent/plan.js +0 -365
  6. package/dist/agent/types.d.ts +42 -0
  7. package/dist/agent/types.d.ts.map +1 -0
  8. package/dist/agent/types.js +4 -0
  9. package/dist/api.d.ts +14 -14
  10. package/dist/api.d.ts.map +1 -1
  11. package/dist/api.js +67 -59
  12. package/dist/cli.js +86 -176
  13. package/dist/engine/ai_enrichment.d.ts +43 -0
  14. package/dist/engine/ai_enrichment.d.ts.map +1 -0
  15. package/dist/engine/ai_enrichment.js +235 -0
  16. package/dist/engine/diff_loader.d.ts +11 -0
  17. package/dist/engine/diff_loader.d.ts.map +1 -0
  18. package/dist/engine/diff_loader.js +74 -0
  19. package/dist/engine/impact_engine.d.ts +36 -0
  20. package/dist/engine/impact_engine.d.ts.map +1 -0
  21. package/dist/engine/impact_engine.js +196 -0
  22. package/dist/engine/plan_builder.d.ts +10 -0
  23. package/dist/engine/plan_builder.d.ts.map +1 -0
  24. package/dist/engine/plan_builder.js +374 -0
  25. package/dist/esm/agent/plan.js +1 -360
  26. package/dist/esm/agent/types.js +3 -0
  27. package/dist/esm/api.js +62 -54
  28. package/dist/esm/cli.js +87 -177
  29. package/dist/esm/engine/ai_enrichment.js +232 -0
  30. package/dist/esm/engine/diff_loader.js +70 -0
  31. package/dist/esm/engine/impact_engine.js +191 -0
  32. package/dist/esm/engine/plan_builder.js +368 -0
  33. package/dist/esm/index.js +6 -3
  34. package/dist/esm/knowledge/route_families.js +59 -1
  35. package/dist/index.d.ts +9 -4
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +14 -5
  38. package/dist/knowledge/route_families.d.ts +19 -0
  39. package/dist/knowledge/route_families.d.ts.map +1 -1
  40. package/dist/knowledge/route_families.js +62 -1
  41. package/package.json +1 -1
  42. package/dist/agent/ai_flow_analysis.d.ts +0 -13
  43. package/dist/agent/ai_flow_analysis.d.ts.map +0 -1
  44. package/dist/agent/ai_flow_analysis.js +0 -334
  45. package/dist/agent/ai_mapping.d.ts +0 -14
  46. package/dist/agent/ai_mapping.d.ts.map +0 -1
  47. package/dist/agent/ai_mapping.js +0 -560
  48. package/dist/agent/analysis.d.ts +0 -64
  49. package/dist/agent/analysis.d.ts.map +0 -1
  50. package/dist/agent/analysis.js +0 -292
  51. package/dist/agent/blast_radius.d.ts +0 -4
  52. package/dist/agent/blast_radius.d.ts.map +0 -1
  53. package/dist/agent/blast_radius.js +0 -37
  54. package/dist/agent/dependency_graph.d.ts +0 -14
  55. package/dist/agent/dependency_graph.d.ts.map +0 -1
  56. package/dist/agent/dependency_graph.js +0 -227
  57. package/dist/agent/flags.d.ts +0 -23
  58. package/dist/agent/flags.d.ts.map +0 -1
  59. package/dist/agent/flags.js +0 -171
  60. package/dist/agent/flow_catalog.d.ts +0 -25
  61. package/dist/agent/flow_catalog.d.ts.map +0 -1
  62. package/dist/agent/flow_catalog.js +0 -115
  63. package/dist/agent/flow_mapping.d.ts +0 -10
  64. package/dist/agent/flow_mapping.d.ts.map +0 -1
  65. package/dist/agent/flow_mapping.js +0 -84
  66. package/dist/agent/framework.d.ts +0 -13
  67. package/dist/agent/framework.d.ts.map +0 -1
  68. package/dist/agent/framework.js +0 -149
  69. package/dist/agent/gap_suggestions.d.ts +0 -14
  70. package/dist/agent/gap_suggestions.d.ts.map +0 -1
  71. package/dist/agent/gap_suggestions.js +0 -101
  72. package/dist/agent/generator.d.ts +0 -10
  73. package/dist/agent/generator.d.ts.map +0 -1
  74. package/dist/agent/generator.js +0 -115
  75. package/dist/agent/operational_insights.d.ts +0 -41
  76. package/dist/agent/operational_insights.d.ts.map +0 -1
  77. package/dist/agent/operational_insights.js +0 -127
  78. package/dist/agent/report.d.ts +0 -97
  79. package/dist/agent/report.d.ts.map +0 -1
  80. package/dist/agent/report.js +0 -159
  81. package/dist/agent/runner.d.ts +0 -7
  82. package/dist/agent/runner.d.ts.map +0 -1
  83. package/dist/agent/runner.js +0 -898
  84. package/dist/agent/selectors.d.ts +0 -10
  85. package/dist/agent/selectors.d.ts.map +0 -1
  86. package/dist/agent/selectors.js +0 -75
  87. package/dist/agent/subsystem_risk.d.ts +0 -23
  88. package/dist/agent/subsystem_risk.d.ts.map +0 -1
  89. package/dist/agent/subsystem_risk.js +0 -207
  90. package/dist/agent/tests.d.ts +0 -19
  91. package/dist/agent/tests.d.ts.map +0 -1
  92. package/dist/agent/tests.js +0 -116
  93. package/dist/agent/traceability.d.ts +0 -22
  94. package/dist/agent/traceability.d.ts.map +0 -1
  95. package/dist/agent/traceability.js +0 -183
  96. package/dist/esm/agent/ai_flow_analysis.js +0 -331
  97. package/dist/esm/agent/ai_mapping.js +0 -557
  98. package/dist/esm/agent/analysis.js +0 -287
  99. package/dist/esm/agent/blast_radius.js +0 -34
  100. package/dist/esm/agent/dependency_graph.js +0 -224
  101. package/dist/esm/agent/flags.js +0 -160
  102. package/dist/esm/agent/flow_catalog.js +0 -112
  103. package/dist/esm/agent/flow_mapping.js +0 -81
  104. package/dist/esm/agent/framework.js +0 -145
  105. package/dist/esm/agent/gap_suggestions.js +0 -98
  106. package/dist/esm/agent/generator.js +0 -112
  107. package/dist/esm/agent/operational_insights.js +0 -124
  108. package/dist/esm/agent/report.js +0 -156
  109. package/dist/esm/agent/runner.js +0 -894
  110. package/dist/esm/agent/selectors.js +0 -71
  111. package/dist/esm/agent/subsystem_risk.js +0 -204
  112. package/dist/esm/agent/tests.js +0 -111
  113. package/dist/esm/agent/traceability.js +0 -180
package/dist/api.js CHANGED
@@ -2,28 +2,23 @@
2
2
  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
3
3
  // See LICENSE.txt for license information.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.analyzeImpact = analyzeImpact;
6
- exports.findGaps = findGaps;
7
- exports.recommendTests = recommendTests;
8
5
  exports.handoffGeneratedTests = handoffGeneratedTests;
9
6
  exports.ingestTraceability = ingestTraceability;
7
+ exports.analyzeImpactDeterministic = analyzeImpactDeterministic;
8
+ exports.recommendTestsDeterministic = recommendTestsDeterministic;
9
+ exports.recommendTestsAI = recommendTestsAI;
10
10
  exports.captureTraceability = captureTraceability;
11
- const fs_1 = require("fs");
12
- const path_1 = require("path");
13
11
  const config_js_1 = require("./agent/config.js");
14
- const runner_js_1 = require("./agent/runner.js");
15
12
  const plan_js_1 = require("./agent/plan.js");
16
- const operational_insights_js_1 = require("./agent/operational_insights.js");
13
+ const impact_engine_js_1 = require("./engine/impact_engine.js");
14
+ const plan_builder_js_1 = require("./engine/plan_builder.js");
15
+ const git_js_1 = require("./agent/git.js");
16
+ const diff_loader_js_1 = require("./engine/diff_loader.js");
17
+ const ai_enrichment_js_1 = require("./engine/ai_enrichment.js");
18
+ const anthropic_provider_js_1 = require("./anthropic_provider.js");
17
19
  const handoff_js_1 = require("./agent/handoff.js");
18
20
  const traceability_ingest_js_1 = require("./agent/traceability_ingest.js");
19
21
  const traceability_capture_js_1 = require("./agent/traceability_capture.js");
20
- function readReportJson(reportPath) {
21
- if (!(0, fs_1.existsSync)(reportPath)) {
22
- throw new Error(`Expected report not found: ${reportPath}`);
23
- }
24
- const raw = (0, fs_1.readFileSync)(reportPath, 'utf-8');
25
- return JSON.parse(raw);
26
- }
27
22
  function resolveAgent(options, mode) {
28
23
  const cwd = options.cwd || process.cwd();
29
24
  const { config } = (0, config_js_1.resolveConfig)(cwd, options.configPath, {
@@ -35,51 +30,6 @@ function resolveAgent(options, mode) {
35
30
  }
36
31
  return config;
37
32
  }
38
- function reportPathFor(configPath, mode) {
39
- return (0, path_1.join)(configPath, '.e2e-ai-agents', mode === 'impact' ? 'impact.json' : 'gap.json');
40
- }
41
- async function analyzeImpact(options = {}) {
42
- const config = resolveAgent(options, 'impact');
43
- await (0, runner_js_1.runImpact)(config, { apply: options.apply ?? false });
44
- const reportRoot = config.testsRoot || config.path;
45
- const reportPath = reportPathFor(reportRoot, 'impact');
46
- const report = readReportJson(reportPath);
47
- return { report, reportPath };
48
- }
49
- async function findGaps(options = {}) {
50
- const config = resolveAgent(options, 'gap');
51
- await (0, runner_js_1.runGap)(config, { apply: options.apply ?? false });
52
- const reportRoot = config.testsRoot || config.path;
53
- const reportPath = reportPathFor(reportRoot, 'gap');
54
- const report = readReportJson(reportPath);
55
- return { report, reportPath };
56
- }
57
- async function recommendTests(options = {}) {
58
- const config = resolveAgent(options, 'impact');
59
- await (0, runner_js_1.runImpact)(config, { apply: options.apply ?? false });
60
- const reportRoot = config.testsRoot || config.path;
61
- const impactPath = reportPathFor(reportRoot, 'impact');
62
- const report = readReportJson(impactPath);
63
- const basePlan = (0, plan_js_1.buildPlanFromImpactReport)(report, config.policy);
64
- const withActions = (0, plan_js_1.attachDeveloperActions)(basePlan, {
65
- appPath: config.path,
66
- testsRoot: reportRoot,
67
- sinceRef: config.git.since,
68
- });
69
- const plan = (0, operational_insights_js_1.applyOperationalInsights)(withActions, reportRoot);
70
- const planPath = (0, plan_js_1.writePlanReport)(reportRoot, plan);
71
- const ciSummaryMarkdown = (0, plan_js_1.renderCiSummaryMarkdown)(plan);
72
- const ciSummaryPath = (0, plan_js_1.writeCiSummary)(reportRoot, ciSummaryMarkdown);
73
- (0, plan_js_1.appendPlanMetrics)(reportRoot, plan);
74
- return {
75
- report,
76
- reportPath: impactPath,
77
- plan,
78
- planPath,
79
- ciSummaryMarkdown,
80
- ciSummaryPath,
81
- };
82
- }
83
33
  function handoffGeneratedTests(options) {
84
34
  return (0, handoff_js_1.finalizeGeneratedTests)(options);
85
35
  }
@@ -93,6 +43,64 @@ function ingestTraceability(options) {
93
43
  const reportRoot = config.testsRoot || config.path;
94
44
  return (0, traceability_ingest_js_1.ingestTraceabilityInput)(reportRoot, config.impact.traceability, options.payload, options.options);
95
45
  }
46
+ function analyzeImpactDeterministic(options = {}) {
47
+ const config = resolveAgent(options, 'impact');
48
+ const reportRoot = config.testsRoot || config.path;
49
+ const gitResult = (0, git_js_1.getChangedFiles)(config.path, config.git.since, { includeUncommitted: config.git.includeUncommitted });
50
+ return (0, impact_engine_js_1.analyzeImpact)(gitResult.files, {
51
+ testsRoot: reportRoot,
52
+ routeFamilies: config.routeFamilies,
53
+ });
54
+ }
55
+ function recommendTestsDeterministic(options = {}) {
56
+ const config = resolveAgent(options, 'impact');
57
+ const reportRoot = config.testsRoot || config.path;
58
+ const gitResult = (0, git_js_1.getChangedFiles)(config.path, config.git.since, { includeUncommitted: config.git.includeUncommitted });
59
+ const impact = (0, impact_engine_js_1.analyzeImpact)(gitResult.files, {
60
+ testsRoot: reportRoot,
61
+ routeFamilies: config.routeFamilies,
62
+ });
63
+ const plan = (0, plan_builder_js_1.buildPlanFromImpact)(impact, config.policy);
64
+ const planPath = (0, plan_builder_js_1.writePlanReport)(reportRoot, plan);
65
+ const ciSummaryMarkdown = (0, plan_builder_js_1.renderCiSummaryMarkdown)(plan);
66
+ const ciSummaryPath = (0, plan_builder_js_1.writeCiSummary)(reportRoot, ciSummaryMarkdown);
67
+ (0, plan_js_1.appendPlanMetrics)(reportRoot, plan);
68
+ return { impact, plan, planPath, ciSummaryMarkdown, ciSummaryPath };
69
+ }
70
+ async function recommendTestsAI(options = {}) {
71
+ const config = resolveAgent(options, 'impact');
72
+ const reportRoot = config.testsRoot || config.path;
73
+ const gitResult = (0, git_js_1.getChangedFiles)(config.path, config.git.since, { includeUncommitted: config.git.includeUncommitted });
74
+ const impact = (0, impact_engine_js_1.analyzeImpact)(gitResult.files, {
75
+ testsRoot: reportRoot,
76
+ routeFamilies: config.routeFamilies,
77
+ });
78
+ const apiKey = process.env.ANTHROPIC_API_KEY;
79
+ let aiEnrichment;
80
+ if (apiKey) {
81
+ const diffs = (0, diff_loader_js_1.loadDiffs)(config.path, config.git.since, gitResult.files);
82
+ const provider = new anthropic_provider_js_1.AnthropicProvider({ apiKey });
83
+ // Collect all known spec paths from impacted features
84
+ const specSet = new Set();
85
+ for (const feature of impact.impactedFeatures) {
86
+ for (const s of feature.playwrightSpecs) {
87
+ specSet.add(s);
88
+ }
89
+ }
90
+ aiEnrichment = await (0, ai_enrichment_js_1.enrichImpactWithAI)({
91
+ deterministicImpact: impact,
92
+ diffs,
93
+ provider,
94
+ specList: [...specSet],
95
+ });
96
+ }
97
+ const plan = (0, plan_builder_js_1.buildPlanFromImpact)(impact, config.policy, aiEnrichment);
98
+ const planPath = (0, plan_builder_js_1.writePlanReport)(reportRoot, plan);
99
+ const ciSummaryMarkdown = (0, plan_builder_js_1.renderCiSummaryMarkdown)(plan);
100
+ const ciSummaryPath = (0, plan_builder_js_1.writeCiSummary)(reportRoot, ciSummaryMarkdown);
101
+ (0, plan_js_1.appendPlanMetrics)(reportRoot, plan);
102
+ return { impact, plan, planPath, ciSummaryMarkdown, ciSummaryPath, aiEnrichment };
103
+ }
96
104
  function captureTraceability(options) {
97
105
  const cwd = options.cwd || process.cwd();
98
106
  const { config } = (0, config_js_1.resolveConfig)(cwd, options.configPath, {
package/dist/cli.js CHANGED
@@ -8,9 +8,10 @@ const path_1 = require("path");
8
8
  const config_js_1 = require("./agent/config.js");
9
9
  const anthropic_provider_js_1 = require("./anthropic_provider.js");
10
10
  const provider_interface_js_1 = require("./provider_interface.js");
11
- const runner_js_1 = require("./agent/runner.js");
12
- const plan_js_1 = require("./agent/plan.js");
13
- const operational_insights_js_1 = require("./agent/operational_insights.js");
11
+ const impact_engine_js_1 = require("./engine/impact_engine.js");
12
+ const plan_builder_js_1 = require("./engine/plan_builder.js");
13
+ const git_js_1 = require("./agent/git.js");
14
+ const api_js_1 = require("./api.js");
14
15
  const feedback_js_1 = require("./agent/feedback.js");
15
16
  const handoff_js_1 = require("./agent/handoff.js");
16
17
  const traceability_ingest_js_1 = require("./agent/traceability_ingest.js");
@@ -61,13 +62,9 @@ function printUsage() {
61
62
  console.log([
62
63
  'Usage:',
63
64
  ' e2e-ai-agents impact --path <app-root> [options]',
64
- ' e2e-ai-agents gap --path <app-root> [options]',
65
65
  ' e2e-ai-agents plan --path <app-root> [options]',
66
- ' e2e-ai-agents generate --path <app-root> [options]',
67
- ' e2e-ai-agents heal --path <app-root> --traceability-report <json> [options]',
68
66
  ' e2e-ai-agents suggest --path <app-root> [options]',
69
- ' e2e-ai-agents approve-and-generate --path <app-root> [options]',
70
- ' e2e-ai-agents auto-heal-pr --path <app-root> [options]',
67
+ ' e2e-ai-agents heal --path <app-root> --traceability-report <json> [options]',
71
68
  ' e2e-ai-agents finalize-generated-tests --path <app-root> [options]',
72
69
  ' e2e-ai-agents feedback --path <app-root> --feedback-input <json>',
73
70
  ' e2e-ai-agents traceability-capture --path <app-root> --traceability-report <json>',
@@ -146,13 +143,9 @@ function parseArgs(argv) {
146
143
  }
147
144
  const command = argv[0];
148
145
  if (command === 'impact'
149
- || command === 'gap'
150
146
  || command === 'plan'
151
- || command === 'generate'
152
147
  || command === 'heal'
153
148
  || command === 'suggest'
154
- || command === 'approve-and-generate'
155
- || command === 'auto-heal-pr'
156
149
  || command === 'finalize-generated-tests'
157
150
  || command === 'feedback'
158
151
  || command === 'traceability-capture'
@@ -502,6 +495,10 @@ function parseArgs(argv) {
502
495
  i += 1;
503
496
  continue;
504
497
  }
498
+ if (arg === '--no-ai') {
499
+ parsed.noAi = true;
500
+ continue;
501
+ }
505
502
  }
506
503
  return parsed;
507
504
  }
@@ -761,117 +758,6 @@ async function main() {
761
758
  }
762
759
  return;
763
760
  }
764
- if (args.command === 'auto-heal-pr') {
765
- if (!args.path && !autoConfig) {
766
- // eslint-disable-next-line no-console
767
- console.error('Error: --path is required for auto-heal-pr command');
768
- process.exit(1);
769
- }
770
- const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
771
- path: args.path,
772
- profile: args.profile,
773
- testsRoot: args.testsRoot,
774
- mode: 'gap',
775
- framework: args.framework,
776
- timeLimitMinutes: args.timeLimitMinutes,
777
- budget: {
778
- maxUSD: args.budgetUSD,
779
- maxTokens: args.budgetTokens,
780
- },
781
- testPatterns: args.testPatterns,
782
- flowPatterns: args.flowPatterns,
783
- flowExclude: args.flowExclude,
784
- flowCatalogPath: args.flowCatalogPath,
785
- specPDF: args.specPDF,
786
- gitSince: args.gitSince,
787
- pipeline: {
788
- enabled: true,
789
- scenarios: args.pipelineScenarios,
790
- outputDir: args.pipelineOutput,
791
- baseUrl: args.pipelineBaseUrl,
792
- browser: args.pipelineBrowser,
793
- headless: args.pipelineHeadless,
794
- project: args.pipelineProject,
795
- parallel: args.pipelineParallel,
796
- dryRun: args.pipelineDryRun,
797
- mcp: args.pipelineMcp,
798
- mcpAllowFallback: args.pipelineMcpAllowFallback,
799
- mcpOnly: args.pipelineMcpOnly,
800
- },
801
- llmProvider: args.llmProvider,
802
- });
803
- if (args.allowFallback) {
804
- config.impact.allowFallback = true;
805
- }
806
- await (0, runner_js_1.runGap)(config, { apply: true });
807
- const reportRoot = config.testsRoot || config.path;
808
- if (args.traceabilityReportPath) {
809
- const unstableSpecs = (0, playwright_report_js_1.extractPlaywrightUnstableSpecs)(args.traceabilityReportPath, [reportRoot, config.path]);
810
- if (unstableSpecs.length > 0) {
811
- const targetedSummary = (0, pipeline_js_1.runTargetedSpecHeal)(reportRoot, unstableSpecs.map((spec) => ({
812
- specPath: spec.specPath,
813
- status: spec.status,
814
- reason: `Playwright report: failingTests=${spec.failingTests}, flakyTests=${spec.flakyTests}`,
815
- })), {
816
- ...config.pipeline,
817
- enabled: true,
818
- heal: true,
819
- });
820
- const healedCount = targetedSummary.results.filter((result) => result.healStatus === 'success').length;
821
- // eslint-disable-next-line no-console
822
- console.log(`Auto-heal targeted unstable specs: ${unstableSpecs.length} (healed=${healedCount})`);
823
- if (targetedSummary.warnings.length > 0) {
824
- // eslint-disable-next-line no-console
825
- console.log(`Auto-heal warnings: ${targetedSummary.warnings.join(' | ')}`);
826
- }
827
- const gapPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents', 'gap.json');
828
- if ((0, fs_1.existsSync)(gapPath)) {
829
- const gap = JSON.parse((0, fs_1.readFileSync)(gapPath, 'utf-8'));
830
- const existingResults = Array.isArray(gap.pipeline?.results) ? gap.pipeline?.results : [];
831
- const existingWarnings = Array.isArray(gap.pipeline?.warnings) ? gap.pipeline?.warnings : [];
832
- gap.pipeline = {
833
- runner: gap.pipeline?.runner || targetedSummary.runner,
834
- results: [...existingResults, ...targetedSummary.results],
835
- warnings: Array.from(new Set([...(existingWarnings || []), ...targetedSummary.warnings])),
836
- };
837
- (0, fs_1.writeFileSync)(gapPath, `${JSON.stringify(gap, null, 2)}\n`, 'utf-8');
838
- }
839
- }
840
- else {
841
- // eslint-disable-next-line no-console
842
- console.log('Auto-heal targeted unstable specs: 0');
843
- }
844
- }
845
- const branchSuffix = new Date().toISOString().replace(/[:.]/g, '-');
846
- const result = (0, handoff_js_1.finalizeGeneratedTests)({
847
- appPath: config.path,
848
- testsRoot: reportRoot,
849
- branch: args.branch || `auto-heal-${branchSuffix}`,
850
- commitMessage: args.commitMessage || 'test(e2e): auto-heal generated specs',
851
- createPr: true,
852
- prTitle: args.prTitle || 'test(e2e): auto-heal generated specs',
853
- prBody: args.prBody || 'Automated e2e-heal run generated by @yasserkhanorg/e2e-agents.',
854
- baseBranch: args.prBase || 'master',
855
- dryRun: args.dryRun,
856
- });
857
- // eslint-disable-next-line no-console
858
- console.log(`Auto-heal repo root: ${result.repoRoot}`);
859
- // eslint-disable-next-line no-console
860
- console.log(`Auto-heal branch: ${result.branch}`);
861
- // eslint-disable-next-line no-console
862
- console.log(`Auto-heal staged paths: ${result.stagedPaths.join(', ') || 'none'}`);
863
- // eslint-disable-next-line no-console
864
- console.log(`Auto-heal commit: ${result.committed ? 'created' : 'skipped'}`);
865
- if (result.commitSha) {
866
- // eslint-disable-next-line no-console
867
- console.log(`Auto-heal commit sha: ${result.commitSha}`);
868
- }
869
- if (result.prUrl) {
870
- // eslint-disable-next-line no-console
871
- console.log(`Auto-heal PR: ${result.prUrl}`);
872
- }
873
- return;
874
- }
875
761
  if (args.command === 'heal') {
876
762
  if (!args.path && !autoConfig) {
877
763
  // eslint-disable-next-line no-console
@@ -936,13 +822,11 @@ async function main() {
936
822
  printUsage();
937
823
  process.exit(1);
938
824
  }
939
- const forcePipelineFromApproval = args.command === 'approve-and-generate' || args.command === 'generate';
940
- const forceAIPipelineFromApproval = args.command === 'approve-and-generate' || args.command === 'generate';
941
- const { config, configPath } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
825
+ const { config } = (0, config_js_1.resolveConfig)(process.cwd(), autoConfig, {
942
826
  path: args.path,
943
827
  profile: args.profile,
944
828
  testsRoot: args.testsRoot,
945
- mode: (args.command === 'gap' || args.command === 'approve-and-generate' || args.command === 'generate') ? 'gap' : 'impact',
829
+ mode: 'impact',
946
830
  framework: args.framework,
947
831
  timeLimitMinutes: args.timeLimitMinutes,
948
832
  budget: {
@@ -956,7 +840,7 @@ async function main() {
956
840
  specPDF: args.specPDF,
957
841
  gitSince: args.gitSince,
958
842
  llmProvider: args.llmProvider,
959
- pipeline: (args.pipeline || forcePipelineFromApproval)
843
+ pipeline: args.pipeline
960
844
  ? {
961
845
  enabled: true,
962
846
  scenarios: args.pipelineScenarios,
@@ -967,9 +851,9 @@ async function main() {
967
851
  project: args.pipelineProject,
968
852
  parallel: args.pipelineParallel,
969
853
  dryRun: args.pipelineDryRun,
970
- mcp: args.pipelineMcp !== undefined ? args.pipelineMcp : forceAIPipelineFromApproval,
854
+ mcp: args.pipelineMcp,
971
855
  mcpAllowFallback: args.pipelineMcpAllowFallback,
972
- mcpOnly: args.pipelineMcpOnly !== undefined ? args.pipelineMcpOnly : forceAIPipelineFromApproval,
856
+ mcpOnly: args.pipelineMcpOnly,
973
857
  mcpCommandTimeoutMs: args.pipelineMcpTimeoutMs,
974
858
  mcpRetries: args.pipelineMcpRetries,
975
859
  }
@@ -990,46 +874,81 @@ async function main() {
990
874
  }
991
875
  : undefined,
992
876
  });
993
- if (args.allowFallback) {
994
- config.impact.allowFallback = true;
995
- }
996
877
  if (args.command === 'impact') {
997
- await (0, runner_js_1.runImpact)(config, { apply: args.apply });
878
+ const reportRoot = config.testsRoot || config.path;
879
+ const gitResult = (0, git_js_1.getChangedFiles)(config.path, config.git.since, { includeUncommitted: config.git.includeUncommitted });
880
+ const impactResult = (0, impact_engine_js_1.analyzeImpact)(gitResult.files, {
881
+ testsRoot: reportRoot,
882
+ routeFamilies: config.routeFamilies,
883
+ });
884
+ // eslint-disable-next-line no-console
885
+ console.log(`Impact: ${impactResult.changedFiles.length} changed files → ${impactResult.impactedFeatures.length} features impacted`);
886
+ // eslint-disable-next-line no-console
887
+ console.log(`Unbound files: ${impactResult.unboundFiles.length}`);
888
+ for (const f of impactResult.impactedFeatures) {
889
+ const label = f.featureId || f.familyId;
890
+ // eslint-disable-next-line no-console
891
+ console.log(` [${f.priority}] ${label}: ${f.coverageStatus} (PW=${f.playwrightSpecs.length}, Cy=${f.cypressSpecs.length})`);
892
+ }
893
+ if (impactResult.warnings.length > 0) {
894
+ for (const w of impactResult.warnings) {
895
+ // eslint-disable-next-line no-console
896
+ console.warn(` Warning: ${w}`);
897
+ }
898
+ }
998
899
  return;
999
900
  }
1000
901
  if (args.command === 'suggest' || args.command === 'plan') {
1001
902
  const reportRoot = config.testsRoot || config.path;
1002
- const impactPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents', 'impact.json');
1003
- try {
1004
- await (0, runner_js_1.runImpact)(config, { apply: args.apply });
1005
- }
1006
- catch (err) {
1007
- // If impact analysis already ran (e.g. a prior CI step wrote impact.json),
1008
- // fall back to that data rather than failing the plan step.
1009
- if ((0, fs_1.existsSync)(impactPath)) {
903
+ const apiOptions = {
904
+ cwd: process.cwd(),
905
+ configPath: autoConfig,
906
+ path: args.path,
907
+ profile: args.profile,
908
+ testsRoot: args.testsRoot,
909
+ gitSince: args.gitSince,
910
+ llmProvider: args.llmProvider,
911
+ policy: args.policyMinConfidence !== undefined ||
912
+ args.policySafeMergeConfidence !== undefined ||
913
+ args.policyWarningsThreshold !== undefined ||
914
+ (args.policyRiskyPatterns && args.policyRiskyPatterns.length > 0) ||
915
+ args.policyEnforcementMode !== undefined ||
916
+ (args.policyBlockActions && args.policyBlockActions.length > 0)
917
+ ? {
918
+ minConfidenceForTargeted: args.policyMinConfidence,
919
+ safeMergeMinConfidence: args.policySafeMergeConfidence,
920
+ forceFullOnWarningsAtOrAbove: args.policyWarningsThreshold,
921
+ riskyFilePatterns: args.policyRiskyPatterns,
922
+ enforcementMode: args.policyEnforcementMode,
923
+ blockOnActions: args.policyBlockActions,
924
+ }
925
+ : undefined,
926
+ };
927
+ let result;
928
+ if (args.noAi) {
929
+ result = (0, api_js_1.recommendTestsDeterministic)(apiOptions);
930
+ }
931
+ else {
932
+ result = await (0, api_js_1.recommendTestsAI)(apiOptions);
933
+ if (result.aiEnrichment) {
934
+ const { aiEnrichment } = result;
1010
935
  // eslint-disable-next-line no-console
1011
- console.warn(`Impact re-run failed (${err instanceof Error ? err.message : String(err)}); using existing impact.json.`);
936
+ console.log(`AI enrichment: ${aiEnrichment.enrichedFeatures.length} features enriched (${aiEnrichment.tokenUsage.input + aiEnrichment.tokenUsage.output} tokens)`);
1012
937
  }
1013
- else {
1014
- throw err;
938
+ else if (!process.env.ANTHROPIC_API_KEY) {
939
+ // eslint-disable-next-line no-console
940
+ console.log('Tip: set ANTHROPIC_API_KEY to enable AI-powered enrichment');
1015
941
  }
1016
942
  }
1017
- if (!(0, fs_1.existsSync)(impactPath)) {
1018
- throw new Error(`Impact report not found at ${impactPath}`);
943
+ const { plan, planPath, ciSummaryMarkdown, ciSummaryPath } = result;
944
+ // Write CI summary to an additional path if --ci-comment-path was specified
945
+ if (args.ciCommentPath) {
946
+ (0, plan_builder_js_1.writeCiSummary)(reportRoot, ciSummaryMarkdown, args.ciCommentPath);
1019
947
  }
1020
- const impact = JSON.parse((0, fs_1.readFileSync)(impactPath, 'utf-8'));
1021
- const basePlan = (0, plan_js_1.buildPlanFromImpactReport)(impact, config.policy);
1022
- const withActions = (0, plan_js_1.attachDeveloperActions)(basePlan, {
1023
- appPath: config.path,
1024
- testsRoot: reportRoot,
1025
- sinceRef: config.git.since,
1026
- configPath,
1027
- });
1028
- const plan = (0, operational_insights_js_1.applyOperationalInsights)(withActions, reportRoot);
1029
- const planPath = (0, plan_js_1.writePlanReport)(reportRoot, plan);
1030
- const summaryMarkdown = (0, plan_js_1.renderCiSummaryMarkdown)(plan);
1031
- const summaryPath = (0, plan_js_1.writeCiSummary)(reportRoot, summaryMarkdown, args.ciCommentPath);
1032
- const metrics = (0, plan_js_1.appendPlanMetrics)(reportRoot, plan);
948
+ const summaryPath = ciSummaryPath;
949
+ // Compute metrics paths (api already wrote metrics; derive paths for GHA output)
950
+ const metricsEventsPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents/metrics.jsonl');
951
+ const metricsSummaryPath = (0, path_1.join)(reportRoot, '.e2e-ai-agents/metrics-summary.json');
1033
952
  const ghaOutput = args.githubOutputPath || process.env.GITHUB_OUTPUT;
1034
953
  if (ghaOutput) {
1035
954
  (0, fs_1.appendFileSync)(ghaOutput, `run_set=${plan.runSet}\n`);
@@ -1041,8 +960,8 @@ async function main() {
1041
960
  (0, fs_1.appendFileSync)(ghaOutput, `required_new_tests_count=${plan.requiredNewTests.length}\n`);
1042
961
  (0, fs_1.appendFileSync)(ghaOutput, `plan_path=${planPath}\n`);
1043
962
  (0, fs_1.appendFileSync)(ghaOutput, `summary_path=${summaryPath}\n`);
1044
- (0, fs_1.appendFileSync)(ghaOutput, `metrics_events_path=${metrics.eventsPath}\n`);
1045
- (0, fs_1.appendFileSync)(ghaOutput, `metrics_summary_path=${metrics.summaryPath}\n`);
963
+ (0, fs_1.appendFileSync)(ghaOutput, `metrics_events_path=${metricsEventsPath}\n`);
964
+ (0, fs_1.appendFileSync)(ghaOutput, `metrics_summary_path=${metricsSummaryPath}\n`);
1046
965
  }
1047
966
  // eslint-disable-next-line no-console
1048
967
  console.log(`Suggested run set: ${plan.runSet} (confidence ${plan.confidence})`);
@@ -1055,26 +974,17 @@ async function main() {
1055
974
  // eslint-disable-next-line no-console
1056
975
  console.log(`CI summary: ${summaryPath}`);
1057
976
  // eslint-disable-next-line no-console
1058
- console.log(`Plan metrics: ${metrics.summaryPath}`);
1059
- if (plan.nextActions) {
1060
- // eslint-disable-next-line no-console
1061
- console.log(`Next action (run existing): ${plan.nextActions.runRecommendedTests || plan.nextActions.runSmokeSuite}`);
1062
- // eslint-disable-next-line no-console
1063
- console.log(`Next action (approve + generate): ${plan.nextActions.approveAndGenerate || plan.nextActions.generateMissingTests}`);
1064
- // eslint-disable-next-line no-console
1065
- console.log(`Next action (heal): ${plan.nextActions.healGeneratedTests}`);
1066
- }
977
+ console.log(`Plan metrics: ${metricsSummaryPath}`);
1067
978
  const failOnLegacyFlag = args.failOnMustAddTests && plan.decision.action === 'must-add-tests';
1068
979
  if (failOnLegacyFlag || plan.enforcement.shouldFail) {
1069
980
  process.exit(2);
1070
981
  }
1071
982
  return;
1072
983
  }
1073
- if (args.command === 'approve-and-generate' || args.command === 'generate') {
1074
- await (0, runner_js_1.runGap)(config, { apply: args.apply });
1075
- return;
1076
- }
1077
- await (0, runner_js_1.runGap)(config, { apply: args.apply });
984
+ // eslint-disable-next-line no-console
985
+ console.error(`Unknown command: ${args.command}`);
986
+ printUsage();
987
+ process.exit(1);
1078
988
  }
1079
989
  async function runLlmHealth() {
1080
990
  if (!process.env.ANTHROPIC_API_KEY) {
@@ -0,0 +1,43 @@
1
+ import type { LLMProvider } from '../provider_interface.js';
2
+ import type { ImpactResult } from './impact_engine.js';
3
+ import type { FeaturePriority } from '../knowledge/route_families.js';
4
+ export interface EnrichedFeature {
5
+ familyId: string;
6
+ featureId?: string;
7
+ priority: FeaturePriority;
8
+ changedFiles: string[];
9
+ coverageStatus: string;
10
+ playwrightSpecs: string[];
11
+ cypressSpecs: string[];
12
+ userFlows: string[];
13
+ aiReasons: string[];
14
+ aiMissingScenarios: string[];
15
+ aiCoveredBy: string[];
16
+ }
17
+ export interface AIEnrichmentResult {
18
+ enrichedFeatures: EnrichedFeature[];
19
+ unboundFileInsights: Array<{
20
+ file: string;
21
+ likelyFeature: string;
22
+ reason: string;
23
+ }>;
24
+ warnings: string[];
25
+ providerName: string;
26
+ tokenUsage: {
27
+ input: number;
28
+ output: number;
29
+ };
30
+ }
31
+ export interface AIEnrichmentOptions {
32
+ deterministicImpact: ImpactResult;
33
+ diffs: Map<string, string>;
34
+ provider: LLMProvider;
35
+ specList: string[];
36
+ manifestSummary?: string;
37
+ }
38
+ /**
39
+ * Enriches a deterministic impact result with AI-generated reasons,
40
+ * missing test scenarios, and coverage insights.
41
+ */
42
+ export declare function enrichImpactWithAI(options: AIEnrichmentOptions): Promise<AIEnrichmentResult>;
43
+ //# sourceMappingURL=ai_enrichment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai_enrichment.d.ts","sourceRoot":"","sources":["../../src/engine/ai_enrichment.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAC,YAAY,EAAkB,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gCAAgC,CAAC;AAGpE,MAAM,WAAW,eAAe;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IAC/B,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,mBAAmB,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IAClF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;CAC/C;AAED,MAAM,WAAW,mBAAmB;IAChC,mBAAmB,EAAE,YAAY,CAAC;IAClC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,QAAQ,EAAE,WAAW,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAoJD;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAkIlG"}