@agent-plan/mcp 0.2.22-next.2 → 0.2.22-next.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAmjDA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAItD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAyjDA,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, findTaskByRef, buildRecap, addChecklistItem, removeChecklistItem, toggleChecklistItem, buildPhaseContextBlock, checkExplicitTaskStart, recommendNextTask, buildResumeRequiredProposal, packageVersionFromModule, resolvedPackageVersion, markFeatureRead, markPhaseRead, markTaskRead, parentReadAdvisory, requirementReadAdvisory, markRequirementRead, invalidateReads } from "@agent-plan/core";
7
+ import { PlanStore, ExportService, withFeatureLock, needsMotivation, findPhaseByRef, findTaskByRef, buildRecap, addChecklistItem, removeChecklistItem, toggleChecklistItem, buildPhaseContextBlock, checkExplicitTaskStart, recommendNextTask, buildResumeRequiredProposal, packageVersionFromModule, resolvedPackageVersion, markFeatureRead, markPhaseRead, markTaskRead, contextReadEligibility, hasReadRequirements, markRequirementRead, invalidateReads } from "@agent-plan/core";
8
8
  import { serve } from "@agent-plan/server";
9
9
  import { createChecklistItemId, createFeatureId, createPhaseId, createTaskId, clampSlug, normalizeSlug, formatPhaseRef, formatFeatureRef, validateResolvedTarget } from "@agent-plan/core/naming";
10
10
  const STATUS_VALUES = ["planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"];
