@agent-plan/mcp 0.2.19-next.0 → 0.2.19-next.1
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 +40 -51
- 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":";AAm5BA,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 } from "@agent-plan/core";
|
|
7
|
+
import { PlanStore, ExportService, withFeatureLock, needsMotivation, findPhaseByRef, buildRecap } from "@agent-plan/core";
|
|
8
8
|
import { serve } from "@agent-plan/server";
|
|
9
9
|
import { createChecklistItemId, createFeatureId, createPhaseId, createShortId, createTaskId, clampSlug, normalizeSlug, formatPhaseRef } from "@agent-plan/core/naming";
|
|
10
10
|
const STATUS_VALUES = ["planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"];
|
|
@@ -55,49 +55,6 @@ async function ensureWebStarted() {
|
|
|
55
55
|
return { localUrl: "" };
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
/** Build a consolidated planner recap (project state + active work + pending
|
|
59
|
-
* handoff + web URL). Harness-agnostic equivalent of Pi's buildStartupResumeSummary. */
|
|
60
|
-
async function buildRecapText(st, web) {
|
|
61
|
-
const [plan, resume, handoffs] = await Promise.all([st.loadAll(), st.loadResume(), st.listHandoffs()]);
|
|
62
|
-
const allTasks = plan.phases.flatMap((p) => p.tasks.map((t) => ({ phase: p, task: t })));
|
|
63
|
-
const totalF = plan.features.features.length;
|
|
64
|
-
const doneF = plan.features.features.filter((f) => f.status === "done").length;
|
|
65
|
-
const activeF = plan.features.features.filter((f) => f.status === "in-progress").length;
|
|
66
|
-
const totalP = plan.phases.length;
|
|
67
|
-
const doneP = plan.phases.filter((p) => p.status === "done").length;
|
|
68
|
-
const activeP = plan.phases.filter((p) => p.status === "in-progress" || p.status === "discovery").length;
|
|
69
|
-
const totalT = allTasks.length;
|
|
70
|
-
const doneT = allTasks.filter(({ task }) => task.status === "done").length;
|
|
71
|
-
const activeT = allTasks.filter(({ task }) => task.status === "in-progress").length;
|
|
72
|
-
const inProgress = allTasks.filter(({ task }) => task.status === "in-progress");
|
|
73
|
-
const focus = inProgress[0];
|
|
74
|
-
const focusFeature = focus ? plan.features.features.find((f) => f.id === focus.phase.featureId) : undefined;
|
|
75
|
-
const lines = [];
|
|
76
|
-
lines.push("## Planner recap");
|
|
77
|
-
const name = plan.project.name || "(unnamed project)";
|
|
78
|
-
lines.push(`Project: ${name}${plan.project.goal ? " — " + plan.project.goal : ""}`);
|
|
79
|
-
lines.push(`Progress: Features ${doneF}/${totalF} done (${activeF} active) · Phases ${doneP}/${totalP} done (${activeP} active) · Tasks ${doneT}/${totalT} done (${activeT} active)`);
|
|
80
|
-
if (focus) {
|
|
81
|
-
lines.push(`Current focus: ${focusFeature?.name ?? "?"} / ${focus.phase.title} / ${focus.task.title} (in-progress)`);
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
lines.push("Current focus: no active task — review the plan and pick the next concrete task");
|
|
85
|
-
}
|
|
86
|
-
if (resume?.nextSteps?.length)
|
|
87
|
-
lines.push(`Next step: ${resume.nextSteps[0]}`);
|
|
88
|
-
if (handoffs.length > 0) {
|
|
89
|
-
lines.push("", `## Pending phase handoffs (${handoffs.length})`);
|
|
90
|
-
handoffs.forEach((h, i) => lines.push(`[${i + 1}] ${h.compositeRef} — ${h.updatedAt} — "${h.firstLine}"`));
|
|
91
|
-
lines.push("", "→ Read the relevant one with planner-handoff-show <ref> (validate against current state), then call planner-handoff-clear <ref> once consumed (delete-on-resume).");
|
|
92
|
-
}
|
|
93
|
-
else if (activeT === 0) {
|
|
94
|
-
lines.push("", "No phase handoff pending and no task in-progress. Use planner-task-add / planner-task-start to begin work.");
|
|
95
|
-
}
|
|
96
|
-
if (web.localUrl) {
|
|
97
|
-
lines.push("", "## Web UI", `🌐 ${web.localUrl}${web.lanUrl ? " (LAN: " + web.lanUrl + ")" : ""}`);
|
|
98
|
-
}
|
|
99
|
-
return lines.join("\n");
|
|
100
|
-
}
|
|
101
58
|
function findFeatureByRef(features, ref) {
|
|
102
59
|
const normalized = ref.trim().toLowerCase();
|
|
103
60
|
return features.find((feature) => feature.id.toLowerCase() === normalized)
|
|
@@ -106,8 +63,25 @@ function findFeatureByRef(features, ref) {
|
|
|
106
63
|
}
|
|
107
64
|
function findTaskByRef(phases, ref) {
|
|
108
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
|
+
}
|
|
109
82
|
for (const phase of phases) {
|
|
110
83
|
const task = phase.tasks.find((entry) => entry.id.toLowerCase() === normalized
|
|
84
|
+
|| (entry.shortId && entry.shortId.toLowerCase() === normalized)
|
|
111
85
|
|| entry.title.toLowerCase() === normalized
|
|
112
86
|
|| entry.title.toLowerCase().includes(normalized));
|
|
113
87
|
if (task)
|
|
@@ -115,6 +89,12 @@ function findTaskByRef(phases, ref) {
|
|
|
115
89
|
}
|
|
116
90
|
return undefined;
|
|
117
91
|
}
|
|
92
|
+
function featureNumberOfPhase(phase, features) {
|
|
93
|
+
return phase.featureId ? features.find((f) => f.id === phase.featureId)?.number : undefined;
|
|
94
|
+
}
|
|
95
|
+
function taskCompositeRef(task, phase, features) {
|
|
96
|
+
return `${formatPhaseRef(phase.number, featureNumberOfPhase(phase, features))}/T${String(task.number).padStart(3, "0")}`;
|
|
97
|
+
}
|
|
118
98
|
function applyTaskLifecycleDates(task, nextStatus, now) {
|
|
119
99
|
const previousStatus = task.status;
|
|
120
100
|
if (nextStatus === "in-progress" && !task.startedAt)
|
|
@@ -625,13 +605,14 @@ server.registerTool("planner-task-add", {
|
|
|
625
605
|
createdAt: timestamp,
|
|
626
606
|
updatedAt: timestamp,
|
|
627
607
|
};
|
|
628
|
-
|
|
608
|
+
await st.updatePhase(found.id, (entry) => {
|
|
629
609
|
entry.tasks.push(task);
|
|
630
610
|
entry.taskIds.push(task.id);
|
|
631
611
|
entry.updatedAt = timestamp;
|
|
632
612
|
return entry;
|
|
633
613
|
});
|
|
634
|
-
|
|
614
|
+
const features = (await st.loadFeatures()).features;
|
|
615
|
+
return writeAndSummarize(st, `✅ Task created: ${taskCompositeRef(task, found, features)} — ${task.title} (planned)${task.shortId ? ` · ${task.shortId}` : ""}`);
|
|
635
616
|
});
|
|
636
617
|
server.registerTool("planner-task-show", {
|
|
637
618
|
description: "Show a task by id or name.",
|
|
@@ -669,7 +650,9 @@ server.registerTool("planner-task-discuss", {
|
|
|
669
650
|
updatedTask = task;
|
|
670
651
|
return phase;
|
|
671
652
|
});
|
|
672
|
-
|
|
653
|
+
const features = (await st.loadFeatures()).features;
|
|
654
|
+
const t = updatedTask ?? found.task;
|
|
655
|
+
return writeAndSummarize(st, `✅ Task discussed/updated: ${taskCompositeRef(t, found.phase, features)} — ${t.title} (${t.status})${t.shortId ? ` · ${t.shortId}` : ""}`);
|
|
673
656
|
});
|
|
674
657
|
server.registerTool("planner-task-update", {
|
|
675
658
|
description: "Update task fields. A motivation is REQUIRED when changing to blocked, canceled, deferred, rejected, waiting, or back to planned from another status.",
|
|
@@ -724,7 +707,9 @@ server.registerTool("planner-task-update", {
|
|
|
724
707
|
return phase;
|
|
725
708
|
});
|
|
726
709
|
await st.syncTaskStatusRollup(found.phase.id);
|
|
727
|
-
|
|
710
|
+
const features = (await st.loadFeatures()).features;
|
|
711
|
+
const t = updatedTask ?? found.task;
|
|
712
|
+
return writeAndSummarize(st, `✅ Task updated: ${taskCompositeRef(t, found.phase, features)} — ${t.title} (${t.status})${t.shortId ? ` · ${t.shortId}` : ""}`);
|
|
728
713
|
});
|
|
729
714
|
server.registerTool("planner-task-delete", {
|
|
730
715
|
description: "Delete a task.",
|
|
@@ -782,7 +767,9 @@ server.registerTool("planner-task-start", {
|
|
|
782
767
|
return phase;
|
|
783
768
|
});
|
|
784
769
|
await st.syncTaskStatusRollup(found.phase.id);
|
|
785
|
-
|
|
770
|
+
const features = (await st.loadFeatures()).features;
|
|
771
|
+
const t = updatedTask ?? found.task;
|
|
772
|
+
return writeAndSummarize(st, `${handoffNotice}✅ Task started: ${taskCompositeRef(t, found.phase, features)} — ${t.title} (in-progress)${t.shortId ? ` · ${t.shortId}` : ""}`);
|
|
786
773
|
});
|
|
787
774
|
server.registerTool("planner-task-complete", {
|
|
788
775
|
description: "Set a task to done. Fails if checklist is incomplete unless force=true.",
|
|
@@ -827,7 +814,9 @@ server.registerTool("planner-task-complete", {
|
|
|
827
814
|
return phase;
|
|
828
815
|
});
|
|
829
816
|
const clearedRef = await st.syncTaskStatusRollup(found.phase.id);
|
|
830
|
-
|
|
817
|
+
const features = (await st.loadFeatures()).features;
|
|
818
|
+
const t = updatedTask ?? found.task;
|
|
819
|
+
return writeAndSummarize(st, `✅ Task completed: ${taskCompositeRef(t, found.phase, features)} — ${t.title} (done)${t.shortId ? ` · ${t.shortId}` : ""}${clearedRef ? ` — phase handoff auto-cleared (${clearedRef})` : ""}`);
|
|
831
820
|
});
|
|
832
821
|
server.registerTool("planner-handoff-list", {
|
|
833
822
|
description: "List all phases with a non-empty entity-scoped handoff (phase.handoff field). Returns composite refs (P00x / P00x(F00x)) with the first line and last-updated time.",
|
|
@@ -931,7 +920,7 @@ server.registerTool("planner-load", {
|
|
|
931
920
|
if (!(await st.exists()))
|
|
932
921
|
return text("No .planner/ found at " + planRoot() + ". Run planner-init first.");
|
|
933
922
|
const web = await ensureWebStarted();
|
|
934
|
-
const recap = await
|
|
923
|
+
const recap = await buildRecap(st, web, { harness: "mcp" });
|
|
935
924
|
return text(recap);
|
|
936
925
|
});
|
|
937
926
|
server.registerTool("planner-disable", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-plan/mcp",
|
|
3
|
-
"version": "0.2.19-next.
|
|
3
|
+
"version": "0.2.19-next.1",
|
|
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.
|
|
45
|
-
"@agent-plan/server": "0.2.19-next.
|
|
44
|
+
"@agent-plan/core": "0.2.19-next.1",
|
|
45
|
+
"@agent-plan/server": "0.2.19-next.1"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsc -p tsconfig.json",
|