Refactor pi plan mode

This commit is contained in:
Martin Pander
2026-07-23 19:39:51 +02:00
parent 23d13f93b7
commit 377d1cb03a
8 changed files with 69 additions and 22 deletions

View File

@@ -3,6 +3,7 @@
let
cfg = config.dot.pi;
piPath = "${config.dot.dotfilesPath}/modules/pi/agent";
piWorkMode = config.dot.llm.pi.workMode or false;
in
{
options.dot.pi = {
@@ -12,14 +13,15 @@ in
};
config = lib.mkIf cfg.enable {
# Out-of-store symlinks so the files stay editable without a nix rebuild.
# Only the managed files are linked; pi's runtime files (auth.json,
# sessions/, models-store.json) are left untouched in ~/.pi/agent.
home.file = {
".pi/agent/settings.json".source =
config.lib.file.mkOutOfStoreSymlink "${piPath}/settings.json";
config.lib.file.mkOutOfStoreSymlink
"${piPath}/${if piWorkMode then "settings.work" else "settings.home"}.json";
".pi/agent/extensions".source =
config.lib.file.mkOutOfStoreSymlink "${piPath}/extensions";
config.lib.file.mkOutOfStoreSymlink "${piPath}/extensions-common";
} // lib.optionalAttrs piWorkMode {
".pi/agent/extensions-work".source =
config.lib.file.mkOutOfStoreSymlink "${piPath}/extensions-work";
};
};
}

View File

@@ -316,10 +316,9 @@ Do just this one step. Do NOT start any other step. Stop when this step is done.
phase = undefined;
return;
}
const where = planFilePath ? `\nSaved to: ${planFilePath}` : "";
const choice = await ctx.ui.select(
`Plan "${plan.title}" — ${plan.steps.length} steps.${where}\nWhat next?`,
["Execute step-by-step", "Stay in plan mode", "Refine the plan"],
`Plan "${plan.title}" — ${plan.steps.length} steps.\nWhat next?`,
["Execute step-by-step", "Stay in plan mode", "Refine the plan", "Save plan to file"],
);
if (choice?.startsWith("Execute")) {
@@ -339,6 +338,14 @@ Do just this one step. Do NOT start any other step. Stop when this step is done.
if (refinement?.trim()) {
pi.sendUserMessage(refinement.trim(), { deliverAs: "followUp" });
}
} else if (choice === "Save plan to file") {
try {
planFilePath = await savePlanFile(ctx, plan);
ctx.ui.notify(`Plan saved to ${planFilePath}`, "info");
persistState();
} catch {
ctx.ui.notify("Failed to save plan.", "error");
}
} else {
phase = "planning";
updateStatus(ctx);
@@ -382,6 +389,23 @@ Do just this one step. Do NOT start any other step. Stop when this step is done.
},
});
pi.registerCommand("plan-save", {
description: "Save the current plan to a file in .pi/plans/",
handler: async (_args, ctx) => {
if (!plan) {
ctx.ui.notify("No plan to save. Submit a plan first.", "info");
return;
}
try {
planFilePath = await savePlanFile(ctx, plan);
ctx.ui.notify(`Plan saved to ${planFilePath}`, "info");
persistState();
} catch {
ctx.ui.notify("Failed to save plan.", "error");
}
},
});
pi.registerShortcut(Key.ctrlAlt("p"), {
description: "Toggle plan mode",
handler: async (ctx) => togglePlanMode(ctx),
@@ -424,23 +448,17 @@ Do just this one step. Do NOT start any other step. Stop when this step is done.
awaitingStep = false;
phase = "awaiting-approval";
try {
planFilePath = await savePlanFile(ctx, plan);
} catch {
planFilePath = undefined;
}
persistState();
updateStatus(ctx);
const loc = planFilePath ? ` and saved to ${planFilePath}` : "";
return {
content: [
{
type: "text",
text: `Plan "${plan.title}" with ${plan.steps.length} step(s) submitted${loc}. Stop here and wait for the user to approve, refine, or execute.`,
text: `Plan "${plan.title}" with ${plan.steps.length} step(s) submitted. Stop here and wait for the user to approve, refine, or execute. Use /plan-save to save the plan to a file.`,
},
],
details: { title: plan.title, steps: plan.steps.length, path: planFilePath ?? null },
details: { title: plan.title, steps: plan.steps.length },
terminate: true,
};
},

View File

@@ -141,13 +141,10 @@ export default function (pi: ExtensionAPI) {
// ── Hide unwanted built-in providers ────────────────────────────────────
const keep = new Set([
"anthropic",
"openai",
// "openrouter",
"openrouter",
]);
const hide = [
"openrouter",
"ant-ling",
"azure-openai",
"azure-openai-responses",

View File

@@ -0,0 +1,11 @@
{
"lastChangelogVersion": "0.81.1",
"theme": "light",
"defaultProvider": "openrouter",
"defaultModel": "deepseek/deepseek-v4-pro",
"defaultThinkingLevel": "high",
"hideThinkingBlock": false,
"enableInstallTelemetry": false,
"doubleEscapeAction": "tree",
"treeFilterMode": "all"
}

View File

@@ -1,8 +1,8 @@
{
"lastChangelogVersion": "0.81.1",
"theme": "light",
"defaultProvider": "anthropic",
"defaultModel": "claude-opus-4-8-default",
"defaultProvider": "openrouter",
"defaultModel": "deepseek/deepseek-v4-flash",
"defaultThinkingLevel": "high",
"hideThinkingBlock": false,
"retry": {

View File

@@ -0,0 +1,19 @@
{
"lastChangelogVersion": "0.81.1",
"theme": "light",
"defaultProvider": "anthropic",
"defaultModel": "claude-opus-4-8-default",
"defaultThinkingLevel": "high",
"hideThinkingBlock": false,
"retry": {
"enabled": true,
"maxRetries": 4,
"baseDelayMs": 10000
},
"enableInstallTelemetry": false,
"doubleEscapeAction": "tree",
"treeFilterMode": "all",
"extensions": [
"~/.pi/agent/extensions-work"
]
}