@aiready/core 0.9.33 → 0.9.37

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/dist/index.mjs CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  import { glob } from "glob";
25
25
  import { readFile } from "fs/promises";
26
26
  import { existsSync } from "fs";
27
- import { join, relative } from "path";
27
+ import { join, relative, dirname } from "path";
28
28
  import ignorePkg from "ignore";
29
29
  var DEFAULT_EXCLUDE = [
30
30
  // Dependencies
@@ -40,6 +40,8 @@ var DEFAULT_EXCLUDE = [
40
40
  "**/cdk.out/**",
41
41
  // Framework-specific build dirs
42
42
  "**/.next/**",
43
+ "**/.sst/**",
44
+ "**/.open-next/**",
43
45
  "**/.nuxt/**",
44
46
  "**/.vuepress/**",
45
47
  "**/.cache/**",
@@ -71,6 +73,28 @@ var DEFAULT_EXCLUDE = [
71
73
  "**/*.log",
72
74
  "**/.DS_Store"
73
75
  ];
76
+ var VAGUE_FILE_NAMES = /* @__PURE__ */ new Set([
77
+ "utils",
78
+ "helpers",
79
+ "helper",
80
+ "misc",
81
+ "common",
82
+ "shared",
83
+ "tools",
84
+ "util",
85
+ "lib",
86
+ "libs",
87
+ "stuff",
88
+ "functions",
89
+ "methods",
90
+ "handlers",
91
+ "data",
92
+ "temp",
93
+ "tmp",
94
+ "test-utils",
95
+ "test-helpers",
96
+ "mocks"
97
+ ]);
74
98
  async function scanFiles(options) {
75
99
  const {
76
100
  rootDir,
@@ -88,20 +112,35 @@ async function scanFiles(options) {
88
112
  ignoreFromFile = [];
89
113
  }
90
114
  }
115
+ const TEST_PATTERNS = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"];
116
+ const baseExclude = options.includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
91
117
  const finalExclude = [
92
- .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...DEFAULT_EXCLUDE])
118
+ .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])
93
119
  ];
94
120
  const files = await glob(include, {
95
121
  cwd: rootDir,
96
122
  ignore: finalExclude,
97
123
  absolute: true
98
124
  });
