@ulpi/codemap 0.3.0 → 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.
- package/README.md +1 -1
- package/dist/index.js +105 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ codemap search "authentication middleware"
|
|
|
33
33
|
|----------|--------|------|------|----------|
|
|
34
34
|
| **Ollama** | `nomic-embed-text` (768), `mxbai-embed-large` (1024), `all-minilm` (384) | varies | Free | Ollama running locally |
|
|
35
35
|
| **OpenAI** | `text-embedding-3-small` (1536), `text-embedding-3-large` (3072) | varies | Paid | `OPENAI_API_KEY` |
|
|
36
|
-
| **ULPI Cloud** | `
|
|
36
|
+
| **ULPI Cloud** | `qwen3-embedding:4b` (2048) | 2048 | Managed | `ULPI_API_KEY` |
|
|
37
37
|
|
|
38
38
|
`codemap init` walks you through provider and model selection interactively.
|
|
39
39
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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);
|