@aiready/core 0.24.23 → 0.24.26

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 (59) hide show
  1. package/dist/chunk-3GCIM6XG.mjs +904 -0
  2. package/dist/chunk-3S5WU6KX.mjs +552 -0
  3. package/dist/chunk-4OMXBYX7.mjs +167 -0
  4. package/dist/chunk-6YWGFKZG.mjs +250 -0
  5. package/dist/chunk-A3BIROBZ.mjs +902 -0
  6. package/dist/chunk-BYMQDORS.mjs +256 -0
  7. package/dist/chunk-CBZNRNEF.mjs +309 -0
  8. package/dist/chunk-ET2WRQSM.mjs +262 -0
  9. package/dist/chunk-F4FTHFHK.mjs +552 -0
  10. package/dist/chunk-G737F72Q.mjs +256 -0
  11. package/dist/chunk-GVFUAIWU.mjs +864 -0
  12. package/dist/chunk-KSEA5XDH.mjs +894 -0
  13. package/dist/chunk-LMIZRJFV.mjs +256 -0
  14. package/dist/chunk-LRPBPWBM.mjs +170 -0
  15. package/dist/chunk-MOTBXU6W.mjs +902 -0
  16. package/dist/chunk-OAH6FVVF.mjs +919 -0
  17. package/dist/chunk-OCM6HLBM.mjs +262 -0
  18. package/dist/chunk-OFBRNGKT.mjs +893 -0
  19. package/dist/chunk-P3KYGPO4.mjs +262 -0
  20. package/dist/chunk-PNWSO6XQ.mjs +250 -0
  21. package/dist/chunk-SO6UKAPR.mjs +164 -0
  22. package/dist/chunk-T2FW6AAF.mjs +552 -0
  23. package/dist/chunk-TQX77RIC.mjs +250 -0
  24. package/dist/chunk-X64EJ3ZO.mjs +314 -0
  25. package/dist/client/index.d.mts +1 -1
  26. package/dist/client/index.d.ts +1 -1
  27. package/dist/client/index.js +32 -5
  28. package/dist/client/index.mjs +1 -1
  29. package/dist/csharp-parser-3CGM6FKB.mjs +9 -0
  30. package/dist/csharp-parser-UWRUYHUH.mjs +9 -0
  31. package/dist/csharp-parser-WIAIE3DD.mjs +9 -0
  32. package/dist/go-parser-AH5QNS4O.mjs +9 -0
  33. package/dist/go-parser-CSAB23BL.mjs +9 -0
  34. package/dist/go-parser-Q3HI32B7.mjs +9 -0
  35. package/dist/index-CL_0jxiJ.d.mts +1315 -0
  36. package/dist/index-CL_0jxiJ.d.ts +1315 -0
  37. package/dist/index-ClwnZa_Y.d.mts +1333 -0
  38. package/dist/index-ClwnZa_Y.d.ts +1333 -0
  39. package/dist/index-DC0cdf0g.d.mts +1321 -0
  40. package/dist/index-DC0cdf0g.d.ts +1321 -0
  41. package/dist/index-DKqKGhcJ.d.mts +1309 -0
  42. package/dist/index-DKqKGhcJ.d.ts +1309 -0
  43. package/dist/index-DNnlhdk0.d.mts +1318 -0
  44. package/dist/index-DNnlhdk0.d.ts +1318 -0
  45. package/dist/index-De2xy_k5.d.mts +1326 -0
  46. package/dist/index-De2xy_k5.d.ts +1326 -0
  47. package/dist/index.d.mts +104 -20
  48. package/dist/index.d.ts +104 -20
  49. package/dist/index.js +581 -147
  50. package/dist/index.mjs +507 -134
  51. package/dist/java-parser-GUKWCEYS.mjs +9 -0
  52. package/dist/java-parser-XTWT5Y5I.mjs +9 -0
  53. package/dist/java-parser-YP5XWLQK.mjs +9 -0
  54. package/dist/python-parser-AOPXUEIV.mjs +8 -0
  55. package/dist/python-parser-FB55P6UA.mjs +8 -0
  56. package/dist/python-parser-WIJPSRKC.mjs +8 -0
  57. package/dist/typescript-parser-5ZWLLMWJ.mjs +7 -0
  58. package/dist/typescript-parser-TWPRLYK6.mjs +7 -0
  59. package/package.json +5 -1
package/dist/index.mjs CHANGED
@@ -52,28 +52,29 @@ import {
52
52
  getToolWeight,
53
53
  normalizeToolName,
54
54
  parseWeightString
55
- } from "./chunk-BE52N7T2.mjs";
55
+ } from "./chunk-OAH6FVVF.mjs";
56
56
  import {
57
57
  TypeScriptParser
58
- } from "./chunk-WPFXQH5F.mjs";
58
+ } from "./chunk-X64EJ3ZO.mjs";
59
59
  import {
60
60
  PythonParser
61
- } from "./chunk-QY5YG2AZ.mjs";
61
+ } from "./chunk-T2FW6AAF.mjs";
62
62
  import {
63
63
  JavaParser
64
- } from "./chunk-6T5O7OAY.mjs";
64
+ } from "./chunk-TQX77RIC.mjs";
65
65
  import {
66
66
  CSharpParser
67
- } from "./chunk-EHUK4VMH.mjs";
67
+ } from "./chunk-G737F72Q.mjs";
68
68
  import {
69
69
  GoParser
70
- } from "./chunk-EJ74KPCV.mjs";
70
+ } from "./chunk-OCM6HLBM.mjs";
71
71
  import "./chunk-3D3I5K5W.mjs";