99
- const gitignorePath = join(rootDir || ".", ".gitignore");
100
- if (existsSync(gitignorePath)) {
125
+ const gitignoreFiles = await glob("**/.gitignore", {
126
+ cwd: rootDir,
127
+ ignore: finalExclude,
128
+ absolute: true
129
+ });
130
+ if (gitignoreFiles.length > 0) {
101
131
  try {
102
- const gitTxt = await readFile(gitignorePath, "utf-8");
103
132
  const ig = ignorePkg();
104
- ig.add(gitTxt);
133
+ for (const gitignorePath of gitignoreFiles) {
134
+ const gitTxt = await readFile(gitignorePath, "utf-8");
135
+ const gitignoreDir = dirname(gitignorePath);
136
+ const relativePrefix = relative(rootDir || ".", gitignoreDir).replace(/\\/g, "/");
137
+ const patterns = gitTxt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#"));
138
+ if (relativePrefix === "." || relativePrefix === "") {
139
+ ig.add(patterns);
140
+ } else {
141
+ ig.add(patterns.map((p) => p.startsWith("/") ? `${relativePrefix}${p}` : `${relativePrefix}/**/${p}`));
142
+ }
143
+ }
105
144
  const filtered = files.filter((f) => {
106
145
  let rel = relative(rootDir || ".", f).replace(/\\/g, "/");
107
146
  if (rel === "") rel = f;
@@ -114,6 +153,54 @@ async function scanFiles(options) {
114
153
  }
115
154
  return files;
116
155
  }
156
+ async function scanEntries(options) {
157
+ const files = await scanFiles(options);
158
+ const { rootDir, include = ["**/*"], exclude, includeTests } = options;
159
+ const ignoreFilePath = join(rootDir || ".", ".aireadyignore");
160
+ let ignoreFromFile = [];
161
+ if (existsSync(ignoreFilePath)) {
162
+ try {
163
+ const txt = await readFile(ignoreFilePath, "utf-8");
164
+ ignoreFromFile = txt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#")).filter((l) => !l.startsWith("!"));
165
+ } catch (e) {
166
+ ignoreFromFile = [];
167
+ }
168
+ }
169
+ const TEST_PATTERNS = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"];
170
+ const baseExclude = includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
171
+ const finalExclude = [.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])];
172
+ const dirs = await glob("**/", {
173
+ cwd: rootDir,
174
+ ignore: finalExclude,
175
+ absolute: true
176
+ });
177
+ const gitignoreFiles = await glob("**/.gitignore", {
178
+ cwd: rootDir,
179
+ ignore: finalExclude,
180
+ absolute: true
181
+ });
182
+ if (gitignoreFiles.length > 0) {
183
+ const ig = ignorePkg();
184
+ for (const gitignorePath of gitignoreFiles) {
185
+ const gitTxt = await readFile(gitignorePath, "utf-8");
186
+ const gitignoreDir = dirname(gitignorePath);
187
+ const relativePrefix = relative(rootDir || ".", gitignoreDir).replace(/\\/g, "/");
188
+ const patterns = gitTxt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#"));
189
+ if (relativePrefix === "." || relativePrefix === "") {
190
+ ig.add(patterns);
191
+ } else {
192
+ ig.add(patterns.map((p) => p.startsWith("/") ? `${relativePrefix}${p}` : `${relativePrefix}/**/${p}`));
193
+ }
194
+ }
195
+ const filteredDirs = dirs.filter((d) => {
196
+ let rel = relative(rootDir || ".", d).replace(/\\/g, "/").replace(/\/$/, "");
197
+ if (rel === "") return true;
198
+ return !ig.ignores(rel);
199
+ });
200
+ return { files, dirs: filteredDirs };
201
+ }
202
+ return { files, dirs };
203
+ }
117
204
  async function readFileContent(filePath) {
118
205
  return readFile(filePath, "utf-8");
119
206
  }
@@ -306,7 +393,7 @@ function estimateTokens(text) {
306
393
 
307
394
  // src/utils/config.ts
308
395
  import { readFileSync, existsSync as existsSync2 } from "fs";
309
- import { join as join2, resolve, dirname } from "path";
396
+ import { join as join2, resolve, dirname as dirname2 } from "path";
310
397
  import { pathToFileURL } from "url";
311
398
  var CONFIG_FILES = [
312
399
  "aiready.json",
@@ -338,13 +425,18 @@ async function loadConfig(rootDir) {
338
425
  return config;
339
426
  } catch (error) {
340
427
  const errorMessage = error instanceof Error ? error.message : String(error);
341
- throw new Error(
428
+ const e = new Error(
342
429
  `Failed to load config from ${configPath}: ${errorMessage}`
343
430
  );
431
+ try {
432
+ e.cause = error instanceof Error ? error : void 0;
433
+ } catch {
434
+ }
435
+ throw e;
344
436
  }
345
437
  }
346
438
  }
347
- const parent = dirname(currentDir);
439
+ const parent = dirname2(currentDir);
348
440
  if (parent === currentDir) {
349
441
  break;
350
442
  }
@@ -377,7 +469,7 @@ function mergeConfigWithDefaults(userConfig, defaults) {
377
469
 
378
470
  // src/utils/cli-helpers.ts
379
471
  import { writeFileSync, mkdirSync, existsSync as existsSync3 } from "fs";
380
- import { join as join3, dirname as dirname2 } from "path";
472
+ import { join as join3, dirname as dirname3 } from "path";
381
473
  function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()) {
382
474
  let outputPath;
383
475
  if (userPath) {
@@ -386,7 +478,7 @@ function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()
386
478
  const aireadyDir = join3(workingDir, ".aiready");
387
479
  outputPath = join3(aireadyDir, defaultFilename);
388
480
  }
389
- const parentDir = dirname2(outputPath);
481
+ const parentDir = dirname3(outputPath);
390
482
  if (!existsSync3(parentDir)) {
391
483
  mkdirSync(parentDir, { recursive: true });
392
484
  }
@@ -404,7 +496,7 @@ async function loadMergedConfig(directory, defaults, cliOptions) {
404
496
  }
405
497
  function handleJSONOutput(data, outputFile, successMessage) {
406
498
  if (outputFile) {
407
- const dir = dirname2(outputFile);
499
+ const dir = dirname3(outputFile);
408
500
  if (!existsSync3(dir)) {
409
501
  mkdirSync(dir, { recursive: true });
410
502
  }
@@ -424,80 +516,65 @@ function getElapsedTime(startTime) {
424
516
 
425
517
  // src/business-metrics.ts
426
518
  var MODEL_PRICING_PRESETS = {
427
- "gpt-4": {
428
- name: "GPT-4",
429
- pricePer1KInputTokens: 0.03,
430
- pricePer1KOutputTokens: 0.06,
431
- contextTier: "standard",
432
- typicalQueriesPerDevPerDay: 40
519
+ "gpt-5.3": {
520
+ name: "GPT-5.3",
521
+ pricePer1KInputTokens: 2e-3,
522
+ pricePer1KOutputTokens: 8e-3,
523
+ contextTier: "frontier",
524
+ typicalQueriesPerDevPerDay: 100
525
+ },
526
+ "claude-4.6": {
527
+ name: "Claude 4.6",
528
+ pricePer1KInputTokens: 15e-4,
529
+ pricePer1KOutputTokens: 75e-4,
530
+ contextTier: "frontier",
531
+ typicalQueriesPerDevPerDay: 100
532
+ },
533
+ "gemini-3.1": {
534
+ name: "Gemini 3.1 Pro",
535
+ pricePer1KInputTokens: 8e-4,
536
+ pricePer1KOutputTokens: 3e-3,
537
+ contextTier: "frontier",
538
+ typicalQueriesPerDevPerDay: 120
433
539
  },
434
540
  "gpt-4o": {
435
- name: "GPT-4o",
541
+ name: "GPT-4o (legacy)",
436
542
  pricePer1KInputTokens: 5e-3,
437
543
  pricePer1KOutputTokens: 0.015,
438
544
  contextTier: "extended",
439
545
  typicalQueriesPerDevPerDay: 60
440
546
  },
441
- "gpt-4o-mini": {
442
- name: "GPT-4o mini",
443
- pricePer1KInputTokens: 15e-5,
444
- pricePer1KOutputTokens: 6e-4,
445
- contextTier: "extended",
446
- typicalQueriesPerDevPerDay: 120
447
- },
448
547
  "claude-3-5-sonnet": {
449
- name: "Claude 3.5 Sonnet",
548
+ name: "Claude 3.5 Sonnet (legacy)",
450
549
  pricePer1KInputTokens: 3e-3,
451
550
  pricePer1KOutputTokens: 0.015,
452
551
  contextTier: "extended",
453
552
  typicalQueriesPerDevPerDay: 80
454
553
  },
455
- "claude-3-7-sonnet": {
456
- name: "Claude 3.7 Sonnet",
457
- pricePer1KInputTokens: 3e-3,
458
- pricePer1KOutputTokens: 0.015,
459
- contextTier: "frontier",
460
- typicalQueriesPerDevPerDay: 80
461
- },
462
- "claude-sonnet-4": {
463
- name: "Claude Sonnet 4",
464
- pricePer1KInputTokens: 3e-3,
465
- pricePer1KOutputTokens: 0.015,
466
- contextTier: "frontier",
467
- typicalQueriesPerDevPerDay: 80
468
- },
469
554
  "gemini-1-5-pro": {
470
- name: "Gemini 1.5 Pro",
555
+ name: "Gemini 1.5 Pro (legacy)",
471
556
  pricePer1KInputTokens: 125e-5,
472
557
  pricePer1KOutputTokens: 5e-3,
473
558
  contextTier: "frontier",
474
559
  typicalQueriesPerDevPerDay: 80
475
560
  },
476
- "gemini-2-0-flash": {
477
- name: "Gemini 2.0 Flash",
478
- pricePer1KInputTokens: 1e-4,
479
- pricePer1KOutputTokens: 4e-4,
480
- contextTier: "frontier",
481
- typicalQueriesPerDevPerDay: 150
482
- },
483
561
  copilot: {
484
562
  name: "GitHub Copilot (subscription)",
485
- // Amortized per-request cost for a $19/month plan at 80 queries/day
486
- pricePer1KInputTokens: 1e-4,
487
- pricePer1KOutputTokens: 1e-4,
488
- contextTier: "extended",
489
- typicalQueriesPerDevPerDay: 80
563
+ pricePer1KInputTokens: 8e-5,
564
+ pricePer1KOutputTokens: 8e-5,
565
+ contextTier: "frontier",
566
+ typicalQueriesPerDevPerDay: 150
490
567
  },
491
568
  "cursor-pro": {
492
569
  name: "Cursor Pro (subscription)",
493
- pricePer1KInputTokens: 1e-4,
494
- pricePer1KOutputTokens: 1e-4,
570
+ pricePer1KInputTokens: 8e-5,
571
+ pricePer1KOutputTokens: 8e-5,
495
572
  contextTier: "frontier",
496
- typicalQueriesPerDevPerDay: 100
573
+ typicalQueriesPerDevPerDay: 200
497
574
  }
498
575
  };
499
576
  function getModelPreset(modelId) {
500
- return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["gpt-4o"];
577
+ return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
501
578
  }
502
579
  var DEFAULT_COST_CONFIG = {
503
580
  pricePer1KTokens: 5e-3,
@@ -510,21 +587,71 @@ var DEFAULT_COST_CONFIG = {
510
587
  };
511
588
  var SEVERITY_TIME_ESTIMATES = {
512
589
  critical: 4,
513
- // Complex architectural issues
590
+ // Complex architectural issues (industry avg: 6.8h)
514
591
  major: 2,
515
- // Significant refactoring needed
592
+ // Significant refactoring needed (avg: 3.4h)
516
593
  minor: 0.5,
517
- // Simple naming/style fixes
594
+ // Simple naming/style fixes (avg: 0.8h)
518
595
  info: 0.25
519
596
  // Documentation improvements
520
597
  };
521
598
  var DEFAULT_HOURLY_RATE = 75;
522
599
  function calculateMonthlyCost(tokenWaste, config = {}) {
600
+ const budget = calculateTokenBudget({
601
+ totalContextTokens: tokenWaste * 2.5,
602
+ // Heuristic: context is larger than waste
603
+ wastedTokens: {
604
+ duplication: tokenWaste * 0.7,
605
+ fragmentation: tokenWaste * 0.3,
606
+ chattiness: 0
607
+ }
608
+ });
609
+ const preset = getModelPreset("claude-4.6");
610
+ return estimateCostFromBudget(budget, preset, config);
611
+ }
612
+ function calculateTokenBudget(params) {
613
+ const { totalContextTokens, wastedTokens } = params;
614
+ const estimatedResponseTokens = params.estimatedResponseTokens ?? totalContextTokens * 0.2;
615
+ const totalWaste = wastedTokens.duplication + wastedTokens.fragmentation + wastedTokens.chattiness;
616
+ const efficiencyRatio = Math.max(
617
+ 0,
618
+ Math.min(1, (totalContextTokens - totalWaste) / Math.max(1, totalContextTokens))
619
+ );
620
+ return {
621
+ totalContextTokens: Math.round(totalContextTokens),
622
+ estimatedResponseTokens: Math.round(estimatedResponseTokens),
623
+ wastedTokens: {
624
+ total: Math.round(totalWaste),
625
+ bySource: {
626
+ duplication: Math.round(wastedTokens.duplication),
627
+ fragmentation: Math.round(wastedTokens.fragmentation),
628
+ chattiness: Math.round(wastedTokens.chattiness)
629
+ }
630
+ },
631
+ efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
632
+ potentialRetrievableTokens: Math.round(totalWaste * 0.8)
633
+ // Heuristic: 80% is fixable
634
+ };
635
+ }
636
+ function estimateCostFromBudget(budget, model, config = {}) {
523
637
  const cfg = { ...DEFAULT_COST_CONFIG, ...config };
524
- const tokensPerDay = tokenWaste * cfg.queriesPerDevPerDay;
638
+ const wastePerQuery = budget.wastedTokens.total;
639
+ const tokensPerDay = wastePerQuery * cfg.queriesPerDevPerDay;
525
640
  const tokensPerMonth = tokensPerDay * cfg.daysPerMonth;
526
- const cost = tokensPerMonth / 1e3 * cfg.pricePer1KTokens;
527
- return Math.round(cost * 100) / 100;
641
+ const totalWeight = cfg.developerCount;
642
+ const baseCost = tokensPerMonth / 1e3 * model.pricePer1KInputTokens * totalWeight;
643
+ let confidence = 0.85;
644
+ if (model.contextTier === "frontier") confidence = 0.7;
645
+ const variance = 0.25;
646
+ const range = [
647
+ Math.round(baseCost * (1 - variance) * 100) / 100,
648
+ Math.round(baseCost * (1 + variance) * 100) / 100
649
+ ];
650
+ return {
651
+ total: Math.round(baseCost * 100) / 100,
652
+ range,
653
+ confidence
654
+ };
528
655
  }
529
656
  function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
530
657
  const counts = {
@@ -907,6 +1034,52 @@ function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
907
1034
  return priorityOrder[a.priority] - priorityOrder[b.priority];
908
1035
  });
909
1036
  }
1037
+ function generateValueChain(params) {
1038
+ const { issueType, count, severity } = params;
1039
+ const impacts = {
1040
+ "duplicate-pattern": {
1041
+ ai: "Ambiguous context leads to code generation variants. AI picks wrong implementation 40% of the time.",
1042
+ dev: "Developers must manually resolve conflicts between suggested variants.",
1043
+ risk: "high"
1044
+ },
1045
+ "context-fragmentation": {
1046
+ ai: "Context window overflow causes model to forget mid-file dependencies resulting in hallucinations.",
1047
+ dev: "Slower AI responses and increased need for manual context pinning.",
1048
+ risk: "critical"
1049
+ },
1050
+ "naming-inconsistency": {
1051
+ ai: "Degraded intent inference. AI misidentifies domain concepts across file boundaries.",
1052
+ dev: "Increased cognitive load for new devs during onboarding.",
1053
+ risk: "moderate"
1054
+ }
1055
+ };
1056
+ const impact = impacts[issueType] || {
1057
+ ai: "Reduced suggestion quality.",
1058
+ dev: "Slowed development velocity.",
1059
+ risk: "moderate"
1060
+ };
1061
+ const productivityLoss = severity === "critical" ? 0.25 : severity === "major" ? 0.1 : 0.05;
1062
+ return {
1063
+ issueType,
1064
+ technicalMetric: "Issue Count",
1065
+ technicalValue: count,
1066
+ aiImpact: {
1067
+ description: impact.ai,
1068
+ scoreImpact: severity === "critical" ? -15 : -5
1069
+ },
1070
+ developerImpact: {
1071
+ description: impact.dev,
1072
+ productivityLoss
1073
+ },
1074
+ businessOutcome: {
1075
+ directCost: count * 12,
1076
+ // Placeholder linear cost
1077
+ opportunityCost: productivityLoss * 15e3,
1078
+ // Monthly avg dev cost $15k
1079
+ riskLevel: impact.risk
1080
+ }
1081
+ };
1082
+ }
910
1083
 
911
1084
  // src/parsers/typescript-parser.ts
912
1085
  import { parse as parse2 } from "@typescript-eslint/typescript-estree";
@@ -2258,7 +2431,7 @@ function calculateExtendedFutureProofScore(params) {
2258
2431
 
2259
2432
  // src/utils/history.ts
2260
2433
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
2261
- import { join as join4, dirname as dirname3 } from "path";
2434
+ import { join as join4, dirname as dirname4 } from "path";
2262
2435
  function getHistoryPath(rootDir) {
2263
2436
  return join4(rootDir, ".aiready", "history.json");
2264
2437
  }
@@ -2277,7 +2450,7 @@ function loadScoreHistory(rootDir) {
2277
2450
  }
2278
2451
  function saveScoreEntry(rootDir, entry) {
2279
2452
  const historyPath = getHistoryPath(rootDir);
2280
- const historyDir = dirname3(historyPath);
2453
+ const historyDir = dirname4(historyPath);
2281
2454
  if (!existsSync4(historyDir)) {
2282
2455
  mkdirSync2(historyDir, { recursive: true });
2283
2456
  }
@@ -2329,6 +2502,78 @@ function clearHistory(rootDir) {
2329
2502
  writeFileSync2(historyPath, JSON.stringify([]));
2330
2503
  }
2331
2504
  }
2505
+
2506
+ // src/utils/history-git.ts
2507
+ import { execSync } from "child_process";
2508
+ function getFileCommitTimestamps(file) {
2509
+ const lineStamps = {};
2510
+ try {
2511
+ const output = execSync(`git blame -t "${file}"`, {
2512
+ encoding: "utf-8",
2513
+ stdio: ["ignore", "pipe", "ignore"]
2514
+ });
2515
+ const lines = output.split("\n");
2516
+ for (const line of lines) {
2517
+ if (!line) continue;
2518
+ const match = line.match(/^\S+\s+\(.*?(\d{10,})\s+[-+]\d+\s+(\d+)\)/);
2519
+ if (match) {
2520
+ const ts = parseInt(match[1], 10);
2521
+ const ln = parseInt(match[2], 10);
2522
+ lineStamps[ln] = ts;
2523
+ }
2524
+ }
2525
+ } catch {
2526
+ }
2527
+ return lineStamps;
2528
+ }
2529
+ function getLineRangeLastModifiedCached(lineStamps, startLine, endLine) {
2530
+ let latest = 0;
2531
+ for (let i = startLine; i <= endLine; i++) {
2532
+ if (lineStamps[i] && lineStamps[i] > latest) {
2533
+ latest = lineStamps[i];
2534
+ }
2535
+ }
2536
+ return latest;
2537
+ }
2538
+ function getRepoMetadata(directory) {
2539
+ const metadata = {};
2540
+ try {
2541
+ try {
2542
+ metadata.url = execSync("git config --get remote.origin.url", {
2543
+ cwd: directory,
2544
+ encoding: "utf-8",
2545
+ stdio: ["ignore", "pipe", "ignore"]
2546
+ }).trim();
2547
+ } catch {
2548
+ }
2549
+ try {
2550
+ metadata.branch = execSync("git rev-parse --abbrev-ref HEAD", {
2551
+ cwd: directory,
2552
+ encoding: "utf-8",
2553
+ stdio: ["ignore", "pipe", "ignore"]
2554
+ }).trim();
2555
+ } catch {
2556
+ }
2557
+ try {
2558
+ metadata.commit = execSync("git rev-parse HEAD", {
2559
+ cwd: directory,
2560
+ encoding: "utf-8",
2561
+ stdio: ["ignore", "pipe", "ignore"]
2562
+ }).trim();
2563
+ } catch {
2564
+ }
2565
+ try {
2566
+ metadata.author = execSync("git log -1 --format=%ae", {
2567
+ cwd: directory,
2568
+ encoding: "utf-8",
2569
+ stdio: ["ignore", "pipe", "ignore"]
2570
+ }).trim();
2571
+ } catch {
2572
+ }
2573
+ } catch {
2574
+ }
2575
+ return metadata;
2576
+ }
2332
2577
  export {
2333
2578
  CONTEXT_TIER_THRESHOLDS,
2334
2579
  DEFAULT_COST_CONFIG,
@@ -2343,6 +2588,7 @@ export {
2343
2588
  SIZE_ADJUSTED_THRESHOLDS,
2344
2589
  TOOL_NAME_MAP,
2345
2590
  TypeScriptParser,
2591
+ VAGUE_FILE_NAMES,
2346
2592
  calculateAgentGrounding,
2347
2593
  calculateAiSignalClarity,
2348
2594
  calculateChangeAmplification,
@@ -2364,7 +2610,9 @@ export {
2364
2610
  calculateSemanticDistance,
2365
2611
  calculateTechnicalDebtInterest,
2366
2612
  calculateTestabilityIndex,
2613
+ calculateTokenBudget,
2367
2614
  clearHistory,
2615
+ estimateCostFromBudget,
2368
2616
  estimateTokens,
2369
2617
  exportHistory,
2370
2618
  extractFunctions,
@@ -2375,10 +2623,13 @@ export {
2375
2623
  formatScore,
2376
2624
  formatToolScore,
2377
2625
  generateHTML,
2626
+ generateValueChain,
2378
2627
  getDebtBreakdown,
2379
2628
  getElapsedTime,
2629
+ getFileCommitTimestamps,
2380
2630
  getFileExtension,
2381
2631
  getHistorySummary,
2632
+ getLineRangeLastModifiedCached,
2382
2633
  getModelPreset,
2383
2634
  getParser,
2384
2635
  getProjectSizeTier,
@@ -2386,6 +2637,7 @@ export {
2386
2637
  getRatingDisplay,
2387
2638
  getRatingWithContext,
2388
2639
  getRecommendedThreshold,
2640
+ getRepoMetadata,
2389
2641
  getSupportedLanguages,
2390
2642
  getToolWeight,
2391
2643
  handleCLIError,
@@ -2404,5 +2656,6 @@ export {
2404
2656
  readFileContent,
2405
2657
  resolveOutputPath,
2406
2658
  saveScoreEntry,
2659
+ scanEntries,
2407
2660
  scanFiles
2408
2661
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/core",
3
- "version": "0.9.33",
3
+ "version": "0.9.37",
4
4
  "description": "Shared utilities for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",