@agent-plan/mcp 0.2.19-next.4 → 0.2.19-next.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA45BA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAItD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAw4BA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAItD"}
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import * as z from "zod/v4";
5
5
  import { join } from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
- import { PlanStore, ExportService, withFeatureLock, needsMotivation, findPhaseByRef, buildRecap } from "@agent-plan/core";
7
+ import { PlanStore, ExportService, withFeatureLock, needsMotivation, findPhaseByRef, findTaskByRef, buildRecap, migrateToGlobalSequence } from "@agent-plan/core";
8
8
  import { serve } from "@agent-plan/server";
9
9
  import { createChecklistItemId, createFeatureId, createPhaseId, createShortId, createTaskId, clampSlug, normalizeSlug, formatPhaseRef, formatFeatureRef } from "@agent-plan/core/naming";
10
10
  const STATUS_VALUES = ["planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"];
@@ -31,6 +31,7 @@ async function requireStore() {
31
31
  if (!planBackfillDone) {
32
32
  planBackfillDone = true;
33
33
  await st.ensureShortIdsAndPriority().catch(() => { });
34
+ await migrateToGlobalSequence(st).catch(() => { });
34
35
  }
35
36
  return st;
36
37
  }
@@ -61,34 +62,6 @@ function findFeatureByRef(features, ref) {
61
62
  ?? features.find((feature) => feature.name.toLowerCase() === normalized)
62
63
  ?? features.find((feature) => feature.name.toLowerCase().includes(normalized));
63
64
  }
64
- function findTaskByRef(phases, ref) {
65
- const normalized = ref.trim().toLowerCase();
66
- if (!normalized)
67
- return undefined;
68
- // Composite ref like "P00x(F00x)/T00x" or "T00x(P00x/F00x)": match by phase + task number.
69
- const tMatch = normalized.match(/t0*(\d+)/);
70
- const pMatch = normalized.match(/p0*(\d+)/);
71
- if (tMatch && pMatch) {
72
- const tNum = parseInt(tMatch[1], 10);
73
- const pNum = parseInt(pMatch[1], 10);
74
- for (const phase of phases) {
75
- if (phase.number !== pNum)
76
- continue;
77
- const task = phase.tasks.find((t) => t.number === tNum);
78
- if (task)
79
- return { phase, task };
80
- }
81
- }
82
- for (const phase of phases) {
83
- const task = phase.tasks.find((entry) => entry.id.toLowerCase() === normalized
84
- || (entry.shortId && entry.shortId.toLowerCase() === normalized)
85
- || entry.title.toLowerCase() === normalized
86
- || entry.title.toLowerCase().includes(normalized));
87
- if (task)
88
- return { phase, task };
89
- }
90
- return undefined;
91
- }
92
65
  function featureNumberOfPhase(phase, features) {
93
66
  return phase.featureId ? features.find((f) => f.id === phase.featureId)?.number : undefined;
94
67
  }
