@agent-plan/pi-adapter 0.2.5 → 0.2.7
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 +62 -5
- package/package.json +7 -7
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;
|
|
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;AA00BtF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAqxF9D"}
|
package/dist/index.js
CHANGED
|
@@ -165,6 +165,24 @@ async function maybeHealStatuses(st) {
|
|
|
165
165
|
function normalizeLanguagePref(value) {
|
|
166
166
|
return value?.trim() ?? "";
|
|
167
167
|
}
|
|
168
|
+
// Safety net: ctx.ui.input() can hang forever when the extension is loaded from
|
|
169
|
+
// an NPM-installed package (the prompt never renders, the await never resolves,
|
|
170
|
+
// and Pi freezes). Race the prompt against a timeout; on timeout, fall back to the
|
|
171
|
+
// empty answer (treated as "no / this session only" by the gating logic) and notify
|
|
172
|
+
// the user how to enable manually. This preserves the interactive prompt for the
|
|
173
|
+
// working case while guaranteeing session_start can never block Pi permanently.
|
|
174
|
+
const PROMPT_TIMEOUT_MS = 20000;
|
|
175
|
+
async function safeInput(ctx, prompt) {
|
|
176
|
+
try {
|
|
177
|
+
return await Promise.race([
|
|
178
|
+
ctx.ui.input(prompt),
|
|
179
|
+
new Promise((resolve) => setTimeout(() => resolve("__PLANNER_PROMPT_TIMEOUT__"), PROMPT_TIMEOUT_MS)),
|
|
180
|
+
]);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return "";
|
|
184
|
+
}
|
|
185
|
+
}
|
|
168
186
|
async function ensureProjectLanguagePreferences(st) {
|
|
169
187
|
const project = await st.loadProject();
|
|
170
188
|
const contentLanguage = normalizeLanguagePref(project.contentLanguage);
|
|
@@ -429,6 +447,24 @@ async function buildHandoffMarkdown(st, reason, overrides) {
|
|
|
429
447
|
: inferredRecentChanges.length > 0
|
|
430
448
|
? inferredRecentChanges.map((entry) => `- ${entry}`)
|
|
431
449
|
: ["- No recent activity recorded"];
|
|
450
|
+
// Auto-derived: all task statuses for the current phase, so a resuming agent
|
|
451
|
+
// sees the full picture (not just the single in-progress task).
|
|
452
|
+
const currentPhaseTaskLines = currentPhase && currentPhase.tasks.length > 0
|
|
453
|
+
? [...currentPhase.tasks]
|
|
454
|
+
.sort((a, b) => a.number - b.number || a.createdAt.localeCompare(b.createdAt))
|
|
455
|
+
.map((task) => `- ${statusIcon(task.status)} \`${task.id}\` — ${task.title} (${task.status})`)
|
|
456
|
+
: [];
|
|
457
|
+
// Caller-supplied rich context sections (locked design decisions,
|
|
458
|
+
// architecture, mode/state flows, plugin/API contracts, data mappings,
|
|
459
|
+
// known gaps, ...) injected between "What was being done" and "How to resume".
|
|
460
|
+
const extraSectionLines = (overrides?.extraSections ?? []).flatMap((section) => [
|
|
461
|
+
"",
|
|
462
|
+
`## ${section.heading}`,
|
|
463
|
+
section.body,
|
|
464
|
+
]);
|
|
465
|
+
const autoCurrentTaskSection = currentPhaseTaskLines.length > 0
|
|
466
|
+
? ["", `## Current Task Statuses (phase ${currentPhase.id})`, ...currentPhaseTaskLines]
|
|
467
|
+
: [];
|
|
432
468
|
return [
|
|
433
469
|
"# Handoff",
|
|
434
470
|
"",
|
|
@@ -448,6 +484,8 @@ async function buildHandoffMarkdown(st, reason, overrides) {
|
|
|
448
484
|
"",
|
|
449
485
|
"## What was being done",
|
|
450
486
|
whatWasBeingDone || "No additional execution notes were captured.",
|
|
487
|
+
...extraSectionLines,
|
|
488
|
+
...autoCurrentTaskSection,
|
|
451
489
|
"",
|
|
452
490
|
"## How to resume",
|
|
453
491
|
howToResume,
|
|
@@ -791,7 +829,7 @@ export default function planPiExtension(pi) {
|
|
|
791
829
|
}
|
|
792
830
|
else {
|
|
793
831
|
try {
|
|
794
|
-
const ans = await ctx
|
|
832
|
+
const ans = await safeInput(ctx, "Planner detected in this project. Enable the planner extension? (y)es / (n)o / (a)lways / n(e)ver)");
|
|
795
833
|
const normalized = ans?.trim().toLowerCase() ?? "";
|
|
796
834
|
if (["a", "always", "sempre"].includes(normalized)) {
|
|
797
835
|
enablePlanner = true;
|
|
@@ -865,7 +903,9 @@ export default function planPiExtension(pi) {
|
|
|
865
903
|
await st.ensureStructureOrdering().catch(() => { });
|
|
866
904
|
// Reconcile derived statuses once on startup: backfills any drift from
|
|
867
905
|
// manual edits, pre-fix data, or tools that bypassed the rollup.
|
|
868
|
-
|
|
906
|
+
// Uses maybeHealStatuses (idempotent via healedStatusRoots guard) so the
|
|
907
|
+
// later call below and before_agent_start don't re-run it (startup dedup).
|
|
908
|
+
await maybeHealStatuses(st).catch(() => { });
|
|
869
909
|
// Decide whether to start the web UI based on persisted prefs or prompt.
|
|
870
910
|
let startWeb = false;
|
|
871
911
|
if (project?.plannerAutoStartWeb) {
|
|
@@ -876,7 +916,7 @@ export default function planPiExtension(pi) {
|
|
|
876
916
|
}
|
|
877
917
|
else if (server === null) {
|
|
878
918
|
try {
|
|
879
|
-
const ans = await ctx
|
|
919
|
+
const ans = await safeInput(ctx, "Start the planner web UI? (y)es / (n)o / (a)lways / n(e)ver)");
|
|
880
920
|
const normalized = ans?.trim().toLowerCase() ?? "";
|
|
881
921
|
if (["a", "always", "sempre"].includes(normalized)) {
|
|
882
922
|
startWeb = true;
|
|
@@ -917,13 +957,19 @@ export default function planPiExtension(pi) {
|
|
|
917
957
|
const url = server?.url ?? "Starting server...";
|
|
918
958
|
ctx.ui.notify(`Planner enabled. Dashboard: ${url}\nAnalyzing project state and preparing resume summary...`, "info");
|
|
919
959
|
await ensureProjectLanguagePreferences(st).catch(() => null);
|
|
920
|
-
|
|
960
|
+
// (maybeHealStatuses already ran above — skipped here thanks to the guard.)
|
|
921
961
|
// Background cleanup of orphan .bak/.tmp.* files (non-blocking)
|
|
922
962
|
st.cleanupOrphanBackups().catch(() => { });
|
|
923
963
|
const handoff = await st.loadHandoff().catch(() => null);
|
|
924
964
|
if (handoff?.content) {
|
|
925
965
|
ctx.ui.notify(`Handoff detected at .planner/HANDOFF.md (updated ${handoff.updatedAt}). It will be loaded automatically when the agent starts.`, "info");
|
|
926
966
|
}
|
|
967
|
+
// Mark heavy init done so before_agent_start doesn't re-run migration,
|
|
968
|
+
// status healing, and language prefs on the first turn (startup dedup:
|
|
969
|
+
// all of these already ran in this session_start block). refreshResume is
|
|
970
|
+
// safe to skip here because the per-turn context builder falls back to
|
|
971
|
+
// loadResume() ?? refreshResume() when building the system prompt.
|
|
972
|
+
plannerHeavyInitDone = true;
|
|
927
973
|
pi.sendMessage({
|
|
928
974
|
customType: "planner-resume-trigger",
|
|
929
975
|
content: "Emit the mandatory planner startup resume summary now.",
|
|
@@ -2748,11 +2794,15 @@ export default function planPiExtension(pi) {
|
|
|
2748
2794
|
pi.registerTool({
|
|
2749
2795
|
name: "plan_write_handoff",
|
|
2750
2796
|
label: "Plan Write Handoff",
|
|
2751
|
-
description: "Write or refresh the canonical .planner/HANDOFF.md document.",
|
|
2797
|
+
description: "Write or refresh the canonical .planner/HANDOFF.md document. Capture full design context, not just operational resume steps: pass extraSections with locked decisions, architecture (with file:line refs), mode/state flows, plugin/API contracts, data mappings, files touched, and known gaps so a resuming agent doesn't re-discover decisions already made.",
|
|
2752
2798
|
parameters: Type.Object({
|
|
2753
2799
|
reason: Type.Optional(Type.String({ description: "Why the handoff is being written" })),
|
|
2754
2800
|
whatWasBeingDone: Type.Optional(Type.String({ description: "Optional override for the current work summary" })),
|
|
2755
2801
|
howToResume: Type.Optional(Type.String({ description: "Optional override for resume instructions" })),
|
|
2802
|
+
extraSections: Type.Optional(Type.Array(Type.Object({
|
|
2803
|
+
heading: Type.String({ description: "Section heading (without the leading ## markers)" }),
|
|
2804
|
+
body: Type.String({ description: "Markdown body of the section. Use file:line refs, IDs, and concrete mappings." }),
|
|
2805
|
+
}), { description: "Rich context sections injected between 'What was being done' and 'How to resume'. Use for: Locked Design Decisions ({id,title,decision,rationale}), Architecture (new modules with file:line), Mode/State Flow (textual diagrams), Plugin/API Contracts, Data Mappings, Files Touched (new/modified/planner), Known Gaps. Maximize the design context a resuming agent needs." })),
|
|
2756
2806
|
}),
|
|
2757
2807
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
2758
2808
|
const st = await requirePlan(ctx);
|
|
@@ -2761,6 +2811,13 @@ export default function planPiExtension(pi) {
|
|
|
2761
2811
|
await writeProjectHandoff(st, params.reason?.trim() || "manual tool handoff", {
|
|
2762
2812
|
...(params.whatWasBeingDone?.trim() ? { whatWasBeingDone: params.whatWasBeingDone.trim() } : {}),
|
|
2763
2813
|
...(params.howToResume?.trim() ? { howToResume: params.howToResume.trim() } : {}),
|
|
2814
|
+
...(params.extraSections && params.extraSections.length > 0
|
|
2815
|
+
? {
|
|
2816
|
+
extraSections: params.extraSections
|
|
2817
|
+
.map((section) => ({ heading: section.heading?.trim() ?? "", body: section.body ?? "" }))
|
|
2818
|
+
.filter((section) => section.heading),
|
|
2819
|
+
}
|
|
2820
|
+
: {}),
|
|
2764
2821
|
});
|
|
2765
2822
|
const handoff = await st.loadHandoff();
|
|
2766
2823
|
return { content: [{ type: "text", text: "Wrote .planner/HANDOFF.md" }], details: handoff ?? {} };
|
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.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Pi agent extension for Agent Plan — structured project planning in .planner/",
|
|
6
6
|
"keywords": [
|
|
@@ -25,11 +25,6 @@
|
|
|
25
25
|
"."
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
|
-
"scripts": {
|
|
29
|
-
"build": "tsc -p tsconfig.json && rm -f dist/.tsbuildinfo",
|
|
30
|
-
"build:full": "pnpm build && ../../scripts/copy-web-ui.sh",
|
|
31
|
-
"check": "tsc -p tsconfig.json --noEmit"
|
|
32
|
-
},
|
|
33
28
|
"dependencies": {
|
|
34
29
|
"@agent-plan/core": "^0.2.0",
|
|
35
30
|
"@agent-plan/server": "^0.2.0",
|
|
@@ -39,5 +34,10 @@
|
|
|
39
34
|
"devDependencies": {
|
|
40
35
|
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
41
36
|
"@earendil-works/pi-ai": "^0.74.0"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.json && rm -f dist/.tsbuildinfo",
|
|
40
|
+
"build:full": "pnpm build && ../../scripts/copy-web-ui.sh",
|
|
41
|
+
"check": "tsc -p tsconfig.json --noEmit"
|
|
42
42
|
}
|
|
43
|
-
}
|
|
43
|
+
}
|