UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

144 lines 6.93 kB
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime"; import { Box, Text } from 'ink'; import React from 'react'; import { ErrorMessage } from '../components/message-box.js'; import ToolMessage from '../components/tool-message.js'; import { useTheme } from '../hooks/useTheme.js'; import { parseToolArguments } from '../utils/tool-args-parser.js'; /** * Tools that should always show expanded (full formatter) output, * even when compact display mode is enabled. */ export const ALWAYS_EXPANDED_TOOLS = new Set([ 'create_task', 'list_tasks', 'update_task', 'delete_task', ]); /** * Compact tool result display - shows "⚒ toolName description" in tool color. */ function CompactToolResult({ toolName, description, }) { const { colors } = useTheme(); return (_jsxs(Text, { color: colors.tool, children: ['\u2692', " ", description] })); } /** * Generate a compact grouped description for N calls of the same tool. * Always uses count-based phrasing for consistency. */ function getGroupedCompactDescription(toolName, count) { const s = count === 1 ? '' : 's'; switch (toolName) { case 'read_file': return `Read ${count} file${s}`; case 'write_file': return `Wrote ${count} file${s}`; case 'string_replace': return `Made ${count} edit${s}`; case 'execute_bash': return `Ran ${count} command${s}`; case 'search_file_contents': return `Searched for ${count} pattern${s}`; case 'find_files': return `Ran ${count} file search${count === 1 ? '' : 'es'}`; case 'list_directory': return `Listed ${count} director${count === 1 ? 'y' : 'ies'}`; case 'web_search': return `Ran ${count} web search${count === 1 ? '' : 'es'}`; case 'fetch_url': return `Fetched ${count} URL${s}`; case 'git_status': case 'git_diff': case 'git_log': return `Ran ${count} git command${s}`; case 'lsp_get_diagnostics': return `Got diagnostics ${count} time${s}`; case 'ask_question': return `Asked ${count} question${s}`; default: return `Executed ${toolName} \u00d7 ${count}`; } } /** * Live display component for running compact tool counts. * Shows accumulated counts during execution (e.g. "⚒ Read 7 files"). * Rendered in the live area (not Static) so it updates in-place. */ export function LiveCompactCounts({ counts }) { const { colors } = useTheme(); return (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: Object.entries(counts).map(([toolName, count]) => (_jsxs(Text, { color: colors.tool, children: ['\u2692', " ", getGroupedCompactDescription(toolName, count)] }, toolName))) })); } /** * Flush accumulated compact counts to the static chat queue. * Called when the conversation loop finishes to persist the summary. */ export function displayCompactCountsSummary(counts, addToChatQueue, getNextComponentKey) { const entries = Object.entries(counts); for (let i = 0; i < entries.length; i++) { const [toolName, count] = entries[i]; const isLast = i === entries.length - 1; const description = getGroupedCompactDescription(toolName, count); if (isLast) { // Last entry gets wrapped in a flex-column Box with marginBottom // to separate from subsequent content (same pattern as ToolMessage) addToChatQueue(_jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsx(CompactToolResult, { toolName: toolName, description: description }) }, `tool-compact-summary-${toolName}-${getNextComponentKey()}`)); } else { addToChatQueue(_jsx(CompactToolResult, { toolName: toolName, description: description }, `tool-compact-summary-${toolName}-${getNextComponentKey()}`)); } } } /** * Display tool result with proper formatting * Extracted to eliminate duplication between useChatHandler and useToolHandler * * @param toolCall - The tool call that was executed * @param result - The result from tool execution * @param toolManager - The tool manager instance (for formatters) * @param addToChatQueue - Function to add components to chat queue * @param getNextComponentKey - Function to generate unique React keys * @param compact - When true, show one-liner instead of full formatter output */ export async function displayToolResult(toolCall, result, toolManager, addToChatQueue, getNextComponentKey, compact) { // Check if this is an error result const isError = result.content.startsWith('Error: '); if (isError) { // Display as error message - always shown in full const errorMessage = result.content.replace(/^Error: /, ''); addToChatQueue(_jsx(ErrorMessage, { message: errorMessage, hideBox: true }, `tool-error-${result.tool_call_id}-${getNextComponentKey()}-${Date.now()}`)); return; } // Compact mode: show count-based one-liner instead of full formatter output // (skip for tools that should always show expanded output) if (compact && !ALWAYS_EXPANDED_TOOLS.has(result.name)) { const description = getGroupedCompactDescription(result.name, 1); addToChatQueue(_jsx(CompactToolResult, { toolName: result.name, description: description }, `tool-compact-${result.tool_call_id}-${getNextComponentKey()}`)); return; } if (toolManager) { const formatter = toolManager.getToolFormatter(result.name); if (formatter) { try { const parsedArgs = parseToolArguments(toolCall.function.arguments); const formattedResult = await formatter(parsedArgs, result.content); if (React.isValidElement(formattedResult)) { addToChatQueue(React.cloneElement(formattedResult, { key: `tool-result-${result.tool_call_id}-${getNextComponentKey()}-${Date.now()}`, })); } else { addToChatQueue(_jsx(ToolMessage, { title: `⚒ ${result.name}`, message: String(formattedResult), hideBox: true }, `tool-result-${result.tool_call_id}-${getNextComponentKey()}-${Date.now()}`)); } } catch { // If formatter fails, show raw result addToChatQueue(_jsx(ToolMessage, { title: `⚒ ${result.name}`, message: result.content, hideBox: true }, `tool-result-${result.tool_call_id}-${getNextComponentKey()}`)); } } else { // No formatter, show raw result addToChatQueue(_jsx(ToolMessage, { title: `⚒ ${result.name}`, message: result.content, hideBox: true }, `tool-result-${result.tool_call_id}-${getNextComponentKey()}`)); } } } //# sourceMappingURL=tool-result-display.js.map