diff --git a/.changeset/quiet-pandas-watch.md b/.changeset/quiet-pandas-watch.md new file mode 100644 index 0000000..696d132 --- /dev/null +++ b/.changeset/quiet-pandas-watch.md @@ -0,0 +1,5 @@ +--- +"sideshow": patch +--- + +Allow Pi-compatible agents to hide the persistent Sideshow TUI status with `SIDESHOW_TUI_STATUS=0` while retaining native tools and trace synchronization. diff --git a/docs/connecting-agents.md b/docs/connecting-agents.md index 77fb0ae..b312cf7 100644 --- a/docs/connecting-agents.md +++ b/docs/connecting-agents.md @@ -41,6 +41,10 @@ pi install npm:sideshow pi -e npm:sideshow ``` +The extension shows its current server or session in Pi's TUI status area by +default. Set `SIDESHOW_TUI_STATUS=0` before launching Pi to hide that status +without disabling the native tools or automatic trace synchronization. + ## MCP Tools: `publish_post`, `update_post`, `list_posts`, `get_post`, diff --git a/extensions/sideshow.d.ts b/extensions/sideshow.d.ts new file mode 100644 index 0000000..ed5ee20 --- /dev/null +++ b/extensions/sideshow.d.ts @@ -0,0 +1 @@ +export default function sideshowExtension(pi: Record): void; diff --git a/extensions/sideshow.js b/extensions/sideshow.js index ade1cf2..4d42e64 100644 --- a/extensions/sideshow.js +++ b/extensions/sideshow.js @@ -104,6 +104,10 @@ function agentName() { return process.env.SIDESHOW_AGENT || "pi"; } +function showTuiStatus() { + return process.env.SIDESHOW_TUI_STATUS !== "0"; +} + function authHeaders(extra = {}) { return { ...(process.env.SIDESHOW_TOKEN @@ -379,12 +383,16 @@ export default function sideshowExtension(pi) { pi.on("session_start", (_event, ctx) => { state.sessionId = reconstructSession(ctx); - ctx.ui.setStatus( - "sideshow", - state.sessionId - ? `sideshow ${state.sessionId}` - : `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`, - ); + if (showTuiStatus()) { + ctx.ui.setStatus( + "sideshow", + state.sessionId + ? `sideshow ${state.sessionId}` + : `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`, + ); + } else { + ctx.ui.setStatus("sideshow", undefined); + } }); pi.on("turn_end", async (_event, ctx) => { @@ -403,7 +411,11 @@ export default function sideshowExtension(pi) { const command = args.trim(); if (command === "reset") { state.sessionId = process.env.SIDESHOW_SESSION || undefined; - ctx.ui.setStatus("sideshow", `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`); + if (showTuiStatus()) { + ctx.ui.setStatus("sideshow", `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`); + } else { + ctx.ui.setStatus("sideshow", undefined); + } ctx.ui.notify("Reset remembered sideshow session", "info"); return; } diff --git a/test/piExtension.test.ts b/test/piExtension.test.ts new file mode 100644 index 0000000..8a2f7e1 --- /dev/null +++ b/test/piExtension.test.ts @@ -0,0 +1,75 @@ +import assert from "node:assert/strict"; +import { afterEach, describe, it } from "node:test"; + +import sideshowExtension from "../extensions/sideshow.js"; + +const originalTuiStatus = process.env.SIDESHOW_TUI_STATUS; + +type Status = [key: string, text: string | undefined]; +interface Context { + sessionManager: { getBranch: () => never[] }; + ui: { + setStatus: (key: string, text: string | undefined) => number; + notify: () => void; + }; +} +type Handler = (event: object, context: Context) => void; +type Command = { handler: (args: string, context: Context) => Promise }; + +afterEach(() => { + if (originalTuiStatus === undefined) delete process.env.SIDESHOW_TUI_STATUS; + else process.env.SIDESHOW_TUI_STATUS = originalTuiStatus; +}); + +function loadExtension() { + const handlers = new Map(); + const commands = new Map(); + const pi = { + on(event: string, handler: Handler) { + handlers.set(event, handler); + }, + registerCommand(name: string, command: Command) { + commands.set(name, command); + }, + registerTool() {}, + }; + sideshowExtension(pi); + return { handlers, commands }; +} + +function context(statuses: Status[]) { + return { + sessionManager: { getBranch: () => [] }, + ui: { + setStatus: (key: string, text: string | undefined) => statuses.push([key, text]), + notify() {}, + }, + }; +} + +describe("Pi extension TUI status", () => { + it("shows status by default", () => { + delete process.env.SIDESHOW_TUI_STATUS; + const { handlers } = loadExtension(); + const statuses: Status[] = []; + + handlers.get("session_start")!({}, context(statuses)); + + assert.deepEqual(statuses, [["sideshow", "sideshow localhost:8228"]]); + }); + + it("clears status when SIDESHOW_TUI_STATUS=0", async () => { + process.env.SIDESHOW_TUI_STATUS = "0"; + const { handlers, commands } = loadExtension(); + const statuses: Status[] = []; + const ctx = context(statuses); + + handlers.get("session_start")!({}, ctx); + await commands.get("sideshow")!.handler("reset", ctx); + + assert.deepEqual(statuses, [ + ["sideshow", undefined], + ["sideshow", undefined], + ]); + }); +});