@vheins/local-memory-mcp 0.18.1 → 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.1") {
85
- pkgVersion = "0.18.1";
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
  }
@@ -4899,7 +4942,7 @@ var TOOL_DEFINITIONS = [
4899
4942
  {
4900
4943
  name: "task-list",
4901
4944
  title: "Task List",
4902
- 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.",
4903
4946
  annotations: {
4904
4947
  readOnlyHint: true,
4905
4948
  idempotentHint: true,
@@ -4916,7 +4959,7 @@ var TOOL_DEFINITIONS = [
4916
4959
  status: {
4917
4960
  type: "string",
4918
4961
  default: "in_progress,pending",
4919
- 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'."
4920
4963
  },
4921
4964
  phase: {
4922
4965
  type: "string",
@@ -6462,6 +6505,7 @@ export {
6462
6505
  MemoryDeleteSchema,
6463
6506
  MemorySummarizeSchema,
6464
6507
  MemorySynthesizeSchema,
6508
+ TaskStatusSchema,
6465
6509
  TaskCreateSchema,
6466
6510
  TaskCreateInteractiveSchema,
6467
6511
  TaskUpdateSchema,
@@ -16,7 +16,7 @@ import {
16
16
  handleTaskClaim,
17
17
  listResources,
18
18
  logger
19
- } from "../chunk-LEXNGJHN.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-LEXNGJHN.js";
66
+ } from "../chunk-AVM7ZGOJ.js";
66
67
 
67
68
  // src/mcp/server.ts
68
69
  import readline from "readline";
@@ -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.1",
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",