import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { loadConfig, type QuietToolsState } from "./config"; import { compactText, policyFor, shouldCompact } from "./compact"; import { readLastArtifacts, writeArtifact } from "./artifacts"; function textParts(content: any[]): string[] { return (content || []) .filter((part) => part && part.type === "text" && typeof part.text === "string") .map((part) => part.text); } function shouldCompactTool(toolName: string, config: ReturnType): boolean { if (toolName === "read") return config.compactRead; if (toolName === "bash") return config.compactBash; if (toolName === "subagent") return config.compactSubagent; return true; } function inputSummary(input: unknown): string { try { const json = JSON.stringify(input ?? {}); return json.length > 500 ? `${json.slice(0, 500)}…` : json; } catch { return ""; } } export default function quietTools(pi: ExtensionAPI) { const state: QuietToolsState = { enabled: loadConfig().enabled, bypassNext: false }; let warned = false; pi.on("tool_call", async (event) => { const config = loadConfig(); if (!state.enabled || !config.compactRead) return; if (event.toolName !== "read") return; const input = event.input as { limit?: number; offset?: number } | undefined; if (!input || input.limit !== undefined || config.readDefaultLimit <= 0) return; input.limit = config.readDefaultLimit; }); pi.on("tool_result", async (event, ctx) => { try { const config = loadConfig(); if (!state.enabled || !config.enabled) return; if (state.bypassNext) { state.bypassNext = false; return; } if (!shouldCompactTool(event.toolName, config)) return; const texts = textParts(event.content as any[]); if (texts.length === 0) return; const fullText = texts.join("\n"); if (!shouldCompact(fullText, config)) return; const compact = compactText(fullText, policyFor(config, Boolean(event.isError))); const artifact = await writeArtifact({ cwd: ctx.cwd, config, toolName: event.toolName, toolCallId: event.toolCallId, text: fullText, lines: compact.originalLines, }); const notice = [ `[quiet-tools] Compacted ${event.toolName} output.`, `Input: ${inputSummary(event.input)}`, `Original: ${compact.originalLines} lines, ${compact.originalChars} chars.`, `Preview: ${compact.previewLines} lines, ${compact.previewChars} chars.`, `Full output saved; read this path if needed: ${artifact.artifactPath}`, "", compact.text, ].join("\n"); const nonText = ((event.content as any[]) || []).filter((part) => part?.type !== "text"); return { content: [...nonText, { type: "text", text: notice }], details: { ...(event.details || {}), quietTools: { compacted: true, artifactPath: artifact.artifactPath, originalChars: compact.originalChars, originalLines: compact.originalLines, previewChars: compact.previewChars, previewLines: compact.previewLines, }, }, }; } catch (error) { if (!warned && ctx.hasUI) { warned = true; const message = error instanceof Error ? error.message : String(error); ctx.ui.notify(`quiet-tools failed open: ${message}`, "warning"); } return; } }); pi.registerCommand("quiet-tools", { description: "Quiet Tools controls: status, on, off, bypass-next, last [n]", handler: async (args, ctx) => { const [sub = "status", maybeCount] = (args || "").trim().split(/\s+/).filter(Boolean); const config = loadConfig(); switch (sub) { case "on": state.enabled = true; ctx.ui.notify("quiet-tools enabled", "info"); break; case "off": state.enabled = false; ctx.ui.notify("quiet-tools disabled", "info"); break; case "bypass-next": state.bypassNext = true; ctx.ui.notify("quiet-tools will bypass the next compactable tool result", "info"); break; case "last": { const count = Math.max(1, Number.parseInt(maybeCount || "5", 10) || 5); const rows = await readLastArtifacts(ctx.cwd, config, count); const text = rows.length === 0 ? "No quiet-tools artifacts found." : rows.map((row) => `${row.timestamp} ${row.toolName} ${row.lines} lines ${row.artifactPath}`).join("\n"); ctx.ui.notify(text, "info"); break; } case "status": ctx.ui.notify( `quiet-tools enabled=${state.enabled && config.enabled} bypassNext=${state.bypassNext} max=${config.maxLines} lines/${config.maxChars} chars artifacts=${config.artifactDir}`, "info", ); break; default: ctx.ui.notify("Usage: /quiet-tools status|on|off|bypass-next|last [n]", "warning"); } }, }); }