@vheins/local-memory-mcp 0.18.0 → 0.18.2

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.
@@ -81,8 +81,8 @@ function loadServerInstructions() {
81
81
  // src/mcp/capabilities.ts
82
82
  var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
83
83
  var pkgVersion = "0.1.0";
84
- if ("0.18.0") {
85
- pkgVersion = "0.18.0";
84
+ if ("0.18.2") {
85
+ pkgVersion = "0.18.2";
86
86
  } else {
87
87
  let searchDir = __dirname2;
88
88
  for (let i = 0; i < 5; i++) {
@@ -666,6 +666,20 @@ var MigrationManager = class {
666
666
  CREATE INDEX IF NOT EXISTS idx_memories_is_global ON memories(is_global);
667
667
  CREATE INDEX IF NOT EXISTS idx_coding_standards_hit_count ON coding_standards(hit_count);
668
668
  `);
669
+ try {
670
+ this.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_code_owner_repo ON tasks(owner, repo, task_code);`);
671
+ } catch {
672
+ const dupRows = this.all(
673
+ "SELECT task_code, COUNT(*) as cnt FROM tasks GROUP BY owner, repo, task_code HAVING cnt > 1"
674
+ );
675
+ if (dupRows.length > 0) {
676
+ const codes = dupRows.map((r) => `'${r.task_code}'`).join(", ");
677
+ throw new Error(
678
+ `Cannot create UNIQUE INDEX on (owner, repo, task_code): ${dupRows.length} duplicate task_code(s) found: ${codes}. Remove duplicates manually and re-run migration.`
679
+ );
680
+ }
681
+ throw new Error("Could not create UNIQUE INDEX on tasks(owner, repo, task_code)");
682
+ }
669
683
  try {
670
684
  this.run("UPDATE tasks SET task_code = substr(id, 1, 8) WHERE task_code IS NULL");
671
685
  } catch {
@@ -1696,6 +1710,9 @@ var MemoryArchiveEntity = class extends BaseEntity {
1696
1710
  };
1697
1711
 
1698
1712
  // src/mcp/entities/task.ts
1713
+ function isSqliteError(err) {
1714
+ return err instanceof Error && typeof err.code === "string";
1715
+ }
1699
1716
  var TaskEntity = class extends BaseEntity {
1700
1717
  coordinationSelect(alias = "t") {
1701
1718
  return `
@@ -1711,40 +1728,52 @@ var TaskEntity = class extends BaseEntity {
1711
1728
  `;
1712
1729
  }
1713
1730
  insertTask(task) {
1714
- this.run(
1715
- `INSERT INTO tasks (
1716
- id, repo, owner, task_code, phase, title, description, status, priority,
1717
- agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, suggested_skills, metadata, parent_id, depends_on, est_tokens, in_progress_at,
1718
- commit_id, changed_files
1719
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1720
- [
1721
- task.id,
1722
- task.repo,
1723
- task.owner || "",
1724
- task.task_code,
1725
- task.phase || null,
1726
- task.title,
1727
- task.description || null,
1728
- task.status || "backlog",
1729
- task.priority || 3,
1730
- task.agent || "unknown",
1731
- task.role || "unknown",
1732
- task.doc_path || null,
1733
- task.created_at,
1734
- task.updated_at,
1735
- task.finished_at || null,
1736
- task.canceled_at || null,
1737
- task.tags ? JSON.stringify(task.tags) : null,
1738
- task.suggested_skills ? JSON.stringify(task.suggested_skills) : null,
1739
- task.metadata ? JSON.stringify(task.metadata) : null,
1740
- task.parent_id || null,
1741
- task.depends_on || null,
1742
- task.est_tokens || 0,
1743
- task.in_progress_at || null,
1744
- task.commit_id || null,
1745
- task.changed_files ? JSON.stringify(task.changed_files) : null
1746
- ]
1747
- );
1731
+ try {
1732
+ this.run(
1733
+ `INSERT INTO tasks (
1734
+ id, repo, owner, task_code, phase, title, description, status, priority,
1735
+ agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, suggested_skills, metadata, parent_id, depends_on, est_tokens, in_progress_at,
1736
+ commit_id, changed_files
1737
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1738
+ [
1739
+ task.id,
1740
+ task.repo,
1741
+ task.owner || "",
1742
+ task.task_code,
1743
+ task.phase || null,
1744
+ task.title,
1745
+ task.description || null,
1746
+ task.status || "backlog",
1747
+ task.priority || 3,
1748
+ task.agent || "unknown",
1749
+ task.role || "unknown",
1750
+ task.doc_path || null,
1751
+ task.created_at,
1752
+ task.updated_at,
1753
+ task.finished_at || null,
1754
+ task.canceled_at || null,
1755
+ task.tags ? JSON.stringify(task.tags) : null,
1756
+ task.suggested_skills ? JSON.stringify(task.suggested_skills) : null,
1757
+ task.metadata ? JSON.stringify(task.metadata) : null,
1758
+ task.parent_id || null,
1759
+ task.depends_on || null,
1760
+ task.est_tokens || 0,
1761
+ task.in_progress_at || null,
1762
+ task.commit_id || null,
1763
+ task.changed_files ? JSON.stringify(task.changed_files) : null
1764
+ ]
1765
+ );
1766
+ } catch (err) {
1767
+ this.handleDuplicateTaskCode(err, task.task_code, task.repo);
1768
+ }
1769
+ }
1770
+ handleDuplicateTaskCode(err, taskCode, repo) {
1771
+ if (isSqliteError(err) && err.code === "SQLITE_CONSTRAINT_UNIQUE") {
1772
+ throw new Error(
1773
+ `Duplicate task_code: '${taskCode}' already exists in repository '${repo}'. The task_code must be unique within the repository.`
1774
+ );
1775
+ }
1776
+ throw err;
1748
1777
  }
1749
1778
  updateTask(id, updates) {
1750
1779
  const fields = [];
@@ -1843,14 +1872,19 @@ var TaskEntity = class extends BaseEntity {
1843
1872
  });
1844
1873
  }
1845
1874
  getTaskByCode(owner, repo, taskCode) {
1875
+ const params = [repo, taskCode];
1876
+ const ownerClause = owner ? "t.owner = ? AND " : "";
1877
+ if (owner) {
1878
+ params.unshift(owner);
1879
+ }
1846
1880
  const row = this.get(
1847
1881
  `SELECT t.*, d.task_code as depends_on_code, p.task_code as parent_code,
1848
1882
  ${this.coordinationSelect("t")}
1849
1883
  FROM tasks t
1850
1884
  LEFT JOIN tasks d ON t.depends_on = d.id
1851
1885
  LEFT JOIN tasks p ON t.parent_id = p.id
1852
- WHERE t.owner = ? AND t.repo = ? AND t.task_code = ?`,
1853
- [owner, repo, taskCode]
1886
+ WHERE ${ownerClause}t.repo = ? AND t.task_code = ?`,
1887
+ params
1854
1888
  );
1855
1889
  return row ? {
1856
1890
  ...this.rowToTask(row),
@@ -1996,8 +2030,12 @@ var TaskEntity = class extends BaseEntity {
1996
2030
  return row?.count ?? 0;
1997
2031
  }
1998
2032
  isTaskCodeDuplicate(owner, repo, task_code, excludeId) {
1999
- let query = "SELECT COUNT(*) as count FROM tasks WHERE owner = ? AND repo = ? AND task_code = ?";
2000
- const params = [owner, repo, task_code];
2033
+ let query = "SELECT COUNT(*) as count FROM tasks WHERE repo = ? AND task_code = ?";
2034
+ const params = [repo, task_code];
2035
+ if (owner) {
2036
+ query = "SELECT COUNT(*) as count FROM tasks WHERE owner = ? AND repo = ? AND task_code = ?";
2037
+ params.unshift(owner);
2038
+ }
2001
2039
  if (excludeId) {
2002
2040
  query += " AND id != ?";
2003
2041
  params.push(excludeId);
@@ -2020,9 +2058,14 @@ var TaskEntity = class extends BaseEntity {
2020
2058
  getExistingTaskCodes(owner, repo, codes) {
2021
2059
  if (codes.length === 0) return /* @__PURE__ */ new Set();
2022
2060
  const placeholders = codes.map(() => "?").join(",");
2061
+ const params = [repo, ...codes];
2062
+ const ownerClause = owner ? "owner = ? AND " : "";
2063
+ if (owner) {
2064
+ params.unshift(owner);
2065
+ }
2023
2066
  const rows = this.all(
2024
- `SELECT task_code FROM tasks WHERE owner = ? AND repo = ? AND task_code IN (${placeholders})`,
2025
- [owner, repo, ...codes]
2067
+ `SELECT task_code FROM tasks WHERE ${ownerClause}repo = ? AND task_code IN (${placeholders})`,
2068
+ params
2026
2069
  );
2027
2070
  return new Set(rows.map((r) => r.task_code));
2028
2071
  }
@@ -3751,7 +3794,7 @@ var TaskCreateInteractiveSchema = SingleTaskCreateSchema.partial().extend({
3751
3794
  structured: z.boolean().default(false)
3752
3795
  });
3753
3796
  var TaskUpdateSchema = z.object({
3754
- owner: z.string().optional().default(""),
3797
+ owner: z.string().min(1),
3755
3798
  repo: z.string().min(1).transform(normalizeRepo),
3756
3799
  id: z.string().uuid().optional(),
3757
3800
  ids: z.array(z.string().uuid()).min(1).optional(),
@@ -3803,7 +3846,7 @@ var TaskSearchSchema = z.object({
3803
3846
  structured: z.boolean().default(false)
3804
3847
  });
3805
3848
  var TaskDeleteSchema = z.object({
3806
- owner: z.string().optional().default(""),
3849
+ owner: z.string().min(1),
3807
3850
  repo: z.string().min(1).transform(normalizeRepo),
3808
3851
  id: z.string().uuid().optional(),
3809
3852
  ids: z.array(z.string().uuid()).min(1).optional(),
@@ -3841,7 +3884,7 @@ var StandardDeleteSchema = z.object({
3841
3884
  }
3842
3885
  );
3843
3886
  var TaskGetSchema = z.object({
3844
- owner: z.string().optional().default(""),
3887
+ owner: z.string().min(1),
3845
3888
  repo: z.string().min(1).transform(normalizeRepo),
3846
3889
  id: z.string().uuid().optional(),
3847
3890
  task_code: z.string().optional(),
@@ -3908,7 +3951,7 @@ var ClaimListSchema = z.object({
3908
3951
  structured: z.boolean().default(false)
3909
3952
  });
3910
3953
  var ClaimReleaseSchema = z.object({
3911
- owner: z.string().optional().default(""),
3954
+ owner: z.string().min(1),
3912
3955
  repo: z.string().min(1).transform(normalizeRepo),
3913
3956
  task_id: z.string().uuid().optional(),
3914
3957
  task_code: z.string().optional(),
@@ -3927,7 +3970,7 @@ var StandardStoreSchema = z.object({
3927
3970
  version: z.string().optional(),
3928
3971
  language: z.string().optional(),
3929
3972
  stack: z.array(z.string()).optional(),
3930
- owner: z.string().optional().default(""),
3973
+ owner: z.string().min(1),
3931
3974
  repo: z.string().transform(normalizeRepo).optional(),
3932
3975
  is_global: z.boolean().optional(),
3933
3976
  tags: z.array(z.string().min(1)).min(1).optional(),
@@ -4124,6 +4167,7 @@ var TOOL_DEFINITIONS = [
4124
4167
  inputSchema: {
4125
4168
  type: "object",
4126
4169
  properties: {
4170
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4127
4171
  repo: { type: "string", description: "Repository name" },
4128
4172
  id: { type: "string", format: "uuid", description: "Task ID (optional if task_code is provided)" },
4129
4173
  task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" },
@@ -4133,7 +4177,7 @@ var TOOL_DEFINITIONS = [
4133
4177
  description: "If true, returns structured JSON without the text content details."
4134
4178
  }
4135
4179
  },
4136
- required: ["repo"]
4180
+ required: ["repo", "owner"]
4137
4181
  }
4138
4182
  },
4139
4183
  {
@@ -4776,6 +4820,7 @@ var TOOL_DEFINITIONS = [
4776
4820
  inputSchema: {
4777
4821
  type: "object",
4778
4822
  properties: {
4823
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4779
4824
  repo: { type: "string", description: "Repository name" },
4780
4825
  id: { type: "string", format: "uuid", description: "Task ID (for single update)" },
4781
4826
  ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk update)" },
@@ -4835,7 +4880,7 @@ var TOOL_DEFINITIONS = [
4835
4880
  },
4836
4881
  structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4837
4882
  },
4838
- required: ["repo"]
4883
+ required: ["repo", "owner"]
4839
4884
  },
4840
4885
  outputSchema: {
4841
4886
  type: "object",
@@ -4868,6 +4913,7 @@ var TOOL_DEFINITIONS = [
4868
4913
  inputSchema: {
4869
4914
  type: "object",
4870
4915
  properties: {
4916
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4871
4917
  repo: { type: "string", description: "Repository name" },
4872
4918
  id: {
4873
4919
  type: "string",
@@ -4878,7 +4924,7 @@ var TOOL_DEFINITIONS = [
4878
4924
  task_code: { type: "string", description: "Task code (e.g. TASK-001). Optional if id is provided." },
4879
4925
  structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4880
4926
  },
4881
- required: ["repo"]
4927
+ required: ["repo", "owner"]
4882
4928
  },
4883
4929
  outputSchema: {
4884
4930
  type: "object",
@@ -4896,7 +4942,7 @@ var TOOL_DEFINITIONS = [
4896
4942
  {
4897
4943
  name: "task-list",
4898
4944
  title: "Task List",
4899
- description: "PRIMARY navigation and search tool for tasks. Returns a compact tabular list of tasks (id, task_code, title, status, priority, updated_at, comments_count). Defaults to in_progress and pending tasks. Use 'query' to filter by code, title, or description. Use 'status' (comma-separated) for specific filters. AGENTS: call this once at start, pick ONE task, then call task-detail.",
4945
+ description: "PRIMARY navigation and search tool for tasks. Returns a compact tabular list of tasks (id, task_code, title, status, priority, updated_at, comments_count). Defaults to in_progress and pending tasks. Use 'query' to filter by code, title, or description. Use 'status' (comma-separated) for specific filters, or 'all' for all statuses. AGENTS: call this once at start, pick ONE task, then call task-detail.",
4900
4946
  annotations: {
4901
4947
  readOnlyHint: true,
4902
4948
  idempotentHint: true,
@@ -4913,7 +4959,7 @@ var TOOL_DEFINITIONS = [
4913
4959
  status: {
4914
4960
  type: "string",
4915
4961
  default: "in_progress,pending",
4916
- description: "Comma-separated status filter (backlog, pending, in_progress, completed, canceled, blocked). Defaults to 'in_progress,pending'."
4962
+ description: "Comma-separated status filter (backlog, pending, in_progress, completed, canceled, blocked) or 'all' for all statuses. Defaults to 'backlog,pending,in_progress,blocked'."
4917
4963
  },
4918
4964
  phase: {
4919
4965
  type: "string",
@@ -5258,6 +5304,7 @@ var TOOL_DEFINITIONS = [
5258
5304
  inputSchema: {
5259
5305
  type: "object",
5260
5306
  properties: {
5307
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5261
5308
  repo: { type: "string", description: "Repository name" },
5262
5309
  task_id: {
5263
5310
  type: "string",
@@ -5268,7 +5315,7 @@ var TOOL_DEFINITIONS = [
5268
5315
  agent: { type: "string", description: "Optional agent name to release only that claim" },
5269
5316
  structured: { type: "boolean", default: false }
5270
5317
  },
5271
- required: ["repo"]
5318
+ required: ["repo", "owner"]
5272
5319
  },
5273
5320
  outputSchema: {
5274
5321
  type: "object",
@@ -5295,6 +5342,7 @@ var TOOL_DEFINITIONS = [
5295
5342
  inputSchema: {
5296
5343
  type: "object",
5297
5344
  properties: {
5345
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5298
5346
  name: { type: "string", minLength: 3, maxLength: 255, description: "Human-readable standard name" },
5299
5347
  content: {
5300
5348
  type: "string",
@@ -5352,7 +5400,8 @@ var TOOL_DEFINITIONS = [
5352
5400
  description: "Array of standards for bulk creation"
5353
5401
  },
5354
5402
  structured: { type: "boolean", default: false }
5355
- }
5403
+ },
5404
+ required: ["owner"]
5356
5405
  },
5357
5406
  outputSchema: {
5358
5407
  type: "object",
@@ -6308,7 +6357,7 @@ async function handleTaskClaim(args, storage) {
6308
6357
  storage.taskComments.insertTaskComment({
6309
6358
  id: randomUUID2(),
6310
6359
  task_id: task.id,
6311
- owner: repo,
6360
+ owner,
6312
6361
  repo,
6313
6362
  comment: `Claimed by ${agent} \u2014 auto-promoted to in_progress`,
6314
6363
  agent,
@@ -6456,6 +6505,7 @@ export {
6456
6505
  MemoryDeleteSchema,
6457
6506
  MemorySummarizeSchema,
6458
6507
  MemorySynthesizeSchema,
6508
+ TaskStatusSchema,
6459
6509
  TaskCreateSchema,
6460
6510
  TaskCreateInteractiveSchema,
6461
6511
  TaskUpdateSchema,
@@ -16,7 +16,7 @@ import {
16
16
  handleTaskClaim,
17
17
  listResources,
18
18
  logger
19
- } from "../chunk-YVPDPZXG.js";
19
+ } from "../chunk-AVM7ZGOJ.js";
20
20
 
21
21
  // src/dashboard/server.ts
22
22
  import express from "express";
@@ -26,6 +26,7 @@ import {
26
26
  TaskGetSchema,
27
27
  TaskListSchema,
28
28
  TaskSearchSchema,
29
+ TaskStatusSchema,
29
30
  TaskUpdateSchema,
30
31
  addLogSink,
31
32
  buildStandardVectorText,
@@ -62,7 +63,7 @@ import {
62
63
  toContextSlug,
63
64
  updateSessionFromInitialize,
64
65
  updateSessionRoots
65
- } from "../chunk-YVPDPZXG.js";
66
+ } from "../chunk-AVM7ZGOJ.js";
66
67
 
67
68
  // src/mcp/server.ts
68
69
  import readline from "readline";
@@ -1307,7 +1308,7 @@ async function handleTaskUpdate(args, storage, vectors2) {
1307
1308
  throw new Error("est_tokens is required when changing task status to completed");
1308
1309
  }
1309
1310
  }
1310
- if (updates.task_code && storage.tasks.isTaskCodeDuplicate(repo, updates.task_code, targetId)) {
1311
+ if (updates.task_code && storage.tasks.isTaskCodeDuplicate(owner, repo, updates.task_code, targetId)) {
1311
1312
  throw new Error(`Duplicate task_code: '${updates.task_code}' already exists`);
1312
1313
  }
1313
1314
  const finalUpdates = { ...updates };
@@ -2502,7 +2503,14 @@ async function handleTaskSearch(args, storage) {
2502
2503
  tasks = storage.tasks.getTasksByRepo(owner, repo, status, void 0, void 0, query);
2503
2504
  }
2504
2505
  } else {
2505
- tasks = storage.tasks.getTasksByRepo(owner, repo, void 0, void 0, void 0, query);
2506
+ tasks = storage.tasks.getTasksByMultipleStatuses(
2507
+ owner,
2508
+ repo,
2509
+ [...TaskStatusSchema.options],
2510
+ void 0,
2511
+ void 0,
2512
+ query
2513
+ );
2506
2514
  }
2507
2515
  if (phase) {
2508
2516
  const phaseLower = phase.toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vheins/local-memory-mcp",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "MCP Local Memory Service for coding copilot agents",
5
5
  "mcpName": "io.github.vheins/local-memory-mcp",
6
6
  "type": "module",