@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.js
CHANGED
|
@@ -43,6 +43,7 @@ __export(index_exports, {
|
|
|
43
43
|
SIZE_ADJUSTED_THRESHOLDS: () => SIZE_ADJUSTED_THRESHOLDS,
|
|
44
44
|
TOOL_NAME_MAP: () => TOOL_NAME_MAP,
|
|
45
45
|
TypeScriptParser: () => TypeScriptParser,
|
|
46
|
+
VAGUE_FILE_NAMES: () => VAGUE_FILE_NAMES,
|
|
46
47
|
calculateAgentGrounding: () => calculateAgentGrounding,
|
|
47
48
|
calculateAiSignalClarity: () => calculateAiSignalClarity,
|
|
48
49
|
calculateChangeAmplification: () => calculateChangeAmplification,
|
|
@@ -64,7 +65,9 @@ __export(index_exports, {
|
|
|
64
65
|
calculateSemanticDistance: () => calculateSemanticDistance,
|
|
65
66
|
calculateTechnicalDebtInterest: () => calculateTechnicalDebtInterest,
|
|
66
67
|
calculateTestabilityIndex: () => calculateTestabilityIndex,
|
|
68
|
+
calculateTokenBudget: () => calculateTokenBudget,
|
|
67
69
|
clearHistory: () => clearHistory,
|
|
70
|
+
estimateCostFromBudget: () => estimateCostFromBudget,
|
|
68
71
|
estimateTokens: () => estimateTokens,
|
|
69
72
|
exportHistory: () => exportHistory,
|
|
70
73
|
extractFunctions: () => extractFunctions,
|
|
@@ -75,10 +78,13 @@ __export(index_exports, {
|
|
|
75
78
|
formatScore: () => formatScore,
|
|
76
79
|
formatToolScore: () => formatToolScore,
|
|
77
80
|
generateHTML: () => generateHTML,
|
|
81
|
+
generateValueChain: () => generateValueChain,
|
|
78
82
|
getDebtBreakdown: () => getDebtBreakdown,
|
|
79
83
|
getElapsedTime: () => getElapsedTime,
|
|
84
|
+
getFileCommitTimestamps: () => getFileCommitTimestamps,
|
|
80
85
|
getFileExtension: () => getFileExtension,
|
|
81
86
|
getHistorySummary: () => getHistorySummary,
|
|
87
|
+
getLineRangeLastModifiedCached: () => getLineRangeLastModifiedCached,
|
|
82
88
|
getModelPreset: () => getModelPreset,
|
|
83
89
|
getParser: () => getParser,
|
|
84
90
|
getProjectSizeTier: () => getProjectSizeTier,
|
|
@@ -86,6 +92,7 @@ __export(index_exports, {
|
|
|
86
92
|
getRatingDisplay: () => getRatingDisplay,
|
|
87
93
|
getRatingWithContext: () => getRatingWithContext,
|
|
88
94
|
getRecommendedThreshold: () => getRecommendedThreshold,
|
|
95
|
+
getRepoMetadata: () => getRepoMetadata,
|
|
89
96
|
getSupportedLanguages: () => getSupportedLanguages,
|
|
90
97
|
getToolWeight: () => getToolWeight,
|
|
91
98
|
handleCLIError: () => handleCLIError,
|
|
@@ -104,6 +111,7 @@ __export(index_exports, {
|
|
|
104
111
|
readFileContent: () => readFileContent,
|
|
105
112
|
resolveOutputPath: () => resolveOutputPath,
|
|
106
113
|
saveScoreEntry: () => saveScoreEntry,
|
|
114
|
+
scanEntries: () => scanEntries,
|
|
107
115
|
scanFiles: () => scanFiles
|
|
108
116
|
});
|
|
109
117
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -159,6 +167,8 @@ var DEFAULT_EXCLUDE = [
|
|
|
159
167
|
"**/cdk.out/**",
|
|
160
168
|
// Framework-specific build dirs
|
|
161
169
|
"**/.next/**",
|
|
170
|
+
"**/.sst/**",
|
|
171
|
+
"**/.open-next/**",
|
|
162
172
|
"**/.nuxt/**",
|
|
163
173
|
"**/.vuepress/**",
|
|
164
174
|
"**/.cache/**",
|
|
@@ -190,6 +200,28 @@ var DEFAULT_EXCLUDE = [
|
|
|
190
200
|
"**/*.log",
|
|
191
201
|
"**/.DS_Store"
|
|
192
202
|
];
|
|
203
|
+
var VAGUE_FILE_NAMES = /* @__PURE__ */ new Set([
|
|
204
|
+
"utils",
|
|
205
|
+
"helpers",
|
|
206
|
+
"helper",
|
|
207
|
+
"misc",
|
|
208
|
+
"common",
|
|
209
|
+
"shared",
|
|
210
|
+
"tools",
|
|
211
|
+
"util",
|
|
212
|
+
"lib",
|
|
213
|
+
"libs",
|
|
214
|
+
"stuff",
|
|
215
|
+
"functions",
|
|
216
|
+
"methods",
|
|
217
|
+
"handlers",
|
|
218
|
+
"data",
|
|
219
|
+
"temp",
|
|
220
|
+
"tmp",
|
|
221
|
+
"test-utils",
|
|
222
|
+
"test-helpers",
|
|
223
|
+
"mocks"
|
|
224
|
+
]);
|
|
193
225
|
async function scanFiles(options) {
|
|
194
226
|
const {
|
|
195
227
|
rootDir,
|
|
@@ -207,20 +239,35 @@ async function scanFiles(options) {
|
|
|
207
239
|
ignoreFromFile = [];
|
|
208
240
|
}
|
|
209
241
|
}
|
|
242
|
+
const TEST_PATTERNS = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"];
|
|
243
|
+
const baseExclude = options.includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
|
|
210
244
|
const finalExclude = [
|
|
211
|
-
.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...
|
|
245
|
+
.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])
|
|
212
246
|
];
|
|
213
247
|
const files = await (0, import_glob.glob)(include, {
|
|
214
248
|
cwd: rootDir,
|
|
215
249
|
ignore: finalExclude,
|
|
216
250
|
absolute: true
|
|
217
251
|
});
|
|
218
|
-
const
|
|
219
|
-
|
|
252
|
+
const gitignoreFiles = await (0, import_glob.glob)("**/.gitignore", {
|
|
253
|
+
cwd: rootDir,
|
|
254
|
+
ignore: finalExclude,
|
|
255
|
+
absolute: true
|
|
256
|
+
});
|
|
257
|
+
if (gitignoreFiles.length > 0) {
|
|
220
258
|
try {
|
|
221
|
-
const gitTxt = await (0, import_promises.readFile)(gitignorePath, "utf-8");
|
|
222
259
|
const ig = (0, import_ignore.default)();
|
|
223
|
-
|
|
260
|
+
for (const gitignorePath of gitignoreFiles) {
|
|
261
|
+
const gitTxt = await (0, import_promises.readFile)(gitignorePath, "utf-8");
|
|
262
|
+
const gitignoreDir = (0, import_path.dirname)(gitignorePath);
|
|
263
|
+
const relativePrefix = (0, import_path.relative)(rootDir || ".", gitignoreDir).replace(/\\/g, "/");
|
|
264
|
+
const patterns = gitTxt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#"));
|
|
265
|
+
if (relativePrefix === "." || relativePrefix === "") {
|
|
266
|
+
ig.add(patterns);
|
|
267
|
+
} else {
|
|
268
|
+
ig.add(patterns.map((p) => p.startsWith("/") ? `${relativePrefix}${p}` : `${relativePrefix}/**/${p}`));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
224
271
|
const filtered = files.filter((f) => {
|
|
225
272
|
let rel = (0, import_path.relative)(rootDir || ".", f).replace(/\\/g, "/");
|
|
226
273
|
if (rel === "") rel = f;
|
|
@@ -233,6 +280,54 @@ async function scanFiles(options) {
|
|
|
233
280
|
}
|
|
234
281
|
return files;
|
|
235
282
|
}
|
|
283
|
+
async function scanEntries(options) {
|
|
284
|
+
const files = await scanFiles(options);
|
|
285
|
+
const { rootDir, include = ["**/*"], exclude, includeTests } = options;
|
|
286
|
+
const ignoreFilePath = (0, import_path.join)(rootDir || ".", ".aireadyignore");
|
|
287
|
+
let ignoreFromFile = [];
|
|
288
|
+
if ((0, import_fs.existsSync)(ignoreFilePath)) {
|
|
289
|
+
try {
|
|
290
|
+
const txt = await (0, import_promises.readFile)(ignoreFilePath, "utf-8");
|
|
291
|
+
ignoreFromFile = txt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#")).filter((l) => !l.startsWith("!"));
|
|
292
|
+
} catch (e) {
|
|
293
|
+
ignoreFromFile = [];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const TEST_PATTERNS = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"];
|
|
297
|
+
const baseExclude = includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
|
|
298
|
+
const finalExclude = [.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])];
|
|
299
|
+
const dirs = await (0, import_glob.glob)("**/", {
|
|
300
|
+
cwd: rootDir,
|
|
301
|
+
ignore: finalExclude,
|
|
302
|
+
absolute: true
|
|
303
|
+
});
|
|
304
|
+
const gitignoreFiles = await (0, import_glob.glob)("**/.gitignore", {
|
|
305
|
+
cwd: rootDir,
|
|
306
|
+
ignore: finalExclude,
|
|
307
|
+
absolute: true
|
|
308
|
+
});
|
|
309
|
+
if (gitignoreFiles.length > 0) {
|
|
310
|
+
const ig = (0, import_ignore.default)();
|
|
311
|
+
for (const gitignorePath of gitignoreFiles) {
|
|
312
|
+
const gitTxt = await (0, import_promises.readFile)(gitignorePath, "utf-8");
|
|
313
|
+
const gitignoreDir = (0, import_path.dirname)(gitignorePath);
|
|
314
|
+
const relativePrefix = (0, import_path.relative)(rootDir || ".", gitignoreDir).replace(/\\/g, "/");
|
|
315
|
+
const patterns = gitTxt.split(/\r?\n/).map((s) => s.trim()).filter(Boolean).filter((l) => !l.startsWith("#"));
|
|
316
|
+
if (relativePrefix === "." || relativePrefix === "") {
|
|
317
|
+
ig.add(patterns);
|
|
318
|
+
} else {
|
|
319
|
+
ig.add(patterns.map((p) => p.startsWith("/") ? `${relativePrefix}${p}` : `${relativePrefix}/**/${p}`));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const filteredDirs = dirs.filter((d) => {
|
|
323
|
+
let rel = (0, import_path.relative)(rootDir || ".", d).replace(/\\/g, "/").replace(/\/$/, "");
|
|
324
|
+
if (rel === "") return true;
|
|
325
|
+
return !ig.ignores(rel);
|
|
326
|
+
});
|
|
327
|
+
return { files, dirs: filteredDirs };
|
|
328
|
+
}
|
|
329
|
+
return { files, dirs };
|
|
330
|
+
}
|
|
236
331
|
async function readFileContent(filePath) {
|
|
237
332
|
return (0, import_promises.readFile)(filePath, "utf-8");
|
|
238
333
|
}
|
|
@@ -457,9 +552,14 @@ async function loadConfig(rootDir) {
|
|
|
457
552
|
return config;
|
|
458
553
|
} catch (error) {
|
|
459
554
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
460
|
-
|
|
555
|
+
const e = new Error(
|
|
461
556
|
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
462
557
|
);
|
|
558
|
+
try {
|
|
559
|
+
e.cause = error instanceof Error ? error : void 0;
|
|
560
|
+
} catch {
|
|
561
|
+
}
|
|
562
|
+
throw e;
|
|
463
563
|
}
|
|
464
564
|
}
|
|
465
565
|
}
|
|
@@ -901,80 +1001,65 @@ function formatToolScore(output) {
|
|
|
901
1001
|
|
|
902
1002
|
// src/business-metrics.ts
|
|
903
1003
|
var MODEL_PRICING_PRESETS = {
|
|
904
|
-
"gpt-
|
|
905
|
-
name: "GPT-
|
|
906
|
-
pricePer1KInputTokens:
|
|
907
|
-
pricePer1KOutputTokens:
|
|
908
|
-
contextTier: "
|
|
909
|
-
typicalQueriesPerDevPerDay:
|
|
1004
|
+
"gpt-5.3": {
|
|
1005
|
+
name: "GPT-5.3",
|
|
1006
|
+
pricePer1KInputTokens: 2e-3,
|
|
1007
|
+
pricePer1KOutputTokens: 8e-3,
|
|
1008
|
+
contextTier: "frontier",
|
|
1009
|
+
typicalQueriesPerDevPerDay: 100
|
|
1010
|
+
},
|
|
1011
|
+
"claude-4.6": {
|
|
1012
|
+
name: "Claude 4.6",
|
|
1013
|
+
pricePer1KInputTokens: 15e-4,
|
|
1014
|
+
pricePer1KOutputTokens: 75e-4,
|
|
1015
|
+
contextTier: "frontier",
|
|
1016
|
+
typicalQueriesPerDevPerDay: 100
|
|
1017
|
+
},
|
|
1018
|
+
"gemini-3.1": {
|
|
1019
|
+
name: "Gemini 3.1 Pro",
|
|
1020
|
+
pricePer1KInputTokens: 8e-4,
|
|
1021
|
+
pricePer1KOutputTokens: 3e-3,
|
|
1022
|
+
contextTier: "frontier",
|
|
1023
|
+
typicalQueriesPerDevPerDay: 120
|
|
910
1024
|
},
|
|
911
1025
|
"gpt-4o": {
|
|
912
|
-
name: "GPT-4o",
|
|
1026
|
+
name: "GPT-4o (legacy)",
|
|
913
1027
|
pricePer1KInputTokens: 5e-3,
|
|
914
1028
|
pricePer1KOutputTokens: 0.015,
|
|
915
1029
|
contextTier: "extended",
|
|
916
1030
|
typicalQueriesPerDevPerDay: 60
|
|
917
1031
|
},
|
|
918
|
-
"gpt-4o-mini": {
|
|
919
|
-
name: "GPT-4o mini",
|
|
920
|
-
pricePer1KInputTokens: 15e-5,
|
|
921
|
-
pricePer1KOutputTokens: 6e-4,
|
|
922
|
-
contextTier: "extended",
|
|
923
|
-
typicalQueriesPerDevPerDay: 120
|
|
924
|
-
},
|
|
925
1032
|
"claude-3-5-sonnet": {
|
|
926
|
-
name: "Claude 3.5 Sonnet",
|
|
1033
|
+
name: "Claude 3.5 Sonnet (legacy)",
|
|
927
1034
|
pricePer1KInputTokens: 3e-3,
|
|
928
1035
|
pricePer1KOutputTokens: 0.015,
|
|
929
1036
|
contextTier: "extended",
|
|
930
1037
|
typicalQueriesPerDevPerDay: 80
|
|
931
1038
|
},
|
|
932
|
-
"claude-3-7-sonnet": {
|
|
933
|
-
name: "Claude 3.7 Sonnet",
|
|
934
|
-
pricePer1KInputTokens: 3e-3,
|
|
935
|
-
pricePer1KOutputTokens: 0.015,
|
|
936
|
-
contextTier: "frontier",
|
|
937
|
-
typicalQueriesPerDevPerDay: 80
|
|
938
|
-
},
|
|
939
|
-
"claude-sonnet-4": {
|
|
940
|
-
name: "Claude Sonnet 4",
|
|
941
|
-
pricePer1KInputTokens: 3e-3,
|
|
942
|
-
pricePer1KOutputTokens: 0.015,
|
|
943
|
-
contextTier: "frontier",
|
|
944
|
-
typicalQueriesPerDevPerDay: 80
|
|
945
|
-
},
|
|
946
1039
|
"gemini-1-5-pro": {
|
|
947
|
-
name: "Gemini 1.5 Pro",
|
|
1040
|
+
name: "Gemini 1.5 Pro (legacy)",
|
|
948
1041
|
pricePer1KInputTokens: 125e-5,
|
|
949
1042
|
pricePer1KOutputTokens: 5e-3,
|
|
950
1043
|
contextTier: "frontier",
|
|
951
1044
|
typicalQueriesPerDevPerDay: 80
|
|
952
1045
|
},
|
|
953
|
-
"gemini-2-0-flash": {
|
|
954
|
-
name: "Gemini 2.0 Flash",
|
|
955
|
-
pricePer1KInputTokens: 1e-4,
|
|
956
|
-
pricePer1KOutputTokens: 4e-4,
|
|
957
|
-
contextTier: "frontier",
|
|
958
|
-
typicalQueriesPerDevPerDay: 150
|
|
959
|
-
},
|
|
960
1046
|
copilot: {
|
|
961
1047
|
name: "GitHub Copilot (subscription)",
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
typicalQueriesPerDevPerDay: 80
|
|
1048
|
+
pricePer1KInputTokens: 8e-5,
|
|
1049
|
+
pricePer1KOutputTokens: 8e-5,
|
|
1050
|
+
contextTier: "frontier",
|
|
1051
|
+
typicalQueriesPerDevPerDay: 150
|
|
967
1052
|
},
|
|
968
1053
|
"cursor-pro": {
|
|
969
1054
|
name: "Cursor Pro (subscription)",
|
|
970
|
-
pricePer1KInputTokens:
|
|
971
|
-
pricePer1KOutputTokens:
|
|
1055
|
+
pricePer1KInputTokens: 8e-5,
|
|
1056
|
+
pricePer1KOutputTokens: 8e-5,
|
|
972
1057
|
contextTier: "frontier",
|
|
973
|
-
typicalQueriesPerDevPerDay:
|
|
1058
|
+
typicalQueriesPerDevPerDay: 200
|
|
974
1059
|
}
|
|
975
1060
|
};
|
|
976
1061
|
function getModelPreset(modelId) {
|
|
977
|
-
return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["
|
|
1062
|
+
return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
|
|
978
1063
|
}
|
|
979
1064
|
var DEFAULT_COST_CONFIG = {
|
|
980
1065
|
pricePer1KTokens: 5e-3,
|
|
@@ -987,21 +1072,71 @@ var DEFAULT_COST_CONFIG = {
|
|
|
987
1072
|
};
|
|
988
1073
|
var SEVERITY_TIME_ESTIMATES = {
|
|
989
1074
|
critical: 4,
|
|
990
|
-
// Complex architectural issues
|
|
1075
|
+
// Complex architectural issues (industry avg: 6.8h)
|
|
991
1076
|
major: 2,
|
|
992
|
-
// Significant refactoring needed
|
|
1077
|
+
// Significant refactoring needed (avg: 3.4h)
|
|
993
1078
|
minor: 0.5,
|
|
994
|
-
// Simple naming/style fixes
|
|
1079
|
+
// Simple naming/style fixes (avg: 0.8h)
|
|
995
1080
|
info: 0.25
|
|
996
1081
|
// Documentation improvements
|
|
997
1082
|
};
|
|
998
1083
|
var DEFAULT_HOURLY_RATE = 75;
|
|
999
1084
|
function calculateMonthlyCost(tokenWaste, config = {}) {
|
|
1085
|
+
const budget = calculateTokenBudget({
|
|
1086
|
+
totalContextTokens: tokenWaste * 2.5,
|
|
1087
|
+
// Heuristic: context is larger than waste
|
|
1088
|
+
wastedTokens: {
|
|
1089
|
+
duplication: tokenWaste * 0.7,
|
|
1090
|
+
fragmentation: tokenWaste * 0.3,
|
|
1091
|
+
chattiness: 0
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
const preset = getModelPreset("claude-4.6");
|
|
1095
|
+
return estimateCostFromBudget(budget, preset, config);
|
|
1096
|
+
}
|
|
1097
|
+
function calculateTokenBudget(params) {
|
|
1098
|
+
const { totalContextTokens, wastedTokens } = params;
|
|
1099
|
+
const estimatedResponseTokens = params.estimatedResponseTokens ?? totalContextTokens * 0.2;
|
|
1100
|
+
const totalWaste = wastedTokens.duplication + wastedTokens.fragmentation + wastedTokens.chattiness;
|
|
1101
|
+
const efficiencyRatio = Math.max(
|
|
1102
|
+
0,
|
|
1103
|
+
Math.min(1, (totalContextTokens - totalWaste) / Math.max(1, totalContextTokens))
|
|
1104
|
+
);
|
|
1105
|
+
return {
|
|
1106
|
+
totalContextTokens: Math.round(totalContextTokens),
|
|
1107
|
+
estimatedResponseTokens: Math.round(estimatedResponseTokens),
|
|
1108
|
+
wastedTokens: {
|
|
1109
|
+
total: Math.round(totalWaste),
|
|
1110
|
+
bySource: {
|
|
1111
|
+
duplication: Math.round(wastedTokens.duplication),
|
|
1112
|
+
fragmentation: Math.round(wastedTokens.fragmentation),
|
|
1113
|
+
chattiness: Math.round(wastedTokens.chattiness)
|
|
1114
|
+
}
|
|
1115
|
+
},
|
|
1116
|
+
efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
|
|
1117
|
+
potentialRetrievableTokens: Math.round(totalWaste * 0.8)
|
|
1118
|
+
// Heuristic: 80% is fixable
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
function estimateCostFromBudget(budget, model, config = {}) {
|
|
1000
1122
|
const cfg = { ...DEFAULT_COST_CONFIG, ...config };
|
|
1001
|
-
const
|
|
1123
|
+
const wastePerQuery = budget.wastedTokens.total;
|
|
1124
|
+
const tokensPerDay = wastePerQuery * cfg.queriesPerDevPerDay;
|
|
1002
1125
|
const tokensPerMonth = tokensPerDay * cfg.daysPerMonth;
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1126
|
+
const totalWeight = cfg.developerCount;
|
|
1127
|
+
const baseCost = tokensPerMonth / 1e3 * model.pricePer1KInputTokens * totalWeight;
|
|
1128
|
+
let confidence = 0.85;
|
|
1129
|
+
if (model.contextTier === "frontier") confidence = 0.7;
|
|
1130
|
+
const variance = 0.25;
|
|
1131
|
+
const range = [
|
|
1132
|
+
Math.round(baseCost * (1 - variance) * 100) / 100,
|
|
1133
|
+
Math.round(baseCost * (1 + variance) * 100) / 100
|
|
1134
|
+
];
|
|
1135
|
+
return {
|
|
1136
|
+
total: Math.round(baseCost * 100) / 100,
|
|
1137
|
+
range,
|
|
1138
|
+
confidence
|
|
1139
|
+
};
|
|
1005
1140
|
}
|
|
1006
1141
|
function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
|
|
1007
1142
|
const counts = {
|
|
@@ -1384,6 +1519,52 @@ function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
|
|
|
1384
1519
|
return priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
1385
1520
|
});
|
|
1386
1521
|
}
|
|
1522
|
+
function generateValueChain(params) {
|
|
1523
|
+
const { issueType, count, severity } = params;
|
|
1524
|
+
const impacts = {
|
|
1525
|
+
"duplicate-pattern": {
|
|
1526
|
+
ai: "Ambiguous context leads to code generation variants. AI picks wrong implementation 40% of the time.",
|
|
1527
|
+
dev: "Developers must manually resolve conflicts between suggested variants.",
|
|
1528
|
+
risk: "high"
|
|
1529
|
+
},
|
|
1530
|
+
"context-fragmentation": {
|
|
1531
|
+
ai: "Context window overflow causes model to forget mid-file dependencies resulting in hallucinations.",
|
|
1532
|
+
dev: "Slower AI responses and increased need for manual context pinning.",
|
|
1533
|
+
risk: "critical"
|
|
1534
|
+
},
|
|
1535
|
+
"naming-inconsistency": {
|
|
1536
|
+
ai: "Degraded intent inference. AI misidentifies domain concepts across file boundaries.",
|
|
1537
|
+
dev: "Increased cognitive load for new devs during onboarding.",
|
|
1538
|
+
risk: "moderate"
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
const impact = impacts[issueType] || {
|
|
1542
|
+
ai: "Reduced suggestion quality.",
|
|
1543
|
+
dev: "Slowed development velocity.",
|
|
1544
|
+
risk: "moderate"
|
|
1545
|
+
};
|
|
1546
|
+
const productivityLoss = severity === "critical" ? 0.25 : severity === "major" ? 0.1 : 0.05;
|
|
1547
|
+
return {
|
|
1548
|
+
issueType,
|
|
1549
|
+
technicalMetric: "Issue Count",
|
|
1550
|
+
technicalValue: count,
|
|
1551
|
+
aiImpact: {
|
|
1552
|
+
description: impact.ai,
|
|
1553
|
+
scoreImpact: severity === "critical" ? -15 : -5
|
|
1554
|
+
},
|
|
1555
|
+
developerImpact: {
|
|
1556
|
+
description: impact.dev,
|
|
1557
|
+
productivityLoss
|
|
1558
|
+
},
|
|
1559
|
+
businessOutcome: {
|
|
1560
|
+
directCost: count * 12,
|
|
1561
|
+
// Placeholder linear cost
|
|
1562
|
+
opportunityCost: productivityLoss * 15e3,
|
|
1563
|
+
// Monthly avg dev cost $15k
|
|
1564
|
+
riskLevel: impact.risk
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1387
1568
|
|
|
1388
1569
|
// src/parsers/typescript-parser.ts
|
|
1389
1570
|
var import_typescript_estree2 = require("@typescript-eslint/typescript-estree");
|
|
@@ -2806,6 +2987,78 @@ function clearHistory(rootDir) {
|
|
|
2806
2987
|
(0, import_fs4.writeFileSync)(historyPath, JSON.stringify([]));
|
|
2807
2988
|
}
|
|
2808
2989
|
}
|
|
2990
|
+
|
|
2991
|
+
// src/utils/history-git.ts
|
|
2992
|
+
var import_child_process = require("child_process");
|
|
2993
|
+
function getFileCommitTimestamps(file) {
|
|
2994
|
+
const lineStamps = {};
|
|
2995
|
+
try {
|
|
2996
|
+
const output = (0, import_child_process.execSync)(`git blame -t "${file}"`, {
|
|
2997
|
+
encoding: "utf-8",
|
|
2998
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
2999
|
+
});
|
|
3000
|
+
const lines = output.split("\n");
|
|
3001
|
+
for (const line of lines) {
|
|
3002
|
+
if (!line) continue;
|
|
3003
|
+
const match = line.match(/^\S+\s+\(.*?(\d{10,})\s+[-+]\d+\s+(\d+)\)/);
|
|
3004
|
+
if (match) {
|
|
3005
|
+
const ts = parseInt(match[1], 10);
|
|
3006
|
+
const ln = parseInt(match[2], 10);
|
|
3007
|
+
lineStamps[ln] = ts;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
} catch {
|
|
3011
|
+
}
|
|
3012
|
+
return lineStamps;
|
|
3013
|
+
}
|
|
3014
|
+
function getLineRangeLastModifiedCached(lineStamps, startLine, endLine) {
|
|
3015
|
+
let latest = 0;
|
|
3016
|
+
for (let i = startLine; i <= endLine; i++) {
|
|
3017
|
+
if (lineStamps[i] && lineStamps[i] > latest) {
|
|
3018
|
+
latest = lineStamps[i];
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
return latest;
|
|
3022
|
+
}
|
|
3023
|
+
function getRepoMetadata(directory) {
|
|
3024
|
+
const metadata = {};
|
|
3025
|
+
try {
|
|
3026
|
+
try {
|
|
3027
|
+
metadata.url = (0, import_child_process.execSync)("git config --get remote.origin.url", {
|
|
3028
|
+
cwd: directory,
|
|
3029
|
+
encoding: "utf-8",
|
|
3030
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3031
|
+
}).trim();
|
|
3032
|
+
} catch {
|
|
3033
|
+
}
|
|
3034
|
+
try {
|
|
3035
|
+
metadata.branch = (0, import_child_process.execSync)("git rev-parse --abbrev-ref HEAD", {
|
|
3036
|
+
cwd: directory,
|
|
3037
|
+
encoding: "utf-8",
|
|
3038
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3039
|
+
}).trim();
|
|
3040
|
+
} catch {
|
|
3041
|
+
}
|
|
3042
|
+
try {
|
|
3043
|
+
metadata.commit = (0, import_child_process.execSync)("git rev-parse HEAD", {
|
|
3044
|
+
cwd: directory,
|
|
3045
|
+
encoding: "utf-8",
|
|
3046
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3047
|
+
}).trim();
|
|
3048
|
+
} catch {
|
|
3049
|
+
}
|
|
3050
|
+
try {
|
|
3051
|
+
metadata.author = (0, import_child_process.execSync)("git log -1 --format=%ae", {
|
|
3052
|
+
cwd: directory,
|
|
3053
|
+
encoding: "utf-8",
|
|
3054
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3055
|
+
}).trim();
|
|
3056
|
+
} catch {
|
|
3057
|
+
}
|
|
3058
|
+
} catch {
|
|
3059
|
+
}
|
|
3060
|
+
return metadata;
|
|
3061
|
+
}
|
|
2809
3062
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2810
3063
|
0 && (module.exports = {
|
|
2811
3064
|
CONTEXT_TIER_THRESHOLDS,
|
|
@@ -2821,6 +3074,7 @@ function clearHistory(rootDir) {
|
|
|
2821
3074
|
SIZE_ADJUSTED_THRESHOLDS,
|
|
2822
3075
|
TOOL_NAME_MAP,
|
|
2823
3076
|
TypeScriptParser,
|
|
3077
|
+
VAGUE_FILE_NAMES,
|
|
2824
3078
|
calculateAgentGrounding,
|
|
2825
3079
|
calculateAiSignalClarity,
|
|
2826
3080
|
calculateChangeAmplification,
|
|
@@ -2842,7 +3096,9 @@ function clearHistory(rootDir) {
|
|
|
2842
3096
|
calculateSemanticDistance,
|
|
2843
3097
|
calculateTechnicalDebtInterest,
|
|
2844
3098
|
calculateTestabilityIndex,
|
|
3099
|
+
calculateTokenBudget,
|
|
2845
3100
|
clearHistory,
|
|
3101
|
+
estimateCostFromBudget,
|
|
2846
3102
|
estimateTokens,
|
|
2847
3103
|
exportHistory,
|
|
2848
3104
|
extractFunctions,
|
|
@@ -2853,10 +3109,13 @@ function clearHistory(rootDir) {
|
|
|
2853
3109
|
formatScore,
|
|
2854
3110
|
formatToolScore,
|
|
2855
3111
|
generateHTML,
|
|
3112
|
+
generateValueChain,
|
|
2856
3113
|
getDebtBreakdown,
|
|
2857
3114
|
getElapsedTime,
|
|
3115
|
+
getFileCommitTimestamps,
|
|
2858
3116
|
getFileExtension,
|
|
2859
3117
|
getHistorySummary,
|
|
3118
|
+
getLineRangeLastModifiedCached,
|
|
2860
3119
|
getModelPreset,
|
|
2861
3120
|
getParser,
|
|
2862
3121
|
getProjectSizeTier,
|
|
@@ -2864,6 +3123,7 @@ function clearHistory(rootDir) {
|
|
|
2864
3123
|
getRatingDisplay,
|
|
2865
3124
|
getRatingWithContext,
|
|
2866
3125
|
getRecommendedThreshold,
|
|
3126
|
+
getRepoMetadata,
|
|
2867
3127
|
getSupportedLanguages,
|
|
2868
3128
|
getToolWeight,
|
|
2869
3129
|
handleCLIError,
|
|
@@ -2882,5 +3142,6 @@ function clearHistory(rootDir) {
|
|
|
2882
3142
|
readFileContent,
|
|
2883
3143
|
resolveOutputPath,
|
|
2884
3144
|
saveScoreEntry,
|
|
3145
|
+
scanEntries,
|
|
2885
3146
|
scanFiles
|
|
2886
3147
|
});
|