@ulpi/codemap 0.3.1 → 0.3.3

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.
Files changed (3) hide show
  1. package/README.md +44 -1
  2. package/dist/index.js +365 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -13,7 +13,7 @@ Code intelligence CLI — hybrid vector + BM25 semantic search, dependency analy
13
13
  - **Cycle detection** — find circular dependencies automatically
14
14
  - **Coupling metrics** — measure afferent/efferent coupling and instability per file
15
15
  - **Real-time watching** — incremental re-indexing on file changes
16
- - **MCP server** — 12 tools for AI agent integration (Claude, Cursor, VS Code, etc.)
16
+ - **MCP server** — 12 tools for AI agent integration (Claude, Cursor, VS Code, Kiro, etc.)
17
17
  - **Per-branch indexing** — separate indexes for each git branch
18
18
  - **Multiple providers** — works with Ollama (free, local), OpenAI, or ULPI Cloud embeddings
19
19
 
@@ -154,6 +154,49 @@ codemap cycles
154
154
  codemap cycles --json
155
155
  ```
156
156
 
157
+ ### `codemap summary <file>`
158
+
159
+ Show file overview with symbols and size.
160
+
161
+ ```bash
162
+ codemap summary src/routes/auth.ts
163
+ codemap summary src/index.ts --json
164
+ ```
165
+
166
+ ### `codemap coupling`
167
+
168
+ Analyze afferent/efferent coupling and instability.
169
+
170
+ ```bash
171
+ codemap coupling
172
+ codemap coupling --module src/routes/ --limit 20
173
+ codemap coupling --json
174
+ ```
175
+
176
+ | Option | Description | Default |
177
+ |--------|-------------|---------|
178
+ | `--module <path>` | Filter to a directory prefix | — |
179
+ | `-l, --limit <n>` | Max results | `50` |
180
+ | `--json` | Output as JSON | — |
181
+
182
+ ### `codemap graph-stats`
183
+
184
+ Show aggregate dependency graph statistics.
185
+
186
+ ```bash
187
+ codemap graph-stats
188
+ codemap graph-stats --json
189
+ ```
190
+
191
+ ### `codemap rebuild-depgraph`
192
+
193
+ Rebuild dependency graph from scratch using tree-sitter tag extraction.
194
+
195
+ ```bash
196
+ codemap rebuild-depgraph
197
+ codemap rebuild-depgraph --json
198
+ ```
199
+
157
200
  ### `codemap ignore`
158
201
 
159
202
  Generate or reset `.codemapignore` with default exclusion patterns.
package/dist/index.js CHANGED
@@ -4098,6 +4098,59 @@ function extractSymbolsFromChunks(chunks) {
4098
4098
  }
4099
4099
  return allSymbols;
4100
4100
  }
4101
+ var TAG_TYPE_MAP = {
4102
+ class: "class",
4103
+ function: "function",
4104
+ method: "method",
4105
+ interface: "interface",
4106
+ type: "type",
4107
+ enum: "enum",
4108
+ module: "class",
4109
+ trait: "interface",
4110
+ struct: "class",
4111
+ property: "variable",
4112
+ constant: "const",
4113
+ variable: "variable",
4114
+ mixin: "class",
4115
+ extension: "class",
4116
+ union: "enum"
4117
+ };
4118
+ function symbolsFromTags(allTags, chunks) {
4119
+ const chunksByFile = /* @__PURE__ */ new Map();
4120
+ for (const chunk of chunks) {
4121
+ let arr = chunksByFile.get(chunk.filePath);
4122
+ if (!arr) {
4123
+ arr = [];
4124
+ chunksByFile.set(chunk.filePath, arr);
4125
+ }
4126
+ arr.push(chunk);
4127
+ }
4128
+ const seen = /* @__PURE__ */ new Set();
4129
+ const symbols = [];
4130
+ for (const [filePath, tags] of allTags) {
4131
+ const fileChunks = chunksByFile.get(filePath);
4132
+ for (const tag of tags) {
4133
+ if (tag.kind !== "def") continue;
4134
+ if (!tag.name || tag.name.length < 2) continue;
4135
+ const symbolType = TAG_TYPE_MAP[tag.symbolType ?? ""] ?? "unknown";
4136
+ const key = `${tag.name}:${filePath}:${tag.line}`;
4137
+ if (seen.has(key)) continue;
4138
+ seen.add(key);
4139
+ const chunk = fileChunks?.find(
4140
+ (c2) => tag.line >= c2.startLine && tag.line <= c2.endLine
4141
+ );
4142
+ symbols.push({
4143
+ name: tag.name,
4144
+ symbolType,
4145
+ filePath,
4146
+ startLine: tag.line,
4147
+ endLine: chunk?.endLine ?? tag.line,
4148
+ chunkId: chunk?.id ?? ""
4149
+ });
4150
+ }
4151
+ }
4152
+ return symbols;
4153
+ }
4101
4154
  function searchSymbols(symbols, query, topK = 20) {
4102
4155
  const q = query.toLowerCase();
4103
4156
  const results = [];
@@ -4512,11 +4565,13 @@ async function runInitPipelineInner(projectDir, config, branch, onProgress, data
4512
4565
  await store.dropTable();
4513
4566
  await embedAndStore(embedder, store, allChunks, onProgress);
4514
4567
  onProgress?.({ phase: "symbols", message: "Extracting symbols..." });
4515
- const symbols = extractSymbolsFromChunks(allChunks);
4568
+ const regexSymbols = extractSymbolsFromChunks(allChunks);
4569
+ const tagSymbols = allTags.size > 0 ? symbolsFromTags(allTags, allChunks) : [];
4570
+ const symbols = mergeSymbols(regexSymbols, tagSymbols);
4516
4571
  saveSymbolIndex(metadataDir, symbols);
4517
4572
  onProgress?.({
4518
4573
  phase: "symbols",
4519
- message: `Extracted ${symbols.length} symbols`,
4574
+ message: `Extracted ${symbols.length} symbols (${tagSymbols.length} from tags, ${regexSymbols.length} from regex)`,
4520
4575
  current: symbols.length,
4521
4576
  total: symbols.length
4522
4577
  });
@@ -4631,7 +4686,10 @@ async function runIncrementalInner(projectDir, config, branch, manifest, changed
4631
4686
  saveBM25Index(metadataDir, bm25Index);
4632
4687
  const allFilePaths2 = scannedFiles.map((f) => f.filePath);
4633
4688
  const allChunks = chunkFiles(projectDir, allFilePaths2, config.chunking);
4634
- const symbols = extractSymbolsFromChunks(allChunks);
4689
+ const regexSymbols = extractSymbolsFromChunks(allChunks);
4690
+ const allTags2 = await extractAllTags(projectDir, allFilePaths2, onProgress);
4691
+ const tagSymbols = allTags2.size > 0 ? symbolsFromTags(allTags2, allChunks) : [];
4692
+ const symbols = mergeSymbols(regexSymbols, tagSymbols);
4635
4693
  saveSymbolIndex(metadataDir, symbols);
4636
4694
  } else {
4637
4695
  const bm25Index = loadBM25Index(metadataDir);
@@ -4703,6 +4761,23 @@ function buildEmbeddingText(chunk) {
4703
4761
  return `${header}
