import { computeContextBreakdown } from "../../modes/utils/context-usage"; import type { SlashCommandRuntime } from "../types"; import { renderAsciiBar } from "./format"; /** * Build the `/context` ACP-mode text. Tries the rich breakdown first * (categories + auto-compact buffer + free slack) and falls back to the * minimal "window/used" lines when the breakdown helper throws. */ export function buildContextReportText(runtime: SlashCommandRuntime): string { try { const breakdown = computeContextBreakdown(runtime.session); if (breakdown.contextWindow <= 0) { return "Context usage is unavailable: no model is selected for this session."; } const usedPct = Math.round((breakdown.usedTokens / breakdown.contextWindow) * 100); const lines = [`Context window: ${breakdown.contextWindow} tokens (${usedPct}% used)`]; for (const category of breakdown.categories) { if (category.tokens === 0) continue; const fraction = category.tokens / breakdown.contextWindow; lines.push(` ${category.label.padEnd(16)} ${renderAsciiBar(fraction)} ${category.tokens} tokens`); } if (breakdown.autoCompactBufferTokens > 0) { const fraction = breakdown.autoCompactBufferTokens / breakdown.contextWindow; lines.push( ` ${"Auto-compact buf".padEnd(16)} ${renderAsciiBar(fraction)} ${breakdown.autoCompactBufferTokens} tokens`, ); } if (breakdown.freeTokens > 0) { const fraction = breakdown.freeTokens / breakdown.contextWindow; lines.push(` ${"Free".padEnd(16)} ${renderAsciiBar(fraction)} ${breakdown.freeTokens} tokens`); } return lines.join("\n"); } catch { const fallback = runtime.session.getContextUsage(); if (!fallback) return "Context usage is unavailable."; return ["Context", `Window: ${fallback.contextWindow}`, `Used: ${fallback.tokens ?? 0}`].join("\n"); } }