@vheins/local-memory-mcp 0.18.1 → 0.18.3
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.
|
|
85
|
-
pkgVersion = "0.18.
|
|
84
|
+
if ("0.18.3") {
|
|
85
|
+
pkgVersion = "0.18.3";
|
|
86
86
|
} else {
|
|
87
87
|
let searchDir = __dirname2;
|
|
88
88
|
for (let i = 0; i < 5; i++) {
|
|
@@ -277,14 +277,14 @@ var MigrationManager = class {
|
|
|
277
277
|
this.db = db;
|
|
278
278
|
}
|
|
279
279
|
db;
|
|
280
|
-
run(sql) {
|
|
281
|
-
this.db.prepare(sql).run();
|
|
280
|
+
run(sql, ...params) {
|
|
281
|
+
this.db.prepare(sql).run(...params);
|
|
282
282
|
}
|
|
283
283
|
exec(sql) {
|
|
284
284
|
this.db.exec(sql);
|
|
285
285
|
}
|
|
286
|
-
all(sql) {
|
|
287
|
-
return this.db.prepare(sql).all();
|
|
286
|
+
all(sql, ...params) {
|
|
287
|
+
return this.db.prepare(sql).all(...params);
|
|
288
288
|
}
|
|
289
289
|
get(sql) {
|
|
290
290
|
return this.db.prepare(sql).get();
|
|
@@ -666,6 +666,37 @@ 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, owner, repo, COUNT(*) as cnt
|
|
674
|
+
FROM tasks
|
|
675
|
+
GROUP BY owner, repo, task_code
|
|
676
|
+
HAVING cnt > 1
|
|
677
|
+
`);
|
|
678
|
+
if (dupRows.length > 0) {
|
|
679
|
+
console.log(`Found ${dupRows.length} duplicate task_code(s). Deduplicating...`);
|
|
680
|
+
for (const dup of dupRows) {
|
|
681
|
+
const rowsToDelete = this.all(
|
|
682
|
+
`SELECT id FROM tasks
|
|
683
|
+
WHERE owner = ? AND repo = ? AND task_code = ?
|
|
684
|
+
ORDER BY updated_at DESC
|
|
685
|
+
OFFSET 1`,
|
|
686
|
+
dup.owner,
|
|
687
|
+
dup.repo,
|
|
688
|
+
dup.task_code
|
|
689
|
+
);
|
|
690
|
+
for (const row of rowsToDelete) {
|
|
691
|
+
this.run("DELETE FROM task_comments WHERE task_id = ?", row.id);
|
|
692
|
+
this.run("DELETE FROM tasks WHERE id = ?", row.id);
|
|
693
|
+
}
|
|
694
|
+
console.log(` Deduplicated ${dup.task_code}: kept 1, removed ${rowsToDelete.length}`);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
this.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_code_owner_repo ON tasks(owner, repo, task_code);`);
|
|
698
|
+
console.log("UNIQUE INDEX on (owner, repo, task_code) created after deduplication.");
|
|
699
|
+
}
|
|
669
700
|
try {
|
|
670
701
|
this.run("UPDATE tasks SET task_code = substr(id, 1, 8) WHERE task_code IS NULL");
|
|
671
702
|
} catch {
|
|
@@ -1696,6 +1727,9 @@ var MemoryArchiveEntity = class extends BaseEntity {
|
|
|
1696
1727
|
};
|
|
1697
1728
|
|
|
1698
1729
|
// src/mcp/entities/task.ts
|
|
1730
|
+
function isSqliteError(err) {
|
|
1731
|
+
return err instanceof Error && typeof err.code === "string";
|
|
1732
|
+
}
|
|
1699
1733
|
var TaskEntity = class extends BaseEntity {
|
|
1700
1734
|
coordinationSelect(alias = "t") {
|
|
1701
1735
|
return `
|
|
@@ -1711,40 +1745,52 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1711
1745
|
`;
|
|
1712
1746
|
}
|
|
1713
1747
|
insertTask(task) {
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
+
try {
|
|
1749
|
+
this.run(
|
|
1750
|
+
`INSERT INTO tasks (
|
|
1751
|
+
id, repo, owner, task_code, phase, title, description, status, priority,
|
|
1752
|
+
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,
|
|
1753
|
+
commit_id, changed_files
|
|
1754
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1755
|
+
[
|
|
1756
|
+
task.id,
|
|
1757
|
+
task.repo,
|
|
1758
|
+
task.owner || "",
|
|
1759
|
+
task.task_code,
|
|
1760
|
+
task.phase || null,
|
|
1761
|
+
task.title,
|
|
1762
|
+
task.description || null,
|
|
1763
|
+
task.status || "backlog",
|
|
1764
|
+
task.priority || 3,
|
|
1765
|
+
task.agent || "unknown",
|
|
1766
|
+
task.role || "unknown",
|
|
1767
|
+
task.doc_path || null,
|
|
1768
|
+
task.created_at,
|
|
1769
|
+
task.updated_at,
|
|
1770
|
+
task.finished_at || null,
|
|
1771
|
+
task.canceled_at || null,
|
|
1772
|
+
task.tags ? JSON.stringify(task.tags) : null,
|
|
1773
|
+
task.suggested_skills ? JSON.stringify(task.suggested_skills) : null,
|
|
1774
|
+
task.metadata ? JSON.stringify(task.metadata) : null,
|
|
1775
|
+
task.parent_id || null,
|
|
1776
|
+
task.depends_on || null,
|
|
1777
|
+
task.est_tokens || 0,
|
|
1778
|
+
task.in_progress_at || null,
|
|
1779
|
+
task.commit_id || null,
|
|
1780
|
+
task.changed_files ? JSON.stringify(task.changed_files) : null
|
|
1781
|
+
]
|
|
1782
|
+
);
|
|
1783
|
+
} catch (err) {
|
|
1784
|
+
this.handleDuplicateTaskCode(err, task.task_code, task.repo);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
handleDuplicateTaskCode(err, taskCode, repo) {
|
|
1788
|
+
if (isSqliteError(err) && err.code === "SQLITE_CONSTRAINT_UNIQUE") {
|
|
1789
|
+
throw new Error(
|
|
1790
|
+
`Duplicate task_code: '${taskCode}' already exists in repository '${repo}'. The task_code must be unique within the repository.`
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
throw err;
|
|
1748
1794
|
}
|
|
1749
1795
|
updateTask(id, updates) {
|
|
1750
1796
|
const fields = [];
|
|
@@ -1843,14 +1889,19 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1843
1889
|
});
|
|
1844
1890
|
}
|
|
1845
1891
|
getTaskByCode(owner, repo, taskCode) {
|
|
1892
|
+
const params = [repo, taskCode];
|
|
1893
|
+
const ownerClause = owner ? "t.owner = ? AND " : "";
|
|
1894
|
+
if (owner) {
|
|
1895
|
+
params.unshift(owner);
|
|
1896
|
+
}
|
|
1846
1897
|
const row = this.get(
|
|
1847
1898
|
`SELECT t.*, d.task_code as depends_on_code, p.task_code as parent_code,
|
|
1848
1899
|
${this.coordinationSelect("t")}
|
|
1849
1900
|
FROM tasks t
|
|
1850
1901
|
LEFT JOIN tasks d ON t.depends_on = d.id
|
|
1851
1902
|
LEFT JOIN tasks p ON t.parent_id = p.id
|
|
1852
|
-
WHERE t.
|
|
1853
|
-
|
|
1903
|
+
WHERE ${ownerClause}t.repo = ? AND t.task_code = ?`,
|
|
1904
|
+
params
|
|
1854
1905
|
);
|
|
1855
1906
|
return row ? {
|
|
1856
1907
|
...this.rowToTask(row),
|
|
@@ -1996,8 +2047,12 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1996
2047
|
return row?.count ?? 0;
|
|
1997
2048
|
}
|
|
1998
2049
|
isTaskCodeDuplicate(owner, repo, task_code, excludeId) {
|
|
1999
|
-
let query = "SELECT COUNT(*) as count FROM tasks WHERE
|
|
2000
|
-
const params = [
|
|
2050
|
+
let query = "SELECT COUNT(*) as count FROM tasks WHERE repo = ? AND task_code = ?";
|
|
2051
|
+
const params = [repo, task_code];
|
|
2052
|
+
if (owner) {
|
|
2053
|
+
query = "SELECT COUNT(*) as count FROM tasks WHERE owner = ? AND repo = ? AND task_code = ?";
|
|
2054
|
+
params.unshift(owner);
|
|
2055
|
+
}
|
|
2001
2056
|
if (excludeId) {
|
|
2002
2057
|
query += " AND id != ?";
|
|
2003
2058
|
params.push(excludeId);
|
|
@@ -2020,9 +2075,14 @@ var TaskEntity = class extends BaseEntity {
|
|
|
2020
2075
|
getExistingTaskCodes(owner, repo, codes) {
|
|
2021
2076
|
if (codes.length === 0) return /* @__PURE__ */ new Set();
|
|
2022
2077
|
const placeholders = codes.map(() => "?").join(",");
|
|
2078
|
+
const params = [repo, ...codes];
|
|
2079
|
+
const ownerClause = owner ? "owner = ? AND " : "";
|
|
2080
|
+
if (owner) {
|
|
2081
|
+
params.unshift(owner);
|
|
2082
|
+
}
|
|
2023
2083
|
const rows = this.all(
|
|
2024
|
-
`SELECT task_code FROM tasks WHERE
|
|
2025
|
-
|
|
2084
|
+
`SELECT task_code FROM tasks WHERE ${ownerClause}repo = ? AND task_code IN (${placeholders})`,
|
|
2085
|
+
params
|
|
2026
2086
|
);
|
|
2027
2087
|
return new Set(rows.map((r) => r.task_code));
|
|
2028
2088
|
}
|
|
@@ -4899,7 +4959,7 @@ var TOOL_DEFINITIONS = [
|
|
|
4899
4959
|
{
|
|
4900
4960
|
name: "task-list",
|
|
4901
4961
|
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.",
|
|
4962
|
+
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
4963
|
annotations: {
|
|
4904
4964
|
readOnlyHint: true,
|
|
4905
4965
|
idempotentHint: true,
|
|
@@ -4916,7 +4976,7 @@ var TOOL_DEFINITIONS = [
|
|
|
4916
4976
|
status: {
|
|
4917
4977
|
type: "string",
|
|
4918
4978
|
default: "in_progress,pending",
|
|
4919
|
-
description: "Comma-separated status filter (backlog, pending, in_progress, completed, canceled, blocked). Defaults to 'in_progress,
|
|
4979
|
+
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
4980
|
},
|
|
4921
4981
|
phase: {
|
|
4922
4982
|
type: "string",
|
|
@@ -6462,6 +6522,7 @@ export {
|
|
|
6462
6522
|
MemoryDeleteSchema,
|
|
6463
6523
|
MemorySummarizeSchema,
|
|
6464
6524
|
MemorySynthesizeSchema,
|
|
6525
|
+
TaskStatusSchema,
|
|
6465
6526
|
TaskCreateSchema,
|
|
6466
6527
|
TaskCreateInteractiveSchema,
|
|
6467
6528
|
TaskUpdateSchema,
|
package/dist/dashboard/server.js
CHANGED
package/dist/mcp/server.js
CHANGED
|
@@ -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-
|
|
66
|
+
} from "../chunk-IHDJKNX4.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.
|
|
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();
|