UNPKG

@vibe-kit/grok-cli

Version:

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

330 lines (326 loc) 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useInputHandler = void 0; const react_1 = require("react"); const ink_1 = require("ink"); function useInputHandler({ agent, chatHistory, setChatHistory, setIsProcessing, setIsStreaming, setTokenCount, setProcessingTime, processingStartTime, isProcessing, isStreaming, }) { const [input, setInput] = (0, react_1.useState)(""); const [showCommandSuggestions, setShowCommandSuggestions] = (0, react_1.useState)(false); const [selectedCommandIndex, setSelectedCommandIndex] = (0, react_1.useState)(0); const [showModelSelection, setShowModelSelection] = (0, react_1.useState)(false); const [selectedModelIndex, setSelectedModelIndex] = (0, react_1.useState)(0); const { exit } = (0, ink_1.useApp)(); const commandSuggestions = [ { command: "/help", description: "Show help information" }, { command: "/clear", description: "Clear chat history" }, { command: "/models", description: "Switch Grok Model" }, { command: "/exit", description: "Exit the application" }, ]; const availableModels = [ { model: "grok-4-latest", description: "Latest Grok-4 model (most capable)" }, { model: "grok-3-latest", description: "Latest Grok-3 model" }, { model: "grok-3-fast", description: "Fast Grok-3 variant" }, { model: "grok-3-mini-fast", description: "Fastest Grok-3 variant" } ]; const handleDirectCommand = async (input) => { const trimmedInput = input.trim(); if (trimmedInput === "/clear") { setChatHistory([]); setInput(""); return true; } if (trimmedInput === "/help") { const helpEntry = { type: "assistant", content: `Grok CLI Help: Built-in Commands: /clear - Clear chat history /help - Show this help /models - Switch Grok models /exit - Exit application exit, quit - Exit application Direct Commands (executed immediately): ls [path] - List directory contents pwd - Show current directory cd <path> - Change directory cat <file> - View file contents mkdir <dir> - Create directory touch <file>- Create empty file For complex operations, just describe what you want in natural language. Examples: "edit package.json and add a new script" "create a new React component called Header" "show me all TypeScript files in this project"`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, helpEntry]); setInput(""); return true; } if (trimmedInput === "/models") { setShowModelSelection(true); setSelectedModelIndex(0); setInput(""); return true; } if (trimmedInput.startsWith("/models ")) { const modelArg = trimmedInput.split(" ")[1]; const modelNames = availableModels.map(m => m.model); if (modelNames.includes(modelArg)) { agent.setModel(modelArg); const confirmEntry = { type: "assistant", content: `✓ Switched to model: ${modelArg}`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, confirmEntry]); } else { const errorEntry = { type: "assistant", content: `Invalid model: ${modelArg} Available models: ${modelNames.join(", ")}`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, errorEntry]); } setInput(""); return true; } const directBashCommands = [ "ls", "pwd", "cd", "cat", "mkdir", "touch", "echo", "grep", "find", "cp", "mv", "rm", ]; const firstWord = trimmedInput.split(" ")[0]; if (directBashCommands.includes(firstWord)) { const userEntry = { type: "user", content: trimmedInput, timestamp: new Date(), }; setChatHistory((prev) => [...prev, userEntry]); try { const result = await agent.executeBashCommand(trimmedInput); const commandEntry = { type: "tool_result", content: result.success ? result.output || "Command completed" : result.error || "Command failed", timestamp: new Date(), toolCall: { id: `bash_${Date.now()}`, type: "function", function: { name: "bash", arguments: JSON.stringify({ command: trimmedInput }), }, }, toolResult: result, }; setChatHistory((prev) => [...prev, commandEntry]); } catch (error) { const errorEntry = { type: "assistant", content: `Error executing command: ${error.message}`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, errorEntry]); } setInput(""); return true; } return false; }; const processUserMessage = async (userInput) => { const userEntry = { type: "user", content: userInput, timestamp: new Date(), }; setChatHistory((prev) => [...prev, userEntry]); setIsProcessing(true); setInput(""); try { setIsStreaming(true); let streamingEntry = null; for await (const chunk of agent.processUserMessageStream(userInput)) { switch (chunk.type) { case "content": if (chunk.content) { if (!streamingEntry) { const newStreamingEntry = { type: "assistant", content: chunk.content, timestamp: new Date(), isStreaming: true, }; setChatHistory((prev) => [...prev, newStreamingEntry]); streamingEntry = newStreamingEntry; } else { setChatHistory((prev) => prev.map((entry, idx) => idx === prev.length - 1 && entry.isStreaming ? { ...entry, content: entry.content + chunk.content } : entry)); } } break; case "token_count": if (chunk.tokenCount !== undefined) { setTokenCount(chunk.tokenCount); } break; case "tool_calls": break; case "tool_result": if (chunk.toolCall && chunk.toolResult) { setChatHistory((prev) => prev.map((entry) => entry.isStreaming ? { ...entry, isStreaming: false } : entry)); const toolResultEntry = { type: "tool_result", content: chunk.toolResult.success ? chunk.toolResult.output || "Success" : chunk.toolResult.error || "Error occurred", timestamp: new Date(), toolCall: chunk.toolCall, toolResult: chunk.toolResult, }; setChatHistory((prev) => [...prev, toolResultEntry]); streamingEntry = null; } break; case "done": if (streamingEntry) { setChatHistory((prev) => prev.map((entry) => entry.isStreaming ? { ...entry, isStreaming: false } : entry)); } setIsStreaming(false); break; } } } catch (error) { const errorEntry = { type: "assistant", content: `Error: ${error.message}`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, errorEntry]); setIsStreaming(false); } setIsProcessing(false); processingStartTime.current = 0; }; (0, ink_1.useInput)(async (inputChar, key) => { if (key.ctrl && inputChar === "c") { exit(); return; } if (key.escape) { if (showCommandSuggestions) { setShowCommandSuggestions(false); setSelectedCommandIndex(0); return; } if (showModelSelection) { setShowModelSelection(false); setSelectedModelIndex(0); return; } if (isProcessing || isStreaming) { setIsProcessing(false); setIsStreaming(false); setTokenCount(0); setProcessingTime(0); processingStartTime.current = 0; return; } } if (showCommandSuggestions) { if (key.upArrow) { setSelectedCommandIndex((prev) => prev === 0 ? commandSuggestions.length - 1 : prev - 1); return; } if (key.downArrow) { setSelectedCommandIndex((prev) => (prev + 1) % commandSuggestions.length); return; } if (key.tab || key.return) { const selectedCommand = commandSuggestions[selectedCommandIndex]; setInput(selectedCommand.command + " "); setShowCommandSuggestions(false); setSelectedCommandIndex(0); return; } } if (showModelSelection) { if (key.upArrow) { setSelectedModelIndex((prev) => prev === 0 ? availableModels.length - 1 : prev - 1); return; } if (key.downArrow) { setSelectedModelIndex((prev) => (prev + 1) % availableModels.length); return; } if (key.tab || key.return) { const selectedModel = availableModels[selectedModelIndex]; agent.setModel(selectedModel.model); const confirmEntry = { type: "assistant", content: `✓ Switched to model: ${selectedModel.model}`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, confirmEntry]); setShowModelSelection(false); setSelectedModelIndex(0); return; } } if (key.return) { const userInput = input.trim(); if (userInput === "exit" || userInput === "quit") { exit(); return; } if (userInput) { const directCommandResult = await handleDirectCommand(userInput); if (!directCommandResult) { await processUserMessage(userInput); } } return; } if (key.backspace || key.delete) { const newInput = input.slice(0, -1); setInput(newInput); if (!newInput.startsWith("/")) { setShowCommandSuggestions(false); setSelectedCommandIndex(0); } return; } if (inputChar && !key.ctrl && !key.meta) { const newInput = input + inputChar; setInput(newInput); if (newInput === "/" || ["ls", "pwd", "cd", "cat", "mkdir", "touch"].some((cmd) => cmd.startsWith(newInput))) { setShowCommandSuggestions(true); setSelectedCommandIndex(0); } else if (!newInput.startsWith("/") && !["ls", "pwd", "cd", "cat", "mkdir", "touch"].some((cmd) => cmd.startsWith(newInput))) { setShowCommandSuggestions(false); setSelectedCommandIndex(0); } } }); return { input, showCommandSuggestions, selectedCommandIndex, showModelSelection, selectedModelIndex, commandSuggestions, availableModels, agent, }; } exports.useInputHandler = useInputHandler; //# sourceMappingURL=use-input-handler.js.map