@ulpi/codemap 0.3.1 → 0.3.2

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 (2) hide show
  1. package/dist/index.js +105 -6
  2. package/package.json +1 -1
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");
@@ -6038,7 +6137,7 @@ function loadMetricsSafe(projectDir, branch) {
6038
6137
  function createCodemapMcpServer() {
6039
6138
  const server = new McpServer({
6040
6139
  name: "codemap",
6041
- version: "0.3.1"
6140
+ version: "0.3.2"
6042
6141
  });
6043
6142
  const dataDir = getDataDir2();
6044
6143
  server.tool(
@@ -6614,7 +6713,7 @@ var __dirname = path13.dirname(fileURLToPath(import.meta.url));
6614
6713
  var grammarsDir = path13.join(__dirname, "grammars");
6615
6714
  setGrammarDir(grammarsDir);
6616
6715
  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)");
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)");
6618
6717
  registerSearch(program);
6619
6718
  registerSymbols(program);
6620
6719
  registerIndex(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulpi/codemap",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "Standalone code intelligence CLI — hybrid vector + BM25 search, dependency analysis, PageRank",
6
6
  "bin": {