@yasserkhanorg/e2e-agents 1.3.2 → 1.4.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 (44) hide show
  1. package/README.md +40 -9
  2. package/dist/cli/commands/train.d.ts +3 -0
  3. package/dist/cli/commands/train.d.ts.map +1 -0
  4. package/dist/cli/commands/train.js +307 -0
  5. package/dist/cli/parse_args.d.ts.map +1 -1
  6. package/dist/cli/parse_args.js +7 -1
  7. package/dist/cli/types.d.ts +6 -1
  8. package/dist/cli/types.d.ts.map +1 -1
  9. package/dist/cli/usage.d.ts.map +1 -1
  10. package/dist/cli/usage.js +7 -1
  11. package/dist/cli.js +5 -0
  12. package/dist/esm/cli/commands/train.js +271 -0
  13. package/dist/esm/cli/parse_args.js +7 -1
  14. package/dist/esm/cli/usage.js +7 -1
  15. package/dist/esm/cli.js +5 -0
  16. package/dist/esm/index.js +5 -0
  17. package/dist/esm/knowledge/route_families.js +2 -2
  18. package/dist/esm/training/enricher.js +273 -0
  19. package/dist/esm/training/merger.js +137 -0
  20. package/dist/esm/training/scanner.js +386 -0
  21. package/dist/esm/training/types.js +6 -0
  22. package/dist/esm/training/validator.js +153 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +15 -1
  26. package/dist/knowledge/route_families.d.ts +2 -0
  27. package/dist/knowledge/route_families.d.ts.map +1 -1
  28. package/dist/knowledge/route_families.js +2 -0
  29. package/dist/training/enricher.d.ts +15 -0
  30. package/dist/training/enricher.d.ts.map +1 -0
  31. package/dist/training/enricher.js +278 -0
  32. package/dist/training/merger.d.ts +5 -0
  33. package/dist/training/merger.d.ts.map +1 -0
  34. package/dist/training/merger.js +141 -0
  35. package/dist/training/scanner.d.ts +5 -0
  36. package/dist/training/scanner.d.ts.map +1 -0
  37. package/dist/training/scanner.js +391 -0
  38. package/dist/training/types.d.ts +109 -0
  39. package/dist/training/types.d.ts.map +1 -0
  40. package/dist/training/types.js +9 -0
  41. package/dist/training/validator.d.ts +16 -0
  42. package/dist/training/validator.d.ts.map +1 -0
  43. package/dist/training/validator.js +160 -0
  44. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@
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.runAgenticGeneration = exports.getSpecsForFamily = exports.buildSpecIndex = exports.loadOrBuildApiSurface = exports.buildApiSurface = exports.getUserFlowsForBinding = exports.getPriorityForBinding = exports.getCypressSpecDirsForBinding = exports.bindFilesToFamilies = exports.loadRouteFamilyManifest = exports.buildQualityFixPrompt = exports.buildHealPrompt = exports.renderHealMarkdown = exports.resolveHealTargets = exports.healFromReport = exports.runHealStage = exports.detectHallucinatedMethods = exports.parseGenerationResponse = exports.buildGenerationPrompt = exports.runGenerationStage = exports.runPipeline = exports.captureTraceabilityInput = exports.ingestTraceabilityInput = exports.finalizeGeneratedTests = exports.readFlakyTests = exports.readCalibration = exports.appendFeedbackAndRecompute = exports.buildPlanFromImpact = exports.extractScenarios = exports.getPartialGaps = exports.getGaps = exports.analyzeImpactV2 = exports.captureTraceability = exports.ingestTraceability = exports.handoffGeneratedTests = exports.recommendTestsDeterministic = exports.analyzeImpactDeterministic = exports.validateProviderSetup = exports.LLMProviderFactory = exports.CustomProvider = exports.checkOpenAISetup = exports.OpenAIProvider = exports.checkOllamaSetup = exports.OllamaProvider = exports.checkAnthropicSetup = exports.AnthropicProvider = exports.UnsupportedCapabilityError = exports.LLMProviderError = void 0;
5
+ exports.mergeFamilies = exports.scanProject = exports.runAgenticGeneration = exports.getSpecsForFamily = exports.buildSpecIndex = exports.loadOrBuildApiSurface = exports.buildApiSurface = exports.getUserFlowsForBinding = exports.getPriorityForBinding = exports.getCypressSpecDirsForBinding = exports.bindFilesToFamilies = exports.loadRouteFamilyManifest = exports.buildQualityFixPrompt = exports.buildHealPrompt = exports.renderHealMarkdown = exports.resolveHealTargets = exports.healFromReport = exports.runHealStage = exports.detectHallucinatedMethods = exports.parseGenerationResponse = exports.buildGenerationPrompt = exports.runGenerationStage = exports.runPipeline = exports.captureTraceabilityInput = exports.ingestTraceabilityInput = exports.finalizeGeneratedTests = exports.readFlakyTests = exports.readCalibration = exports.appendFeedbackAndRecompute = exports.buildPlanFromImpact = exports.extractScenarios = exports.getPartialGaps = exports.getGaps = exports.analyzeImpactV2 = exports.captureTraceability = exports.ingestTraceability = exports.handoffGeneratedTests = exports.recommendTestsDeterministic = exports.analyzeImpactDeterministic = exports.validateProviderSetup = exports.LLMProviderFactory = exports.CustomProvider = exports.checkOpenAISetup = exports.OpenAIProvider = exports.checkOllamaSetup = exports.OllamaProvider = exports.checkAnthropicSetup = exports.AnthropicProvider = exports.UnsupportedCapabilityError = exports.LLMProviderError = void 0;
6
+ exports.formatValidationReport = exports.buildValidationReport = exports.validateCommit = exports.getCommitFiles = exports.enrichFamilies = exports.detectStaleFamilies = void 0;
6
7
  var provider_interface_js_1 = require("./provider_interface.js");
