Heray-Was-Here
Server : Apache
System : Linux vps103298.mylogin.co 4.18.0-513.11.1.el8_9.x86_64 #1 SMP Wed Jan 17 02:00:40 EST 2024 x86_64
User : calvet ( 273824)
PHP Version : 7.4.33
Disable Function : NONE
Directory :  /usr/local/lib/node_modules/@google/gemini-cli/dist/src/ui/hooks/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : //usr/local/lib/node_modules/@google/gemini-cli/dist/src/ui/hooks/slashCommandProcessor.js
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { useCallback, useMemo } from 'react';
import open from 'open';
import process from 'node:process';
import { useStateAndRef } from './useStateAndRef.js';
import { GitService, Logger, MCPDiscoveryState, MCPServerStatus, getMCPDiscoveryState, getMCPServerStatus, } from '@google/gemini-cli-core';
import { useSessionStats } from '../contexts/SessionContext.js';
import { MessageType, } from '../types.js';
import { promises as fs } from 'fs';
import path from 'path';
import { createShowMemoryAction } from './useShowMemoryCommand.js';
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
import { formatDuration, formatMemoryUsage } from '../utils/formatters.js';
import { getCliVersion } from '../../utils/version.js';
/**
 * Hook to define and process slash commands (e.g., /help, /clear).
 */
