UNPKG

giga-code

Version:

A personal AI CLI assistant powered by Grok for local development.

1,140 lines (1,135 loc) 48.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.useInputHandler = void 0; const react_1 = require("react"); const ink_1 = require("ink"); const confirmation_service_1 = require("../utils/confirmation-service"); const added_mcp_servers_1 = require("../utils/added-mcp-servers"); const fuzzy_search_1 = require("../utils/fuzzy-search"); const instance_models_1 = require("../utils/instance-models"); const file_finder_1 = require("../utils/file-finder"); const added_models_1 = require("../utils/added-models"); const session_manager_1 = require("../utils/session-manager"); const mode_manager_1 = require("../utils/mode-manager"); // Helper function to get OpenRouter models consistently const getOpenRouterModels = (models) => { return models.filter(model => model.description.includes('(OpenRouter)')); }; function useInputHandler({ agent, chatHistory, setChatHistory, setIsProcessing, setIsStreaming, setTokenCount, setProcessingTime, setStatusMessage, processingStartTime, isProcessing, isStreaming, isConfirmationActive = false, onModeChange, }) { 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 [showProviderSettings, setShowProviderSettings] = (0, react_1.useState)(false); const [selectedProviderIndex, setSelectedProviderIndex] = (0, react_1.useState)(0); const [showAddModel, setShowAddModel] = (0, react_1.useState)(false); const [showDeleteModel, setShowDeleteModel] = (0, react_1.useState)(false); const [showPromptsList, setShowPromptsList] = (0, react_1.useState)(false); const [showAddPrompt, setShowAddPrompt] = (0, react_1.useState)(false); const [showDeletePrompt, setShowDeletePrompt] = (0, react_1.useState)(false); const [showMcpServers, setShowMcpServers] = (0, react_1.useState)(false); const [showAddMcpServer, setShowAddMcpServer] = (0, react_1.useState)(false); const [showDeleteMcpServer, setShowDeleteMcpServer] = (0, react_1.useState)(false); const [selectedMcpServerIndex, setSelectedMcpServerIndex] = (0, react_1.useState)(0); const [dynamicModels, setDynamicModels] = (0, react_1.useState)([]); const [allModels, setAllModels] = (0, react_1.useState)([]); const [mcpServers, setMcpServers] = (0, react_1.useState)([]); const [showConversationHistory, setShowConversationHistory] = (0, react_1.useState)(false); const [showTemperatureSelector, setShowTemperatureSelector] = (0, react_1.useState)(false); const [currentTemperature, setCurrentTemperature] = (0, react_1.useState)(0.7); const [showExpertModels, setShowExpertModels] = (0, react_1.useState)(false); const [showExaApiKeyInput, setShowExaApiKeyInput] = (0, react_1.useState)(false); // Route selection state const [showRouteSelection, setShowRouteSelection] = (0, react_1.useState)(false); const [routeViewMode, setRouteViewMode] = (0, react_1.useState)('models'); const [selectedRouteModelIndex, setSelectedRouteModelIndex] = (0, react_1.useState)(0); const [selectedRouteProviderIndex, setSelectedRouteProviderIndex] = (0, react_1.useState)(0); const [currentSelectedModel, setCurrentSelectedModel] = (0, react_1.useState)(''); const [routeProviders, setRouteProviders] = (0, react_1.useState)([]); const [isLoadingProviders, setIsLoadingProviders] = (0, react_1.useState)(false); // Command history state const [commandHistory, setCommandHistory] = (0, react_1.useState)([]); const [historyIndex, setHistoryIndex] = (0, react_1.useState)(-1); const [temporaryInput, setTemporaryInput] = (0, react_1.useState)(""); // File finder state const [showFileFinder, setShowFileFinder] = (0, react_1.useState)(false); const [selectedFileIndex, setSelectedFileIndex] = (0, react_1.useState)(0); const [availableFiles, setAvailableFiles] = (0, react_1.useState)([]); const [filteredFiles, setFilteredFiles] = (0, react_1.useState)([]); const [fileQuery, setFileQuery] = (0, react_1.useState)(""); // Streaming content batching state const streamingBufferRef = (0, react_1.useRef)(""); const batchTimeoutRef = (0, react_1.useRef)(null); // Double Ctrl+C detection as fallback const lastCtrlCRef = (0, react_1.useRef)(0); const ctrlCTimeoutRef = (0, react_1.useRef)(null); // Batched state update for streaming content const flushStreamingBuffer = (0, react_1.useCallback)(() => { if (streamingBufferRef.current) { const content = streamingBufferRef.current; streamingBufferRef.current = ""; setChatHistory((prev) => prev.map((entry, idx) => idx === prev.length - 1 && entry.isStreaming ? { ...entry, content: entry.content + content } : entry)); } if (batchTimeoutRef.current) { clearTimeout(batchTimeoutRef.current); batchTimeoutRef.current = null; } }, [setChatHistory]); // Debounced streaming content update const addStreamingContent = (0, react_1.useCallback)((content) => { streamingBufferRef.current += content; if (batchTimeoutRef.current) { clearTimeout(batchTimeoutRef.current); } batchTimeoutRef.current = setTimeout(() => { setImmediate(flushStreamingBuffer); }, 16); // 16ms debounce for ~60fps updates }, [flushStreamingBuffer]); // Helper function to add command to history const addToHistory = (command) => { if (command.trim() && command !== commandHistory[commandHistory.length - 1]) { setCommandHistory(prev => [...prev, command.trim()]); } setHistoryIndex(-1); setTemporaryInput(""); }; const closeProviderSettings = () => { setShowProviderSettings(false); setSelectedProviderIndex(0); }; const closeAddModel = () => { setShowAddModel(false); }; const closeDeleteModel = () => { setShowDeleteModel(false); }; const closePromptsList = () => { setShowPromptsList(false); }; const closeAddPrompt = () => { setShowAddPrompt(false); }; const closeDeletePrompt = () => { setShowDeletePrompt(false); }; const closeMcpServers = () => { setShowMcpServers(false); setSelectedMcpServerIndex(0); }; const closeAddMcpServer = () => { setShowAddMcpServer(false); }; const closeDeleteMcpServer = () => { setShowDeleteMcpServer(false); }; const closeRouteSelection = () => { setShowRouteSelection(false); setRouteViewMode('models'); setSelectedRouteModelIndex(0); setSelectedRouteProviderIndex(0); setCurrentSelectedModel(''); setRouteProviders([]); setIsLoadingProviders(false); }; const closeConversationHistory = () => { setShowConversationHistory(false); }; const closeTemperatureSelector = () => { setShowTemperatureSelector(false); }; const closeExpertModels = () => { setShowExpertModels(false); }; const closeExaApiKeyInput = () => { setShowExaApiKeyInput(false); }; const closeFileFinder = () => { setShowFileFinder(false); setSelectedFileIndex(0); setFilteredFiles([]); setFileQuery(""); }; const updateFileFinder = (currentInput) => { const queryInfo = (0, file_finder_1.extractFileQuery)(currentInput); if (queryInfo) { const { query, isDirectory } = queryInfo; setFileQuery(query); if (isDirectory) { // Handle directory search - use simple filtering const filtered = (0, file_finder_1.getFilteredItems)(availableFiles, query, true); setFilteredFiles(filtered); setShowFileFinder(true); // Always show when @ query is active setSelectedFileIndex(0); } else { // Handle file search if (query === '') { // Just @ typed, show recent/common files const recentFiles = availableFiles .filter(file => !file.isDirectory) .map(file => file.relativePath) .sort() .slice(0, 10); setFilteredFiles(recentFiles); } else { // Use fuzzy search for better matching const filtered = (0, fuzzy_search_1.fuzzySearch)(query, availableFiles.filter(file => !file.isDirectory), (file) => file.relativePath, 10).map(file => file.relativePath); setFilteredFiles(filtered); } setShowFileFinder(true); // Always show when @ query is active setSelectedFileIndex(0); } } else { setShowFileFinder(false); setFilteredFiles([]); setFileQuery(""); } }; const refreshModels = (allModelsData) => { if (allModelsData) { setAllModels(allModelsData); } const instanceModels = (0, instance_models_1.getInstanceAvailableModels)(); const modelOptions = instanceModels.map(model => ({ model: model.model, description: `${model.description}${model.isFavorite ? ' ⭐' : ''}${model.isRecentlyUsed ? ' 🕒' : ''}` })); setDynamicModels(modelOptions); }; const refreshMcpServers = () => { const addedServers = (0, added_mcp_servers_1.loadAddedMcpServers)(); setMcpServers(addedServers); }; // Helper function to get current filtered suggestions based on input const getFilteredSuggestions = (currentInput) => { return currentInput.startsWith("/") ? (0, fuzzy_search_1.fuzzySearch)(currentInput.substring(1), // Remove the "/" for fuzzy matching commandSuggestions.filter(s => s.command.startsWith("/")), (suggestion) => suggestion.command.substring(1), // Remove "/" for matching 8) : (0, fuzzy_search_1.fuzzySearch)(currentInput, commandSuggestions, (suggestion) => suggestion.command, 8); }; (0, react_1.useEffect)(() => { refreshModels(); refreshMcpServers(); setCurrentTemperature(session_manager_1.sessionManager.getTemperature()); // Load available files try { const files = (0, file_finder_1.getAllFiles)(); setAvailableFiles(files); } catch (error) { console.error('Failed to load files:', error); } // Cleanup function to clear any pending timeouts return () => { if (batchTimeoutRef.current) { clearTimeout(batchTimeoutRef.current); batchTimeoutRef.current = null; } if (ctrlCTimeoutRef.current) { clearTimeout(ctrlCTimeoutRef.current); ctrlCTimeoutRef.current = null; } }; }, []); const { exit } = (0, ink_1.useApp)(); const commandSuggestions = [ { command: "/help", description: "Show help information" }, { command: "/intro", description: "Show getting started information" }, { command: "/clear", description: "Start new conversation" }, { command: "/history", description: "Browse conversation history" }, { command: "/models", description: "Switch Grok Model" }, { command: "/route", description: "Configure model provider routing" }, { command: "/add-model", description: "Add models from providers" }, { command: "/delete-model", description: "Delete added models" }, { command: "/prompts", description: "View custom prompts" }, { command: "/add-prompt", description: "Add custom prompt" }, { command: "/delete-prompt", description: "Delete custom prompt" }, { command: "/mcps", description: "View MCP servers" }, { command: "/add-mcp", description: "Add MCP server" }, { command: "/delete-mcp", description: "Delete MCP server" }, { command: "/sampling", description: "Adjust sampling temperature" }, { command: "/experts", description: "Configure expert model routing" }, { command: "/providers", description: "Configure API Keys" }, { command: "/exa", description: "Set your EXA API key for search" }, { command: "/exit", description: "Exit the application" }, ]; const availableModels = []; const providerList = [ { name: "OpenRouter", keyName: "openRouterApiKey", description: "OpenRouter API (Multi-model access)" }, { name: "Anthropic", keyName: "anthropicApiKey", description: "Claude models" }, { name: "Google", keyName: "googleApiKey", description: "Gemini models" }, { name: "xAI", keyName: "xaiApiKey", description: "Grok models" }, { name: "Groq", keyName: "groqApiKey", description: "Fast inference" }, { name: "Cerebras", keyName: "cerebrasApiKey", description: "Cerebras models" }, { name: "OpenAI", keyName: "openaiApiKey", description: "GPT models" }, { name: "Ollama", keyName: "ollamaBaseUrl", description: "Local Ollama models" }, ]; const handleDirectCommand = async (input) => { const trimmedInput = input.trim(); if (trimmedInput === "/clear") { // Clear console and show GIGA art again console.clear(); const cfonts = require('cfonts'); cfonts.say("GIGA", { font: "3d", align: "left", colors: ["magenta", "gray"], space: true, maxLength: "0", gradient: ["magenta", "cyan"], independentGradient: false, transitionGradient: true, env: "node", }); // Reset chat history setChatHistory([]); // Reset processing states setIsProcessing(false); setIsStreaming(false); setTokenCount(0); setProcessingTime(0); processingStartTime.current = 0; // Reset confirmation service session flags const confirmationService = confirmation_service_1.ConfirmationService.getInstance(); confirmationService.resetSession(); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/intro") { const introEntry = { type: "assistant", content: `Getting started: 1. First time? Configure API keys: /providers 2. To enable web search, set your EXA API key: /exa 3. Add models from your providers: /add-model 4. Select your preferred model: /models 5. Ask questions, edit files, or run commands! Need help? Type /help for more information.`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, introEntry]); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/help") { const helpEntry = { type: "assistant", content: `GIGA Help: Built-in Commands: /clear - Start new conversation /help - Show this help /intro - Show getting started information /history - Browse conversation history (Ctrl+H) /models - Switch models /route - Configure model provider routing /add-model - Add models from providers /delete-model - Delete added models /prompts - View custom prompts /add-prompt - Add custom prompt /delete-prompt- Delete custom prompt /mcps - View MCP servers /add-mcp - Add MCP server /delete-mcp - Delete MCP server /sampling - Adjust sampling temperature /experts - Configure expert model routing /providers - Configure API keys /exa - Set your EXA API key for search /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]); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/history") { setShowConversationHistory(true); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/models") { setShowModelSelection(true); setSelectedModelIndex(0); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/providers") { setShowProviderSettings(true); setSelectedProviderIndex(0); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/add-model") { setShowAddModel(true); addToHistory(trimmedInput); setInput(""); // Fetch models when the add model dialog is opened const { fetchModelsWithFallback } = await Promise.resolve().then(() => __importStar(require('../utils/dynamic-model-fetcher'))); const { loadApiKeys } = await Promise.resolve().then(() => __importStar(require('../utils/api-keys'))); const keys = loadApiKeys(); const result = await fetchModelsWithFallback('openrouter', keys.openRouterApiKey || ''); if (result.allModels) { refreshModels(result.allModels); } return true; } if (trimmedInput === "/delete-model") { setShowDeleteModel(true); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/mcps") { setShowMcpServers(true); setSelectedMcpServerIndex(0); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/add-mcp") { setShowAddMcpServer(true); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/delete-mcp") { setShowDeleteMcpServer(true); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/prompts") { setShowPromptsList(true); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/add-prompt") { setShowAddPrompt(true); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/delete-prompt") { setShowDeletePrompt(true); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/route") { setShowRouteSelection(true); setRouteViewMode('models'); setSelectedRouteModelIndex(0); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/sampling") { setShowTemperatureSelector(true); setCurrentTemperature(session_manager_1.sessionManager.getTemperature()); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/experts") { setShowExpertModels(true); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput === "/exa") { setShowExaApiKeyInput(true); addToHistory(trimmedInput); setInput(""); return true; } if (trimmedInput.startsWith("/models ")) { const modelArg = trimmedInput.split(" ")[1]; const modelNames = dynamicModels.map(m => m.model); if (modelNames.includes(modelArg)) { (0, instance_models_1.onModelSelected)(modelArg); agent.setModel(modelArg, allModels); 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]); } addToHistory(trimmedInput); 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]); } addToHistory(trimmedInput); setInput(""); return true; } return false; }; const processUserMessage = async (userInput) => { const userEntry = { type: "user", content: userInput, timestamp: new Date(), }; setChatHistory((prev) => [...prev, userEntry]); addToHistory(userInput); 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 { // Use batched streaming content update addStreamingContent(chunk.content); } } break; case "token_count": if (chunk.tokenCount !== undefined) { setTokenCount(chunk.tokenCount); } break; case "tool_calls": if (chunk.toolCalls) { // Stop streaming for the current assistant message setChatHistory((prev) => prev.map((entry) => entry.isStreaming ? { ...entry, isStreaming: false, toolCalls: chunk.toolCalls } : entry)); streamingEntry = null; } 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": // Flush any remaining buffered content flushStreamingBuffer(); 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; }; const handleRouteSelectionInput = async (inputChar, key) => { if (key.escape) { if (routeViewMode === 'providers') { // Go back to models view setRouteViewMode('models'); setSelectedRouteProviderIndex(0); setCurrentSelectedModel(''); setRouteProviders([]); setIsLoadingProviders(false); } else { // Close route selection entirely closeRouteSelection(); } return; } if (routeViewMode === 'models') { // Filter to only show OpenRouter models (same as the component filter) const openRouterModels = getOpenRouterModels(dynamicModels); if (key.upArrow) { setSelectedRouteModelIndex((prev) => prev === 0 ? openRouterModels.length - 1 : prev - 1); return; } if (key.downArrow) { setSelectedRouteModelIndex((prev) => (prev + 1) % openRouterModels.length); return; } if (key.return || key.tab) { const selectedModel = openRouterModels[selectedRouteModelIndex]; console.log(`DEBUG: Selected model index: ${selectedRouteModelIndex}, Model: ${selectedModel?.model}`); if (selectedModel) { // Move to provider selection setCurrentSelectedModel(selectedModel.model); setRouteViewMode('providers'); setSelectedRouteProviderIndex(0); setIsLoadingProviders(true); setRouteProviders([]); try { const { loadApiKeys } = await Promise.resolve().then(() => __importStar(require('../utils/api-keys'))); const { getModelProvidersWithFallback } = await Promise.resolve().then(() => __importStar(require('../utils/openrouter-providers'))); const apiKeys = loadApiKeys(); const openRouterKey = apiKeys.openRouterApiKey; if (openRouterKey) { const modelProviders = await getModelProvidersWithFallback(selectedModel.model, openRouterKey); setRouteProviders(modelProviders); } else { // Show error message const errorEntry = { type: "assistant", content: "OpenRouter API key is required to fetch providers. Please configure it in /providers.", timestamp: new Date(), }; setChatHistory((prev) => [...prev, errorEntry]); closeRouteSelection(); } } catch (error) { console.error('Error fetching providers:', error); setRouteProviders([]); } finally { setIsLoadingProviders(false); } } return; } } else if (routeViewMode === 'providers') { if (key.upArrow) { setSelectedRouteProviderIndex((prev) => prev === 0 ? routeProviders.length - 1 : prev - 1); return; } if (key.downArrow) { setSelectedRouteProviderIndex((prev) => (prev + 1) % routeProviders.length); return; } if (key.return || key.tab) { const selectedProvider = routeProviders[selectedRouteProviderIndex]; console.log(`DEBUG: Setting provider ${selectedProvider?.id} for model ${currentSelectedModel}`); if (selectedProvider && currentSelectedModel) { // Save OpenRouter provider preference to the global models file const success = (0, added_models_1.setOpenRouterProvider)(currentSelectedModel, selectedProvider.id); console.log(`DEBUG: setOpenRouterProvider result: ${success}`); if (success) { // Show confirmation message const confirmEntry = { type: "assistant", content: `✓ Set OpenRouter provider for ${currentSelectedModel}: ${selectedProvider.name}`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, confirmEntry]); } else { const errorEntry = { type: "assistant", content: `❌ Failed to set provider for ${currentSelectedModel} - model not found in OpenRouter`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, errorEntry]); } closeRouteSelection(); } return; } } }; (0, ink_1.useInput)(async (inputChar, key) => { // Don't handle input if confirmation dialog or prompt dialogs are active if (isConfirmationActive || showAddPrompt || showDeletePrompt || showPromptsList || showRouteSelection || showConversationHistory || showTemperatureSelector || showExpertModels || showExaApiKeyInput) { // Special handling for route selection if (showRouteSelection) { await handleRouteSelectionInput(inputChar, key); } // Special handling for temperature selector if (showTemperatureSelector) { if (key.escape) { closeTemperatureSelector(); return; } if (key.leftArrow) { const newTemp = Math.max(0.0, currentTemperature - 0.1); setCurrentTemperature(Math.round(newTemp * 10) / 10); return; } if (key.rightArrow) { const newTemp = Math.min(1.0, currentTemperature + 0.1); setCurrentTemperature(Math.round(newTemp * 10) / 10); return; } if (key.return) { session_manager_1.sessionManager.setTemperature(currentTemperature); const confirmEntry = { type: "assistant", content: `✓ Temperature set to ${currentTemperature.toFixed(1)}`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, confirmEntry]); closeTemperatureSelector(); return; } } return; } // Ctrl+C is handled by the process-level handler for immediate exit if (key.ctrl && inputChar === "c") { // Let the process-level handler deal with it return; } // Handle Shift+Tab for mode cycling if (key.shift && key.tab) { const newMode = mode_manager_1.modeManager.cycleMode(); agent.updateMode(newMode); if (onModeChange) { onModeChange(newMode); } // Mode change will be reflected in the SessionStatus component automatically // No need to add chat history entries for mode switches return; } if (key.ctrl && inputChar === "h") { setShowConversationHistory(true); return; } if (key.escape) { if (showCommandSuggestions) { setShowCommandSuggestions(false); setSelectedCommandIndex(0); return; } if (showModelSelection) { setShowModelSelection(false); setSelectedModelIndex(0); return; } if (showProviderSettings) { setShowProviderSettings(false); setSelectedProviderIndex(0); return; } if (showAddModel) { setShowAddModel(false); return; } if (showDeleteModel) { setShowDeleteModel(false); return; } if (showPromptsList) { setShowPromptsList(false); return; } if (showAddPrompt) { setShowAddPrompt(false); return; } if (showDeletePrompt) { setShowDeletePrompt(false); return; } if (showConversationHistory) { setShowConversationHistory(false); return; } if (showTemperatureSelector) { setShowTemperatureSelector(false); return; } if (showExpertModels) { setShowExpertModels(false); return; } if (showExaApiKeyInput) { closeExaApiKeyInput(); return; } if (showMcpServers) { setShowMcpServers(false); setSelectedMcpServerIndex(0); return; } if (showAddMcpServer) { setShowAddMcpServer(false); return; } if (showDeleteMcpServer) { setShowDeleteMcpServer(false); return; } if (isProcessing || isStreaming) { agent.abortCurrentOperation(); setIsProcessing(false); setIsStreaming(false); setTokenCount(0); setProcessingTime(0); processingStartTime.current = 0; return; } } if (showCommandSuggestions) { const filteredSuggestions = getFilteredSuggestions(input); if (key.upArrow) { setSelectedCommandIndex((prev) => prev === 0 ? filteredSuggestions.length - 1 : prev - 1); return; } if (key.downArrow) { setSelectedCommandIndex((prev) => (prev + 1) % filteredSuggestions.length); return; } if (key.tab || key.return) { const selectedCommand = filteredSuggestions[selectedCommandIndex]; if (selectedCommand) { setInput(selectedCommand.command + " "); setShowCommandSuggestions(false); setSelectedCommandIndex(0); } return; } } if (showModelSelection) { if (key.upArrow) { setSelectedModelIndex((prev) => prev === 0 ? dynamicModels.length - 1 : prev - 1); return; } if (key.downArrow) { setSelectedModelIndex((prev) => (prev + 1) % dynamicModels.length); return; } if (key.tab || key.return) { const selectedModel = dynamicModels[selectedModelIndex]; (0, instance_models_1.onModelSelected)(selectedModel.model); agent.setModel(selectedModel.model, allModels); const confirmEntry = { type: "assistant", content: `✓ Switched to model: ${selectedModel.model}`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, confirmEntry]); setShowModelSelection(false); setSelectedModelIndex(0); return; } } if (showMcpServers) { if (key.upArrow) { setSelectedMcpServerIndex((prev) => prev === 0 ? mcpServers.length - 1 : prev - 1); return; } if (key.downArrow) { setSelectedMcpServerIndex((prev) => (prev + 1) % mcpServers.length); return; } if (key.tab || key.return) { const selectedServer = mcpServers[selectedMcpServerIndex]; if (selectedServer) { const serverInfo = { type: "assistant", content: `MCP Server: ${selectedServer.name} Command: ${selectedServer.command} ${selectedServer.args ? `Args: ${selectedServer.args.join(' ')}` : ''} ${selectedServer.env ? `Environment: ${Object.entries(selectedServer.env).map(([k, v]) => `${k}=${v}`).join(' ')}` : ''} ${selectedServer.description ? `Description: ${selectedServer.description}` : ''} Added: ${new Date(selectedServer.dateAdded).toLocaleDateString()}`, timestamp: new Date(), }; setChatHistory((prev) => [...prev, serverInfo]); } setShowMcpServers(false); setSelectedMcpServerIndex(0); return; } } if (showAddModel || showDeleteModel || showAddMcpServer || showDeleteMcpServer) { return; } if (showProviderSettings) { return; } // Handle file finder navigation when active (takes precedence over history) if (showFileFinder && filteredFiles.length > 0) { if (key.upArrow) { setSelectedFileIndex((prev) => prev === 0 ? filteredFiles.length - 1 : prev - 1); return; } if (key.downArrow) { setSelectedFileIndex((prev) => (prev + 1) % filteredFiles.length); return; } if (key.return || key.tab) { const selectedFile = filteredFiles[selectedFileIndex]; if (selectedFile) { const newInput = (0, file_finder_1.replaceFileQuery)(input, selectedFile); setInput(newInput); closeFileFinder(); } return; } } // Handle command history navigation with up/down arrows if (key.upArrow && !showCommandSuggestions && !showModelSelection && !showMcpServers) { if (commandHistory.length > 0) { if (historyIndex === -1) { // Store current input before navigating history setTemporaryInput(input); setHistoryIndex(commandHistory.length - 1); setInput(commandHistory[commandHistory.length - 1]); } else if (historyIndex > 0) { setHistoryIndex(historyIndex - 1); setInput(commandHistory[historyIndex - 1]); } } return; } if (key.downArrow && !showCommandSuggestions && !showModelSelection && !showMcpServers) { if (commandHistory.length > 0 && historyIndex !== -1) { if (historyIndex < commandHistory.length - 1) { setHistoryIndex(historyIndex + 1); setInput(commandHistory[historyIndex + 1]); } else { // Return to original input setHistoryIndex(-1); setInput(temporaryInput); } } 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); // Reset history navigation when user edits input if (historyIndex !== -1) { setHistoryIndex(-1); setTemporaryInput(""); } // Update file finder updateFileFinder(newInput); if (!newInput.startsWith("/")) { setShowCommandSuggestions(false); setSelectedCommandIndex(0); } else if (showCommandSuggestions) { // Reset selected index when input changes to avoid out-of-bounds setSelectedCommandIndex(0); } return; } if (inputChar && !key.ctrl && !key.meta) { const newInput = input + inputChar; setInput(newInput); // Reset history navigation when user types new input if (historyIndex !== -1) { setHistoryIndex(-1); setTemporaryInput(""); } // Update file finder based on @ symbol updateFileFinder(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); } else if (showCommandSuggestions) { // Reset selected index when input changes to avoid out-of-bounds setSelectedCommandIndex(0); } } }); return { input, showCommandSuggestions, selectedCommandIndex, showModelSelection, selectedModelIndex, showProviderSettings, selectedProviderIndex, showAddModel, showDeleteModel, showPromptsList, showAddPrompt, showDeletePrompt, showMcpServers, showAddMcpServer, showDeleteMcpServer, selectedMcpServerIndex, showConversationHistory, showTemperatureSelector, currentTemperature, showExpertModels, showExaApiKeyInput, showRouteSelection, routeViewMode, selectedRouteModelIndex, selectedRouteProviderIndex, currentSelectedModel, routeProviders, isLoadingProviders, showFileFinder, selectedFileIndex, filteredFiles, fileQuery, commandSuggestions, availableModels: dynamicModels, mcpServers, providerList, closeProviderSettings, closeAddModel, closeDeleteModel, closePromptsList, closeAddPrompt, closeDeletePrompt, closeMcpServers, closeAddMcpServer, closeDeleteMcpServer, closeConversationHistory, closeTemperatureSelector, closeExpertModels, closeExaApiKeyInput, closeRouteSelection, closeFileFinder, refreshModels, refreshMcpServers, openRouterModels: getOpenRouterModels(dynamicModels), agent, }; } exports.useInputHandler = useInputHandler; //# sourceMappingURL=use-input-handler.js.map