@@ -450,7 +450,8 @@ server.registerTool("planner-feature-show", {
450
450
  if (!resolvedFeature.ok)
451
451
  return text(resolvedFeature.error);
452
452
  const feature = resolvedFeature.feature;
453
- markFeatureRead(feature.id);
453
+ if (full)
454
+ markFeatureRead(feature.id);
454
455
  const phases = (await st.loadAllPhases()).filter((phase) => phase.featureId === feature.id);
455
456
  const summary = `${feature.name} — ${formatFeatureRef(feature.number)}${feature.shortId ? ` · ${feature.shortId}` : ""} (${feature.status}; ${phases.length} phases)`;
456
457
  return text(full ? `${summary}\n\n${feature.description || ""}` : summary);
@@ -658,7 +659,8 @@ server.registerTool("planner-phase-show", {
658
659
  const phase = findPhaseByRef(await st.loadAllPhases(), features, ref);
659
660
  if (!phase)
660
661
  return text(`Phase not found: ${ref}`);
661
- markPhaseRead(phase.id, phase.featureId);
662
+ if (full)
663
+ markPhaseRead(phase.id, phase.featureId);
662
664
  const linkedRequirements = await st.linkedRequirementsForPhase(phase.id);
663
665
  const reqCount = linkedRequirements.length;
664
666
  const summary = `${phase.title} — ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, features))}${phase.shortId ? ` · ${phase.shortId}` : ""} (${phase.status}; ${phase.tasks.length} tasks${reqCount ? `; ${reqCount} linked requirement${reqCount === 1 ? "" : "s"}` : ""})`;
@@ -849,7 +851,8 @@ server.registerTool("planner-task-show", {
849
851
  const found = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
850
852
  if (!found)
851
853
  return text(`Task not found: ${ref}`);
852
- markTaskRead(found.task.id, found.phase.id, found.phase.featureId);
854
+ if (full)
855
+ markTaskRead(found.task.id, found.phase.id, found.phase.featureId);
853
856
  const features = (await st.loadFeatures()).features;
854
857
  const summary = `${found.task.title} — ${taskCompositeRef(found.task, found.phase, features)}${found.task.shortId ? ` · ${found.task.shortId}` : ""} (${found.task.status}; phase ${formatPhaseRef(found.phase.number, featureNumberOfPhase(found.phase, features))})`;
855
858
  if (!full)
@@ -929,8 +932,8 @@ server.registerTool("planner-task-update", {
929
932
  const found = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, ref);
930
933
  if (!found)
931
934
  return text(`Task not found: ${ref}`);
932
- if (found.task.pauseSnapshot && status === "in-progress" && status !== found.task.status) {
933
- return text("Resume checkpoint lifecycle transitions require planner-task-start so the checkpoint and return stack remain consistent.");
935
+ if (status === "in-progress" && status !== found.task.status) {
936
+ return text("Task start/resume transitions require planner-task-start so lifecycle state, checkpoints, and return stacks remain consistent.");
934
937
  }
935
938
  // Validate motivation requirement for status transitions.
936
939
  if (status !== undefined && needsMotivation(found.task.status, status)) {
@@ -1222,6 +1225,16 @@ server.registerTool("planner-task-switch", {
1222
1225
  if (source.task.status !== "in-progress" && !source.task.pauseSnapshot) {
1223
1226
  return text(`Task switch denied: source is ${source.task.status}; only in-progress or checkpointed work can be switched.`);
1224
1227
  }
1228
+ const contextEligibility = contextReadEligibility(target.task.id, target.phase.id, target.phase.featureId);
1229
+ if (!contextEligibility.eligible)
1230
+ return text(`Task switch denied: context reads required. ${contextEligibility.reason}`, contextEligibility);
1231
+ const linkedRequirementIds = [
1232
+ ...(await st.linkedRequirementsForPhase(target.phase.id)).map((requirement) => requirement.id),
1233
+ ...(target.phase.featureId ? (await st.linkedRequirementsForFeature(target.phase.featureId)).map((requirement) => requirement.id) : []),
1234
+ ];
1235
+ if (!hasReadRequirements(linkedRequirementIds)) {
1236
+ return text("Task switch denied: read the requirements linked to the target phase and feature before switching.", { requirementIds: linkedRequirementIds });
1237
+ }
1225
1238
  const eligibility = checkExplicitTaskStart(features, phases, target.task.id, project.workDeviations);
1226
1239
  if (!eligibility.eligible)
1227
1240
  return text(`Task switch denied: ${eligibility.reason}`);
@@ -1310,22 +1323,16 @@ server.registerTool("planner-task-switch", {
1310
1323
  await st.syncTaskStatusRollup(source.phase.id);
1311
1324
  if (target.phase.id !== source.phase.id)
1312
1325
  await st.syncTaskStatusRollup(target.phase.id);
1313
- const targetReadAdvisory = parentReadAdvisory(target.phase.featureId, target.phase.id);
1314
- const targetRequirementAdvisory = requirementReadAdvisory([
1315
- ...(await st.linkedRequirementsForPhase(target.phase.id)).map((r) => r.id),
1316
- ...(target.phase.featureId ? (await st.linkedRequirementsForFeature(target.phase.featureId)).map((r) => r.id) : []),
1317
- ]);
1326
+ invalidateReads();
1318
1327
  return writeAndSummarize(st, [
1319
1328
  `🔀 Task switched: ${taskCompositeRef(source.task, source.phase, features)} → ${taskCompositeRef(target.task, target.phase, features)}`,
1320
1329
  `Resume checkpoint: ${snapshot.whatWasBeingDone}`,
1321
1330
  `Return target: ${taskCompositeRef(source.task, source.phase, features)} from ${snapshot.resumeLocation}`,
1322
1331
  `Normal priority was deliberately overridden; completing the temporary task will emit RESUME REQUIRED.`,
1323
- ...(targetReadAdvisory ? [targetReadAdvisory.trimStart()] : []),
1324
- ...(targetRequirementAdvisory ? [targetRequirementAdvisory.trimStart()] : []),
1325
- ].join("\n"), { deviation: record, snapshot, resumeTaskId: source.task.id, temporaryTaskId: target.task.id, ...(targetReadAdvisory || targetRequirementAdvisory ? { readRequired: true } : {}) });
1332
+ ].join("\n"), { deviation: record, snapshot, resumeTaskId: source.task.id, temporaryTaskId: target.task.id });
1326
1333
  });
1327
1334
  server.registerTool("planner-task-start", {
1328
- description: "Set a task to in-progress or resume checkpointed work. BEFORE calling, use planner-task-show with full=true and planner-phase-show with full=true for the parent phase, including linked requirements. A different active task must be suspended through planner-task-switch first. Otherwise follow active/pending-resume/feature → phase → task priority guidance.",
1335
+ description: "Set a task to in-progress or resume checkpointed work. BEFORE calling, use planner-task-show(full=true), then planner-phase-show(full=true), then planner-feature-show(full=true) for the same lineage, in that exact order. Read linked requirements with planner-requirement-list when present. A different active task must first be suspended through planner-task-switch.",
1329
1336
  inputSchema: { task: z.string().min(1) },
1330
1337
  }, async ({ task: ref }) => {
1331
1338
  const st = await requireStore();
@@ -1337,6 +1344,16 @@ server.registerTool("planner-task-start", {
1337
1344
  return text(`Task ${found.task.id} is already in-progress.`);
1338
1345
  if (found.task.status === "done")
1339
1346
  return text(`Task ${found.task.id} is done. Use planner-task-update to reopen.`);
1347
+ const contextEligibility = contextReadEligibility(found.task.id, found.phase.id, found.phase.featureId);
1348
+ if (!contextEligibility.eligible)
1349
+ return text(`Task start denied: context reads required. ${contextEligibility.reason}`, contextEligibility);
1350
+ const linkedRequirementIds = [
1351
+ ...(await st.linkedRequirementsForPhase(found.phase.id)).map((requirement) => requirement.id),
1352
+ ...(found.phase.featureId ? (await st.linkedRequirementsForFeature(found.phase.featureId)).map((requirement) => requirement.id) : []),
1353
+ ];
1354
+ if (!hasReadRequirements(linkedRequirementIds)) {
1355
+ return text("Task start denied: read the requirements linked to the task's phase and feature first.", { requirementIds: linkedRequirementIds });
1356
+ }
1340
1357
  const project = await st.loadProject();
1341
1358
  const phases = await st.loadAllPhases();
1342
1359
  const eligibility = checkExplicitTaskStart(features, phases, found.task.id, project.workDeviations);
@@ -1429,14 +1446,8 @@ server.registerTool("planner-task-start", {
1429
1446
  if (resumedDeviation)
1430
1447
  await st.setWorkDeviationState(resumedDeviation.id, "resumed", timestamp);
1431
1448
  const t = updatedTask ?? found.task;
1432
- const readAdvisory = parentReadAdvisory(parentFeature?.id, found.phase.id);
1433
- const requirementAdvisory = requirementReadAdvisory([
1434
- ...(phaseWithRequirements.linkedRequirements ?? []).map((r) => r.id),
1435
- ...featureRequirements.map((r) => r.id),
1436
- ]);
1437
- return writeAndSummarize(st, `✅ Task started: ${taskCompositeRef(t, found.phase, features)} — ${t.title} (in-progress)${t.shortId ? ` · ${t.shortId}` : ""}${phaseContext}${advisory}${readAdvisory}${requirementAdvisory}`, {
1449
+ return writeAndSummarize(st, `✅ Task started: ${taskCompositeRef(t, found.phase, features)} — ${t.title} (in-progress)${t.shortId ? ` · ${t.shortId}` : ""}${phaseContext}${advisory}`, {
1438
1450
  ...(resumeProposal ? { resumeRequired: resumeProposal.structured } : {}),
1439
- ...(readAdvisory || requirementAdvisory ? { readRequired: true } : {}),
1440
1451
  });
1441
1452
  });
1442
1453
  server.registerTool("planner-task-complete", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-plan/mcp",
3
- "version": "0.2.22-next.2",
3
+ "version": "0.2.22-next.3",
4
4
  "private": false,
5
5
  "description": "MCP server for Agent Plan, exposing planner-* tools over stdio.",
6
6
  "license": "MIT",
@@ -43,8 +43,8 @@
43
43
  "dependencies": {
44
44
  "@modelcontextprotocol/sdk": "^1.17.0",
45
45
  "zod": "^3.24.0",
46
- "@agent-plan/core": "0.2.22-next.2",
47
- "@agent-plan/server": "0.2.22-next.2"
46
+ "@agent-plan/core": "0.2.22-next.3",
47
+ "@agent-plan/server": "0.2.22-next.3"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "tsc -p tsconfig.json",