@@ -289,7 +262,7 @@ server.registerTool("planner-feature-add", {
289
262
  const priority = await st.nextPriority("feature");
290
263
  const feature = {
291
264
  id: createFeatureId(),
292
- number: existingFeatures.length + 1,
265
+ number: await st.allocFeatureNumber(),
293
266
  shortId,
294
267
  priority,
295
268
  name: name.trim(),
@@ -316,7 +289,7 @@ server.registerTool("planner-feature-add", {
316
289
  });
317
290
  server.registerTool("planner-feature-show", {
318
291
  description: "Show a feature by id or name.",
319
- inputSchema: { feature: z.string().min(1).describe("Feature id or name") },
292
+ inputSchema: { feature: z.string().min(1).describe("Feature ref. Accepts F00x/P00x/T00x composite, bare P00x/T00x (global), 5-char shortId, UUID, or title.") },
320
293
  }, async ({ feature: ref }) => {
321
294
  const st = await requireStore();
322
295
  const features = (await st.loadFeatures()).features;
@@ -324,12 +297,12 @@ server.registerTool("planner-feature-show", {
324
297
  if (!feature)
325
298
  return text(`Feature not found: ${ref}`);
326
299
  const phases = (await st.loadAllPhases()).filter((phase) => phase.featureId === feature.id);
327
- return text(`${feature.name} (${feature.id}) — ${feature.status}; ${phases.length} phases`, { feature, phases });
300
+ return text(`${feature.name} — ${formatFeatureRef(feature.number)}${feature.shortId ? ` · ${feature.shortId}` : ""} (${feature.status}; ${phases.length} phases)`, { feature, phases });
328
301
  });
329
302
  server.registerTool("planner-feature-update", {
330
303
  description: "Update a feature.",
331
304
  inputSchema: {
332
- feature: z.string().min(1).describe("Feature id or name"),
305
+ feature: z.string().min(1).describe("Feature ref. Accepts F00x/P00x/T00x composite, bare P00x/T00x (global), 5-char shortId, UUID, or title."),
333
306
  name: z.string().optional(),
334
307
  description: z.string().optional(),
335
308
  status: z.enum(STATUS_VALUES).optional(),
@@ -374,7 +347,7 @@ server.registerTool("planner-feature-update", {
374
347
  server.registerTool("planner-feature-delete", {
375
348
  description: "Delete a feature. By default, phases are unlinked rather than deleted.",
376
349
  inputSchema: {
377
- feature: z.string().min(1).describe("Feature id or name"),
350
+ feature: z.string().min(1).describe("Feature ref. Accepts F00x/P00x/T00x composite, bare P00x/T00x (global), 5-char shortId, UUID, or title."),
378
351
  cascade: z.boolean().optional().describe("Also delete phases belonging to this feature"),
379
352
  },
380
353
  }, async ({ feature: ref, cascade }) => {
@@ -404,7 +377,7 @@ server.registerTool("planner-phase-add", {
404
377
  description: "Create a phase with a rich description. REQUIRED: description must include code references (file:line), current implementation state, dependencies, specific files/systems to modify, and behaviors to preserve. The description is the primary context for future agents; one-liners cause misalignment.",
405
378
  inputSchema: {
406
379
  title: z.string().min(1),
407
- feature: z.string().optional().describe("Feature id or name"),
380
+ feature: z.string().optional().describe("Feature ref. Accepts F00x/P00x/T00x composite, bare P00x/T00x (global), 5-char shortId, UUID, or title."),
408
381
  summary: z.string().optional().describe("One-line summary of the phase"),
409
382
  description: z.string().min(50, "Description must be at least 50 characters — include code references (file:line), current state, structs/traits involved, concrete work items, behaviors to preserve. Prefix with 'design-only' for pre-implementation design tasks.").describe("Required code references (file:line), current state, structs/traits involved, concrete work items, behaviors to preserve. Not a one-liner."),
410
383
  },
@@ -424,7 +397,7 @@ server.registerTool("planner-phase-add", {
424
397
  const priority = await st.nextPriority("phase", feature?.id);
425
398
  phase = {
426
399
  id: createPhaseId(),
427
- number: featurePhases.reduce((max, entry) => Math.max(max, entry.number), 0) + 1,
400
+ number: await st.allocPhaseNumber(),
428
401
  shortId,
429
402
  priority,
430
403
  slug: normalizeSlug(title),
@@ -472,18 +445,19 @@ server.registerTool("planner-phase-add", {
472
445
  });
473
446
  server.registerTool("planner-phase-show", {
474
447
  description: "Show a phase by id or name.",
475
- inputSchema: { phase: z.string().min(1).describe("Phase id or name") },
448
+ inputSchema: { phase: z.string().min(1).describe("Phase ref. Accepts F00x/P00x/T00x composite, bare P00x/T00x (global), 5-char shortId, UUID, or title.") },
476
449
  }, async ({ phase: ref }) => {
477
450
  const st = await requireStore();
478
- const phase = findPhaseByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
451
+ const features = (await st.loadFeatures()).features;
452
+ const phase = findPhaseByRef(await st.loadAllPhases(), features, ref);
479
453
  if (!phase)
480
454
  return text(`Phase not found: ${ref}`);
481
- return text(`${phase.title} (${phase.id}) — ${phase.status}; ${phase.tasks.length} tasks`, { phase });
455
+ return text(`${phase.title} — ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, features))}${phase.shortId ? ` · ${phase.shortId}` : ""} (${phase.status}; ${phase.tasks.length} tasks)`, { phase });
482
456
  });
483
457
  server.registerTool("planner-phase-discuss", {
484
458
  description: "Persist phase discovery fields and mark phase planned.",
485
459
  inputSchema: {
486
- phase: z.string().min(1).describe("Phase id or name"),
460
+ phase: z.string().min(1).describe("Phase ref. Accepts F00x/P00x/T00x composite, bare P00x/T00x (global), 5-char shortId, UUID, or title."),
487
461
  goal: z.string().optional(),
488
462
  summary: z.string().optional(),
489
463
  scope: z.string().optional(),
@@ -568,12 +542,13 @@ server.registerTool("planner-phase-delete", {
568
542
  feature.phaseIds = feature.phaseIds.filter((id) => id !== phase.id);
569
543
  return doc;
570
544
  });
571
- return writeAndSummarize(st, `Phase deleted: ${phase.id}`, { deleted: phase.id });
545
+ const dFeat = (await st.loadFeatures()).features.find((f) => f.id === phase.featureId);
546
+ return writeAndSummarize(st, `Phase deleted: ${formatPhaseRef(phase.number, dFeat?.number)}${phase.shortId ? ` · ${phase.shortId}` : ""}`, { deleted: phase.id });
572
547
  });
573
548
  server.registerTool("planner-task-add", {
574
549
  description: "Create a task with a rich description. REQUIRED: description must include code references (file:line), what already exists vs what needs to be built, specific structs/traits/systems to modify, concrete implementation steps, and edge cases to handle. The description is the execution context for agents; one-liners cause misalignment.",
575
550
  inputSchema: {
576
- phase: z.string().min(1).describe("Phase id or name"),
551
+ phase: z.string().min(1).describe("Phase ref. Accepts F00x/P00x/T00x composite, bare P00x/T00x (global), 5-char shortId, UUID, or title."),
577
552
  title: z.string().min(1),
578
553
  description: z.string().min(50, "Description must be at least 50 characters — include code references (file:line), current state vs desired state, structs/traits to modify, concrete implementation steps, edge cases. Prefix with 'design-only' for pre-implementation design tasks.").describe("Required code references (file:line), current state vs desired state, structs/traits to modify, concrete implementation steps, edge cases. Not a one-liner."),
579
554
  checklist: z.array(z.string()).optional(),
@@ -590,7 +565,7 @@ server.registerTool("planner-task-add", {
590
565
  const task = {
591
566
  id: taskId,
592
567
  phaseId: found.id,
593
- number: found.tasks.length + 1,
568
+ number: await st.allocTaskNumber(),
594
569
  shortId,
595
570
  priority,
596
571
  shortName: clampSlug(title, 30, `task-${Date.now().toString(36)}`),
@@ -620,13 +595,13 @@ server.registerTool("planner-task-add", {
620
595
  });
621
596
  server.registerTool("planner-task-show", {
622
597
  description: "Show a task by id or name.",
623
- inputSchema: { task: z.string().min(1).describe("Task id or name") },
598
+ inputSchema: { task: z.string().min(1).describe("Task ref. Accepts F00x/P00x/T00x composite, bare P00x/T00x (global), 5-char shortId, UUID, or title.") },
624
599
  }, async ({ task: ref }) => {
625
600
  const st = await requireStore();
626
- const found = findTaskByRef(await st.loadAllPhases(), ref);
601
+ const found = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
627
602
  if (!found)
628
603
  return text(`Task not found: ${ref}`);
629
- return text(`${found.task.title} (${found.task.id}) — ${found.task.status}; phase ${found.phase.id}`, { task: found.task, phase: found.phase });
604
+ return text(`${found.task.title} — ${taskCompositeRef(found.task, found.phase, (await st.loadFeatures()).features)}${found.task.shortId ? ` · ${found.task.shortId}` : ""} (${found.task.status}; phase ${formatPhaseRef(found.phase.number, featureNumberOfPhase(found.phase, (await st.loadFeatures()).features))})`, { task: found.task, phase: found.phase });
630
605
  });
631
606
  server.registerTool("planner-task-discuss", {
632
607
  description: "Persist task execution notes/checklist.",
@@ -637,7 +612,7 @@ server.registerTool("planner-task-discuss", {
637
612
  },
638
613
  }, async ({ task: ref, description, checklist }) => {
639
614
  const st = await requireStore();
640
- const found = findTaskByRef(await st.loadAllPhases(), ref);
615
+ const found = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
641
616
  if (!found)
642
617
  return text(`Task not found: ${ref}`);
643
618
  let updatedTask;
@@ -670,7 +645,7 @@ server.registerTool("planner-task-update", {
670
645
  },
671
646
  }, async ({ task: ref, title, status, description, motivation, priority }) => {
672
647
  const st = await requireStore();
673
- const found = findTaskByRef(await st.loadAllPhases(), ref);
648
+ const found = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
674
649
  if (!found)
675
650
  return text(`Task not found: ${ref}`);
676
651
  // Validate motivation requirement for status transitions.
@@ -720,7 +695,7 @@ server.registerTool("planner-task-delete", {
720
695
  inputSchema: { task: z.string().min(1) },
721
696
  }, async ({ task: ref }) => {
722
697
  const st = await requireStore();
723
- const found = findTaskByRef(await st.loadAllPhases(), ref);
698
+ const found = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
724
699
  if (!found)
725
700
  return text(`Task not found: ${ref}`);
726
701
  await st.updatePhase(found.phase.id, (phase) => {
@@ -730,14 +705,15 @@ server.registerTool("planner-task-delete", {
730
705
  return phase;
731
706
  });
732
707
  await st.syncTaskStatusRollup(found.phase.id);
733
- return writeAndSummarize(st, `Task deleted: ${found.task.id}`, { deleted: found.task.id });
708
+ const tdFeatures = (await st.loadFeatures()).features;
709
+ return writeAndSummarize(st, `Task deleted: ${taskCompositeRef(found.task, found.phase, tdFeatures)}${found.task.shortId ? ` · ${found.task.shortId}` : ""}`, { deleted: found.task.id });
734
710
  });
735
711
  server.registerTool("planner-task-start", {
736
712
  description: "Set a task to in-progress.",
737
713
  inputSchema: { task: z.string().min(1) },
738
714
  }, async ({ task: ref }) => {
739
715
  const st = await requireStore();
740
- const found = findTaskByRef(await st.loadAllPhases(), ref);
716
+ const found = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
741
717
  if (!found)
742
718
  return text(`Task not found: ${ref}`);
743
719
  // Auto-archive the handoff on THIS task's phase (reason: task-started) — the
@@ -790,7 +766,7 @@ server.registerTool("planner-task-complete", {
790
766
  },
791
767
  }, async ({ task: ref, force, description_update }) => {
792
768
  const st = await requireStore();
793
- const found = findTaskByRef(await st.loadAllPhases(), ref);
769
+ const found = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
794
770
  if (!found)
795
771
  return text(`Task not found: ${ref}`);
796
772
  const unchecked = found.task.checklist.filter((item) => !item.checked);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-plan/mcp",
3
- "version": "0.2.19-next.4",
3
+ "version": "0.2.19-next.5",
4
4
  "private": false,
5
5
  "description": "MCP server for Agent Plan, exposing planner-* tools over stdio.",
6
6
  "license": "MIT",
@@ -41,8 +41,8 @@
41
41
  "dependencies": {
42
42
  "@modelcontextprotocol/sdk": "^1.17.0",
43
43
  "zod": "^3.24.0",
44
- "@agent-plan/core": "0.2.19-next.4",
45
- "@agent-plan/server": "0.2.19-next.4"
44
+ "@agent-plan/core": "0.2.19-next.5",
45
+ "@agent-plan/server": "0.2.19-next.5"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "tsc -p tsconfig.json",