cto-ai-cli 4.0.0 → 5.0.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.
@@ -24769,10 +24769,7 @@ async function auditProject(projectPath, filePaths, options = {}) {
24769
24769
  }
24770
24770
 
24771
24771
  // src/engine/pruner.ts
24772
- import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
24773
24772
  import { readFile as readFile4 } from "fs/promises";
24774
- import { existsSync as existsSync5 } from "fs";
24775
- import { join as join4 } from "path";
24776
24773
  var TS_EXTENSIONS2 = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx", "mts", "mjs"]);
24777
24774
  async function pruneFile(file, level) {
24778
24775
  if (level === "excluded") {
@@ -24795,23 +24792,7 @@ async function pruneTypeScript(file, level) {
24795
24792
  } catch {
24796
24793
  return emptyResult(file, level);
24797
24794
  }
24798
- let project;
24799
- try {
24800
- const tsConfigPath = findTsConfig(file.path);
24801
- project = new Project2({
24802
- tsConfigFilePath: tsConfigPath,
24803
- skipAddingFilesFromTsConfig: true,
24804
- compilerOptions: tsConfigPath ? void 0 : { allowJs: true, esModuleInterop: true }
24805
- });
24806
- project.createSourceFile(file.path, content, { overwrite: true });
24807
- } catch {
24808
- return pruneGenericFromContent(file, content, level);
24809
- }
24810
- const sourceFile = project.getSourceFiles()[0];
24811
- if (!sourceFile) {
24812
- return pruneGenericFromContent(file, content, level);
24813
- }
24814
- const prunedContent = level === "signatures" ? extractSignaturesAST(sourceFile) : extractSkeletonAST(sourceFile);
24795
+ const prunedContent = level === "signatures" ? extractSignaturesRegex(content) : extractSkeletonRegex(content);
24815
24796
  const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, "utf-8"));
24816
24797
  const savingsPercent = file.tokens > 0 ? (file.tokens - prunedTokens) / file.tokens * 100 : 0;
24817
24798
  return {
@@ -24823,131 +24804,281 @@ async function pruneTypeScript(file, level) {
24823
24804
  savingsPercent: Math.max(0, savingsPercent)
24824
24805
  };
24825
24806
  }
