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

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":";AAm5BA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAItD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA45BA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAItD"}
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { join } from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
7
  import { PlanStore, ExportService, withFeatureLock, needsMotivation, findPhaseByRef, buildRecap } from "@agent-plan/core";
8
8
  import { serve } from "@agent-plan/server";
9
- import { createChecklistItemId, createFeatureId, createPhaseId, createShortId, createTaskId, clampSlug, normalizeSlug, formatPhaseRef } from "@agent-plan/core/naming";
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"];
11
11
  const PHASE_STATUS_VALUES = ["draft", "discovery", ...STATUS_VALUES];
12
12
  function text(textValue, structuredContent) {
@@ -189,7 +189,7 @@ server.registerTool("planner-init", {
189
189
  if (goal !== undefined)
190
190
  project.goal = goal.trim();
191
191
  await st.saveProject(project);
192
- return writeAndSummarize(st, `.planner/ initialized for "${project.name}"`, { project });
192
+ return writeAndSummarize(st, `.planner/ initialized for "${project.name}"`);
193
193
  });
194
194
  server.registerTool("planner-show", {
195
195
  description: "Show the current planner overview.",
@@ -227,7 +227,7 @@ server.registerTool("planner-project-language", {
227
227
  if (chatLanguage !== undefined)
228
228
  project.chatLanguage = chatLanguage.trim();
229
229
  await st.saveProject(project);
230
- return writeAndSummarize(st, `Saved language preferences: content=${project.contentLanguage || "(unset)"}, chat=${project.chatLanguage || "(unset)"}`, { project });
230
+ return writeAndSummarize(st, `Saved language preferences: content=${project.contentLanguage || "(unset)"}, chat=${project.chatLanguage || "(unset)"}`);
231
231
  });
232
232
  server.registerTool("planner-project-discuss", {
233
233
  description: "Record/update project-level discovery fields. This is the MCP equivalent of grouped project discuss without Pi UI prompts.",
@@ -258,7 +258,7 @@ server.registerTool("planner-project-discuss", {
258
258
  if (params.decisions !== undefined)
259
259
  project.decisions = params.decisions.map((entry) => entry.trim()).filter(Boolean);
260
260
  await st.saveProject(project);
261
- return writeAndSummarize(st, `Project discussed/updated: ${project.name}`, { project });
261
+ return writeAndSummarize(st, `Project discussed/updated: ${project.name}`);
262
262
  });
263
263
  server.registerTool("planner-feature-list", {
264
264
  description: "List features with phase/task counts.",
@@ -312,7 +312,7 @@ server.registerTool("planner-feature-add", {
312
312
  doc.features.push(feature);
313
313
  return doc;
314
314
  });
315
- return writeAndSummarize(st, `Feature created: ${feature.id} — ${feature.name}`, { feature });
315
+ return writeAndSummarize(st, `✅ Feature created: ${formatFeatureRef(feature.number)} — ${feature.name}${feature.shortId ? ` · ${feature.shortId}` : ""}`);
316
316
  });
317
317
  server.registerTool("planner-feature-show", {
318
318
  description: "Show a feature by id or name.",
@@ -369,7 +369,7 @@ server.registerTool("planner-feature-update", {
369
369
  return doc;
370
370
  });
371
371
  const result = updated.features.find((entry) => entry.id === feature.id);
372
- return writeAndSummarize(st, `Feature updated: ${result.id} — ${result.name}`, { feature: result });
372
+ return writeAndSummarize(st, `✅ Feature updated: ${formatFeatureRef(result.number)} — ${result.name}${result.shortId ? ` · ${result.shortId}` : ""}`);
373
373
  });
374
374
  server.registerTool("planner-feature-delete", {
375
375
  description: "Delete a feature. By default, phases are unlinked rather than deleted.",
@@ -452,6 +452,8 @@ server.registerTool("planner-phase-add", {
452
452
  updatedAt: timestamp,
453
453
  handoff: "",
454
454
  handoffUpdatedAt: "",
455
+ handoffReadAt: "",
456
+ handoffHistory: [],
455
457
  };
456
458
  await st.savePhase(phase);
457
459
  if (feature) {
@@ -466,7 +468,7 @@ server.registerTool("planner-phase-add", {
466
468
  });
467
469
  if (!phase)
468
470
  return text("Phase creation failed.");
469
- return writeAndSummarize(st, `Phase created: ${phase.id} — ${phase.title}`, { phase });
471
+ return writeAndSummarize(st, `✅ Phase created: ${formatPhaseRef(phase.number, feature?.number)} — ${phase.title}${phase.shortId ? ` · ${phase.shortId}` : ""}`);
470
472
  });
471
473
  server.registerTool("planner-phase-show", {
472
474
  description: "Show a phase by id or name.",
@@ -492,7 +494,8 @@ server.registerTool("planner-phase-discuss", {
492
494
  },
493
495
  }, async ({ phase: ref, ...updates }) => {
494
496
  const st = await requireStore();
495
- const found = findPhaseByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
497
+ const features = (await st.loadFeatures()).features;
498
+ const found = findPhaseByRef(await st.loadAllPhases(), features, ref);
496
499
  if (!found)
497
500
  return text(`Phase not found: ${ref}`);
498
501
  const phase = await st.updatePhase(found.id, (entry) => {
@@ -517,7 +520,7 @@ server.registerTool("planner-phase-discuss", {
517
520
  entry.updatedAt = nowISO();
518
521
  return entry;
519
522
  });
520
- return writeAndSummarize(st, `Phase discussed/planned: ${phase.id} — ${phase.title}`, { phase });
523
+ return writeAndSummarize(st, `✅ Phase discussed/planned: ${formatPhaseRef(found.number, featureNumberOfPhase(found, features))} — ${phase.title}${phase.shortId ? ` · ${phase.shortId}` : ""}`);
521
524
  });
522
525
  server.registerTool("planner-phase-update", {
523
526
  description: "Update phase fields.",
@@ -531,7 +534,8 @@ server.registerTool("planner-phase-update", {
531
534
  },
532
535
  }, async ({ phase: ref, ...updates }) => {
533
536
  const st = await requireStore();
534
- const found = findPhaseByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
537
+ const features = (await st.loadFeatures()).features;
538
+ const found = findPhaseByRef(await st.loadAllPhases(), features, ref);
535
539
  if (!found)
536
540
  return text(`Phase not found: ${ref}`);
537
541
  const phase = await st.updatePhase(found.id, (entry) => {
@@ -548,7 +552,7 @@ server.registerTool("planner-phase-update", {
548
552
  entry.updatedAt = nowISO();
549
553
  return entry;
550
554
  });
551
- return writeAndSummarize(st, `Phase updated: ${phase.id} — ${phase.title}`, { phase });
555
+ return writeAndSummarize(st, `✅ Phase updated: ${formatPhaseRef(found.number, featureNumberOfPhase(found, features))} — ${phase.title}${phase.shortId ? ` · ${phase.shortId}` : ""}`);
552
556
  });
553
557
  server.registerTool("planner-phase-delete", {
554
558
  description: "Delete a phase and unlink it from features.",
@@ -733,16 +737,22 @@ server.registerTool("planner-task-start", {
733
737
  inputSchema: { task: z.string().min(1) },
734
738
  }, async ({ task: ref }) => {
735
739
  const st = await requireStore();
736
- // Hygiene notice (non-blocking): surface an existing handoff but never block
737
- // task_start — the handoff is a captured-context artifact, not a lock (e.g. a
738
- // small modification after writing a handoff should start without forcing
739
- // deletion, which would lose context).
740
- const handoffNotice = (await st.listHandoffs()).length > 0
741
- ? "ℹ️ One or more phases have a pending handoff — if relevant to the task you're starting, read it with planner-handoff-show <ref>, then clear with planner-handoff-clear. Proceeding with task start.\n"
742
- : "";
743
740
  const found = findTaskByRef(await st.loadAllPhases(), ref);
744
741
  if (!found)
745
742
  return text(`Task not found: ${ref}`);
743
+ // Auto-archive the handoff on THIS task's phase (reason: task-started) — the
744
+ // agent is now actively working, so the captured context is consumed. Other
745
+ // phases' handoffs are left untouched (informational, non-blocking).
746
+ const allHandoffs = await st.listHandoffs();
747
+ let handoffNotice = "";
748
+ if ((found.phase.handoff ?? "") !== "") {
749
+ await st.clearPhaseHandoff(found.phase.id, "task-started").catch(() => { });
750
+ handoffNotice = "📦 Archived handoff for this phase (task started) — recover via planner-handoff-list + .planner/handoff-archive/.\n";
751
+ }
752
+ const _otherHandoffs = allHandoffs.filter((h) => h.phaseId !== found.phase.id);
753
+ if (_otherHandoffs.length > 0) {
754
+ handoffNotice += `ℹ️ ${_otherHandoffs.length} other phase handoff(s) pending — review with planner-handoff-list if relevant.\n`;
755
+ }
746
756
  const timestamp = nowISO();
747
757
  let updatedTask;
748
758
  await st.updatePhase(found.phase.id, (phase) => {
@@ -870,8 +880,8 @@ server.registerTool("planner-handoff-clear", {
870
880
  const r = await resolvePhaseForHandoff(st, phaseRef);
871
881
  if (!r.ok)
872
882
  return text(`❌ ${r.error}`);
873
- await st.clearPhaseHandoff(r.phase.id);
874
- return text(`✅ Cleared handoff on ${r.compositeRef}. (handoffUpdatedAt preserved as audit)`, { phaseRef: r.compositeRef, phaseId: r.phase.id });
883
+ await st.clearPhaseHandoff(r.phase.id, "manual");
884
+ return text(`✅ Cleared handoff on ${r.compositeRef} (handoffUpdatedAt preserved as audit; archived to .planner/handoff-archive/).`, { phaseRef: r.compositeRef, phaseId: r.phase.id });
875
885
  });
876
886
  server.registerTool("planner-web", {
877
887
  description: "Manage the planner web dashboard (start/status/stop) from MCP stdio. Runs an in-process HTTP+WS server bound to LAN; it lives as long as this MCP process. plan-root comes from AGENT_PLAN_ROOT or cwd()/.planner.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-plan/mcp",
3
- "version": "0.2.19-next.2",
3
+ "version": "0.2.19-next.4",
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.2",
45
- "@agent-plan/server": "0.2.19-next.2"
44
+ "@agent-plan/core": "0.2.19-next.4",
45
+ "@agent-plan/server": "0.2.19-next.4"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "tsc -p tsconfig.json",