4704
4762
  ${chunk.content}`;
4705
4763
  }
4764
+ function mergeSymbols(regexSymbols, tagSymbols) {
4765
+ const seen = /* @__PURE__ */ new Set();
4766
+ const merged = [];
4767
+ for (const sym of tagSymbols) {
4768
+ const key = `${sym.name}:${sym.filePath}:${sym.startLine}`;
4769
+ if (seen.has(key)) continue;
4770
+ seen.add(key);
4771
+ merged.push(sym);
4772
+ }
4773
+ for (const sym of regexSymbols) {
4774
+ const key = `${sym.name}:${sym.filePath}:${sym.startLine}`;
4775
+ if (seen.has(key)) continue;
4776
+ seen.add(key);
4777
+ merged.push(sym);
4778
+ }
4779
+ return merged;
4780
+ }
4706
4781
  async function extractAllTags(projectDir, filePaths, onProgress) {
4707
4782
  const allTags = /* @__PURE__ */ new Map();
4708
4783
  try {
@@ -5000,6 +5075,7 @@ async function searchCode(projectDir, query, options) {
5000
5075
  const hybridConfig = config.hybrid;
5001
5076
  if (hybridConfig.enabled) {
5002
5077
  return await hybridSearch(
5078
+ projectDir,
5003
5079
  store,
5004
5080
  metadataDir,
5005
5081
  queryVector,
@@ -5027,7 +5103,7 @@ async function searchCode(projectDir, query, options) {
5027
5103
  await store.close();
5028
5104
  }
5029
5105
  }
5030
- async function hybridSearch(store, metadataDir, queryVector, query, weights, vectorK, bm25K, limit, threshold, pathPrefix, includeTests, includeDocs) {
5106
+ async function hybridSearch(projectDir, store, metadataDir, queryVector, query, weights, vectorK, bm25K, limit, threshold, pathPrefix, includeTests, includeDocs) {
5031
5107
  const vectorResults = await store.query(queryVector, vectorK);
5032
5108
  const bm25Index = loadBM25Index(metadataDir);
5033
5109
  const bm25Results = bm25Index ? queryBM25(bm25Index, query, bm25K) : [];
@@ -5051,6 +5127,7 @@ async function hybridSearch(store, metadataDir, queryVector, query, weights, vec
5051
5127
  weights,
5052
5128
  Math.max(limit * 3, 80)
5053
5129
  );
5130
+ enrichBM25Candidates(projectDir, fused);
5054
5131
  return applyFilters(fused, limit, threshold, pathPrefix, includeTests, includeDocs);
5055
5132
  }
5056
5133
  async function vectorOnlySearch(store, queryVector, limit, threshold, pathPrefix, includeTests, includeDocs) {
@@ -5089,6 +5166,28 @@ function applyFilters(candidates, limit, threshold, pathPrefix, includeTests, in
5089
5166
  symbolScore: c2.symbolScore
5090
5167
  }));
5091
5168
  }
5169
+ function enrichBM25Candidates(projectDir, candidates) {
5170
+ for (const c2 of candidates) {
5171
+ if (c2.snippet !== "" || c2.startLine !== 0) continue;
5172
+ const lastColon = c2.id.lastIndexOf(":");
5173
+ if (lastColon === -1) continue;
5174
+ const beforeHash = c2.id.slice(0, lastColon);
5175
+ const secondLastColon = beforeHash.lastIndexOf(":");
5176
+ if (secondLastColon === -1) continue;
5177
+ const lineRange = beforeHash.slice(secondLastColon + 1);
5178
+ const match = lineRange.match(/^(\d+)-(\d+)$/);
5179
+ if (!match) continue;
5180
+ c2.startLine = parseInt(match[1], 10);
5181
+ c2.endLine = parseInt(match[2], 10);
5182
+ try {
5183
+ const absPath = path10.join(projectDir, c2.filePath);
5184
+ const content = fs10.readFileSync(absPath, "utf-8");
5185
+ const lines = content.split("\n");
5186
+ c2.snippet = lines.slice(c2.startLine - 1, c2.endLine).join("\n").slice(0, 200);
5187
+ } catch {
5188
+ }
5189
+ }
5190
+ }
5092
5191
  function loadGraphRanks(metadataDir) {
5093
5192
  try {
5094
5193
  const file = path10.join(metadataDir, "depgraph", "pagerank.json");
@@ -5793,8 +5892,64 @@ function registerInit(program2) {
5793
5892
  console.log(` ${chalk3.cyan("codemap index")} Index your project`);
5794
5893
  console.log(` ${chalk3.cyan("codemap search")} Search your code`);
5795
5894
  console.log(` ${chalk3.cyan("codemap serve")} Start MCP server`);
5895
+ console.log();
5896
+ printAgentSnippet();
5796
5897
  });
5797
5898
  }
5899
+ function printAgentSnippet() {
5900
+ console.log(chalk3.bold("Add to your CLAUDE.md / agents.md / rules:"));
5901
+ console.log(chalk3.dim("\u2500".repeat(60)));
5902
+ console.log(`
5903
+ ## Code Intelligence (codemap)
5904
+
5905
+ This project is indexed with codemap for semantic code search,
5906
+ symbol lookup, dependency analysis, and PageRank file ranking.
5907
+
5908
+ ### MCP Tools
5909
+
5910
+ | Tool | Description |
5911
+ |------|-------------|
5912
+ | \`mcp__codemap__search_code\` | Hybrid vector + BM25 semantic code search |
5913
+ | \`mcp__codemap__search_symbols\` | Find functions, classes, types by name |
5914
+ | \`mcp__codemap__get_file_summary\` | File overview with symbols and line count |
5915
+ | \`mcp__codemap__get_index_stats\` | Index statistics (files, chunks, size) |
5916
+ | \`mcp__codemap__get_dependencies\` | Files this file imports (outgoing) |
5917
+ | \`mcp__codemap__get_dependents\` | Files that import this file (incoming) |
5918
+ | \`mcp__codemap__get_file_rank\` | PageRank importance score |
5919
+ | \`mcp__codemap__find_cycles\` | Detect circular dependencies |
5920
+ | \`mcp__codemap__get_coupling_metrics\` | Afferent/efferent coupling analysis |
5921
+ | \`mcp__codemap__get_depgraph_stats\` | Aggregate dependency graph statistics |
5922
+ | \`mcp__codemap__reindex\` | Re-index the codebase |
5923
+ | \`mcp__codemap__rebuild_depgraph\` | Rebuild dependency graph |
5924
+
5925
+ ### CLI Commands
5926
+
5927
+ | Command | Description |
5928
+ |---------|-------------|
5929
+ | \`codemap search <query>\` | Hybrid vector + BM25 semantic code search |
5930
+ | \`codemap symbols <query>\` | Find functions, classes, types by name |
5931
+ | \`codemap summary <file>\` | File overview with symbols and line count |
5932
+ | \`codemap status\` | Index statistics (files, chunks, size) |
5933
+ | \`codemap deps <file>\` | Files this file imports (outgoing) |
5934
+ | \`codemap dependents <file>\` | Files that import this file (incoming) |
5935
+ | \`codemap rank [file]\` | PageRank importance score |
5936
+ | \`codemap cycles\` | Detect circular dependencies |
5937
+ | \`codemap coupling\` | Afferent/efferent coupling analysis |
5938
+ | \`codemap graph-stats\` | Aggregate dependency graph statistics |
5939
+ | \`codemap index\` | Re-index the codebase |
5940
+ | \`codemap rebuild-depgraph\` | Rebuild dependency graph |
5941
+
5942
+ ### When to Use
5943
+
5944
+ - **Before editing**: \`search_code\` or \`search_symbols\` to find relevant files
5945
+ - **Understanding impact**: \`get_dependents\` to see what breaks if you change a file
5946
+ - **Architecture review**: \`get_coupling_metrics\` and \`find_cycles\` for code health
5947
+ - **Navigation**: \`get_file_rank\` to identify the most important files
5948
+ `.trim());
5949
+ console.log();
5950
+ console.log(chalk3.dim("\u2500".repeat(60)));
5951
+ console.log(chalk3.dim("Copy the above into your CLAUDE.md, .cursorrules, or agent config."));
5952
+ }
5798
5953
  var ULPI_API_BASE = "https://codemap.ulpi.io";
