@ulpi/codemap 0.3.2 → 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 +261 -1
  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
@@ -5892,8 +5892,64 @@ function registerInit(program2) {
5892
5892
  console.log(` ${chalk3.cyan("codemap index")} Index your project`);
5893
5893
  console.log(` ${chalk3.cyan("codemap search")} Search your code`);
5894
5894
  console.log(` ${chalk3.cyan("codemap serve")} Start MCP server`);
5895
+ console.log();
5896
+ printAgentSnippet();
5895
5897
  });
5896
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
+ }
5897
5953
  var ULPI_API_BASE = "https://codemap.ulpi.io";
5898
5954
  var ULPI_AUTH_BASE = "https://codemap.ulpi.io/api/v1";
5899
5955
  function ask(prompt) {
@@ -6708,12 +6764,212 @@ function registerIgnore(program2) {
6708
6764
  });
6709
6765
  }
6710
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
+
6711
6967
  // src/index.ts
6712
6968
  var __dirname = path13.dirname(fileURLToPath(import.meta.url));
6713
6969
  var grammarsDir = path13.join(__dirname, "grammars");
6714
6970
  setGrammarDir(grammarsDir);
6715
6971
  var program = new Command();
6716
- program.name("codemap").description("Code intelligence CLI \u2014 hybrid search, dependency analysis, PageRank").version("0.3.2").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)");
6717
6973
  registerSearch(program);
6718
6974
  registerSymbols(program);
6719
6975
  registerIndex(program);
@@ -6727,4 +6983,8 @@ registerCycles(program);
6727
6983
  registerConfig(program);
6728
6984
  registerInit(program);
6729
6985
  registerIgnore(program);
6986
+ registerSummary(program);
6987
+ registerCoupling(program);
6988
+ registerGraphStats(program);
6989
+ registerRebuild(program);
6730
6990
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulpi/codemap",
3
- "version": "0.3.2",
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": {