@yasserkhanorg/e2e-agents 1.8.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/dist/crew/context.d.ts.map +1 -1
- package/dist/esm/knowledge/route_families.js +2 -2
- package/dist/esm/logger.js +1 -2
- package/dist/esm/ollama_provider.js +1 -1
- package/dist/esm/provider_factory.js +6 -10
- package/dist/esm/training/enricher.js +4 -3
- package/dist/esm/training/validator.js +2 -1
- package/dist/knowledge/route_families.d.ts.map +1 -1
- package/dist/knowledge/route_families.js +2 -2
- package/dist/logger.d.ts +1 -2
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +1 -2
- package/dist/ollama_provider.js +1 -1
- package/dist/provider_factory.d.ts.map +1 -1
- package/dist/provider_factory.js +6 -10
- package/dist/training/enricher.d.ts.map +1 -1
- package/dist/training/enricher.js +4 -3
- package/dist/training/validator.d.ts.map +1 -1
- package/dist/training/validator.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,9 @@ AI-powered E2E test impact analysis, generation, healing, and autonomous QA for
|
|
|
11
11
|
Given a git diff, `e2e-ai-agents` determines which E2E test flows are impacted, identifies coverage gaps, and can generate or heal Playwright tests — all from the CLI. The companion `e2e-qa-agent` goes further: it opens a real browser, explores your app autonomously, and produces a QA report with findings and a release-readiness verdict.
|
|
12
12
|
|
|
13
13
|
**Pipeline:** `impact` → `plan` → `generate` → `heal` → `finalize`
|
|
14
|
+
**Crew (v1.8.0):** `impact` + `cross-impact` + `regression-advisor` → `strategist` → `test-designer` → `generator` → `executor` → `healer`
|
|
15
|
+
|
|
16
|
+
> **How does this compare to other tools?** See [docs/comparison.md](docs/comparison.md) for a detailed analysis against Launchable, Codecov ATS, Qodo, Testsigma, mabl, GitHub Copilot, and others.
|
|
14
17
|
|
|
15
18
|
## Installation
|
|
16
19
|
|
|
@@ -54,6 +57,58 @@ npx e2e-ai-agents llm-health
|
|
|
54
57
|
|
|
55
58
|
`plan` and `suggest` are aliases. `analyze` is a convenience wrapper that runs impact + plan and optionally generation/healing in one invocation. Use `--help` for all available flags.
|
|
56
59
|
|
|
60
|
+
## Multi-Agent Crew (v1.8.0)
|
|
61
|
+
|
|
62
|
+
The Crew orchestrates 10 specialized agents for deep test analysis. While the standard pipeline gives a fast pass/fail gate, the Crew produces structured test designs, cross-family impact maps, and prioritized test strategies.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Quick strategy: impact + strategy recommendations (~$0.10, ~1 min)
|
|
66
|
+
npx e2e-ai-agents crew --workflow quick-check --path /path/to/project --tests-root ./e2e-tests --since origin/master
|
|
67
|
+
|
|
68
|
+
# Full test design without generation (~$0.50-2.00, ~5-40 min)
|
|
69
|
+
npx e2e-ai-agents crew --workflow design-only --path /path/to/project --tests-root ./e2e-tests --since origin/master
|
|
70
|
+
|
|
71
|
+
# End-to-end: design + generate + execute + heal (~$2-5, ~10-60 min)
|
|
72
|
+
npx e2e-ai-agents crew --workflow full-qa --path /path/to/project --tests-root ./e2e-tests --since origin/master
|
|
73
|
+
|
|
74
|
+
# With budget cap and JSON output
|
|
75
|
+
npx e2e-ai-agents crew --workflow design-only --budget-usd 2.00 --json --path /path/to/project --tests-root ./e2e-tests --since origin/master
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### What the Crew Adds Beyond the Pipeline
|
|
79
|
+
|
|
80
|
+
| Capability | Pipeline | Crew |
|
|
81
|
+
|-----------|---------|------|
|
|
82
|
+
| Impact detection | Per-family, isolated | Same + cross-family ripple detection |
|
|
83
|
+
| Test scenarios | Flat `scenariosToAdd` strings | Structured `TestCase[]` with type, preconditions, steps, expected outcome, rationale |
|
|
84
|
+
| Test categories | None | 9: happy-path, edge-case, boundary, negative, state-transition, race-condition, permission, accessibility, performance |
|
|
85
|
+
| Strategy | None | Per-flow approach (full-test / smoke-test / skip) with priority and rationale |
|
|
86
|
+
| Regression awareness | None | Risk scoring from flaky history, calibration data, and file-pattern heuristics |
|
|
87
|
+
|
|
88
|
+
### Programmatic API
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { CrewOrchestrator, ImpactAnalystAgent, StrategistAgent, TestDesignerAgent, CrossImpactAgent, RegressionAdvisorAgent } from '@yasserkhanorg/e2e-agents';
|
|
92
|
+
|
|
93
|
+
const orchestrator = new CrewOrchestrator();
|
|
94
|
+
orchestrator.registerAgent(new ImpactAnalystAgent());
|
|
95
|
+
orchestrator.registerAgent(new CrossImpactAgent());
|
|
96
|
+
orchestrator.registerAgent(new RegressionAdvisorAgent());
|
|
97
|
+
orchestrator.registerAgent(new StrategistAgent());
|
|
98
|
+
orchestrator.registerAgent(new TestDesignerAgent());
|
|
99
|
+
|
|
100
|
+
const result = await orchestrator.run({
|
|
101
|
+
appPath: './webapp',
|
|
102
|
+
testsRoot: './e2e-tests',
|
|
103
|
+
gitSince: 'origin/master',
|
|
104
|
+
workflow: 'design-only',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
console.log(result.context.testDesigns); // Structured test cases
|
|
108
|
+
console.log(result.context.crossImpacts); // Cross-family links
|
|
109
|
+
console.log(result.context.strategyEntries); // Prioritized strategy
|
|
110
|
+
```
|
|
111
|
+
|
|
57
112
|
## Route-Families Training
|
|
58
113
|
|
|
59
114
|
### What it produces
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/crew/context.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAE,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AACrF,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/crew/context.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAE,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AACrF,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAC,WAAW,EAAE,gBAAgB,EAAC,MAAM,kCAAkC,CAAC;AACpF,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAC,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAC,MAAM,YAAY,CAAC;AAEhG,MAAM,WAAW,WAAW;IAExB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,WAAW,EAAE,CAAC;IAC7B,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,UAAU,EAAE,iBAAiB,CAAC;IAC9B,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAG1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAG1B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,cAAc,EAAE,aAAa,EAAE,CAAC;IAGhC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,CAa1D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAe5F"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// See LICENSE.txt for license information.
|
|
3
3
|
import { existsSync, readFileSync, statSync } from 'fs';
|
|
4
4
|
import { join } from 'path';
|
|
5
|
+
import { logger } from '../logger.js';
|
|
5
6
|
const manifestCache = new Map();
|
|
6
7
|
export function matchesGlob(filePath, pattern) {
|
|
7
8
|
const normalized = filePath.replace(/\\/g, '/');
|
|
@@ -154,8 +155,7 @@ export function loadRouteFamilyManifest(testsRoot, config) {
|
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
if (config?.strict) {
|
|
157
|
-
|
|
158
|
-
console.warn('[e2e-agents] Route family manifest not found. The manifest is optional context for AI enrichment — create .e2e-ai-agents/route-families.json to enable family-level routing hints.');
|
|
158
|
+
logger.warn('Route family manifest not found. Create .e2e-ai-agents/route-families.json to enable family-level routing hints.');
|
|
159
159
|
}
|
|
160
160
|
return null;
|
|
161
161
|
}
|
package/dist/esm/logger.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
2
|
// See LICENSE.txt for license information.
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* Replaces 18 console.log statements with configurable logging
|
|
4
|
+
* Structured logging system.
|
|
6
5
|
* Environment variable: LOG_LEVEL (ERROR, WARN, INFO, DEBUG)
|
|
7
6
|
*/
|
|
8
7
|
export var LogLevel;
|
|
@@ -103,7 +103,7 @@ export class OllamaProvider extends BaseProvider {
|
|
|
103
103
|
// SECURITY: Validate and sanitize URL
|
|
104
104
|
const urlValidation = validateOllamaUrl(config.baseUrl);
|
|
105
105
|
if (!urlValidation.valid && urlValidation.warning) {
|
|
106
|
-
|
|
106
|
+
logger.warn(urlValidation.warning);
|
|
107
107
|
}
|
|
108
108
|
// SECURITY: Validate timeout
|
|
109
109
|
const timeout = validateTimeout(config.timeout);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
2
|
// See LICENSE.txt for license information.
|
|
3
3
|
import { AnthropicProvider } from './anthropic_provider.js';
|
|
4
|
+
import { logger } from './logger.js';
|
|
4
5
|
import { CustomProvider } from './custom_provider.js';
|
|
5
6
|
import { OllamaProvider } from './ollama_provider.js';
|
|
6
7
|
import { OpenAIProvider } from './openai_provider.js';
|
|
@@ -131,8 +132,7 @@ export class LLMProviderFactory {
|
|
|
131
132
|
const ollama = new OllamaProvider({});
|
|
132
133
|
const health = await ollama.checkHealth();
|
|
133
134
|
if (health.healthy) {
|
|
134
|
-
|
|
135
|
-
console.log('Auto-detected Ollama provider (free, local)');
|
|
135
|
+
logger.info('Auto-detected Ollama provider (free, local)');
|
|
136
136
|
return ollama;
|
|
137
137
|
}
|
|
138
138
|
throw new Error('No LLM provider available. Please either:\n' +
|
|
@@ -218,8 +218,7 @@ class HybridProvider {
|
|
|
218
218
|
}
|
|
219
219
|
async generateText(prompt, options) {
|
|
220
220
|
// Use primary for text generation (free)
|
|
221
|
-
|
|
222
|
-
console.log(`[Hybrid] Using ${this.primary.name} for text generation`);
|
|
221
|
+
logger.debug(`[Hybrid] Using ${this.primary.name} for text generation`);
|
|
223
222
|
return await this.primary.generateText(prompt, options);
|
|
224
223
|
}
|
|
225
224
|
async analyzeImage(images, prompt, options) {
|
|
@@ -227,8 +226,7 @@ class HybridProvider {
|
|
|
227
226
|
if (this.useFallbackFor.has('vision')) {
|
|
228
227
|
// Use fallback if primary doesn't support vision
|
|
229
228
|
if (!this.primary.capabilities.vision) {
|
|
230
|
-
|
|
231
|
-
console.log(`[Hybrid] Using ${this.fallback.name} for vision analysis (primary doesn't support vision)`);
|
|
229
|
+
logger.debug(`[Hybrid] Using ${this.fallback.name} for vision analysis (primary doesn't support vision)`);
|
|
232
230
|
if (!this.fallback.analyzeImage) {
|
|
233
231
|
throw new UnsupportedCapabilityError(this.name, 'vision');
|
|
234
232
|
}
|
|
@@ -237,8 +235,7 @@ class HybridProvider {
|
|
|
237
235
|
}
|
|
238
236
|
// Try primary first
|
|
239
237
|
if (this.primary.analyzeImage) {
|
|
240
|
-
|
|
241
|
-
console.log(`[Hybrid] Using ${this.primary.name} for vision analysis`);
|
|
238
|
+
logger.debug(`[Hybrid] Using ${this.primary.name} for vision analysis`);
|
|
242
239
|
return await this.primary.analyzeImage(images, prompt, options);
|
|
243
240
|
}
|
|
244
241
|
throw new UnsupportedCapabilityError(this.name, 'vision');
|
|
@@ -248,8 +245,7 @@ class HybridProvider {
|
|
|
248
245
|
if (!this.primary.streamText) {
|
|
249
246
|
throw new UnsupportedCapabilityError(this.primary.name, 'streaming');
|
|
250
247
|
}
|
|
251
|
-
|
|
252
|
-
console.log(`[Hybrid] Using ${this.primary.name} for streaming`);
|
|
248
|
+
logger.debug(`[Hybrid] Using ${this.primary.name} for streaming`);
|
|
253
249
|
yield* this.primary.streamText(prompt, options);
|
|
254
250
|
}
|
|
255
251
|
getUsageStats() {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// See LICENSE.txt for license information.
|
|
3
3
|
import { lstatSync, readdirSync, readFileSync } from 'fs';
|
|
4
4
|
import { join, relative, resolve } from 'path';
|
|
5
|
+
import { logger } from '../logger.js';
|
|
5
6
|
import { isGuessedRoute } from './types.js';
|
|
6
7
|
const MAX_FILES_PER_FAMILY = 20;
|
|
7
8
|
const MAX_LINES_PER_FILE = 50;
|
|
@@ -289,11 +290,11 @@ export async function enrichFamilies(families, scanned, projectRoot, provider, b
|
|
|
289
290
|
// Truncate at the last complete section boundary to avoid malformed input
|
|
290
291
|
const lastSectionEnd = prompt.lastIndexOf('\n---\n', MAX_PROMPT_CHARS);
|
|
291
292
|
if (lastSectionEnd > 0) {
|
|
292
|
-
|
|
293
|
+
logger.warn(`[train] Prompt truncated from ${prompt.length} chars at section boundary`);
|
|
293
294
|
prompt = prompt.slice(0, lastSectionEnd);
|
|
294
295
|
}
|
|
295
296
|
else {
|
|
296
|
-
|
|
297
|
+
logger.warn(`[train] Prompt truncated from ${prompt.length} to ${MAX_PROMPT_CHARS} chars`);
|
|
297
298
|
prompt = prompt.slice(0, MAX_PROMPT_CHARS);
|
|
298
299
|
}
|
|
299
300
|
}
|
|
@@ -325,7 +326,7 @@ export async function enrichFamilies(families, scanned, projectRoot, provider, b
|
|
|
325
326
|
}
|
|
326
327
|
catch (error) {
|
|
327
328
|
// On LLM failure, keep families unchanged
|
|
328
|
-
|
|
329
|
+
logger.warn(`[train] LLM enrichment failed for chunk: ${error instanceof Error ? error.message : String(error)}`);
|
|
329
330
|
enriched.push(...chunk);
|
|
330
331
|
}
|
|
331
332
|
finally {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// See LICENSE.txt for license information.
|
|
3
3
|
import { execFileSync } from 'child_process';
|
|
4
4
|
import { resolve } from 'path';
|
|
5
|
+
import { logger } from '../logger.js';
|
|
5
6
|
import { bindFilesToFamilies } from '../knowledge/route_families.js';
|
|
6
7
|
/**
|
|
7
8
|
* Glob-style patterns for infrastructure / cross-cutting files that will never
|
|
@@ -103,7 +104,7 @@ export function getCommitFiles(projectRoot, since) {
|
|
|
103
104
|
});
|
|
104
105
|
}
|
|
105
106
|
catch (error) {
|
|
106
|
-
|
|
107
|
+
logger.warn(`[train] git log failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
107
108
|
return [];
|
|
108
109
|
}
|
|
109
110
|
return parseGitLog(log);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route_families.d.ts","sourceRoot":"","sources":["../../src/knowledge/route_families.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"route_families.d.ts","sourceRoot":"","sources":["../../src/knowledge/route_families.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@ exports.getRoutesForBinding = getRoutesForBinding;
|
|
|
16
16
|
exports.clearManifestCache = clearManifestCache;
|
|
17
17
|
const fs_1 = require("fs");
|
|
18
18
|
const path_1 = require("path");
|
|
19
|
+
const logger_js_1 = require("../logger.js");
|
|
19
20
|
const manifestCache = new Map();
|
|
20
21
|
function matchesGlob(filePath, pattern) {
|
|
21
22
|
const normalized = filePath.replace(/\\/g, '/');
|
|
@@ -168,8 +169,7 @@ function loadRouteFamilyManifest(testsRoot, config) {
|
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
if (config?.strict) {
|
|
171
|
-
|
|
172
|
-
console.warn('[e2e-agents] Route family manifest not found. The manifest is optional context for AI enrichment — create .e2e-ai-agents/route-families.json to enable family-level routing hints.');
|
|
172
|
+
logger_js_1.logger.warn('Route family manifest not found. Create .e2e-ai-agents/route-families.json to enable family-level routing hints.');
|
|
173
173
|
}
|
|
174
174
|
return null;
|
|
175
175
|
}
|
package/dist/logger.d.ts
CHANGED
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,oBAAY,QAAQ;IAChB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACZ;AAqCD,qBAAa,MAAM;IACf,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,QAAQ,CAAU;gBAEd,QAAQ,CAAC,EAAE,QAAQ;IAK/B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM/D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM9D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM/D,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAI/B,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAInC;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG;QAAC,GAAG,EAAE,MAAM,MAAM,CAAA;KAAC;IAWzC,OAAO,CAAC,GAAG;CAoBd;AAGD,eAAO,MAAM,MAAM,QAAe,CAAC"}
|
package/dist/logger.js
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.logger = exports.Logger = exports.LogLevel = void 0;
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* Replaces 18 console.log statements with configurable logging
|
|
7
|
+
* Structured logging system.
|
|
9
8
|
* Environment variable: LOG_LEVEL (ERROR, WARN, INFO, DEBUG)
|
|
10
9
|
*/
|
|
11
10
|
var LogLevel;
|
package/dist/ollama_provider.js
CHANGED
|
@@ -110,7 +110,7 @@ class OllamaProvider extends base_provider_js_1.BaseProvider {
|
|
|
110
110
|
// SECURITY: Validate and sanitize URL
|
|
111
111
|
const urlValidation = validateOllamaUrl(config.baseUrl);
|
|
112
112
|
if (!urlValidation.valid && urlValidation.warning) {
|
|
113
|
-
|
|
113
|
+
logger_js_1.logger.warn(urlValidation.warning);
|
|
114
114
|
}
|
|
115
115
|
// SECURITY: Validate timeout
|
|
116
116
|
const timeout = validateTimeout(config.timeout);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider_factory.d.ts","sourceRoot":"","sources":["../src/provider_factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider_factory.d.ts","sourceRoot":"","sources":["../src/provider_factory.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAKR,WAAW,EAIX,cAAc,EAEjB,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,kBAAkB;IAC3B;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,WAAW;IAmBlD;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAWtD;;;;;;;;;OASG;WACU,aAAa,IAAI,OAAO,CAAC,WAAW,CAAC;IAyElD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW;CAkC/D;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,OAAO,EAAE,cAAc,CAAC;IAExB;;;OAGG;IACH,QAAQ,EAAE,cAAc,CAAC;IAEzB;;;OAGG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,mBAAmB,GAAG,wBAAwB,CAAC,CAAC;CACrF;AA+ID;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC;IACxE,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC,CAsCD"}
|
package/dist/provider_factory.js
CHANGED
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
5
5
|
exports.LLMProviderFactory = void 0;
|
|
6
6
|
exports.validateProviderSetup = validateProviderSetup;
|
|
7
7
|
const anthropic_provider_js_1 = require("./anthropic_provider.js");
|
|
8
|
+
const logger_js_1 = require("./logger.js");
|
|
8
9
|
const custom_provider_js_1 = require("./custom_provider.js");
|
|
9
10
|
const ollama_provider_js_1 = require("./ollama_provider.js");
|
|
10
11
|
const openai_provider_js_1 = require("./openai_provider.js");
|
|
@@ -135,8 +136,7 @@ class LLMProviderFactory {
|
|
|
135
136
|
const ollama = new ollama_provider_js_1.OllamaProvider({});
|
|
136
137
|
const health = await ollama.checkHealth();
|
|
137
138
|
if (health.healthy) {
|
|
138
|
-
|
|
139
|
-
console.log('Auto-detected Ollama provider (free, local)');
|
|
139
|
+
logger_js_1.logger.info('Auto-detected Ollama provider (free, local)');
|
|
140
140
|
return ollama;
|
|
141
141
|
}
|
|
142
142
|
throw new Error('No LLM provider available. Please either:\n' +
|
|
@@ -223,8 +223,7 @@ class HybridProvider {
|
|
|
223
223
|
}
|
|
224
224
|
async generateText(prompt, options) {
|
|
225
225
|
// Use primary for text generation (free)
|
|
226
|
-
|
|
227
|
-
console.log(`[Hybrid] Using ${this.primary.name} for text generation`);
|
|
226
|
+
logger_js_1.logger.debug(`[Hybrid] Using ${this.primary.name} for text generation`);
|
|
228
227
|
return await this.primary.generateText(prompt, options);
|
|
229
228
|
}
|
|
230
229
|
async analyzeImage(images, prompt, options) {
|
|
@@ -232,8 +231,7 @@ class HybridProvider {
|
|
|
232
231
|
if (this.useFallbackFor.has('vision')) {
|
|
233
232
|
// Use fallback if primary doesn't support vision
|
|
234
233
|
if (!this.primary.capabilities.vision) {
|
|
235
|
-
|
|
236
|
-
console.log(`[Hybrid] Using ${this.fallback.name} for vision analysis (primary doesn't support vision)`);
|
|
234
|
+
logger_js_1.logger.debug(`[Hybrid] Using ${this.fallback.name} for vision analysis (primary doesn't support vision)`);
|
|
237
235
|
if (!this.fallback.analyzeImage) {
|
|
238
236
|
throw new provider_interface_js_1.UnsupportedCapabilityError(this.name, 'vision');
|
|
239
237
|
}
|
|
@@ -242,8 +240,7 @@ class HybridProvider {
|
|
|
242
240
|
}
|
|
243
241
|
// Try primary first
|
|
244
242
|
if (this.primary.analyzeImage) {
|
|
245
|
-
|
|
246
|
-
console.log(`[Hybrid] Using ${this.primary.name} for vision analysis`);
|
|
243
|
+
logger_js_1.logger.debug(`[Hybrid] Using ${this.primary.name} for vision analysis`);
|
|
247
244
|
return await this.primary.analyzeImage(images, prompt, options);
|
|
248
245
|
}
|
|
249
246
|
throw new provider_interface_js_1.UnsupportedCapabilityError(this.name, 'vision');
|
|
@@ -253,8 +250,7 @@ class HybridProvider {
|
|
|
253
250
|
if (!this.primary.streamText) {
|
|
254
251
|
throw new provider_interface_js_1.UnsupportedCapabilityError(this.primary.name, 'streaming');
|
|
255
252
|
}
|
|
256
|
-
|
|
257
|
-
console.log(`[Hybrid] Using ${this.primary.name} for streaming`);
|
|
253
|
+
logger_js_1.logger.debug(`[Hybrid] Using ${this.primary.name} for streaming`);
|
|
258
254
|
yield* this.primary.streamText(prompt, options);
|
|
259
255
|
}
|
|
260
256
|
getUsageStats() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enricher.d.ts","sourceRoot":"","sources":["../../src/training/enricher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"enricher.d.ts","sourceRoot":"","sources":["../../src/training/enricher.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAGhE,OAAO,KAAK,EAAC,gBAAgB,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AAkLhE,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,EAAE,CAmBlE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAwBrE;AAkCD,wBAAsB,cAAc,CAChC,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,EAAE,aAAa,EAAE,EACxB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,gBAAgB,CAAC,CAuF3B"}
|
|
@@ -7,6 +7,7 @@ exports.parseEnrichResponse = parseEnrichResponse;
|
|
|
7
7
|
exports.enrichFamilies = enrichFamilies;
|
|
8
8
|
const fs_1 = require("fs");
|
|
9
9
|
const path_1 = require("path");
|
|
10
|
+
const logger_js_1 = require("../logger.js");
|
|
10
11
|
const types_js_1 = require("./types.js");
|
|
11
12
|
const MAX_FILES_PER_FAMILY = 20;
|
|
12
13
|
const MAX_LINES_PER_FILE = 50;
|
|
@@ -294,11 +295,11 @@ async function enrichFamilies(families, scanned, projectRoot, provider, budgetUS
|
|
|
294
295
|
// Truncate at the last complete section boundary to avoid malformed input
|
|
295
296
|
const lastSectionEnd = prompt.lastIndexOf('\n---\n', MAX_PROMPT_CHARS);
|
|
296
297
|
if (lastSectionEnd > 0) {
|
|
297
|
-
|
|
298
|
+
logger_js_1.logger.warn(`[train] Prompt truncated from ${prompt.length} chars at section boundary`);
|
|
298
299
|
prompt = prompt.slice(0, lastSectionEnd);
|
|
299
300
|
}
|
|
300
301
|
else {
|
|
301
|
-
|
|
302
|
+
logger_js_1.logger.warn(`[train] Prompt truncated from ${prompt.length} to ${MAX_PROMPT_CHARS} chars`);
|
|
302
303
|
prompt = prompt.slice(0, MAX_PROMPT_CHARS);
|
|
303
304
|
}
|
|
304
305
|
}
|
|
@@ -330,7 +331,7 @@ async function enrichFamilies(families, scanned, projectRoot, provider, budgetUS
|
|
|
330
331
|
}
|
|
331
332
|
catch (error) {
|
|
332
333
|
// On LLM failure, keep families unchanged
|
|
333
|
-
|
|
334
|
+
logger_js_1.logger.warn(`[train] LLM enrichment failed for chunk: ${error instanceof Error ? error.message : String(error)}`);
|
|
334
335
|
enriched.push(...chunk);
|
|
335
336
|
}
|
|
336
337
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/training/validator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/training/validator.ts"],"names":[],"mappings":"AAQA,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"}
|
|
@@ -10,6 +10,7 @@ exports.buildValidationReport = buildValidationReport;
|
|
|
10
10
|
exports.formatValidationReport = formatValidationReport;
|
|
11
11
|
const child_process_1 = require("child_process");
|
|
12
12
|
const path_1 = require("path");
|
|
13
|
+
const logger_js_1 = require("../logger.js");
|
|
13
14
|
const route_families_js_1 = require("../knowledge/route_families.js");
|
|
14
15
|
/**
|
|
15
16
|
* Glob-style patterns for infrastructure / cross-cutting files that will never
|
|
@@ -111,7 +112,7 @@ function getCommitFiles(projectRoot, since) {
|
|
|
111
112
|
});
|
|
112
113
|
}
|
|
113
114
|
catch (error) {
|
|
114
|
-
|
|
115
|
+
logger_js_1.logger.warn(`[train] git log failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
115
116
|
return [];
|
|
116
117
|
}
|
|
117
118
|
return parseGitLog(log);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yasserkhanorg/e2e-agents",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.1",
|
|
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",
|