From 09691fe2e8f35df5a5860656773a0540409ed5b0 Mon Sep 17 00:00:00 2001 From: ace-pm Date: Wed, 15 Apr 2026 16:18:15 +0200 Subject: [PATCH] Add SF-TUI extension main entry point. Integrates footer rendering, working vibes, prompt history stash, and git status tracking into unified TUI enhancement extension. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/resources/extensions/sf-tui/index.ts | 84 ++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/resources/extensions/sf-tui/index.ts diff --git a/src/resources/extensions/sf-tui/index.ts b/src/resources/extensions/sf-tui/index.ts new file mode 100644 index 000000000..9de2865cb --- /dev/null +++ b/src/resources/extensions/sf-tui/index.ts @@ -0,0 +1,84 @@ +/** + * SF-TUI — Unified TUI enhancements for Singularity Forge + * + * Features: + * - Rich footer: git branch, model, session cost, context usage + * - Working vibes: context-aware loading messages + * - Prompt history stash: Ctrl+Alt+H overlay with quick-pick + */ + +import type { ExtensionAPI, ExtensionContext } from "@sf-run/pi-coding-agent"; +import { Key } from "@sf-run/pi-tui"; +import { isAutoActive } from "../sf/auto.js"; +import { openStashOverlay, readStash, pushStash, writeStash } from "./stash.js"; +import { renderFooter } from "./footer.js"; +import { setVibeForPrompt, setVibeForTool, clearVibe } from "./vibes.js"; +import { invalidateGitStatus } from "./git.js"; + +export { setVibeForPrompt, setVibeForTool, clearVibe } from "./vibes.js"; + +function installFooter(ctx: ExtensionContext): void { + if (!ctx.hasUI) return; + ctx.ui.setFooter((tui, theme, footerData) => { + return { + render: (width: number) => { + if (isAutoActive()) return []; + return renderFooter(theme, footerData, ctx, width); + }, + invalidate: () => {}, + dispose: () => {}, + }; + }); +} + +export default function sfTui(pi: ExtensionAPI): void { + const stash = readStash(); + let wasAutoActive = false; + + pi.on("session_start", async (_event, ctx) => { + if (!ctx.hasUI) return; + + installFooter(ctx); + + pi.registerShortcut(Key.ctrlAlt("h"), { + description: "Open prompt history stash", + handler: openStashOverlay, + }); + pi.registerShortcut(Key.ctrlShift("h"), { + description: "Open prompt history stash (fallback)", + handler: openStashOverlay, + }); + + wasAutoActive = isAutoActive(); + }); + + pi.on("before_agent_start", async (event) => { + const prompt = event.prompt?.trim(); + if (prompt) { + pushStash(stash, prompt); + writeStash(stash); + } + }); + + pi.on("tool_call", async (event, ctx) => { + setVibeForTool(ctx, event.toolName, event.input); + }); + + pi.on("tool_result", async (_event, ctx) => { + invalidateGitStatus(); + const autoNow = isAutoActive(); + if (!autoNow && wasAutoActive) { + installFooter(ctx); + } + wasAutoActive = autoNow; + }); + + pi.on("agent_end", async (_event, ctx) => { + clearVibe(ctx); + const autoNow = isAutoActive(); + if (!autoNow) { + installFooter(ctx); + } + wasAutoActive = autoNow; + }); +}