@liendev/lien 0.29.0 → 0.30.0
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/dist/index.js +60 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8524,7 +8524,8 @@ var ListFunctionsSchema = external_exports.object({
|
|
|
8524
8524
|
),
|
|
8525
8525
|
language: external_exports.string().optional().describe(
|
|
8526
8526
|
"Filter by programming language.\n\nExamples: 'typescript', 'python', 'javascript', 'php'\n\nIf omitted, searches all languages."
|
|
8527
|
-
)
|
|
8527
|
+
),
|
|
8528
|
+
symbolType: external_exports.enum(["function", "method", "class", "interface"]).optional().describe("Filter by symbol type. If omitted, returns all types.")
|
|
8528
8529
|
});
|
|
8529
8530
|
|
|
8530
8531
|
// src/mcp/schemas/dependents.schema.ts
|
|
@@ -8647,6 +8648,10 @@ Batch calls are more efficient than multiple single-file calls.`
|
|
|
8647
8648
|
Examples:
|
|
8648
8649
|
- "Show all controllers" \u2192 list_functions({ pattern: ".*Controller.*" })
|
|
8649
8650
|
- "Find service classes" \u2192 list_functions({ pattern: ".*Service$" })
|
|
8651
|
+
- "List all class methods" \u2192 list_functions({ symbolType: "method" })
|
|
8652
|
+
- "Find standalone functions" \u2192 list_functions({ symbolType: "function" })
|
|
8653
|
+
|
|
8654
|
+
Filter by symbol type (function, method, class, interface) to narrow results.
|
|
8650
8655
|
|
|
8651
8656
|
10x faster than semantic_search for structural/architectural queries. Use semantic_search instead when searching by what code DOES.`
|
|
8652
8657
|
),
|
|
@@ -8803,6 +8808,25 @@ function deduplicateResults(results) {
|
|
|
8803
8808
|
return true;
|
|
8804
8809
|
});
|
|
8805
8810
|
}
|
|
8811
|
+
function cleanMetadataValue(key, value) {
|
|
8812
|
+
if (value === void 0 || value === "") return null;
|
|
8813
|
+
if (Array.isArray(value)) {
|
|
8814
|
+
const filtered = value.filter((v) => v !== "");
|
|
8815
|
+
return filtered.length > 0 ? filtered : null;
|
|
8816
|
+
}
|
|
8817
|
+
if (key === "symbols" && typeof value === "object" && value !== null) {
|
|
8818
|
+
const symbols = value;
|
|
8819
|
+
const filterArr = (arr) => Array.isArray(arr) ? arr.filter((s) => s !== "") : [];
|
|
8820
|
+
const filtered = {
|
|
8821
|
+
functions: filterArr(symbols.functions),
|
|
8822
|
+
classes: filterArr(symbols.classes),
|
|
8823
|
+
interfaces: filterArr(symbols.interfaces)
|
|
8824
|
+
};
|
|
8825
|
+
const hasAny = filtered.functions.length > 0 || filtered.classes.length > 0 || filtered.interfaces.length > 0;
|
|
8826
|
+
return hasAny ? filtered : null;
|
|
8827
|
+
}
|
|
8828
|
+
return value;
|
|
8829
|
+
}
|
|
8806
8830
|
function pickMetadata(metadata, allowlist) {
|
|
8807
8831
|
const result = {
|
|
8808
8832
|
file: metadata.file,
|
|
@@ -8812,8 +8836,9 @@ function pickMetadata(metadata, allowlist) {
|
|
|
8812
8836
|
const out = result;
|
|
8813
8837
|
for (const key of allowlist) {
|
|
8814
8838
|
if (key === "file" || key === "startLine" || key === "endLine") continue;
|
|
8815
|
-
|
|
8816
|
-
|
|
8839
|
+
const cleaned = cleanMetadataValue(key, metadata[key]);
|
|
8840
|
+
if (cleaned !== null) {
|
|
8841
|
+
out[key] = cleaned;
|
|
8817
8842
|
}
|
|
8818
8843
|
}
|
|
8819
8844
|
return result;
|
|
@@ -8881,17 +8906,12 @@ async function handleSemanticSearch(args, ctx) {
|
|
|
8881
8906
|
}
|
|
8882
8907
|
log(`Returning ${results.length} results`);
|
|
8883
8908
|
const shaped = shapeResults(results, "semantic_search");
|
|
8884
|
-
|
|
8909
|
+
return {
|
|
8885
8910
|
indexInfo: getIndexMetadata(),
|
|
8886
|
-
results: shaped
|
|
8911
|
+
results: shaped,
|
|
8912
|
+
...crossRepo && vectorDB instanceof QdrantDB && { groupedByRepo: groupResultsByRepo(shaped) },
|
|
8913
|
+
...notes.length > 0 && { note: notes.join(" ") }
|
|
8887
8914
|
};
|
|
8888
|
-
if (crossRepo && vectorDB instanceof QdrantDB) {
|
|
8889
|
-
response.groupedByRepo = groupResultsByRepo(shaped);
|
|
8890
|
-
}
|
|
8891
|
-
if (notes.length > 0) {
|
|
8892
|
-
response.note = notes.join(" ");
|
|
8893
|
-
}
|
|
8894
|
-
return response;
|
|
8895
8915
|
}
|
|
8896
8916
|
)(args);
|
|
8897
8917
|
}
|
|
@@ -9086,7 +9106,9 @@ async function findRelatedChunks(filepaths, fileChunksMap, ctx) {
|
|
|
9086
9106
|
const targetCanonical = getCanonicalPath(filepath, workspaceRoot);
|
|
9087
9107
|
relatedChunksMap[index] = related.filter((r) => {
|
|
9088
9108
|
const chunkCanonical = getCanonicalPath(r.metadata.file, workspaceRoot);
|
|
9089
|
-
|
|
9109
|
+
if (chunkCanonical === targetCanonical) return false;
|
|
9110
|
+
if (r.metadata.language === "markdown") return false;
|
|
9111
|
+
return true;
|
|
9090
9112
|
});
|
|
9091
9113
|
});
|
|
9092
9114
|
return relatedChunksMap;
|
|
@@ -9122,23 +9144,15 @@ function findTestAssociations(filepaths, allChunks, ctx) {
|
|
|
9122
9144
|
return Array.from(testFiles);
|
|
9123
9145
|
});
|
|
9124
9146
|
}
|
|
9125
|
-
function deduplicateChunks(fileChunks, relatedChunks
|
|
9126
|
-
|
|
9127
|
-
return [...fileChunks, ...relatedChunks].filter((chunk) => {
|
|
9128
|
-
const canonicalFile = getCanonicalPath(chunk.metadata.file, workspaceRoot);
|
|
9129
|
-
const chunkId = `${canonicalFile}:${chunk.metadata.startLine}-${chunk.metadata.endLine}`;
|
|
9130
|
-
if (seenChunks.has(chunkId)) return false;
|
|
9131
|
-
seenChunks.add(chunkId);
|
|
9132
|
-
return true;
|
|
9133
|
-
});
|
|
9147
|
+
function deduplicateChunks(fileChunks, relatedChunks) {
|
|
9148
|
+
return deduplicateResults([...fileChunks, ...relatedChunks]);
|
|
9134
9149
|
}
|
|
9135
|
-
function buildFilesData(filepaths, fileChunksMap, relatedChunksMap, testAssociationsMap
|
|
9150
|
+
function buildFilesData(filepaths, fileChunksMap, relatedChunksMap, testAssociationsMap) {
|
|
9136
9151
|
const filesData = {};
|
|
9137
9152
|
filepaths.forEach((filepath, i) => {
|
|
9138
9153
|
const dedupedChunks = deduplicateChunks(
|
|
9139
9154
|
fileChunksMap[i],
|
|
9140
|
-
relatedChunksMap[i] || []
|
|
9141
|
-
workspaceRoot
|
|
9155
|
+
relatedChunksMap[i] || []
|
|
9142
9156
|
);
|
|
9143
9157
|
filesData[filepath] = {
|
|
9144
9158
|
chunks: dedupedChunks,
|
|
@@ -9216,8 +9230,7 @@ async function handleGetFilesContext(args, ctx) {
|
|
|
9216
9230
|
filepaths,
|
|
9217
9231
|
fileChunksMap,
|
|
9218
9232
|
relatedChunksMap,
|
|
9219
|
-
testAssociationsMap
|
|
9220
|
-
workspaceRoot
|
|
9233
|
+
testAssociationsMap
|
|
9221
9234
|
);
|
|
9222
9235
|
const totalChunks = Object.values(filesData).reduce(
|
|
9223
9236
|
(sum, f) => sum + f.chunks.length,
|
|
@@ -9232,6 +9245,7 @@ async function handleGetFilesContext(args, ctx) {
|
|
|
9232
9245
|
}
|
|
9233
9246
|
|
|
9234
9247
|
// src/mcp/handlers/list-functions.ts
|
|
9248
|
+
import { SYMBOL_TYPE_MATCHES } from "@liendev/core";
|
|
9235
9249
|
async function performContentScan(vectorDB, args, log) {
|
|
9236
9250
|
log("Falling back to content scan...");
|
|
9237
9251
|
let results = await vectorDB.scanWithFilter({
|
|
@@ -9246,6 +9260,13 @@ async function performContentScan(vectorDB, args, log) {
|
|
|
9246
9260
|
return symbolName && regex.test(symbolName);
|
|
9247
9261
|
});
|
|
9248
9262
|
}
|
|
9263
|
+
if (args.symbolType) {
|
|
9264
|
+
const allowedTypes = SYMBOL_TYPE_MATCHES[args.symbolType];
|
|
9265
|
+
results = results.filter((r) => {
|
|
9266
|
+
const recordType2 = r.metadata?.symbolType;
|
|
9267
|
+
return recordType2 && allowedTypes?.has(recordType2);
|
|
9268
|
+
});
|
|
9269
|
+
}
|
|
9249
9270
|
return {
|
|
9250
9271
|
results: results.slice(0, 50),
|
|
9251
9272
|
method: "content"
|
|
@@ -9263,9 +9284,10 @@ async function handleListFunctions(args, ctx) {
|
|
|
9263
9284
|
const results = await vectorDB.querySymbols({
|
|
9264
9285
|
language: validatedArgs.language,
|
|
9265
9286
|
pattern: validatedArgs.pattern,
|
|
9287
|
+
symbolType: validatedArgs.symbolType,
|
|
9266
9288
|
limit: 50
|
|
9267
9289
|
});
|
|
9268
|
-
if (results.length === 0 && (validatedArgs.language || validatedArgs.pattern)) {
|
|
9290
|
+
if (results.length === 0 && (validatedArgs.language || validatedArgs.pattern || validatedArgs.symbolType)) {
|
|
9269
9291
|
log("No symbol results, falling back to content scan...");
|
|
9270
9292
|
queryResult = await performContentScan(vectorDB, validatedArgs, log);
|
|
9271
9293
|
} else {
|
|
@@ -9796,7 +9818,11 @@ async function handleGetComplexity(args, ctx) {
|
|
|
9796
9818
|
threshold,
|
|
9797
9819
|
top ?? 10
|
|
9798
9820
|
);
|
|
9799
|
-
const
|
|
9821
|
+
const note = buildCrossRepoFallbackNote(fallback);
|
|
9822
|
+
if (note) {
|
|
9823
|
+
log("Warning: crossRepo=true requires Qdrant backend. Falling back to single-repo analysis.", "warning");
|
|
9824
|
+
}
|
|
9825
|
+
return {
|
|
9800
9826
|
indexInfo: getIndexMetadata(),
|
|
9801
9827
|
summary: {
|
|
9802
9828
|
filesAnalyzed: report.summary.filesAnalyzed,
|
|
@@ -9805,17 +9831,12 @@ async function handleGetComplexity(args, ctx) {
|
|
|
9805
9831
|
violationCount: violations.length,
|
|
9806
9832
|
bySeverity
|
|
9807
9833
|
},
|
|
9808
|
-
violations: topViolations
|
|
9834
|
+
violations: topViolations,
|
|
9835
|
+
...crossRepo && !fallback && allChunks.length > 0 && {
|
|
9836
|
+
groupedByRepo: groupViolationsByRepo(topViolations, allChunks)
|
|
9837
|
+
},
|
|
9838
|
+
...note && { note }
|
|
9809
9839
|
};
|
|
9810
|
-
if (crossRepo && !fallback && allChunks.length > 0) {
|
|
9811
|
-
response.groupedByRepo = groupViolationsByRepo(topViolations, allChunks);
|
|
9812
|
-
}
|
|
9813
|
-
const note = buildCrossRepoFallbackNote(fallback);
|
|
9814
|
-
if (note) {
|
|
9815
|
-
log("Warning: crossRepo=true requires Qdrant backend. Falling back to single-repo analysis.", "warning");
|
|
9816
|
-
response.note = note;
|
|
9817
|
-
}
|
|
9818
|
-
return response;
|
|
9819
9840
|
}
|
|
9820
9841
|
)(args);
|
|
9821
9842
|
}
|