import type { ExtensionAPI, ExtensionContext, } from "@earendil-works/pi-coding-agent"; import { formatContext, OneDevContextManager, type OneDevSessionContext, } from "./context.js"; import { MutationApprovalManager } from "./mutation-approvals.js"; import { OneDevSourceWatchManager } from "./source-watch.js"; import { registerOnedevTools, type OnedevToolController, } from "./tools/index.js"; import type { ToolDeps } from "./tools/common.js"; const SETTINGS_ENTRY = "onedev-settings"; interface OnedevSettings { footerStatus: boolean; } export default function onedevExtension(pi: ExtensionAPI): void { let context: OneDevSessionContext | undefined; let controller: OnedevToolController | undefined; let footerStatus = true; const contextManager = new OneDevContextManager((command, args, options) => pi.exec(command, args, options), ); const approvals = new MutationApprovalManager(); const deps: ToolDeps = { exec: (command, args, options) => pi.exec(command, args, options), context: () => { if (!context) throw new Error("OneDev extension is still initializing"); return context; }, refreshContext: async (options) => { context = await contextManager.refresh(options); return context; }, approvals, }; const applyStatus = (ctx: { hasUI: boolean; ui: { setStatus: (key: string, value: string | undefined) => void }; }): void => { if (!ctx.hasUI) return; if (footerStatus && context?.status === "ready" && context.project) { ctx.ui.setStatus("onedev", `onedev: ${context.project}`); } else { ctx.ui.setStatus("onedev", undefined); } }; const restoreSettings = (ctx: ExtensionContext): void => { footerStatus = true; for (const entry of ctx.sessionManager.getBranch()) { if (entry.type !== "custom" || entry.customType !== SETTINGS_ENTRY) continue; const saved = entry.data as Partial | undefined; if (typeof saved?.footerStatus === "boolean") { footerStatus = saved.footerStatus; } } }; pi.registerCommand("od-context", { description: "Show the active OneDev server, project, and user", handler: (_args, ctx) => { if (!context) throw new Error("OneDev extension is still initializing"); ctx.ui.notify( formatContext(context), context.status === "ready" ? "info" : "warning", ); return Promise.resolve(); }, }); pi.registerCommand("od-health", { description: "Re-check the tod CLI configuration and OneDev server", handler: async (_args, ctx) => { context = await contextManager.refresh({ verifyAuth: true, force: true, }); applyStatus(ctx); ctx.ui.notify( formatContext(context), context.status === "ready" ? "info" : "warning", ); }, }); pi.registerCommand("od-settings", { description: "Configure OneDev TUI settings", handler: async (_args, ctx) => { if (ctx.mode !== "tui") { ctx.ui.notify("/od-settings requires TUI mode", "error"); return; } const selected = await ctx.ui.select( `OneDev footer status (currently ${footerStatus ? "enabled" : "disabled"})`, ["Enabled", "Disabled"], ); if (!selected) return; const enabled = selected === "Enabled"; if (enabled !== footerStatus) { pi.appendEntry(SETTINGS_ENTRY, { footerStatus: enabled, }); footerStatus = enabled; applyStatus(ctx); } ctx.ui.notify( `OneDev footer status ${enabled ? "enabled" : "disabled"}`, "info", ); }, }); const watches = new OneDevSourceWatchManager( { exec: deps.exec, context: deps.context }, (emission) => { pi.sendMessage( { customType: "onedev-watch", content: emission.message, display: true, details: emission.details, }, { triggerTurn: true, deliverAs: "steer" }, ); }, ); controller = registerOnedevTools(pi, deps, watches); pi.on("session_start", async (_event, ctx) => { watches.clear(); controller?.reset(); approvals.resetSession(); restoreSettings(ctx); contextManager.reset(ctx.cwd); context = await contextManager.refresh(); applyStatus(ctx); }); pi.on("session_tree", (_event, ctx) => { watches.clear(); restoreSettings(ctx); applyStatus(ctx); }); pi.on("session_shutdown", (_event, ctx) => { if (ctx.hasUI) ctx.ui.setStatus("onedev", undefined); approvals.resetSession(); watches.clear(); contextManager.clear(); context = undefined; }); }