24826
- function extractSignaturesAST(sf) {
24807
+ function extractSignaturesRegex(content) {
24808
+ const lines = content.split("\n");
24827
24809
  const parts = [];
24828
- for (const imp of sf.getImportDeclarations()) {
24829
- parts.push(imp.getText());
24830
- }
24831
- if (parts.length > 0) parts.push("");
24832
- for (const ta of sf.getTypeAliases()) {
24833
- addJSDoc(ta, parts);
24834
- parts.push(ta.getText());
24835
- }
24836
- for (const iface of sf.getInterfaces()) {
24837
- addJSDoc(iface, parts);
24838
- parts.push(iface.getText());
24839
- }
24840
- for (const en of sf.getEnums()) {
24841
- addJSDoc(en, parts);
24842
- parts.push(en.getText());
24843
- }
24844
- for (const fn of sf.getFunctions()) {
24845
- addJSDoc(fn, parts);
24846
- const isExported = fn.isExported();
24847
- const isAsync = fn.isAsync();
24848
- const name = fn.getName() ?? "<anonymous>";
24849
- const params = fn.getParameters().map((p) => p.getText()).join(", ");
24850
- const returnType = fn.getReturnTypeNode()?.getText();
24851
- const returnStr = returnType ? `: ${returnType}` : "";
24852
- const prefix = isExported ? "export " : "";
24853
- const asyncStr = isAsync ? "async " : "";
24854
- parts.push(`${prefix}${asyncStr}function ${name}(${params})${returnStr} { /* ... */ }`);
24855
- }
24856
- for (const stmt of sf.getVariableStatements()) {
24857
- for (const decl of stmt.getDeclarations()) {
24858
- const init = decl.getInitializer();
24859
- if (init && (init.getKind() === SyntaxKind2.ArrowFunction || init.getKind() === SyntaxKind2.FunctionExpression)) {
24860
- addJSDoc(stmt, parts);
24861
- const isExported = stmt.isExported();
24862
- const prefix = isExported ? "export " : "";
24863
- const kind = stmt.getDeclarationKind();
24864
- const name = decl.getName();
24865
- const typeNode = decl.getTypeNode()?.getText();
24866
- const typeStr = typeNode ? `: ${typeNode}` : "";
24867
- parts.push(`${prefix}${kind} ${name}${typeStr} = /* ... */;`);
24868
- } else {
24869
- addJSDoc(stmt, parts);
24870
- parts.push(stmt.getText());
24871
- }
24872
- }
24873
- }
24874
- for (const cls of sf.getClasses()) {
24875
- addJSDoc(cls, parts);
24876
- const isExported = cls.isExported();
24877
- const prefix = isExported ? "export " : "";
24878
- const name = cls.getName() ?? "<anonymous>";
24879
- const ext = cls.getExtends()?.getText();
24880
- const impl = cls.getImplements().map((i) => i.getText()).join(", ");
24881
- let header = `${prefix}class ${name}`;
24882
- if (ext) header += ` extends ${ext}`;
24883
- if (impl) header += ` implements ${impl}`;
24884
- header += " {";
24885
- parts.push(header);
24886
- for (const prop of cls.getProperties()) {
24887
- parts.push(` ${prop.getText()}`);
24888
- }
24889
- const ctor = cls.getConstructors()[0];
24890
- if (ctor) {
24891
- const ctorParams = ctor.getParameters().map((p) => p.getText()).join(", ");
24892
- parts.push(` constructor(${ctorParams}) { /* ... */ }`);
24893
- }
24894
- for (const method of cls.getMethods()) {
24895
- const isStatic = method.isStatic();
24896
- const isAsync = method.isAsync();
24897
- const methodName = method.getName();
24898
- const methodParams = method.getParameters().map((p) => p.getText()).join(", ");
24899
- const returnType = method.getReturnTypeNode()?.getText();
24900
- const returnStr = returnType ? `: ${returnType}` : "";
24901
- const staticStr = isStatic ? "static " : "";
24902
- const asyncStr = isAsync ? "async " : "";
24903
- parts.push(` ${staticStr}${asyncStr}${methodName}(${methodParams})${returnStr} { /* ... */ }`);
24904
- }
24905
- parts.push("}");
24906
- }
24907
- for (const exp of sf.getExportDeclarations()) {
24908
- parts.push(exp.getText());
24909
- }
24910
- for (const exp of sf.getExportAssignments()) {
24911
- parts.push(exp.getText());
24810
+ let i = 0;
24811
+ while (i < lines.length) {
24812
+ const line = lines[i];
24813
+ const trimmed = line.trim();
24814
+ if (trimmed === "") {
24815
+ i++;
24816
+ continue;
24817
+ }
24818
+ if (trimmed.startsWith("/**")) {
24819
+ const docLines = [];
24820
+ while (i < lines.length) {
24821
+ docLines.push(lines[i]);
24822
+ if (lines[i].includes("*/")) {
24823
+ i++;
24824
+ break;
24825
+ }
24826
+ i++;
24827
+ }
24828
+ parts.push(docLines.join("\n"));
24829
+ continue;
24830
+ }
24831
+ if (trimmed.startsWith("//")) {
24832
+ parts.push(line);
24833
+ i++;
24834
+ continue;
24835
+ }
24836
+ if (/^\s*(import|export)\s/.test(line) && (trimmed.includes(" from ") || trimmed.startsWith("import "))) {
24837
+ const block = collectBracedLine(lines, i);
24838
+ parts.push(block.text);
24839
+ i = block.nextIndex;
24840
+ continue;
24841
+ }
24842
+ if (/^\s*export\s*(\{|\*)/.test(trimmed)) {
24843
+ const block = collectBracedLine(lines, i);
24844
+ parts.push(block.text);
24845
+ i = block.nextIndex;
24846
+ continue;
24847
+ }
24848
+ if (/^\s*(export\s+)?type\s+\w/.test(trimmed) && !trimmed.startsWith("typeof")) {
24849
+ const block = collectBalanced(lines, i);
24850
+ parts.push(block.text);
24851
+ i = block.nextIndex;
24852
+ continue;
24853
+ }
24854
+ if (/^\s*(export\s+)?interface\s+\w/.test(trimmed)) {
24855
+ const block = collectBalanced(lines, i);
24856
+ parts.push(block.text);
24857
+ i = block.nextIndex;
24858
+ continue;
24859
+ }
24860
+ if (/^\s*(export\s+)?(const\s+)?enum\s+\w/.test(trimmed)) {
24861
+ const block = collectBalanced(lines, i);
24862
+ parts.push(block.text);
24863
+ i = block.nextIndex;
24864
+ continue;
24865
+ }
24866
+ const fnMatch = trimmed.match(/^(export\s+)?(async\s+)?function\s+(\w+)/);
24867
+ if (fnMatch) {
24868
+ const sig = extractFnSignature(lines, i);
24869
+ parts.push(`${sig} { /* ... */ }`);
24870
+ i = skipBlock(lines, i);
24871
+ continue;
24872
+ }
24873
+ const arrowMatch = trimmed.match(/^(export\s+)?(const|let|var)\s+(\w+)/);
24874
+ if (arrowMatch && looksLikeFunctionDecl(lines, i)) {
24875
+ const prefix = trimmed.match(/^((?:export\s+)?(?:const|let|var)\s+\w+[^=]*=)/)?.[1];
24876
+ if (prefix) {
24877
+ parts.push(`${prefix} /* ... */;`);
24878
+ }
24879
+ i = skipBlock(lines, i);
24880
+ continue;
24881
+ }
24882
+ if (arrowMatch) {
24883
+ const block = collectStatement(lines, i);
24884
+ parts.push(block.text);
24885
+ i = block.nextIndex;
24886
+ continue;
24887
+ }
24888
+ if (/^\s*(export\s+)?(abstract\s+)?class\s+\w/.test(trimmed)) {
24889
+ const classOutline = extractClassOutline(lines, i);
24890
+ parts.push(classOutline.text);
24891
+ i = classOutline.nextIndex;
24892
+ continue;
24893
+ }
24894
+ i++;
24912
24895
  }
24913
24896
  return parts.join("\n");
24914
24897
  }
24915
- function extractSkeletonAST(sf) {
24898
+ function extractSkeletonRegex(content) {
24899
+ const lines = content.split("\n");
24916
24900
  const parts = [];
24917
- for (const imp of sf.getImportDeclarations()) {
24918
- parts.push(imp.getText());
24919
- }
24920
- if (parts.length > 0) parts.push("");
24921
- for (const ta of sf.getTypeAliases()) {
24922
- if (ta.isExported()) parts.push(ta.getText());
24923
- }
24924
- for (const iface of sf.getInterfaces()) {
24925
- if (!iface.isExported()) continue;
24926
- const ext = iface.getExtends().map((e) => e.getText());
24927
- const extStr = ext.length > 0 ? ` extends ${ext.join(", ")}` : "";
24928
- parts.push(`export interface ${iface.getName()}${extStr} { /* ${iface.getProperties().length} props */ }`);
24929
- }
24930
- for (const en of sf.getEnums()) {
24931
- if (!en.isExported()) continue;
24932
- const members = en.getMembers().map((m) => m.getName());
24933
- parts.push(`export enum ${en.getName()} { ${members.join(", ")} }`);
24934
- }
24935
- for (const fn of sf.getFunctions()) {
24936
- if (!fn.isExported()) continue;
24937
- const name = fn.getName() ?? "<anonymous>";
24938
- const params = fn.getParameters().map((p) => p.getText()).join(", ");
24939
- parts.push(`export function ${name}(${params});`);
24940
- }
24941
- for (const cls of sf.getClasses()) {
24942
- if (!cls.isExported()) continue;
24943
- const methods = cls.getMethods().map((m) => m.getName());
24944
- parts.push(`export class ${cls.getName()} { /* methods: ${methods.join(", ")} */ }`);
24945
- }
24946
- for (const exp of sf.getExportDeclarations()) {
24947
- parts.push(exp.getText());
24901
+ let i = 0;
24902
+ while (i < lines.length) {
24903
+ const trimmed = lines[i].trim();
24904
+ if (/^import\s/.test(trimmed)) {
24905
+ const block = collectBracedLine(lines, i);
24906
+ parts.push(block.text);
24907
+ i = block.nextIndex;
24908
+ continue;
24909
+ }
24910
+ if (/^export\s+(type|interface)\s+\w/.test(trimmed)) {
24911
+ const block = collectBalanced(lines, i);
24912
+ parts.push(block.text);
24913
+ i = block.nextIndex;
24914
+ continue;
24915
+ }
24916
+ if (/^export\s+(const\s+)?enum\s+\w/.test(trimmed)) {
24917
+ const block = collectBalanced(lines, i);
24918
+ parts.push(block.text);
24919
+ i = block.nextIndex;
24920
+ continue;
24921
+ }
24922
+ if (/^export\s+(async\s+)?function\s+\w/.test(trimmed)) {
24923
+ const sig = extractFnSignature(lines, i);
24924
+ parts.push(`${sig};`);
24925
+ i = skipBlock(lines, i);
24926
+ continue;
24927
+ }
24928
+ if (/^export\s+(abstract\s+)?class\s+/.test(trimmed)) {
24929
+ const nameMatch = trimmed.match(/class\s+(\w+)/);
24930
+ const name = nameMatch?.[1] ?? "Unknown";
24931
+ const end = skipBlock(lines, i);
24932
+ const methods = [];
24933
+ for (let j = i + 1; j < end; j++) {
24934
+ const mt = lines[j].trim();
24935
+ const mm = mt.match(/^(?:static\s+)?(?:async\s+)?(\w+)\s*\(/);
24936
+ if (mm && mm[1] !== "constructor") methods.push(mm[1]);
24937
+ }
24938
+ parts.push(`export class ${name} { /* methods: ${methods.join(", ")} */ }`);
24939
+ i = end;
24940
+ continue;
24941
+ }
24942
+ if (/^export\s*(\{|\*)/.test(trimmed)) {
24943
+ const block = collectBracedLine(lines, i);
24944
+ parts.push(block.text);
24945
+ i = block.nextIndex;
24946
+ continue;
24947
+ }
24948
+ i++;
24948
24949
  }
24949
24950
  return parts.join("\n");
24950
24951
  }
24952
+ function collectBracedLine(lines, start) {
24953
+ let text = lines[start];
24954
+ let i = start + 1;
24955
+ while (i < lines.length && !text.includes(";") && !text.trimEnd().endsWith("'") && !text.trimEnd().endsWith('"')) {
24956
+ text += "\n" + lines[i];
24957
+ i++;
24958
+ }
24959
+ return { text, nextIndex: i };
24960
+ }
24961
+ function collectBalanced(lines, start) {
24962
+ let depth = 0;
24963
+ let text = "";
24964
+ let i = start;
24965
+ let started = false;
24966
+ while (i < lines.length) {
24967
+ const line = lines[i];
24968
+ text += (text ? "\n" : "") + line;
24969
+ for (const ch of line) {
24970
+ if (ch === "{" || ch === "(") {
24971
+ depth++;
24972
+ started = true;
24973
+ }
24974
+ if (ch === "}" || ch === ")") depth--;
24975
+ }
24976
+ i++;
24977
+ if (started && depth <= 0) break;
24978
+ if (!started && line.includes(";")) break;
24979
+ }
24980
+ return { text, nextIndex: i };
24981
+ }
24982
+ function collectStatement(lines, start) {
24983
+ let text = lines[start];
24984
+ let i = start + 1;
24985
+ if (text.includes(";")) return { text, nextIndex: i };
24986
+ let depth = 0;
24987
+ for (const ch of text) {
24988
+ if (ch === "{" || ch === "(" || ch === "[") depth++;
24989
+ if (ch === "}" || ch === ")" || ch === "]") depth--;
24990
+ }
24991
+ while (i < lines.length && depth > 0) {
24992
+ text += "\n" + lines[i];
24993
+ for (const ch of lines[i]) {
24994
+ if (ch === "{" || ch === "(" || ch === "[") depth++;
24995
+ if (ch === "}" || ch === ")" || ch === "]") depth--;
24996
+ }
24997
+ i++;
24998
+ }
24999
+ return { text, nextIndex: i };
25000
+ }
25001
+ function extractFnSignature(lines, start) {
25002
+ let sig = "";
25003
+ let i = start;
25004
+ while (i < lines.length) {
25005
+ const line = lines[i].trim();
25006
+ sig += (sig ? " " : "") + line;
25007
+ if (line.includes("{")) {
25008
+ sig = sig.replace(/\s*\{[^]*$/, "").trim();
25009
+ break;
25010
+ }
25011
+ i++;
25012
+ }
25013
+ return sig;
25014
+ }
25015
+ function skipBlock(lines, start) {
25016
+ let depth = 0;
25017
+ let i = start;
25018
+ let foundBrace = false;
25019
+ while (i < lines.length) {
25020
+ for (const ch of lines[i]) {
25021
+ if (ch === "{") {
25022
+ depth++;
25023
+ foundBrace = true;
25024
+ }
25025
+ if (ch === "}") depth--;
25026
+ }
25027
+ i++;
25028
+ if (foundBrace && depth <= 0) break;
25029
+ if (!foundBrace && lines[i - 1].includes(";")) break;
25030
+ }
25031
+ return i;
25032
+ }
25033
+ function looksLikeFunctionDecl(lines, start) {
25034
+ const chunk = lines.slice(start, Math.min(start + 5, lines.length)).join(" ");
25035
+ return /=>/.test(chunk) || /=\s*function/.test(chunk);
25036
+ }
25037
+ function extractClassOutline(lines, start) {
25038
+ const header = lines[start].trim();
25039
+ let headerText = header;
25040
+ let i = start + 1;
25041
+ if (!header.includes("{")) {
25042
+ while (i < lines.length) {
25043
+ headerText += " " + lines[i].trim();
25044
+ if (lines[i].includes("{")) {
25045
+ i++;
25046
+ break;
25047
+ }
25048
+ i++;
25049
+ }
25050
+ } else {
25051
+ i = start + 1;
25052
+ }
25053
+ const bodyParts = [headerText.replace(/\{[^]*$/, "{").trim()];
25054
+ let depth = 1;
25055
+ while (i < lines.length && depth > 0) {
25056
+ const line = lines[i];
25057
+ const trimmed = line.trim();
25058
+ for (const ch of line) {
25059
+ if (ch === "{") depth++;
25060
+ if (ch === "}") depth--;
25061
+ }
25062
+ if (depth <= 0) {
25063
+ i++;
25064
+ break;
25065
+ }
25066
+ if (depth === 1) {
25067
+ if (/^(private|protected|public|readonly|static|#)/.test(trimmed) && !trimmed.includes("(")) {
25068
+ bodyParts.push(` ${trimmed}`);
25069
+ } else if (/^constructor\s*\(/.test(trimmed)) {
25070
+ const sig = extractFnSignature(lines, i);
25071
+ bodyParts.push(` ${sig} { /* ... */ }`);
25072
+ } else if (/^(?:static\s+)?(?:async\s+)?(?:get\s+|set\s+)?\w+\s*[(<]/.test(trimmed) && !trimmed.startsWith("//")) {
25073
+ const sig = extractFnSignature(lines, i);
25074
+ bodyParts.push(` ${sig} { /* ... */ }`);
25075
+ }
25076
+ }
25077
+ i++;
25078
+ }
25079
+ bodyParts.push("}");
25080
+ return { text: bodyParts.join("\n"), nextIndex: i };
25081
+ }
24951
25082
  async function pruneGeneric(file, level) {
24952
25083
  let content;
24953
25084
  try {
@@ -25008,22 +25139,6 @@ function emptyResult(file, level) {
25008
25139
  savingsPercent: 100
25009
25140
  };
25010
25141
  }
25011
- function addJSDoc(node, parts) {
25012
- if (!node.getJsDocs) return;
25013
- const docs = node.getJsDocs();
25014
- if (docs.length > 0) {
25015
- parts.push(docs[0].getText());
25016
- }
25017
- }
25018
- function findTsConfig(filePath) {
25019
- let dir = filePath;
25020
- for (let i = 0; i < 10; i++) {
25021
- dir = join4(dir, "..");
25022
- const candidate = join4(dir, "tsconfig.json");
25023
- if (existsSync5(candidate)) return candidate;
25024
- }
25025
- return void 0;
25026
- }
25027
25142
 
25028
25143
  // src/engine/graph-utils.ts
25029
25144
  function buildAdjacencyList(edges) {
@@ -25934,7 +26049,7 @@ function emptyResult2(baseBranch) {
25934
26049
  // src/engine/quality-gate.ts
25935
26050
  import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
25936
26051
  import { resolve as resolve5 } from "path";
25937
- import { existsSync as existsSync6 } from "fs";
26052
+ import { existsSync as existsSync5 } from "fs";
25938
26053
  var DEFAULT_GATE_CONFIG = {
25939
26054
  threshold: 70,
25940
26055
  failOnSecrets: true,
@@ -25945,7 +26060,7 @@ var DEFAULT_GATE_CONFIG = {
25945
26060
  };
25946
26061
  async function loadBaseline(projectPath, baselinePath) {
25947
26062
  const filePath = resolve5(projectPath, baselinePath || ".cto/baseline.json");
25948
- if (!existsSync6(filePath)) return null;
26063
+ if (!existsSync5(filePath)) return null;
25949
26064
  try {
25950
26065
  const content = await readFile5(filePath, "utf-8");
25951
26066
  return JSON.parse(content);
@@ -25955,7 +26070,7 @@ async function loadBaseline(projectPath, baselinePath) {
25955
26070
  }
25956
26071
  async function saveBaseline(projectPath, score, commit, branch, baselinePath) {
25957
26072
  const dir = resolve5(projectPath, ".cto");
25958
- if (!existsSync6(dir)) await mkdir2(dir, { recursive: true });
26073
+ if (!existsSync5(dir)) await mkdir2(dir, { recursive: true });
25959
26074
  const baseline = {
25960
26075
  score: score.overall,
25961
26076
  grade: score.grade,