@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.
@@ -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.1") {
85
- pkgVersion = "0.16.1";
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: z.record(z.string(), z.any()).optional(),
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
- const task = storage.tasks.getTaskById(taskId);
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
- const task = storage.tasks.getTaskByCode(repo, task_code);
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
@@ -16,7 +16,7 @@ import {
16
16
  handleTaskClaim,
17
17
  listResources,
18
18
  logger
19
- } from "../chunk-C5MOQQAU.js";
19
+ } from "../chunk-MSP4MIT7.js";
20
20
 
21
21
  // src/dashboard/server.ts
22
22
  import express from "express";
@@ -60,7 +60,7 @@ import {
60
60
  toContextSlug,
61
61
  updateSessionFromInitialize,
62
62
  updateSessionRoots
63
- } from "../chunk-C5MOQQAU.js";
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 → task-update(in_progress) → task-update(completed)
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) + task-update→in_progress(agent, role, identity) | G0✅ | ownership | —
23
- S3 | research: check task-detail for suggested_skills load each via skill() tool; then memory-search + standard-search(MANDATORY per task — even sub-agents/decomposed) + hydrate relevant | S2✅ | context | —
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.1",
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
  }