codecortex-ai 0.1.1 → 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/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  > Persistent codebase knowledge layer for AI agents. Your AI shouldn't re-learn your codebase every session.
4
4
 
5
+ [Website](https://codecortex-ai.vercel.app) · [npm](https://www.npmjs.com/package/codecortex-ai) · [GitHub](https://github.com/rushikeshmore/CodeCortex)
6
+
5
7
  ## The Problem
6
8
 
7
9
  Every AI coding session starts from scratch. When context compacts or a new session begins, the AI re-scans the entire codebase. Same files, same tokens, same wasted time. It's like hiring a new developer every session who has to re-learn everything before writing a single line.
@@ -15,7 +17,7 @@ Every AI coding session starts from scratch. When context compacts or a new sess
15
17
 
16
18
  CodeCortex pre-digests codebases into layered knowledge files and serves them to any AI agent via MCP. Instead of re-understanding your codebase every session, the AI starts with knowledge.
17
19
 
18
- **Hybrid extraction:** tree-sitter native N-API for structure (symbols, imports, calls across 27 languages) + host LLM for semantics (what modules do, why they're built that way). Zero extra API keys.
20
+ **Hybrid extraction:** tree-sitter native N-API for structure (symbols, imports, calls across 28 languages) + host LLM for semantics (what modules do, why they're built that way). Zero extra API keys.
19
21
 
20
22
  ## Quick Start
21
23
 
@@ -136,11 +138,11 @@ vs. raw scan of entire codebase: ~37,800 tokens
136
138
 
137
139
  85-90% token reduction. 7-10x efficiency gain.
138
140
 
139
- ## Supported Languages (27)
141
+ ## Supported Languages (28)
140
142
 
141
143
  | Category | Languages |
142
144
  |----------|-----------|
143
- | Web | TypeScript, TSX, JavaScript |
145
+ | Web | TypeScript, TSX, JavaScript, Liquid |
144
146
  | Systems | C, C++, Objective-C, Rust, Zig, Go |
145
147
  | JVM | Java, Kotlin, Scala |
146
148
  | .NET | C# |
@@ -152,7 +154,7 @@ vs. raw scan of entire codebase: ~37,800 tokens
152
154
  ## Tech Stack
153
155
 
154
156
  - TypeScript ESM, Node.js 20+
155
- - `tree-sitter` (native N-API) + 27 language grammar packages
157
+ - `tree-sitter` (native N-API) + 28 language grammar packages
156
158
  - `@modelcontextprotocol/sdk` - MCP server
157
159
  - `commander` - CLI
158
160
  - `simple-git` - git integration
@@ -947,16 +947,21 @@ export {
947
947
  writeManifest,
948
948
  createManifest,
949
949
  updateManifest,
950
+ readGraph,
950
951
  writeGraph,
951
952
  buildGraph,
953
+ getModuleDependencies,
952
954
  enrichCouplingWithImports,
955
+ readModuleDoc,
956
+ writeModuleDoc,
953
957
  listModuleDocs,
954
958
  listDecisions,
955
959
  writeSession,
956
960
  listSessions,
957
961
  getLatestSession,
958
962
  createSession,
963
+ searchKnowledge,
959
964
  createServer,
960
965
  startServer
961
966
  };
962
- //# sourceMappingURL=chunk-F4WTE7R3.js.map
967
+ //# sourceMappingURL=chunk-DWUIP3WA.js.map
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-F4WTE7R3.js";
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
- console.log(" Written: cortex.yaml, symbols.json, graph.json, temporal.json, overview.md, patterns.md");
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 existsSync3 } from "fs";
1776
+ import { existsSync as existsSync4 } from "fs";
1693
1777
  async function serveCommand(opts) {
1694
1778
  const root = resolve2(opts.root);
1695
- if (!existsSync3(cortexPath(root, "cortex.yaml"))) {
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 existsSync4 } from "fs";
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 (!existsSync4(cortexPath(root, "cortex.yaml"))) {
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 existsSync5 } from "fs";
1948
+ import { existsSync as existsSync6 } from "fs";
1860
1949
  async function statusCommand(opts) {
1861
1950
  const root = resolve4(opts.root);
1862
- if (!existsSync5(cortexPath(root, "cortex.yaml"))) {
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("0.1.0");
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