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

67 lines 3.71 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { Box, Text, useInput } from 'ink'; import { useEffect, useState } from 'react'; import ToolMessage from '../components/tool-message.js'; import { TRUNCATION_OUTPUT_LIMIT } from '../constants.js'; import { useTheme } from '../hooks/useTheme.js'; import { bashExecutor } from '../services/bash-executor.js'; import { calculateTokens } from '../utils/token-calculator.js'; export default function BashProgress({ executionId, command, completedState, isLive = false, }) { const { colors } = useTheme(); // If completedState is provided, use it directly (static mode) const [state, setState] = useState(completedState ?? { executionId, command, outputPreview: '', fullOutput: '', stderr: '', isComplete: false, exitCode: null, error: null, }); // Subscribe to bash executor events (only if not in static mode) useEffect(() => { // Skip event subscription if we have a completed state if (completedState) return; const handleUpdate = (update) => { if (update.executionId === executionId) { setState(update); } }; bashExecutor.on('start', handleUpdate); bashExecutor.on('progress', handleUpdate); bashExecutor.on('complete', handleUpdate); // Get initial state if execution already started const initialState = bashExecutor.getState(executionId); if (initialState) { setState(initialState); } return () => { bashExecutor.off('start', handleUpdate); bashExecutor.off('progress', handleUpdate); bashExecutor.off('complete', handleUpdate); }; }, [executionId, completedState]); // Handle escape key to cancel execution (only if not in static mode) useInput((_input, key) => { if (key.escape && !state.isComplete && !completedState) { bashExecutor.cancel(executionId); } }); // Determine dot color let dotColor = colors.secondary; if (state.isComplete) { dotColor = state.exitCode === 0 && !state.error ? colors.success : colors.error; } // Calculate output stats for completed state (use truncated size to match what LLM receives) const totalOutput = state.fullOutput + state.stderr; const truncatedOutput = totalOutput.length > TRUNCATION_OUTPUT_LIMIT ? totalOutput.substring(0, TRUNCATION_OUTPUT_LIMIT) : totalOutput; const estimatedTokens = calculateTokens(truncatedOutput); const messageContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.tool, children: "\u2692 execute_bash" }), _jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Command: " }), _jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: colors.primary, children: command }) })] }), state.isComplete && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Status: " }), _jsx(Text, { color: dotColor, children: "\u25CF" })] })), !state.isComplete && state.outputPreview && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.secondary, children: "Output: " }), _jsx(Text, { color: colors.text, dimColor: true, children: state.outputPreview })] })), state.isComplete && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Tokens: " }), _jsxs(Text, { color: colors.text, children: ["~", estimatedTokens] })] }))] })); return (_jsx(ToolMessage, { message: messageContent, hideBox: true, isLive: isLive })); } //# sourceMappingURL=bash-progress.js.map