@yasserkhanorg/e2e-agents 1.7.7 → 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/agent/git.d.ts +5 -0
- package/dist/agent/git.d.ts.map +1 -1
- package/dist/agent/git.js +13 -0
- package/dist/agents/coverage-evaluator.d.ts +8 -0
- package/dist/agents/coverage-evaluator.d.ts.map +1 -0
- package/dist/agents/coverage-evaluator.js +41 -0
- package/dist/agents/cross-impact.d.ts +13 -0
- package/dist/agents/cross-impact.d.ts.map +1 -0
- package/dist/agents/cross-impact.js +135 -0
- package/dist/agents/executor.d.ts +8 -0
- package/dist/agents/executor.d.ts.map +1 -0
- package/dist/agents/executor.js +70 -0
- package/dist/agents/explorer.d.ts +12 -0
- package/dist/agents/explorer.d.ts.map +1 -0
- package/dist/agents/explorer.js +43 -0
- package/dist/agents/generator.d.ts +8 -0
- package/dist/agents/generator.d.ts.map +1 -0
- package/dist/agents/generator.js +77 -0
- package/dist/agents/healer.d.ts +8 -0
- package/dist/agents/healer.d.ts.map +1 -0
- package/dist/agents/healer.js +31 -0
- package/dist/agents/impact-analyst.d.ts +8 -0
- package/dist/agents/impact-analyst.d.ts.map +1 -0
- package/dist/agents/impact-analyst.js +38 -0
- package/dist/agents/regression-advisor.d.ts +8 -0
- package/dist/agents/regression-advisor.d.ts.map +1 -0
- package/dist/agents/regression-advisor.js +116 -0
- package/dist/agents/strategist.d.ts +9 -0
- package/dist/agents/strategist.d.ts.map +1 -0
- package/dist/agents/strategist.js +87 -0
- package/dist/agents/test-designer.d.ts +8 -0
- package/dist/agents/test-designer.d.ts.map +1 -0
- package/dist/agents/test-designer.js +106 -0
- package/dist/cli/commands/crew.d.ts +3 -0
- package/dist/cli/commands/crew.d.ts.map +1 -0
- package/dist/cli/commands/crew.js +137 -0
- package/dist/cli/parse_args.d.ts.map +1 -1
- package/dist/cli/parse_args.js +2 -1
- package/dist/cli/types.d.ts +2 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli.js +5 -0
- package/dist/crew/context.d.ts +40 -0
- package/dist/crew/context.d.ts.map +1 -0
- package/dist/crew/context.js +36 -0
- package/dist/crew/orchestrator.d.ts +36 -0
- package/dist/crew/orchestrator.d.ts.map +1 -0
- package/dist/crew/orchestrator.js +171 -0
- package/dist/crew/protocol.d.ts +33 -0
- package/dist/crew/protocol.d.ts.map +1 -0
- package/dist/crew/protocol.js +4 -0
- package/dist/crew/provider.d.ts +3 -0
- package/dist/crew/provider.d.ts.map +1 -0
- package/dist/crew/provider.js +16 -0
- package/dist/crew/sanitize.d.ts +3 -0
- package/dist/crew/sanitize.d.ts.map +1 -0
- package/dist/crew/sanitize.js +31 -0
- package/dist/crew/types.d.ts +52 -0
- package/dist/crew/types.d.ts.map +1 -0
- package/dist/crew/types.js +4 -0
- package/dist/crew/workflows.d.ts +52 -0
- package/dist/crew/workflows.d.ts.map +1 -0
- package/dist/crew/workflows.js +36 -0
- package/dist/esm/agent/git.js +12 -0
- package/dist/esm/agents/coverage-evaluator.js +37 -0
- package/dist/esm/agents/cross-impact.js +131 -0
- package/dist/esm/agents/executor.js +66 -0
- package/dist/esm/agents/explorer.js +39 -0
- package/dist/esm/agents/generator.js +73 -0
- package/dist/esm/agents/healer.js +27 -0
- package/dist/esm/agents/impact-analyst.js +34 -0
- package/dist/esm/agents/regression-advisor.js +112 -0
- package/dist/esm/agents/strategist.js +83 -0
- package/dist/esm/agents/test-designer.js +102 -0
- package/dist/esm/cli/commands/crew.js +134 -0
- package/dist/esm/cli/parse_args.js +2 -1
- package/dist/esm/cli.js +5 -0
- package/dist/esm/crew/context.js +32 -0
- package/dist/esm/crew/orchestrator.js +167 -0
- package/dist/esm/crew/protocol.js +3 -0
- package/dist/esm/crew/provider.js +13 -0
- package/dist/esm/crew/sanitize.js +27 -0
- package/dist/esm/crew/types.js +3 -0
- package/dist/esm/crew/workflows.js +33 -0
- package/dist/esm/index.js +14 -0
- 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/prompts/cross-impact.js +71 -0
- package/dist/esm/prompts/strategist.js +79 -0
- package/dist/esm/prompts/test-designer.js +107 -0
- 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/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -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/prompts/cross-impact.d.ts +22 -0
- package/dist/prompts/cross-impact.d.ts.map +1 -0
- package/dist/prompts/cross-impact.js +75 -0
- package/dist/prompts/strategist.d.ts +25 -0
- package/dist/prompts/strategist.d.ts.map +1 -0
- package/dist/prompts/strategist.js +83 -0
- package/dist/prompts/test-designer.d.ts +33 -0
- package/dist/prompts/test-designer.d.ts.map +1 -0
- package/dist/prompts/test-designer.js +111 -0
- 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
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.buildStrategistPrompt = buildStrategistPrompt;
|
|
6
|
+
exports.parseStrategistResponse = parseStrategistResponse;
|
|
7
|
+
const sanitize_js_1 = require("../crew/sanitize.js");
|
|
8
|
+
function buildStrategistPrompt(ctx) {
|
|
9
|
+
const flowsBlock = ctx.impactedFlows
|
|
10
|
+
.map((f) => {
|
|
11
|
+
const specs = f.existingSpecs.map((s) => `${s.path} (${s.coverageLevel})`).join(', ') || 'none';
|
|
12
|
+
return [
|
|
13
|
+
`- ${f.flowId} (${f.priority}): ${f.flowName}`,
|
|
14
|
+
` Route Family: ${f.routeFamily}`,
|
|
15
|
+
` Action: ${f.action}`,
|
|
16
|
+
` Confidence: ${f.confidence}%`,
|
|
17
|
+
` Existing Coverage: ${specs}`,
|
|
18
|
+
` User Actions: ${(0, sanitize_js_1.sanitizeForPrompt)(f.userActions.join('; ') || 'unknown')}`,
|
|
19
|
+
` Changed Files: ${f.changedFiles.join(', ')}`,
|
|
20
|
+
].join('\n');
|
|
21
|
+
})
|
|
22
|
+
.join('\n\n');
|
|
23
|
+
const crossImpactBlock = ctx.crossImpacts.length > 0
|
|
24
|
+
? ctx.crossImpacts.map((ci) => `- ${ci.sourceFamily} → ${ci.affectedFamily} (${ci.riskLevel}): ${ci.sharedDependency} — ${ci.evidence}`).join('\n')
|
|
25
|
+
: 'No cross-family impacts detected.';
|
|
26
|
+
const regressionBlock = ctx.regressionRisks.length > 0
|
|
27
|
+
? ctx.regressionRisks.map((r) => `- ${r.familyId} (risk=${r.riskScore}): ${r.reason}`).join('\n')
|
|
28
|
+
: 'No regression risk data available.';
|
|
29
|
+
return [
|
|
30
|
+
'You are a senior QA strategist designing the overall test strategy for a code change.',
|
|
31
|
+
'',
|
|
32
|
+
`IMPACTED FLOWS (${ctx.impactedFlows.length}):`,
|
|
33
|
+
flowsBlock,
|
|
34
|
+
'',
|
|
35
|
+
'CROSS-FAMILY IMPACTS:',
|
|
36
|
+
crossImpactBlock,
|
|
37
|
+
'',
|
|
38
|
+
'REGRESSION RISK:',
|
|
39
|
+
regressionBlock,
|
|
40
|
+
'',
|
|
41
|
+
'TASK: Design a prioritized test strategy for each impacted flow.',
|
|
42
|
+
'',
|
|
43
|
+
'For each flow, decide:',
|
|
44
|
+
'1. Approach: full-test (comprehensive), smoke-test (critical path only), skip, or manual-review',
|
|
45
|
+
'2. Priority: P0 (critical path), P1 (important), P2 (nice to have)',
|
|
46
|
+
'3. Test categories to cover (from: happy-path, edge-case, boundary, negative, state-transition, race-condition, permission, accessibility, performance)',
|
|
47
|
+
'4. Cross-impact risk level based on shared dependencies',
|
|
48
|
+
'',
|
|
49
|
+
'Return strict JSON only with this shape:',
|
|
50
|
+
'{"strategy":[{"flowId":"<id>","flowName":"<name>","priority":"P0|P1|P2","approach":"full-test|smoke-test|skip|manual-review","rationale":"<why this approach>","testCategories":["happy-path","edge-case",...],"crossImpactRisk":"high|medium|low|none"}]}',
|
|
51
|
+
'',
|
|
52
|
+
'Rules:',
|
|
53
|
+
'- P0 flows with create_spec or add_scenarios action should always get full-test.',
|
|
54
|
+
'- Flows with high cross-impact risk should be promoted to at least P1.',
|
|
55
|
+
'- Flows with high regression risk should include edge-case and boundary categories.',
|
|
56
|
+
'- Skip flows only if confidence < 30 AND no cross-impact risk.',
|
|
57
|
+
'- Include accessibility category for any flow involving interactive UI elements.',
|
|
58
|
+
'- Include permission category for any flow involving role-based features.',
|
|
59
|
+
'- Keep rationale concise (1-2 sentences) explaining why this approach was chosen.',
|
|
60
|
+
].join('\n');
|
|
61
|
+
}
|
|
62
|
+
function parseStrategistResponse(text) {
|
|
63
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
64
|
+
const candidates = fenced ? [fenced[1], text] : [text];
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
const start = candidate.indexOf('{');
|
|
67
|
+
const end = candidate.lastIndexOf('}');
|
|
68
|
+
if (start < 0 || end <= start) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const raw = candidate.slice(start, end + 1);
|
|
72
|
+
try {
|
|
73
|
+
const parsed = JSON.parse(raw);
|
|
74
|
+
if (parsed && Array.isArray(parsed.strategy)) {
|
|
75
|
+
return parsed;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Designer prompt — designs structured test cases across 9 categories.
|
|
3
|
+
* Replaces flat scenariosToAdd with rich TestCase[] that feed the Generator.
|
|
4
|
+
*/
|
|
5
|
+
import type { FlowDecision } from '../validation/output_schema.js';
|
|
6
|
+
import { type ApiSurfaceCatalog } from '../knowledge/api_surface.js';
|
|
7
|
+
import type { SpecEntry } from '../knowledge/spec_index.js';
|
|
8
|
+
import type { StrategyEntry, CrossImpact } from '../crew/types.js';
|
|
9
|
+
export interface TestDesignerPromptContext {
|
|
10
|
+
flow: FlowDecision;
|
|
11
|
+
strategy: StrategyEntry;
|
|
12
|
+
apiSurface: ApiSurfaceCatalog;
|
|
13
|
+
existingSpecs: SpecEntry[];
|
|
14
|
+
crossImpacts: CrossImpact[];
|
|
15
|
+
}
|
|
16
|
+
export declare function buildTestDesignerPrompt(ctx: TestDesignerPromptContext): string;
|
|
17
|
+
export interface TestDesignerAgentResponse {
|
|
18
|
+
testDesign: {
|
|
19
|
+
flowId: string;
|
|
20
|
+
flowName: string;
|
|
21
|
+
testCases: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
type: 'happy-path' | 'edge-case' | 'boundary' | 'negative' | 'state-transition' | 'race-condition' | 'permission' | 'accessibility' | 'performance' | string;
|
|
24
|
+
preconditions: string[];
|
|
25
|
+
steps: string[];
|
|
26
|
+
expectedOutcome: string;
|
|
27
|
+
priority: 'P0' | 'P1' | 'P2' | string;
|
|
28
|
+
rationale: string;
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export declare function parseTestDesignerResponse(text: string): TestDesignerAgentResponse | null;
|
|
33
|
+
//# sourceMappingURL=test-designer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-designer.d.ts","sourceRoot":"","sources":["../../src/prompts/test-designer.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAA4B,KAAK,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAC9F,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAAC,aAAa,EAAE,WAAW,EAAa,MAAM,kBAAkB,CAAC;AAG7E,MAAM,WAAW,yBAAyB;IACtC,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,EAAE,SAAS,EAAE,CAAC;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAC;CAC/B;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,yBAAyB,GAAG,MAAM,CAuF9E;AAED,MAAM,WAAW,yBAAyB;IACtC,UAAU,EAAE;QACR,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,KAAK,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,YAAY,GAAG,eAAe,GAAG,aAAa,GAAG,MAAM,CAAC;YAC7J,aAAa,EAAE,MAAM,EAAE,CAAC;YACxB,KAAK,EAAE,MAAM,EAAE,CAAC;YAChB,eAAe,EAAE,MAAM,CAAC;YACxB,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;YACtC,SAAS,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACN,CAAC;CACL;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB,GAAG,IAAI,CAqBxF"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.buildTestDesignerPrompt = buildTestDesignerPrompt;
|
|
6
|
+
exports.parseTestDesignerResponse = parseTestDesignerResponse;
|
|
7
|
+
const api_surface_js_1 = require("../knowledge/api_surface.js");
|
|
8
|
+
const sanitize_js_1 = require("../crew/sanitize.js");
|
|
9
|
+
function buildTestDesignerPrompt(ctx) {
|
|
10
|
+
const relevantClasses = ctx.apiSurface.pageObjects
|
|
11
|
+
.map((po) => po.className)
|
|
12
|
+
.filter((name) => {
|
|
13
|
+
const lower = name.toLowerCase();
|
|
14
|
+
const hints = [ctx.flow.routeFamily, ctx.flow.featureId, ...ctx.flow.userActions.join(' ').split(/\s+/)]
|
|
15
|
+
.filter(Boolean)
|
|
16
|
+
.map((s) => s.toLowerCase().replace(/[^a-z]/g, ''));
|
|
17
|
+
return lower.includes('page') || hints.some((h) => h.length > 3 && lower.includes(h));
|
|
18
|
+
})
|
|
19
|
+
.slice(0, 10);
|
|
20
|
+
const apiBlock = relevantClasses.length > 0
|
|
21
|
+
? (0, api_surface_js_1.formatApiSurfaceForPrompt)(ctx.apiSurface, relevantClasses)
|
|
22
|
+
: 'No page objects available.';
|
|
23
|
+
const existingSpecsBlock = ctx.existingSpecs.length > 0
|
|
24
|
+
? ctx.existingSpecs.map((s) => `- ${s.relativePath}: ${s.testTitles.join(', ')}`).join('\n')
|
|
25
|
+
: 'No existing specs.';
|
|
26
|
+
const crossImpactBlock = ctx.crossImpacts.length > 0
|
|
27
|
+
? ctx.crossImpacts.map((ci) => `- ${ci.sourceFamily} → ${ci.affectedFamily}: ${ci.sharedDependency} (${ci.riskLevel})`).join('\n')
|
|
28
|
+
: 'None detected.';
|
|
29
|
+
const categories = ctx.strategy.testCategories.join(', ');
|
|
30
|
+
return [
|
|
31
|
+
'You are a senior QA engineer designing comprehensive test cases for a Mattermost user flow.',
|
|
32
|
+
'',
|
|
33
|
+
`FLOW: ${ctx.flow.flowName}`,
|
|
34
|
+
`Flow ID: ${ctx.flow.flowId}`,
|
|
35
|
+
`Route Family: ${ctx.flow.routeFamily}${ctx.flow.featureId ? ` / ${ctx.flow.featureId}` : ''}`,
|
|
36
|
+
`Route: ${ctx.flow.specificRoute || '(not specified)'}`,
|
|
37
|
+
`Priority: ${ctx.strategy.priority}`,
|
|
38
|
+
`Approach: ${ctx.strategy.approach}`,
|
|
39
|
+
`User Actions: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.flow.userActions.join('; ') || 'unknown')}`,
|
|
40
|
+
`Evidence: ${(0, sanitize_js_1.sanitizeForPrompt)(ctx.flow.evidence)}`,
|
|
41
|
+
'',
|
|
42
|
+
`REQUIRED TEST CATEGORIES: ${categories}`,
|
|
43
|
+
'',
|
|
44
|
+
'AVAILABLE PAGE OBJECTS:',
|
|
45
|
+
apiBlock,
|
|
46
|
+
'',
|
|
47
|
+
'EXISTING SPECS (avoid duplicating these):',
|
|
48
|
+
existingSpecsBlock,
|
|
49
|
+
'',
|
|
50
|
+
'CROSS-FAMILY IMPACTS:',
|
|
51
|
+
crossImpactBlock,
|
|
52
|
+
'',
|
|
53
|
+
'TASK: Design structured test cases for this flow.',
|
|
54
|
+
'',
|
|
55
|
+
'Return strict JSON only with this shape:',
|
|
56
|
+
'{"testDesign":{"flowId":"<id>","flowName":"<name>","testCases":[{"name":"<descriptive name>","type":"<category>","preconditions":["<state required>"],"steps":["<user action>"],"expectedOutcome":"<what should happen>","priority":"P0|P1|P2","rationale":"<why this test matters>"}]}}',
|
|
57
|
+
'',
|
|
58
|
+
'TYPE VALUES: happy-path, edge-case, boundary, negative, state-transition, race-condition, permission, accessibility, performance',
|
|
59
|
+
'',
|
|
60
|
+
'Rules:',
|
|
61
|
+
'- Every test must describe a specific USER ACTION, not an implementation detail.',
|
|
62
|
+
'- Steps must be concrete: "click Create Channel button" not "test channel creation".',
|
|
63
|
+
'- Include preconditions (logged-in role, existing data state, etc.).',
|
|
64
|
+
'- Reference only page objects and methods listed above.',
|
|
65
|
+
'- Include a mandatory rationale explaining why this specific test case matters.',
|
|
66
|
+
'- Do NOT duplicate tests already covered by existing specs.',
|
|
67
|
+
'- Maximum 15 test cases per flow.',
|
|
68
|
+
'- For accessibility: test keyboard navigation, screen reader support, ARIA labels.',
|
|
69
|
+
'- For performance: test with realistic data volumes, measure load times.',
|
|
70
|
+
'- For edge cases: test unicode input, max-length fields, empty states, concurrent edits.',
|
|
71
|
+
'',
|
|
72
|
+
'FEW-SHOT EXAMPLES:',
|
|
73
|
+
'',
|
|
74
|
+
'Edge case example:',
|
|
75
|
+
'```json',
|
|
76
|
+
'{"name":"channel creation with unicode characters and max-length name","type":"edge-case","preconditions":["logged in as team member","team has < 1000 channels"],"steps":["open create channel dialog","enter 64-character name with emoji and CJK characters","click Create"],"expectedOutcome":"channel created successfully, name renders correctly in sidebar and header","priority":"P1","rationale":"catches encoding issues in channel name storage and rendering"}',
|
|
77
|
+
'```',
|
|
78
|
+
'',
|
|
79
|
+
'Permission example:',
|
|
80
|
+
'```json',
|
|
81
|
+
'{"name":"guest user cannot archive a public channel","type":"permission","preconditions":["logged in as guest user","guest has access to public channel"],"steps":["open channel header menu","look for Archive Channel option"],"expectedOutcome":"Archive Channel option is not visible in the menu","priority":"P0","rationale":"permission escalation bug — guests archiving channels could disrupt entire teams"}',
|
|
82
|
+
'```',
|
|
83
|
+
'',
|
|
84
|
+
'Accessibility example:',
|
|
85
|
+
'```json',
|
|
86
|
+
'{"name":"keyboard navigation through channel switcher results","type":"accessibility","preconditions":["logged in","channel switcher open via Ctrl+K"],"steps":["type partial channel name","press ArrowDown to navigate results","press Enter to select"],"expectedOutcome":"focus moves visually and via aria-activedescendant, selected channel opens","priority":"P1","rationale":"screen reader users rely on keyboard navigation — broken focus management makes the app unusable"}',
|
|
87
|
+
'```',
|
|
88
|
+
].join('\n');
|
|
89
|
+
}
|
|
90
|
+
function parseTestDesignerResponse(text) {
|
|
91
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
92
|
+
const candidates = fenced ? [fenced[1], text] : [text];
|
|
93
|
+
for (const candidate of candidates) {
|
|
94
|
+
const start = candidate.indexOf('{');
|
|
95
|
+
const end = candidate.lastIndexOf('}');
|
|
96
|
+
if (start < 0 || end <= start) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const raw = candidate.slice(start, end + 1);
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(raw);
|
|
102
|
+
if (parsed?.testDesign?.testCases && Array.isArray(parsed.testDesign.testCases)) {
|
|
103
|
+
return parsed;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
@@ -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.
|
|
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",
|