72
72
  import {
73
+ getDirname,
73
74
  getWasmPath,
74
75
  initTreeSitter,
75
76
  setupParser
76
- } from "./chunk-2N7ISIKE.mjs";
77
+ } from "./chunk-LRPBPWBM.mjs";
77
78
  import {
78
79
  LANGUAGE_EXTENSIONS,
79
80
  Language,
@@ -334,8 +335,108 @@ var ToolRegistry = class _ToolRegistry {
334
335
  import { glob } from "glob";
335
336
  import { readFile } from "fs/promises";
336
337
  import { existsSync } from "fs";
337
- import { join, relative, dirname } from "path";
338
+ import { join, relative, dirname, resolve } from "path";
338
339
  import ignorePkg from "ignore";
340
+
341
+ // src/utils/history-git.ts
342
+ import { execSync } from "child_process";
343
+ function getFileCommitTimestamps(file) {
344
+ const lineStamps = {};
345
+ try {
346
+ const output = execSync(`git blame -t "${file}"`, {
347
+ encoding: "utf-8",
348
+ stdio: ["ignore", "pipe", "ignore"]
349
+ });
350
+ const lines = output.split("\n");
351
+ for (const line of lines) {
352
+ if (!line) continue;
353
+ const match = line.match(/^\S+\s+\(.*?(\d{10,})\s+[-+]\d+\s+(\d+)\)/);
354
+ if (match) {
355
+ const ts = parseInt(match[1], 10);
356
+ const ln = parseInt(match[2], 10);
357
+ lineStamps[ln] = ts;
358
+ }
359
+ }
360
+ } catch {
361
+ }
362
+ return lineStamps;
363
+ }
364
+ function getLineRangeLastModifiedCached(lineStamps, startLine, endLine) {
365
+ let latest = 0;
366
+ for (let i = startLine; i <= endLine; i++) {
367
+ if (lineStamps[i] && lineStamps[i] > latest) {
368
+ latest = lineStamps[i];
369
+ }
370
+ }
371
+ return latest;
372
+ }
373
+ function getRepoMetadata(directory) {
374
+ const metadata = {};
375
+ try {
376
+ try {
377
+ metadata.url = execSync("git config --get remote.origin.url", {
378
+ cwd: directory,
379
+ encoding: "utf-8",
380
+ stdio: ["ignore", "pipe", "ignore"]
381
+ }).trim();
382
+ } catch {
383
+ }
384
+ try {
385
+ metadata.branch = execSync("git rev-parse --abbrev-ref HEAD", {
386
+ cwd: directory,
387
+ encoding: "utf-8",
388
+ stdio: ["ignore", "pipe", "ignore"]
389
+ }).trim();
390
+ } catch {
391
+ }
392
+ try {
393
+ metadata.commit = execSync("git rev-parse HEAD", {
394
+ cwd: directory,
395
+ encoding: "utf-8",
396
+ stdio: ["ignore", "pipe", "ignore"]
397
+ }).trim();
398
+ } catch {
399
+ }
400
+ try {
401
+ metadata.author = execSync("git log -1 --format=%ae", {
402
+ cwd: directory,
403
+ encoding: "utf-8",
404
+ stdio: ["ignore", "pipe", "ignore"]
405
+ }).trim();
406
+ } catch {
407
+ }
408
+ } catch {
409
+ }
410
+ return metadata;
411
+ }
412
+ function getChangedFiles(directory) {
413
+ const changedFiles = /* @__PURE__ */ new Set();
414
+ try {
415
+ const trackedOutput = execSync("git diff HEAD --name-only", {
416
+ cwd: directory,
417
+ encoding: "utf-8",
418
+ stdio: ["ignore", "pipe", "ignore"]
419
+ }).trim();
420
+ if (trackedOutput) {
421
+ trackedOutput.split("\n").forEach((f) => changedFiles.add(f));
422
+ }
423
+ const untrackedOutput = execSync(
424
+ "git ls-files --others --exclude-standard",
425
+ {
426
+ cwd: directory,
427
+ encoding: "utf-8",
428
+ stdio: ["ignore", "pipe", "ignore"]
429
+ }
430
+ ).trim();
431
+ if (untrackedOutput) {
432
+ untrackedOutput.split("\n").forEach((f) => changedFiles.add(f));
433
+ }
434
+ } catch {
435
+ }
436
+ return Array.from(changedFiles);
437
+ }
438
+
439
+ // src/utils/file-scanner.ts
339
440
  var DEFAULT_EXCLUDE = [
340
441
  // Dependencies
341
442
  "**/node_modules/**",
@@ -407,12 +508,12 @@ var VAGUE_FILE_NAMES = /* @__PURE__ */ new Set([
407
508
  ]);
408
509
  async function scanFiles(options) {
409
510
  const {
410
- rootDir,
511
+ rootDir = ".",
411
512
  include = ["**/*.{ts,tsx,js,jsx,py,java,go,rs,cs}"],
412
513
  // Multi-language support
413
514
  exclude
414
515
  } = options;
415
- const ignoreFilePath = join(rootDir || ".", ".aireadyignore");
516
+ const ignoreFilePath = join(rootDir, ".aireadyignore");
416
517
  let ignoreFromFile = [];
417
518
  if (existsSync(ignoreFilePath)) {
418
519
  try {
@@ -445,13 +546,14 @@ async function scanFiles(options) {
445
546
  // Minimal ignore for gitignore discovery
446
547
  absolute: true
447
548
  });
549
+ let filtered = files;
448
550
  if (gitignoreFiles.length > 0) {
449
551
  try {
450
552
  const ig = ignorePkg();
451
553
  for (const gitignorePath of gitignoreFiles) {
452
554
  const gitTxt = await readFile(gitignorePath, "utf-8");
453
555
  const gitignoreDir = dirname(gitignorePath);
454
- const relativePrefix = relative(rootDir || ".", gitignoreDir).replace(
556
+ const relativePrefix = relative(rootDir, gitignoreDir).replace(
455
557
  /\\/g,
456
558
  "/"
457
559
  );
@@ -466,22 +568,26 @@ async function scanFiles(options) {
466
568
  );
467
569
  }
468
570
  }
469
- const filtered = files.filter((f) => {
470
- let rel = relative(rootDir || ".", f).replace(/\\/g, "/");
571
+ filtered = files.filter((f) => {
572
+ let rel = relative(rootDir, f).replace(/\\/g, "/");
471
573
  if (rel === "") rel = f;
472
574
  return !ig.ignores(rel);
473
575
  });
474
- return filtered;
475
576
  } catch {
476
- return files;
477
577
  }
478
578
  }
479
- return files;
579
+ if (options.changedFilesOnly) {
580
+ const changedFiles = getChangedFiles(rootDir).map(
581
+ (f) => resolve(rootDir, f)
582
+ );
583
+ return filtered.filter((f) => changedFiles.includes(f));
584
+ }
585
+ return filtered;
480
586
  }
481
587
  async function scanEntries(options) {
482
588
  const files = await scanFiles(options);
483
- const { rootDir, exclude, includeTests } = options;
484
- const ignoreFilePath = join(rootDir || ".", ".aireadyignore");
589
+ const { rootDir = ".", exclude, includeTests } = options;
590
+ const ignoreFilePath = join(rootDir, ".aireadyignore");
485
591
  let ignoreFromFile = [];
486
592
  if (existsSync(ignoreFilePath)) {
487
593
  try {
@@ -517,7 +623,7 @@ async function scanEntries(options) {
517
623
  for (const gitignorePath of gitignoreFiles) {
518
624
  const gitTxt = await readFile(gitignorePath, "utf-8");
519
625
  const gitignoreDir = dirname(gitignorePath);
520
- const relativePrefix = relative(rootDir || ".", gitignoreDir).replace(
626
+ const relativePrefix = relative(rootDir, gitignoreDir).replace(
521
627
  /\\/g,
522
628
  "/"
523
629
  );
@@ -533,7 +639,7 @@ async function scanEntries(options) {
533
639
  }
534
640
  }
535
641
  const filteredDirs = dirs.filter((d) => {
536
- let rel = relative(rootDir || ".", d).replace(/\\/g, "/");
642
+ let rel = relative(rootDir, d).replace(/\\/g, "/");
537
643
  if (rel === "") return true;
538
644
  if (!rel.endsWith("/")) rel += "/";
539
645
  return !ig.ignores(rel);
@@ -595,13 +701,20 @@ function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()
595
701
  ensureDir(outputPath);
596
702
  return outputPath;
597
703
  }
704
+ function safeJsonStringify(obj, indent = 2) {
705
+ return JSON.stringify(
706
+ obj,
707
+ (_, v) => typeof v === "bigint" ? v.toString() : v,
708
+ indent
709
+ );
710
+ }
598
711
  function handleJSONOutput(data, outputFile, successMessage) {
599
712
  if (outputFile) {
600
713
  ensureDir(outputFile);
601
- writeFileSync(outputFile, JSON.stringify(data, null, 2));
714
+ writeFileSync(outputFile, safeJsonStringify(data, 2));
602
715
  console.log(successMessage || `\u2705 Results saved to ${outputFile}`);
603
716
  } else {
604
- console.log(JSON.stringify(data, null, 2));
717
+ console.log(safeJsonStringify(data, 2));
605
718
  }
606
719
  }
607
720
  function findLatestReport(dirPath) {
@@ -792,7 +905,7 @@ function handleCLIError(error, commandName) {
792
905
 
793
906
  // src/utils/cli-helpers.ts
794
907
  async function loadMergedConfig(directory, defaults, cliOptions) {
795
- const config = await loadConfig(directory);
908
+ const { config } = await loadConfig(directory);
796
909
  const mergedConfig = mergeConfigWithDefaults(config, defaults);
797
910
  const result = {
798
911
  ...mergedConfig,
@@ -954,27 +1067,27 @@ var ParserFactory = class _ParserFactory {
954
1067
  Object.entries(LANGUAGE_EXTENSIONS).map(([ext, lang]) => [ext, lang])
955
1068
  );
956
1069
  this.registerLazyParser("typescript" /* TypeScript */, async () => {
957
- const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-WALISXF4.mjs");
1070
+ const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-5ZWLLMWJ.mjs");
958
1071
  return new TypeScriptParser2();
959
1072
  });
960
1073
  this.registerLazyParser("javascript" /* JavaScript */, async () => {
961
- const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-WALISXF4.mjs");
1074
+ const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-5ZWLLMWJ.mjs");
962
1075
  return new TypeScriptParser2();
963
1076
  });
964
1077
  this.registerLazyParser("python" /* Python */, async () => {
965
- const { PythonParser: PythonParser2 } = await import("./python-parser-7NCR7VCQ.mjs");
1078
+ const { PythonParser: PythonParser2 } = await import("./python-parser-WIJPSRKC.mjs");
966
1079
  return new PythonParser2();
967
1080
  });
968
1081
  this.registerLazyParser("java" /* Java */, async () => {
969
- const { JavaParser: JavaParser2 } = await import("./java-parser-WGOXKULP.mjs");
1082
+ const { JavaParser: JavaParser2 } = await import("./java-parser-YP5XWLQK.mjs");
970
1083
  return new JavaParser2();
971
1084
  });
972
1085
  this.registerLazyParser("csharp" /* CSharp */, async () => {
973
- const { CSharpParser: CSharpParser2 } = await import("./csharp-parser-JE5MWHQS.mjs");
1086
+ const { CSharpParser: CSharpParser2 } = await import("./csharp-parser-WIAIE3DD.mjs");
974
1087
  return new CSharpParser2();
975
1088
  });
976
1089
  this.registerLazyParser("go" /* Go */, async () => {
977
- const { GoParser: GoParser2 } = await import("./go-parser-T7PR6WJI.mjs");
1090
+ const { GoParser: GoParser2 } = await import("./go-parser-Q3HI32B7.mjs");
978
1091
  return new GoParser2();
979
1092
  });
980
1093
  }
@@ -1323,8 +1436,7 @@ function estimateTokens(text) {
1323
1436
 
1324
1437
  // src/utils/config.ts
1325
1438
  import { readFileSync, existsSync as existsSync3 } from "fs";
1326
- import { join as join3, resolve, dirname as dirname3 } from "path";
1327
- import { pathToFileURL } from "url";
1439
+ import { join as join3, resolve as resolve2, dirname as dirname3 } from "path";
1328
1440
  var CONFIG_FILES = [
1329
1441
  "aiready.json",
1330
1442
  "aiready.config.json",
@@ -1333,8 +1445,160 @@ var CONFIG_FILES = [
1333
1445
  "aiready.config.js",
1334
1446
  ".aireadyrc.js"
1335
1447
  ];
1336
- async function loadConfig(rootDir) {
1337
- let currentDir = resolve(rootDir);
1448
+ var DEFAULT_AUTO_EXCLUDE_PATTERNS = {
1449
+ tests: [
1450
+ "**/*.test.ts",
1451
+ "**/*.test.tsx",
1452
+ "**/*.test.js",
1453
+ "**/*.test.jsx",
1454
+ "**/*.spec.ts",
1455
+ "**/*.spec.tsx",
1456
+ "**/*.spec.js",
1457
+ "**/*.spec.jsx",
1458
+ "**/__tests__/**",
1459
+ "**/tests/**"
1460
+ ],
1461
+ mocks: [
1462
+ "**/__mocks__/**",
1463
+ "**/*.mock.ts",
1464
+ "**/*.mock.tsx",
1465
+ "**/*.mock.js",
1466
+ "**/*.mock.jsx"
1467
+ ],
1468
+ barrels: ["**/index.ts", "**/index.js"],
1469
+ generated: [
1470
+ "**/.next/**",
1471
+ "**/.sst/**",
1472
+ "**/.cache/**",
1473
+ "**/dist/**",
1474
+ "**/build/**"
1475
+ ]
1476
+ };
1477
+ function deepMerge(base, override) {
1478
+ const result = { ...base };
1479
+ for (const key of Object.keys(override)) {
1480
+ const baseVal = base[key];
1481
+ const overrideVal = override[key];
1482
+ if (baseVal && overrideVal && typeof baseVal === "object" && typeof overrideVal === "object" && !Array.isArray(baseVal) && !Array.isArray(overrideVal)) {
1483
+ result[key] = deepMerge(baseVal, overrideVal);
1484
+ } else if (overrideVal !== void 0) {
1485
+ result[key] = overrideVal;
1486
+ }
1487
+ }
1488
+ return result;
1489
+ }
1490
+ function checkPatternWarnings(config, configPath) {
1491
+ const warnings = [];
1492
+ const excludeArray = Array.isArray(config.exclude) ? config.exclude : config.exclude ? Object.values(config.exclude).flat() : [];
1493
+ const seen = /* @__PURE__ */ new Set();
1494
+ for (const pattern of excludeArray) {
1495
+ if (seen.has(pattern)) {
1496
+ warnings.push({
1497
+ rule: "duplicate-exclude",
1498
+ message: `Duplicate pattern '${pattern}' in exclude array`
1499
+ });
1500
+ }
1501
+ seen.add(pattern);
1502
+ if (pattern.match(/^\*\*\/[^/]+\.[^/]+$/)) {
1503
+ warnings.push({
1504
+ rule: "single-file-glob",
1505
+ message: `Single-file glob '${pattern}' - use without ** prefix`,
1506
+ suggestion: pattern.replace(/^\*\*\//, "")
1507
+ });
1508
+ }
1509
+ const normalized = pattern.replace(/^\*\*\//, "").replace(/^\*\//, "");
1510
+ for (const other of seen) {
1511
+ if (other === pattern) continue;
1512
+ const otherNormalized = other.replace(/^\*\*\//, "").replace(/^\*\//, "");
1513
+ if (normalized === otherNormalized) {
1514
+ warnings.push({
1515
+ rule: "overlapping-pattern",
1516
+ message: `Patterns '${pattern}' and '${other}' likely match the same files`
1517
+ });
1518
+ }
1519
+ }
1520
+ }
1521
+ if (config.extends) {
1522
+ warnings.push({
1523
+ rule: "config-inheritance",
1524
+ message: `Config extends '${config.extends}' - ensure base config exists`
1525
+ });
1526
+ }
1527
+ return warnings;
1528
+ }
1529
+ function resolveConfigPath(extendsPath, baseConfigPath) {
1530
+ const baseDir = dirname3(baseConfigPath);
1531
+ return resolve2(baseDir, extendsPath);
1532
+ }
1533
+ async function loadConfigWithInheritance(configPath, alreadyLoaded = /* @__PURE__ */ new Set()) {
1534
+ const resolvedPath = resolve2(configPath);
1535
+ if (alreadyLoaded.has(resolvedPath)) {
1536
+ throw new Error(
1537
+ `Circular config inheritance detected: ${Array.from(alreadyLoaded).join(" -> ")} -> ${resolvedPath}`
1538
+ );
1539
+ }
1540
+ alreadyLoaded.add(resolvedPath);
1541
+ const content = readFileSync(resolvedPath, "utf-8");
1542
+ let rawConfig = JSON.parse(content);
1543
+ const warnings = [];
1544
+ const legacyKeys = ["toolConfigs", "scanConfig", "aiReady"];
1545
+ const foundLegacy = legacyKeys.filter((key) => key in rawConfig);
1546
+ if (foundLegacy.length > 0) {
1547
+ console.warn(
1548
+ `\u26A0\uFE0F Legacy configuration keys found: ${foundLegacy.join(", ")}. Please migrate to the new schema.`
1549
+ );
1550
+ }
1551
+ if (rawConfig.extends) {
1552
+ const baseConfigPath = resolveConfigPath(rawConfig.extends, resolvedPath);
1553
+ if (!existsSync3(baseConfigPath)) {
1554
+ throw new Error(
1555
+ `Base config not found: ${rawConfig.extends} (resolved to ${baseConfigPath})`
1556
+ );
1557
+ }
1558
+ const baseResult = await loadConfigWithInheritance(
1559
+ baseConfigPath,
1560
+ alreadyLoaded
1561
+ );
1562
+ rawConfig = deepMerge(baseResult.config, rawConfig);
1563
+ warnings.push(...baseResult.warnings);
1564
+ }
1565
+ warnings.push(...checkPatternWarnings(rawConfig, resolvedPath));
1566
+ const config = AIReadyConfigSchema.parse(rawConfig);
1567
+ return { config, warnings };
1568
+ }
1569
+ function applyAutoExclusions(config, projectRoot) {
1570
+ const autoExclude = config.autoExclude ?? {
1571
+ tests: true,
1572
+ mocks: true,
1573
+ barrels: false,
1574
+ generated: true
1575
+ };
1576
+ if (!autoExclude.tests && !autoExclude.mocks && !autoExclude.barrels && !autoExclude.generated) {
1577
+ return config;
1578
+ }
1579
+ const patterns = [];
1580
+ if (autoExclude.tests) {
1581
+ patterns.push(...DEFAULT_AUTO_EXCLUDE_PATTERNS.tests);
1582
+ }
1583
+ if (autoExclude.mocks) {
1584
+ patterns.push(...DEFAULT_AUTO_EXCLUDE_PATTERNS.mocks);
1585
+ }
1586
+ if (autoExclude.barrels) {
1587
+ patterns.push(...DEFAULT_AUTO_EXCLUDE_PATTERNS.barrels);
1588
+ }
1589
+ if (autoExclude.generated) {
1590
+ patterns.push(...DEFAULT_AUTO_EXCLUDE_PATTERNS.generated);
1591
+ }
1592
+ const existingExclude = config.exclude ?? [];
1593
+ const existingArray = Array.isArray(existingExclude) ? existingExclude : existingExclude?.global ?? [];
1594
+ return {
1595
+ ...config,
1596
+ exclude: [...existingArray, ...patterns]
1597
+ };
1598
+ }
1599
+ async function loadConfig(rootDir, options) {
1600
+ const warnings = [];
1601
+ let currentDir = resolve2(rootDir);
1338
1602
  while (true) {
1339
1603
  const foundConfigs = [];
1340
1604
  for (const configFile of CONFIG_FILES) {
@@ -1349,39 +1613,17 @@ async function loadConfig(rootDir) {
1349
1613
  ", "
1350
1614
  )}. Using ${foundConfigs[0]}.`
1351
1615
  );
1352
- } else {
1353
1616
  }
1354
1617
  const configFile = foundConfigs[0];
1355
1618
  const configPath = join3(currentDir, configFile);
1356
1619
  try {
1357
- let config;
1358
- if (configFile.endsWith(".js")) {
1359
- const fileUrl = pathToFileURL(configPath).href;
1360
- const module = await import(`${fileUrl}?t=${Date.now()}`);
1361
- config = module.default || module;
1362
- } else {
1363
- const content = readFileSync(configPath, "utf-8");
1364
- config = JSON.parse(content);
1620
+ const result = await loadConfigWithInheritance(configPath);
1621
+ warnings.push(...result.warnings);
1622
+ let config = result.config;
1623
+ if (options?.applyAutoExclude !== false) {
1624
+ config = applyAutoExclusions(config, currentDir);
1365
1625
  }
1366
- const legacyKeys = ["toolConfigs"];
1367
- const rootLevelTools = [
1368
- "pattern-detect",
1369
- "context-analyzer",
1370
- "naming-consistency",
1371
- "ai-signal-clarity"
1372
- ];
1373
- const allKeys = Object.keys(config);
1374
- const foundLegacy = allKeys.filter(
1375
- (k) => legacyKeys.includes(k) || rootLevelTools.includes(k)
1376
- );
1377
- if (foundLegacy.length > 0) {
1378
- console.warn(
1379
- `\u26A0\uFE0F Legacy configuration keys found: ${foundLegacy.join(
1380
- ", "
1381
- )}. These are deprecated and should be moved under the "tools" key.`
1382
- );
1383
- }
1384
- return AIReadyConfigSchema.parse(config);
1626
+ return { config, warnings };
1385
1627
  } catch (error) {
1386
1628
  const errorMessage = error instanceof Error ? error.message : String(error);
1387
1629
  const configError = new Error(
@@ -1400,7 +1642,21 @@ async function loadConfig(rootDir) {
1400
1642
  }
1401
1643
  currentDir = parent;
1402
1644
  }
1403
- return null;
1645
+ return { config: null, warnings };
1646
+ }
1647
+ function validateConfig(configPath) {
1648
+ try {
1649
+ const content = readFileSync(configPath, "utf-8");
1650
+ const config = JSON.parse(content);
1651
+ return checkPatternWarnings(config, configPath);
1652
+ } catch (error) {
1653
+ return [
1654
+ {
1655
+ rule: "parse-error",
1656
+ message: `Failed to parse config: ${error instanceof Error ? error.message : String(error)}`
1657
+ }
1658
+ ];
1659
+ }
1404
1660
  }
1405
1661
  function mergeConfigWithDefaults(userConfig, defaults) {
1406
1662
  if (!userConfig) return defaults;
@@ -3644,78 +3900,6 @@ function clearHistory(rootDir) {
3644
3900
  }
3645
3901
  }
3646
3902
 
3647
- // src/utils/history-git.ts
3648
- import { execSync } from "child_process";
3649
- function getFileCommitTimestamps(file) {
3650
- const lineStamps = {};
3651
- try {
3652
- const output = execSync(`git blame -t "${file}"`, {
3653
- encoding: "utf-8",
3654
- stdio: ["ignore", "pipe", "ignore"]
3655
- });
3656
- const lines = output.split("\n");
3657
- for (const line of lines) {
3658
- if (!line) continue;
3659
- const match = line.match(/^\S+\s+\(.*?(\d{10,})\s+[-+]\d+\s+(\d+)\)/);
3660
- if (match) {
3661
- const ts = parseInt(match[1], 10);
3662
- const ln = parseInt(match[2], 10);
3663
- lineStamps[ln] = ts;
3664
- }
3665
- }
3666
- } catch {
3667
- }
3668
- return lineStamps;
3669
- }
3670
- function getLineRangeLastModifiedCached(lineStamps, startLine, endLine) {
3671
- let latest = 0;
3672
- for (let i = startLine; i <= endLine; i++) {
3673
- if (lineStamps[i] && lineStamps[i] > latest) {
3674
- latest = lineStamps[i];
3675
- }
3676
- }
3677
- return latest;
3678
- }
3679
- function getRepoMetadata(directory) {
3680
- const metadata = {};
3681
- try {
3682
- try {
3683
- metadata.url = execSync("git config --get remote.origin.url", {
3684
- cwd: directory,
3685
- encoding: "utf-8",
3686
- stdio: ["ignore", "pipe", "ignore"]
3687
- }).trim();
3688
- } catch {
3689
- }
3690
- try {
3691
- metadata.branch = execSync("git rev-parse --abbrev-ref HEAD", {
3692
- cwd: directory,
3693
- encoding: "utf-8",
3694
- stdio: ["ignore", "pipe", "ignore"]
3695
- }).trim();
3696
- } catch {
3697
- }
3698
- try {
3699
- metadata.commit = execSync("git rev-parse HEAD", {
3700
- cwd: directory,
3701
- encoding: "utf-8",
3702
- stdio: ["ignore", "pipe", "ignore"]
3703
- }).trim();
3704
- } catch {
3705
- }
3706
- try {
3707
- metadata.author = execSync("git log -1 --format=%ae", {
3708
- cwd: directory,
3709
- encoding: "utf-8",
3710
- stdio: ["ignore", "pipe", "ignore"]
3711
- }).trim();
3712
- } catch {
3713
- }
3714
- } catch {
3715
- }
3716
- return metadata;
3717
- }
3718
-
3719
3903
  // src/utils/github-utils.ts
3720
3904
  function emitAnnotation(params) {
3721
3905
  const { level, file, line, col, title, message } = params;
@@ -3927,6 +4111,188 @@ async function withErrorHandling(operation, context) {
3927
4111
  return { success: false, error: `${contextPrefix}${message}` };
3928
4112
  }
3929
4113
  }
4114
+
4115
+ // src/growth/agents.ts
4116
+ import { execSync as execSync2 } from "child_process";
4117
+ var GitHubIssueResolverAgent = class {
4118
+ constructor(options) {
4119
+ this.generate = options.generate;
4120
+ if (!options.trustedAuthors || options.trustedAuthors.length === 0) {
4121
+ throw new Error(
4122
+ "[GitHubIssueResolverAgent] Security Error: trustedAuthors must be explicitly configured."
4123
+ );
4124
+ }
4125
+ this.trustedAuthors = options.trustedAuthors;
4126
+ }
4127
+ async resolve(issue, workingDir) {
4128
+ console.log(
4129
+ `[GitHubIssueResolverAgent] Resolving issue #${issue.number}: ${issue.title}`
4130
+ );
4131
+ const strategy = await this.identifyStrategy(issue);
4132
+ console.log(`[GitHubIssueResolverAgent] Selected Strategy: ${strategy}`);
4133
+ try {
4134
+ switch (strategy) {
4135
+ case "CORE_EVOLUTION_SYNC":
4136
+ if (issue.author && !this.trustedAuthors.includes(issue.author)) {
4137
+ console.warn(
4138
+ `[GitHubIssueResolverAgent] Unauthorized evolution attempt by ${issue.author}. Blocking.`
4139
+ );
4140
+ return {
4141
+ success: false,
4142
+ message: `Unauthorized actor: ${issue.author} is not in trustedAuthors list.`
4143
+ };
4144
+ }
4145
+ return await this.executeSubtreeSync(issue, workingDir);
4146
+ case "EVOLUTION_CONTRIBUTION":
4147
+ return await this.applyContributionPattern(issue, workingDir);
4148
+ case "BUG_FIX":
4149
+ return await this.applyAgenticPatch(issue, workingDir);
4150
+ default:
4151
+ return { success: false, message: `Unknown strategy: ${strategy}` };
4152
+ }
4153
+ } catch (error) {
4154
+ console.error(
4155
+ `[GitHubIssueResolverAgent] Resolution failed for issue #${issue.number}:`,
4156
+ error.message
4157
+ );
4158
+ return {
4159
+ success: false,
4160
+ message: `Resolution failed: ${error.message}`
4161
+ };
4162
+ }
4163
+ }
4164
+ async identifyStrategy(issue) {
4165
+ if (issue.labels.includes("evolution-sync")) return "CORE_EVOLUTION_SYNC";
4166
+ if (issue.labels.includes("evolution-contribution"))
4167
+ return "EVOLUTION_CONTRIBUTION";
4168
+ if (issue.labels.includes("bug")) return "BUG_FIX";
4169
+ const prompt = `
4170
+ Analyze the GitHub Issue:
4171
+ Title: ${issue.title}
4172
+ Labels: ${issue.labels.join(", ")}
4173
+
4174
+ Categorize into: CORE_EVOLUTION_SYNC, EVOLUTION_CONTRIBUTION, BUG_FIX, or UNKNOWN.
4175
+ Only return the category name.
4176
+ `;
4177
+ return (await this.generate(prompt)).trim();
4178
+ }
4179
+ async executeSubtreeSync(issue, workingDir) {
4180
+ const hubVersion = this.extractVersion(issue.body);
4181
+ const prefix = "core/";
4182
+ const hubUrl = "https://github.com/serverlessclaw/serverlessclaw.git";
4183
+ console.log(
4184
+ `[GitHubIssueResolverAgent] Performing Subtree Pull (Hub v${hubVersion}) into ${workingDir}...`
4185
+ );
4186
+ try {
4187
+ try {
4188
+ execSync2(`git remote add hub ${hubUrl}`, {
4189
+ cwd: workingDir,
4190
+ stdio: "ignore"
4191
+ });
4192
+ } catch (e) {
4193
+ if (!e.message.includes("already exists")) {
4194
+ console.warn(
4195
+ `[GitHubIssueResolverAgent] Remote add warning: ${e.message}`
4196
+ );
4197
+ }
4198
+ }
4199
+ execSync2(`git fetch hub`, { cwd: workingDir, stdio: "pipe" });
4200
+ console.log(
4201
+ `[GitHubIssueResolverAgent] Executing subtree pull for v${hubVersion}...`
4202
+ );
4203
+ execSync2(
4204
+ `git subtree pull --prefix=${prefix} hub ${hubVersion} --squash -m "chore(sync): evolution upgrade to ${hubVersion}"`,
4205
+ {
4206
+ cwd: workingDir,
4207
+ env: { ...process.env, GIT_MERGE_AUTOEDIT: "no" }
4208
+ }
4209
+ );
4210
+ return {
4211
+ success: true,
4212
+ message: `Successfully sync'd Hub ${hubVersion} via subtree merge`
4213
+ };
4214
+ } catch (error) {
4215
+ console.error(
4216
+ `[GitHubIssueResolverAgent] Subtree sync failed: ${error.message}`
4217
+ );
4218
+ throw new Error(`Subtree sync failed: ${error.message}`, {
4219
+ cause: error
4220
+ });
4221
+ }
4222
+ }
4223
+ async applyContributionPattern(issue, workingDir) {
4224
+ console.log(
4225
+ `[GitHubIssueResolverAgent] Pattern absorption requested: ${issue.title}`
4226
+ );
4227
+ return {
4228
+ success: true,
4229
+ message: `Contribution strategy identified. Pattern "${issue.title}" is ready for human screening and promotion.`
4230
+ };
4231
+ }
4232
+ async applyAgenticPatch(issue, workingDir) {
4233
+ console.log(`[GitHubIssueResolverAgent] Bug fix requested: ${issue.title}`);
4234
+ return {
4235
+ success: true,
4236
+ message: `Patch strategy identified. Agentic fix for "${issue.title}" is being drafted.`
4237
+ };
4238
+ }
4239
+ extractVersion(body) {
4240
+ const match = body.match(/v\d+\.\d+\.\d+/);
4241
+ if (match) return match[0];
4242
+ const branchMatch = body.match(/branch:?\s*([a-zA-Z0-9.\-_/]+)/i);
4243
+ if (branchMatch) return branchMatch[1];
4244
+ return "main";
4245
+ }
4246
+ };
4247
+
4248
+ // src/services/github-service.ts
4249
+ import { Octokit } from "@octokit/rest";
4250
+ var GitHubService = class {
4251
+ constructor(token) {
4252
+ this.octokit = new Octokit({ auth: token });
4253
+ }
4254
+ /**
4255
+ * Creates an issue in a repository.
4256
+ */
4257
+ async createHubIssue(owner, repo, issue) {
4258
+ const { data } = await this.octokit.issues.create({
4259
+ owner,
4260
+ repo,
4261
+ title: issue.title,
4262
+ body: issue.body,
4263
+ labels: issue.labels || ["innovation-harvest"]
4264
+ });
4265
+ return data.number;
4266
+ }
4267
+ /**
4268
+ * Creates a Pull Request in a repository.
4269
+ */
4270
+ async createPullRequest(owner, repo, pr) {
4271
+ const { data } = await this.octokit.pulls.create({
4272
+ owner,
4273
+ repo,
4274
+ title: pr.title,
4275
+ body: pr.body,
4276
+ head: pr.head,
4277
+ base: pr.base
4278
+ });
4279
+ return data.number;
4280
+ }
4281
+ /**
4282
+ * Scans a repository content.
4283
+ */
4284
+ async listRecentChangesInRepo(owner, repo, path = "") {
4285
+ const { data } = await this.octokit.repos.getContent({
4286
+ owner,
4287
+ repo,
4288
+ path
4289
+ });
4290
+ if (Array.isArray(data)) {
4291
+ return data.map((file) => file.path);
4292
+ }
4293
+ return [];
4294
+ }
4295
+ };
3930
4296
  export {
3931
4297
  AIReadyConfigSchema,
3932
4298
  AWS_CONSTANTS,
@@ -3936,6 +4302,7 @@ export {
3936
4302
  COMMON_FINE_TUNING_OPTIONS,
3937
4303
  CONTEXT_TIER_THRESHOLDS,
3938
4304
  CSharpParser,
4305
+ DEFAULT_AUTO_EXCLUDE_PATTERNS,
3939
4306
  DEFAULT_COST_CONFIG,
3940
4307
  DEFAULT_EXCLUDE,
3941
4308
  DEFAULT_TOOL_WEIGHTS,
@@ -3944,6 +4311,8 @@ export {
3944
4311
  FRIENDLY_TOOL_NAMES,
3945
4312
  GLOBAL_INFRA_OPTIONS,
3946
4313
  GLOBAL_SCAN_OPTIONS,
4314
+ GitHubIssueResolverAgent,
4315
+ GitHubService,
3947
4316
  GoParser,
3948
4317
  IssueSchema,
3949
4318
  IssueType,
@@ -4048,6 +4417,8 @@ export {
4048
4417
  generateStatCards,
4049
4418
  generateTable,
4050
4419
  generateValueChain,
4420
+ getChangedFiles,
4421
+ getDirname,
4051
4422
  getElapsedTime,
4052
4423
  getErrorMessage,
4053
4424
  getFileCommitTimestamps,
@@ -4116,12 +4487,14 @@ export {
4116
4487
  resolveOutputPath,
4117
4488
  runBatchAnalysis,
4118
4489
  runStandardCliAction,
4490
+ safeJsonStringify,
4119
4491
  saveScoreEntry,
4120
4492
  scanEntries,
4121
4493
  scanFiles,
4122
4494
  setupParser,
4123
4495
  severityToAnnotationLevel,
4124
4496
  toErrorMessage,
4497
+ validateConfig,
4125
4498
  validateSpokeOutput,
4126
4499
  validateWithSchema,
4127
4500
  withErrorHandling,