UNPKG

@vibe-kit/grok-cli

Version:

An open-source AI agent that brings the power of Grok directly into your terminal.

164 lines 9.15 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChatHistory = void 0; const react_1 = __importDefault(require("react")); const ink_1 = require("ink"); const diff_renderer_1 = require("./diff-renderer"); const markdown_renderer_1 = require("../utils/markdown-renderer"); // Memoized ChatEntry component to prevent unnecessary re-renders const MemoizedChatEntry = react_1.default.memo(({ entry, index }) => { const renderDiff = (diffContent, filename) => { return (react_1.default.createElement(diff_renderer_1.DiffRenderer, { diffContent: diffContent, filename: filename, terminalWidth: 80 })); }; const renderFileContent = (content) => { const lines = content.split("\n"); // Calculate minimum indentation like DiffRenderer does let baseIndentation = Infinity; for (const line of lines) { if (line.trim() === "") continue; const firstCharIndex = line.search(/\S/); const currentIndent = firstCharIndex === -1 ? 0 : firstCharIndex; baseIndentation = Math.min(baseIndentation, currentIndent); } if (!isFinite(baseIndentation)) { baseIndentation = 0; } return lines.map((line, index) => { const displayContent = line.substring(baseIndentation); return (react_1.default.createElement(ink_1.Text, { key: index, color: "gray" }, displayContent)); }); }; switch (entry.type) { case "user": return (react_1.default.createElement(ink_1.Box, { key: index, flexDirection: "column", marginTop: 1 }, react_1.default.createElement(ink_1.Box, null, react_1.default.createElement(ink_1.Text, { color: "gray" }, ">", " ", entry.content)))); case "assistant": return (react_1.default.createElement(ink_1.Box, { key: index, flexDirection: "column", marginTop: 1 }, react_1.default.createElement(ink_1.Box, { flexDirection: "row", alignItems: "flex-start" }, react_1.default.createElement(ink_1.Text, { color: "white" }, "\u23FA "), react_1.default.createElement(ink_1.Box, { flexDirection: "column", flexGrow: 1 }, entry.toolCalls ? ( // If there are tool calls, just show plain text react_1.default.createElement(ink_1.Text, { color: "white" }, entry.content.trim())) : ( // If no tool calls, render as markdown react_1.default.createElement(markdown_renderer_1.MarkdownRenderer, { content: entry.content.trim() })), entry.isStreaming && react_1.default.createElement(ink_1.Text, { color: "cyan" }, "\u2588"))))); case "tool_call": case "tool_result": const getToolActionName = (toolName) => { // Handle MCP tools with mcp__servername__toolname format if (toolName.startsWith("mcp__")) { const parts = toolName.split("__"); if (parts.length >= 3) { const serverName = parts[1]; const actualToolName = parts.slice(2).join("__"); return `${serverName.charAt(0).toUpperCase() + serverName.slice(1)}(${actualToolName.replace(/_/g, " ")})`; } } switch (toolName) { case "view_file": return "Read"; case "str_replace_editor": return "Update"; case "create_file": return "Create"; case "bash": return "Bash"; case "search": return "Search"; case "create_todo_list": return "Created Todo"; case "update_todo_list": return "Updated Todo"; default: return "Tool"; } }; const toolName = entry.toolCall?.function?.name || "unknown"; const actionName = getToolActionName(toolName); const getFilePath = (toolCall) => { if (toolCall?.function?.arguments) { try { const args = JSON.parse(toolCall.function.arguments); if (toolCall.function.name === "search") { return args.query; } return args.path || args.file_path || args.command || ""; } catch { return ""; } } return ""; }; const filePath = getFilePath(entry.toolCall); const isExecuting = entry.type === "tool_call" || !entry.toolResult; // Format JSON content for better readability const formatToolContent = (content, toolName) => { if (toolName.startsWith("mcp__")) { try { // Try to parse as JSON and format it const parsed = JSON.parse(content); if (Array.isArray(parsed)) { // For arrays, show a summary instead of full JSON return `Found ${parsed.length} items`; } else if (typeof parsed === 'object') { // For objects, show a formatted version return JSON.stringify(parsed, null, 2); } } catch { // If not JSON, return as is return content; } } return content; }; const shouldShowDiff = entry.toolCall?.function?.name === "str_replace_editor" && entry.toolResult?.success && entry.content.includes("Updated") && entry.content.includes("---") && entry.content.includes("+++"); const shouldShowFileContent = (entry.toolCall?.function?.name === "view_file" || entry.toolCall?.function?.name === "create_file") && entry.toolResult?.success && !shouldShowDiff; return (react_1.default.createElement(ink_1.Box, { key: index, flexDirection: "column", marginTop: 1 }, react_1.default.createElement(ink_1.Box, null, react_1.default.createElement(ink_1.Text, { color: "magenta" }, "\u23FA"), react_1.default.createElement(ink_1.Text, { color: "white" }, " ", filePath ? `${actionName}(${filePath})` : actionName)), react_1.default.createElement(ink_1.Box, { marginLeft: 2, flexDirection: "column" }, isExecuting ? (react_1.default.createElement(ink_1.Text, { color: "cyan" }, "\u23BF Executing...")) : shouldShowFileContent ? (react_1.default.createElement(ink_1.Box, { flexDirection: "column" }, react_1.default.createElement(ink_1.Text, { color: "gray" }, "\u23BF File contents:"), react_1.default.createElement(ink_1.Box, { marginLeft: 2, flexDirection: "column" }, renderFileContent(entry.content)))) : shouldShowDiff ? ( // For diff results, show only the summary line, not the raw content react_1.default.createElement(ink_1.Text, { color: "gray" }, "\u23BF ", entry.content.split("\n")[0])) : (react_1.default.createElement(ink_1.Text, { color: "gray" }, "\u23BF ", formatToolContent(entry.content, toolName)))), shouldShowDiff && !isExecuting && (react_1.default.createElement(ink_1.Box, { marginLeft: 4, flexDirection: "column" }, renderDiff(entry.content, filePath))))); default: return null; } }); MemoizedChatEntry.displayName = "MemoizedChatEntry"; function ChatHistory({ entries, isConfirmationActive = false, }) { // Filter out tool_call entries with "Executing..." when confirmation is active const filteredEntries = isConfirmationActive ? entries.filter((entry) => !(entry.type === "tool_call" && entry.content === "Executing...")) : entries; return (react_1.default.createElement(ink_1.Box, { flexDirection: "column" }, filteredEntries.slice(-20).map((entry, index) => (react_1.default.createElement(MemoizedChatEntry, { key: `${entry.timestamp.getTime()}-${index}`, entry: entry, index: index }))))); } exports.ChatHistory = ChatHistory; //# sourceMappingURL=chat-history.js.map