@vheins/local-memory-mcp 0.8.17 → 0.8.19

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.
@@ -1,27 +1,25 @@
1
1
  // src/mcp/capabilities.ts
2
2
  import { fileURLToPath } from "url";
3
3
  import path from "path";
4
- import fs from "fs";
5
4
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
6
5
  var pkgVersion = "0.1.0";
7
- var candidates = [
8
- path.join(__dirname, "../../package.json"),
9
- // dev: dist/mcp/ -> root
10
- path.join(__dirname, "../../../package.json"),
11
- // global install: lib/node_modules/.../dist/mcp/
12
- path.join(__dirname, "../../../../package.json")
13
- // deeper nesting
14
- ];
15
- for (const pkgPath of candidates) {
16
- try {
17
- if (fs.existsSync(pkgPath)) {
18
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
19
- if (pkg.name === "@vheins/local-memory-mcp" && pkg.version) {
20
- pkgVersion = pkg.version;
21
- break;
6
+ if ("0.8.19") {
7
+ pkgVersion = "0.8.19";
8
+ } else {
9
+ let searchDir = __dirname;
10
+ for (let i = 0; i < 5; i++) {
11
+ const candidate = path.join(searchDir, "package.json");
12
+ try {
13
+ if (fs.existsSync(candidate)) {
14
+ const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
15
+ if (pkg.name === "@vheins/local-memory-mcp" && pkg.version) {
16
+ pkgVersion = pkg.version;
17
+ break;
18
+ }
22
19
  }
20
+ } catch {
23
21
  }
24
- } catch {
22
+ searchDir = path.dirname(searchDir);
25
23
  }
26
24
  }
27
25
  var MCP_PROTOCOL_VERSION = "2025-11-25";
@@ -47,7 +45,7 @@ var CAPABILITIES = {
47
45
  };
48
46
 
49
47
  // src/mcp/utils/logger.ts
50
- import fs2 from "fs";
48
+ import fs from "fs";
51
49
  var LEVELS = {
52
50
  debug: 0,
53
51
  info: 1,
@@ -167,11 +165,11 @@ function addLogSink(sink) {
167
165
  }
168
166
  var LOG_LEVEL_VALUES = Object.keys(LEVELS);
169
167
  function createFileSink(logDir, maxFiles = 5) {
170
- fs2.mkdirSync(logDir, { recursive: true });
171
- const existing = fs2.readdirSync(logDir).filter((f) => f.startsWith("mcp-") && f.endsWith(".log")).sort();
168
+ fs.mkdirSync(logDir, { recursive: true });
169
+ const existing = fs.readdirSync(logDir).filter((f) => f.startsWith("mcp-") && f.endsWith(".log")).sort();
172
170
  while (existing.length >= maxFiles) {
173
171
  try {
174
- fs2.unlinkSync(`${logDir}/${existing.shift()}`);
172
+ fs.unlinkSync(`${logDir}/${existing.shift()}`);
175
173
  } catch {
176
174
  }
177
175
  }
@@ -181,7 +179,7 @@ function createFileSink(logDir, maxFiles = 5) {
181
179
  const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${payload.level.toUpperCase()}] [pid:${process.pid}] ${JSON.stringify(payload.data)}
182
180
  `;
183
181
  try {
184
- fs2.appendFileSync(logFile, line);
182
+ fs.appendFileSync(logFile, line);
185
183
  } catch {
186
184
  }
187
185
  };
@@ -190,7 +188,7 @@ function createFileSink(logDir, maxFiles = 5) {
190
188
  // src/mcp/storage/sqlite.ts
191
189
  import Database from "better-sqlite3";
192
190
  import path3 from "path";
193
- import fs4 from "fs";
191
+ import fs3 from "fs";
194
192
  import os from "os";
195
193
 
196
194
  // src/mcp/storage/migrations.ts
@@ -1291,15 +1289,15 @@ var MemoryEntity = class extends BaseEntity {
1291
1289
  let sql = `SELECT * FROM memories WHERE (${where.join(" AND ")}) AND (expires_at IS NULL OR expires_at > ?)`;
1292
1290
  if (!includeArchived) sql += " AND status = 'active'";
1293
1291
  sql += ` ORDER BY CASE WHEN repo = ? THEN 0 ELSE 1 END, importance DESC, created_at DESC LIMIT 100`;
1294
- const candidates2 = this.all(sql, [...params, now.toISOString(), repo]);
1295
- if (candidates2.length < 5) {
1292
+ const candidates = this.all(sql, [...params, now.toISOString(), repo]);
1293
+ if (candidates.length < 5) {
1296
1294
  const recentSql = `SELECT * FROM memories WHERE (${where.join(" OR ")}) AND status = 'active' AND (expires_at IS NULL OR expires_at > ?) ORDER BY created_at DESC LIMIT 10`;
1297
1295
  const recent = this.all(recentSql, [...params, now.toISOString()]);
1298
1296
  for (const r of recent) {
1299
- if (!candidates2.find((c) => c.id === r.id)) candidates2.push(r);
1297
+ if (!candidates.find((c) => c.id === r.id)) candidates.push(r);
1300
1298
  }
1301
1299
  }
1302
- return candidates2.map((row) => {
1300
+ return candidates.map((row) => {
1303
1301
  const memory = this.rowToMemoryEntry(row);
1304
1302
  const isExpired = row.expires_at && new Date(row.expires_at) <= now;
1305
1303
  const isArchived = row.status === "archived" && !includeArchived;
@@ -1912,7 +1910,7 @@ var SummaryEntity = class extends BaseEntity {
1912
1910
  // src/mcp/storage/write-lock.ts
1913
1911
  import lockfile from "proper-lockfile";
1914
1912
  import path2 from "path";
1915
- import fs3 from "fs";
1913
+ import fs2 from "fs";
1916
1914
  var LOCK_STALE_MS = 3e4;
1917
1915
  var LOCK_RETRY_DELAY_MS = 200;
1918
1916
  var LOCK_RETRY_COUNT = 250;
@@ -1921,9 +1919,9 @@ var WriteLock = class {
1921
1919
  locked = false;
1922
1920
  constructor(dbPath) {
1923
1921
  this.lockTarget = dbPath;
1924
- if (!fs3.existsSync(dbPath)) {
1925
- fs3.mkdirSync(path2.dirname(dbPath), { recursive: true });
1926
- fs3.writeFileSync(dbPath, "");
1922
+ if (!fs2.existsSync(dbPath)) {
1923
+ fs2.mkdirSync(path2.dirname(dbPath), { recursive: true });
1924
+ fs2.writeFileSync(dbPath, "");
1927
1925
  }
1928
1926
  }
1929
1927
  /**
@@ -1977,11 +1975,11 @@ function resolveDbPath() {
1977
1975
  if (process.env.MEMORY_DB_PATH) return process.env.MEMORY_DB_PATH;
1978
1976
  const standardConfigDir = process.platform === "win32" ? path3.join(os.homedir(), ".local-memory-mcp") : process.platform === "darwin" ? path3.join(os.homedir(), "Library", "Application Support", "local-memory-mcp") : path3.join(os.homedir(), ".config", "local-memory-mcp");
1979
1977
  const standardPath = path3.join(standardConfigDir, "memory.db");
1980
- if (fs4.existsSync(standardPath)) return standardPath;
1978
+ if (fs3.existsSync(standardPath)) return standardPath;
1981
1979
  const legacyPath = path3.join(os.homedir(), ".config", "local-memory-mcp", "memory.db");
1982
- if (fs4.existsSync(legacyPath)) return legacyPath;
1980
+ if (fs3.existsSync(legacyPath)) return legacyPath;
1983
1981
  const localCwdFile = path3.join(process.cwd(), "storage", "memory.db");
1984
- if (fs4.existsSync(localCwdFile)) return localCwdFile;
1982
+ if (fs3.existsSync(localCwdFile)) return localCwdFile;
1985
1983
  return standardPath;
1986
1984
  }
1987
1985
  var DB_PATH = resolveDbPath();
@@ -1999,8 +1997,8 @@ var SQLiteStore = class _SQLiteStore {
1999
1997
  this.dbPathInstance = finalPath;
2000
1998
  if (finalPath !== ":memory:") {
2001
1999
  const dbDir = path3.dirname(finalPath);
2002
- if (!fs4.existsSync(dbDir)) {
2003
- fs4.mkdirSync(dbDir, { recursive: true });
2000
+ if (!fs3.existsSync(dbDir)) {
2001
+ fs3.mkdirSync(dbDir, { recursive: true });
2004
2002
  }
2005
2003
  }
2006
2004
  this.db = new Database(finalPath);
@@ -2052,12 +2050,12 @@ var SQLiteStore = class _SQLiteStore {
2052
2050
  */
2053
2051
  _attemptRecovery(dbPath) {
2054
2052
  const backupPath = dbPath + ".backup";
2055
- if (fs4.existsSync(backupPath)) {
2053
+ if (fs3.existsSync(backupPath)) {
2056
2054
  logger.warn("[SQLiteStore] Attempting recovery from backup", { backupPath });
2057
2055
  try {
2058
2056
  const corruptPath = `${dbPath}.corrupt_${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").slice(0, 15)}`;
2059
- fs4.copyFileSync(dbPath, corruptPath);
2060
- fs4.copyFileSync(backupPath, dbPath);
2057
+ fs3.copyFileSync(dbPath, corruptPath);
2058
+ fs3.copyFileSync(backupPath, dbPath);
2061
2059
  logger.warn("[SQLiteStore] Recovery successful. Corrupt file saved to", { corruptPath });
2062
2060
  } catch (err) {
2063
2061
  logger.error("[SQLiteStore] Recovery failed", { error: String(err) });
@@ -2075,7 +2073,7 @@ var SQLiteStore = class _SQLiteStore {
2075
2073
  try {
2076
2074
  this.db.pragma("wal_checkpoint(PASSIVE)");
2077
2075
  const backupPath = this.dbPathInstance + ".backup";
2078
- fs4.copyFileSync(this.dbPathInstance, backupPath);
2076
+ fs3.copyFileSync(this.dbPathInstance, backupPath);
2079
2077
  } catch (err) {
2080
2078
  logger.warn("[SQLiteStore] Backup failed", { error: String(err) });
2081
2079
  }
@@ -2398,8 +2396,8 @@ var TaskUpdateSchema = z.object({
2398
2396
  est_tokens: z.number().int().min(0).optional(),
2399
2397
  force: z.boolean().optional(),
2400
2398
  structured: z.boolean().default(false)
2401
- }).refine((data) => data.id !== void 0 || data.ids !== void 0, {
2402
- message: "Either 'id' or 'ids' must be provided for update"
2399
+ }).refine((data) => data.id !== void 0 || data.ids !== void 0 || data.task_code !== void 0, {
2400
+ message: "Either 'id', 'ids', or 'task_code' must be provided for update"
2403
2401
  }).refine((data) => Object.keys(data).length > 2, {
2404
2402
  message: "At least one field besides repo and id/ids must be provided for update"
2405
2403
  });
@@ -3276,8 +3274,8 @@ function invalidPaginationParams(message) {
3276
3274
 
3277
3275
  // src/mcp/utils/completion.ts
3278
3276
  var MAX_COMPLETION_VALUES = 100;
3279
- function rankCompletionValues(candidates2, input) {
3280
- const unique = [...new Set(candidates2.filter(Boolean))];
3277
+ function rankCompletionValues(candidates, input) {
3278
+ const unique = [...new Set(candidates.filter(Boolean))];
3281
3279
  const needle = input.trim().toLowerCase();
3282
3280
  if (!needle) {
3283
3281
  return unique.slice(0, MAX_COMPLETION_VALUES);
@@ -3682,14 +3680,14 @@ function invalidCompletionParams(message) {
3682
3680
  }
3683
3681
 
3684
3682
  // src/mcp/prompts/loader.ts
3685
- import fs5 from "fs";
3683
+ import fs4 from "fs";
3686
3684
  import path5 from "path";
3687
3685
  import { fileURLToPath as fileURLToPath3 } from "url";
3688
3686
  import matter from "gray-matter";
3689
3687
  var __filename = fileURLToPath3(import.meta.url);
3690
3688
  var __dirname2 = path5.dirname(__filename);
3691
3689
  function findPromptDir() {
3692
- const candidates2 = [
3690
+ const candidates = [
3693
3691
  // Production if chunked into dist/
3694
3692
  "./prompts",
3695
3693
  // Production if inlined into dist/mcp/
@@ -3697,9 +3695,9 @@ function findPromptDir() {
3697
3695
  // Dev: /src/mcp/prompts/definitions (next to loader.ts)
3698
3696
  "./definitions"
3699
3697
  ].map((relPath) => path5.resolve(__dirname2, relPath));
3700
- for (const dir of candidates2) {
3701
- if (fs5.existsSync(dir)) {
3702
- const files = fs5.readdirSync(dir);
3698
+ for (const dir of candidates) {
3699
+ if (fs4.existsSync(dir)) {
3700
+ const files = fs4.readdirSync(dir);
3703
3701
  if (files.some((f) => f.endsWith(".md"))) {
3704
3702
  return dir;
3705
3703
  }
@@ -3709,15 +3707,15 @@ function findPromptDir() {
3709
3707
  }
3710
3708
  var PROMPT_DIR = findPromptDir();
3711
3709
  function listPromptFiles() {
3712
- if (!fs5.existsSync(PROMPT_DIR)) return [];
3713
- return fs5.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
3710
+ if (!fs4.existsSync(PROMPT_DIR)) return [];
3711
+ return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
3714
3712
  }
3715
3713
  function loadPromptFromMarkdown(name) {
3716
3714
  const filePath = path5.join(PROMPT_DIR, `${name}.md`);
3717
- if (!fs5.existsSync(filePath)) {
3715
+ if (!fs4.existsSync(filePath)) {
3718
3716
  throw new Error(`Prompt file not found: ${filePath}`);
3719
3717
  }
3720
- const fileContent = fs5.readFileSync(filePath, "utf-8");
3718
+ const fileContent = fs4.readFileSync(filePath, "utf-8");
3721
3719
  const { data, content } = matter(fileContent);
3722
3720
  return {
3723
3721
  name: data.name || name,
@@ -8,7 +8,7 @@ import {
8
8
  createFileSink,
9
9
  listResources,
10
10
  logger
11
- } from "../chunk-LWORZXCL.js";
11
+ } from "../chunk-TR5SQKA3.js";
12
12
 
13
13
  // src/dashboard/server.ts
14
14
  import express from "express";
@@ -43,7 +43,7 @@ import {
43
43
  setLogLevel,
44
44
  updateSessionFromInitialize,
45
45
  updateSessionRoots
46
- } from "../chunk-LWORZXCL.js";
46
+ } from "../chunk-TR5SQKA3.js";
47
47
 
48
48
  // src/mcp/server.ts
49
49
  import readline from "readline";
@@ -1267,7 +1267,13 @@ function addRequiredStringField(properties, required, task, field, schema) {
1267
1267
  async function handleTaskUpdate(args, storage, vectors2) {
1268
1268
  const updateData = TaskUpdateSchema.parse(args);
1269
1269
  const { repo, id, ids, comment, force, ...updates } = updateData;
1270
- const targetIds = ids || (id ? [id] : []);
1270
+ let resolvedId = id;
1271
+ if (!resolvedId && !ids && updates.task_code) {
1272
+ const found = storage.tasks.getTaskByCode(repo, updates.task_code);
1273
+ if (!found) throw new Error(`Task not found: ${updates.task_code}`);
1274
+ resolvedId = found.id;
1275
+ }
1276
+ const targetIds = ids || (resolvedId ? [resolvedId] : []);
1271
1277
  if (targetIds.length === 0) {
1272
1278
  throw new Error("Either 'id' or 'ids' must be provided for update");
1273
1279
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vheins/local-memory-mcp",
3
- "version": "0.8.17",
3
+ "version": "0.8.19",
4
4
  "description": "MCP Local Memory Service for coding copilot agents",
5
5
  "mcpName": "io.github.vheins/local-memory-mcp",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  "author": "Muhammad Rheza Alfin <m.rheza.alfin@gmail.com>",
28
28
  "license": "MIT",
29
29
  "scripts": {
30
- "build": "rm -rf dist && npm run dashboard:build && tsup src/mcp/server.ts src/dashboard/server.ts --format esm && mkdir -p bin && cp -r src/mcp/prompts/definitions dist/prompts/ && printf \"#!/usr/bin/env node\\nimport '../dist/dashboard/server.js';\\n\" > bin/mcp-memory-dashboard.js && shx chmod +x dist/mcp/server.js dist/dashboard/server.js bin/mcp-memory-server.js bin/mcp-memory-dashboard.js",
30
+ "build": "rm -rf dist && npm run dashboard:build && tsup --config tsup.config.ts && mkdir -p bin && cp -r src/mcp/prompts/definitions dist/prompts/ && printf \"#!/usr/bin/env node\\nimport '../dist/dashboard/server.js';\\n\" > bin/mcp-memory-dashboard.js && shx chmod +x dist/mcp/server.js dist/dashboard/server.js bin/mcp-memory-server.js bin/mcp-memory-dashboard.js",
31
31
  "dashboard:build": "vite build --config src/dashboard/ui/vite.config.ts",
32
32
  "dashboard:dev": "vite dev --config src/dashboard/ui/vite.config.ts",
33
33
  "prepare": "npm run build",