@yasserkhanorg/e2e-agents 1.6.0 → 1.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.
@@ -12,7 +12,20 @@ export declare function discoverTestDirs(projectRoot: string): DiscoveredDir[];
12
12
  *
13
13
  * Each domain becomes a candidate family with precise serverPaths.
14
14
  */
15
- export declare function discoverServerDerivedFamilies(serverRoot: string): ScannedFamily[];
15
+ export declare function discoverServerDerivedFamilies(serverRoot: string): {
16
+ multiTierFamilies: ScannedFamily[];
17
+ singleTierFamilies: ScannedFamily[];
18
+ };
16
19
  export declare function discoverTestDerivedFamilies(testsRoot: string): ScannedFamily[];
17
- export declare function scanProject(projectRoot: string, testsRoot?: string, serverRoot?: string): ScanResult;
20
+ /**
21
+ * Discover test library paths (page objects, helpers) organized by feature.
22
+ * Walks well-known test lib directories and maps subdirectories to family IDs.
23
+ */
24
+ export declare function discoverTestLibPaths(testsRoot: string): Map<string, string[]>;
25
+ /**
26
+ * Discover files in well-known directories (types, utils) whose basename
27
+ * maps directly to a family ID.
28
+ */
29
+ export declare function discoverNameMatchedPaths(appPath: string, gitRepoRoot?: string): Map<string, string[]>;
30
+ export declare function scanProject(projectRoot: string, testsRoot?: string, serverRoot?: string, gitRepoRoot?: string): ScanResult;
18
31
  //# sourceMappingURL=scanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/training/scanner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAkB,UAAU,EAAC,MAAM,YAAY,CAAC;AAgJzF,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CA+BvE;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CA6DrE;AAuLD;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,EAAE,CA0HjF;AAED,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE,CAiG9E;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,CA6IpG"}
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/training/scanner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAkB,UAAU,EAAC,MAAM,YAAY,CAAC;AAgJzF,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CA+BvE;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CA6DrE;AAuLD;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG;IAAC,iBAAiB,EAAE,aAAa,EAAE,CAAC;IAAC,kBAAkB,EAAE,aAAa,EAAE,CAAA;CAAC,CAgI3I;AAED,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE,CAiG9E;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAmC7E;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACpC,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACrB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAmDvB;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,CA0L1H"}
@@ -6,6 +6,8 @@ exports.discoverSourceDirs = discoverSourceDirs;
6
6
  exports.discoverTestDirs = discoverTestDirs;
7
7
  exports.discoverServerDerivedFamilies = discoverServerDerivedFamilies;
8
8
  exports.discoverTestDerivedFamilies = discoverTestDerivedFamilies;
9
+ exports.discoverTestLibPaths = discoverTestLibPaths;
10
+ exports.discoverNameMatchedPaths = discoverNameMatchedPaths;
9
11
  exports.scanProject = scanProject;
10
12
  const fs_1 = require("fs");
11
13
  const path_1 = require("path");
@@ -520,15 +522,15 @@ function discoverServerDerivedFamilies(serverRoot) {
520
522
  }
521
523
  }
522
524
  // Build families from grouped domains.
523
- // Only include server-only families that span 2 tiers (architecturally significant).
524
- const families = [];
525
+ // Multi-tier families (≥2 tiers) can be new families.
526
+ // Single-tier families can only merge into existing families.
527
+ const multiTierFamilies = [];
528
+ const singleTierFamilies = [];
525
529
  for (const [domain, paths] of familyPaths) {
526
530
  if (paths.size === 0)
527
531
  continue;
528
532
  const tierCount = familyTiers.get(domain)?.size ?? 0;
529
- if (tierCount < 2)
530
- continue; // Skip single-tier domains (likely infrastructure)
531
- families.push({
533
+ const family = {
532
534
  id: domain,
533
535
  routes: [`/${domain.replace(/_/g, '-')}`],
534
536
  webappPaths: [],
@@ -538,9 +540,15 @@ function discoverServerDerivedFamilies(serverRoot) {
538
540
  tags: [],
539
541
  features: [],
540
542
  routesGuessed: true,
541
- });
543
+ };
544
+ if (tierCount >= 2) {
545
+ multiTierFamilies.push(family);
546
+ }
547
+ else {
548
+ singleTierFamilies.push(family);
549
+ }
542
550
  }
543
- return families;
551
+ return { multiTierFamilies, singleTierFamilies };
544
552
  }
