mirror of
https://github.com/wassname/pi-plan.git
synced 2026-06-27 16:46:20 +08:00
Fix newSession context bug + mark unwired loop prompts (external review)
External review (deepseek-v4-pro, docs/reviews/review.md) found: - Real bug: reviewLoop cast agent_end's ExtensionContext to ExtensionCommandContext and called newSession on it, which would crash the "fresh context" path. Now the /plan command handler's context (which has newSession) is saved and reused, with a graceful in-place fallback (burneikis pattern). - Dead code: continuation + loopJudge prompts are unused (the autonomous loop is intentionally cut). Marked NOT-YET-WIRED in the prompts.ts flow header rather than removed, so the full intended flow stays reviewable. Review also confirmed: no comment bloat, no over-engineering. Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
+11
-8
@@ -44,6 +44,8 @@ export default function piPlanExtension(pi: ExtensionAPI): void {
|
||||
let state: PlanState = { isPlanMode: false, objective: null, judgeModel: null };
|
||||
// Reminder cadence: fire when an active goal exists but plan.md was not touched since last turn.
|
||||
let lastInjectedPlan = "";
|
||||
// newSession is only on the command-handler context; agent_end's ctx lacks it. Save it from /plan.
|
||||
let savedCmdCtx: ExtensionCommandContext | null = null;
|
||||
|
||||
const planPath = (ctx: ExtensionContext) => join(ctx.cwd, "plan.md");
|
||||
const readPlan = (ctx: ExtensionContext): string => (existsSync(planPath(ctx)) ? readFileSync(planPath(ctx), "utf-8") : "");
|
||||
@@ -87,6 +89,7 @@ export default function piPlanExtension(pi: ExtensionAPI): void {
|
||||
pi.registerCommand("plan", {
|
||||
description: "Plan mode: set up goals (with evidence) in plan.md, then work them. /plan <objective>",
|
||||
handler: async (args, ctx) => {
|
||||
savedCmdCtx = ctx; // ctx here is an ExtensionCommandContext (has newSession); keep it for later
|
||||
const arg = args.trim();
|
||||
if (arg === "clear") {
|
||||
await clearPlan(ctx);
|
||||
@@ -144,7 +147,7 @@ export default function piPlanExtension(pi: ExtensionAPI): void {
|
||||
|
||||
// --- review loop (after the agent drafts the plan) --------------------------------------------
|
||||
|
||||
async function reviewLoop(ctx: ExtensionContext, cmdCtx: ExtensionCommandContext): Promise<void> {
|
||||
async function reviewLoop(ctx: ExtensionContext): Promise<void> {
|
||||
while (true) {
|
||||
const doc = parse(readPlan(ctx));
|
||||
const choice = await ctx.ui.select(`Plan: ${doc.goals.length} goal(s). What next?`, [
|
||||
@@ -158,7 +161,7 @@ export default function piPlanExtension(pi: ExtensionAPI): void {
|
||||
ctx.ui.notify("Left plan mode. plan.md kept.", "info");
|
||||
return;
|
||||
}
|
||||
if (choice.startsWith("Ready")) return startExecution(ctx, cmdCtx);
|
||||
if (choice.startsWith("Ready")) return startExecution(ctx);
|
||||
if (choice.startsWith("Edit")) {
|
||||
const changes = await ctx.ui.editor("What should change about the plan?", "");
|
||||
if (changes?.trim()) {
|
||||
@@ -180,10 +183,10 @@ export default function piPlanExtension(pi: ExtensionAPI): void {
|
||||
updateWidget(ctx);
|
||||
}
|
||||
|
||||
async function startExecution(ctx: ExtensionContext, cmdCtx: ExtensionCommandContext): Promise<void> {
|
||||
// Offer a clean execution context (D13): some runs want the fresh handoff, some want to keep it.
|
||||
async function startExecution(ctx: ExtensionContext): Promise<void> {
|
||||
// Offer a clean execution context (D13). newSession lives only on the saved command context.
|
||||
let fresh = false;
|
||||
if (ctx.hasUI) {
|
||||
if (ctx.hasUI && savedCmdCtx) {
|
||||
const choice = await ctx.ui.select("Start working the plan in...", [
|
||||
"This context (keep history)",
|
||||
"A fresh, compacted context",
|
||||
@@ -194,8 +197,8 @@ export default function piPlanExtension(pi: ExtensionAPI): void {
|
||||
const doc = parse(readPlan(ctx));
|
||||
if (doc.objective) pi.setSessionName(`Plan: ${doc.objective}`);
|
||||
|
||||
if (fresh) {
|
||||
const result = await cmdCtx.newSession({ parentSession: ctx.sessionManager.getSessionFile() });
|
||||
if (fresh && savedCmdCtx) {
|
||||
const result = await savedCmdCtx.newSession({ parentSession: ctx.sessionManager.getSessionFile() });
|
||||
if (result.cancelled) {
|
||||
ctx.ui.notify("Execution cancelled.", "warning");
|
||||
return;
|
||||
@@ -269,7 +272,7 @@ export default function piPlanExtension(pi: ExtensionAPI): void {
|
||||
ctx.ui.notify("No goals found in plan.md yet — ask the agent to draft them.", "warning");
|
||||
return;
|
||||
}
|
||||
await reviewLoop(ctx, ctx as ExtensionCommandContext);
|
||||
await reviewLoop(ctx);
|
||||
});
|
||||
|
||||
// Keep only the freshest injected plan summary; strip stale ones so history does not bloat and
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
*
|
||||
* Read top to bottom to see the whole process. 5 and 6 are kept adjacent on
|
||||
* purpose: the cheap-foolable vs must-not-be-fooled contrast is the design.
|
||||
*
|
||||
* WIRED in index.ts: 1 planDrafting, 2 planInjection, 3 reminder, 6 evidenceJudge.
|
||||
* NOT YET WIRED: 4 continuation and 5 loopJudge define the autonomous re-prompt loop, which is
|
||||
* intentionally not built in v1 (an until-done-style loop was judged too complex). They stay here so
|
||||
* the full intended flow is reviewable; wire them if/when the loop is added.
|
||||
*/
|
||||
|
||||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user