@vheins/local-memory-mcp 0.16.1 → 0.16.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.
- package/dist/{chunk-C5MOQQAU.js → chunk-MSP4MIT7.js} +58 -5
- package/dist/dashboard/server.js +1 -1
- package/dist/mcp/server.js +37 -2
- package/dist/prompts/create-task.md +8 -0
- package/dist/prompts/server/instructions.md +1 -1
- package/dist/prompts/task-memory-executor.md +3 -2
- package/package.json +4 -2
|
@@ -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.16.
|
|
85
|
-
pkgVersion = "0.16.
|
|
84
|
+
if ("0.16.3") {
|
|
85
|
+
pkgVersion = "0.16.3";
|
|
86
86
|
} else {
|
|
87
87
|
let searchDir = __dirname2;
|
|
88
88
|
for (let i = 0; i < 5; i++) {
|
|
@@ -1908,6 +1908,18 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1908
1908
|
const row = this.get(query, params);
|
|
1909
1909
|
return (row?.count ?? 0) > 0;
|
|
1910
1910
|
}
|
|
1911
|
+
getChildrenByParentId(id) {
|
|
1912
|
+
return this.all(
|
|
1913
|
+
"SELECT task_code, title, status FROM tasks WHERE parent_id = ? ORDER BY created_at ASC",
|
|
1914
|
+
[id]
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
getDependedByTaskId(id) {
|
|
1918
|
+
return this.all(
|
|
1919
|
+
"SELECT task_code, title, status FROM tasks WHERE depends_on = ? ORDER BY created_at ASC",
|
|
1920
|
+
[id]
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1911
1923
|
getExistingTaskCodes(repo, codes) {
|
|
1912
1924
|
if (codes.length === 0) return /* @__PURE__ */ new Set();
|
|
1913
1925
|
const placeholders = codes.map(() => "?").join(",");
|
|
@@ -3463,6 +3475,27 @@ var MemorySynthesizeSchema = z.object({
|
|
|
3463
3475
|
});
|
|
3464
3476
|
var TaskStatusSchema = z.enum(["backlog", "pending", "in_progress", "completed", "canceled", "blocked"]);
|
|
3465
3477
|
var TaskPrioritySchema = z.number().min(1).max(5);
|
|
3478
|
+
var TaskMetadataSchema = z.record(z.string(), z.any()).optional().superRefine((metadata, ctx) => {
|
|
3479
|
+
if (!metadata) return;
|
|
3480
|
+
if (metadata.required_skills !== void 0) {
|
|
3481
|
+
if (!Array.isArray(metadata.required_skills)) {
|
|
3482
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "metadata.required_skills must be an array of strings", path: ["metadata", "required_skills"] });
|
|
3483
|
+
} else if (metadata.required_skills.length === 0) {
|
|
3484
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "metadata.required_skills must not be empty when present", path: ["metadata", "required_skills"] });
|
|
3485
|
+
} else if (!metadata.required_skills.every((s) => typeof s === "string" && s.length > 0)) {
|
|
3486
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "metadata.required_skills must be an array of non-empty strings", path: ["metadata", "required_skills"] });
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
if (metadata.fsm_gates !== void 0) {
|
|
3490
|
+
if (!Array.isArray(metadata.fsm_gates)) {
|
|
3491
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "metadata.fsm_gates must be an array of strings", path: ["metadata", "fsm_gates"] });
|
|
3492
|
+
} else if (metadata.fsm_gates.length === 0) {
|
|
3493
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "metadata.fsm_gates must not be empty when present", path: ["metadata", "fsm_gates"] });
|
|
3494
|
+
} else if (!metadata.fsm_gates.every((s) => typeof s === "string" && s.length > 0)) {
|
|
3495
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "metadata.fsm_gates must be an array of non-empty strings", path: ["metadata", "fsm_gates"] });
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
});
|
|
3466
3499
|
var SingleTaskCreateSchema = z.object({
|
|
3467
3500
|
task_code: z.string().min(1).optional(),
|
|
3468
3501
|
phase: z.string().min(1),
|
|
@@ -3475,7 +3508,7 @@ var SingleTaskCreateSchema = z.object({
|
|
|
3475
3508
|
doc_path: z.string().optional(),
|
|
3476
3509
|
tags: z.array(z.string()).optional(),
|
|
3477
3510
|
suggested_skills: z.array(z.string()).optional(),
|
|
3478
|
-
metadata:
|
|
3511
|
+
metadata: TaskMetadataSchema,
|
|
3479
3512
|
parent_id: z.string().optional(),
|
|
3480
3513
|
depends_on: z.string().optional(),
|
|
3481
3514
|
est_tokens: z.number().int().min(0).optional()
|
|
@@ -5737,6 +5770,9 @@ async function completePromptArgument(name, argName, value, contextArguments, da
|
|
|
5737
5770
|
return [];
|
|
5738
5771
|
}
|
|
5739
5772
|
|
|
5773
|
+
// src/mcp/tools/handoff.manage.ts
|
|
5774
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
5775
|
+
|
|
5740
5776
|
// src/mcp/utils/mcp-response.ts
|
|
5741
5777
|
import { z as z2 } from "zod";
|
|
5742
5778
|
var McpAnnotationsSchema = z2.object({
|
|
@@ -5982,14 +6018,15 @@ async function handleTaskClaim(args, storage) {
|
|
|
5982
6018
|
const { repo, task_id, task_code, agent, role, metadata, structured } = validated;
|
|
5983
6019
|
let taskId = task_id;
|
|
5984
6020
|
let resolvedTaskCode;
|
|
6021
|
+
let task = null;
|
|
5985
6022
|
if (taskId) {
|
|
5986
|
-
|
|
6023
|
+
task = storage.tasks.getTaskById(taskId);
|
|
5987
6024
|
if (!task || task.repo !== repo) {
|
|
5988
6025
|
throw new Error(`Task not found: ${taskId} in repo ${repo}`);
|
|
5989
6026
|
}
|
|
5990
6027
|
resolvedTaskCode = task.task_code;
|
|
5991
6028
|
} else if (task_code) {
|
|
5992
|
-
|
|
6029
|
+
task = storage.tasks.getTaskByCode(repo, task_code);
|
|
5993
6030
|
if (!task) {
|
|
5994
6031
|
throw new Error(`Task not found: ${task_code} in repo ${repo}`);
|
|
5995
6032
|
}
|
|
@@ -6005,6 +6042,22 @@ async function handleTaskClaim(args, storage) {
|
|
|
6005
6042
|
role,
|
|
6006
6043
|
metadata
|
|
6007
6044
|
});
|
|
6045
|
+
if (task && task.status !== "completed") {
|
|
6046
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6047
|
+
storage.tasks.updateTask(task.id, { status: "in_progress", in_progress_at: now });
|
|
6048
|
+
storage.taskComments.insertTaskComment({
|
|
6049
|
+
id: randomUUID2(),
|
|
6050
|
+
task_id: task.id,
|
|
6051
|
+
repo,
|
|
6052
|
+
comment: `Claimed by ${agent} \u2014 auto-promoted to in_progress`,
|
|
6053
|
+
agent,
|
|
6054
|
+
role: role || "unknown",
|
|
6055
|
+
model: "system",
|
|
6056
|
+
previous_status: task.status,
|
|
6057
|
+
next_status: "in_progress",
|
|
6058
|
+
created_at: now
|
|
6059
|
+
});
|
|
6060
|
+
}
|
|
6008
6061
|
const responseData = {
|
|
6009
6062
|
...claim,
|
|
6010
6063
|
task_code: resolvedTaskCode
|
package/dist/dashboard/server.js
CHANGED
package/dist/mcp/server.js
CHANGED
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
toContextSlug,
|
|
61
61
|
updateSessionFromInitialize,
|
|
62
62
|
updateSessionRoots
|
|
63
|
-
} from "../chunk-
|
|
63
|
+
} from "../chunk-MSP4MIT7.js";
|
|
64
64
|
|
|
65
65
|
// src/mcp/server.ts
|
|
66
66
|
import readline from "readline";
|
|
@@ -2370,24 +2370,57 @@ async function handleTaskGet(args, storage) {
|
|
|
2370
2370
|
throw new Error(`Task not found: ${id || task_code} in repo ${repo}`);
|
|
2371
2371
|
}
|
|
2372
2372
|
const comments = storage.taskComments.getTaskCommentsByTaskId(task.id);
|
|
2373
|
+
const children = storage.tasks.getChildrenByParentId(task.id);
|
|
2374
|
+
const depended_by = storage.tasks.getDependedByTaskId(task.id);
|
|
2373
2375
|
let contentSummary;
|
|
2374
2376
|
if (!isStructuredRequest) {
|
|
2375
2377
|
const lines = [
|
|
2376
2378
|
`Task: ${task.title}`,
|
|
2377
2379
|
`Code: ${task.task_code}`,
|
|
2380
|
+
`Repo: ${task.repo}`,
|
|
2378
2381
|
`Status: ${task.status}`,
|
|
2379
2382
|
`Priority: ${task.priority}`,
|
|
2380
2383
|
`ID: ${task.id}`
|
|
2381
2384
|
];
|
|
2382
2385
|
if (task.phase) lines.push(`Phase: ${task.phase}`);
|
|
2386
|
+
if (task.parent_code) lines.push(`Parent: ${task.parent_code} (${task.parent_id || ""})`);
|
|
2387
|
+
if (task.depends_on_code) lines.push(`Depends On: ${task.depends_on_code} (${task.depends_on || ""})`);
|
|
2388
|
+
if (task.doc_path) lines.push(`Doc Path: ${task.doc_path}`);
|
|
2383
2389
|
if (task.description) lines.push(`Description: ${task.description}`);
|
|
2390
|
+
if (task.tags && task.tags.length > 0) lines.push(`Tags: ${task.tags.join(", ")}`);
|
|
2384
2391
|
if (task.suggested_skills && task.suggested_skills.length > 0)
|
|
2385
2392
|
lines.push(`Suggested Skills: ${task.suggested_skills.join(", ")}`);
|
|
2393
|
+
if (task.est_tokens) lines.push(`Est Tokens: ${task.est_tokens}`);
|
|
2394
|
+
if (task.commit_id) lines.push(`Commit: ${task.commit_id}`);
|
|
2395
|
+
if (task.changed_files && task.changed_files.length > 0)
|
|
2396
|
+
lines.push(`Changed Files: ${task.changed_files.join(", ")}`);
|
|
2386
2397
|
if (task.metadata) lines.push(`Metadata: ${JSON.stringify(task.metadata)}`);
|
|
2398
|
+
if (task.comments_count !== void 0) lines.push(`Comments: ${task.comments_count}`);
|
|
2399
|
+
if (task.coordination) {
|
|
2400
|
+
if (task.coordination.active_claim_count > 0) {
|
|
2401
|
+
lines.push(`Claim: ${task.coordination.active_claim_agent || "?"} (${task.coordination.active_claim_role || ""}) since ${task.coordination.active_claim_claimed_at || ""}`);
|
|
2402
|
+
}
|
|
2403
|
+
if (task.coordination.pending_handoff_count > 0) {
|
|
2404
|
+
lines.push(`Handoff: ${task.coordination.pending_handoff_summary || ""} \u2192 ${task.coordination.pending_handoff_to_agent || "?"}`);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2387
2407
|
lines.push(`Created: ${task.created_at}`);
|
|
2388
2408
|
if (task.updated_at) lines.push(`Updated: ${task.updated_at}`);
|
|
2389
2409
|
if (task.in_progress_at) lines.push(`Started: ${task.in_progress_at}`);
|
|
2390
2410
|
if (task.finished_at) lines.push(`Finished: ${task.finished_at}`);
|
|
2411
|
+
if (task.canceled_at) lines.push(`Canceled: ${task.canceled_at}`);
|
|
2412
|
+
if (children.length > 0) {
|
|
2413
|
+
lines.push("", "--- Children ---");
|
|
2414
|
+
for (const c of children) {
|
|
2415
|
+
lines.push(`- ${c.task_code}: ${c.title} (${c.status})`);
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
if (depended_by.length > 0) {
|
|
2419
|
+
lines.push("", "--- Depended By ---");
|
|
2420
|
+
for (const d of depended_by) {
|
|
2421
|
+
lines.push(`- ${d.task_code}: ${d.title} (${d.status})`);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2391
2424
|
if (comments.length > 0) {
|
|
2392
2425
|
lines.push("", "--- History ---");
|
|
2393
2426
|
for (const c of comments) {
|
|
@@ -2400,7 +2433,9 @@ async function handleTaskGet(args, storage) {
|
|
|
2400
2433
|
}
|
|
2401
2434
|
const structuredData = {
|
|
2402
2435
|
...task,
|
|
2403
|
-
comments
|
|
2436
|
+
comments,
|
|
2437
|
+
children,
|
|
2438
|
+
depended_by
|
|
2404
2439
|
};
|
|
2405
2440
|
return createMcpResponse(structuredData, contentSummary || "", {
|
|
2406
2441
|
contentSummary,
|
|
@@ -42,6 +42,14 @@ G2 | sprint? | src=.agents/documents/tasks/sprints/ | → route sprint flow |
|
|
|
42
42
|
|
|
43
43
|
`1=Low` `2=Normal` `3=Medium` `4=High` `5=Critical` (ascending urgency)
|
|
44
44
|
|
|
45
|
+
## 3B. SKILL TRACKING METADATA
|
|
46
|
+
When a task's description references specific skills (e.g., "Load feature-decomposition skill"), set `metadata.required_skills` and `metadata.fsm_gates`:
|
|
47
|
+
|
|
48
|
+
- `metadata.required_skills`: `["skill-name-1", "skill-name-2"]` — array of skill names the task depends on.
|
|
49
|
+
- `metadata.fsm_gates`: `["S0", "S1", "G0"]` — array of FSM step/gate identifiers the skill requires.
|
|
50
|
+
- **Optional**: Only set when the task explicitly references a skill. Most tasks do NOT need skill tracking.
|
|
51
|
+
- **Enforcement**: The `task-memory-executor` G0.5 gate checks `metadata.required_skills` before execution. If a skill is listed but wasn't loaded, the executor blocks the task.
|
|
52
|
+
|
|
45
53
|
## Blueprint Flow (G1→)
|
|
46
54
|
|
|
47
55
|
Root parent → phase parents P0..P10 → child tasks per breakdown row
|
|
@@ -21,7 +21,7 @@ S2 | continue to task or respond | S1✅ | ready | —
|
|
|
21
21
|
- memory-acknowledge after code gen from memory
|
|
22
22
|
- Global scope = cross-repo only; prefer repo-specific
|
|
23
23
|
|
|
24
|
-
**Tasks**: task-list → task-claim →
|
|
24
|
+
**Tasks**: task-list → task-claim(auto → in_progress) → task-update(completed)
|
|
25
25
|
|
|
26
26
|
- Register via task-create before execution
|
|
27
27
|
- NEVER skip in_progress
|
|
@@ -19,8 +19,9 @@ Guard: S(N) req S(N-1)✅; dependency-ready filter (depends_on+parent_id done)
|
|
|
19
19
|
S0 | sync: resolve identity (arg→auto `<runner>-<randomName>`, 1x reuse all loop) + task-list(ONCE) + handoff-list(pending, close stale) + audit stale in_progress(>30m, hydrate via task-detail) | — | filtered queue | —
|
|
20
20
|
S1 | hydrate: task-detail ONCE per task — MUST cache, MUST reuse all steps, NO re-fetch | S0✅ | full task | —
|
|
21
21
|
G0 | readiness: depends_on✅ AND parent_id✅? if all blocked → report blockers + pause | S1✅ | → S2 / skip+pick next | —
|
|
22
|
-
S2 | claim: task-claim(with identity metadata)
|
|
23
|
-
|
|
22
|
+
S2 | claim: task-claim(with identity metadata) [auto → in_progress] | G0✅ | ownership | —
|
|
23
|
+
G0.5 | skill readiness: check task.metadata.required_skills — if present, load each via skill() and verify FSM completed (all gates passed). If skill not loaded or FSM incomplete → ⛔ block with reason. If absent → skip (most tasks don't use skills). Check task-detail for suggested_skills too — if present, load each. | S2✅ | → S3 / ⛔ → blocker | —
|
|
24
|
+
S3 | research: memory-search + standard-search(MANDATORY per task — even sub-agents/decomposed) + hydrate relevant | G0.5✅ | context | —
|
|
24
25
|
S4 | execute: trace logic+callsites+docs — DO NOT infer from file presence; decompose if too broad | S3✅ | changes | —
|
|
25
26
|
S5 | validate: tests + linters + type-check + browser(if UI — MANDATORY: console errors, overflow, responsive, core interactions) + logic audit all paths | S4✅ | verification | —
|
|
26
27
|
S6 | finalize: task-update→completed(evidence: inspected files, verified logic, test results) + memory-store(insights) + standard-store(rules) + handoff(if work remains — with identity) + retrospective + report | S5✅ | completion | —
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vheins/local-memory-mcp",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.3",
|
|
4
4
|
"description": "MCP Local Memory Service for coding copilot agents",
|
|
5
5
|
"mcpName": "io.github.vheins/local-memory-mcp",
|
|
6
6
|
"type": "module",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"better-sqlite3": "^12.9.0",
|
|
51
51
|
"chart.js": "^4.5.1",
|
|
52
52
|
"dompurify": "^3.3.3",
|
|
53
|
+
"esbuild": "0.28.1",
|
|
53
54
|
"express": "^5.2.1",
|
|
54
55
|
"gray-matter": "^4.0.3",
|
|
55
56
|
"marked": "^18.0.0",
|
|
@@ -91,6 +92,7 @@
|
|
|
91
92
|
},
|
|
92
93
|
"overrides": {
|
|
93
94
|
"protobufjs": "^7.5.8",
|
|
94
|
-
"brace-expansion": "^5.0.6"
|
|
95
|
+
"brace-expansion": "^5.0.6",
|
|
96
|
+
"esbuild": "0.28.1"
|
|
95
97
|
}
|
|
96
98
|
}
|