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/cli/v2/index.js
CHANGED
|
@@ -935,7 +935,7 @@ var analyzeCommand = new Command2("analyze").description("Analyze project struct
|
|
|
935
935
|
// src/cli/v2/interact.ts
|
|
936
936
|
import { Command as Command3 } from "commander";
|
|
937
937
|
import chalk3 from "chalk";
|
|
938
|
-
import { resolve as resolve8, join as
|
|
938
|
+
import { resolve as resolve8, join as join8 } from "path";
|
|
939
939
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
940
940
|
|
|
941
941
|
// src/engine/cache.ts
|
|
@@ -1512,10 +1512,7 @@ function deduplicateFindings(findings) {
|
|
|
1512
1512
|
}
|
|
1513
1513
|
|
|
1514
1514
|
// src/engine/pruner.ts
|
|
1515
|
-
import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
1516
1515
|
import { readFile as readFile5 } from "fs/promises";
|
|
1517
|
-
import { existsSync as existsSync4 } from "fs";
|
|
1518
|
-
import { join as join7 } from "path";
|
|
1519
1516
|
var TS_EXTENSIONS2 = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx", "mts", "mjs"]);
|
|
1520
1517
|
async function pruneFile(file, level) {
|
|
1521
1518
|
if (level === "excluded") {
|
|
@@ -1538,23 +1535,7 @@ async function pruneTypeScript(file, level) {
|
|
|
1538
1535
|
} catch {
|
|
1539
1536
|
return emptyResult2(file, level);
|
|
1540
1537
|
}
|
|
1541
|
-
|
|
1542
|
-
try {
|
|
1543
|
-
const tsConfigPath = findTsConfig(file.path);
|
|
1544
|
-
project = new Project2({
|
|
1545
|
-
tsConfigFilePath: tsConfigPath,
|
|
1546
|
-
skipAddingFilesFromTsConfig: true,
|
|
1547
|
-
compilerOptions: tsConfigPath ? void 0 : { allowJs: true, esModuleInterop: true }
|
|
1548
|
-
});
|
|
1549
|
-
project.createSourceFile(file.path, content, { overwrite: true });
|
|
1550
|
-
} catch {
|
|
1551
|
-
return pruneGenericFromContent(file, content, level);
|
|
1552
|
-
}
|
|
1553
|
-
const sourceFile = project.getSourceFiles()[0];
|
|
1554
|
-
if (!sourceFile) {
|
|
1555
|
-
return pruneGenericFromContent(file, content, level);
|
|
1556
|
-
}
|
|
1557
|
-
const prunedContent = level === "signatures" ? extractSignaturesAST(sourceFile) : extractSkeletonAST(sourceFile);
|
|
1538
|
+
const prunedContent = level === "signatures" ? extractSignaturesRegex(content) : extractSkeletonRegex(content);
|
|
1558
1539
|
const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, "utf-8"));
|
|
1559
1540
|
const savingsPercent = file.tokens > 0 ? (file.tokens - prunedTokens) / file.tokens * 100 : 0;
|
|
1560
1541
|
return {
|
|
@@ -1566,131 +1547,281 @@ async function pruneTypeScript(file, level) {
|
|
|
1566
1547
|
savingsPercent: Math.max(0, savingsPercent)
|
|
1567
1548
|
};
|
|
1568
1549
|
}
|
|
1569
|
-
function
|
|
1550
|
+
function extractSignaturesRegex(content) {
|
|
1551
|
+
const lines = content.split("\n");
|
|
1570
1552
|
const parts = [];
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
parts.push(
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
const
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1553
|
+
let i = 0;
|
|
1554
|
+
while (i < lines.length) {
|
|
1555
|
+
const line = lines[i];
|
|
1556
|
+
const trimmed = line.trim();
|
|
1557
|
+
if (trimmed === "") {
|
|
1558
|
+
i++;
|
|
1559
|
+
continue;
|
|
1560
|
+
}
|
|
1561
|
+
if (trimmed.startsWith("/**")) {
|
|
1562
|
+
const docLines = [];
|
|
1563
|
+
while (i < lines.length) {
|
|
1564
|
+
docLines.push(lines[i]);
|
|
1565
|
+
if (lines[i].includes("*/")) {
|
|
1566
|
+
i++;
|
|
1567
|
+
break;
|
|
1568
|
+
}
|
|
1569
|
+
i++;
|
|
1570
|
+
}
|
|
1571
|
+
parts.push(docLines.join("\n"));
|
|
1572
|
+
continue;
|
|
1573
|
+
}
|
|
1574
|
+
if (trimmed.startsWith("//")) {
|
|
1575
|
+
parts.push(line);
|
|
1576
|
+
i++;
|
|
1577
|
+
continue;
|
|
1578
|
+
}
|
|
1579
|
+
if (/^\s*(import|export)\s/.test(line) && (trimmed.includes(" from ") || trimmed.startsWith("import "))) {
|
|
1580
|
+
const block = collectBracedLine(lines, i);
|
|
1581
|
+
parts.push(block.text);
|
|
1582
|
+
i = block.nextIndex;
|
|
1583
|
+
continue;
|
|
1584
|
+
}
|
|
1585
|
+
if (/^\s*export\s*(\{|\*)/.test(trimmed)) {
|
|
1586
|
+
const block = collectBracedLine(lines, i);
|
|
1587
|
+
parts.push(block.text);
|
|
1588
|
+
i = block.nextIndex;
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
if (/^\s*(export\s+)?type\s+\w/.test(trimmed) && !trimmed.startsWith("typeof")) {
|
|
1592
|
+
const block = collectBalanced(lines, i);
|
|
1593
|
+
parts.push(block.text);
|
|
1594
|
+
i = block.nextIndex;
|
|
1595
|
+
continue;
|
|
1596
|
+
}
|
|
1597
|
+
if (/^\s*(export\s+)?interface\s+\w/.test(trimmed)) {
|
|
1598
|
+
const block = collectBalanced(lines, i);
|
|
1599
|
+
parts.push(block.text);
|
|
1600
|
+
i = block.nextIndex;
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
if (/^\s*(export\s+)?(const\s+)?enum\s+\w/.test(trimmed)) {
|
|
1604
|
+
const block = collectBalanced(lines, i);
|
|
1605
|
+
parts.push(block.text);
|
|
1606
|
+
i = block.nextIndex;
|
|
1607
|
+
continue;
|
|
1608
|
+
}
|
|
1609
|
+
const fnMatch = trimmed.match(/^(export\s+)?(async\s+)?function\s+(\w+)/);
|
|
1610
|
+
if (fnMatch) {
|
|
1611
|
+
const sig = extractFnSignature(lines, i);
|
|
1612
|
+
parts.push(`${sig} { /* ... */ }`);
|
|
1613
|
+
i = skipBlock(lines, i);
|
|
1614
|
+
continue;
|
|
1615
|
+
}
|
|
1616
|
+
const arrowMatch = trimmed.match(/^(export\s+)?(const|let|var)\s+(\w+)/);
|
|
1617
|
+
if (arrowMatch && looksLikeFunctionDecl(lines, i)) {
|
|
1618
|
+
const prefix = trimmed.match(/^((?:export\s+)?(?:const|let|var)\s+\w+[^=]*=)/)?.[1];
|
|
1619
|
+
if (prefix) {
|
|
1620
|
+
parts.push(`${prefix} /* ... */;`);
|
|
1621
|
+
}
|
|
1622
|
+
i = skipBlock(lines, i);
|
|
1623
|
+
continue;
|
|
1624
|
+
}
|
|
1625
|
+
if (arrowMatch) {
|
|
1626
|
+
const block = collectStatement(lines, i);
|
|
1627
|
+
parts.push(block.text);
|
|
1628
|
+
i = block.nextIndex;
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
if (/^\s*(export\s+)?(abstract\s+)?class\s+\w/.test(trimmed)) {
|
|
1632
|
+
const classOutline = extractClassOutline(lines, i);
|
|
1633
|
+
parts.push(classOutline.text);
|
|
1634
|
+
i = classOutline.nextIndex;
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
i++;
|
|
1655
1638
|
}
|
|
1656
1639
|
return parts.join("\n");
|
|
1657
1640
|
}
|
|
1658
|
-
function
|
|
1641
|
+
function extractSkeletonRegex(content) {
|
|
1642
|
+
const lines = content.split("\n");
|
|
1659
1643
|
const parts = [];
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1644
|
+
let i = 0;
|
|
1645
|
+
while (i < lines.length) {
|
|
1646
|
+
const trimmed = lines[i].trim();
|
|
1647
|
+
if (/^import\s/.test(trimmed)) {
|
|
1648
|
+
const block = collectBracedLine(lines, i);
|
|
1649
|
+
parts.push(block.text);
|
|
1650
|
+
i = block.nextIndex;
|
|
1651
|
+
continue;
|
|
1652
|
+
}
|
|
1653
|
+
if (/^export\s+(type|interface)\s+\w/.test(trimmed)) {
|
|
1654
|
+
const block = collectBalanced(lines, i);
|
|
1655
|
+
parts.push(block.text);
|
|
1656
|
+
i = block.nextIndex;
|
|
1657
|
+
continue;
|
|
1658
|
+
}
|
|
1659
|
+
if (/^export\s+(const\s+)?enum\s+\w/.test(trimmed)) {
|
|
1660
|
+
const block = collectBalanced(lines, i);
|
|
1661
|
+
parts.push(block.text);
|
|
1662
|
+
i = block.nextIndex;
|
|
1663
|
+
continue;
|
|
1664
|
+
}
|
|
1665
|
+
if (/^export\s+(async\s+)?function\s+\w/.test(trimmed)) {
|
|
1666
|
+
const sig = extractFnSignature(lines, i);
|
|
1667
|
+
parts.push(`${sig};`);
|
|
1668
|
+
i = skipBlock(lines, i);
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
if (/^export\s+(abstract\s+)?class\s+/.test(trimmed)) {
|
|
1672
|
+
const nameMatch = trimmed.match(/class\s+(\w+)/);
|
|
1673
|
+
const name = nameMatch?.[1] ?? "Unknown";
|
|
1674
|
+
const end = skipBlock(lines, i);
|
|
1675
|
+
const methods = [];
|
|
1676
|
+
for (let j = i + 1; j < end; j++) {
|
|
1677
|
+
const mt = lines[j].trim();
|
|
1678
|
+
const mm = mt.match(/^(?:static\s+)?(?:async\s+)?(\w+)\s*\(/);
|
|
1679
|
+
if (mm && mm[1] !== "constructor") methods.push(mm[1]);
|
|
1680
|
+
}
|
|
1681
|
+
parts.push(`export class ${name} { /* methods: ${methods.join(", ")} */ }`);
|
|
1682
|
+
i = end;
|
|
1683
|
+
continue;
|
|
1684
|
+
}
|
|
1685
|
+
if (/^export\s*(\{|\*)/.test(trimmed)) {
|
|
1686
|
+
const block = collectBracedLine(lines, i);
|
|
1687
|
+
parts.push(block.text);
|
|
1688
|
+
i = block.nextIndex;
|
|
1689
|
+
continue;
|
|
1690
|
+
}
|
|
1691
|
+
i++;
|
|
1691
1692
|
}
|
|
1692
1693
|
return parts.join("\n");
|
|
1693
1694
|
}
|
|
1695
|
+
function collectBracedLine(lines, start) {
|
|
1696
|
+
let text = lines[start];
|
|
1697
|
+
let i = start + 1;
|
|
1698
|
+
while (i < lines.length && !text.includes(";") && !text.trimEnd().endsWith("'") && !text.trimEnd().endsWith('"')) {
|
|
1699
|
+
text += "\n" + lines[i];
|
|
1700
|
+
i++;
|
|
1701
|
+
}
|
|
1702
|
+
return { text, nextIndex: i };
|
|
1703
|
+
}
|
|
1704
|
+
function collectBalanced(lines, start) {
|
|
1705
|
+
let depth = 0;
|
|
1706
|
+
let text = "";
|
|
1707
|
+
let i = start;
|
|
1708
|
+
let started = false;
|
|
1709
|
+
while (i < lines.length) {
|
|
1710
|
+
const line = lines[i];
|
|
1711
|
+
text += (text ? "\n" : "") + line;
|
|
1712
|
+
for (const ch of line) {
|
|
1713
|
+
if (ch === "{" || ch === "(") {
|
|
1714
|
+
depth++;
|
|
1715
|
+
started = true;
|
|
1716
|
+
}
|
|
1717
|
+
if (ch === "}" || ch === ")") depth--;
|
|
1718
|
+
}
|
|
1719
|
+
i++;
|
|
1720
|
+
if (started && depth <= 0) break;
|
|
1721
|
+
if (!started && line.includes(";")) break;
|
|
1722
|
+
}
|
|
1723
|
+
return { text, nextIndex: i };
|
|
1724
|
+
}
|
|
1725
|
+
function collectStatement(lines, start) {
|
|
1726
|
+
let text = lines[start];
|
|
1727
|
+
let i = start + 1;
|
|
1728
|
+
if (text.includes(";")) return { text, nextIndex: i };
|
|
1729
|
+
let depth = 0;
|
|
1730
|
+
for (const ch of text) {
|
|
1731
|
+
if (ch === "{" || ch === "(" || ch === "[") depth++;
|
|
1732
|
+
if (ch === "}" || ch === ")" || ch === "]") depth--;
|
|
1733
|
+
}
|
|
1734
|
+
while (i < lines.length && depth > 0) {
|
|
1735
|
+
text += "\n" + lines[i];
|
|
1736
|
+
for (const ch of lines[i]) {
|
|
1737
|
+
if (ch === "{" || ch === "(" || ch === "[") depth++;
|
|
1738
|
+
if (ch === "}" || ch === ")" || ch === "]") depth--;
|
|
1739
|
+
}
|
|
1740
|
+
i++;
|
|
1741
|
+
}
|
|
1742
|
+
return { text, nextIndex: i };
|
|
1743
|
+
}
|
|
1744
|
+
function extractFnSignature(lines, start) {
|
|
1745
|
+
let sig = "";
|
|
1746
|
+
let i = start;
|
|
1747
|
+
while (i < lines.length) {
|
|
1748
|
+
const line = lines[i].trim();
|
|
1749
|
+
sig += (sig ? " " : "") + line;
|
|
1750
|
+
if (line.includes("{")) {
|
|
1751
|
+
sig = sig.replace(/\s*\{[^]*$/, "").trim();
|
|
1752
|
+
break;
|
|
1753
|
+
}
|
|
1754
|
+
i++;
|
|
1755
|
+
}
|
|
1756
|
+
return sig;
|
|
1757
|
+
}
|
|
1758
|
+
function skipBlock(lines, start) {
|
|
1759
|
+
let depth = 0;
|
|
1760
|
+
let i = start;
|
|
1761
|
+
let foundBrace = false;
|
|
1762
|
+
while (i < lines.length) {
|
|
1763
|
+
for (const ch of lines[i]) {
|
|
1764
|
+
if (ch === "{") {
|
|
1765
|
+
depth++;
|
|
1766
|
+
foundBrace = true;
|
|
1767
|
+
}
|
|
1768
|
+
if (ch === "}") depth--;
|
|
1769
|
+
}
|
|
1770
|
+
i++;
|
|
1771
|
+
if (foundBrace && depth <= 0) break;
|
|
1772
|
+
if (!foundBrace && lines[i - 1].includes(";")) break;
|
|
1773
|
+
}
|
|
1774
|
+
return i;
|
|
1775
|
+
}
|
|
1776
|
+
function looksLikeFunctionDecl(lines, start) {
|
|
1777
|
+
const chunk = lines.slice(start, Math.min(start + 5, lines.length)).join(" ");
|
|
1778
|
+
return /=>/.test(chunk) || /=\s*function/.test(chunk);
|
|
1779
|
+
}
|
|
1780
|
+
function extractClassOutline(lines, start) {
|
|
1781
|
+
const header = lines[start].trim();
|
|
1782
|
+
let headerText = header;
|
|
1783
|
+
let i = start + 1;
|
|
1784
|
+
if (!header.includes("{")) {
|
|
1785
|
+
while (i < lines.length) {
|
|
1786
|
+
headerText += " " + lines[i].trim();
|
|
1787
|
+
if (lines[i].includes("{")) {
|
|
1788
|
+
i++;
|
|
1789
|
+
break;
|
|
1790
|
+
}
|
|
1791
|
+
i++;
|
|
1792
|
+
}
|
|
1793
|
+
} else {
|
|
1794
|
+
i = start + 1;
|
|
1795
|
+
}
|
|
1796
|
+
const bodyParts = [headerText.replace(/\{[^]*$/, "{").trim()];
|
|
1797
|
+
let depth = 1;
|
|
1798
|
+
while (i < lines.length && depth > 0) {
|
|
1799
|
+
const line = lines[i];
|
|
1800
|
+
const trimmed = line.trim();
|
|
1801
|
+
for (const ch of line) {
|
|
1802
|
+
if (ch === "{") depth++;
|
|
1803
|
+
if (ch === "}") depth--;
|
|
1804
|
+
}
|
|
1805
|
+
if (depth <= 0) {
|
|
1806
|
+
i++;
|
|
1807
|
+
break;
|
|
1808
|
+
}
|
|
1809
|
+
if (depth === 1) {
|
|
1810
|
+
if (/^(private|protected|public|readonly|static|#)/.test(trimmed) && !trimmed.includes("(")) {
|
|
1811
|
+
bodyParts.push(` ${trimmed}`);
|
|
1812
|
+
} else if (/^constructor\s*\(/.test(trimmed)) {
|
|
1813
|
+
const sig = extractFnSignature(lines, i);
|
|
1814
|
+
bodyParts.push(` ${sig} { /* ... */ }`);
|
|
1815
|
+
} else if (/^(?:static\s+)?(?:async\s+)?(?:get\s+|set\s+)?\w+\s*[(<]/.test(trimmed) && !trimmed.startsWith("//")) {
|
|
1816
|
+
const sig = extractFnSignature(lines, i);
|
|
1817
|
+
bodyParts.push(` ${sig} { /* ... */ }`);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
i++;
|
|
1821
|
+
}
|
|
1822
|
+
bodyParts.push("}");
|
|
1823
|
+
return { text: bodyParts.join("\n"), nextIndex: i };
|
|
1824
|
+
}
|
|
1694
1825
|
async function pruneGeneric(file, level) {
|
|
1695
1826
|
let content;
|
|
1696
1827
|
try {
|
|
@@ -1751,22 +1882,6 @@ function emptyResult2(file, level) {
|
|
|
1751
1882
|
savingsPercent: 100
|
|
1752
1883
|
};
|
|
1753
1884
|
}
|
|
1754
|
-
function addJSDoc(node, parts) {
|
|
1755
|
-
if (!node.getJsDocs) return;
|
|
1756
|
-
const docs = node.getJsDocs();
|
|
1757
|
-
if (docs.length > 0) {
|
|
1758
|
-
parts.push(docs[0].getText());
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
function findTsConfig(filePath) {
|
|
1762
|
-
let dir = filePath;
|
|
1763
|
-
for (let i = 0; i < 10; i++) {
|
|
1764
|
-
dir = join7(dir, "..");
|
|
1765
|
-
const candidate = join7(dir, "tsconfig.json");
|
|
1766
|
-
if (existsSync4(candidate)) return candidate;
|
|
1767
|
-
}
|
|
1768
|
-
return void 0;
|
|
1769
|
-
}
|
|
1770
1885
|
|
|
1771
1886
|
// src/engine/coverage.ts
|
|
1772
1887
|
function calculateCoverage(targetPaths, includedPaths, allFiles, graph, depth = 2) {
|
|
@@ -2472,18 +2587,18 @@ function makeSection(id, role, content) {
|
|
|
2472
2587
|
// src/govern/audit.ts
|
|
2473
2588
|
import { randomUUID, createHash as createHash5 } from "crypto";
|
|
2474
2589
|
import { readdir as readdir3, chmod } from "fs/promises";
|
|
2475
|
-
import { join as
|
|
2590
|
+
import { join as join7 } from "path";
|
|
2476
2591
|
import { userInfo } from "os";
|
|
2477
2592
|
import { homedir } from "os";
|
|
2478
2593
|
var CTO_DIR = ".cto-ai";
|
|
2479
2594
|
var AUDIT_DIR = "audit";
|
|
2480
2595
|
var MAX_ENTRIES_PER_FILE = 500;
|
|
2481
2596
|
function getAuditDir() {
|
|
2482
|
-
return
|
|
2597
|
+
return join7(homedir(), CTO_DIR, AUDIT_DIR);
|
|
2483
2598
|
}
|
|
2484
2599
|
function getCurrentAuditFile() {
|
|
2485
2600
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0].replace(/-/g, "");
|
|
2486
|
-
return
|
|
2601
|
+
return join7(getAuditDir(), `audit_${date}.json`);
|
|
2487
2602
|
}
|
|
2488
2603
|
function computeIntegrityHash(entry) {
|
|
2489
2604
|
const payload = JSON.stringify({
|
|
@@ -2511,7 +2626,7 @@ async function readJSON(filePath) {
|
|
|
2511
2626
|
}
|
|
2512
2627
|
async function writeJSON(filePath, data) {
|
|
2513
2628
|
const { writeFile: writeFile7 } = await import("fs/promises");
|
|
2514
|
-
await ensureDir(
|
|
2629
|
+
await ensureDir(join7(filePath, ".."));
|
|
2515
2630
|
await writeFile7(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
2516
2631
|
}
|
|
2517
2632
|
async function logAudit(action, projectPath, details = {}) {
|
|
@@ -2561,7 +2676,7 @@ async function getAuditEntries(options = {}) {
|
|
|
2561
2676
|
const limit = options.limit ?? 100;
|
|
2562
2677
|
for (const file of auditFiles) {
|
|
2563
2678
|
if (allEntries.length >= limit) break;
|
|
2564
|
-
const entries = await readJSON(
|
|
2679
|
+
const entries = await readJSON(join7(auditDir, file));
|
|
2565
2680
|
if (!entries) continue;
|
|
2566
2681
|
for (const entry of entries.reverse()) {
|
|
2567
2682
|
if (allEntries.length >= limit) break;
|
|
@@ -2610,7 +2725,7 @@ async function purgeOldAuditEntries(retentionDays) {
|
|
|
2610
2725
|
const dateStr = file.replace("audit_", "").replace(".json", "");
|
|
2611
2726
|
if (dateStr < cutoffStr) {
|
|
2612
2727
|
try {
|
|
2613
|
-
await unlink(
|
|
2728
|
+
await unlink(join7(auditDir, file));
|
|
2614
2729
|
purged++;
|
|
2615
2730
|
} catch {
|
|
2616
2731
|
}
|
|
@@ -2837,7 +2952,7 @@ var interactCommand = new Command3("interact").description("Build optimized AI c
|
|
|
2837
2952
|
console.log("");
|
|
2838
2953
|
}
|
|
2839
2954
|
if (opts.output === "file") {
|
|
2840
|
-
const outPath =
|
|
2955
|
+
const outPath = join8(resolve8(opts.path ?? "."), ".cto", `prompt-${plan.id}.md`);
|
|
2841
2956
|
await writeFile4(outPath, plan.prompt.rendered, "utf-8");
|
|
2842
2957
|
console.log(chalk3.green(` \u{1F4BE} Prompt saved to ${outPath}`));
|
|
2843
2958
|
console.log("");
|
|
@@ -2867,7 +2982,7 @@ var interactCommand = new Command3("interact").description("Build optimized AI c
|
|
|
2867
2982
|
// src/cli/v2/snapshot.ts
|
|
2868
2983
|
import { Command as Command4 } from "commander";
|
|
2869
2984
|
import chalk4 from "chalk";
|
|
2870
|
-
import { resolve as resolve9, join as
|
|
2985
|
+
import { resolve as resolve9, join as join9 } from "path";
|
|
2871
2986
|
import { readFile as readFile7, writeFile as writeFile5, readdir as readdir4, mkdir as mkdir3 } from "fs/promises";
|
|
2872
2987
|
|
|
2873
2988
|
// src/govern/snapshot.ts
|
|
@@ -2970,7 +3085,7 @@ function hashString(input) {
|
|
|
2970
3085
|
// src/cli/v2/snapshot.ts
|
|
2971
3086
|
var SNAPSHOTS_DIR = ".cto/snapshots";
|
|
2972
3087
|
async function ensureSnapshotsDir(projectPath) {
|
|
2973
|
-
const dir =
|
|
3088
|
+
const dir = join9(projectPath, SNAPSHOTS_DIR);
|
|
2974
3089
|
await mkdir3(dir, { recursive: true });
|
|
2975
3090
|
return dir;
|
|
2976
3091
|
}
|
|
@@ -2998,7 +3113,7 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
|
|
|
2998
3113
|
createdBy: process.env.USER ?? "unknown"
|
|
2999
3114
|
});
|
|
3000
3115
|
const dir = await ensureSnapshotsDir(projectPath);
|
|
3001
|
-
const filePath =
|
|
3116
|
+
const filePath = join9(dir, `${name}.json`);
|
|
3002
3117
|
await writeFile5(filePath, JSON.stringify(snap, null, 2), "utf-8");
|
|
3003
3118
|
console.log("");
|
|
3004
3119
|
console.log(chalk4.bold.cyan(` \u{1F4F8} Snapshot "${name}" created`));
|
|
@@ -3021,7 +3136,7 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
|
|
|
3021
3136
|
try {
|
|
3022
3137
|
const projectPath = resolve9(opts.path ?? ".");
|
|
3023
3138
|
const budget = parseInt(opts.budget ?? "50000", 10);
|
|
3024
|
-
const snapPath =
|
|
3139
|
+
const snapPath = join9(projectPath, SNAPSHOTS_DIR, `${name}.json`);
|
|
3025
3140
|
const snap = await loadSnapshot(snapPath);
|
|
3026
3141
|
console.log(chalk4.dim(`
|
|
3027
3142
|
Re-analyzing project...`));
|
|
@@ -3058,9 +3173,9 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
|
|
|
3058
3173
|
new Command4("compare").description("Compare two snapshots").argument("<older>", "Older snapshot name").argument("<newer>", "Newer snapshot name").option("-p, --path <path>", "Project path", ".").action(async (older, newer, opts) => {
|
|
3059
3174
|
try {
|
|
3060
3175
|
const projectPath = resolve9(opts.path ?? ".");
|
|
3061
|
-
const dir =
|
|
3062
|
-
const snap1 = await loadSnapshot(
|
|
3063
|
-
const snap2 = await loadSnapshot(
|
|
3176
|
+
const dir = join9(projectPath, SNAPSHOTS_DIR);
|
|
3177
|
+
const snap1 = await loadSnapshot(join9(dir, `${older}.json`));
|
|
3178
|
+
const snap2 = await loadSnapshot(join9(dir, `${newer}.json`));
|
|
3064
3179
|
const diff = compareSnapshots(snap1, snap2);
|
|
3065
3180
|
console.log("");
|
|
3066
3181
|
console.log(chalk4.bold.cyan(` \u{1F4CA} Snapshot Comparison: ${older} \u2192 ${newer}`));
|
|
@@ -3093,7 +3208,7 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
|
|
|
3093
3208
|
new Command4("list").description("List saved snapshots").option("-p, --path <path>", "Project path", ".").action(async (opts) => {
|
|
3094
3209
|
try {
|
|
3095
3210
|
const projectPath = resolve9(opts.path ?? ".");
|
|
3096
|
-
const dir =
|
|
3211
|
+
const dir = join9(projectPath, SNAPSHOTS_DIR);
|
|
3097
3212
|
let files;
|
|
3098
3213
|
try {
|
|
3099
3214
|
files = await readdir4(dir);
|
|
@@ -3111,7 +3226,7 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
|
|
|
3111
3226
|
console.log("");
|
|
3112
3227
|
for (const file of snapFiles) {
|
|
3113
3228
|
try {
|
|
3114
|
-
const snap = await loadSnapshot(
|
|
3229
|
+
const snap = await loadSnapshot(join9(dir, file));
|
|
3115
3230
|
const date = new Date(snap.createdAt).toLocaleString();
|
|
3116
3231
|
console.log(` ${chalk4.bold(snap.name)} \u2014 ${snap.files.length} files, ~${Math.round(snap.totalTokens / 1e3)}K tokens, coverage ${snap.coverageScore}%`);
|
|
3117
3232
|
console.log(` ${chalk4.dim(`Created: ${date} | Hash: ${snap.hash}`)}`);
|
|
@@ -3222,7 +3337,7 @@ var auditCommand = new Command5("audit").description("View and manage the audit
|
|
|
3222
3337
|
// src/cli/v2/policy.ts
|
|
3223
3338
|
import { Command as Command6 } from "commander";
|
|
3224
3339
|
import chalk6 from "chalk";
|
|
3225
|
-
import { resolve as resolve10, join as
|
|
3340
|
+
import { resolve as resolve10, join as join10 } from "path";
|
|
3226
3341
|
import { readFile as readFile8, writeFile as writeFile6, mkdir as mkdir4 } from "fs/promises";
|
|
3227
3342
|
|
|
3228
3343
|
// src/govern/policy.ts
|
|
@@ -3377,16 +3492,16 @@ function fileMatchesCategory(path, category) {
|
|
|
3377
3492
|
var POLICY_FILE2 = ".cto/policy.json";
|
|
3378
3493
|
async function loadPolicy(projectPath) {
|
|
3379
3494
|
try {
|
|
3380
|
-
const content = await readFile8(
|
|
3495
|
+
const content = await readFile8(join10(projectPath, POLICY_FILE2), "utf-8");
|
|
3381
3496
|
return JSON.parse(content);
|
|
3382
3497
|
} catch {
|
|
3383
3498
|
return DEFAULT_POLICY;
|
|
3384
3499
|
}
|
|
3385
3500
|
}
|
|
3386
3501
|
async function savePolicy(projectPath, policy) {
|
|
3387
|
-
const dir =
|
|
3502
|
+
const dir = join10(projectPath, ".cto");
|
|
3388
3503
|
await mkdir4(dir, { recursive: true });
|
|
3389
|
-
await writeFile6(
|
|
3504
|
+
await writeFile6(join10(projectPath, POLICY_FILE2), JSON.stringify(policy, null, 2), "utf-8");
|
|
3390
3505
|
}
|
|
3391
3506
|
var policyCommand = new Command6("policy").description("Manage context selection policies").addCommand(
|
|
3392
3507
|
new Command6("show").description("Show current policy rules").option("-p, --path <path>", "Project path", ".").option("--json", "Output as JSON").action(async (opts) => {
|
|
@@ -3530,7 +3645,7 @@ var policyCommand = new Command6("policy").description("Manage context selection
|
|
|
3530
3645
|
const projectPath = resolve10(opts.path ?? ".");
|
|
3531
3646
|
await savePolicy(projectPath, DEFAULT_POLICY);
|
|
3532
3647
|
console.log(chalk6.green(`
|
|
3533
|
-
\u2705 Default policy initialized at ${
|
|
3648
|
+
\u2705 Default policy initialized at ${join10(projectPath, POLICY_FILE2)}
|
|
3534
3649
|
`));
|
|
3535
3650
|
} catch (err) {
|
|
3536
3651
|
console.error(chalk6.red(`
|