export const useSlashCommandProcessor = (config, settings, history, addItem, clearItems, loadHistory, refreshStatic, setShowHelp, onDebugMessage, openThemeDialog, openAuthDialog, openEditorDialog, performMemoryRefresh, toggleCorgiMode, showToolDescriptions = false, setQuittingMessages, openPrivacyNotice) => {
    const session = useSessionStats();
    const gitService = useMemo(() => {
        if (!config?.getProjectRoot()) {
            return;
        }
        return new GitService(config.getProjectRoot());
    }, [config]);
    const pendingHistoryItems = [];
    const [pendingCompressionItemRef, setPendingCompressionItem] = useStateAndRef(null);
    if (pendingCompressionItemRef.current != null) {
        pendingHistoryItems.push(pendingCompressionItemRef.current);
    }
    const addMessage = useCallback((message) => {
        // Convert Message to HistoryItemWithoutId
        let historyItemContent;
        if (message.type === MessageType.ABOUT) {
            historyItemContent = {
                type: 'about',
                cliVersion: message.cliVersion,
                osVersion: message.osVersion,
                sandboxEnv: message.sandboxEnv,
                modelVersion: message.modelVersion,
                selectedAuthType: message.selectedAuthType,
                gcpProject: message.gcpProject,
            };
        }
        else if (message.type === MessageType.STATS) {
            historyItemContent = {
                type: 'stats',
                duration: message.duration,
            };
        }
        else if (message.type === MessageType.MODEL_STATS) {
            historyItemContent = {
                type: 'model_stats',
            };
        }
        else if (message.type === MessageType.TOOL_STATS) {
            historyItemContent = {
                type: 'tool_stats',
            };
        }
        else if (message.type === MessageType.QUIT) {
            historyItemContent = {
                type: 'quit',
                duration: message.duration,
            };
        }
        else if (message.type === MessageType.COMPRESSION) {
            historyItemContent = {
                type: 'compression',
                compression: message.compression,
            };
        }
        else {
            historyItemContent = {
                type: message.type,
                text: message.content,
            };
        }
        addItem(historyItemContent, message.timestamp.getTime());
    }, [addItem]);
    const showMemoryAction = useCallback(async () => {
        const actionFn = createShowMemoryAction(config, settings, addMessage);
        await actionFn();
    }, [config, settings, addMessage]);
    const addMemoryAction = useCallback((_mainCommand, _subCommand, args) => {
        if (!args || args.trim() === '') {
            addMessage({
                type: MessageType.ERROR,
                content: 'Usage: /memory add <text to remember>',
                timestamp: new Date(),
            });
            return;
        }
        // UI feedback for attempting to schedule
        addMessage({
            type: MessageType.INFO,
            content: `Attempting to save to memory: "${args.trim()}"`,
            timestamp: new Date(),
        });
        // Return info for scheduling the tool call
        return {
            shouldScheduleTool: true,
            toolName: 'save_memory',
            toolArgs: { fact: args.trim() },
        };
    }, [addMessage]);
    const savedChatTags = useCallback(async () => {
        const geminiDir = config?.getProjectTempDir();
        if (!geminiDir) {
            return [];
        }
        try {
            const files = await fs.readdir(geminiDir);
            return files
                .filter((file) => file.startsWith('checkpoint-') && file.endsWith('.json'))
                .map((file) => file.replace('checkpoint-', '').replace('.json', ''));
        }
        catch (_err) {
            return [];
        }
    }, [config]);
    const slashCommands = useMemo(() => {
        const commands = [
            {
                name: 'help',
                altName: '?',
                description: 'for help on gemini-cli',
                action: (_mainCommand, _subCommand, _args) => {
                    onDebugMessage('Opening help.');
                    setShowHelp(true);
                },
            },
            {
                name: 'docs',
                description: 'open full Gemini CLI documentation in your browser',
                action: async (_mainCommand, _subCommand, _args) => {
                    const docsUrl = 'https://goo.gle/gemini-cli-docs';
                    if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
                        addMessage({
                            type: MessageType.INFO,
                            content: `Please open the following URL in your browser to view the documentation:\n${docsUrl}`,
                            timestamp: new Date(),
                        });
                    }
                    else {
                        addMessage({
                            type: MessageType.INFO,
                            content: `Opening documentation in your browser: ${docsUrl}`,
                            timestamp: new Date(),
                        });
                        await open(docsUrl);
                    }
                },
            },
            {
                name: 'clear',
                description: 'clear the screen and conversation history',
                action: async (_mainCommand, _subCommand, _args) => {
                    onDebugMessage('Clearing terminal and resetting chat.');
                    clearItems();
                    await config?.getGeminiClient()?.resetChat();
                    console.clear();
                    refreshStatic();
                },
            },
            {
                name: 'theme',
                description: 'change the theme',
                action: (_mainCommand, _subCommand, _args) => {
                    openThemeDialog();
                },
            },
            {
                name: 'auth',
                description: 'change the auth method',
                action: (_mainCommand, _subCommand, _args) => {
                    openAuthDialog();
                },
            },
            {
                name: 'editor',
                description: 'set external editor preference',
                action: (_mainCommand, _subCommand, _args) => {
                    openEditorDialog();
                },
            },
            {
                name: 'privacy',
                description: 'display the privacy notice',
                action: (_mainCommand, _subCommand, _args) => {
                    openPrivacyNotice();
                },
            },
            {
                name: 'stats',
                altName: 'usage',
                description: 'check session stats. Usage: /stats [model|tools]',
                action: (_mainCommand, subCommand, _args) => {
                    if (subCommand === 'model') {
                        addMessage({
                            type: MessageType.MODEL_STATS,
                            timestamp: new Date(),
                        });
                        return;
                    }
                    else if (subCommand === 'tools') {
                        addMessage({
                            type: MessageType.TOOL_STATS,
                            timestamp: new Date(),
                        });
                        return;
                    }
                    const now = new Date();
                    const { sessionStartTime } = session.stats;
                    const wallDuration = now.getTime() - sessionStartTime.getTime();
                    addMessage({
                        type: MessageType.STATS,
                        duration: formatDuration(wallDuration),
                        timestamp: new Date(),
                    });
                },
            },
            {
                name: 'mcp',
                description: 'list configured MCP servers and tools',
                action: async (_mainCommand, _subCommand, _args) => {
                    // Check if the _subCommand includes a specific flag to control description visibility
                    let useShowDescriptions = showToolDescriptions;
                    if (_subCommand === 'desc' || _subCommand === 'descriptions') {
                        useShowDescriptions = true;
                    }
                    else if (_subCommand === 'nodesc' ||
                        _subCommand === 'nodescriptions') {
                        useShowDescriptions = false;
                    }
                    else if (_args === 'desc' || _args === 'descriptions') {
                        useShowDescriptions = true;
                    }
                    else if (_args === 'nodesc' || _args === 'nodescriptions') {
                        useShowDescriptions = false;
                    }
                    // Check if the _subCommand includes a specific flag to show detailed tool schema
                    let useShowSchema = false;
                    if (_subCommand === 'schema' || _args === 'schema') {
                        useShowSchema = true;
                    }
                    const toolRegistry = await config?.getToolRegistry();
                    if (!toolRegistry) {
                        addMessage({
                            type: MessageType.ERROR,
                            content: 'Could not retrieve tool registry.',
                            timestamp: new Date(),
                        });
                        return;
                    }
                    const mcpServers = config?.getMcpServers() || {};
                    const serverNames = Object.keys(mcpServers);
                    if (serverNames.length === 0) {
                        const docsUrl = 'https://goo.gle/gemini-cli-docs-mcp';
                        if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
                            addMessage({
                                type: MessageType.INFO,
                                content: `No MCP servers configured. Please open the following URL in your browser to view documentation:\n${docsUrl}`,
                                timestamp: new Date(),
                            });
                        }
                        else {
                            addMessage({
                                type: MessageType.INFO,
                                content: `No MCP servers configured. Opening documentation in your browser: ${docsUrl}`,
                                timestamp: new Date(),
                            });
                            await open(docsUrl);
                        }
                        return;
                    }
                    // Check if any servers are still connecting
                    const connectingServers = serverNames.filter((name) => getMCPServerStatus(name) === MCPServerStatus.CONNECTING);
                    const discoveryState = getMCPDiscoveryState();
                    let message = '';
                    // Add overall discovery status message if needed
                    if (discoveryState === MCPDiscoveryState.IN_PROGRESS ||
                        connectingServers.length > 0) {
                        message += `\u001b[33m⏳ MCP servers are starting up (${connectingServers.length} initializing)...\u001b[0m\n`;
                        message += `\u001b[90mNote: First startup may take longer. Tool availability will update automatically.\u001b[0m\n\n`;
                    }
                    message += 'Configured MCP servers:\n\n';
                    for (const serverName of serverNames) {
                        const serverTools = toolRegistry.getToolsByServer(serverName);
                        const status = getMCPServerStatus(serverName);
                        // Add status indicator with descriptive text
                        let statusIndicator = '';
                        let statusText = '';
                        switch (status) {
                            case MCPServerStatus.CONNECTED:
                                statusIndicator = '🟢';
                                statusText = 'Ready';
                                break;
                            case MCPServerStatus.CONNECTING:
                                statusIndicator = '🔄';
                                statusText = 'Starting... (first startup may take longer)';
                                break;
                            case MCPServerStatus.DISCONNECTED:
                            default:
                                statusIndicator = '🔴';
                                statusText = 'Disconnected';
                                break;
                        }
                        // Get server description if available
                        const server = mcpServers[serverName];
                        // Format server header with bold formatting and status
                        message += `${statusIndicator} \u001b[1m${serverName}\u001b[0m - ${statusText}`;
                        // Add tool count with conditional messaging
                        if (status === MCPServerStatus.CONNECTED) {
                            message += ` (${serverTools.length} tools)`;
                        }
                        else if (status === MCPServerStatus.CONNECTING) {
                            message += ` (tools will appear when ready)`;
                        }
                        else {
                            message += ` (${serverTools.length} tools cached)`;
                        }
                        // Add server description with proper handling of multi-line descriptions
                        if ((useShowDescriptions || useShowSchema) && server?.description) {
                            const greenColor = '\u001b[32m';
                            const resetColor = '\u001b[0m';
                            const descLines = server.description.trim().split('\n');
                            if (descLines) {
                                message += ':\n';
                                for (const descLine of descLines) {
                                    message += `    ${greenColor}${descLine}${resetColor}\n`;
                                }
                            }
                            else {
                                message += '\n';
                            }
                        }
                        else {
                            message += '\n';
                        }
                        // Reset formatting after server entry
                        message += '\u001b[0m';
                        if (serverTools.length > 0) {
                            serverTools.forEach((tool) => {
                                if ((useShowDescriptions || useShowSchema) &&
                                    tool.description) {
                                    // Format tool name in cyan using simple ANSI cyan color
                                    message += `  - \u001b[36m${tool.name}\u001b[0m`;
                                    // Apply green color to the description text
                                    const greenColor = '\u001b[32m';
                                    const resetColor = '\u001b[0m';
                                    // Handle multi-line descriptions by properly indenting and preserving formatting
                                    const descLines = tool.description.trim().split('\n');
                                    if (descLines) {
                                        message += ':\n';
                                        for (const descLine of descLines) {
                                            message += `      ${greenColor}${descLine}${resetColor}\n`;
                                        }
                                    }
                                    else {
                                        message += '\n';
                                    }
                                    // Reset is handled inline with each line now
                                }
                                else {
                                    // Use cyan color for the tool name even when not showing descriptions
                                    message += `  - \u001b[36m${tool.name}\u001b[0m\n`;
                                }
                                if (useShowSchema) {
                                    // Prefix the parameters in cyan
                                    message += `    \u001b[36mParameters:\u001b[0m\n`;
                                    // Apply green color to the parameter text
                                    const greenColor = '\u001b[32m';
                                    const resetColor = '\u001b[0m';
                                    const paramsLines = JSON.stringify(tool.schema.parameters, null, 2)
                                        .trim()
                                        .split('\n');
                                    if (paramsLines) {
                                        for (const paramsLine of paramsLines) {
                                            message += `      ${greenColor}${paramsLine}${resetColor}\n`;
                                        }
                                    }
                                }
                            });
                        }
                        else {
                            message += '  No tools available\n';
                        }
                        message += '\n';
                    }
                    // Make sure to reset any ANSI formatting at the end to prevent it from affecting the terminal
                    message += '\u001b[0m';
                    addMessage({
                        type: MessageType.INFO,
                        content: message,
                        timestamp: new Date(),
                    });
                },
            },
            {
                name: 'memory',
                description: 'manage memory. Usage: /memory <show|refresh|add> [text for add]',
                action: (mainCommand, subCommand, args) => {
                    switch (subCommand) {
                        case 'show':
                            showMemoryAction();
                            return;
                        case 'refresh':
                            performMemoryRefresh();
                            return;
                        case 'add':
                            return addMemoryAction(mainCommand, subCommand, args); // Return the object
                        case undefined:
                            addMessage({
                                type: MessageType.ERROR,
                                content: 'Missing command\nUsage: /memory <show|refresh|add> [text for add]',
                                timestamp: new Date(),
                            });
                            return;
                        default:
                            addMessage({
                                type: MessageType.ERROR,
                                content: `Unknown /memory command: ${subCommand}. Available: show, refresh, add`,
                                timestamp: new Date(),
                            });
                            return;
                    }
                },
            },
            {
                name: 'tools',
                description: 'list available Gemini CLI tools',
                action: async (_mainCommand, _subCommand, _args) => {
                    // Check if the _subCommand includes a specific flag to control description visibility
                    let useShowDescriptions = showToolDescriptions;
                    if (_subCommand === 'desc' || _subCommand === 'descriptions') {
                        useShowDescriptions = true;
                    }
                    else if (_subCommand === 'nodesc' ||
                        _subCommand === 'nodescriptions') {
                        useShowDescriptions = false;
                    }
                    else if (_args === 'desc' || _args === 'descriptions') {
                        useShowDescriptions = true;
                    }
                    else if (_args === 'nodesc' || _args === 'nodescriptions') {
                        useShowDescriptions = false;
                    }
                    const toolRegistry = await config?.getToolRegistry();
                    const tools = toolRegistry?.getAllTools();
                    if (!tools) {
                        addMessage({
                            type: MessageType.ERROR,
                            content: 'Could not retrieve tools.',
                            timestamp: new Date(),
                        });
                        return;
                    }
                    // Filter out MCP tools by checking if they have a serverName property
                    const geminiTools = tools.filter((tool) => !('serverName' in tool));
                    let message = 'Available Gemini CLI tools:\n\n';
                    if (geminiTools.length > 0) {
                        geminiTools.forEach((tool) => {
                            if (useShowDescriptions && tool.description) {
                                // Format tool name in cyan using simple ANSI cyan color
                                message += `  - \u001b[36m${tool.displayName} (${tool.name})\u001b[0m:\n`;
                                // Apply green color to the description text
                                const greenColor = '\u001b[32m';
                                const resetColor = '\u001b[0m';
                                // Handle multi-line descriptions by properly indenting and preserving formatting
                                const descLines = tool.description.trim().split('\n');
                                // If there are multiple lines, add proper indentation for each line
                                if (descLines) {
                                    for (const descLine of descLines) {
                                        message += `      ${greenColor}${descLine}${resetColor}\n`;
                                    }
                                }
                            }
                            else {
                                // Use cyan color for the tool name even when not showing descriptions
                                message += `  - \u001b[36m${tool.displayName}\u001b[0m\n`;
                            }
                        });
                    }
                    else {
                        message += '  No tools available\n';
                    }
                    message += '\n';
                    // Make sure to reset any ANSI formatting at the end to prevent it from affecting the terminal
                    message += '\u001b[0m';
                    addMessage({
                        type: MessageType.INFO,
                        content: message,
                        timestamp: new Date(),
                    });
                },
            },
            {
                name: 'corgi',
                action: (_mainCommand, _subCommand, _args) => {
                    toggleCorgiMode();
                },
            },
            {
                name: 'about',
                description: 'show version info',
                action: async (_mainCommand, _subCommand, _args) => {
                    const osVersion = process.platform;
                    let sandboxEnv = 'no sandbox';
                    if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
                        sandboxEnv = process.env.SANDBOX;
                    }
                    else if (process.env.SANDBOX === 'sandbox-exec') {
                        sandboxEnv = `sandbox-exec (${process.env.SEATBELT_PROFILE || 'unknown'})`;
                    }
                    const modelVersion = config?.getModel() || 'Unknown';
                    const cliVersion = await getCliVersion();
                    const selectedAuthType = settings.merged.selectedAuthType || '';
                    const gcpProject = process.env.GOOGLE_CLOUD_PROJECT || '';
                    addMessage({
                        type: MessageType.ABOUT,
                        timestamp: new Date(),
                        cliVersion,
                        osVersion,
                        sandboxEnv,
                        modelVersion,
                        selectedAuthType,
                        gcpProject,
                    });
                },
            },
            {
                name: 'bug',
                description: 'submit a bug report',
                action: async (_mainCommand, _subCommand, args) => {
                    let bugDescription = _subCommand || '';
                    if (args) {
                        bugDescription += ` ${args}`;
                    }
                    bugDescription = bugDescription.trim();
                    const osVersion = `${process.platform} ${process.version}`;
                    let sandboxEnv = 'no sandbox';
                    if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
                        sandboxEnv = process.env.SANDBOX.replace(/^gemini-(?:code-)?/, '');
                    }
                    else if (process.env.SANDBOX === 'sandbox-exec') {
                        sandboxEnv = `sandbox-exec (${process.env.SEATBELT_PROFILE || 'unknown'})`;
                    }
                    const modelVersion = config?.getModel() || 'Unknown';
                    const cliVersion = await getCliVersion();
                    const memoryUsage = formatMemoryUsage(process.memoryUsage().rss);
                    const info = `
*   **CLI Version:** ${cliVersion}
*   **Git Commit:** ${GIT_COMMIT_INFO}
*   **Operating System:** ${osVersion}
*   **Sandbox Environment:** ${sandboxEnv}
*   **Model Version:** ${modelVersion}
*   **Memory Usage:** ${memoryUsage}
`;
                    let bugReportUrl = 'https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml&title={title}&info={info}';
                    const bugCommand = config?.getBugCommand();
                    if (bugCommand?.urlTemplate) {
                        bugReportUrl = bugCommand.urlTemplate;
                    }
                    bugReportUrl = bugReportUrl
                        .replace('{title}', encodeURIComponent(bugDescription))
                        .replace('{info}', encodeURIComponent(info));
                    addMessage({
                        type: MessageType.INFO,
                        content: `To submit your bug report, please open the following URL in your browser:\n${bugReportUrl}`,
                        timestamp: new Date(),
                    });
                    (async () => {
                        try {
                            await open(bugReportUrl);
                        }
                        catch (error) {
                            const errorMessage = error instanceof Error ? error.message : String(error);
                            addMessage({
                                type: MessageType.ERROR,
                                content: `Could not open URL in browser: ${errorMessage}`,
                                timestamp: new Date(),
                            });
                        }
                    })();
                },
            },
            {
                name: 'chat',
                description: 'Manage conversation history. Usage: /chat <list|save|resume> [tag]',
                action: async (_mainCommand, subCommand, args) => {
                    const tag = (args || '').trim();
                    const logger = new Logger(config?.getSessionId() || '');
                    await logger.initialize();
                    const chat = await config?.getGeminiClient()?.getChat();
                    if (!chat) {
                        addMessage({
                            type: MessageType.ERROR,
                            content: 'No chat client available for conversation status.',
                            timestamp: new Date(),
                        });
                        return;
                    }
                    if (!subCommand) {
                        addMessage({
                            type: MessageType.ERROR,
                            content: 'Missing command\nUsage: /chat <list|save|resume> [tag]',
                            timestamp: new Date(),
                        });
                        return;
                    }
                    switch (subCommand) {
                        case 'save': {
                            const history = chat.getHistory();
                            if (history.length > 0) {
                                await logger.saveCheckpoint(chat?.getHistory() || [], tag);
                                addMessage({
                                    type: MessageType.INFO,
                                    content: `Conversation checkpoint saved${tag ? ' with tag: ' + tag : ''}.`,
                                    timestamp: new Date(),
                                });
                            }
                            else {
                                addMessage({
                                    type: MessageType.INFO,
                                    content: 'No conversation found to save.',
                                    timestamp: new Date(),
                                });
                            }
                            return;
                        }
                        case 'resume':
                        case 'restore':
                        case 'load': {
                            const conversation = await logger.loadCheckpoint(tag);
                            if (conversation.length === 0) {
                                addMessage({
                                    type: MessageType.INFO,
                                    content: `No saved checkpoint found${tag ? ' with tag: ' + tag : ''}.`,
                                    timestamp: new Date(),
                                });
                                return;
                            }
                            clearItems();
                            chat.clearHistory();
                            const rolemap = {
                                user: MessageType.USER,
                                model: MessageType.GEMINI,
                            };
                            let hasSystemPrompt = false;
                            let i = 0;
                            for (const item of conversation) {
                                i += 1;
                                // Add each item to history regardless of whether we display
                                // it.
                                chat.addHistory(item);
                                const text = item.parts
                                    ?.filter((m) => !!m.text)
                                    .map((m) => m.text)
                                    .join('') || '';
                                if (!text) {
                                    // Parsing Part[] back to various non-text output not yet implemented.
                                    continue;
                                }
                                if (i === 1 && text.match(/context for our chat/)) {
                                    hasSystemPrompt = true;
                                }
                                if (i > 2 || !hasSystemPrompt) {
                                    addItem({
                                        type: (item.role && rolemap[item.role]) || MessageType.GEMINI,
                                        text,
                                    }, i);
                                }
                            }
                            console.clear();
                            refreshStatic();
                            return;
                        }
                        case 'list':
                            addMessage({
                                type: MessageType.INFO,
                                content: 'list of saved conversations: ' +
                                    (await savedChatTags()).join(', '),
                                timestamp: new Date(),
                            });
                            return;
                        default:
                            addMessage({
                                type: MessageType.ERROR,
                                content: `Unknown /chat command: ${subCommand}. Available: list, save, resume`,
                                timestamp: new Date(),
                            });
                            return;
                    }
                },
                completion: async () => (await savedChatTags()).map((tag) => 'resume ' + tag),
            },
            {
                name: 'quit',
                altName: 'exit',
                description: 'exit the cli',
                action: async (mainCommand, _subCommand, _args) => {
                    const now = new Date();
                    const { sessionStartTime } = session.stats;
                    const wallDuration = now.getTime() - sessionStartTime.getTime();
                    setQuittingMessages([
                        {
                            type: 'user',
                            text: `/${mainCommand}`,
                            id: now.getTime() - 1,
                        },
                        {
                            type: 'quit',
                            duration: formatDuration(wallDuration),
                            id: now.getTime(),
                        },
                    ]);
                    setTimeout(() => {
                        process.exit(0);
                    }, 100);
                },
            },
            {
                name: 'compress',
                altName: 'summarize',
                description: 'Compresses the context by replacing it with a summary.',
                action: async (_mainCommand, _subCommand, _args) => {
                    if (pendingCompressionItemRef.current !== null) {
                        addMessage({
                            type: MessageType.ERROR,
                            content: 'Already compressing, wait for previous request to complete',
                            timestamp: new Date(),
                        });
                        return;
                    }
                    setPendingCompressionItem({
                        type: MessageType.COMPRESSION,
                        compression: {
                            isPending: true,
                            originalTokenCount: null,
                            newTokenCount: null,
                        },
                    });
                    try {
                        const compressed = await config
                            .getGeminiClient()
                            .tryCompressChat(true);
                        if (compressed) {
                            addMessage({
                                type: MessageType.COMPRESSION,
                                compression: {
                                    isPending: false,
                                    originalTokenCount: compressed.originalTokenCount,
                                    newTokenCount: compressed.newTokenCount,
                                },
                                timestamp: new Date(),
                            });
                        }
                        else {
                            addMessage({
                                type: MessageType.ERROR,
                                content: 'Failed to compress chat history.',
                                timestamp: new Date(),
                            });
                        }
                    }
                    catch (e) {
                        addMessage({
                            type: MessageType.ERROR,
                            content: `Failed to compress chat history: ${e instanceof Error ? e.message : String(e)}`,
                            timestamp: new Date(),
                        });
                    }
                    setPendingCompressionItem(null);
                },
            },
        ];
        if (config?.getCheckpointingEnabled()) {
            commands.push({
                name: 'restore',
                description: 'restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested',
                completion: async () => {
                    const checkpointDir = config?.getProjectTempDir()
                        ? path.join(config.getProjectTempDir(), 'checkpoints')
                        : undefined;
                    if (!checkpointDir) {
                        return [];
                    }
                    try {
                        const files = await fs.readdir(checkpointDir);
                        return files
                            .filter((file) => file.endsWith('.json'))
                            .map((file) => file.replace('.json', ''));
                    }
                    catch (_err) {
                        return [];
                    }
                },
                action: async (_mainCommand, subCommand, _args) => {
                    const checkpointDir = config?.getProjectTempDir()
                        ? path.join(config.getProjectTempDir(), 'checkpoints')
                        : undefined;
                    if (!checkpointDir) {
                        addMessage({
                            type: MessageType.ERROR,
                            content: 'Could not determine the .gemini directory path.',
                            timestamp: new Date(),
                        });
                        return;
                    }
                    try {
                        // Ensure the directory exists before trying to read it.
                        await fs.mkdir(checkpointDir, { recursive: true });
                        const files = await fs.readdir(checkpointDir);
                        const jsonFiles = files.filter((file) => file.endsWith('.json'));
                        if (!subCommand) {
                            if (jsonFiles.length === 0) {
                                addMessage({
                                    type: MessageType.INFO,
                                    content: 'No restorable tool calls found.',
                                    timestamp: new Date(),
                                });
                                return;
                            }
                            const truncatedFiles = jsonFiles.map((file) => {
                                const components = file.split('.');
                                if (components.length <= 1) {
                                    return file;
                                }
                                components.pop();
                                return components.join('.');
                            });
                            const fileList = truncatedFiles.join('\n');
                            addMessage({
                                type: MessageType.INFO,
                                content: `Available tool calls to restore:\n\n${fileList}`,
                                timestamp: new Date(),
                            });
                            return;
                        }
                        const selectedFile = subCommand.endsWith('.json')
                            ? subCommand
                            : `${subCommand}.json`;
                        if (!jsonFiles.includes(selectedFile)) {
                            addMessage({
                                type: MessageType.ERROR,
                                content: `File not found: ${selectedFile}`,
                                timestamp: new Date(),
                            });
                            return;
                        }
                        const filePath = path.join(checkpointDir, selectedFile);
                        const data = await fs.readFile(filePath, 'utf-8');
                        const toolCallData = JSON.parse(data);
                        if (toolCallData.history) {
                            loadHistory(toolCallData.history);
                        }
                        if (toolCallData.clientHistory) {
                            await config
                                ?.getGeminiClient()
                                ?.setHistory(toolCallData.clientHistory);
                        }
                        if (toolCallData.commitHash) {
                            await gitService?.restoreProjectFromSnapshot(toolCallData.commitHash);
                            addMessage({
                                type: MessageType.INFO,
                                content: `Restored project to the state before the tool call.`,
                                timestamp: new Date(),
                            });
                        }
                        return {
                            shouldScheduleTool: true,
                            toolName: toolCallData.toolCall.name,
                            toolArgs: toolCallData.toolCall.args,
                        };
                    }
                    catch (error) {
                        addMessage({
                            type: MessageType.ERROR,
                            content: `Could not read restorable tool calls. This is the error: ${error}`,
                            timestamp: new Date(),
                        });
                    }
                },
            });
        }
        return commands;
    }, [
        onDebugMessage,
        setShowHelp,
        refreshStatic,
        openThemeDialog,
        openAuthDialog,
        openEditorDialog,
        clearItems,
        performMemoryRefresh,
        showMemoryAction,
        addMemoryAction,
        addMessage,
        toggleCorgiMode,
        savedChatTags,
        config,
        settings,
        showToolDescriptions,
        session,
        gitService,
        loadHistory,
        addItem,
        setQuittingMessages,
        pendingCompressionItemRef,
        setPendingCompressionItem,
        openPrivacyNotice,
    ]);
    const handleSlashCommand = useCallback(async (rawQuery) => {
        if (typeof rawQuery !== 'string') {
            return false;
        }
        const trimmed = rawQuery.trim();
        if (!trimmed.startsWith('/') && !trimmed.startsWith('?')) {
            return false;
        }
        const userMessageTimestamp = Date.now();
        if (trimmed !== '/quit' && trimmed !== '/exit') {
            addItem({ type: MessageType.USER, text: trimmed }, userMessageTimestamp);
        }
        let subCommand;
        let args;
        const commandToMatch = (() => {
            if (trimmed.startsWith('?')) {
                return 'help';
            }
            const parts = trimmed.substring(1).trim().split(/\s+/);
            if (parts.length > 1) {
                subCommand = parts[1];
            }
            if (parts.length > 2) {
                args = parts.slice(2).join(' ');
            }
            return parts[0];
        })();
        const mainCommand = commandToMatch;
        for (const cmd of slashCommands) {
            if (mainCommand === cmd.name || mainCommand === cmd.altName) {
                const actionResult = await cmd.action(mainCommand, subCommand, args);
                if (typeof actionResult === 'object' &&
                    actionResult?.shouldScheduleTool) {
                    return actionResult; // Return the object for useGeminiStream
                }
                return true; // Command was handled, but no tool to schedule
            }
        }
        addMessage({
            type: MessageType.ERROR,
            content: `Unknown command: ${trimmed}`,
            timestamp: new Date(),
        });
        return true; // Indicate command was processed (even if unknown)
    }, [addItem, slashCommands, addMessage]);
    return { handleSlashCommand, slashCommands, pendingHistoryItems };
};
//# sourceMappingURL=slashCommandProcessor.js.map

Hry