@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
JavaScript
;
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