545
553
  function discoverTestDerivedFamilies(testsRoot) {
546
554
  const resolved = (0, path_1.resolve)(testsRoot);
@@ -627,7 +635,113 @@ function discoverTestDerivedFamilies(testsRoot) {
627
635
  }
628
636
  return Array.from(familyMap.values());
629
637
  }
630
- function scanProject(projectRoot, testsRoot, serverRoot) {
638
+ /**
639
+ * Discover test library paths (page objects, helpers) organized by feature.
640
+ * Walks well-known test lib directories and maps subdirectories to family IDs.
641
+ */
642
+ function discoverTestLibPaths(testsRoot) {
643
+ const resolved = (0, path_1.resolve)(testsRoot);
644
+ const result = new Map();
645
+ const libDirs = [
646
+ 'lib/src/ui/components',
647
+ 'lib/src/ui/pages',
648
+ 'lib/src/server',
649
+ ];
650
+ for (const libDir of libDirs) {
651
+ const fullDir = (0, path_1.join)(resolved, libDir);
652
+ if (!(0, fs_1.existsSync)(fullDir))
653
+ continue;
654
+ let entries;
655
+ try {
656
+ entries = (0, fs_1.readdirSync)(fullDir);
657
+ }
658
+ catch {
659
+ continue;
660
+ }
661
+ for (const entry of entries) {
662
+ if (isSkipped(entry))
663
+ continue;
664
+ const fullPath = (0, path_1.join)(fullDir, entry);
665
+ try {
666
+ const stat = (0, fs_1.lstatSync)(fullPath);
667
+ if (stat.isSymbolicLink() || !stat.isDirectory())
668
+ continue;
669
+ }
670
+ catch {
671
+ continue;
672
+ }
673
+ const familyId = normalizeId(entry);
674
+ const relPath = (0, path_1.relative)(resolved, fullPath).replace(/\\/g, '/');
675
+ const pattern = `${relPath}/*`;
676
+ if (!result.has(familyId))
677
+ result.set(familyId, []);
678
+ result.get(familyId).push(pattern);
679
+ }
680
+ }
681
+ return result;
682
+ }
683
+ /**
684
+ * Discover files in well-known directories (types, utils) whose basename
685
+ * maps directly to a family ID.
686
+ */
687
+ function discoverNameMatchedPaths(appPath, gitRepoRoot) {
688
+ const result = new Map();
689
+ const resolvedApp = (0, path_1.resolve)(appPath);
690
+ const scanRoots = [
691
+ { root: (0, path_1.join)(resolvedApp, 'src/utils'), base: resolvedApp },
692
+ { root: (0, path_1.join)(resolvedApp, 'src/types'), base: resolvedApp },
693
+ ];
694
+ // Monorepo-aware: scan platform types directory
695
+ if (gitRepoRoot) {
696
+ const resolvedGitRoot = (0, path_1.resolve)(gitRepoRoot);
697
+ const platformTypes = (0, path_1.join)(resolvedGitRoot, 'webapp/platform/types/src');
698
+ if ((0, fs_1.existsSync)(platformTypes)) {
699
+ scanRoots.push({ root: platformTypes, base: resolvedGitRoot });
700
+ }
701
+ const platformClient = (0, path_1.join)(resolvedGitRoot, 'webapp/platform/client/src');
702
+ if ((0, fs_1.existsSync)(platformClient)) {
703
+ scanRoots.push({ root: platformClient, base: resolvedGitRoot });
704
+ }
705
+ }
706
+ for (const { root, base } of scanRoots) {
707
+ if (!(0, fs_1.existsSync)(root))
708
+ continue;
709
+ let entries;
710
+ try {
711
+ entries = (0, fs_1.readdirSync)(root);
712
+ }
713
+ catch {
714
+ continue;
715
+ }
716
+ for (const entry of entries) {
717
+ if (entry.startsWith('.'))
718
+ continue;
719
+ const ext = entry.slice(entry.lastIndexOf('.'));
720
+ if (!['.ts', '.tsx', '.js', '.jsx'].includes(ext))
721
+ continue;
722
+ const fullPath = (0, path_1.join)(root, entry);
723
+ try {
724
+ const stat = (0, fs_1.lstatSync)(fullPath);
725
+ if (!stat.isFile() || stat.isSymbolicLink())
726
+ continue;
727
+ }
728
+ catch {
729
+ continue;
730
+ }
731
+ // Strip extension and normalize
732
+ const baseName = entry.slice(0, entry.lastIndexOf('.'));
733
+ const familyId = normalizeId(baseName);
734
+ if (familyId.length < 3)
735
+ continue;
736
+ const relPath = (0, path_1.relative)(base, fullPath).replace(/\\/g, '/');
737
+ if (!result.has(familyId))
738
+ result.set(familyId, []);
739
+ result.get(familyId).push(relPath);
740
+ }
741
+ }
742
+ return result;
743
+ }
744
+ function scanProject(projectRoot, testsRoot, serverRoot, gitRepoRoot) {
631
745
  const resolved = (0, path_1.resolve)(projectRoot);
632
746
  const resolvedTestsRoot = testsRoot ? (0, path_1.resolve)(testsRoot) : resolved;
633
747
  const sourceDirs = discoverSourceDirs(resolved);
@@ -690,9 +804,12 @@ function scanProject(projectRoot, testsRoot, serverRoot) {
690
804
  // When a separate serverRoot is provided, discover families from Go source
691
805
  // filenames across the three-tier backend (api4, app, store).
692
806
  if (serverRoot) {
693
- const serverFamilies = discoverServerDerivedFamilies((0, path_1.resolve)(serverRoot));
807
+ const { multiTierFamilies: serverMulti, singleTierFamilies: serverSingle } = discoverServerDerivedFamilies((0, path_1.resolve)(serverRoot));
694
808
  const existingIds = new Set(families.map((f) => f.id));
695
- for (const sf of serverFamilies) {
809
+ // Merge ALL server families (multi + single tier) into existing families,
810
+ // but only add NEW families if they span ≥2 tiers.
811
+ const allServerFamilies = [...serverMulti, ...serverSingle];
812
+ for (const sf of allServerFamilies) {
696
813
  // Try exact match, then singular/plural variants
697
814
  let target = families.find((f) => f.id === sf.id);
698
815
  if (!target && !sf.id.endsWith('s')) {
@@ -709,13 +826,53 @@ function scanProject(projectRoot, testsRoot, serverRoot) {
709
826
  }
710
827
  }
711
828
  }
712
- else {
713
- // New server-only family
829
+ else if (serverMulti.includes(sf)) {
830
+ // Only add new families if they span ≥2 tiers
714
831
  families.push(sf);
715
832
  existingIds.add(sf.id);
716
833
  }
717
834
  }
718
835
  }
836
+ // Merge test library paths (page objects, helpers) into existing families
837
+ if (testsRoot) {
838
+ const testLibPaths = discoverTestLibPaths(resolvedTestsRoot);
839
+ for (const [libFamilyId, patterns] of testLibPaths) {
840
+ let target = families.find((f) => f.id === libFamilyId);
841
+ if (!target && !libFamilyId.endsWith('s')) {
842
+ target = families.find((f) => f.id === libFamilyId + 's');
843
+ }
844
+ if (!target && libFamilyId.endsWith('s')) {
845
+ target = families.find((f) => f.id === libFamilyId.slice(0, -1));
846
+ }
847
+ if (target) {
848
+ for (const p of patterns) {
849
+ if (!target.webappPaths.includes(p)) {
850
+ target.webappPaths.push(p);
851
+ }
852
+ }
853
+ }
854
+ }
855
+ }
856
+ // Merge name-matched type/util files into existing families
857
+ {
858
+ const nameMatchedPaths = discoverNameMatchedPaths(resolved, gitRepoRoot);
859
+ for (const [nmFamilyId, paths] of nameMatchedPaths) {
860
+ let target = families.find((f) => f.id === nmFamilyId);
861
+ if (!target && !nmFamilyId.endsWith('s')) {
862
+ target = families.find((f) => f.id === nmFamilyId + 's');
863
+ }
864
+ if (!target && nmFamilyId.endsWith('s')) {
865
+ target = families.find((f) => f.id === nmFamilyId.slice(0, -1));
866
+ }
867
+ if (target) {
868
+ for (const p of paths) {
869
+ if (!target.webappPaths.includes(p)) {
870
+ target.webappPaths.push(p);
871
+ }
872
+ }
873
+ }
874
+ }
875
+ }
719
876
  const familyIds = new Set(families.map((f) => f.id));
720
877
  const unmatchedSourceDirs = sourceDirs.filter((d) => !familyIds.has(normalizeId(d.familyHint)));
721
878
  const unmatchedTestDirs = testDirs.filter((d) => !familyIds.has(normalizeId(d.familyHint)));
@@ -47,6 +47,10 @@ export interface EnrichmentResult {
47
47
  tokensUsed: number;
48
48
  costUSD: number;
49
49
  skippedFamilies: string[];
50
+ /** Number of LLM requests made */
51
+ requestCount?: number;
52
+ /** Average response time per LLM request in ms */
53
+ avgResponseMs?: number;
50
54
  }
51
55
  /** A single commit's validation result */
52
56
  export interface CommitValidation {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/training/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/training/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAE,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAErF,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC1B,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACnD,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,wDAAwD;IACxD,aAAa,EAAE,OAAO,CAAC;CAC1B;AAED,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,0CAA0C;AAC1C,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,mBAAmB,EAAE,aAAa,EAAE,CAAC;IACrC,iBAAiB,EAAE,aAAa,EAAE,CAAC;IACnC,KAAK,EAAE;QACH,gBAAgB,EAAE,MAAM,CAAC;QACzB,cAAc,EAAE,MAAM,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;KACvB,CAAC;CACL;AAED,+BAA+B;AAC/B,MAAM,WAAW,gBAAgB;IAC7B,gBAAgB,EAAE,WAAW,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,gCAAgC;AAChC,MAAM,WAAW,gBAAgB;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,EAAE,KAAK,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,eAAe,EAAE,MAAM,CAAC;KAC3B,CAAC,CAAC;CACN;AAED,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IACxB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,oCAAoC;AACpC,MAAM,WAAW,YAAY;IACzB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,MAAM,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,QAAQ,EAAE,OAAO,CAAC;IAClB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,2BAA2B;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAExD"}
@@ -1,5 +1,10 @@
1
1
  import type { RouteFamilyManifest } from '../knowledge/route_families.js';
2
2
  import type { CommitValidation, ValidationReport } from './types.js';
3
+ /**
4
+ * Check if a file path matches any infrastructure glob pattern.
5
+ * Uses simple string matching — no external glob library needed.
6
+ */
7
+ export declare function isInfraFile(filePath: string): boolean;
3
8
  export declare function parseGitLog(log: string): Array<{
4
9
  hash: string;
5
10
  message: string;
@@ -1 +1 @@
1
- {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/training/validator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAExE,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,YAAY,CAAC;AAEnE,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;AAED,wBAAgB,cAAc,CAC1B,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAChB,gBAAgB,CA6BlB;AAED,wBAAgB,qBAAqB,CACjC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,QAAQ,EAAE,mBAAmB,GAC9B,gBAAgB,CAkDlB;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAgCvE"}
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/training/validator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAExE,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,YAAY,CAAC;AAgBnE;;;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;AAED,wBAAgB,cAAc,CAC1B,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAChB,gBAAgB,CA6BlB;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"}
@@ -2,6 +2,7 @@
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.isInfraFile = isInfraFile;
5
6
  exports.parseGitLog = parseGitLog;
6
7
  exports.getCommitFiles = getCommitFiles;
7
8
  exports.validateCommit = validateCommit;
@@ -10,6 +11,62 @@ exports.formatValidationReport = formatValidationReport;
10
11
  const child_process_1 = require("child_process");
11
12
  const path_1 = require("path");
12
13
  const route_families_js_1 = require("../knowledge/route_families.js");
14
+ /**
15
+ * Glob-style patterns for infrastructure / cross-cutting files that will never
16
+ * belong to a single route family. Excluded from coverage calculations.
17
+ */
18
+ const INFRA_GLOBS = [
19
+ 'Makefile', 'go.mod', 'go.sum',
20
+ '*.lock',
21
+ '**/mocks/*', '**/storetest/*', '**/testlib/*',
22
+ '**/i18n/*',
23
+ '**/.github/*', '**/scripts/*',
24
+ '**/docker-compose*',
25
+ '**/__fixtures__/*', '**/test_templates/*',
26
+ ];
27
+ /**
28
+ * Check if a file path matches any infrastructure glob pattern.
29
+ * Uses simple string matching — no external glob library needed.
30
+ */
31
+ function isInfraFile(filePath) {
32
+ const normalized = filePath.replace(/\\/g, '/');
33
+ for (const pattern of INFRA_GLOBS) {
34
+ if (pattern.startsWith('**/')) {
35
+ // Match anywhere in the path
36
+ const suffix = pattern.slice(3);
37
+ if (suffix.endsWith('/*')) {
38
+ // Directory match: **/mocks/* → any segment named "mocks" with a child
39
+ const dirName = suffix.slice(0, -2);
40
+ if (normalized.includes(`/${dirName}/`) || normalized.startsWith(`${dirName}/`))
41
+ return true;
42
+ }
43
+ else if (suffix.endsWith('*')) {
44
+ // Prefix match: **/docker-compose* → file starting with docker-compose
45
+ const prefix = suffix.slice(0, -1);
46
+ const base = normalized.split('/').pop() || '';
47
+ if (base.startsWith(prefix))
48
+ return true;
49
+ }
50
+ else {
51
+ if (normalized.endsWith(`/${suffix}`) || normalized === suffix)
52
+ return true;
53
+ }
54
+ }
55
+ else if (pattern.startsWith('*.')) {
56
+ // Extension match: *.lock
57
+ const ext = pattern.slice(1);
58
+ if (normalized.endsWith(ext))
59
+ return true;
60
+ }
61
+ else {
62
+ // Exact basename match: Makefile, go.mod, go.sum
63
+ const base = normalized.split('/').pop() || '';
64
+ if (base === pattern)
65
+ return true;
66
+ }
67
+ }
68
+ return false;
69
+ }
13
70
  function parseGitLog(log) {
14
71
  const commits = [];
15
72
  let current = null;
@@ -56,10 +113,10 @@ function getCommitFiles(projectRoot, since) {
56
113
  return parseGitLog(log);
57
114
  }
58
115
  function validateCommit(manifest, files, hash, message) {
59
- // Filter out non-source files
116
+ // Filter out non-source files and infrastructure files
60
117
  const sourceFiles = files.filter((f) => {
61
118
  return !f.endsWith('.md') && !f.endsWith('.json') && !f.endsWith('.yml') && !f.endsWith('.yaml') &&
62
- !f.startsWith('.') && !f.includes('node_modules/');
119
+ !f.startsWith('.') && !f.includes('node_modules/') && !isInfraFile(f);
63
120
  });
64
121
  if (sourceFiles.length === 0) {
65
122
  return { hash, message, changedFiles: [], boundFiles: 0, unboundFiles: [], familiesHit: [] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yasserkhanorg/e2e-agents",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
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",