@agent-plan/mcp 0.2.22-next.2 → 0.2.22-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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -32
- package/package.json +3 -3
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAwjDA,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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
933
|
-
return text("
|
|
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)) {
|
|
@@ -1109,12 +1112,12 @@ server.registerTool("planner-task-delete", {
|
|
|
1109
1112
|
return writeAndSummarize(st, `Task deleted: ${taskCompositeRef(found.task, found.phase, tdFeatures)}${found.task.shortId ? ` · ${found.task.shortId}` : ""}`, { deleted: found.task.id });
|
|
1110
1113
|
});
|
|
1111
1114
|
server.registerTool("planner-task-recommend", {
|
|
1112
|
-
description: "Return the harness-agnostic recommended task: continue one active task, otherwise choose ready work by feature → phase → task priority. Reports dependency, availability, and approved deviation/resume context without starting or blocking work.",
|
|
1115
|
+
description: "Return the harness-agnostic recommended task: continue one active task or the current phase, otherwise choose ready work by feature → phase → task priority. Reports dependency, availability, and approved deviation/resume context without starting or blocking work.",
|
|
1113
1116
|
inputSchema: {},
|
|
1114
1117
|
}, async () => {
|
|
1115
1118
|
const st = await requireStore();
|
|
1116
|
-
const [features, phases, project] = await Promise.all([st.loadFeatures(), st.loadAllPhases(), st.loadProject()]);
|
|
1117
|
-
const result = recommendNextTask(features.features, phases, project.workDeviations);
|
|
1119
|
+
const [features, phases, project, resume] = await Promise.all([st.loadFeatures(), st.loadAllPhases(), st.loadProject(), st.loadResume()]);
|
|
1120
|
+
const result = recommendNextTask(features.features, phases, project.workDeviations, resume?.currentPhaseId);
|
|
1118
1121
|
if (!result.candidate)
|
|
1119
1122
|
return text(`No task recommendation: ${result.reason}`, { kind: result.kind, reason: result.reason, activeTaskIds: result.activeCandidates?.map((candidate) => candidate.task.id) ?? [] });
|
|
1120
1123
|
const { candidate } = result;
|
|
@@ -1128,11 +1131,11 @@ server.registerTool("planner-task-deviation", {
|
|
|
1128
1131
|
inputSchema: { temporary_task: z.string().min(1), resume_task: z.string().min(1).optional(), reason: z.string().min(1) },
|
|
1129
1132
|
}, async ({ temporary_task, resume_task, reason }) => {
|
|
1130
1133
|
const st = await requireStore();
|
|
1131
|
-
const [features, phases, project] = await Promise.all([st.loadFeatures(), st.loadAllPhases(), st.loadProject()]);
|
|
1134
|
+
const [features, phases, project, focus] = await Promise.all([st.loadFeatures(), st.loadAllPhases(), st.loadProject(), st.loadResume()]);
|
|
1132
1135
|
const temporary = findTaskByRef(phases, features.features, temporary_task);
|
|
1133
1136
|
if (!temporary)
|
|
1134
1137
|
return text(`Task not found: ${temporary_task}`);
|
|
1135
|
-
const selected = recommendNextTask(features.features, phases, project.workDeviations);
|
|
1138
|
+
const selected = recommendNextTask(features.features, phases, project.workDeviations, focus?.currentPhaseId);
|
|
1136
1139
|
const resume = resume_task ? findTaskByRef(phases, features.features, resume_task) : selected.candidate;
|
|
1137
1140
|
if (!resume)
|
|
1138
1141
|
return text(`No resume task is available. Provide resume_task explicitly. ${selected.reason}`);
|
|
@@ -1209,7 +1212,7 @@ server.registerTool("planner-task-switch", {
|
|
|
1209
1212
|
},
|
|
1210
1213
|
}, async ({ from_task, to_task, reason, what_was_being_done, resume_location, how_to_resume, switched_by }) => {
|
|
1211
1214
|
const st = await requireStore();
|
|
1212
|
-
const [featuresDoc, phases, project] = await Promise.all([st.loadFeatures(), st.loadAllPhases(), st.loadProject()]);
|
|
1215
|
+
const [featuresDoc, phases, project, focus] = await Promise.all([st.loadFeatures(), st.loadAllPhases(), st.loadProject(), st.loadResume()]);
|
|
1213
1216
|
const features = featuresDoc.features;
|
|
1214
1217
|
const source = findTaskByRef(phases, features, from_task);
|
|
1215
1218
|
const target = findTaskByRef(phases, features, to_task);
|
|
@@ -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}`);
|
|
@@ -1231,7 +1244,7 @@ server.registerTool("planner-task-switch", {
|
|
|
1231
1244
|
resumeLocation: resume_location, howToResume: how_to_resume,
|
|
1232
1245
|
relatedTaskId: target.task.id, pausedAt: timestamp, pausedBy: switched_by?.trim() ?? "",
|
|
1233
1246
|
};
|
|
1234
|
-
const selection = recommendNextTask(features, phases, project.workDeviations);
|
|
1247
|
+
const selection = recommendNextTask(features, phases, project.workDeviations, focus?.currentPhaseId);
|
|
1235
1248
|
const record = {
|
|
1236
1249
|
id: crypto.randomUUID(), recommendedTaskId: selection.candidate?.task.id ?? source.task.id,
|
|
1237
1250
|
temporaryTaskId: target.task.id, resumeTaskId: source.task.id, reason, snapshot,
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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,12 +1344,21 @@ 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.`);
|
|
1340
|
-
const
|
|
1341
|
-
|
|
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
|
+
}
|
|
1357
|
+
const [project, phases, focus] = await Promise.all([st.loadProject(), st.loadAllPhases(), st.loadResume()]);
|
|
1342
1358
|
const eligibility = checkExplicitTaskStart(features, phases, found.task.id, project.workDeviations);
|
|
1343
1359
|
if (!eligibility.eligible)
|
|
1344
1360
|
return text(`Task start denied: ${eligibility.reason}`, { reason: eligibility.reason });
|
|
1345
|
-
const selection = recommendNextTask(features, phases, project.workDeviations);
|
|
1361
|
+
const selection = recommendNextTask(features, phases, project.workDeviations, focus?.currentPhaseId);
|
|
1346
1362
|
if (selection.kind === "conflict") {
|
|
1347
1363
|
return text(`${selection.reason} Pause/reconcile active tasks before starting another task.`, {
|
|
1348
1364
|
activeTaskIds: selection.activeCandidates?.map((candidate) => candidate.task.id) ?? [],
|
|
@@ -1429,14 +1445,8 @@ server.registerTool("planner-task-start", {
|
|
|
1429
1445
|
if (resumedDeviation)
|
|
1430
1446
|
await st.setWorkDeviationState(resumedDeviation.id, "resumed", timestamp);
|
|
1431
1447
|
const t = updatedTask ?? found.task;
|
|
1432
|
-
|
|
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}`, {
|
|
1448
|
+
return writeAndSummarize(st, `✅ Task started: ${taskCompositeRef(t, found.phase, features)} — ${t.title} (in-progress)${t.shortId ? ` · ${t.shortId}` : ""}${phaseContext}${advisory}`, {
|
|
1438
1449
|
...(resumeProposal ? { resumeRequired: resumeProposal.structured } : {}),
|
|
1439
|
-
...(readAdvisory || requirementAdvisory ? { readRequired: true } : {}),
|
|
1440
1450
|
});
|
|
1441
1451
|
});
|
|
1442
1452
|
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.
|
|
3
|
+
"version": "0.2.22-next.4",
|
|
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.
|
|
47
|
-
"@agent-plan/server": "0.2.22-next.
|
|
46
|
+
"@agent-plan/core": "0.2.22-next.4",
|
|
47
|
+
"@agent-plan/server": "0.2.22-next.4"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsc -p tsconfig.json",
|