@vheins/local-memory-mcp 0.8.48 → 0.9.1
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/{chunk-QCCKQZ5G.js → chunk-RLAFKS7B.js} +381 -40
- package/dist/dashboard/public/assets/{index-BaWIMG1g.css → index-CNE1bKgp.css} +1 -1
- package/dist/dashboard/public/assets/index-CWCaRdJn.js +86 -0
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/server.js +179 -7
- package/dist/mcp/server.js +262 -105
- package/dist/prompts/csl-scrapper.md +5 -1
- package/package.json +1 -1
- package/dist/dashboard/public/assets/index-DEY6a44I.js +0 -86
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-CWCaRdJn.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CNE1bKgp.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="app"></div>
|
package/dist/dashboard/server.js
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
import {
|
|
3
3
|
MCP_PROTOCOL_VERSION,
|
|
4
4
|
PROMPTS,
|
|
5
|
+
RealVectorStore,
|
|
5
6
|
SQLiteStore,
|
|
6
7
|
TOOL_DEFINITIONS,
|
|
7
8
|
addLogSink,
|
|
9
|
+
buildStandardVectorText,
|
|
8
10
|
createFileSink,
|
|
9
11
|
listResources,
|
|
10
12
|
logger
|
|
11
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-RLAFKS7B.js";
|
|
12
14
|
|
|
13
15
|
// src/dashboard/server.ts
|
|
14
16
|
import express from "express";
|
|
@@ -206,10 +208,14 @@ function sleep(ms) {
|
|
|
206
208
|
// src/dashboard/lib/context.ts
|
|
207
209
|
var db = await SQLiteStore.create();
|
|
208
210
|
var mcpClient = new MCPClient();
|
|
211
|
+
var vectors = new RealVectorStore(db);
|
|
209
212
|
var startTime = Date.now();
|
|
213
|
+
vectors.initialize().catch((err) => {
|
|
214
|
+
logger.warn("[Dashboard] Initial vector model loading failed. Will retry on first use.", { error: String(err) });
|
|
215
|
+
});
|
|
210
216
|
|
|
211
217
|
// src/dashboard/routes/index.ts
|
|
212
|
-
import { Router as
|
|
218
|
+
import { Router as Router5 } from "express";
|
|
213
219
|
|
|
214
220
|
// src/dashboard/routes/system.routes.ts
|
|
215
221
|
import { Router } from "express";
|
|
@@ -868,12 +874,178 @@ router3.put("/comments/:id", TasksController.updateComment);
|
|
|
868
874
|
router3.delete("/comments/:id", TasksController.deleteComment);
|
|
869
875
|
var task_routes_default = router3;
|
|
870
876
|
|
|
871
|
-
// src/dashboard/routes/
|
|
877
|
+
// src/dashboard/routes/standard.routes.ts
|
|
878
|
+
import { Router as Router4 } from "express";
|
|
879
|
+
|
|
880
|
+
// src/dashboard/controllers/StandardsController.ts
|
|
881
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
882
|
+
var StandardsController = class {
|
|
883
|
+
static async list(req, res) {
|
|
884
|
+
try {
|
|
885
|
+
await db.refresh();
|
|
886
|
+
const { repo, query, language, stack, tags, is_global } = req.query;
|
|
887
|
+
const page = Math.max(1, parseInt(req.query.page || "1", 10));
|
|
888
|
+
const pageSize = Math.min(100, Math.max(1, parseInt(req.query.pageSize || "100", 10)));
|
|
889
|
+
const stackList = typeof stack === "string" ? stack.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
890
|
+
const tagList = typeof tags === "string" ? tags.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
891
|
+
const items = db.standards.search({
|
|
892
|
+
query,
|
|
893
|
+
language,
|
|
894
|
+
stack: stackList[0],
|
|
895
|
+
tag: tagList[0],
|
|
896
|
+
repo,
|
|
897
|
+
is_global: is_global === void 0 ? void 0 : String(is_global) === "true",
|
|
898
|
+
limit: pageSize,
|
|
899
|
+
offset: (page - 1) * pageSize
|
|
900
|
+
});
|
|
901
|
+
const total = db.standards.search({
|
|
902
|
+
query,
|
|
903
|
+
language,
|
|
904
|
+
stack: stackList[0],
|
|
905
|
+
tag: tagList[0],
|
|
906
|
+
repo,
|
|
907
|
+
is_global: is_global === void 0 ? void 0 : String(is_global) === "true",
|
|
908
|
+
limit: 1e5,
|
|
909
|
+
offset: 0
|
|
910
|
+
}).length;
|
|
911
|
+
res.json(
|
|
912
|
+
jsonApiRes(items, "standard", {
|
|
913
|
+
meta: {
|
|
914
|
+
page,
|
|
915
|
+
pageSize,
|
|
916
|
+
totalItems: total,
|
|
917
|
+
totalPages: Math.ceil(total / pageSize)
|
|
918
|
+
}
|
|
919
|
+
})
|
|
920
|
+
);
|
|
921
|
+
} catch (err) {
|
|
922
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
923
|
+
res.status(500).json(jsonApiError(message));
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
static async get(req, res) {
|
|
927
|
+
try {
|
|
928
|
+
await db.refresh();
|
|
929
|
+
const standard = db.standards.getById(req.params.id);
|
|
930
|
+
if (!standard) throw new Error("Coding standard not found");
|
|
931
|
+
db.standards.incrementHitCounts([standard.id]);
|
|
932
|
+
db.actions.logAction("read", standard.repo || "global", { query: standard.title, resultCount: 1 });
|
|
933
|
+
res.json(jsonApiRes(standard, "standard"));
|
|
934
|
+
} catch (err) {
|
|
935
|
+
const message = err instanceof Error ? err.message : "Coding standard not found";
|
|
936
|
+
res.status(404).json(jsonApiError(message, 404));
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
static async create(req, res) {
|
|
940
|
+
try {
|
|
941
|
+
await db.refresh();
|
|
942
|
+
const attributes = getAttributes(req);
|
|
943
|
+
const { title, content, tags, metadata } = attributes;
|
|
944
|
+
if (!title || !content || !Array.isArray(tags) || tags.length === 0 || !metadata) {
|
|
945
|
+
return res.status(400).json(jsonApiError("Required fields missing", 400));
|
|
946
|
+
}
|
|
947
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
948
|
+
const entry = {
|
|
949
|
+
id: randomUUID3(),
|
|
950
|
+
title: String(title),
|
|
951
|
+
content: String(content),
|
|
952
|
+
parent_id: attributes.parent_id || null,
|
|
953
|
+
context: String(attributes.context || "general"),
|
|
954
|
+
version: String(attributes.version || "1.0.0"),
|
|
955
|
+
language: attributes.language || null,
|
|
956
|
+
stack: Array.isArray(attributes.stack) ? attributes.stack : [],
|
|
957
|
+
is_global: attributes.is_global !== false,
|
|
958
|
+
repo: attributes.repo || null,
|
|
959
|
+
tags,
|
|
960
|
+
metadata,
|
|
961
|
+
created_at: now,
|
|
962
|
+
updated_at: now,
|
|
963
|
+
hit_count: 0,
|
|
964
|
+
last_used_at: null,
|
|
965
|
+
agent: String(attributes.agent || "dashboard"),
|
|
966
|
+
model: String(attributes.model || "web-ui")
|
|
967
|
+
};
|
|
968
|
+
await db.withWrite(async () => {
|
|
969
|
+
db.standards.insert(entry);
|
|
970
|
+
await vectors.upsert(entry.id, buildStandardVectorText(entry), "standard");
|
|
971
|
+
db.actions.logAction("write", entry.repo || "global", { query: entry.title, resultCount: 1 });
|
|
972
|
+
});
|
|
973
|
+
res.json(jsonApiRes(entry, "standard"));
|
|
974
|
+
} catch (err) {
|
|
975
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
976
|
+
res.status(500).json(jsonApiError(message));
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
static async update(req, res) {
|
|
980
|
+
try {
|
|
981
|
+
await db.refresh();
|
|
982
|
+
const existing = db.standards.getById(req.params.id);
|
|
983
|
+
if (!existing) return res.status(404).json(jsonApiError("Coding standard not found", 404));
|
|
984
|
+
const attributes = getAttributes(req);
|
|
985
|
+
const updates = {};
|
|
986
|
+
if (attributes.title !== void 0) updates.title = attributes.title;
|
|
987
|
+
if (attributes.content !== void 0) updates.content = attributes.content;
|
|
988
|
+
if (attributes.parent_id !== void 0) updates.parent_id = attributes.parent_id === null ? null : attributes.parent_id;
|
|
989
|
+
if (attributes.context !== void 0) updates.context = attributes.context;
|
|
990
|
+
if (attributes.version !== void 0) updates.version = attributes.version;
|
|
991
|
+
if (attributes.language !== void 0) updates.language = attributes.language || null;
|
|
992
|
+
if (Array.isArray(attributes.stack)) updates.stack = attributes.stack;
|
|
993
|
+
if (typeof attributes.is_global === "boolean") updates.is_global = attributes.is_global;
|
|
994
|
+
if (attributes.repo !== void 0) updates.repo = attributes.repo;
|
|
995
|
+
if (Array.isArray(attributes.tags)) updates.tags = attributes.tags;
|
|
996
|
+
if (attributes.metadata !== void 0) updates.metadata = attributes.metadata;
|
|
997
|
+
if (attributes.agent !== void 0) updates.agent = attributes.agent;
|
|
998
|
+
if (attributes.model !== void 0) updates.model = attributes.model;
|
|
999
|
+
await db.withWrite(async () => {
|
|
1000
|
+
db.standards.update(existing.id, updates);
|
|
1001
|
+
const merged = {
|
|
1002
|
+
...existing,
|
|
1003
|
+
...updates,
|
|
1004
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1005
|
+
};
|
|
1006
|
+
await vectors.upsert(existing.id, buildStandardVectorText(merged), "standard");
|
|
1007
|
+
db.actions.logAction("update", existing.repo || "global", { query: existing.title, resultCount: 1 });
|
|
1008
|
+
});
|
|
1009
|
+
res.json(jsonApiRes({ message: "Updated" }, "status"));
|
|
1010
|
+
} catch (err) {
|
|
1011
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1012
|
+
res.status(500).json(jsonApiError(message));
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
static async delete(req, res) {
|
|
1016
|
+
try {
|
|
1017
|
+
await db.refresh();
|
|
1018
|
+
const existing = db.standards.getById(req.params.id);
|
|
1019
|
+
if (!existing) return res.status(404).json(jsonApiError("Coding standard not found", 404));
|
|
1020
|
+
await db.withWrite(async () => {
|
|
1021
|
+
db.standards.delete(existing.id);
|
|
1022
|
+
await vectors.remove(existing.id, "standard");
|
|
1023
|
+
db.actions.logAction("delete", existing.repo || "global", { query: existing.title, resultCount: 1 });
|
|
1024
|
+
});
|
|
1025
|
+
res.json(jsonApiRes({ message: "Deleted" }, "status"));
|
|
1026
|
+
} catch (err) {
|
|
1027
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1028
|
+
res.status(500).json(jsonApiError(message));
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1033
|
+
// src/dashboard/routes/standard.routes.ts
|
|
872
1034
|
var router4 = Router4();
|
|
873
|
-
router4.
|
|
874
|
-
router4.
|
|
875
|
-
router4.
|
|
876
|
-
|
|
1035
|
+
router4.get("/", StandardsController.list);
|
|
1036
|
+
router4.post("/", StandardsController.create);
|
|
1037
|
+
router4.get("/:id", StandardsController.get);
|
|
1038
|
+
router4.put("/:id", StandardsController.update);
|
|
1039
|
+
router4.delete("/:id", StandardsController.delete);
|
|
1040
|
+
var standard_routes_default = router4;
|
|
1041
|
+
|
|
1042
|
+
// src/dashboard/routes/index.ts
|
|
1043
|
+
var router5 = Router5();
|
|
1044
|
+
router5.use("/", system_routes_default);
|
|
1045
|
+
router5.use("/memories", memory_routes_default);
|
|
1046
|
+
router5.use("/tasks", task_routes_default);
|
|
1047
|
+
router5.use("/standards", standard_routes_default);
|
|
1048
|
+
var routes_default = router5;
|
|
877
1049
|
|
|
878
1050
|
// src/dashboard/server.ts
|
|
879
1051
|
var __dirname3 = path3.dirname(fileURLToPath3(import.meta.url));
|
package/dist/mcp/server.js
CHANGED
|
@@ -15,9 +15,13 @@ import {
|
|
|
15
15
|
MemorySummarizeSchema,
|
|
16
16
|
MemorySynthesizeSchema,
|
|
17
17
|
MemoryUpdateSchema,
|
|
18
|
+
RealVectorStore,
|
|
18
19
|
SQLiteStore,
|
|
20
|
+
StandardDeleteSchema,
|
|
21
|
+
StandardDetailSchema,
|
|
19
22
|
StandardSearchSchema,
|
|
20
23
|
StandardStoreSchema,
|
|
24
|
+
StandardUpdateSchema,
|
|
21
25
|
TOOL_DEFINITIONS,
|
|
22
26
|
TaskClaimSchema,
|
|
23
27
|
TaskCreateInteractiveSchema,
|
|
@@ -27,6 +31,7 @@ import {
|
|
|
27
31
|
TaskListSchema,
|
|
28
32
|
TaskUpdateSchema,
|
|
29
33
|
addLogSink,
|
|
34
|
+
buildStandardVectorText,
|
|
30
35
|
completePromptArgument,
|
|
31
36
|
completeResourceArgument,
|
|
32
37
|
createFileSink,
|
|
@@ -49,7 +54,7 @@ import {
|
|
|
49
54
|
setLogLevel,
|
|
50
55
|
updateSessionFromInitialize,
|
|
51
56
|
updateSessionRoots
|
|
52
|
-
} from "../chunk-
|
|
57
|
+
} from "../chunk-RLAFKS7B.js";
|
|
53
58
|
|
|
54
59
|
// src/mcp/server.ts
|
|
55
60
|
import readline from "readline";
|
|
@@ -1803,13 +1808,14 @@ async function handleTaskClaim(args, storage) {
|
|
|
1803
1808
|
|
|
1804
1809
|
// src/mcp/tools/standard.store.ts
|
|
1805
1810
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1806
|
-
async function handleStandardStore(params, db2) {
|
|
1811
|
+
async function handleStandardStore(params, db2, vectors2) {
|
|
1807
1812
|
const validated = StandardStoreSchema.parse(params);
|
|
1808
1813
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1809
1814
|
const entry = {
|
|
1810
1815
|
id: randomUUID3(),
|
|
1811
1816
|
title: validated.name,
|
|
1812
1817
|
content: validated.content,
|
|
1818
|
+
parent_id: validated.parent_id || null,
|
|
1813
1819
|
context: validated.context || "general",
|
|
1814
1820
|
version: validated.version || "1.0.0",
|
|
1815
1821
|
language: validated.language || null,
|
|
@@ -1817,13 +1823,20 @@ async function handleStandardStore(params, db2) {
|
|
|
1817
1823
|
is_global: validated.is_global !== false,
|
|
1818
1824
|
repo: validated.repo || null,
|
|
1819
1825
|
tags: validated.tags || [],
|
|
1820
|
-
metadata: validated.metadata
|
|
1826
|
+
metadata: validated.metadata,
|
|
1821
1827
|
created_at: now,
|
|
1822
1828
|
updated_at: now,
|
|
1829
|
+
hit_count: 0,
|
|
1830
|
+
last_used_at: null,
|
|
1823
1831
|
agent: validated.agent || "unknown",
|
|
1824
1832
|
model: validated.model || "unknown"
|
|
1825
1833
|
};
|
|
1826
1834
|
db2.standards.insert(entry);
|
|
1835
|
+
try {
|
|
1836
|
+
await vectors2.upsert(entry.id, buildStandardVectorText(entry), "standard");
|
|
1837
|
+
} catch (error) {
|
|
1838
|
+
logger.warn("Failed to generate standard vector embedding", { error: String(error) });
|
|
1839
|
+
}
|
|
1827
1840
|
logger.info("[Tool] standard.store - saved coding standard", {
|
|
1828
1841
|
standardId: entry.id,
|
|
1829
1842
|
title: entry.title,
|
|
@@ -1845,52 +1858,260 @@ async function handleStandardStore(params, db2) {
|
|
|
1845
1858
|
}
|
|
1846
1859
|
|
|
1847
1860
|
// src/mcp/tools/standard.search.ts
|
|
1848
|
-
|
|
1861
|
+
var HYBRID_WEIGHTS_STANDARD = {
|
|
1862
|
+
similarity: 0.55,
|
|
1863
|
+
vector: 0.35,
|
|
1864
|
+
usage: 0.1
|
|
1865
|
+
};
|
|
1866
|
+
async function handleStandardSearch(params, db2, vectors2) {
|
|
1849
1867
|
const validated = StandardSearchSchema.parse(params);
|
|
1850
|
-
const
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
+
const searchQuery = expandQuery(validated.query || "", void 0);
|
|
1869
|
+
const fetchLimit = (validated.offset + validated.limit) * 3;
|
|
1870
|
+
const similarityResults = searchQuery ? db2.standards.searchBySimilarity(searchQuery, {
|
|
1871
|
+
context: validated.context,
|
|
1872
|
+
version: validated.version,
|
|
1873
|
+
language: validated.language,
|
|
1874
|
+
stack: validated.stack,
|
|
1875
|
+
tags: validated.tags,
|
|
1876
|
+
repo: validated.repo,
|
|
1877
|
+
is_global: validated.is_global,
|
|
1878
|
+
limit: fetchLimit
|
|
1879
|
+
}) : db2.standards.search({
|
|
1880
|
+
context: validated.context,
|
|
1881
|
+
version: validated.version,
|
|
1882
|
+
language: validated.language,
|
|
1883
|
+
stack: validated.stack?.[0],
|
|
1884
|
+
tag: validated.tags?.[0],
|
|
1885
|
+
repo: validated.repo,
|
|
1886
|
+
is_global: validated.is_global,
|
|
1887
|
+
limit: fetchLimit,
|
|
1888
|
+
offset: 0
|
|
1889
|
+
}).map((standard) => ({ ...standard, similarity: 0.5 }));
|
|
1890
|
+
let scoredStandards = [];
|
|
1891
|
+
try {
|
|
1892
|
+
const vectorResults = searchQuery ? await vectors2.search(searchQuery, similarityResults.length || validated.limit, validated.repo, "standard") : [];
|
|
1893
|
+
const vectorScoreMap = new Map(vectorResults.map((row) => [row.id, row.score]));
|
|
1894
|
+
if (similarityResults.length > 0) {
|
|
1895
|
+
scoredStandards = similarityResults.map((candidate) => {
|
|
1896
|
+
const vectorScore = vectorScoreMap.get(candidate.id) ?? 0;
|
|
1897
|
+
const usageScore = Math.min(1, candidate.hit_count / 10);
|
|
1898
|
+
const finalScore = candidate.similarity * HYBRID_WEIGHTS_STANDARD.similarity + vectorScore * HYBRID_WEIGHTS_STANDARD.vector + usageScore * HYBRID_WEIGHTS_STANDARD.usage;
|
|
1899
|
+
return {
|
|
1900
|
+
standard: candidate,
|
|
1901
|
+
similarityScore: candidate.similarity,
|
|
1902
|
+
vectorScore,
|
|
1903
|
+
finalScore
|
|
1904
|
+
};
|
|
1905
|
+
});
|
|
1906
|
+
} else if (vectorResults.length > 0) {
|
|
1907
|
+
const fetched = db2.standards.getByIds(vectorResults.map((row) => row.id));
|
|
1908
|
+
const standardMap = new Map(fetched.map((standard) => [standard.id, standard]));
|
|
1909
|
+
scoredStandards = vectorResults.flatMap((row) => {
|
|
1910
|
+
const standard = standardMap.get(row.id);
|
|
1911
|
+
if (!standard) return [];
|
|
1912
|
+
const usageScore = Math.min(1, standard.hit_count / 10);
|
|
1913
|
+
return [
|
|
1914
|
+
{
|
|
1915
|
+
standard,
|
|
1916
|
+
similarityScore: 0,
|
|
1917
|
+
vectorScore: row.score,
|
|
1918
|
+
finalScore: row.score * 0.9 + usageScore * 0.1
|
|
1919
|
+
}
|
|
1920
|
+
];
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
} catch (error) {
|
|
1924
|
+
logger.warn("Standard vector search failed, using similarity only", { error: String(error) });
|
|
1925
|
+
scoredStandards = similarityResults.map((candidate) => ({
|
|
1926
|
+
standard: candidate,
|
|
1927
|
+
similarityScore: candidate.similarity,
|
|
1928
|
+
vectorScore: 0,
|
|
1929
|
+
finalScore: candidate.similarity * 0.9 + Math.min(1, candidate.hit_count / 10) * 0.1
|
|
1930
|
+
}));
|
|
1868
1931
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1932
|
+
scoredStandards.sort((a, b) => b.finalScore - a.finalScore);
|
|
1933
|
+
const threshold = scoredStandards.length <= 5 ? 0.08 : 0.2;
|
|
1934
|
+
let results = scoredStandards.filter((candidate) => candidate.finalScore >= threshold).map((candidate) => candidate.standard);
|
|
1935
|
+
if (results.length === 0 && scoredStandards.length > 0) {
|
|
1936
|
+
results = [scoredStandards[0].standard];
|
|
1871
1937
|
}
|
|
1872
|
-
const
|
|
1938
|
+
const paginatedResults = results.slice(validated.offset, validated.offset + validated.limit);
|
|
1939
|
+
db2.standards.incrementHitCounts(paginatedResults.map((standard) => standard.id));
|
|
1873
1940
|
logger.info("[Tool] standard.search - searched coding standards", {
|
|
1874
|
-
resultCount:
|
|
1941
|
+
resultCount: paginatedResults.length,
|
|
1875
1942
|
stack: validated.stack,
|
|
1876
1943
|
language: validated.language,
|
|
1944
|
+
context: validated.context,
|
|
1877
1945
|
version: validated.version
|
|
1878
1946
|
});
|
|
1947
|
+
const COLUMNS = ["id", "title", "context", "language", "scope", "tags", "updated_at"];
|
|
1948
|
+
const rows = paginatedResults.map((standard) => [
|
|
1949
|
+
standard.id,
|
|
1950
|
+
standard.title,
|
|
1951
|
+
standard.context,
|
|
1952
|
+
standard.language || "-",
|
|
1953
|
+
standard.is_global ? "global" : standard.repo || "-",
|
|
1954
|
+
standard.tags.join(", "),
|
|
1955
|
+
standard.updated_at
|
|
1956
|
+
]);
|
|
1957
|
+
let contentSummary;
|
|
1958
|
+
if (paginatedResults.length > 0) {
|
|
1959
|
+
const parts = [
|
|
1960
|
+
"Standards:",
|
|
1961
|
+
"- title|context|language|scope",
|
|
1962
|
+
...paginatedResults.map(
|
|
1963
|
+
(standard) => `- ${standard.title}|${standard.context}|${standard.language || "-"}|${standard.is_global ? "global" : standard.repo || "-"}`
|
|
1964
|
+
),
|
|
1965
|
+
"",
|
|
1966
|
+
"Use standard-detail with id for full content."
|
|
1967
|
+
];
|
|
1968
|
+
contentSummary = parts.join("\n");
|
|
1969
|
+
} else {
|
|
1970
|
+
contentSummary = "No matching coding standards found.";
|
|
1971
|
+
}
|
|
1879
1972
|
return createMcpResponse(
|
|
1880
1973
|
{
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
count:
|
|
1884
|
-
|
|
1974
|
+
schema: "standard-search",
|
|
1975
|
+
query: validated.query || "",
|
|
1976
|
+
count: paginatedResults.length,
|
|
1977
|
+
total: results.length,
|
|
1978
|
+
offset: validated.offset,
|
|
1979
|
+
limit: validated.limit,
|
|
1980
|
+
results: {
|
|
1981
|
+
columns: [...COLUMNS],
|
|
1982
|
+
rows
|
|
1983
|
+
}
|
|
1885
1984
|
},
|
|
1886
1985
|
`Found ${results.length} coding standards matching your query`,
|
|
1887
1986
|
{
|
|
1888
|
-
|
|
1987
|
+
contentSummary,
|
|
1988
|
+
structuredContentPathHint: "results",
|
|
1889
1989
|
includeSerializedStructuredContent: true
|
|
1890
1990
|
}
|
|
1891
1991
|
);
|
|
1892
1992
|
}
|
|
1893
1993
|
|
|
1994
|
+
// src/mcp/tools/standard.update.ts
|
|
1995
|
+
async function handleStandardUpdate(params, db2, vectors2) {
|
|
1996
|
+
const validated = StandardUpdateSchema.parse(params);
|
|
1997
|
+
const existing = db2.standards.getById(validated.id);
|
|
1998
|
+
if (!existing) {
|
|
1999
|
+
throw new Error(`Coding standard not found: ${validated.id}`);
|
|
2000
|
+
}
|
|
2001
|
+
const updates = {};
|
|
2002
|
+
if (validated.name !== void 0) updates.title = validated.name;
|
|
2003
|
+
if (validated.content !== void 0) updates.content = validated.content;
|
|
2004
|
+
if (validated.parent_id !== void 0) updates.parent_id = validated.parent_id;
|
|
2005
|
+
if (validated.context !== void 0) updates.context = validated.context;
|
|
2006
|
+
if (validated.version !== void 0) updates.version = validated.version;
|
|
2007
|
+
if (validated.language !== void 0) updates.language = validated.language;
|
|
2008
|
+
if (validated.stack !== void 0) updates.stack = validated.stack;
|
|
2009
|
+
if (validated.repo !== void 0) updates.repo = validated.repo;
|
|
2010
|
+
if (validated.is_global !== void 0) updates.is_global = validated.is_global;
|
|
2011
|
+
if (validated.tags !== void 0) updates.tags = validated.tags;
|
|
2012
|
+
if (validated.metadata !== void 0) updates.metadata = validated.metadata;
|
|
2013
|
+
if (validated.agent !== void 0) updates.agent = validated.agent;
|
|
2014
|
+
if (validated.model !== void 0) updates.model = validated.model;
|
|
2015
|
+
db2.standards.update(validated.id, updates);
|
|
2016
|
+
const merged = {
|
|
2017
|
+
...existing,
|
|
2018
|
+
...updates,
|
|
2019
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2020
|
+
};
|
|
2021
|
+
if (validated.name !== void 0 || validated.content !== void 0 || validated.context !== void 0 || validated.version !== void 0 || validated.language !== void 0 || validated.stack !== void 0 || validated.tags !== void 0 || validated.metadata !== void 0) {
|
|
2022
|
+
await vectors2.upsert(validated.id, buildStandardVectorText(merged), "standard");
|
|
2023
|
+
}
|
|
2024
|
+
logger.info("[Tool] standard.update - updated coding standard", {
|
|
2025
|
+
standardId: validated.id,
|
|
2026
|
+
fields: Object.keys(updates)
|
|
2027
|
+
});
|
|
2028
|
+
return createMcpResponse(
|
|
2029
|
+
{
|
|
2030
|
+
success: true,
|
|
2031
|
+
id: validated.id,
|
|
2032
|
+
updatedFields: Object.keys(updates)
|
|
2033
|
+
},
|
|
2034
|
+
`Updated coding standard ${validated.id}. Fields: ${Object.keys(updates).join(", ") || "none"}.`,
|
|
2035
|
+
{
|
|
2036
|
+
structuredContentPathHint: "updatedFields",
|
|
2037
|
+
includeSerializedStructuredContent: true
|
|
2038
|
+
}
|
|
2039
|
+
);
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
// src/mcp/tools/standard.detail.ts
|
|
2043
|
+
async function handleStandardDetail(args, storage) {
|
|
2044
|
+
const validated = StandardDetailSchema.parse(args);
|
|
2045
|
+
const standard = storage.standards.getById(validated.id);
|
|
2046
|
+
if (!standard) {
|
|
2047
|
+
throw new Error(`Coding standard not found: ${validated.id}`);
|
|
2048
|
+
}
|
|
2049
|
+
storage.standards.incrementHitCounts([standard.id]);
|
|
2050
|
+
const lines = [
|
|
2051
|
+
`ID: ${standard.id}`,
|
|
2052
|
+
`Title: ${standard.title}`,
|
|
2053
|
+
`Parent ID: ${standard.parent_id || "-"}`,
|
|
2054
|
+
`Context: ${standard.context}`,
|
|
2055
|
+
`Version: ${standard.version}`,
|
|
2056
|
+
`Language: ${standard.language || "-"}`,
|
|
2057
|
+
`Scope: ${standard.is_global ? "global" : standard.repo || "-"}`,
|
|
2058
|
+
`Created: ${standard.created_at}`,
|
|
2059
|
+
`Updated: ${standard.updated_at}`
|
|
2060
|
+
];
|
|
2061
|
+
if (standard.stack.length > 0) lines.push(`Stack: ${standard.stack.join(", ")}`);
|
|
2062
|
+
if (standard.tags.length > 0) lines.push(`Tags: ${standard.tags.join(", ")}`);
|
|
2063
|
+
if (Object.keys(standard.metadata).length > 0) lines.push(`Metadata: ${JSON.stringify(standard.metadata)}`);
|
|
2064
|
+
if (standard.content) {
|
|
2065
|
+
lines.push("", "--- Content ---", standard.content);
|
|
2066
|
+
}
|
|
2067
|
+
const content = lines.join("\n");
|
|
2068
|
+
return createMcpResponse(standard, content, {
|
|
2069
|
+
contentSummary: content,
|
|
2070
|
+
includeSerializedStructuredContent: validated.structured
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
// src/mcp/tools/standard.delete.ts
|
|
2075
|
+
async function handleStandardDelete(params, db2, vectors2) {
|
|
2076
|
+
const validated = StandardDeleteSchema.parse(params);
|
|
2077
|
+
const { id, ids, repo, structured } = validated;
|
|
2078
|
+
const targetIds = ids || (id ? [id] : []);
|
|
2079
|
+
if (targetIds.length === 0) {
|
|
2080
|
+
throw new Error("Either 'id' or 'ids' must be provided for deletion");
|
|
2081
|
+
}
|
|
2082
|
+
let deletedCount = 0;
|
|
2083
|
+
const deletedTitles = [];
|
|
2084
|
+
let lastRepo = repo || "unknown";
|
|
2085
|
+
for (const targetId of targetIds) {
|
|
2086
|
+
const existing = db2.standards.getById(targetId);
|
|
2087
|
+
if (existing) {
|
|
2088
|
+
lastRepo = existing.repo || (existing.is_global ? "global" : lastRepo);
|
|
2089
|
+
deletedTitles.push(existing.title);
|
|
2090
|
+
db2.standards.delete(targetId);
|
|
2091
|
+
await vectors2.remove(targetId, "standard");
|
|
2092
|
+
deletedCount++;
|
|
2093
|
+
} else if (id) {
|
|
2094
|
+
throw new Error(`Coding standard not found: ${targetId}`);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
logger.info("[Tool] standard.delete", { repo: lastRepo, count: deletedCount });
|
|
2098
|
+
return createMcpResponse(
|
|
2099
|
+
{
|
|
2100
|
+
success: true,
|
|
2101
|
+
id: id || void 0,
|
|
2102
|
+
ids: ids || void 0,
|
|
2103
|
+
repo: lastRepo,
|
|
2104
|
+
deletedCount,
|
|
2105
|
+
deletedTitles: deletedTitles.length > 10 ? [...deletedTitles.slice(0, 10), "..."] : deletedTitles
|
|
2106
|
+
},
|
|
2107
|
+
`Deleted ${deletedCount} coding standard(s) from "${lastRepo}".`,
|
|
2108
|
+
{
|
|
2109
|
+
structuredContentPathHint: "deletedCount",
|
|
2110
|
+
includeSerializedStructuredContent: structured
|
|
2111
|
+
}
|
|
2112
|
+
);
|
|
2113
|
+
}
|
|
2114
|
+
|
|
1894
2115
|
// src/mcp/tools/task.get.ts
|
|
1895
2116
|
async function handleTaskGet(args, storage) {
|
|
1896
2117
|
const validated = TaskGetSchema.parse(args);
|
|
@@ -2012,6 +2233,8 @@ function createRouter(db2, vectors2, options) {
|
|
|
2012
2233
|
"handoff-create",
|
|
2013
2234
|
"handoff-update",
|
|
2014
2235
|
"standard-store",
|
|
2236
|
+
"standard-update",
|
|
2237
|
+
"standard-delete",
|
|
2015
2238
|
"task-create",
|
|
2016
2239
|
"task-create-interactive",
|
|
2017
2240
|
"task-claim",
|
|
@@ -2060,9 +2283,15 @@ function createRouter(db2, vectors2, options) {
|
|
|
2060
2283
|
case "task-claim":
|
|
2061
2284
|
return await handleTaskClaim(args, db2);
|
|
2062
2285
|
case "standard-store":
|
|
2063
|
-
return await handleStandardStore(args, db2);
|
|
2286
|
+
return await handleStandardStore(args, db2, vectors2);
|
|
2287
|
+
case "standard-update":
|
|
2288
|
+
return await handleStandardUpdate(args, db2, vectors2);
|
|
2289
|
+
case "standard-detail":
|
|
2290
|
+
return await handleStandardDetail(args, db2);
|
|
2291
|
+
case "standard-delete":
|
|
2292
|
+
return await handleStandardDelete(args, db2, vectors2);
|
|
2064
2293
|
case "standard-search":
|
|
2065
|
-
return await handleStandardSearch(args, db2);
|
|
2294
|
+
return await handleStandardSearch(args, db2, vectors2);
|
|
2066
2295
|
case "task-create":
|
|
2067
2296
|
return await handleTaskCreate(args, db2);
|
|
2068
2297
|
case "task-create-interactive":
|
|
@@ -2211,78 +2440,6 @@ function normalizePageLimit(value, fallback) {
|
|
|
2211
2440
|
return Math.min(value, 100);
|
|
2212
2441
|
}
|
|
2213
2442
|
|
|
2214
|
-
// src/mcp/storage/vectors.ts
|
|
2215
|
-
import { pipeline, env } from "@xenova/transformers";
|
|
2216
|
-
if (process.env.MCP_SERVER === "true") {
|
|
2217
|
-
env.backends.onnx.logLevel = "error";
|
|
2218
|
-
}
|
|
2219
|
-
var RealVectorStore = class {
|
|
2220
|
-
db;
|
|
2221
|
-
extractor = null;
|
|
2222
|
-
modelName = "Xenova/all-MiniLM-L6-v2";
|
|
2223
|
-
constructor(db2) {
|
|
2224
|
-
this.db = db2;
|
|
2225
|
-
}
|
|
2226
|
-
/**
|
|
2227
|
-
* Triggers background loading of the vector model.
|
|
2228
|
-
* Useful for avoiding timeouts on the first search/upsert request.
|
|
2229
|
-
*/
|
|
2230
|
-
async initialize() {
|
|
2231
|
-
await this.getExtractor();
|
|
2232
|
-
}
|
|
2233
|
-
async getExtractor() {
|
|
2234
|
-
if (!this.extractor) {
|
|
2235
|
-
this.extractor = await pipeline("feature-extraction", this.modelName);
|
|
2236
|
-
}
|
|
2237
|
-
return this.extractor;
|
|
2238
|
-
}
|
|
2239
|
-
cosineSimilarity(v1, v2) {
|
|
2240
|
-
let dotProduct = 0;
|
|
2241
|
-
let mag1 = 0;
|
|
2242
|
-
let mag2 = 0;
|
|
2243
|
-
for (let i = 0; i < v1.length; i++) {
|
|
2244
|
-
dotProduct += v1[i] * v2[i];
|
|
2245
|
-
mag1 += v1[i] * v1[i];
|
|
2246
|
-
mag2 += v2[i] * v2[i];
|
|
2247
|
-
}
|
|
2248
|
-
const mag = Math.sqrt(mag1) * Math.sqrt(mag2);
|
|
2249
|
-
return mag === 0 ? 0 : dotProduct / mag;
|
|
2250
|
-
}
|
|
2251
|
-
async upsert(id, text) {
|
|
2252
|
-
try {
|
|
2253
|
-
const extractor = await this.getExtractor();
|
|
2254
|
-
const output = await extractor(text, { pooling: "mean", normalize: true });
|
|
2255
|
-
const vector = Array.from(output.data);
|
|
2256
|
-
this.db.memories.upsertVectorEmbedding(id, vector);
|
|
2257
|
-
} catch (error) {
|
|
2258
|
-
logger.error("[Vectors] Error during upsert", { id, error: String(error) });
|
|
2259
|
-
throw error;
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2262
|
-
async remove(id) {
|
|
2263
|
-
if (!id) return;
|
|
2264
|
-
}
|
|
2265
|
-
async search(query, limit, repo) {
|
|
2266
|
-
try {
|
|
2267
|
-
const extractor = await this.getExtractor();
|
|
2268
|
-
const output = await extractor(query, { pooling: "mean", normalize: true });
|
|
2269
|
-
const queryVector = Array.from(output.data);
|
|
2270
|
-
const rows = this.db.memories.getVectorCandidates(repo, 100);
|
|
2271
|
-
const results = rows.map((row) => {
|
|
2272
|
-
const memoryVector = JSON.parse(row.vector);
|
|
2273
|
-
return {
|
|
2274
|
-
id: row.memory_id,
|
|
2275
|
-
score: this.cosineSimilarity(queryVector, memoryVector)
|
|
2276
|
-
};
|
|
2277
|
-
});
|
|
2278
|
-
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
2279
|
-
} catch (error) {
|
|
2280
|
-
logger.error("[Vectors] Error during search", { error: String(error) });
|
|
2281
|
-
return [];
|
|
2282
|
-
}
|
|
2283
|
-
}
|
|
2284
|
-
};
|
|
2285
|
-
|
|
2286
2443
|
// src/mcp/server.ts
|
|
2287
2444
|
import fs2 from "fs";
|
|
2288
2445
|
import path3 from "path";
|