@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.
- package/README.md +44 -1
- package/dist/index.js +261 -1
- 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.
|
|
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();
|