Name goals files <last 6 of session id>-vN.md, as main's plan files did

Session IDs start with a timestamp, so the last 6 characters are the distinguishing part.
A new /goals new in the same session makes the next version and never overwrites (flag wx).

Co-Authored-By: Claude <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
wassname
2026-09-24 12:39:46 +08:00
co-authored by Claude
parent 22041d4680
commit 6878b48a2a
3 changed files with 19 additions and 6 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ pi-goals bundles the scheduler. Install pi-subagents yourself (0.70.1 tested); t
## Use
```
/goals new <idea> discuss and draft .pi/goals/<session>-<id>.md; nothing runs yet
/goals new <idea> discuss and draft .pi/goals/<last 6 of session id>-vN.md; nothing runs yet
/goals review show the draft with Ready / Refine / Edit / Cancel
/goals pause remove the scheduled loop; the file stays
/goals resume start a new loop for the paused goals
+8 -4
View File
@@ -1,6 +1,6 @@
// PI/OpenAI: one agent, one goals file, human Ready, and a stock scheduled loop.
import { randomUUID } from "node:crypto";
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join, relative, resolve } from "node:path";
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
@@ -124,11 +124,15 @@ export default function piGoals(pi: ExtensionAPI): void {
}
if (arg !== "new" && !arg.startsWith("new ")) throw new Error("Use /goals new [idea], review, pause, resume, clear, or judge.");
if (state.file) throw new Error("Clear the current goals before starting new ones; the file is preserved.");
const file = join(ctx.cwd, ".pi/goals", `${ctx.sessionManager.getSessionId()}-${randomUUID().slice(0, 8)}.md`);
mkdirSync(dirname(file), { recursive: true });
const idea = arg.slice(3).trim();
const dir = join(ctx.cwd, ".pi/goals");
mkdirSync(dir, { recursive: true });
// Session IDs start with a timestamp; the last 6 characters differ between sessions.
const suffix = ctx.sessionManager.getSessionId().slice(-6);
const taken = readdirSync(dir).map(name => Number(new RegExp(`^${suffix}-v(\\d+)\\.md$`).exec(name)?.[1] ?? 0));
const file = join(dir, `${suffix}-v${Math.max(0, ...taken) + 1}.md`);
// Slash commands skip the input hook; keep the user's opening words verbatim too.
writeFileSync(file, idea ? appendInterview("", `/goals new ${idea}`).trimStart() : "");
writeFileSync(file, idea ? appendInterview("", `/goals new ${idea}`).trimStart() : "", { flag: "wx" });
state = { ...state, owner: ctx.sessionManager.getSessionId(), phase: "planning", file };
generation++; reviewRequested = false; resyncDue = false;
persist(); refresh(ctx);
+10 -1
View File
@@ -16,7 +16,7 @@ describe("planning and Ready", () => {
const h = setup({ choices: ["Ready"] });
await h.commands.get("goals").handler("new plot the data", h.ctx);
const file = h.branch.findLast(e => e.customType === "pi-goals-single-agent").data.file;
expect(file).toMatch(/\.pi\/goals\/sess-[\w]+\.md$/);
expect(file).toMatch(/\.pi\/goals\/sess-v1\.md$/);
expect(readFileSync(file, "utf8")).toContain("> /goals new plot the data");
writeFileSync(file, GOALS);
await h.hook("input", { source: "interactive", text: "yes, reuse judge_demos.py" });
@@ -32,6 +32,15 @@ describe("planning and Ready", () => {
expect(await h.hook("tool_call", { toolName: "write", input: { path: "train.py" } })).toBeUndefined();
});
it("a second /goals new in the same session makes -v2 and keeps v1", async () => {
const h = setup();
await h.commands.get("goals").handler("new first", h.ctx);
await h.commands.get("goals").handler("clear", h.ctx);
await h.commands.get("goals").handler("new second", h.ctx);
expect(h.branch.findLast(e => e.customType === "pi-goals-single-agent").data.file).toMatch(/sess-v2\.md$/);
expect(readFileSync(`${h.cwd}/.pi/goals/sess-v1.md`, "utf8")).toContain("new first");
});
it("Cancel starts nothing and keeps the file", async () => {
const h = setup({ choices: ["Cancel"] });
const file = await ready(h);