codecortex-ai 0.1.0 → 0.2.1
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/cli/index.js
CHANGED
|
@@ -7,21 +7,27 @@ import {
|
|
|
7
7
|
enrichCouplingWithImports,
|
|
8
8
|
ensureDir,
|
|
9
9
|
getLatestSession,
|
|
10
|
+
getModuleDependencies,
|
|
10
11
|
listDecisions,
|
|
11
12
|
listModuleDocs,
|
|
12
13
|
listSessions,
|
|
13
14
|
readFile,
|
|
15
|
+
readGraph,
|
|
14
16
|
readManifest,
|
|
17
|
+
readModuleDoc,
|
|
18
|
+
searchKnowledge,
|
|
15
19
|
startServer,
|
|
16
20
|
updateManifest,
|
|
17
21
|
writeFile,
|
|
18
22
|
writeGraph,
|
|
19
23
|
writeJsonStream,
|
|
20
24
|
writeManifest,
|
|
25
|
+
writeModuleDoc,
|
|
21
26
|
writeSession
|
|
22
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-DWUIP3WA.js";
|
|
23
28
|
|
|
24
29
|
// src/cli/index.ts
|
|
30
|
+
import { createRequire as createRequire2 } from "module";
|
|
25
31
|
import { Command } from "commander";
|
|
26
32
|
|
|
27
33
|
// src/cli/commands/init.ts
|
|
@@ -74,9 +80,11 @@ var LANGUAGE_LOADERS = {
|
|
|
74
80
|
ocaml: () => require2("tree-sitter-ocaml").ocaml,
|
|
75
81
|
elm: () => require2("tree-sitter-elm"),
|
|
76
82
|
elisp: () => require2("tree-sitter-elisp"),
|
|
83
|
+
// Web / Templating
|
|
84
|
+
vue: () => require2("tree-sitter-vue"),
|
|
85
|
+
liquid: () => require2("tree-sitter-liquid"),
|
|
77
86
|
// Web3 / Other
|
|
78
87
|
solidity: () => require2("tree-sitter-solidity"),
|
|
79
|
-
vue: () => require2("tree-sitter-vue"),
|
|
80
88
|
ql: () => require2("tree-sitter-ql")
|
|
81
89
|
};
|
|
82
90
|
var EXTENSION_MAP = {
|
|
@@ -147,6 +155,8 @@ var EXTENSION_MAP = {
|
|
|
147
155
|
".sol": "solidity",
|
|
148
156
|
// Vue
|
|
149
157
|
".vue": "vue",
|
|
158
|
+
// Liquid
|
|
159
|
+
".liquid": "liquid",
|
|
150
160
|
// CodeQL
|
|
151
161
|
".ql": "ql"
|
|
152
162
|
};
|
|
@@ -1492,6 +1502,75 @@ function extractGenericCalls(tree, file) {
|
|
|
1492
1502
|
|
|
1493
1503
|
// src/cli/commands/init.ts
|
|
1494
1504
|
import { readFile as readFile2 } from "fs/promises";
|
|
1505
|
+
|
|
1506
|
+
// src/core/module-gen.ts
|
|
1507
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1508
|
+
async function generateStructuralModuleDocs(projectRoot, data) {
|
|
1509
|
+
let generated = 0;
|
|
1510
|
+
for (const mod of data.graph.modules) {
|
|
1511
|
+
const docPath = cortexPath(projectRoot, "modules", `${mod.name}.md`);
|
|
1512
|
+
if (existsSync3(docPath)) continue;
|
|
1513
|
+
const moduleFiles = new Set(mod.files);
|
|
1514
|
+
const exported = data.symbols.filter((s) => moduleFiles.has(s.file) && s.exported).map((s) => {
|
|
1515
|
+
const loc = s.endLine > s.startLine ? `${s.file}:${s.startLine}-${s.endLine}` : `${s.file}:${s.startLine}`;
|
|
1516
|
+
return `${s.name} (${s.kind}, ${loc})`;
|
|
1517
|
+
});
|
|
1518
|
+
const deps = getModuleDependencies(data.graph, mod.name);
|
|
1519
|
+
const importsFromModules = /* @__PURE__ */ new Set();
|
|
1520
|
+
const importedByModules = /* @__PURE__ */ new Set();
|
|
1521
|
+
for (const edge of deps.imports) {
|
|
1522
|
+
for (const other of data.graph.modules) {
|
|
1523
|
+
if (other.name !== mod.name && other.files.includes(edge.target)) {
|
|
1524
|
+
importsFromModules.add(other.name);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
for (const edge of deps.importedBy) {
|
|
1529
|
+
for (const other of data.graph.modules) {
|
|
1530
|
+
if (other.name !== mod.name && other.files.includes(edge.source)) {
|
|
1531
|
+
importedByModules.add(other.name);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
const depLines = [];
|
|
1536
|
+
if (importsFromModules.size > 0) depLines.push(`Imports from: ${[...importsFromModules].join(", ")}`);
|
|
1537
|
+
if (importedByModules.size > 0) depLines.push(`Imported by: ${[...importedByModules].join(", ")}`);
|
|
1538
|
+
let temporalSignals;
|
|
1539
|
+
if (data.temporal) {
|
|
1540
|
+
const hotspots = data.temporal.hotspots.filter((h) => moduleFiles.has(h.file));
|
|
1541
|
+
const topHotspot = hotspots[0];
|
|
1542
|
+
const couplings = data.temporal.coupling.filter(
|
|
1543
|
+
(c) => moduleFiles.has(c.fileA) || moduleFiles.has(c.fileB)
|
|
1544
|
+
);
|
|
1545
|
+
const coupledWith = couplings.map((c) => {
|
|
1546
|
+
const other = moduleFiles.has(c.fileA) ? c.fileB : c.fileA;
|
|
1547
|
+
return `${other} (${c.cochanges} co-changes, ${Math.round(c.strength * 100)}%)`;
|
|
1548
|
+
});
|
|
1549
|
+
const bugs = data.temporal.bugHistory.filter((b) => moduleFiles.has(b.file));
|
|
1550
|
+
temporalSignals = {
|
|
1551
|
+
churn: topHotspot ? `${topHotspot.changes} changes (${topHotspot.stability})` : "no hotspot data",
|
|
1552
|
+
coupledWith,
|
|
1553
|
+
stability: topHotspot?.stability ?? "unknown",
|
|
1554
|
+
bugHistory: bugs.flatMap((b) => b.lessons),
|
|
1555
|
+
lastChanged: topHotspot?.lastChanged ?? "unknown"
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
const analysis = {
|
|
1559
|
+
name: mod.name,
|
|
1560
|
+
purpose: `${mod.files.length} files, ${mod.lines} lines (${mod.language}). Auto-generated from code structure \u2014 use \`analyze_module\` MCP tool for semantic analysis.`,
|
|
1561
|
+
dataFlow: `Files: ${mod.files.join(", ")}`,
|
|
1562
|
+
publicApi: exported,
|
|
1563
|
+
gotchas: [],
|
|
1564
|
+
dependencies: depLines,
|
|
1565
|
+
temporalSignals
|
|
1566
|
+
};
|
|
1567
|
+
await writeModuleDoc(projectRoot, analysis);
|
|
1568
|
+
generated++;
|
|
1569
|
+
}
|
|
1570
|
+
return generated;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// src/cli/commands/init.ts
|
|
1495
1574
|
async function initCommand(opts) {
|
|
1496
1575
|
const root = resolve(opts.root);
|
|
1497
1576
|
const days = parseInt(opts.days, 10);
|
|
@@ -1619,7 +1698,12 @@ async function initCommand(opts) {
|
|
|
1619
1698
|
});
|
|
1620
1699
|
await writeManifest(root, manifest);
|
|
1621
1700
|
await writeFile(cortexPath(root, "patterns.md"), "# Coding Patterns\n\nNo patterns recorded yet. Use `update_patterns` to add patterns.\n");
|
|
1622
|
-
|
|
1701
|
+
const moduleDocsGenerated = await generateStructuralModuleDocs(root, {
|
|
1702
|
+
graph,
|
|
1703
|
+
symbols: allSymbols,
|
|
1704
|
+
temporal: temporalData
|
|
1705
|
+
});
|
|
1706
|
+
console.log(` Written: cortex.yaml, symbols.json, graph.json, temporal.json, overview.md, patterns.md, ${moduleDocsGenerated} module docs`);
|
|
1623
1707
|
console.log("");
|
|
1624
1708
|
console.log("Step 6/6: Generating constitution...");
|
|
1625
1709
|
await generateConstitution(root, {
|
|
@@ -1689,10 +1773,10 @@ function generateOverview(project) {
|
|
|
1689
1773
|
|
|
1690
1774
|
// src/cli/commands/serve.ts
|
|
1691
1775
|
import { resolve as resolve2 } from "path";
|
|
1692
|
-
import { existsSync as
|
|
1776
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1693
1777
|
async function serveCommand(opts) {
|
|
1694
1778
|
const root = resolve2(opts.root);
|
|
1695
|
-
if (!
|
|
1779
|
+
if (!existsSync4(cortexPath(root, "cortex.yaml"))) {
|
|
1696
1780
|
console.error("Error: No CodeCortex knowledge found.");
|
|
1697
1781
|
console.error(`Run 'codecortex init' first to analyze the codebase.`);
|
|
1698
1782
|
console.error(`Expected: ${cortexPath(root, "cortex.yaml")}`);
|
|
@@ -1703,7 +1787,7 @@ async function serveCommand(opts) {
|
|
|
1703
1787
|
|
|
1704
1788
|
// src/cli/commands/update.ts
|
|
1705
1789
|
import { resolve as resolve3 } from "path";
|
|
1706
|
-
import { existsSync as
|
|
1790
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1707
1791
|
|
|
1708
1792
|
// src/git/diff.ts
|
|
1709
1793
|
import simpleGit2 from "simple-git";
|
|
@@ -1739,7 +1823,7 @@ import { readFile as fsRead3 } from "fs/promises";
|
|
|
1739
1823
|
async function updateCommand(opts) {
|
|
1740
1824
|
const root = resolve3(opts.root);
|
|
1741
1825
|
const days = parseInt(opts.days, 10);
|
|
1742
|
-
if (!
|
|
1826
|
+
if (!existsSync5(cortexPath(root, "cortex.yaml"))) {
|
|
1743
1827
|
console.error("Error: No CodeCortex knowledge found. Run `codecortex init` first.");
|
|
1744
1828
|
process.exit(1);
|
|
1745
1829
|
}
|
|
@@ -1821,6 +1905,11 @@ async function updateCommand(opts) {
|
|
|
1821
1905
|
if (temporalData) {
|
|
1822
1906
|
await writeFile(cortexPath(root, "temporal.json"), JSON.stringify(temporalData, null, 2));
|
|
1823
1907
|
}
|
|
1908
|
+
await generateStructuralModuleDocs(root, {
|
|
1909
|
+
graph,
|
|
1910
|
+
symbols: allSymbols,
|
|
1911
|
+
temporal: temporalData
|
|
1912
|
+
});
|
|
1824
1913
|
await updateManifest(root, {
|
|
1825
1914
|
totalFiles: project.files.length,
|
|
1826
1915
|
totalSymbols: allSymbols.length,
|
|
@@ -1856,10 +1945,10 @@ async function updateCommand(opts) {
|
|
|
1856
1945
|
|
|
1857
1946
|
// src/cli/commands/status.ts
|
|
1858
1947
|
import { resolve as resolve4 } from "path";
|
|
1859
|
-
import { existsSync as
|
|
1948
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1860
1949
|
async function statusCommand(opts) {
|
|
1861
1950
|
const root = resolve4(opts.root);
|
|
1862
|
-
if (!
|
|
1951
|
+
if (!existsSync6(cortexPath(root, "cortex.yaml"))) {
|
|
1863
1952
|
console.log("No CodeCortex knowledge found.");
|
|
1864
1953
|
console.log(`Run 'codecortex init' to analyze this codebase.`);
|
|
1865
1954
|
return;
|
|
@@ -1946,12 +2035,300 @@ async function statusCommand(opts) {
|
|
|
1946
2035
|
}
|
|
1947
2036
|
}
|
|
1948
2037
|
|
|
2038
|
+
// src/cli/commands/symbols.ts
|
|
2039
|
+
import { resolve as resolve5 } from "path";
|
|
2040
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2041
|
+
async function symbolsCommand(query, opts) {
|
|
2042
|
+
const root = resolve5(opts.root);
|
|
2043
|
+
if (!existsSync7(cortexPath(root, "cortex.yaml"))) {
|
|
2044
|
+
console.log("No CodeCortex knowledge found.");
|
|
2045
|
+
console.log(`Run 'codecortex init' to analyze this codebase.`);
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
const content = await readFile(cortexPath(root, "symbols.json"));
|
|
2049
|
+
if (!content) {
|
|
2050
|
+
console.log("No symbol index found. Run `codecortex init` first.");
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
const index = JSON.parse(content);
|
|
2054
|
+
if (!query && !opts.kind && !opts.file && !opts.exported) {
|
|
2055
|
+
printSummary(index);
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
let matches = index.symbols;
|
|
2059
|
+
if (query) {
|
|
2060
|
+
const q = query.toLowerCase();
|
|
2061
|
+
matches = matches.filter((s) => s.name.toLowerCase().includes(q));
|
|
2062
|
+
}
|
|
2063
|
+
if (opts.kind) {
|
|
2064
|
+
matches = matches.filter((s) => s.kind === opts.kind);
|
|
2065
|
+
}
|
|
2066
|
+
if (opts.file) {
|
|
2067
|
+
matches = matches.filter((s) => s.file.includes(opts.file));
|
|
2068
|
+
}
|
|
2069
|
+
if (opts.exported) {
|
|
2070
|
+
matches = matches.filter((s) => s.exported);
|
|
2071
|
+
}
|
|
2072
|
+
const limit = parseInt(opts.limit ?? "30", 10);
|
|
2073
|
+
const display = matches.slice(0, limit);
|
|
2074
|
+
if (display.length === 0) {
|
|
2075
|
+
console.log(`No symbols found${query ? ` matching "${query}"` : ""}.`);
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
console.log("");
|
|
2079
|
+
for (const s of display) {
|
|
2080
|
+
const lines = s.endLine > s.startLine ? `${s.startLine}-${s.endLine}` : `${s.startLine}`;
|
|
2081
|
+
const exp = s.exported ? "exported" : "local";
|
|
2082
|
+
console.log(` ${pad(s.kind, 12)} ${pad(s.name, 30)} ${pad(s.file, 40)} ${pad(lines, 10)} ${exp}`);
|
|
2083
|
+
if (s.signature) {
|
|
2084
|
+
console.log(`${" ".repeat(14)}${s.signature}`);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
console.log("");
|
|
2088
|
+
if (matches.length > limit) {
|
|
2089
|
+
console.log(`Showing ${limit} of ${matches.length} matches. Use -l to show more.`);
|
|
2090
|
+
} else {
|
|
2091
|
+
console.log(`${matches.length} symbol${matches.length === 1 ? "" : "s"} found.`);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
function printSummary(index) {
|
|
2095
|
+
console.log("");
|
|
2096
|
+
console.log(`Symbol Index: ${index.total} symbols`);
|
|
2097
|
+
console.log("\u2500".repeat(50));
|
|
2098
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
2099
|
+
for (const s of index.symbols) {
|
|
2100
|
+
byKind.set(s.kind, (byKind.get(s.kind) ?? 0) + 1);
|
|
2101
|
+
}
|
|
2102
|
+
console.log("");
|
|
2103
|
+
console.log("By Kind:");
|
|
2104
|
+
for (const [kind, count] of [...byKind.entries()].sort((a, b) => b[1] - a[1])) {
|
|
2105
|
+
console.log(` ${pad(kind, 14)} ${count}`);
|
|
2106
|
+
}
|
|
2107
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
2108
|
+
for (const s of index.symbols) {
|
|
2109
|
+
byFile.set(s.file, (byFile.get(s.file) ?? 0) + 1);
|
|
2110
|
+
}
|
|
2111
|
+
console.log("");
|
|
2112
|
+
console.log("Top Files:");
|
|
2113
|
+
for (const [file, count] of [...byFile.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10)) {
|
|
2114
|
+
console.log(` ${pad(file, 45)} ${count} symbols`);
|
|
2115
|
+
}
|
|
2116
|
+
const exported = index.symbols.filter((s) => s.exported).length;
|
|
2117
|
+
console.log("");
|
|
2118
|
+
console.log(`Exported: ${exported}/${index.total}`);
|
|
2119
|
+
console.log("");
|
|
2120
|
+
console.log("Run `codecortex symbols <query>` to search.");
|
|
2121
|
+
}
|
|
2122
|
+
function pad(str, len) {
|
|
2123
|
+
return str.length >= len ? str : str + " ".repeat(len - str.length);
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
// src/cli/commands/search.ts
|
|
2127
|
+
import { resolve as resolve6 } from "path";
|
|
2128
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2129
|
+
async function searchCommand(query, opts) {
|
|
2130
|
+
const root = resolve6(opts.root);
|
|
2131
|
+
if (!existsSync8(cortexPath(root, "cortex.yaml"))) {
|
|
2132
|
+
console.log("No CodeCortex knowledge found.");
|
|
2133
|
+
console.log(`Run 'codecortex init' to analyze this codebase.`);
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
const results = await searchKnowledge(root, query);
|
|
2137
|
+
const limit = parseInt(opts.limit ?? "20", 10);
|
|
2138
|
+
const display = results.slice(0, limit);
|
|
2139
|
+
if (display.length === 0) {
|
|
2140
|
+
console.log(`No results for "${query}".`);
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2143
|
+
console.log("");
|
|
2144
|
+
console.log(`Search: "${query}"`);
|
|
2145
|
+
console.log("\u2500".repeat(50));
|
|
2146
|
+
for (let i = 0; i < display.length; i++) {
|
|
2147
|
+
const r = display[i];
|
|
2148
|
+
console.log("");
|
|
2149
|
+
console.log(` [${i + 1}] ${r.file}:${r.line}`);
|
|
2150
|
+
const ctxLines = r.context.split("\n");
|
|
2151
|
+
for (const line of ctxLines) {
|
|
2152
|
+
const trimmed = line.trim();
|
|
2153
|
+
if (!trimmed) continue;
|
|
2154
|
+
if (trimmed.toLowerCase().includes(query.toLowerCase())) {
|
|
2155
|
+
console.log(` > ${trimmed}`);
|
|
2156
|
+
} else {
|
|
2157
|
+
console.log(` ${trimmed}`);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
console.log("");
|
|
2162
|
+
if (results.length > limit) {
|
|
2163
|
+
console.log(`Showing ${limit} of ${results.length} results. Use -l to show more.`);
|
|
2164
|
+
} else {
|
|
2165
|
+
console.log(`${results.length} result${results.length === 1 ? "" : "s"} found.`);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
// src/cli/commands/modules.ts
|
|
2170
|
+
import { resolve as resolve7 } from "path";
|
|
2171
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2172
|
+
async function modulesCommand(name, opts) {
|
|
2173
|
+
const root = resolve7(opts.root);
|
|
2174
|
+
if (!existsSync9(cortexPath(root, "cortex.yaml"))) {
|
|
2175
|
+
console.log("No CodeCortex knowledge found.");
|
|
2176
|
+
console.log(`Run 'codecortex init' to analyze this codebase.`);
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
const graph = await readGraph(root);
|
|
2180
|
+
if (!graph) {
|
|
2181
|
+
console.log("No dependency graph found. Run `codecortex init` first.");
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
2184
|
+
if (!name) {
|
|
2185
|
+
await printModuleList(root, graph);
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
await printModuleDetail(root, name, graph);
|
|
2189
|
+
}
|
|
2190
|
+
async function printModuleList(root, graph) {
|
|
2191
|
+
const docsAvailable = new Set(await listModuleDocs(root));
|
|
2192
|
+
console.log("");
|
|
2193
|
+
console.log(`Modules: ${graph.modules.length}`);
|
|
2194
|
+
console.log("\u2500".repeat(70));
|
|
2195
|
+
console.log("");
|
|
2196
|
+
console.log(` ${pad2("MODULE", 18)} ${pad2("FILES", 6)} ${pad2("LINES", 7)} ${pad2("SYMBOLS", 8)} ${pad2("LANG", 14)} DOC`);
|
|
2197
|
+
for (const mod of [...graph.modules].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
2198
|
+
const hasDoc = docsAvailable.has(mod.name) ? "yes" : "--";
|
|
2199
|
+
console.log(` ${pad2(mod.name, 18)} ${pad2(String(mod.files.length), 6)} ${pad2(String(mod.lines), 7)} ${pad2(String(mod.symbols), 8)} ${pad2(mod.language, 14)} ${hasDoc}`);
|
|
2200
|
+
}
|
|
2201
|
+
const withDocs = graph.modules.filter((m) => docsAvailable.has(m.name)).length;
|
|
2202
|
+
console.log("");
|
|
2203
|
+
console.log(`${graph.modules.length} modules, ${withDocs} with docs.`);
|
|
2204
|
+
console.log("");
|
|
2205
|
+
console.log("Run `codecortex modules <name>` to deep-dive into a module.");
|
|
2206
|
+
}
|
|
2207
|
+
async function printModuleDetail(root, name, graph) {
|
|
2208
|
+
const mod = graph.modules.find((m) => m.name === name);
|
|
2209
|
+
if (!mod) {
|
|
2210
|
+
const available = graph.modules.map((m) => m.name).join(", ");
|
|
2211
|
+
console.log(`Module "${name}" not found. Available: ${available}`);
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
console.log("");
|
|
2215
|
+
console.log(`Module: ${name}`);
|
|
2216
|
+
console.log("\u2550".repeat(50));
|
|
2217
|
+
const doc = await readModuleDoc(root, name);
|
|
2218
|
+
if (doc) {
|
|
2219
|
+
console.log("");
|
|
2220
|
+
console.log(doc);
|
|
2221
|
+
} else {
|
|
2222
|
+
console.log("");
|
|
2223
|
+
console.log(`No module doc for "${name}".`);
|
|
2224
|
+
console.log(`Run \`codecortex init\` to generate structural docs.`);
|
|
2225
|
+
}
|
|
2226
|
+
const deps = getModuleDependencies(graph, name);
|
|
2227
|
+
if (deps.imports.length > 0) {
|
|
2228
|
+
console.log("");
|
|
2229
|
+
console.log("Imports:");
|
|
2230
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2231
|
+
for (const edge of deps.imports) {
|
|
2232
|
+
const key = `${edge.source} -> ${edge.target}`;
|
|
2233
|
+
if (seen.has(key)) continue;
|
|
2234
|
+
seen.add(key);
|
|
2235
|
+
const specifiers = edge.specifiers.length > 0 ? ` [${edge.specifiers.join(", ")}]` : "";
|
|
2236
|
+
console.log(` ${edge.source} -> ${edge.target}${specifiers}`);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
if (deps.importedBy.length > 0) {
|
|
2240
|
+
console.log("");
|
|
2241
|
+
console.log("Imported By:");
|
|
2242
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2243
|
+
for (const edge of deps.importedBy) {
|
|
2244
|
+
const key = `${edge.source} -> ${edge.target}`;
|
|
2245
|
+
if (seen.has(key)) continue;
|
|
2246
|
+
seen.add(key);
|
|
2247
|
+
const specifiers = edge.specifiers.length > 0 ? ` [${edge.specifiers.join(", ")}]` : "";
|
|
2248
|
+
console.log(` ${edge.source} -> ${edge.target}${specifiers}`);
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
console.log("");
|
|
2252
|
+
console.log(`Stats: ${mod.files.length} files, ${mod.lines} lines, ${mod.symbols} symbols (${mod.language})`);
|
|
2253
|
+
}
|
|
2254
|
+
function pad2(str, len) {
|
|
2255
|
+
return str.length >= len ? str : str + " ".repeat(len - str.length);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
// src/cli/commands/hotspots.ts
|
|
2259
|
+
import { resolve as resolve8 } from "path";
|
|
2260
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2261
|
+
async function hotspotsCommand(opts) {
|
|
2262
|
+
const root = resolve8(opts.root);
|
|
2263
|
+
if (!existsSync10(cortexPath(root, "cortex.yaml"))) {
|
|
2264
|
+
console.log("No CodeCortex knowledge found.");
|
|
2265
|
+
console.log(`Run 'codecortex init' to analyze this codebase.`);
|
|
2266
|
+
return;
|
|
2267
|
+
}
|
|
2268
|
+
const content = await readFile(cortexPath(root, "temporal.json"));
|
|
2269
|
+
if (!content) {
|
|
2270
|
+
console.log("No temporal data found. Run `codecortex init` in a git repository.");
|
|
2271
|
+
return;
|
|
2272
|
+
}
|
|
2273
|
+
const temporal = JSON.parse(content);
|
|
2274
|
+
const limit = parseInt(opts.limit ?? "15", 10);
|
|
2275
|
+
const riskMap = /* @__PURE__ */ new Map();
|
|
2276
|
+
for (const h of temporal.hotspots) {
|
|
2277
|
+
riskMap.set(h.file, { churn: h.changes, couplings: 0, bugs: 0, risk: h.changes });
|
|
2278
|
+
}
|
|
2279
|
+
for (const c of temporal.coupling) {
|
|
2280
|
+
for (const f of [c.fileA, c.fileB]) {
|
|
2281
|
+
const entry = riskMap.get(f) ?? { churn: 0, couplings: 0, bugs: 0, risk: 0 };
|
|
2282
|
+
entry.couplings++;
|
|
2283
|
+
entry.risk += c.strength * 2;
|
|
2284
|
+
riskMap.set(f, entry);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
for (const b of temporal.bugHistory) {
|
|
2288
|
+
const entry = riskMap.get(b.file) ?? { churn: 0, couplings: 0, bugs: 0, risk: 0 };
|
|
2289
|
+
entry.bugs = b.fixCommits;
|
|
2290
|
+
entry.risk += b.fixCommits * 3;
|
|
2291
|
+
riskMap.set(b.file, entry);
|
|
2292
|
+
}
|
|
2293
|
+
const ranked = [...riskMap.entries()].sort((a, b) => b[1].risk - a[1].risk).slice(0, limit).map(([file, data]) => ({ file, ...data, risk: Math.round(data.risk * 100) / 100 }));
|
|
2294
|
+
console.log("");
|
|
2295
|
+
console.log(`Hotspots \u2014 ${temporal.periodDays} days, ${temporal.totalCommits} commits`);
|
|
2296
|
+
console.log("\u2500".repeat(70));
|
|
2297
|
+
console.log("");
|
|
2298
|
+
console.log(` ${pad3("#", 4)} ${pad3("FILE", 42)} ${pad3("CHURN", 6)} ${pad3("CPLS", 5)} ${pad3("BUGS", 5)} RISK`);
|
|
2299
|
+
for (let i = 0; i < ranked.length; i++) {
|
|
2300
|
+
const r = ranked[i];
|
|
2301
|
+
console.log(` ${pad3(String(i + 1), 4)} ${pad3(r.file, 42)} ${pad3(String(r.churn), 6)} ${pad3(String(r.couplings), 5)} ${pad3(String(r.bugs), 5)} ${r.risk.toFixed(2)}`);
|
|
2302
|
+
}
|
|
2303
|
+
const hidden = temporal.coupling.filter((c) => !c.hasImport && c.strength >= 0.3);
|
|
2304
|
+
if (hidden.length > 0) {
|
|
2305
|
+
console.log("");
|
|
2306
|
+
console.log("Hidden Dependencies (co-change but no import):");
|
|
2307
|
+
for (const h of hidden.slice(0, 10)) {
|
|
2308
|
+
console.log(` ${h.fileA} <-> ${h.fileB} (${h.cochanges} co-changes, ${Math.round(h.strength * 100)}%)`);
|
|
2309
|
+
}
|
|
2310
|
+
if (hidden.length > 10) {
|
|
2311
|
+
console.log(` ... and ${hidden.length - 10} more`);
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
console.log("");
|
|
2315
|
+
}
|
|
2316
|
+
function pad3(str, len) {
|
|
2317
|
+
return str.length >= len ? str : str + " ".repeat(len - str.length);
|
|
2318
|
+
}
|
|
2319
|
+
|
|
1949
2320
|
// src/cli/index.ts
|
|
2321
|
+
var require3 = createRequire2(import.meta.url);
|
|
2322
|
+
var { version } = require3("../../package.json");
|
|
1950
2323
|
var program = new Command();
|
|
1951
|
-
program.name("codecortex").description("Persistent, AI-powered codebase knowledge layer").version(
|
|
2324
|
+
program.name("codecortex").description("Persistent, AI-powered codebase knowledge layer").version(version);
|
|
1952
2325
|
program.command("init").description("Initialize codebase knowledge: discover files, extract symbols, analyze git history").option("-r, --root <path>", "Project root directory", process.cwd()).option("-d, --days <number>", "Days of git history to analyze", "90").action(initCommand);
|
|
1953
2326
|
program.command("serve").description("Start MCP server (stdio transport) for AI agent access").option("-r, --root <path>", "Project root directory", process.cwd()).action(serveCommand);
|
|
1954
2327
|
program.command("update").description("Update knowledge for changed files since last session").option("-r, --root <path>", "Project root directory", process.cwd()).option("-d, --days <number>", "Days of git history to re-analyze", "90").action(updateCommand);
|
|
1955
2328
|
program.command("status").description("Show knowledge freshness, stale modules, and symbol counts").option("-r, --root <path>", "Project root directory", process.cwd()).action(statusCommand);
|
|
2329
|
+
program.command("symbols [query]").description("Browse and filter the symbol index").option("-r, --root <path>", "Project root directory", process.cwd()).option("-k, --kind <kind>", "Filter by kind: function, class, interface, type, const, enum, method, property, variable").option("-f, --file <path>", "Filter by file path (partial match)").option("-e, --exported", "Show only exported symbols").option("-l, --limit <number>", "Max results", "30").action((query, opts) => symbolsCommand(query, opts));
|
|
2330
|
+
program.command("search <query>").description("Search across all CodeCortex knowledge files").option("-r, --root <path>", "Project root directory", process.cwd()).option("-l, --limit <number>", "Max results", "20").action((query, opts) => searchCommand(query, opts));
|
|
2331
|
+
program.command("modules [name]").description("List modules or deep-dive into a specific module").option("-r, --root <path>", "Project root directory", process.cwd()).action((name, opts) => modulesCommand(name, opts));
|
|
2332
|
+
program.command("hotspots").description("Show files ranked by risk: churn + coupling + bug history").option("-r, --root <path>", "Project root directory", process.cwd()).option("-l, --limit <number>", "Number of files to show", "15").action(hotspotsCommand);
|
|
1956
2333
|
program.parse();
|
|
1957
2334
|
//# sourceMappingURL=index.js.map
|