@vheins/local-memory-mcp 0.9.5 → 0.9.8
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.
|
@@ -3,8 +3,8 @@ import { fileURLToPath } from "url";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
5
|
var pkgVersion = "0.1.0";
|
|
6
|
-
if ("0.9.
|
|
7
|
-
pkgVersion = "0.9.
|
|
6
|
+
if ("0.9.8") {
|
|
7
|
+
pkgVersion = "0.9.8";
|
|
8
8
|
} else {
|
|
9
9
|
let searchDir = __dirname;
|
|
10
10
|
for (let i = 0; i < 5; i++) {
|
package/dist/dashboard/server.js
CHANGED
package/dist/mcp/server.js
CHANGED
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
toContextSlug,
|
|
56
56
|
updateSessionFromInitialize,
|
|
57
57
|
updateSessionRoots
|
|
58
|
-
} from "../chunk-
|
|
58
|
+
} from "../chunk-CSEGUZWJ.js";
|
|
59
59
|
|
|
60
60
|
// src/mcp/server.ts
|
|
61
61
|
import readline from "readline";
|
|
@@ -1901,10 +1901,63 @@ async function handleStandardStore(params, db2, vectors2) {
|
|
|
1901
1901
|
|
|
1902
1902
|
// src/mcp/tools/standard.search.ts
|
|
1903
1903
|
var HYBRID_WEIGHTS_STANDARD = {
|
|
1904
|
-
similarity: 0.
|
|
1905
|
-
vector: 0.
|
|
1906
|
-
|
|
1904
|
+
similarity: 0.4,
|
|
1905
|
+
vector: 0.25,
|
|
1906
|
+
keyword: 0.3,
|
|
1907
|
+
usage: 0.05
|
|
1907
1908
|
};
|
|
1909
|
+
function tokenizeSearchText(value) {
|
|
1910
|
+
return value.toLowerCase().split(/[^a-z0-9]+/g).map((token) => token.trim()).filter((token) => token.length >= 2);
|
|
1911
|
+
}
|
|
1912
|
+
function scoreKeywordRelevance(query, standard) {
|
|
1913
|
+
const queryTokens = Array.from(new Set(tokenizeSearchText(query)));
|
|
1914
|
+
if (queryTokens.length === 0) return 0;
|
|
1915
|
+
const titleText = standard.title.toLowerCase();
|
|
1916
|
+
const contextText = standard.context.toLowerCase();
|
|
1917
|
+
const tagText = standard.tags.join(" ").toLowerCase();
|
|
1918
|
+
const stackText = standard.stack.join(" ").toLowerCase();
|
|
1919
|
+
const contentText = standard.content.toLowerCase();
|
|
1920
|
+
const queryPhrase = query.trim().toLowerCase();
|
|
1921
|
+
let titleMatches = 0;
|
|
1922
|
+
let contextMatches = 0;
|
|
1923
|
+
let tagMatches = 0;
|
|
1924
|
+
let stackMatches = 0;
|
|
1925
|
+
let contentMatches = 0;
|
|
1926
|
+
for (const token of queryTokens) {
|
|
1927
|
+
if (titleText.includes(token)) titleMatches += 1;
|
|
1928
|
+
if (contextText.includes(token)) contextMatches += 1;
|
|
1929
|
+
if (tagText.includes(token)) tagMatches += 1;
|
|
1930
|
+
if (stackText.includes(token)) stackMatches += 1;
|
|
1931
|
+
if (contentText.includes(token)) contentMatches += 1;
|
|
1932
|
+
}
|
|
1933
|
+
const titleCoverage = titleMatches / queryTokens.length;
|
|
1934
|
+
const contextCoverage = contextMatches / queryTokens.length;
|
|
1935
|
+
const tagCoverage = tagMatches / queryTokens.length;
|
|
1936
|
+
const stackCoverage = stackMatches / queryTokens.length;
|
|
1937
|
+
const contentCoverage = contentMatches / queryTokens.length;
|
|
1938
|
+
const exactPhraseBonus = queryPhrase.length >= 6 && (titleText.includes(queryPhrase) || contentText.includes(queryPhrase) || tagText.includes(queryPhrase)) ? 0.2 : 0;
|
|
1939
|
+
return Math.min(
|
|
1940
|
+
1,
|
|
1941
|
+
titleCoverage * 0.45 + contextCoverage * 0.15 + tagCoverage * 0.15 + stackCoverage * 0.05 + contentCoverage * 0.2 + exactPhraseBonus
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
function collectMatchedTerms(query, standard) {
|
|
1945
|
+
const queryTokens = Array.from(new Set(tokenizeSearchText(query)));
|
|
1946
|
+
if (queryTokens.length === 0) return [];
|
|
1947
|
+
const searchableFields = [
|
|
1948
|
+
standard.title.toLowerCase(),
|
|
1949
|
+
standard.context.toLowerCase(),
|
|
1950
|
+
standard.tags.join(" ").toLowerCase(),
|
|
1951
|
+
standard.stack.join(" ").toLowerCase(),
|
|
1952
|
+
standard.content.toLowerCase()
|
|
1953
|
+
];
|
|
1954
|
+
return queryTokens.filter((token) => searchableFields.some((field) => field.includes(token)));
|
|
1955
|
+
}
|
|
1956
|
+
function toConfidence(finalScore, keywordScore) {
|
|
1957
|
+
if (finalScore >= 0.72 || keywordScore >= 0.85) return "high";
|
|
1958
|
+
if (finalScore >= 0.42 || keywordScore >= 0.45) return "medium";
|
|
1959
|
+
return "low";
|
|
1960
|
+
}
|
|
1908
1961
|
async function handleStandardSearch(params, db2, vectors2) {
|
|
1909
1962
|
const validated = StandardSearchSchema.parse(params);
|
|
1910
1963
|
const searchQuery = expandQuery(validated.query || "", void 0);
|
|
@@ -1936,13 +1989,18 @@ async function handleStandardSearch(params, db2, vectors2) {
|
|
|
1936
1989
|
if (similarityResults.length > 0) {
|
|
1937
1990
|
scoredStandards = similarityResults.map((candidate) => {
|
|
1938
1991
|
const vectorScore = vectorScoreMap.get(candidate.id) ?? 0;
|
|
1992
|
+
const keywordScore = scoreKeywordRelevance(validated.query || "", candidate);
|
|
1993
|
+
const matchedTerms = collectMatchedTerms(validated.query || "", candidate);
|
|
1939
1994
|
const usageScore = Math.min(1, candidate.hit_count / 10);
|
|
1940
|
-
const finalScore = candidate.similarity * HYBRID_WEIGHTS_STANDARD.similarity + vectorScore * HYBRID_WEIGHTS_STANDARD.vector + usageScore * HYBRID_WEIGHTS_STANDARD.usage;
|
|
1995
|
+
const finalScore = candidate.similarity * HYBRID_WEIGHTS_STANDARD.similarity + vectorScore * HYBRID_WEIGHTS_STANDARD.vector + keywordScore * HYBRID_WEIGHTS_STANDARD.keyword + usageScore * HYBRID_WEIGHTS_STANDARD.usage;
|
|
1941
1996
|
return {
|
|
1942
1997
|
standard: candidate,
|
|
1943
1998
|
similarityScore: candidate.similarity,
|
|
1944
1999
|
vectorScore,
|
|
1945
|
-
|
|
2000
|
+
keywordScore,
|
|
2001
|
+
matchedTerms,
|
|
2002
|
+
finalScore,
|
|
2003
|
+
confidence: toConfidence(finalScore, keywordScore)
|
|
1946
2004
|
};
|
|
1947
2005
|
});
|
|
1948
2006
|
} else if (vectorResults.length > 0) {
|
|
@@ -1951,43 +2009,58 @@ async function handleStandardSearch(params, db2, vectors2) {
|
|
|
1951
2009
|
scoredStandards = vectorResults.flatMap((row) => {
|
|
1952
2010
|
const standard = standardMap.get(row.id);
|
|
1953
2011
|
if (!standard) return [];
|
|
2012
|
+
const keywordScore = scoreKeywordRelevance(validated.query || "", standard);
|
|
2013
|
+
const matchedTerms = collectMatchedTerms(validated.query || "", standard);
|
|
1954
2014
|
const usageScore = Math.min(1, standard.hit_count / 10);
|
|
2015
|
+
const finalScore = row.score * HYBRID_WEIGHTS_STANDARD.vector + keywordScore * HYBRID_WEIGHTS_STANDARD.keyword + usageScore * (1 - HYBRID_WEIGHTS_STANDARD.vector - HYBRID_WEIGHTS_STANDARD.keyword);
|
|
1955
2016
|
return [
|
|
1956
2017
|
{
|
|
1957
2018
|
standard,
|
|
1958
2019
|
similarityScore: 0,
|
|
1959
2020
|
vectorScore: row.score,
|
|
1960
|
-
|
|
2021
|
+
keywordScore,
|
|
2022
|
+
matchedTerms,
|
|
2023
|
+
finalScore,
|
|
2024
|
+
confidence: toConfidence(finalScore, keywordScore)
|
|
1961
2025
|
}
|
|
1962
2026
|
];
|
|
1963
2027
|
});
|
|
1964
2028
|
}
|
|
1965
2029
|
} catch (error) {
|
|
1966
2030
|
logger.warn("Standard vector search failed, using similarity only", { error: String(error) });
|
|
1967
|
-
scoredStandards = similarityResults.map((candidate) =>
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2031
|
+
scoredStandards = similarityResults.map((candidate) => {
|
|
2032
|
+
const keywordScore = scoreKeywordRelevance(validated.query || "", candidate);
|
|
2033
|
+
const matchedTerms = collectMatchedTerms(validated.query || "", candidate);
|
|
2034
|
+
const finalScore = candidate.similarity * (HYBRID_WEIGHTS_STANDARD.similarity + HYBRID_WEIGHTS_STANDARD.vector * 0.5) + keywordScore * HYBRID_WEIGHTS_STANDARD.keyword + Math.min(1, candidate.hit_count / 10) * HYBRID_WEIGHTS_STANDARD.usage;
|
|
2035
|
+
return {
|
|
2036
|
+
standard: candidate,
|
|
2037
|
+
similarityScore: candidate.similarity,
|
|
2038
|
+
vectorScore: 0,
|
|
2039
|
+
keywordScore,
|
|
2040
|
+
matchedTerms,
|
|
2041
|
+
finalScore,
|
|
2042
|
+
confidence: toConfidence(finalScore, keywordScore)
|
|
2043
|
+
};
|
|
2044
|
+
});
|
|
1973
2045
|
}
|
|
1974
2046
|
scoredStandards.sort((a, b) => b.finalScore - a.finalScore);
|
|
1975
2047
|
const threshold = scoredStandards.length <= 5 ? 0.08 : 0.2;
|
|
1976
|
-
let results = scoredStandards.filter((candidate) => candidate.finalScore >= threshold)
|
|
2048
|
+
let results = scoredStandards.filter((candidate) => candidate.finalScore >= threshold);
|
|
1977
2049
|
if (results.length === 0 && scoredStandards.length > 0) {
|
|
1978
|
-
results = [scoredStandards[0]
|
|
2050
|
+
results = [scoredStandards[0]];
|
|
1979
2051
|
}
|
|
1980
2052
|
const paginatedResults = results.slice(validated.offset, validated.offset + validated.limit);
|
|
1981
|
-
db2.standards.incrementHitCounts(paginatedResults.map((standard) => standard.id));
|
|
2053
|
+
db2.standards.incrementHitCounts(paginatedResults.map(({ standard }) => standard.id));
|
|
1982
2054
|
logger.info("[Tool] standard.search - searched coding standards", {
|
|
1983
2055
|
resultCount: paginatedResults.length,
|
|
1984
2056
|
stack: validated.stack,
|
|
1985
2057
|
language: validated.language,
|
|
1986
2058
|
context: validated.context,
|
|
1987
|
-
version: validated.version
|
|
2059
|
+
version: validated.version,
|
|
2060
|
+
topConfidence: paginatedResults[0]?.confidence
|
|
1988
2061
|
});
|
|
1989
|
-
const COLUMNS = ["code", "id", "title", "context", "language", "scope", "tags", "updated_at"];
|
|
1990
|
-
const rows = paginatedResults.map((standard) => [
|
|
2062
|
+
const COLUMNS = ["code", "id", "title", "context", "language", "scope", "tags", "confidence", "score", "matched_terms", "updated_at"];
|
|
2063
|
+
const rows = paginatedResults.map(({ standard, confidence, finalScore, matchedTerms }) => [
|
|
1991
2064
|
standard.code ?? "-",
|
|
1992
2065
|
standard.id,
|
|
1993
2066
|
standard.title,
|
|
@@ -1995,18 +2068,21 @@ async function handleStandardSearch(params, db2, vectors2) {
|
|
|
1995
2068
|
standard.language || "-",
|
|
1996
2069
|
standard.is_global ? "global" : standard.repo || "-",
|
|
1997
2070
|
standard.tags.join(", "),
|
|
2071
|
+
confidence,
|
|
2072
|
+
Number(finalScore.toFixed(3)),
|
|
2073
|
+
matchedTerms.join(", "),
|
|
1998
2074
|
standard.updated_at
|
|
1999
2075
|
]);
|
|
2000
2076
|
let contentSummary;
|
|
2001
2077
|
if (paginatedResults.length > 0) {
|
|
2002
2078
|
const parts = [
|
|
2003
2079
|
"Standards:",
|
|
2004
|
-
"- title|context|language|scope",
|
|
2080
|
+
"- code|confidence|matched_terms|title|context|language|scope",
|
|
2005
2081
|
...paginatedResults.map(
|
|
2006
|
-
(standard) => `- ${standard.title}|${standard.context}|${standard.language || "-"}|${standard.is_global ? "global" : standard.repo || "-"}`
|
|
2082
|
+
({ standard, confidence, matchedTerms }) => `- ${standard.code ?? "-"}|${confidence}|${matchedTerms.join(", ")}|${standard.title}|${standard.context}|${standard.language || "-"}|${standard.is_global ? "global" : standard.repo || "-"}`
|
|
2007
2083
|
),
|
|
2008
2084
|
"",
|
|
2009
|
-
"Use standard-detail with
|
|
2085
|
+
"Use standard-detail with code for full content."
|
|
2010
2086
|
];
|
|
2011
2087
|
contentSummary = parts.join("\n");
|
|
2012
2088
|
} else {
|