From f54dc79f7a9c16a3bbd364824ac044398d9cd45d Mon Sep 17 00:00:00 2001 From: wassname2 Date: Mon, 14 Sep 2026 20:48:24 +0800 Subject: [PATCH] fix: shorten external plan labels in goal widget --- README.md | 2 +- src/index.ts | 6 ++++-- test/goals.test.ts | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ff7c82d..94df55d 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ pi /goals ``` -`/goals` shows actions for the current mode. Drafts offer Edit, Discuss and Approve. Discuss returns to chat and waits for your input. Menu New asks for optional instructions before creating a plan; submit blank to use the conversation, or cancel to leave things unchanged. Typed `/goals new ` still starts directly. Quit (`exit` or `clear`) leaves the original plan unchanged, removes this session's goal check-in, and clears goal state without a model call. Matching check-in names with missing or different session bindings are left unchanged with a warning. Worker processes are unchanged; inspect their native panes and use their exact Intercom identities for steering. New creates a separate draft without overwriting earlier plans, named `.pi/plan/-vN.md` using the next version after existing files. The title stays inside the plan; old files are not renamed. The widget shows a plain `✓` and the relative plan path (the fallback for unverified terminal links). +`/goals` shows actions for the current mode. Drafts offer Edit, Discuss and Approve. Discuss returns to chat and waits for your input. Menu New asks for optional instructions before creating a plan; submit blank to use the conversation, or cancel to leave things unchanged. Typed `/goals new ` still starts directly. Quit (`exit` or `clear`) leaves the original plan unchanged, removes this session's goal check-in, and clears goal state without a model call. Matching check-in names with missing or different session bindings are left unchanged with a warning. Worker processes are unchanged; inspect their native panes and use their exact Intercom identities for steering. New creates a separate draft without overwriting earlier plans, named `.pi/plan/-vN.md` using the next version after existing files. The title stays inside the plan; old files are not renamed. The widget shows a plain `✓` and the relative plan path for inside-project plans. External plans use the filename with an `(external)` marker; `/goals status` keeps the full location. These are plain labels, not terminal links. ### Native worker lifecycle and limits diff --git a/src/index.ts b/src/index.ts index 2d0e08f..741d257 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ // Pi/OpenAI: Plan and supervise in the main chat; delegate implementation to a visible worker. import { createHash, randomUUID } from "node:crypto"; import { type FSWatcher, mkdirSync, readdirSync, readFileSync, watch, writeFileSync } from "node:fs"; -import { dirname, isAbsolute, join, relative, resolve } from "node:path"; +import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path"; import { type ExtensionAPI, type ExtensionContext, withFileMutationQueue } from "@earendil-works/pi-coding-agent"; import { INTERCOM_EXTENSION_REGISTER_EVENT, type IntercomExtensionChannel, type IntercomExtensionRegistration } from "pi-intercom/extension-api.js"; import { CronStorage } from "pi-schedule-prompt/src/storage.js"; @@ -182,7 +182,9 @@ export default function mainSupervisor(pi: ExtensionAPI) { }).filter(Boolean); lines.push(`… ${counts.join(", ")}`); } - lines.unshift(relative(ctx.cwd, state.plan!)); // Readable path fallback; terminal link activation is not verified. + const planPath = relative(ctx.cwd, state.plan!); + const external = isAbsolute(planPath) || planPath === ".." || planPath.startsWith(`..${sep}`); + lines.unshift(external ? `${basename(state.plan!)} (external)` : planPath); ctx.ui.setWidget("goals", lines); } function watchPlan(ctx: ExtensionContext) { diff --git a/test/goals.test.ts b/test/goals.test.ts index 078ddb5..3bc90c6 100644 --- a/test/goals.test.ts +++ b/test/goals.test.ts @@ -742,6 +742,10 @@ it.each(["solo", "supervising"])("%s widget omits long tasks without altering th if (mode === "solo") { f.ctx.ui.select.mockResolvedValueOnce("Worker confirmed stopped"); await f.command("solo"); } else await f.command("ready"); expect(f.ctx.ui.setWidget.mock.lastCall?.[1]).toEqual([relative(f.ctx.cwd, f.path), "◼ G1: first output", "◻ G2: second output"]); + f.ctx.cwd = join(f.ctx.cwd, "another-project", "nested"); + await f.command("status"); + expect(f.ctx.ui.setWidget.mock.lastCall?.[1]).toEqual([`${basename(f.path)} (external)`, "◼ G1: first output", "◻ G2: second output"]); + expect(f.ctx.ui.notify).toHaveBeenLastCalledWith(expect.stringContaining(`Plan: ${f.path}`), "info"); expect(readFileSync(f.path, "utf8")).toBe(text); });