From 80ef0227daecfc9923b39e94cb59467988f80747 Mon Sep 17 00:00:00 2001 From: Ed Heltzel <402910+edheltzel@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:06:17 -0400 Subject: [PATCH] feat: complete on when poteto is off, off when on --- e2e/unit/poteto-command.test.ts | 50 +++++++++++++++++++++++++++++++++ extensions/pstack.ts | 17 ++++++++--- 2 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 e2e/unit/poteto-command.test.ts diff --git a/e2e/unit/poteto-command.test.ts b/e2e/unit/poteto-command.test.ts new file mode 100644 index 0000000..417ba74 --- /dev/null +++ b/e2e/unit/poteto-command.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from "vitest"; +import pstackExtension from "../../extensions/pstack.ts"; + +type Completions = { value: string; label: string }[] | null; + +function boot() { + let command: { + getArgumentCompletions?: (prefix: string) => Completions; + handler: (args: string, ctx: unknown) => Promise; + }; + const messages: string[] = []; + pstackExtension({ + setLabel() {}, + on() {}, + appendEntry() {}, + sendUserMessage(text: string) { + messages.push(text); + }, + registerCommand(_name: string, opts: typeof command) { + command = opts; + }, + }); + const ctx = { + mode: "rpc", + isIdle: () => true, + sessionManager: { + getSessionId: () => "s1", + getSessionFile: () => undefined, + }, + ui: { setStatus() {}, notify() {} }, + }; + return { command: command!, messages, ctx }; +} + +describe("poteto-mode completions", () => { + it("lists on when off and off when on", async () => { + const { command, ctx } = boot(); + expect(command.getArgumentCompletions?.("")).toEqual([{ value: "on", label: "on" }]); + await command.handler("", ctx); + expect(command.getArgumentCompletions?.("")).toEqual([{ value: "off", label: "off" }]); + await command.handler("off", ctx); + expect(command.getArgumentCompletions?.("")).toEqual([{ value: "on", label: "on" }]); + }); + + it("does not send on as a skill task", async () => { + const { command, messages, ctx } = boot(); + await command.handler("on", ctx); + expect(messages).toEqual(["/skill:do-poteto-mode"]); + }); +}); diff --git a/extensions/pstack.ts b/extensions/pstack.ts index 96043f0..3659533 100644 --- a/extensions/pstack.ts +++ b/extensions/pstack.ts @@ -94,6 +94,7 @@ export default function pstackExtension(pi: any): void { } const modeBySession = new Map(); + let lastOn = false; function setStatus(ctx: any, on: boolean): void { if (ctx?.mode !== "tui") return; @@ -109,6 +110,7 @@ export default function pstackExtension(pi: any): void { const file = sessionFileFromCtx(ctx); if (sid) modeBySession.set(sid, enabled); if (file) modeBySession.set(file, enabled); + lastOn = enabled; try { pi.appendEntry("pstack-mode", { enabled }); } catch { @@ -122,12 +124,14 @@ export default function pstackExtension(pi: any): void { if (sid) modeBySession.delete(sid); const on = isPotetoOn(ctx, modeBySession); setStatus(ctx, on); + lastOn = on; return on; } pi.on("session_shutdown", async (_event: unknown, ctx: any) => { const sid = sessionIdFromCtx(ctx); if (sid) modeBySession.delete(sid); + lastOn = false; setStatus(ctx, false); }); @@ -157,11 +161,12 @@ export default function pstackExtension(pi: any): void { }); pi.registerCommand("poteto-mode", { - description: "Enable or disable sticky pstack Poteto Mode. Usage: /poteto-mode [task] | /poteto-mode off", + description: "Enable or disable sticky pstack Poteto Mode. Usage: /poteto-mode [on|off|task]", getArgumentCompletions: (prefix: string) => { const token = String(prefix ?? "").trim().toLowerCase(); - if (!token || "off".startsWith(token)) { - return [{ value: "off", label: "off" }]; + const value = lastOn ? "off" : "on"; + if (!token || value.startsWith(token)) { + return [{ value, label: value }]; } return null; }, @@ -186,7 +191,11 @@ export default function pstackExtension(pi: any): void { } catch { // ignore } - const payload = `${POTETO_SKILL}${raw ? ` ${raw}` : ""}`; + const task = + token === "on" || token === "enable" || token === "start" + ? raw.slice(token.length).trim() + : raw; + const payload = `${POTETO_SKILL}${task ? ` ${task}` : ""}`; if (typeof pi.sendUserMessage === "function") { const idle = typeof ctx?.isIdle === "function" ? ctx.isIdle() : true; pi.sendUserMessage(