@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/client.d.mts +65 -3
- package/dist/client.d.ts +65 -3
- package/dist/index.d.mts +70 -7
- package/dist/index.d.ts +70 -7
- package/dist/index.js +319 -58
- package/dist/index.mjs +319 -66
- package/package.json +1 -1
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, ...
|
|
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
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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-
|
|
428
|
-
name: "GPT-
|
|
429
|
-
pricePer1KInputTokens:
|
|
430
|
-
pricePer1KOutputTokens:
|
|
431
|
-
contextTier: "
|
|
432
|
-
typicalQueriesPerDevPerDay:
|
|
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
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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:
|
|
494
|
-
pricePer1KOutputTokens:
|
|
570
|
+
pricePer1KInputTokens: 8e-5,
|
|
571
|
+
pricePer1KOutputTokens: 8e-5,
|
|
495
572
|
contextTier: "frontier",
|
|
496
|
-
typicalQueriesPerDevPerDay:
|
|
573
|
+
typicalQueriesPerDevPerDay: 200
|
|
497
574
|
}
|
|
498
575
|
};
|
|
499
576
|
function getModelPreset(modelId) {
|
|
500
|
-
return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["
|
|
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
|
|
638
|
+
const wastePerQuery = budget.wastedTokens.total;
|
|
639
|
+
const tokensPerDay = wastePerQuery * cfg.queriesPerDevPerDay;
|
|
525
640
|
const tokensPerMonth = tokensPerDay * cfg.daysPerMonth;
|
|
526
|
-
const
|
|
527
|
-
|
|
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
|
|
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 =
|
|
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
|
};
|