5799
5954
  var ULPI_AUTH_BASE = "https://codemap.ulpi.io/api/v1";
5800
5955
  function ask(prompt) {
@@ -6038,7 +6193,7 @@ function loadMetricsSafe(projectDir, branch) {
6038
6193
  function createCodemapMcpServer() {
6039
6194
  const server = new McpServer({
6040
6195
  name: "codemap",
6041
- version: "0.3.1"
6196
+ version: "0.3.2"
6042
6197
  });
6043
6198
  const dataDir = getDataDir2();
6044
6199
  server.tool(
@@ -6609,12 +6764,212 @@ function registerIgnore(program2) {
6609
6764
  });
6610
6765
  }
6611
6766
 
6767
+ // src/commands/summary.ts
6768
+ import chalk13 from "chalk";
6769
+ import fs17 from "fs";
6770
+ function registerSummary(program2) {
6771
+ program2.command("summary <file>").description("Show file overview with symbols and size").option("--json", "Output as JSON").action(async (file, opts) => {
6772
+ const projectDir = program2.opts().cwd || process.cwd();
6773
+ const branch = getCurrentBranch(projectDir);
6774
+ const fullPath = `${projectDir}/${file}`;
6775
+ if (!fs17.existsSync(fullPath)) {
6776
+ console.error(chalk13.red(`File not found: ${file}`));
6777
+ process.exit(1);
6778
+ return;
6779
+ }
6780
+ const content = fs17.readFileSync(fullPath, "utf-8");
6781
+ const lines = content.split("\n");
6782
+ const metaDir = codemapMetadataDir(projectDir, branch, getDataDir2());
6783
+ const symbolIndex = loadSymbolIndex(metaDir);
6784
+ const fileSymbols = symbolIndex.filter((s) => s.filePath === file);
6785
+ const result = {
6786
+ filePath: file,
6787
+ sizeBytes: Buffer.byteLength(content, "utf-8"),
6788
+ lineCount: lines.length,
6789
+ symbols: fileSymbols.map((s) => ({
6790
+ name: s.name,
6791
+ type: s.symbolType,
6792
+ line: s.startLine
6793
+ }))
6794
+ };
6795
+ if (opts.json) {
6796
+ console.log(JSON.stringify(result, null, 2));
6797
+ return;
6798
+ }
6799
+ console.log(chalk13.bold(`File Summary: ${chalk13.cyan(file)}`));
6800
+ console.log(chalk13.dim("-".repeat(40)));
6801
+ console.log(`Size: ${formatBytes2(result.sizeBytes)}`);
6802
+ console.log(`Lines: ${result.lineCount}`);
6803
+ console.log(`Symbols: ${result.symbols.length}`);
6804
+ console.log();
6805
+ if (result.symbols.length > 0) {
6806
+ console.log(chalk13.bold("Symbols:"));
6807
+ for (const sym of result.symbols) {
6808
+ console.log(` ${chalk13.dim(sym.type.padEnd(10))} ${chalk13.cyan(sym.name)} ${chalk13.dim(`L${sym.line}`)}`);
6809
+ }
6810
+ }
6811
+ });
6812
+ }
6813
+ function formatBytes2(bytes) {
6814
+ if (bytes < 1024) return `${bytes}B`;
6815
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
6816
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
6817
+ }
6818
+
6819
+ // src/commands/coupling.ts
6820
+ import chalk14 from "chalk";
6821
+ function registerCoupling(program2) {
6822
+ program2.command("coupling").description("Analyze afferent/efferent coupling and instability").option("--module <path>", "Filter to a directory prefix (e.g. src/routes/)").option("-l, --limit <n>", "Max results", "50").option("--json", "Output as JSON").action(async (opts) => {
6823
+ const projectDir = program2.opts().cwd || process.cwd();
6824
+ const branch = getCurrentBranch(projectDir);
6825
+ const graphFile = depgraphGraphFile(projectDir, branch, getDataDir2());
6826
+ let graph;
6827
+ try {
6828
+ graph = loadGraph(graphFile);
6829
+ } catch {
6830
+ console.error(chalk14.red("Dependency graph not available. Run `codemap index` first."));
6831
+ process.exit(1);
6832
+ return;
6833
+ }
6834
+ let coupling = computeCoupling(graph);
6835
+ if (opts.module) {
6836
+ coupling = coupling.filter((c2) => c2.filePath.startsWith(opts.module));
6837
+ }
6838
+ coupling.sort((a, b) => b.instability - a.instability);
6839
+ coupling = coupling.slice(0, parseInt(opts.limit, 10));
6840
+ const results = coupling.map((c2) => ({
6841
+ file: c2.filePath,
6842
+ fanIn: c2.afferentCoupling,
6843
+ fanOut: c2.efferentCoupling,
6844
+ instability: Math.round(c2.instability * 1e3) / 1e3
6845
+ }));
6846
+ if (opts.json) {
6847
+ console.log(JSON.stringify(results, null, 2));
6848
+ return;
6849
+ }
6850
+ console.log(chalk14.bold("Coupling Metrics (sorted by instability)"));
6851
+ console.log(chalk14.dim("-".repeat(60)));
6852
+ console.log(
6853
+ `${"File".padEnd(40)} ${"In".padStart(4)} ${"Out".padStart(4)} ${"Inst".padStart(6)}`
6854
+ );
6855
+ console.log(chalk14.dim("-".repeat(60)));
6856
+ for (const r2 of results) {
6857
+ const instColor = r2.instability >= 0.8 ? chalk14.red : r2.instability >= 0.5 ? chalk14.yellow : chalk14.green;
6858
+ console.log(
6859
+ `${chalk14.cyan(r2.file.slice(-38).padEnd(40))} ${String(r2.fanIn).padStart(4)} ${String(r2.fanOut).padStart(4)} ${instColor(String(r2.instability).padStart(6))}`
6860
+ );
6861
+ }
6862
+ });
6863
+ }
6864
+
6865
+ // src/commands/graph-stats.ts
6866
+ import chalk15 from "chalk";
6867
+ function registerGraphStats(program2) {
6868
+ program2.command("graph-stats").description("Show aggregate dependency graph statistics").option("--json", "Output as JSON").action(async (opts) => {
6869
+ const projectDir = program2.opts().cwd || process.cwd();
6870
+ const branch = getCurrentBranch(projectDir);
6871
+ let metrics;
6872
+ try {
6873
+ metrics = loadMetrics(depgraphMetricsFile(projectDir, branch, getDataDir2()));
6874
+ } catch {
6875
+ console.error(chalk15.red("Dependency metrics not available. Run `codemap index` first."));
6876
+ process.exit(1);
6877
+ return;
6878
+ }
6879
+ const result = {
6880
+ totalFiles: metrics.totalFiles,
6881
+ totalEdges: metrics.totalEdges,
6882
+ totalDefinitions: metrics.totalDefinitions,
6883
+ totalReferences: metrics.totalReferences,
6884
+ cycleCount: metrics.cycles.length,
6885
+ avgFanIn: Math.round(metrics.avgFanIn * 100) / 100,
6886
+ avgFanOut: Math.round(metrics.avgFanOut * 100) / 100,
6887
+ maxFanIn: metrics.maxFanIn,
6888
+ maxFanOut: metrics.maxFanOut,
6889
+ hotspots: metrics.hotspots.slice(0, 10).map((h) => ({
6890
+ file: h.filePath,
6891
+ connectivity: h.connectivity,
6892
+ pageRank: Math.round(h.pageRank * 1e4) / 1e4
6893
+ }))
6894
+ };
6895
+ if (opts.json) {
6896
+ console.log(JSON.stringify(result, null, 2));
6897
+ return;
6898
+ }
6899
+ console.log(chalk15.bold("Dependency Graph Statistics"));
6900
+ console.log(chalk15.dim("-".repeat(40)));
6901
+ console.log(`Files: ${result.totalFiles}`);
6902
+ console.log(`Edges: ${result.totalEdges}`);
6903
+ console.log(`Definitions: ${result.totalDefinitions}`);
6904
+ console.log(`References: ${result.totalReferences}`);
6905
+ console.log(`Cycles: ${result.cycleCount}`);
6906
+ console.log(`Avg fan-in: ${result.avgFanIn}`);
6907
+ console.log(`Avg fan-out: ${result.avgFanOut}`);
6908
+ console.log(`Max fan-in: ${result.maxFanIn}`);
6909
+ console.log(`Max fan-out: ${result.maxFanOut}`);
6910
+ if (result.hotspots.length > 0) {
6911
+ console.log();
6912
+ console.log(chalk15.bold("Top Hotspots:"));
6913
+ for (const h of result.hotspots) {
6914
+ console.log(
6915
+ ` ${chalk15.cyan(h.file)} ${chalk15.dim(`connectivity=${h.connectivity} rank=${h.pageRank}`)}`
6916
+ );
6917
+ }
6918
+ }
6919
+ });
6920
+ }
6921
+
6922
+ // src/commands/rebuild.ts
6923
+ import chalk16 from "chalk";
6924
+ function registerRebuild(program2) {
6925
+ program2.command("rebuild-depgraph").description("Rebuild dependency graph from scratch using tree-sitter tag extraction").option("--json", "Output as JSON").action(async (opts) => {
6926
+ const projectDir = program2.opts().cwd || process.cwd();
6927
+ const config = loadCliConfig(projectDir);
6928
+ bridgeEnvVars(config);
6929
+ const branch = getCurrentBranch(projectDir);
6930
+ const dataDir = getDataDir2();
6931
+ try {
6932
+ const result = await rebuildDepgraph(projectDir, { branch, dataDir });
6933
+ if (result.taggedFiles === 0) {
6934
+ console.error(chalk16.yellow("No tags extracted \u2014 check that the project contains supported language files."));
6935
+ return;
6936
+ }
6937
+ if (opts.json) {
6938
+ console.log(JSON.stringify({
6939
+ message: "Dependency graph rebuilt",
6940
+ totalFiles: result.nodeCount,
6941
+ totalEdges: result.edgeCount,
6942
+ definitions: result.definitionCount,
6943
+ references: result.referenceCount,
6944
+ cycleCount: result.cycleCount,
6945
+ taggedFiles: result.taggedFiles,
6946
+ durationMs: result.durationMs
6947
+ }, null, 2));
6948
+ return;
6949
+ }
6950
+ console.log(chalk16.green("Dependency graph rebuilt"));
6951
+ console.log(chalk16.dim("-".repeat(40)));
6952
+ console.log(`Tagged files: ${result.taggedFiles}/${result.totalFiles}`);
6953
+ console.log(`Nodes: ${result.nodeCount}`);
6954
+ console.log(`Edges: ${result.edgeCount}`);
6955
+ console.log(`Definitions: ${result.definitionCount}`);
6956
+ console.log(`References: ${result.referenceCount}`);
6957
+ console.log(`Cycles: ${result.cycleCount}`);
6958
+ console.log(`Duration: ${(result.durationMs / 1e3).toFixed(1)}s`);
6959
+ } catch (err) {
6960
+ const msg = err instanceof Error ? err.message : String(err);
6961
+ console.error(chalk16.red(`Depgraph rebuild failed: ${msg}`));
6962
+ process.exit(1);
6963
+ }
6964
+ });
6965
+ }
6966
+
6612
6967
  // src/index.ts
6613
6968
  var __dirname = path13.dirname(fileURLToPath(import.meta.url));
6614
6969
  var grammarsDir = path13.join(__dirname, "grammars");
6615
6970
  setGrammarDir(grammarsDir);
6616
6971
  var program = new Command();
6617
- program.name("codemap").description("Code intelligence CLI \u2014 hybrid search, dependency analysis, PageRank").version("0.3.1").option("--cwd <dir>", "Project directory (default: cwd)");
6972
+ program.name("codemap").description("Code intelligence CLI \u2014 hybrid search, dependency analysis, PageRank").version("0.3.3").option("--cwd <dir>", "Project directory (default: cwd)");
6618
6973
  registerSearch(program);
6619
6974
  registerSymbols(program);
6620
6975
  registerIndex(program);
@@ -6628,4 +6983,8 @@ registerCycles(program);
6628
6983
  registerConfig(program);
6629
6984
  registerInit(program);
6630
6985
  registerIgnore(program);
6986
+ registerSummary(program);
6987
+ registerCoupling(program);
6988
+ registerGraphStats(program);
6989
+ registerRebuild(program);
6631
6990
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulpi/codemap",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "type": "module",
5
5
  "description": "Standalone code intelligence CLI — hybrid vector + BM25 search, dependency analysis, PageRank",
6
6
  "bin": {