cto-ai-cli 4.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +201 -2
- package/README.md +217 -312
- package/dist/action/index.js +281 -162
- package/dist/api/dashboard.js +281 -162
- package/dist/api/dashboard.js.map +1 -1
- package/dist/api/server.js +362 -184
- package/dist/api/server.js.map +1 -1
- package/dist/cli/gateway.js +358 -229
- package/dist/cli/score.js +2426 -1225
- package/dist/cli/v2/index.js +290 -175
- package/dist/cli/v2/index.js.map +1 -1
- package/dist/engine/index.d.ts +150 -1
- package/dist/engine/index.js +1130 -219
- package/dist/engine/index.js.map +1 -1
- package/dist/fsevents-X6WP4TKM.node +0 -0
- package/dist/gateway/index.d.ts +2 -2
- package/dist/gateway/index.js +358 -229
- package/dist/gateway/index.js.map +1 -1
- package/dist/interact/index.js +263 -148
- package/dist/interact/index.js.map +1 -1
- package/dist/mcp/v2.js +297 -178
- package/dist/mcp/v2.js.map +1 -1
- package/package.json +8 -22
- package/dist/core/index.d.ts +0 -717
- package/dist/core/index.js +0 -4446
- package/dist/core/index.js.map +0 -1
package/dist/api/dashboard.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/api/dashboard.ts
|
|
4
|
-
import { resolve as resolve5, join as
|
|
4
|
+
import { resolve as resolve5, join as join5 } from "path";
|
|
5
5
|
import { writeFile as writeFile2, mkdir, readFile as readFile5 } from "fs/promises";
|
|
6
6
|
|
|
7
7
|
// src/engine/analyzer.ts
|
|
@@ -592,8 +592,8 @@ async function analyzeProject(projectPath, config) {
|
|
|
592
592
|
maxDepth: mergedConfig.analysis.maxDepth
|
|
593
593
|
});
|
|
594
594
|
const tokenMethod = mergedConfig.tokens.method;
|
|
595
|
-
const
|
|
596
|
-
|
|
595
|
+
const BATCH_SIZE = 50;
|
|
596
|
+
async function estimateFileTokens(entry) {
|
|
597
597
|
let tokens;
|
|
598
598
|
if (tokenMethod === "tiktoken") {
|
|
599
599
|
try {
|
|
@@ -605,7 +605,7 @@ async function analyzeProject(projectPath, config) {
|
|
|
605
605
|
} else {
|
|
606
606
|
tokens = countTokensChars4(entry.size);
|
|
607
607
|
}
|
|
608
|
-
|
|
608
|
+
return {
|
|
609
609
|
path: entry.path,
|
|
610
610
|
relativePath: entry.relativePath,
|
|
611
611
|
extension: entry.extension,
|
|
@@ -614,16 +614,20 @@ async function analyzeProject(projectPath, config) {
|
|
|
614
614
|
lines: entry.lines,
|
|
615
615
|
lastModified: entry.lastModified,
|
|
616
616
|
kind: classifyFileKind(entry.relativePath),
|
|
617
|
-
// Graph data — populated by graph analysis
|
|
618
617
|
imports: [],
|
|
619
618
|
importedBy: [],
|
|
620
619
|
isHub: false,
|
|
621
620
|
complexity: 0,
|
|
622
|
-
// Risk data — populated by risk analysis
|
|
623
621
|
riskScore: 0,
|
|
624
622
|
riskFactors: [],
|
|
625
623
|
exclusionImpact: "none"
|
|
626
|
-
}
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
const files = [];
|
|
627
|
+
for (let i = 0; i < walkEntries.length; i += BATCH_SIZE) {
|
|
628
|
+
const batch = walkEntries.slice(i, i + BATCH_SIZE);
|
|
629
|
+
const results = await Promise.all(batch.map(estimateFileTokens));
|
|
630
|
+
files.push(...results);
|
|
627
631
|
}
|
|
628
632
|
const graph = buildProjectGraph(absPath, files);
|
|
629
633
|
for (const file of files) {
|
|
@@ -1005,10 +1009,7 @@ function deduplicateFindings(findings) {
|
|
|
1005
1009
|
}
|
|
1006
1010
|
|
|
1007
1011
|
// src/engine/pruner.ts
|
|
1008
|
-
import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
1009
1012
|
import { readFile as readFile4 } from "fs/promises";
|
|
1010
|
-
import { existsSync as existsSync3 } from "fs";
|
|
1011
|
-
import { join as join5 } from "path";
|
|
1012
1013
|
var TS_EXTENSIONS2 = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx", "mts", "mjs"]);
|
|
1013
1014
|
async function pruneFile(file, level) {
|
|
1014
1015
|
if (level === "excluded") {
|
|
@@ -1031,23 +1032,7 @@ async function pruneTypeScript(file, level) {
|
|
|
1031
1032
|
} catch {
|
|
1032
1033
|
return emptyResult(file, level);
|
|
1033
1034
|
}
|
|
1034
|
-
|
|
1035
|
-
try {
|
|
1036
|
-
const tsConfigPath = findTsConfig(file.path);
|
|
1037
|
-
project = new Project2({
|
|
1038
|
-
tsConfigFilePath: tsConfigPath,
|
|
1039
|
-
skipAddingFilesFromTsConfig: true,
|
|
1040
|
-
compilerOptions: tsConfigPath ? void 0 : { allowJs: true, esModuleInterop: true }
|
|
1041
|
-
});
|
|
1042
|
-
project.createSourceFile(file.path, content, { overwrite: true });
|
|
1043
|
-
} catch {
|
|
1044
|
-
return pruneGenericFromContent(file, content, level);
|
|
1045
|
-
}
|
|
1046
|
-
const sourceFile = project.getSourceFiles()[0];
|
|
1047
|
-
if (!sourceFile) {
|
|
1048
|
-
return pruneGenericFromContent(file, content, level);
|
|
1049
|
-
}
|
|
1050
|
-
const prunedContent = level === "signatures" ? extractSignaturesAST(sourceFile) : extractSkeletonAST(sourceFile);
|
|
1035
|
+
const prunedContent = level === "signatures" ? extractSignaturesRegex(content) : extractSkeletonRegex(content);
|
|
1051
1036
|
const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, "utf-8"));
|
|
1052
1037
|
const savingsPercent = file.tokens > 0 ? (file.tokens - prunedTokens) / file.tokens * 100 : 0;
|
|
1053
1038
|
return {
|
|
@@ -1059,131 +1044,281 @@ async function pruneTypeScript(file, level) {
|
|
|
1059
1044
|
savingsPercent: Math.max(0, savingsPercent)
|
|
1060
1045
|
};
|
|
1061
1046
|
}
|
|
1062
|
-
function
|
|
1047
|
+
function extractSignaturesRegex(content) {
|
|
1048
|
+
const lines = content.split("\n");
|
|
1063
1049
|
const parts = [];
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
addJSDoc(fn, parts);
|
|
1082
|
-
const isExported = fn.isExported();
|
|
1083
|
-
const isAsync = fn.isAsync();
|
|
1084
|
-
const name = fn.getName() ?? "<anonymous>";
|
|
1085
|
-
const params = fn.getParameters().map((p) => p.getText()).join(", ");
|
|
1086
|
-
const returnType = fn.getReturnTypeNode()?.getText();
|
|
1087
|
-
const returnStr = returnType ? `: ${returnType}` : "";
|
|
1088
|
-
const prefix = isExported ? "export " : "";
|
|
1089
|
-
const asyncStr = isAsync ? "async " : "";
|
|
1090
|
-
parts.push(`${prefix}${asyncStr}function ${name}(${params})${returnStr} { /* ... */ }`);
|
|
1091
|
-
}
|
|
1092
|
-
for (const stmt of sf.getVariableStatements()) {
|
|
1093
|
-
for (const decl of stmt.getDeclarations()) {
|
|
1094
|
-
const init = decl.getInitializer();
|
|
1095
|
-
if (init && (init.getKind() === SyntaxKind2.ArrowFunction || init.getKind() === SyntaxKind2.FunctionExpression)) {
|
|
1096
|
-
addJSDoc(stmt, parts);
|
|
1097
|
-
const isExported = stmt.isExported();
|
|
1098
|
-
const prefix = isExported ? "export " : "";
|
|
1099
|
-
const kind = stmt.getDeclarationKind();
|
|
1100
|
-
const name = decl.getName();
|
|
1101
|
-
const typeNode = decl.getTypeNode()?.getText();
|
|
1102
|
-
const typeStr = typeNode ? `: ${typeNode}` : "";
|
|
1103
|
-
parts.push(`${prefix}${kind} ${name}${typeStr} = /* ... */;`);
|
|
1104
|
-
} else {
|
|
1105
|
-
addJSDoc(stmt, parts);
|
|
1106
|
-
parts.push(stmt.getText());
|
|
1050
|
+
let i = 0;
|
|
1051
|
+
while (i < lines.length) {
|
|
1052
|
+
const line = lines[i];
|
|
1053
|
+
const trimmed = line.trim();
|
|
1054
|
+
if (trimmed === "") {
|
|
1055
|
+
i++;
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1058
|
+
if (trimmed.startsWith("/**")) {
|
|
1059
|
+
const docLines = [];
|
|
1060
|
+
while (i < lines.length) {
|
|
1061
|
+
docLines.push(lines[i]);
|
|
1062
|
+
if (lines[i].includes("*/")) {
|
|
1063
|
+
i++;
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
i++;
|
|
1107
1067
|
}
|
|
1068
|
+
parts.push(docLines.join("\n"));
|
|
1069
|
+
continue;
|
|
1108
1070
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
if (
|
|
1127
|
-
const
|
|
1128
|
-
parts.push(
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
const
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1071
|
+
if (trimmed.startsWith("//")) {
|
|
1072
|
+
parts.push(line);
|
|
1073
|
+
i++;
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
if (/^\s*(import|export)\s/.test(line) && (trimmed.includes(" from ") || trimmed.startsWith("import "))) {
|
|
1077
|
+
const block = collectBracedLine(lines, i);
|
|
1078
|
+
parts.push(block.text);
|
|
1079
|
+
i = block.nextIndex;
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
if (/^\s*export\s*(\{|\*)/.test(trimmed)) {
|
|
1083
|
+
const block = collectBracedLine(lines, i);
|
|
1084
|
+
parts.push(block.text);
|
|
1085
|
+
i = block.nextIndex;
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
if (/^\s*(export\s+)?type\s+\w/.test(trimmed) && !trimmed.startsWith("typeof")) {
|
|
1089
|
+
const block = collectBalanced(lines, i);
|
|
1090
|
+
parts.push(block.text);
|
|
1091
|
+
i = block.nextIndex;
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
if (/^\s*(export\s+)?interface\s+\w/.test(trimmed)) {
|
|
1095
|
+
const block = collectBalanced(lines, i);
|
|
1096
|
+
parts.push(block.text);
|
|
1097
|
+
i = block.nextIndex;
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
if (/^\s*(export\s+)?(const\s+)?enum\s+\w/.test(trimmed)) {
|
|
1101
|
+
const block = collectBalanced(lines, i);
|
|
1102
|
+
parts.push(block.text);
|
|
1103
|
+
i = block.nextIndex;
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
const fnMatch = trimmed.match(/^(export\s+)?(async\s+)?function\s+(\w+)/);
|
|
1107
|
+
if (fnMatch) {
|
|
1108
|
+
const sig = extractFnSignature(lines, i);
|
|
1109
|
+
parts.push(`${sig} { /* ... */ }`);
|
|
1110
|
+
i = skipBlock(lines, i);
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
const arrowMatch = trimmed.match(/^(export\s+)?(const|let|var)\s+(\w+)/);
|
|
1114
|
+
if (arrowMatch && looksLikeFunctionDecl(lines, i)) {
|
|
1115
|
+
const prefix = trimmed.match(/^((?:export\s+)?(?:const|let|var)\s+\w+[^=]*=)/)?.[1];
|
|
1116
|
+
if (prefix) {
|
|
1117
|
+
parts.push(`${prefix} /* ... */;`);
|
|
1118
|
+
}
|
|
1119
|
+
i = skipBlock(lines, i);
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
if (arrowMatch) {
|
|
1123
|
+
const block = collectStatement(lines, i);
|
|
1124
|
+
parts.push(block.text);
|
|
1125
|
+
i = block.nextIndex;
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
if (/^\s*(export\s+)?(abstract\s+)?class\s+\w/.test(trimmed)) {
|
|
1129
|
+
const classOutline = extractClassOutline(lines, i);
|
|
1130
|
+
parts.push(classOutline.text);
|
|
1131
|
+
i = classOutline.nextIndex;
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
i++;
|
|
1148
1135
|
}
|
|
1149
1136
|
return parts.join("\n");
|
|
1150
1137
|
}
|
|
1151
|
-
function
|
|
1138
|
+
function extractSkeletonRegex(content) {
|
|
1139
|
+
const lines = content.split("\n");
|
|
1152
1140
|
const parts = [];
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1141
|
+
let i = 0;
|
|
1142
|
+
while (i < lines.length) {
|
|
1143
|
+
const trimmed = lines[i].trim();
|
|
1144
|
+
if (/^import\s/.test(trimmed)) {
|
|
1145
|
+
const block = collectBracedLine(lines, i);
|
|
1146
|
+
parts.push(block.text);
|
|
1147
|
+
i = block.nextIndex;
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
if (/^export\s+(type|interface)\s+\w/.test(trimmed)) {
|
|
1151
|
+
const block = collectBalanced(lines, i);
|
|
1152
|
+
parts.push(block.text);
|
|
1153
|
+
i = block.nextIndex;
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
if (/^export\s+(const\s+)?enum\s+\w/.test(trimmed)) {
|
|
1157
|
+
const block = collectBalanced(lines, i);
|
|
1158
|
+
parts.push(block.text);
|
|
1159
|
+
i = block.nextIndex;
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
if (/^export\s+(async\s+)?function\s+\w/.test(trimmed)) {
|
|
1163
|
+
const sig = extractFnSignature(lines, i);
|
|
1164
|
+
parts.push(`${sig};`);
|
|
1165
|
+
i = skipBlock(lines, i);
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
if (/^export\s+(abstract\s+)?class\s+/.test(trimmed)) {
|
|
1169
|
+
const nameMatch = trimmed.match(/class\s+(\w+)/);
|
|
1170
|
+
const name = nameMatch?.[1] ?? "Unknown";
|
|
1171
|
+
const end = skipBlock(lines, i);
|
|
1172
|
+
const methods = [];
|
|
1173
|
+
for (let j = i + 1; j < end; j++) {
|
|
1174
|
+
const mt = lines[j].trim();
|
|
1175
|
+
const mm = mt.match(/^(?:static\s+)?(?:async\s+)?(\w+)\s*\(/);
|
|
1176
|
+
if (mm && mm[1] !== "constructor") methods.push(mm[1]);
|
|
1177
|
+
}
|
|
1178
|
+
parts.push(`export class ${name} { /* methods: ${methods.join(", ")} */ }`);
|
|
1179
|
+
i = end;
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
if (/^export\s*(\{|\*)/.test(trimmed)) {
|
|
1183
|
+
const block = collectBracedLine(lines, i);
|
|
1184
|
+
parts.push(block.text);
|
|
1185
|
+
i = block.nextIndex;
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
i++;
|
|
1184
1189
|
}
|
|
1185
1190
|
return parts.join("\n");
|
|
1186
1191
|
}
|
|
1192
|
+
function collectBracedLine(lines, start) {
|
|
1193
|
+
let text = lines[start];
|
|
1194
|
+
let i = start + 1;
|
|
1195
|
+
while (i < lines.length && !text.includes(";") && !text.trimEnd().endsWith("'") && !text.trimEnd().endsWith('"')) {
|
|
1196
|
+
text += "\n" + lines[i];
|
|
1197
|
+
i++;
|
|
1198
|
+
}
|
|
1199
|
+
return { text, nextIndex: i };
|
|
1200
|
+
}
|
|
1201
|
+
function collectBalanced(lines, start) {
|
|
1202
|
+
let depth = 0;
|
|
1203
|
+
let text = "";
|
|
1204
|
+
let i = start;
|
|
1205
|
+
let started = false;
|
|
1206
|
+
while (i < lines.length) {
|
|
1207
|
+
const line = lines[i];
|
|
1208
|
+
text += (text ? "\n" : "") + line;
|
|
1209
|
+
for (const ch of line) {
|
|
1210
|
+
if (ch === "{" || ch === "(") {
|
|
1211
|
+
depth++;
|
|
1212
|
+
started = true;
|
|
1213
|
+
}
|
|
1214
|
+
if (ch === "}" || ch === ")") depth--;
|
|
1215
|
+
}
|
|
1216
|
+
i++;
|
|
1217
|
+
if (started && depth <= 0) break;
|
|
1218
|
+
if (!started && line.includes(";")) break;
|
|
1219
|
+
}
|
|
1220
|
+
return { text, nextIndex: i };
|
|
1221
|
+
}
|
|
1222
|
+
function collectStatement(lines, start) {
|
|
1223
|
+
let text = lines[start];
|
|
1224
|
+
let i = start + 1;
|
|
1225
|
+
if (text.includes(";")) return { text, nextIndex: i };
|
|
1226
|
+
let depth = 0;
|
|
1227
|
+
for (const ch of text) {
|
|
1228
|
+
if (ch === "{" || ch === "(" || ch === "[") depth++;
|
|
1229
|
+
if (ch === "}" || ch === ")" || ch === "]") depth--;
|
|
1230
|
+
}
|
|
1231
|
+
while (i < lines.length && depth > 0) {
|
|
1232
|
+
text += "\n" + lines[i];
|
|
1233
|
+
for (const ch of lines[i]) {
|
|
1234
|
+
if (ch === "{" || ch === "(" || ch === "[") depth++;
|
|
1235
|
+
if (ch === "}" || ch === ")" || ch === "]") depth--;
|
|
1236
|
+
}
|
|
1237
|
+
i++;
|
|
1238
|
+
}
|
|
1239
|
+
return { text, nextIndex: i };
|
|
1240
|
+
}
|
|
1241
|
+
function extractFnSignature(lines, start) {
|
|
1242
|
+
let sig = "";
|
|
1243
|
+
let i = start;
|
|
1244
|
+
while (i < lines.length) {
|
|
1245
|
+
const line = lines[i].trim();
|
|
1246
|
+
sig += (sig ? " " : "") + line;
|
|
1247
|
+
if (line.includes("{")) {
|
|
1248
|
+
sig = sig.replace(/\s*\{[^]*$/, "").trim();
|
|
1249
|
+
break;
|
|
1250
|
+
}
|
|
1251
|
+
i++;
|
|
1252
|
+
}
|
|
1253
|
+
return sig;
|
|
1254
|
+
}
|
|
1255
|
+
function skipBlock(lines, start) {
|
|
1256
|
+
let depth = 0;
|
|
1257
|
+
let i = start;
|
|
1258
|
+
let foundBrace = false;
|
|
1259
|
+
while (i < lines.length) {
|
|
1260
|
+
for (const ch of lines[i]) {
|
|
1261
|
+
if (ch === "{") {
|
|
1262
|
+
depth++;
|
|
1263
|
+
foundBrace = true;
|
|
1264
|
+
}
|
|
1265
|
+
if (ch === "}") depth--;
|
|
1266
|
+
}
|
|
1267
|
+
i++;
|
|
1268
|
+
if (foundBrace && depth <= 0) break;
|
|
1269
|
+
if (!foundBrace && lines[i - 1].includes(";")) break;
|
|
1270
|
+
}
|
|
1271
|
+
return i;
|
|
1272
|
+
}
|
|
1273
|
+
function looksLikeFunctionDecl(lines, start) {
|
|
1274
|
+
const chunk = lines.slice(start, Math.min(start + 5, lines.length)).join(" ");
|
|
1275
|
+
return /=>/.test(chunk) || /=\s*function/.test(chunk);
|
|
1276
|
+
}
|
|
1277
|
+
function extractClassOutline(lines, start) {
|
|
1278
|
+
const header = lines[start].trim();
|
|
1279
|
+
let headerText = header;
|
|
1280
|
+
let i = start + 1;
|
|
1281
|
+
if (!header.includes("{")) {
|
|
1282
|
+
while (i < lines.length) {
|
|
1283
|
+
headerText += " " + lines[i].trim();
|
|
1284
|
+
if (lines[i].includes("{")) {
|
|
1285
|
+
i++;
|
|
1286
|
+
break;
|
|
1287
|
+
}
|
|
1288
|
+
i++;
|
|
1289
|
+
}
|
|
1290
|
+
} else {
|
|
1291
|
+
i = start + 1;
|
|
1292
|
+
}
|
|
1293
|
+
const bodyParts = [headerText.replace(/\{[^]*$/, "{").trim()];
|
|
1294
|
+
let depth = 1;
|
|
1295
|
+
while (i < lines.length && depth > 0) {
|
|
1296
|
+
const line = lines[i];
|
|
1297
|
+
const trimmed = line.trim();
|
|
1298
|
+
for (const ch of line) {
|
|
1299
|
+
if (ch === "{") depth++;
|
|
1300
|
+
if (ch === "}") depth--;
|
|
1301
|
+
}
|
|
1302
|
+
if (depth <= 0) {
|
|
1303
|
+
i++;
|
|
1304
|
+
break;
|
|
1305
|
+
}
|
|
1306
|
+
if (depth === 1) {
|
|
1307
|
+
if (/^(private|protected|public|readonly|static|#)/.test(trimmed) && !trimmed.includes("(")) {
|
|
1308
|
+
bodyParts.push(` ${trimmed}`);
|
|
1309
|
+
} else if (/^constructor\s*\(/.test(trimmed)) {
|
|
1310
|
+
const sig = extractFnSignature(lines, i);
|
|
1311
|
+
bodyParts.push(` ${sig} { /* ... */ }`);
|
|
1312
|
+
} else if (/^(?:static\s+)?(?:async\s+)?(?:get\s+|set\s+)?\w+\s*[(<]/.test(trimmed) && !trimmed.startsWith("//")) {
|
|
1313
|
+
const sig = extractFnSignature(lines, i);
|
|
1314
|
+
bodyParts.push(` ${sig} { /* ... */ }`);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
i++;
|
|
1318
|
+
}
|
|
1319
|
+
bodyParts.push("}");
|
|
1320
|
+
return { text: bodyParts.join("\n"), nextIndex: i };
|
|
1321
|
+
}
|
|
1187
1322
|
async function pruneGeneric(file, level) {
|
|
1188
1323
|
let content;
|
|
1189
1324
|
try {
|
|
@@ -1244,22 +1379,6 @@ function emptyResult(file, level) {
|
|
|
1244
1379
|
savingsPercent: 100
|
|
1245
1380
|
};
|
|
1246
1381
|
}
|
|
1247
|
-
function addJSDoc(node, parts) {
|
|
1248
|
-
if (!node.getJsDocs) return;
|
|
1249
|
-
const docs = node.getJsDocs();
|
|
1250
|
-
if (docs.length > 0) {
|
|
1251
|
-
parts.push(docs[0].getText());
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
function findTsConfig(filePath) {
|
|
1255
|
-
let dir = filePath;
|
|
1256
|
-
for (let i = 0; i < 10; i++) {
|
|
1257
|
-
dir = join5(dir, "..");
|
|
1258
|
-
const candidate = join5(dir, "tsconfig.json");
|
|
1259
|
-
if (existsSync3(candidate)) return candidate;
|
|
1260
|
-
}
|
|
1261
|
-
return void 0;
|
|
1262
|
-
}
|
|
1263
1382
|
|
|
1264
1383
|
// src/engine/graph-utils.ts
|
|
1265
1384
|
function buildAdjacencyList(edges) {
|
|
@@ -1952,7 +2071,7 @@ function computeStrategyScore(strategy, budget) {
|
|
|
1952
2071
|
// src/api/dashboard.ts
|
|
1953
2072
|
async function loadHistory(ctoDir) {
|
|
1954
2073
|
try {
|
|
1955
|
-
const raw = await readFile5(
|
|
2074
|
+
const raw = await readFile5(join5(ctoDir, "history.json"), "utf-8");
|
|
1956
2075
|
return JSON.parse(raw);
|
|
1957
2076
|
} catch {
|
|
1958
2077
|
return [];
|
|
@@ -1960,11 +2079,11 @@ async function loadHistory(ctoDir) {
|
|
|
1960
2079
|
}
|
|
1961
2080
|
async function saveHistory(ctoDir, history) {
|
|
1962
2081
|
await mkdir(ctoDir, { recursive: true });
|
|
1963
|
-
await writeFile2(
|
|
2082
|
+
await writeFile2(join5(ctoDir, "history.json"), JSON.stringify(history, null, 2));
|
|
1964
2083
|
}
|
|
1965
2084
|
async function generateDashboard(projectPath, task = "general code review and refactoring", budget = 5e4) {
|
|
1966
2085
|
const absPath = resolve5(projectPath);
|
|
1967
|
-
const ctoDir =
|
|
2086
|
+
const ctoDir = join5(absPath, ".cto");
|
|
1968
2087
|
const analysis = await getCachedAnalysis(absPath);
|
|
1969
2088
|
const score = await computeContextScore(analysis, task, budget);
|
|
1970
2089
|
const benchmark = await runBenchmark(analysis, task, budget);
|
|
@@ -1994,7 +2113,7 @@ async function generateDashboard(projectPath, task = "general code review and re
|
|
|
1994
2113
|
generatedAt: /* @__PURE__ */ new Date()
|
|
1995
2114
|
};
|
|
1996
2115
|
const html = renderDashboardHTML(data);
|
|
1997
|
-
const htmlPath =
|
|
2116
|
+
const htmlPath = join5(ctoDir, "dashboard.html");
|
|
1998
2117
|
await mkdir(ctoDir, { recursive: true });
|
|
1999
2118
|
await writeFile2(htmlPath, html, "utf-8");
|
|
2000
2119
|
return { htmlPath, data };
|