@vheins/local-memory-mcp 0.19.3 → 0.19.5

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.
@@ -132,6 +132,7 @@ import fs3 from "fs";
132
132
  import os from "os";
133
133
 
134
134
  // src/mcp/storage/migrations.ts
135
+ var SCHEMA_VERSION = 2;
135
136
  var MigrationManager = class {
136
137
  constructor(db) {
137
138
  this.db = db;
@@ -149,7 +150,25 @@ var MigrationManager = class {
149
150
  get(sql) {
150
151
  return this.db.prepare(sql).get();
151
152
  }
153
+ getSchemaVersion() {
154
+ try {
155
+ const row = this.get("SELECT version FROM _schema_version LIMIT 1");
156
+ return row ? row.version : 0;
157
+ } catch {
158
+ return 0;
159
+ }
160
+ }
161
+ setSchemaVersion(version) {
162
+ this.exec(`CREATE TABLE IF NOT EXISTS _schema_version (version INTEGER NOT NULL)`);
163
+ this.run("DELETE FROM _schema_version");
164
+ this.run("INSERT INTO _schema_version (version) VALUES (?)", version);
165
+ }
152
166
  migrate() {
167
+ const currentVersion = this.getSchemaVersion();
168
+ if (currentVersion >= SCHEMA_VERSION) {
169
+ logger.debug(`[Migration] Schema already at version ${SCHEMA_VERSION}, skipping`);
170
+ return;
171
+ }
153
172
  this.exec(`
154
173
  CREATE TABLE IF NOT EXISTS memories (
155
174
  id TEXT PRIMARY KEY,
@@ -599,6 +618,8 @@ var MigrationManager = class {
599
618
  this.run("UPDATE tasks SET task_code = substr(id, 1, 8) WHERE task_code IS NULL");
600
619
  } catch {
601
620
  }
621
+ this.setSchemaVersion(SCHEMA_VERSION);
622
+ logger.info(`[Migration] Schema upgraded to version ${SCHEMA_VERSION}`);
602
623
  }
603
624
  ensureMemoryTypeConstraint() {
604
625
  const tableSql = this.get("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memories'");
@@ -3208,7 +3229,11 @@ var SQLiteStore = class _SQLiteStore {
3208
3229
  this.db.pragma("foreign_keys = ON");
3209
3230
  this.db.pragma("wal_autocheckpoint = 100");
3210
3231
  if (finalPath !== ":memory:") {
3211
- this._startupChecks(finalPath);
3232
+ try {
3233
+ this.db.pragma("wal_checkpoint(PASSIVE)");
3234
+ } catch (err) {
3235
+ logger.warn("[SQLiteStore] WAL checkpoint failed on startup", { error: String(err) });
3236
+ }
3212
3237
  }
3213
3238
  const migrator = new MigrationManager(this.db);
3214
3239
  migrator.migrate();
@@ -3494,24 +3519,29 @@ function findPromptDir() {
3494
3519
  return path4.resolve(__dirname, "./definitions");
3495
3520
  }
3496
3521
  var PROMPT_DIR = findPromptDir();
3522
+ var promptCache = /* @__PURE__ */ new Map();
3497
3523
  function listPromptFiles() {
3498
3524
  if (!fs4.existsSync(PROMPT_DIR)) return [];
3499
3525
  return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
3500
3526
  }
3501
3527
  function loadPromptFromMarkdown(name) {
3528
+ const cached = promptCache.get(name);
3529
+ if (cached) return cached;
3502
3530
  const filePath = path4.join(PROMPT_DIR, `${name}.md`);
3503
3531
  if (!fs4.existsSync(filePath)) {
3504
3532
  throw new Error(`Prompt file not found: ${filePath}`);
3505
3533
  }
3506
3534
  const fileContent = fs4.readFileSync(filePath, "utf-8");
3507
3535
  const { data, content } = matter(fileContent);
3508
- return {
3536
+ const result = {
3509
3537
  name: data.name || name,
3510
3538
  description: data.description || "",
3511
3539
  arguments: data.arguments || [],
3512
3540
  agent: data.agent,
3513
3541
  content: content.trim()
3514
3542
  };
3543
+ promptCache.set(name, result);
3544
+ return result;
3515
3545
  }
3516
3546
  function findServerInstructionsDir() {
3517
3547
  const candidates = [
@@ -3533,14 +3563,17 @@ function findServerInstructionsDir() {
3533
3563
  return path4.resolve(__dirname, "./server");
3534
3564
  }
3535
3565
  var SERVER_DIR = findServerInstructionsDir();
3566
+ var serverInstructionsCache = null;
3536
3567
  function loadServerInstructions() {
3568
+ if (serverInstructionsCache !== null) return serverInstructionsCache;
3537
3569
  const filePath = path4.join(SERVER_DIR, "instructions.md");
3538
3570
  if (!fs4.existsSync(filePath)) {
3539
3571
  throw new Error(`Server instructions file not found: ${filePath}`);
3540
3572
  }
3541
3573
  const fileContent = fs4.readFileSync(filePath, "utf-8");
3542
3574
  const { content } = matter(fileContent);
3543
- return content.trim();
3575
+ serverInstructionsCache = content.trim();
3576
+ return serverInstructionsCache;
3544
3577
  }
3545
3578
 
3546
3579
  // src/mcp/tools/definitions/memory.ts
@@ -4255,7 +4288,10 @@ var TASK_TOOL_DEFINITIONS = [
4255
4288
  properties: {
4256
4289
  owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4257
4290
  repo: { type: "string", description: "Repository name" },
4258
- task_code: { type: "string", description: "Task code (e.g. TASK-001)" },
4291
+ task_code: {
4292
+ type: "string",
4293
+ description: "Task code (e.g. PERF-1, TASK-001). Use instead of 'id' for string code lookup."
4294
+ },
4259
4295
  structured: {
4260
4296
  type: "boolean",
4261
4297
  default: false,
@@ -4273,7 +4309,7 @@ var TASK_TOOL_DEFINITIONS = [
4273
4309
  type: "array",
4274
4310
  items: { type: "string" },
4275
4311
  minItems: 1,
4276
- description: "Array of task codes (e.g. TASK-001)"
4312
+ description: "Array of task codes (e.g. PERF-1, TASK-001). Use instead of 'ids' when identifying tasks by code rather than UUID."
4277
4313
  },
4278
4314
  structured: {
4279
4315
  type: "boolean",
@@ -4401,7 +4437,7 @@ var TASK_TOOL_DEFINITIONS = [
4401
4437
  {
4402
4438
  name: "task-update",
4403
4439
  title: "Task Update",
4404
- description: "Update one or more tasks. Supports single update via 'id' or bulk update via 'ids'. Provide only the fields that need to be changed. MANDATORY WORKFLOW: You cannot move a task from 'pending' or 'blocked' directly to 'completed'. You MUST move it to 'in_progress' first. When changing status to 'completed', include 'est_tokens' with the estimated total tokens actually used for the task. You may also include 'commit_id' (git commit hash) and 'changed_files' (list of files changed) for traceability.",
4440
+ description: "Update one or more tasks. Supports single update via 'id' (UUID) or 'task_code' (e.g. PERF-1), bulk via 'ids' (UUID array) or 'task_codes' (string array). Use 'task_code'/'task_codes' for human-readable identifiers, 'id'/'ids' for UUID lookups. Provide only the fields that need to be changed. MANDATORY WORKFLOW: Cannot move 'pending'/'blocked' \u2192 'completed' directly; MUST go through 'in_progress' first. Include 'est_tokens' when moving to 'completed'.",
4405
4441
  annotations: {
4406
4442
  readOnlyHint: false,
4407
4443
  idempotentHint: false,
@@ -4482,7 +4518,7 @@ var TASK_TOOL_DEFINITIONS = [
4482
4518
  ids: {
4483
4519
  type: "array",
4484
4520
  items: { type: "string", format: "uuid" },
4485
- description: "Task IDs (for bulk update)"
4521
+ description: "Task UUIDs (for bulk update). NOT task codes \u2014 use 'task_codes' for PERF-1 style identifiers."
4486
4522
  },
4487
4523
  phase: { type: "string" },
4488
4524
  title: { type: "string", minLength: 3, maxLength: 100 },
@@ -4613,7 +4649,7 @@ var TASK_TOOL_DEFINITIONS = [
4613
4649
  type: "array",
4614
4650
  items: { type: "string" },
4615
4651
  minItems: 1,
4616
- description: "Array of task codes (e.g. TASK-001)"
4652
+ description: "Array of task codes (e.g. PERF-1, TASK-001). Use instead of 'ids' when identifying tasks by code rather than UUID."
4617
4653
  },
4618
4654
  phase: { type: "string" },
4619
4655
  title: { type: "string", minLength: 3, maxLength: 100 },
@@ -4723,7 +4759,7 @@ var TASK_TOOL_DEFINITIONS = [
4723
4759
  ids: {
4724
4760
  type: "array",
4725
4761
  items: { type: "string", format: "uuid" },
4726
- description: "Task IDs (for bulk deletion)"
4762
+ description: "Task UUIDs (for bulk deletion). NOT task codes \u2014 use 'task_codes' for PERF-1 style identifiers."
4727
4763
  },
4728
4764
  structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4729
4765
  }
@@ -4734,7 +4770,10 @@ var TASK_TOOL_DEFINITIONS = [
4734
4770
  properties: {
4735
4771
  owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4736
4772
  repo: { type: "string", description: "Repository name" },
4737
- task_code: { type: "string", description: "Task code (e.g. TASK-001)" },
4773
+ task_code: {
4774
+ type: "string",
4775
+ description: "Task code (e.g. PERF-1, TASK-001). Use instead of 'id' for string code lookup."
4776
+ },
4738
4777
  structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4739
4778
  }
4740
4779
  },
@@ -4748,7 +4787,7 @@ var TASK_TOOL_DEFINITIONS = [
4748
4787
  type: "array",
4749
4788
  items: { type: "string" },
4750
4789
  minItems: 1,
4751
- description: "Array of task codes (e.g. TASK-001)"
4790
+ description: "Array of task codes (e.g. PERF-1, TASK-001). Use instead of 'ids' when identifying tasks by code rather than UUID."
4752
4791
  },
4753
4792
  structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4754
4793
  }
@@ -5622,7 +5661,7 @@ var AGENT_TOOL_DEFINITIONS = [
5622
5661
  {
5623
5662
  name: "decision-log",
5624
5663
  title: "Decision Logger",
5625
- description: "Logs a structured decision as a memory entry. Accepts a summary of what was decided, the context/situation, the rationale, and optional alternatives considered. Internally reuses the memory-store handler with type='decision' and importance=4. Owner and repo are auto-inferred from session when omitted.",
5664
+ description: "Logs a structured decision as a memory entry (type='decision', importance=4). Fields: summary (max 255 chars \u2014 becomes the memory title), context (situation/background, min 10 chars), rationale (why it was decided, min 10 chars), and alternatives (ARRAY of strings, NOT a single string \u2014 e.g. ['option A', 'option B']). Do NOT pass 'title' \u2014 that field does not exist on this tool. Owner and repo are auto-inferred from session when omitted.",
5626
5665
  annotations: {
5627
5666
  readOnlyHint: false,
5628
5667
  idempotentHint: false,
@@ -6683,7 +6722,9 @@ var TaskUpdateSchema = z4.object({
6683
6722
  structured: z4.boolean().default(false)
6684
6723
  }).refine(
6685
6724
  (data) => data.id !== void 0 || data.ids !== void 0 || data.task_code !== void 0 || data.task_codes !== void 0,
6686
- { message: "Either 'id', 'ids', 'task_code', or 'task_codes' must be provided for update" }
6725
+ {
6726
+ message: "Either 'id' (UUID), 'ids' (array of UUIDs), 'task_code' (string code like PERF-1), or 'task_codes' (array of string codes) must be provided. Note: 'ids' expects UUID format, not task codes \u2014 use 'task_code'/'task_codes' for string identifiers."
6727
+ }
6687
6728
  ).refine((data) => Object.keys(data).length > 2, {
6688
6729
  message: "At least one field besides repo and id/ids must be provided for update"
6689
6730
  });
@@ -6906,10 +6947,10 @@ var AgentContextSchema = z7.object({
6906
6947
  structured: z7.boolean().default(false)
6907
6948
  });
6908
6949
  var DecisionLogSchema = z7.object({
6909
- summary: z7.string().min(3).max(255, { message: "Summary must be at most 255 characters" }),
6910
- context: z7.string().min(10, { message: "Context must be at least 10 characters" }),
6911
- rationale: z7.string().min(10, { message: "Rationale must be at least 10 characters" }),
6912
- alternatives: z7.array(z7.string()).optional(),
6950
+ summary: z7.string().min(3, { message: "Summary must be at least 3 characters (this becomes the memory title)" }).max(255, { message: "Summary must be at most 255 characters (this becomes the memory title)" }),
6951
+ context: z7.string().min(10, { message: "Context must be at least 10 characters describing the situation" }),
6952
+ rationale: z7.string().min(10, { message: "Rationale must be at least 10 characters explaining why the decision was made" }),
6953
+ alternatives: z7.array(z7.string(), { message: "Alternatives must be an array of strings, e.g. ['option A', 'option B']" }).optional(),
6913
6954
  tags: z7.array(z7.string()).optional(),
6914
6955
  owner: z7.string().optional(),
6915
6956
  repo: z7.string().optional(),