UNPKG

@earendil-works/pi-coding-agent

Version:

Coding agent CLI with read, bash, edit, write tools and session management

89 lines • 4.24 kB
/** * Presentation for the shell tools. * * Renderers live apart from the implementation so a process that only displays tool output does not * load the execution path or its typebox parameter schema. `bash.ts` spreads these into the shell * tool definition, so the tool's public shape is unchanged. */ import { Container, Spacer, Text } from "@earendil-works/pi-tui"; import { keyHint } from "../../../modes/interactive/components/keybinding-hints.js"; import { VisualLinePreview } from "../../../modes/interactive/components/visual-truncate.js"; import { theme } from "../../../modes/interactive/theme/theme.js"; import { getTextOutput, invalidArgText, startToolTimer, str, toolTimerText } from "../render-utils.js"; import { DEFAULT_MAX_BYTES, formatSize } from "../truncate.js"; const BASH_PREVIEW_LINES = 5; export const BASH_UPDATE_THROTTLE_MS = 100; function formatShellCall(args, prompt) { const command = str(args?.command); const timeout = args?.timeout; const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : ""; const commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg("toolOutput", "..."); return theme.fg("toolTitle", theme.bold(`${prompt} ${commandDisplay}`)) + timeoutSuffix; } function rebuildBashResultRenderComponent(component, result, options, showImages, timerText) { component.clear(); let output = getTextOutput(result, showImages).trim(); const truncation = result.details?.truncation; const fullOutputPath = result.details?.fullOutputPath; if (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith("]")) { const footerStart = output.lastIndexOf("\n\n["); if (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) { output = output.slice(0, footerStart).trimEnd(); } } if (output) { const styledOutput = output .split("\n") .map((line) => theme.fg("toolOutput", line)) .join("\n"); if (options.expanded) { component.addChild(new Text(`\n${styledOutput}`, 0, 0)); } else { component.addChild(new Spacer(1)); component.addChild(new VisualLinePreview({ text: styledOutput, maxVisualLines: BASH_PREVIEW_LINES, keep: "end", formatHint: (hidden) => theme.fg("muted", `... (${hidden} earlier lines,`) + ` ${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`, })); } } if (truncation?.truncated || fullOutputPath) { const warnings = []; if (fullOutputPath) { warnings.push(`Full output: ${fullOutputPath}`); } if (truncation?.truncated) { if (truncation.truncatedBy === "lines") { warnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`); } else { warnings.push(`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`); } } component.addChild(new Text(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`, 0, 0)); } if (timerText) component.addChild(new Text(`\n${theme.fg("muted", timerText)}`, 0, 0)); } /** Shell renderers are shared by bash and powershell, which differ only in the prompt they display. */ export function createShellRenderers(prompt) { return { renderCall(args, _theme, context) { startToolTimer(context); const text = context.lastComponent ?? new Text("", 0, 0); text.setText(formatShellCall(args, prompt)); return text; }, renderResult(result, options, _theme, context) { const timerText = toolTimerText(context, options.isPartial); const component = context.lastComponent ?? new Container(); rebuildBashResultRenderComponent(component, result, options, context.showImages, timerText); component.invalidate(); return component; }, }; } //# sourceMappingURL=bash.js.map