@agent-plan/pi-adapter 0.2.13 ā 0.2.15
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 +121 -25
- package/package.json +4 -4
- package/web-ui-dist/assets/index-Tma57h5W.js +191 -0
- package/web-ui-dist/assets/index-U-dx9Eua.css +1 -0
- package/web-ui-dist/index.html +2 -2
- package/web-ui-dist/assets/index-BIunmzuQ.css +0 -1
- package/web-ui-dist/assets/index-C5I8PEJ9.js +0 -191
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iCAAiC,CAAC;AA+yBtF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iCAAiC,CAAC;AA+yBtF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAgyF9D"}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { Type } from "typebox";
|
|
12
12
|
import { ExportService, PlanStore, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, withFeatureLock, needsMotivation } from "@agent-plan/core";
|
|
13
|
-
import { createChecklistItemId, createFeatureId, createPhaseId, createTaskId, normalizeSlug } from "@agent-plan/core/naming";
|
|
13
|
+
import { createChecklistItemId, createFeatureId, createPhaseId, createTaskId, clampSlug, normalizeSlug } from "@agent-plan/core/naming";
|
|
14
14
|
import { join, dirname } from "node:path";
|
|
15
15
|
import { homedir } from "node:os";
|
|
16
16
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
@@ -920,6 +920,12 @@ export default function planPiExtension(pi) {
|
|
|
920
920
|
// git pull, build, test, ls, etc. always work.
|
|
921
921
|
if (event.toolName !== "edit" && event.toolName !== "write")
|
|
922
922
|
return;
|
|
923
|
+
// Planner-internal writes (.planner/HANDOFF.md, resume.json, generated plan
|
|
924
|
+
// files, etc.) are planner operations, NOT code edits. They never require a
|
|
925
|
+
// task in-progress ā skip the guard so the handoff can always be written.
|
|
926
|
+
const targetPath = String(event.input?.path ?? event.input?.filePath ?? "");
|
|
927
|
+
if (targetPath && (targetPath.includes("/.planner/") || targetPath.includes("\\.planner\\") || targetPath.startsWith(".planner/")))
|
|
928
|
+
return;
|
|
923
929
|
const st = loadStore(ctx);
|
|
924
930
|
if (!(await st.exists().catch(() => false)))
|
|
925
931
|
return;
|
|
@@ -1816,8 +1822,7 @@ export default function planPiExtension(pi) {
|
|
|
1816
1822
|
ctx.ui.notify("Aborted", "warning");
|
|
1817
1823
|
return;
|
|
1818
1824
|
}
|
|
1819
|
-
const shortName = title.
|
|
1820
|
-
.replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 30);
|
|
1825
|
+
const shortName = clampSlug(title, 30, `task-${Date.now().toString(36)}`);
|
|
1821
1826
|
const taskNum = phase.tasks.length + 1;
|
|
1822
1827
|
const taskId = createTaskId();
|
|
1823
1828
|
const now = nowISO();
|
|
@@ -1990,8 +1995,7 @@ export default function planPiExtension(pi) {
|
|
|
1990
1995
|
return;
|
|
1991
1996
|
}
|
|
1992
1997
|
if (normalizedStatus === "in-progress" && existsSync(join(st.root, "HANDOFF.md"))) {
|
|
1993
|
-
ctx.ui.notify("
|
|
1994
|
-
return;
|
|
1998
|
+
ctx.ui.notify("ā¹ļø A handoff exists at .planner/HANDOFF.md ā read it for context, then delete it with /planner handoff clear (or plan_delete_handoff) when no longer needed. Proceeding with the status change.", "info");
|
|
1995
1999
|
}
|
|
1996
2000
|
applyTaskLifecycleDates(task, normalizedStatus, now);
|
|
1997
2001
|
}
|
|
@@ -2032,10 +2036,12 @@ export default function planPiExtension(pi) {
|
|
|
2032
2036
|
ctx.ui.notify(`Task "${task.title}" is already in-progress.`, "info");
|
|
2033
2037
|
return;
|
|
2034
2038
|
}
|
|
2035
|
-
// Hygiene
|
|
2039
|
+
// Hygiene notice (non-blocking): surface an existing handoff but never
|
|
2040
|
+
// block task_start ā the handoff is a captured-context artifact, not a
|
|
2041
|
+
// lock (a small modification after writing a handoff should start without
|
|
2042
|
+
// forcing deletion, which would lose context).
|
|
2036
2043
|
if (existsSync(join(st.root, "HANDOFF.md"))) {
|
|
2037
|
-
ctx.ui.notify("
|
|
2038
|
-
return;
|
|
2044
|
+
ctx.ui.notify("ā¹ļø A handoff exists at .planner/HANDOFF.md ā read it for context, then delete it with /planner handoff clear (or plan_delete_handoff) when no longer needed. Proceeding with task start.", "info");
|
|
2039
2045
|
}
|
|
2040
2046
|
const now = nowISO();
|
|
2041
2047
|
// Record status change in the incremental statusLog.
|
|
@@ -2277,6 +2283,10 @@ export default function planPiExtension(pi) {
|
|
|
2277
2283
|
// at the END of that summary (not in a notify here) so it stays visible.
|
|
2278
2284
|
startupResumePromptPending = true;
|
|
2279
2285
|
startupResumeSummaryPending = true;
|
|
2286
|
+
// Force the recap turn to rebuild the context (slow path) so the resume
|
|
2287
|
+
// protocol is injected fresh. The protocol is appended per-turn and is
|
|
2288
|
+
// never baked into the cache, so later turns won't see it.
|
|
2289
|
+
contextBlockDirty = true;
|
|
2280
2290
|
if (startupResumeSummaryTimer)
|
|
2281
2291
|
clearTimeout(startupResumeSummaryTimer);
|
|
2282
2292
|
startupResumeSummaryTimer = setTimeout(() => {
|
|
@@ -3173,8 +3183,7 @@ export default function planPiExtension(pi) {
|
|
|
3173
3183
|
catch {
|
|
3174
3184
|
return { content: [{ type: "text", text: `Phase not found: ${params.phaseId}` }], details: {} };
|
|
3175
3185
|
}
|
|
3176
|
-
const
|
|
3177
|
-
const shortName = rawShort.trim() || `task-${Date.now().toString(36)}`; // never empty (fixes regex invalid_string)
|
|
3186
|
+
const shortName = clampSlug(params.shortName ?? params.title, 30, `task-${Date.now().toString(36)}`); // clamp+strip trailing dash; never empty
|
|
3178
3187
|
const taskId = createTaskId();
|
|
3179
3188
|
const now = nowISO();
|
|
3180
3189
|
const initialStatus = params.status ?? "planned";
|
|
@@ -3404,6 +3413,90 @@ export default function planPiExtension(pi) {
|
|
|
3404
3413
|
return { content: [{ type: "text", text: `ā
Task completed: ${completedTask.id} ā ${completedTask.title} (done)` }], details: completedTask };
|
|
3405
3414
|
},
|
|
3406
3415
|
});
|
|
3416
|
+
// āā Web lifecycle tools (agent-callable; parity with @agent-plan/mcp planner-web) āā
|
|
3417
|
+
// These let the agent manage the web dashboard directly, without relying
|
|
3418
|
+
// on the /planner slash command (which is not intercepted when the planner
|
|
3419
|
+
// is disabled by default). Planner operations are NOT code edits.
|
|
3420
|
+
pi.registerTool({
|
|
3421
|
+
name: "planner-web",
|
|
3422
|
+
label: "Planner Web",
|
|
3423
|
+
description: "Manage the planner web dashboard (start/status/stop). The dashboard is LAN-bound (0.0.0.0) with a dynamic OS-assigned port by default. Planner operations are NOT code edits and need no active task. The web does NOT auto-start; call action=start explicitly.",
|
|
3424
|
+
parameters: Type.Object({
|
|
3425
|
+
action: Type.Union([Type.Literal("start"), Type.Literal("stop"), Type.Literal("status")], { description: "start | stop | status. Default: status" }),
|
|
3426
|
+
port: Type.Optional(Type.Number({ description: "Optional requested port for start. Omit/0 ā OS assigns a free port." })),
|
|
3427
|
+
visibility: Type.Optional(Type.Union([Type.Literal("local"), Type.Literal("lan")], { description: "Bind scope for start. Default: lan (0.0.0.0, reachable from other devices)." })),
|
|
3428
|
+
}),
|
|
3429
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3430
|
+
const action = params.action ?? "status";
|
|
3431
|
+
if (action === "start") {
|
|
3432
|
+
const st = await requirePlan(ctx);
|
|
3433
|
+
if (!st)
|
|
3434
|
+
return { content: [{ type: "text", text: "No .planner/ found. Run plan_init first." }], details: { running: false } };
|
|
3435
|
+
const visibility = params.visibility ?? "lan";
|
|
3436
|
+
if (server) {
|
|
3437
|
+
const srv = server;
|
|
3438
|
+
return { content: [{ type: "text", text: `Web UI already running. Local: ${srv.localUrl}${srv.lanUrl ? ` ā LAN: ${srv.lanUrl}` : ""} (mode: ${srv.mode}, port: ${lastKnownWebPort ?? "?"})` }], details: { running: true, localUrl: srv.localUrl, lanUrl: srv.lanUrl, port: lastKnownWebPort, mode: srv.mode } };
|
|
3439
|
+
}
|
|
3440
|
+
await startServer(ctx, params.port && params.port > 0 ? params.port : undefined, visibility);
|
|
3441
|
+
const srv = server;
|
|
3442
|
+
if (!srv)
|
|
3443
|
+
return { content: [{ type: "text", text: "Failed to start web server." }], details: { running: false } };
|
|
3444
|
+
return { content: [{ type: "text", text: `Web UI started. Local: ${srv.localUrl}${srv.lanUrl ? ` ā LAN: ${srv.lanUrl}` : ""} (mode: ${srv.mode}, port: ${lastKnownWebPort ?? "?"})` }], details: { running: true, localUrl: srv.localUrl, lanUrl: srv.lanUrl, port: lastKnownWebPort, mode: srv.mode } };
|
|
3445
|
+
}
|
|
3446
|
+
if (action === "stop") {
|
|
3447
|
+
if (!server)
|
|
3448
|
+
return { content: [{ type: "text", text: "Web UI not running." }], details: { running: false } };
|
|
3449
|
+
const stoppedUrl = server.localUrl;
|
|
3450
|
+
await stopServer();
|
|
3451
|
+
return { content: [{ type: "text", text: `Web UI stopped (was ${stoppedUrl}).` }], details: { running: false } };
|
|
3452
|
+
}
|
|
3453
|
+
// status
|
|
3454
|
+
const srv = server;
|
|
3455
|
+
if (!srv)
|
|
3456
|
+
return { content: [{ type: "text", text: "Web UI not running. Use planner-web with action=start to start the dashboard." }], details: { running: false } };
|
|
3457
|
+
return { content: [{ type: "text", text: `Web UI running. Local: ${srv.localUrl}${srv.lanUrl ? ` ā LAN: ${srv.lanUrl}` : ""} (mode: ${srv.mode}, port: ${lastKnownWebPort ?? "?"})` }], details: { running: true, localUrl: srv.localUrl, lanUrl: srv.lanUrl, port: lastKnownWebPort, mode: srv.mode } };
|
|
3458
|
+
},
|
|
3459
|
+
});
|
|
3460
|
+
pi.registerTool({
|
|
3461
|
+
name: "planner-load",
|
|
3462
|
+
label: "Planner Load",
|
|
3463
|
+
description: "Enable the planner for this project and start the web dashboard (LAN), then return a resume recap (project status + handoff if present + Web UI address). This is the explicit way to enable the planner ā it does NOT auto-start. Planner operations are NOT code edits.",
|
|
3464
|
+
parameters: Type.Object({}),
|
|
3465
|
+
async execute(_id, _params, _signal, _onUpdate, ctx) {
|
|
3466
|
+
const st = await requirePlan(ctx);
|
|
3467
|
+
if (!st)
|
|
3468
|
+
return { content: [{ type: "text", text: "No .planner/ found. Run plan_init first." }], details: { enabled: false, running: false } };
|
|
3469
|
+
plannerSessionEnabled = true;
|
|
3470
|
+
if (!server) {
|
|
3471
|
+
await startServer(ctx, undefined, "lan").catch(() => { });
|
|
3472
|
+
}
|
|
3473
|
+
let recap = "";
|
|
3474
|
+
try {
|
|
3475
|
+
recap = await buildStartupResumeSummary(st);
|
|
3476
|
+
}
|
|
3477
|
+
catch (e) {
|
|
3478
|
+
recap = `(recap unavailable: ${e instanceof Error ? e.message : String(e)})`;
|
|
3479
|
+
}
|
|
3480
|
+
const srv = server;
|
|
3481
|
+
const webLine = srv ? `\nš Web UI: ${srv.localUrl}${lastKnownWebPort ? ` (port ${lastKnownWebPort})` : ""}${srv.lanUrl ? ` ā LAN: ${srv.lanUrl}` : ""}` : "\nš Web UI: not running";
|
|
3482
|
+
return { content: [{ type: "text", text: `${recap}${webLine}` }], details: { enabled: true, running: Boolean(srv), localUrl: srv?.localUrl, lanUrl: srv?.lanUrl, port: lastKnownWebPort } };
|
|
3483
|
+
},
|
|
3484
|
+
});
|
|
3485
|
+
pi.registerTool({
|
|
3486
|
+
name: "planner-stop",
|
|
3487
|
+
label: "Planner Stop",
|
|
3488
|
+
description: "Disable the planner for this project and stop the web dashboard. Alias: planner-disable. Planner operations are NOT code edits.",
|
|
3489
|
+
parameters: Type.Object({}),
|
|
3490
|
+
async execute(_id, _params, _signal, _onUpdate, _ctx) {
|
|
3491
|
+
plannerSessionEnabled = false;
|
|
3492
|
+
await stopServer().catch(() => { });
|
|
3493
|
+
try {
|
|
3494
|
+
capturedPi?.appendEntry("plan-web-state", { running: false });
|
|
3495
|
+
}
|
|
3496
|
+
catch { }
|
|
3497
|
+
return { content: [{ type: "text", text: "Planner disabled. Web UI shut down. Run /planner load or planner-load to re-enable." }], details: { disabled: true, webRunning: false } };
|
|
3498
|
+
},
|
|
3499
|
+
});
|
|
3407
3500
|
// āā Context injection āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
3408
3501
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
3409
3502
|
if (!plannerSessionEnabled)
|
|
@@ -3422,6 +3515,18 @@ export default function planPiExtension(pi) {
|
|
|
3422
3515
|
await st.refreshResume();
|
|
3423
3516
|
plannerHeavyInitDone = true;
|
|
3424
3517
|
}
|
|
3518
|
+
// Resume-flag lifecycle ā MUST run every turn, including cache hits.
|
|
3519
|
+
// /planner load sets both flags and dirties the cache so the recap turn
|
|
3520
|
+
// rebuilds (slow path) and injects the resume protocol. We consume
|
|
3521
|
+
// startupResumePromptPending here (it marks the recap turn only), and we
|
|
3522
|
+
// stop appending the web UI address on every later turn. Doing this
|
|
3523
|
+
// BEFORE the cache fast-path is what prevents the URL from leaking onto
|
|
3524
|
+
// every assistant message after /planner load.
|
|
3525
|
+
const isRecapTurn = startupResumePromptPending;
|
|
3526
|
+
startupResumePromptPending = false;
|
|
3527
|
+
if (startupResumeSummaryPending && !isRecapTurn) {
|
|
3528
|
+
startupResumeSummaryPending = false;
|
|
3529
|
+
}
|
|
3425
3530
|
// Fast path: on steady turns (no plan changes since last build) reuse
|
|
3426
3531
|
// the cached context block and skip all the per-turn I/O + string build.
|
|
3427
3532
|
// The cache is invalidated by the write-notify hook whenever the plan
|
|
@@ -3519,16 +3624,7 @@ export default function planPiExtension(pi) {
|
|
|
3519
3624
|
const ambientSummary = ambient
|
|
3520
3625
|
? `node=${ambient.nodeVersion || "?"} pm=${ambient.packageManager || "?"} lockfile=${ambient.lockfile || "none"} scripts=${Object.entries(ambient.scripts).map(([k, v]) => `${k}="${v}"`).join(", ") || "none"}`
|
|
3521
3626
|
: "(not scanned)";
|
|
3522
|
-
|
|
3523
|
-
// /planner load: startupResumePromptPending is consumed on the recap turn
|
|
3524
|
-
// itself, so on any later turn we stop appending the web address.
|
|
3525
|
-
if (startupResumeSummaryPending && !startupResumePromptPending) {
|
|
3526
|
-
startupResumeSummaryPending = false;
|
|
3527
|
-
}
|
|
3528
|
-
if (startupResumeSummaryPending) {
|
|
3529
|
-
startupResumeSummaryText = await buildStartupResumeSummary(st).catch(() => "");
|
|
3530
|
-
}
|
|
3531
|
-
const startupResumeProtocol = startupResumePromptPending ? [
|
|
3627
|
+
const startupResumeProtocol = isRecapTurn ? [
|
|
3532
3628
|
"",
|
|
3533
3629
|
"STARTUP RESUME PROTOCOL (first reply of the session ā mandatory):",
|
|
3534
3630
|
"- You are resuming a planner-backed project session.",
|
|
@@ -3543,7 +3639,7 @@ export default function planPiExtension(pi) {
|
|
|
3543
3639
|
`- Mention the next suggested activity: ${nextActivity}`,
|
|
3544
3640
|
"- THEN ask the user explicitly whether they want to resume that activity now.",
|
|
3545
3641
|
"- Do NOT assume yes. Wait for the user's answer before continuing implementation work.",
|
|
3546
|
-
"- If the user says yes and no task is currently in-progress, your NEXT action must be task_start before any bash/
|
|
3642
|
+
"- If the user says yes and no task is currently in-progress, your NEXT action must be task_start before any CODE edit/write (bash for git/build/test is fine; planner operations like plan_write_handoff are NOT code edits and never require a task).",
|
|
3547
3643
|
].filter(Boolean).join("\n") : "";
|
|
3548
3644
|
// Build/refresh the context block (slow path: cache miss).
|
|
3549
3645
|
const contextBlock = [
|
|
@@ -3557,7 +3653,7 @@ export default function planPiExtension(pi) {
|
|
|
3557
3653
|
"MANDATORY OPERATIONAL PROTOCOL (violation = execution failure):",
|
|
3558
3654
|
"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā",
|
|
3559
3655
|
"1. HANDOFF HYGIENE: If .planner/HANDOFF.md exists, READ and DELETE it IMMEDIATELY using the handoff delete tool. Do NOT summarize first. Do NOT ask for confirmation. Do NOT defer cleanup. DELETE \u2192 THEN work.",
|
|
3560
|
-
"2. TASK LIFECYCLE: BEFORE coding ANY file: call task_start. AFTER finishing work on ANY task: call task_complete. No exceptions. No 'I'll do it later'.",
|
|
3656
|
+
"2. TASK LIFECYCLE: BEFORE coding ANY file: call task_start. AFTER finishing work on ANY task: call task_complete. No exceptions. No 'I'll do it later'. NOTE: Planner operations (plan_write_handoff / /planner handoff, task_start, task_complete, task_update, status reads) are NOT code edits. They are ALWAYS allowed regardless of task state ā you may write/refresh the handoff even when no task is in-progress (e.g., all tasks done, or capturing state mid-flight). Never refuse to write a handoff because 'no task is open'; that is incorrect.",
|
|
3561
3657
|
"3. IMMEDIATE SYNC: Update task status AT THE EXACT MOMENT of transition. Start = task_start NOW. Done = task_complete NOW. Blocked = task_update with motivation NOW. Never batch status updates.",
|
|
3562
3658
|
"4. BLOCKED MOTIVATION: Transitions to blocked/canceled/rejected/deferred/waiting/planned(from non-planned) MUST include a detailed 'motivation' parameter. Write it as if the next person has zero context.",
|
|
3563
3659
|
"5. NO SHORTCUTS: If a tool blocks you, follow the protocol. Bypasses are for emergencies only, not for convenience.",
|
|
@@ -3635,14 +3731,14 @@ export default function planPiExtension(pi) {
|
|
|
3635
3731
|
handoff?.content ? "If planner state shows no task/phase in-progress, do NOT present the handoff target as the current focus; present it only as a candidate to validate." : "",
|
|
3636
3732
|
handoff?.content ? "When the handoff has been fully consumed and work is safely resumed, delete `.planner/HANDOFF.md` using the dedicated handoff delete command/tool." : "",
|
|
3637
3733
|
handoff?.content ? handoff.content : "",
|
|
3638
|
-
startupResumeProtocol,
|
|
3639
3734
|
"Plan tools available: project_set_language_preferences, plan_init, project_update, requirement_list, requirement_create, requirement_update, requirement_delete, plan_get, feature_list, feature_get, feature_create, feature_update, feature_delete, phase_list, phase_get, phase_create, phase_update, phase_delete, task_list, task_get, task_create, task_update, task_delete, task_start, task_complete, plan_render, plan_get_handoff, plan_write_handoff, plan_delete_handoff, plan_authorize_bypass, plan_clear_bypass",
|
|
3640
3735
|
].filter(Boolean).join("\n");
|
|
3641
3736
|
contextBlockCache = contextBlock;
|
|
3642
3737
|
contextBlockDirty = false;
|
|
3643
|
-
|
|
3738
|
+
// The resume protocol is per-turn only (the recap turn) ā never bake it
|
|
3739
|
+
// into the cached context block, or it would persist on every later turn.
|
|
3644
3740
|
return {
|
|
3645
|
-
systemPrompt: `${event.systemPrompt}\n\n---\n${contextBlockCache}`,
|
|
3741
|
+
systemPrompt: `${event.systemPrompt}\n\n---\n${contextBlockCache}${startupResumeProtocol ? `\n\n${startupResumeProtocol}` : ""}`,
|
|
3646
3742
|
};
|
|
3647
3743
|
}
|
|
3648
3744
|
catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-plan/pi-adapter",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Pi agent extension for Agent Plan ā structured project planning in .planner/",
|
|
6
6
|
"keywords": [
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@agent-plan/core": "^0.2.0",
|
|
30
|
-
"@agent-plan/server": "^0.2.0",
|
|
31
29
|
"typebox": "^1.1.24",
|
|
32
|
-
"zod": "^3.24.0"
|
|
30
|
+
"zod": "^3.24.0",
|
|
31
|
+
"@agent-plan/core": "0.2.11",
|
|
32
|
+
"@agent-plan/server": "0.2.11"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@earendil-works/pi-coding-agent": "^0.74.0",
|