7
8
  Object.defineProperty(exports, "LLMProviderError", { enumerable: true, get: function () { return provider_interface_js_1.LLMProviderError; } });
8
9
  Object.defineProperty(exports, "UnsupportedCapabilityError", { enumerable: true, get: function () { return provider_interface_js_1.UnsupportedCapabilityError; } });
@@ -81,3 +82,16 @@ Object.defineProperty(exports, "getSpecsForFamily", { enumerable: true, get: fun
81
82
  // Agentic generation
82
83
  var runner_js_1 = require("./agentic/runner.js");
83
84
  Object.defineProperty(exports, "runAgenticGeneration", { enumerable: true, get: function () { return runner_js_1.runAgenticGeneration; } });
85
+ // Training (route-families bootstrap and maintenance)
86
+ var scanner_js_1 = require("./training/scanner.js");
87
+ Object.defineProperty(exports, "scanProject", { enumerable: true, get: function () { return scanner_js_1.scanProject; } });
88
+ var merger_js_1 = require("./training/merger.js");
89
+ Object.defineProperty(exports, "mergeFamilies", { enumerable: true, get: function () { return merger_js_1.mergeFamilies; } });
90
+ Object.defineProperty(exports, "detectStaleFamilies", { enumerable: true, get: function () { return merger_js_1.detectStaleFamilies; } });
91
+ var enricher_js_1 = require("./training/enricher.js");
92
+ Object.defineProperty(exports, "enrichFamilies", { enumerable: true, get: function () { return enricher_js_1.enrichFamilies; } });
93
+ var validator_js_1 = require("./training/validator.js");
94
+ Object.defineProperty(exports, "getCommitFiles", { enumerable: true, get: function () { return validator_js_1.getCommitFiles; } });
95
+ Object.defineProperty(exports, "validateCommit", { enumerable: true, get: function () { return validator_js_1.validateCommit; } });
96
+ Object.defineProperty(exports, "buildValidationReport", { enumerable: true, get: function () { return validator_js_1.buildValidationReport; } });
97
+ Object.defineProperty(exports, "formatValidationReport", { enumerable: true, get: function () { return validator_js_1.formatValidationReport; } });
@@ -39,6 +39,8 @@ export interface RouteFamilyConfig {
39
39
  manifestPath?: string;
40
40
  strict?: boolean;
41
41
  }
42
+ export declare function matchesGlob(filePath: string, pattern: string): boolean;
43
+ export declare function matchesAnyPattern(filePath: string, patterns: string[]): boolean;
42
44
  export declare function loadRouteFamilyManifest(testsRoot: string, config?: RouteFamilyConfig): RouteFamilyManifest | null;
43
45
  export declare function bindFilesToFamilies(changedFiles: string[], manifest: RouteFamilyManifest): FileBinding[];
44
46
  export declare function getFamilyById(manifest: RouteFamilyManifest, familyId: string): RouteFamily | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"route_families.d.ts","sourceRoot":"","sources":["../../src/knowledge/route_families.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route_families.d.ts","sourceRoot":"","sources":["../../src/knowledge/route_families.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,8 @@
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.matchesGlob = matchesGlob;
6
+ exports.matchesAnyPattern = matchesAnyPattern;
5
7
  exports.loadRouteFamilyManifest = loadRouteFamilyManifest;
6
8
  exports.bindFilesToFamilies = bindFilesToFamilies;
7
9
  exports.getFamilyById = getFamilyById;
@@ -0,0 +1,15 @@
1
+ import type { LLMProvider } from '../provider_interface.js';
2
+ import type { RouteFamily } from '../knowledge/route_families.js';
3
+ import type { EnrichmentResult, ScannedFamily } from './types.js';
4
+ export interface EnrichedEntry {
5
+ id: string;
6
+ priority?: 'P0' | 'P1' | 'P2';
7
+ userFlows?: string[];
8
+ routes?: string[];
9
+ pageObjects?: string[];
10
+ components?: string[];
11
+ }
12
+ export declare function validateEntries(parsed: unknown[]): EnrichedEntry[];
13
+ export declare function parseEnrichResponse(response: string): EnrichedEntry[];
14
+ export declare function enrichFamilies(families: RouteFamily[], scanned: ScannedFamily[], projectRoot: string, provider: LLMProvider, budgetUSD: number): Promise<EnrichmentResult>;
15
+ //# sourceMappingURL=enricher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enricher.d.ts","sourceRoot":"","sources":["../../src/training/enricher.ts"],"names":[],"mappings":"AAMA,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;AAmIhE,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;CACzB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,EAAE,CAiBlE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAwBrE;AA2BD,wBAAsB,cAAc,CAChC,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,EAAE,aAAa,EAAE,EACxB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,MAAM,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAgF3B"}
@@ -0,0 +1,278 @@
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.validateEntries = validateEntries;
6
+ exports.parseEnrichResponse = parseEnrichResponse;
7
+ exports.enrichFamilies = enrichFamilies;
8
+ const fs_1 = require("fs");
9
+ const path_1 = require("path");
10
+ const types_js_1 = require("./types.js");
11
+ const MAX_FILES_PER_FAMILY = 20;
12
+ const MAX_LINES_PER_FILE = 50;
13
+ const LLM_TIMEOUT_MS = 60000;
14
+ const MAX_PROMPT_CHARS = 100000;
15
+ const SENSITIVE_PATTERNS = [
16
+ /[._]env/, /secret/i, /credential/i, /\.pem$/, /\.key$/, /password/i,
17
+ /config\/secrets/, /fixtures\/.*auth/i, /\.npmrc/, /\.netrc/,
18
+ /id_rsa/, /id_ed25519/, /\.p12$/, /\.pfx$/, /tokens?\.json/i,
19
+ ];
20
+ const SKIP_DIRS = new Set([
21
+ 'node_modules', '.git', 'dist', 'build', '.next', '.nuxt', 'coverage',
22
+ ]);
23
+ function sampleFiles(dir, maxFiles) {
24
+ const files = [];
25
+ function walk(d, depth = 0, maxDepth = 10) {
26
+ if (files.length >= maxFiles)
27
+ return;
28
+ if (depth > maxDepth)
29
+ return;
30
+ try {
31
+ for (const entry of (0, fs_1.readdirSync)(d)) {
32
+ if (files.length >= maxFiles)
33
+ return;
34
+ // Skip dot-dirs and known heavy directories
35
+ if (entry.startsWith('.') || SKIP_DIRS.has(entry))
36
+ continue;
37
+ const full = (0, path_1.join)(d, entry);
38
+ try {
39
+ // Skip symlinks
40
+ const lstat = (0, fs_1.lstatSync)(full);
41
+ if (lstat.isSymbolicLink())
42
+ continue;
43
+ // Skip sensitive files (test against relative path from scan root)
44
+ const relPath = (0, path_1.relative)(dir, full);
45
+ if (SENSITIVE_PATTERNS.some((p) => p.test(relPath) || p.test(entry)))
46
+ continue;
47
+ if (lstat.isDirectory()) {
48
+ walk(full, depth + 1, maxDepth);
49
+ }
50
+ else if (lstat.isFile() && lstat.size < 50000) {
51
+ const ext = entry.slice(entry.lastIndexOf('.'));
52
+ if (['.ts', '.tsx', '.js', '.jsx', '.go', '.py', '.rs'].includes(ext)) {
53
+ const content = (0, fs_1.readFileSync)(full, 'utf-8');
54
+ const lines = content.split('\n').slice(0, MAX_LINES_PER_FILE).join('\n');
55
+ files.push({ path: full, content: lines });
56
+ }
57
+ }
58
+ }
59
+ catch { /* skip */ }
60
+ }
61
+ }
62
+ catch { /* skip */ }
63
+ }
64
+ walk(dir);
65
+ return files;
66
+ }
67
+ function buildEnrichPrompt(families, projectRoot) {
68
+ const sections = [];
69
+ for (const family of families) {
70
+ const allDirs = [
71
+ ...family.webappPaths.map((p) => p.replace(/\/?\*.*$/, '')),
72
+ ...family.serverPaths.map((p) => p.replace(/\/?\*.*$/, '')),
73
+ ];
74
+ const samples = [];
75
+ for (const dir of allDirs) {
76
+ if (!dir)
77
+ continue;
78
+ const fullDir = (0, path_1.join)((0, path_1.resolve)(projectRoot), dir);
79
+ samples.push(...sampleFiles(fullDir, MAX_FILES_PER_FAMILY - samples.length));
80
+ if (samples.length >= MAX_FILES_PER_FAMILY)
81
+ break;
82
+ }
83
+ // Sample spec descriptions
84
+ const specSamples = [];
85
+ for (const specDir of family.specDirs) {
86
+ const fullDir = (0, path_1.join)((0, path_1.resolve)(projectRoot), specDir);
87
+ const specFiles = sampleFiles(fullDir, 5);
88
+ for (const sf of specFiles) {
89
+ const matches = sf.content.match(/(?:test|it|describe)\s*\(\s*['"`]([^'"`]+)/g);
90
+ if (matches) {
91
+ specSamples.push(...matches.map((m) => m.replace(/(?:test|it|describe)\s*\(\s*['"`]/, '')));
92
+ }
93
+ }
94
+ }
95
+ sections.push(`## Family: ${family.id}
96
+ Routes (guessed): ${JSON.stringify(family.routes)}
97
+ Webapp paths: ${JSON.stringify(family.webappPaths)}
98
+ Server paths: ${JSON.stringify(family.serverPaths)}
99
+ Spec dirs: ${JSON.stringify(family.specDirs)}
100
+ Tags: ${JSON.stringify(family.tags)}
101
+ Features: ${family.features.map((f) => f.id).join(', ') || 'none'}
102
+
103
+ Sample files (${samples.length}):
104
+ ${samples.map((s) => `### ${(0, path_1.relative)(projectRoot, s.path)}\n\`\`\`\n${s.content}\n\`\`\``).join('\n')}
105
+
106
+ Test descriptions:
107
+ ${specSamples.length > 0 ? specSamples.map((d) => `- ${d}`).join('\n') : '(none found)'}
108
+ `);
109
+ }
110
+ return `You are analyzing a codebase to enrich route-family definitions for an E2E test impact analysis tool.
111
+
112
+ For each family below, provide:
113
+ 1. **priority**: P0 (critical user flow), P1 (important), or P2 (nice-to-have)
114
+ 2. **userFlows**: Array of human-readable flow names (e.g., "Create channel", "Search messages")
115
+ 3. **routes**: Improved URL patterns (e.g., "/{team}/channels/{channel}" instead of "/channels")
116
+ 4. **pageObjects**: Array of page object class names found in the code
117
+ 5. **components**: Array of UI component names relevant to this family
118
+
119
+ Respond in JSON format:
120
+ \`\`\`json
121
+ [
122
+ {
123
+ "id": "family_id",
124
+ "priority": "P0",
125
+ "userFlows": ["Flow name 1", "Flow name 2"],
126
+ "routes": ["/improved/route/{param}"],
127
+ "pageObjects": ["PageName"],
128
+ "components": ["ComponentName"]
129
+ }
130
+ ]
131
+ \`\`\`
132
+
133
+ ${sections.join('\n---\n')}`;
134
+ }
135
+ function validateEntries(parsed) {
136
+ const filterStrings = (arr, maxLen) => {
137
+ if (!Array.isArray(arr))
138
+ return undefined;
139
+ const filtered = arr.filter((v) => typeof v === 'string' && v.length < maxLen);
140
+ return filtered.length > 0 ? filtered : undefined;
141
+ };
142
+ return parsed
143
+ .filter((e) => !!e && typeof e.id === 'string')
144
+ .map((entry) => ({
145
+ id: entry.id,
146
+ priority: ['P0', 'P1', 'P2'].includes(entry.priority) ? entry.priority : undefined,
147
+ routes: filterStrings(entry.routes, 200),
148
+ userFlows: filterStrings(entry.userFlows, 500),
149
+ pageObjects: filterStrings(entry.pageObjects, 200),
150
+ components: filterStrings(entry.components, 200),
151
+ }));
152
+ }
153
+ function parseEnrichResponse(response) {
154
+ // Extract JSON from response (may be wrapped in markdown code block)
155
+ const jsonMatch = response.match(/```(?:json)?\s*([\s\S]*?)```/) || [null, response];
156
+ const jsonStr = jsonMatch[1]?.trim() || response.trim();
157
+ try {
158
+ const parsed = JSON.parse(jsonStr);
159
+ if (Array.isArray(parsed)) {
160
+ return validateEntries(parsed);
161
+ }
162
+ }
163
+ catch {
164
+ // Try to find any JSON array in the response
165
+ const arrayMatch = response.match(/\[[\s\S]*\]/);
166
+ if (arrayMatch) {
167
+ try {
168
+ const parsed = JSON.parse(arrayMatch[0]);
169
+ if (Array.isArray(parsed)) {
170
+ return validateEntries(parsed);
171
+ }
172
+ }
173
+ catch {
174
+ // give up
175
+ }
176
+ }
177
+ }
178
+ return [];
179
+ }
180
+ function applyEnrichment(family, enriched) {
181
+ const result = { ...family };
182
+ if (enriched.priority && !family.priority) {
183
+ result.priority = enriched.priority;
184
+ }
185
+ if (enriched.userFlows && (!family.userFlows || family.userFlows.length === 0)) {
186
+ result.userFlows = enriched.userFlows;
187
+ }
188
+ if (enriched.routes && enriched.routes.length > 0) {
189
+ // Only replace if current routes look like guesses
190
+ if ((0, types_js_1.isGuessedRoute)(family.routes)) {
191
+ result.routes = enriched.routes;
192
+ }
193
+ }
194
+ if (enriched.pageObjects && (!family.pageObjects || family.pageObjects.length === 0)) {
195
+ result.pageObjects = enriched.pageObjects;
196
+ }
197
+ if (enriched.components && (!family.components || family.components.length === 0)) {
198
+ result.components = enriched.components;
199
+ }
200
+ return result;
201
+ }
202
+ async function enrichFamilies(families, scanned, projectRoot, provider, budgetUSD) {
203
+ const scannedMap = new Map(scanned.map((s) => [s.id, s]));
204
+ const enriched = [];
205
+ let totalTokens = 0;
206
+ let totalCost = 0;
207
+ const skipped = [];
208
+ // Process in chunks of 4 families
209
+ const chunkSize = 4;
210
+ for (let i = 0; i < families.length; i += chunkSize) {
211
+ if (totalCost >= budgetUSD) {
212
+ for (let j = i; j < families.length; j++) {
213
+ skipped.push(families[j].id);
214
+ enriched.push(families[j]);
215
+ }
216
+ break;
217
+ }
218
+ const chunk = families.slice(i, i + chunkSize);
219
+ const scannedChunk = chunk
220
+ .map((f) => scannedMap.get(f.id))
221
+ .filter((s) => s !== undefined);
222
+ if (scannedChunk.length === 0) {
223
+ enriched.push(...chunk);
224
+ continue;
225
+ }
226
+ let prompt = buildEnrichPrompt(scannedChunk, projectRoot);
227
+ if (prompt.length > MAX_PROMPT_CHARS) {
228
+ // Truncate at the last complete section boundary to avoid malformed input
229
+ const lastSectionEnd = prompt.lastIndexOf('\n---\n', MAX_PROMPT_CHARS);
230
+ if (lastSectionEnd > 0) {
231
+ console.warn(`[train] Prompt truncated from ${prompt.length} chars at section boundary`);
232
+ prompt = prompt.slice(0, lastSectionEnd);
233
+ }
234
+ else {
235
+ console.warn(`[train] Prompt truncated from ${prompt.length} to ${MAX_PROMPT_CHARS} chars`);
236
+ prompt = prompt.slice(0, MAX_PROMPT_CHARS);
237
+ }
238
+ }
239
+ let timer;
240
+ try {
241
+ const timeoutPromise = new Promise((_, reject) => {
242
+ timer = setTimeout(() => reject(new Error('LLM request timed out')), LLM_TIMEOUT_MS);
243
+ });
244
+ const response = await Promise.race([
245
+ provider.generateText(prompt, { maxTokens: 4096, temperature: 0.3 }),
246
+ timeoutPromise,
247
+ ]);
248
+ totalTokens += (response.usage?.inputTokens ?? 0) + (response.usage?.outputTokens ?? 0);
249
+ totalCost += response.cost ?? 0;
250
+ const entries = parseEnrichResponse(response.text);
251
+ const entryMap = new Map(entries.map((e) => [e.id, e]));
252
+ for (const family of chunk) {
253
+ const entry = entryMap.get(family.id);
254
+ if (entry) {
255
+ enriched.push(applyEnrichment(family, entry));
256
+ }
257
+ else {
258
+ enriched.push(family);
259
+ }
260
+ }
261
+ }
262
+ catch (error) {
263
+ // On LLM failure, keep families unchanged
264
+ console.warn(`[train] LLM enrichment failed for chunk: ${error instanceof Error ? error.message : String(error)}`);
265
+ enriched.push(...chunk);
266
+ }
267
+ finally {
268
+ if (timer)
269
+ clearTimeout(timer);
270
+ }
271
+ }
272
+ return {
273
+ enrichedFamilies: enriched,
274
+ tokensUsed: totalTokens,
275
+ costUSD: Math.round(totalCost * 100) / 100,
276
+ skippedFamilies: skipped,
277
+ };
278
+ }
@@ -0,0 +1,5 @@
1
+ import type { RouteFamilyManifest } from '../knowledge/route_families.js';
2
+ import type { MergeResult, ScannedFamily } from './types.js';
3
+ export declare function mergeFamilies(existing: RouteFamilyManifest | null, scanned: ScannedFamily[]): MergeResult;
4
+ export declare function detectStaleFamilies(manifest: RouteFamilyManifest, projectRoot: string): string[];
5
+ //# sourceMappingURL=merger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merger.d.ts","sourceRoot":"","sources":["../../src/training/merger.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAc,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAGrF,OAAO,KAAK,EAAC,WAAW,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AAqE3D,wBAAgB,aAAa,CACzB,QAAQ,EAAE,mBAAmB,GAAG,IAAI,EACpC,OAAO,EAAE,aAAa,EAAE,GACzB,WAAW,CAyCb;AAED,wBAAgB,mBAAmB,CAC/B,QAAQ,EAAE,mBAAmB,EAC7B,WAAW,EAAE,MAAM,GACpB,MAAM,EAAE,CA8BV"}
@@ -0,0 +1,141 @@
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.mergeFamilies = mergeFamilies;
6
+ exports.detectStaleFamilies = detectStaleFamilies;
7
+ const fs_1 = require("fs");
8
+ const path_1 = require("path");
9
+ const types_js_1 = require("./types.js");
10
+ function unionArrays(existing, incoming) {
11
+ const set = new Set(existing || []);
12
+ for (const item of incoming) {
13
+ set.add(item);
14
+ }
15
+ return Array.from(set);
16
+ }
17
+ function mergeFamily(existing, scanned) {
18
+ const merged = { ...existing };
19
+ // Structural fields: union arrays
20
+ merged.webappPaths = unionArrays(existing.webappPaths, scanned.webappPaths);
21
+ merged.serverPaths = unionArrays(existing.serverPaths, scanned.serverPaths);
22
+ merged.specDirs = unionArrays(existing.specDirs, scanned.specDirs);
23
+ merged.cypressSpecDirs = unionArrays(existing.cypressSpecDirs, scanned.cypressSpecDirs);
24
+ merged.tags = unionArrays(existing.tags, scanned.tags);
25
+ // Routes: only update if existing looks like a guess
26
+ if ((0, types_js_1.isGuessedRoute)(existing.routes) && !(0, types_js_1.isGuessedRoute)(scanned.routes)) {
27
+ merged.routes = scanned.routes;
28
+ }
29
+ // Human-curated fields: never overwrite (priority, userFlows, pageObjects, components)
30
+ // Merge features
31
+ if (scanned.features.length > 0) {
32
+ const existingFeatures = existing.features || [];
33
+ const existingIds = new Set(existingFeatures.map((f) => f.id));
34
+ const newFeatures = scanned.features.filter((f) => !existingIds.has(f.id));
35
+ if (newFeatures.length > 0) {
36
+ merged.features = [
37
+ ...existingFeatures,
38
+ ...newFeatures.map((f) => ({
39
+ id: f.id,
40
+ webappPaths: f.webappPaths,
41
+ serverPaths: f.serverPaths,
42
+ specDirs: f.specDirs,
43
+ })),
44
+ ];
45
+ }
46
+ }
47
+ return merged;
48
+ }
49
+ function scannedToRouteFamily(scanned) {
50
+ const family = {
51
+ id: scanned.id,
52
+ routes: scanned.routes,
53
+ };
54
+ if (scanned.webappPaths.length > 0)
55
+ family.webappPaths = scanned.webappPaths;
56
+ if (scanned.serverPaths.length > 0)
57
+ family.serverPaths = scanned.serverPaths;
58
+ if (scanned.specDirs.length > 0)
59
+ family.specDirs = scanned.specDirs;
60
+ if (scanned.cypressSpecDirs.length > 0)
61
+ family.cypressSpecDirs = scanned.cypressSpecDirs;
62
+ if (scanned.tags.length > 0)
63
+ family.tags = scanned.tags;
64
+ if (scanned.features.length > 0) {
65
+ family.features = scanned.features.map((f) => ({
66
+ id: f.id,
67
+ webappPaths: f.webappPaths.length > 0 ? f.webappPaths : undefined,
68
+ serverPaths: f.serverPaths.length > 0 ? f.serverPaths : undefined,
69
+ specDirs: f.specDirs.length > 0 ? f.specDirs : undefined,
70
+ }));
71
+ }
72
+ return family;
73
+ }
74
+ function mergeFamilies(existing, scanned) {
75
+ const existingFamilies = existing?.families || [];
76
+ const existingMap = new Map(existingFamilies.map((f) => [f.id, f]));
77
+ const scannedMap = new Map(scanned.map((f) => [f.id, f]));
78
+ const newFamilies = [];
79
+ const updatedFamilies = [];
80
+ const mergedFamilies = [];
81
+ // Process existing families
82
+ for (const ef of existingFamilies) {
83
+ const sf = scannedMap.get(ef.id);
84
+ if (sf) {
85
+ mergedFamilies.push(mergeFamily(ef, sf));
86
+ updatedFamilies.push(ef.id);
87
+ }
88
+ else {
89
+ // Keep untouched
90
+ mergedFamilies.push({ ...ef });
91
+ }
92
+ }
93
+ // Add new families from scanner
94
+ for (const sf of scanned) {
95
+ if (!existingMap.has(sf.id)) {
96
+ mergedFamilies.push(scannedToRouteFamily(sf));
97
+ newFamilies.push(sf.id);
98
+ }
99
+ }
100
+ const parts = [];
101
+ if (updatedFamilies.length > 0)
102
+ parts.push(`${updatedFamilies.length} families updated`);
103
+ if (newFamilies.length > 0)
104
+ parts.push(`${newFamilies.length} new families added`);
105
+ if (parts.length === 0)
106
+ parts.push('no changes');
107
+ return {
108
+ manifest: { families: mergedFamilies, source: existing?.source || 'train-scan' },
109
+ newFamilies,
110
+ updatedFamilies,
111
+ staleFamilies: [],
112
+ summary: parts.join(', '),
113
+ };
114
+ }
115
+ function detectStaleFamilies(manifest, projectRoot) {
116
+ const resolved = (0, path_1.resolve)(projectRoot);
117
+ const stale = [];
118
+ for (const family of manifest.families) {
119
+ const allPatterns = [
120
+ ...(family.webappPaths || []),
121
+ ...(family.serverPaths || []),
122
+ ...(family.specDirs || []),
123
+ ];
124
+ if (allPatterns.length === 0)
125
+ continue;
126
+ // Check if any pattern resolves to existing files/dirs
127
+ let hasAny = false;
128
+ for (const pattern of allPatterns) {
129
+ // Strip trailing glob (* or **) to get the directory
130
+ const dirPart = pattern.replace(/\/?\*.*$/, '');
131
+ if (dirPart && (0, fs_1.existsSync)((0, path_1.join)(resolved, dirPart))) {
132
+ hasAny = true;
133
+ break;
134
+ }
135
+ }
136
+ if (!hasAny) {
137
+ stale.push(family.id);
138
+ }
139
+ }
140
+ return stale;
141
+ }
@@ -0,0 +1,5 @@
1
+ import type { DiscoveredDir, ScanResult } from './types.js';
2
+ export declare function discoverSourceDirs(projectRoot: string): DiscoveredDir[];
3
+ export declare function discoverTestDirs(projectRoot: string): DiscoveredDir[];
4
+ export declare function scanProject(projectRoot: string): ScanResult;
5
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/training/scanner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,aAAa,EAAiC,UAAU,EAAC,MAAM,YAAY,CAAC;AAgGzF,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CA+BvE;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CA6DrE;AA6ID,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CA+E3D"}