import type { ExtensionAPI, ExtensionContext, } from "@earendil-works/pi-coding-agent"; import { compressText, looksExactSensitive, type CompressionResult, type DeflufferProfile, } from "./defluffer"; type AnimationMode = "widget" | "off"; type Settings = { enabled: boolean; profile: DeflufferProfile; minSavingsPct: number; skipSlashCommands: boolean; skipSteering: boolean; skipExactSensitive: boolean; showWidget: boolean; showNotify: boolean; animation: AnimationMode; }; type Stats = { seen: number; transformed: number; skipped: number; savedTokens: number; originalTokens: number; defluffedTokens: number; skipReasons: Record; last: Array<{ at: number; profile: DeflufferProfile; originalTokens: number; defluffedTokens: number; savedTokens: number; savingsPct: number; }>; }; const DEFAULT_SETTINGS: Settings = { enabled: true, profile: "standardGuardedDedupe", minSavingsPct: 3, skipSlashCommands: true, skipSteering: true, skipExactSensitive: true, showWidget: true, showNotify: false, animation: "widget", }; const CUSTOM_SETTINGS = "defluffer-settings"; const CUSTOM_METRIC = "defluffer-metric"; const WIDGET_ID = "defluffer"; const STATUS_ID = "defluffer"; function emptyStats(): Stats { return { seen: 0, transformed: 0, skipped: 0, savedTokens: 0, originalTokens: 0, defluffedTokens: 0, skipReasons: {}, last: [], }; } function fmtPct(value: number): string { return `${value.toFixed(1)}%`; } function snippet(text: string, max = 96): string { const oneLine = text.replace(/\s+/g, " ").trim(); return oneLine.length > max ? `${oneLine.slice(0, max - 1)}…` : oneLine; } function mergeSettings(value: unknown): Settings | undefined { if (!value || typeof value !== "object") return undefined; const input = value as Partial; return { ...DEFAULT_SETTINGS, ...input, profile: input.profile === "off" || input.profile === "safe" || input.profile === "standardGuardedDedupe" ? input.profile : DEFAULT_SETTINGS.profile, animation: input.animation === "off" || input.animation === "widget" ? input.animation : DEFAULT_SETTINGS.animation, minSavingsPct: typeof input.minSavingsPct === "number" ? input.minSavingsPct : DEFAULT_SETTINGS.minSavingsPct, }; } function restoreFromSession( ctx: ExtensionContext, settings: Settings, stats: Stats, ): Settings { let restoredSettings = settings; const branch = ctx.sessionManager.getBranch(); for (const entry of branch as any[]) { if (entry?.type !== "custom") continue; if (entry.customType === CUSTOM_SETTINGS) { restoredSettings = mergeSettings(entry.data) ?? restoredSettings; } if (entry.customType === CUSTOM_METRIC && entry.data) { const data = entry.data as Partial; stats.transformed++; stats.seen++; stats.savedTokens += Number(data.savedTokens || 0); stats.originalTokens += Number(data.originalTokens || 0); stats.defluffedTokens += Number(data.defluffedTokens || 0); stats.last.push({ at: Date.now(), profile: (data.profile as DeflufferProfile) || restoredSettings.profile, originalTokens: Number(data.originalTokens || 0), defluffedTokens: Number(data.defluffedTokens || 0), savedTokens: Number(data.savedTokens || 0), savingsPct: Number(data.savingsPct || 0), }); } } stats.last = stats.last.slice(-5); return restoredSettings; } function skipReason( event: { text: string; source?: string; streamingBehavior?: string; images?: unknown[]; }, settings: Settings, ): string | undefined { const text = event.text; if (!settings.enabled || settings.profile === "off") return "disabled"; if (event.source === "extension") return "extension input"; if (settings.skipSteering && event.streamingBehavior === "steer") return "steering input"; if (settings.skipSlashCommands && text.trimStart().startsWith("/")) return "slash command"; if ((event.images?.length || 0) > 0 && text.trim().length < 200) return "image prompt"; if (settings.skipExactSensitive) return looksExactSensitive(text); return undefined; } function applySkip(stats: Stats, reason: string): void { stats.skipped++; stats.skipReasons[reason] = (stats.skipReasons[reason] || 0) + 1; } function applyMetric(stats: Stats, result: CompressionResult): void { stats.transformed++; stats.savedTokens += result.savedTokens; stats.originalTokens += result.originalTokens; stats.defluffedTokens += result.defluffedTokens; stats.last.push({ at: Date.now(), profile: result.profile, originalTokens: result.originalTokens, defluffedTokens: result.defluffedTokens, savedTokens: result.savedTokens, savingsPct: result.savingsPct, }); stats.last = stats.last.slice(-5); } function statusText( ctx: ExtensionContext, settings: Settings, stats: Stats, ): string { if (!settings.enabled || settings.profile === "off") return ctx.ui.theme.fg("dim", "Defluff: off"); const avg = stats.originalTokens ? (stats.savedTokens / stats.originalTokens) * 100 : 0; return ctx.ui.theme.fg( "dim", `Defluff: ${settings.profile} · saved ${stats.savedTokens} est toks · avg ${fmtPct(avg)}`, ); } function updateStatus( ctx: ExtensionContext, settings: Settings, stats: Stats, ): void { if (!ctx.hasUI) return; ctx.ui.setStatus(STATUS_ID, statusText(ctx, settings, stats)); } function clearWidgetLater(ctx: ExtensionContext, ms = 1600): void { setTimeout(() => { if (ctx.hasUI) ctx.ui.setWidget(WIDGET_ID, undefined); }, ms).unref?.(); } function showWidget(ctx: ExtensionContext, result: CompressionResult): void { if (!ctx.hasUI || ctx.mode !== "tui") return; const title = `✂ Defluffed ${result.originalTokens} → ${result.defluffedTokens} est tokens (-${fmtPct(result.savingsPct)})`; const frames = [ [ ctx.ui.theme.fg("accent", "✂ Defluffing input…"), ctx.ui.theme.fg("dim", snippet(result.original)), ], [ ctx.ui.theme.fg("accent", title), ctx.ui.theme.fg("muted", `Before: ${snippet(result.original)}`), ], [ ctx.ui.theme.fg("success", title), ctx.ui.theme.fg("muted", `After: ${snippet(result.text)}`), ], ]; frames.forEach((lines, index) => { setTimeout(() => { if (ctx.hasUI) ctx.ui.setWidget(WIDGET_ID, lines); }, index * 180).unref?.(); }); clearWidgetLater(ctx, 1800); } function previewText(result: CompressionResult): string { return [ `Profile: ${result.profile}`, `Tokens: ${result.originalTokens} → ${result.defluffedTokens} (${result.savedTokens >= 0 ? "-" : "+"}${Math.abs(result.savedTokens)} / ${fmtPct(result.savingsPct)})`, `Changed: ${result.changed}${result.changed ? "" : " (no change)"}`, result.safe ? "Safety: ok" : `Safety: blocked (${result.reason})`, "", "Defluffed:", result.text, ].join("\n"); } function statsText(settings: Settings, stats: Stats): string { const avg = stats.originalTokens ? (stats.savedTokens / stats.originalTokens) * 100 : 0; const skipReasons = Object.entries(stats.skipReasons) .sort((a, b) => b[1] - a[1]) .map(([reason, count]) => `- ${reason}: ${count}`) .join("\n") || "- none"; const last = stats.last .map( (m) => `- ${m.originalTokens} → ${m.defluffedTokens} saved ${m.savedTokens} (${fmtPct(m.savingsPct)})`, ) .join("\n") || "- none"; return [ `Enabled: ${settings.enabled}`, `Profile: ${settings.profile}`, `Min savings: ${fmtPct(settings.minSavingsPct)}`, `Seen: ${stats.seen}`, `Transformed: ${stats.transformed}`, `Skipped: ${stats.skipped}`, `Saved est tokens: ${stats.savedTokens}`, `Average savings: ${fmtPct(avg)}`, "", "Skip reasons:", skipReasons, "", "Recent transforms:", last, ].join("\n"); } export default function (pi: ExtensionAPI) { let settings: Settings = { ...DEFAULT_SETTINGS }; let stats: Stats = emptyStats(); pi.on("session_start", async (_event, ctx) => { stats = emptyStats(); settings = restoreFromSession(ctx, settings, stats); updateStatus(ctx, settings, stats); }); pi.on("input", async (event, ctx) => { stats.seen++; const reason = skipReason(event, settings); if (reason) { applySkip(stats, reason); updateStatus(ctx, settings, stats); return { action: "continue" as const }; } const result = compressText(event.text, settings.profile); if (!result.changed) { applySkip(stats, "no change"); updateStatus(ctx, settings, stats); return { action: "continue" as const }; } if (!result.safe) { applySkip(stats, result.reason || "integrity check failed"); updateStatus(ctx, settings, stats); return { action: "continue" as const }; } if (result.savingsPct < settings.minSavingsPct || result.savedTokens <= 0) { applySkip(stats, "below threshold"); updateStatus(ctx, settings, stats); return { action: "continue" as const }; } applyMetric(stats, result); pi.appendEntry(CUSTOM_METRIC, { profile: result.profile, originalTokens: result.originalTokens, defluffedTokens: result.defluffedTokens, savedTokens: result.savedTokens, savingsPct: result.savingsPct, }); if (settings.showWidget && settings.animation === "widget") showWidget(ctx, result); if (settings.showNotify && ctx.hasUI) { ctx.ui.notify( `Defluffed ${result.originalTokens} → ${result.defluffedTokens} est tokens (-${fmtPct(result.savingsPct)})`, "info", ); } updateStatus(ctx, settings, stats); return { action: "transform" as const, text: result.text, images: event.images, }; }); pi.on("agent_end", async (_event, ctx) => { updateStatus(ctx, settings, stats); }); pi.registerCommand("defluff", { description: "Configure prompt defluffing: on, off, status, stats, profile, min, preview, animation.", handler: async (args, ctx) => { const [cmdRaw, ...rest] = args.trim().split(/\s+/).filter(Boolean); const cmd = (cmdRaw || "status").toLowerCase(); const restText = rest.join(" "); if (cmd === "on") { settings.enabled = true; if (settings.profile === "off") settings.profile = "standardGuardedDedupe"; pi.appendEntry(CUSTOM_SETTINGS, settings); updateStatus(ctx, settings, stats); ctx.ui.notify("Defluffer enabled", "info"); return; } if (cmd === "off") { settings.enabled = false; pi.appendEntry(CUSTOM_SETTINGS, settings); updateStatus(ctx, settings, stats); ctx.ui.notify("Defluffer disabled", "info"); return; } if (cmd === "profile") { const next = rest[0] as DeflufferProfile | undefined; if ( next !== "off" && next !== "safe" && next !== "standardGuardedDedupe" ) { ctx.ui.notify( "Usage: /defluff profile off|safe|standardGuardedDedupe", "error", ); return; } settings.profile = next; settings.enabled = next !== "off"; pi.appendEntry(CUSTOM_SETTINGS, settings); updateStatus(ctx, settings, stats); ctx.ui.notify(`Defluffer profile: ${next}`, "info"); return; } if (cmd === "min") { const value = Number(rest[0]); if (!Number.isFinite(value) || value < 0 || value > 80) { ctx.ui.notify("Usage: /defluff min <0-80>", "error"); return; } settings.minSavingsPct = value; pi.appendEntry(CUSTOM_SETTINGS, settings); updateStatus(ctx, settings, stats); ctx.ui.notify(`Defluffer min savings: ${fmtPct(value)}`, "info"); return; } if (cmd === "animation") { const next = rest[0] as AnimationMode | undefined; if (next !== "widget" && next !== "off") { ctx.ui.notify("Usage: /defluff animation widget|off", "error"); return; } settings.animation = next; settings.showWidget = next !== "off"; pi.appendEntry(CUSTOM_SETTINGS, settings); ctx.ui.notify(`Defluffer animation: ${next}`, "info"); return; } if (cmd === "preview") { const text = restText || (ctx.hasUI ? ctx.ui.getEditorText() : ""); if (!text.trim()) { ctx.ui.notify( "Usage: /defluff preview (or put text in editor)", "warning", ); return; } const result = compressText(text, settings.profile); const output = previewText(result); if (ctx.hasUI) { ctx.ui.setWidget(WIDGET_ID, output.split("\n")); clearWidgetLater(ctx, 6000); } ctx.ui.notify( `Preview: ${result.originalTokens} → ${result.defluffedTokens} est tokens`, "info", ); return; } if (cmd === "stats" || cmd === "status") { const output = statsText(settings, stats); if (ctx.hasUI) { ctx.ui.setWidget(WIDGET_ID, output.split("\n")); clearWidgetLater(ctx, 8000); } ctx.ui.notify( `Defluffer ${settings.enabled ? "on" : "off"}; saved ${stats.savedTokens} est tokens`, "info", ); updateStatus(ctx, settings, stats); return; } if (cmd === "reset-stats") { stats = emptyStats(); updateStatus(ctx, settings, stats); ctx.ui.notify("Defluffer stats reset for current runtime", "info"); return; } ctx.ui.notify( "Usage: /defluff on|off|status|stats|profile|min|preview|animation|reset-stats", "error", ); }, }); }