@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.
@@ -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-DEY6a44I.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-BaWIMG1g.css">
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>
@@ -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-QCCKQZ5G.js";
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 Router4 } from "express";
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/index.ts
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.use("/", system_routes_default);
874
- router4.use("/memories", memory_routes_default);
875
- router4.use("/tasks", task_routes_default);
876
- var routes_default = router4;
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));
@@ -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-QCCKQZ5G.js";
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
- async function handleStandardSearch(params, db2) {
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 searchOptions = {
1851
- limit: validated.limit || 20,
1852
- offset: validated.offset || 0
1853
- };
1854
- if (validated.query) {
1855
- searchOptions.query = validated.query;
1856
- }
1857
- if (validated.stack && validated.stack.length > 0) {
1858
- searchOptions.stack = validated.stack[0];
1859
- }
1860
- if (validated.language) {
1861
- searchOptions.language = validated.language;
1862
- }
1863
- if (validated.version) {
1864
- searchOptions.context = validated.version;
1865
- }
1866
- if (validated.repo !== void 0) {
1867
- searchOptions.repo = validated.repo;
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
- if (validated.is_global !== void 0) {
1870
- searchOptions.is_global = validated.is_global;
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 results = db2.standards.search(searchOptions);
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: results.length,
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
- success: true,
1882
- standards: results,
1883
- count: results.length,
1884
- message: results.length === 0 ? "No matching coding standards found." : `Found ${results.length} matching standards.`
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
- structuredContentPathHint: "standards",
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";