2025-04-18 17:44:24 -07:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
2025-08-26 00:04:53 +02:00
|
|
|
import type React from 'react';
|
|
|
|
|
import { useMemo } from 'react';
|
2025-09-05 15:37:29 -07:00
|
|
|
import { Box, Text } from 'ink';
|
2026-02-17 18:41:43 -08:00
|
|
|
import type {
|
|
|
|
|
HistoryItem,
|
|
|
|
|
HistoryItemWithoutId,
|
|
|
|
|
IndividualToolCallDisplay,
|
|
|
|
|
} from '../../types.js';
|
2026-02-13 17:20:14 -05:00
|
|
|
import { ToolCallStatus, mapCoreStatusToDisplayStatus } from '../../types.js';
|
2025-04-18 19:09:41 -04:00
|
|
|
import { ToolMessage } from './ToolMessage.js';
|
2025-11-19 15:49:39 -08:00
|
|
|
import { ShellToolMessage } from './ShellToolMessage.js';
|
2025-09-06 01:39:02 -04:00
|
|
|
import { theme } from '../../semantic-colors.js';
|
|
|
|
|
import { useConfig } from '../../contexts/ConfigContext.js';
|
2026-02-20 16:26:11 -08:00
|
|
|
import { isShellTool, isThisShellFocused } from './ToolShared.js';
|
2026-02-27 14:15:10 -05:00
|
|
|
import {
|
|
|
|
|
shouldHideToolCall,
|
|
|
|
|
CoreToolCallStatus,
|
|
|
|
|
} from '@google/gemini-cli-core';
|
2026-01-27 16:06:24 -08:00
|
|
|
import { ShowMoreLines } from '../ShowMoreLines.js';
|
|
|
|
|
import { useUIState } from '../../contexts/UIStateContext.js';
|
2026-02-20 16:26:11 -08:00
|
|
|
import { useAlternateBuffer } from '../../hooks/useAlternateBuffer.js';
|
|
|
|
|
import {
|
|
|
|
|
calculateShellMaxLines,
|
|
|
|
|
calculateToolContentMaxLines,
|
|
|
|
|
} from '../../utils/toolLayoutUtils.js';
|
2026-02-17 18:41:43 -08:00
|
|
|
import { getToolGroupBorderAppearance } from '../../utils/borderStyles.js';
|
2026-02-27 14:15:10 -05:00
|
|
|
import { useSettings } from '../../contexts/SettingsContext.js';
|
Initial commit of Gemini Code CLI
This commit introduces the initial codebase for the Gemini Code CLI, a command-line interface designed to facilitate interaction with the Gemini API for software engineering tasks.
The code was migrated from a previous git repository as a single squashed commit.
Core Features & Components:
* **Gemini Integration:** Leverages the `@google/genai` SDK to interact with the Gemini models, supporting chat history, streaming responses, and function calling (tools).
* **Terminal UI:** Built with Ink (React for CLIs) providing an interactive chat interface within the terminal, including input prompts, message display, loading indicators, and tool interaction elements.
* **Tooling Framework:** Implements a robust tool system allowing Gemini to interact with the local environment. Includes tools for:
* File system listing (`ls`)
* File reading (`read-file`)
* Content searching (`grep`)
* File globbing (`glob`)
* File editing (`edit`)
* File writing (`write-file`)
* Executing bash commands (`terminal`)
* **State Management:** Handles the streaming state of Gemini responses and manages the conversation history.
* **Configuration:** Parses command-line arguments (`yargs`) and loads environment variables (`dotenv`) for setup.
* **Project Structure:** Organized into `core`, `ui`, `tools`, `config`, and `utils` directories using TypeScript. Includes basic build (`tsc`) and start scripts.
This initial version establishes the foundation for a powerful CLI tool enabling developers to use Gemini for coding assistance directly in their terminal environment.
---
Created by yours truly: __Gemini Code__
2025-04-15 21:41:08 -07:00
|
|
|
|
|
|
|
|
interface ToolGroupMessageProps {
|
2026-02-17 18:41:43 -08:00
|
|
|
item: HistoryItem | HistoryItemWithoutId;
|
2025-04-17 18:06:21 -04:00
|
|
|
toolCalls: IndividualToolCallDisplay[];
|
2025-06-19 20:17:23 +00:00
|
|
|
availableTerminalHeight?: number;
|
|
|
|
|
terminalWidth: number;
|
2025-09-11 13:27:27 -07:00
|
|
|
onShellInputSubmit?: (input: string) => void;
|
2026-01-27 16:06:24 -08:00
|
|
|
borderTop?: boolean;
|
|
|
|
|
borderBottom?: boolean;
|
2026-02-20 16:26:11 -08:00
|
|
|
isExpandable?: boolean;
|
Initial commit of Gemini Code CLI
This commit introduces the initial codebase for the Gemini Code CLI, a command-line interface designed to facilitate interaction with the Gemini API for software engineering tasks.
The code was migrated from a previous git repository as a single squashed commit.
Core Features & Components:
* **Gemini Integration:** Leverages the `@google/genai` SDK to interact with the Gemini models, supporting chat history, streaming responses, and function calling (tools).
* **Terminal UI:** Built with Ink (React for CLIs) providing an interactive chat interface within the terminal, including input prompts, message display, loading indicators, and tool interaction elements.
* **Tooling Framework:** Implements a robust tool system allowing Gemini to interact with the local environment. Includes tools for:
* File system listing (`ls`)
* File reading (`read-file`)
* Content searching (`grep`)
* File globbing (`glob`)
* File editing (`edit`)
* File writing (`write-file`)
* Executing bash commands (`terminal`)
* **State Management:** Handles the streaming state of Gemini responses and manages the conversation history.
* **Configuration:** Parses command-line arguments (`yargs`) and loads environment variables (`dotenv`) for setup.
* **Project Structure:** Organized into `core`, `ui`, `tools`, `config`, and `utils` directories using TypeScript. Includes basic build (`tsc`) and start scripts.
This initial version establishes the foundation for a powerful CLI tool enabling developers to use Gemini for coding assistance directly in their terminal environment.
---
Created by yours truly: __Gemini Code__
2025-04-15 21:41:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Main component renders the border and maps the tools using ToolMessage
|
2026-02-08 00:09:48 -08:00
|
|
|
const TOOL_MESSAGE_HORIZONTAL_MARGIN = 4;
|
|
|
|
|
|
2025-04-18 19:09:41 -04:00
|
|
|
export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
|
2026-02-17 18:41:43 -08:00
|
|
|
item,
|
2026-01-27 13:30:44 -05:00
|
|
|
toolCalls: allToolCalls,
|
2025-05-15 00:19:41 -07:00
|
|
|
availableTerminalHeight,
|
2025-06-19 20:17:23 +00:00
|
|
|
terminalWidth,
|
2026-01-27 16:06:24 -08:00
|
|
|
borderTop: borderTopOverride,
|
|
|
|
|
borderBottom: borderBottomOverride,
|
2026-02-20 16:26:11 -08:00
|
|
|
isExpandable,
|
2025-04-17 18:06:21 -04:00
|
|
|
}) => {
|
2026-02-27 14:15:10 -05:00
|
|
|
const settings = useSettings();
|
|
|
|
|
const isLowErrorVerbosity = settings.merged.ui?.errorVerbosity !== 'full';
|
|
|
|
|
|
2026-02-13 18:15:21 -05:00
|
|
|
// Filter out tool calls that should be hidden (e.g. in-progress Ask User, or Plan Mode operations).
|
2026-01-27 13:30:44 -05:00
|
|
|
const toolCalls = useMemo(
|
2026-02-12 16:49:07 -05:00
|
|
|
() =>
|
2026-02-27 14:15:10 -05:00
|
|
|
allToolCalls.filter((t) => {
|
|
|
|
|
if (
|
|
|
|
|
isLowErrorVerbosity &&
|
|
|
|
|
t.status === CoreToolCallStatus.Error &&
|
|
|
|
|
!t.isClientInitiated
|
|
|
|
|
) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return !shouldHideToolCall({
|
|
|
|
|
displayName: t.name,
|
|
|
|
|
status: t.status,
|
|
|
|
|
approvalMode: t.approvalMode,
|
|
|
|
|
hasResultDisplay: !!t.resultDisplay,
|
|
|
|
|
});
|
|
|
|
|
}),
|
|
|
|
|
[allToolCalls, isLowErrorVerbosity],
|
2026-01-27 13:30:44 -05:00
|
|
|
);
|
|
|
|
|
|
2026-01-23 20:32:35 -05:00
|
|
|
const config = useConfig();
|
2026-02-17 18:41:43 -08:00
|
|
|
const {
|
|
|
|
|
constrainHeight,
|
|
|
|
|
activePtyId,
|
|
|
|
|
embeddedShellFocused,
|
|
|
|
|
backgroundShells,
|
|
|
|
|
pendingHistoryItems,
|
|
|
|
|
} = useUIState();
|
2026-02-20 16:26:11 -08:00
|
|
|
const isAlternateBuffer = useAlternateBuffer();
|
2026-02-17 18:41:43 -08:00
|
|
|
|
|
|
|
|
const { borderColor, borderDimColor } = useMemo(
|
|
|
|
|
() =>
|
|
|
|
|
getToolGroupBorderAppearance(
|
|
|
|
|
item,
|
|
|
|
|
activePtyId,
|
|
|
|
|
embeddedShellFocused,
|
|
|
|
|
pendingHistoryItems,
|
|
|
|
|
backgroundShells,
|
|
|
|
|
),
|
|
|
|
|
[
|
|
|
|
|
item,
|
|
|
|
|
activePtyId,
|
|
|
|
|
embeddedShellFocused,
|
|
|
|
|
pendingHistoryItems,
|
|
|
|
|
backgroundShells,
|
|
|
|
|
],
|
|
|
|
|
);
|
2026-01-23 20:32:35 -05:00
|
|
|
|
2026-02-11 19:46:58 -05:00
|
|
|
// We HIDE tools that are still in pre-execution states (Confirming, Pending)
|
|
|
|
|
// from the History log. They live in the Global Queue or wait for their turn.
|
|
|
|
|
// Only show tools that are actually running or finished.
|
|
|
|
|
// We explicitly exclude Pending and Confirming to ensure they only
|
|
|
|
|
// appear in the Global Queue until they are approved and start executing.
|
|
|
|
|
const visibleToolCalls = useMemo(
|
|
|
|
|
() =>
|
2026-02-13 17:20:14 -05:00
|
|
|
toolCalls.filter((t) => {
|
|
|
|
|
const displayStatus = mapCoreStatusToDisplayStatus(t.status);
|
|
|
|
|
return (
|
|
|
|
|
displayStatus !== ToolCallStatus.Pending &&
|
|
|
|
|
displayStatus !== ToolCallStatus.Confirming
|
|
|
|
|
);
|
|
|
|
|
}),
|
2026-02-18 23:51:03 +02:00
|
|
|
|
2026-02-11 19:46:58 -05:00
|
|
|
[toolCalls],
|
|
|
|
|
);
|
2026-01-23 20:32:35 -05:00
|
|
|
|
2026-02-18 23:51:03 +02:00
|
|
|
const staticHeight = /* border */ 2;
|
2025-05-15 00:19:41 -07:00
|
|
|
|
2025-06-19 20:17:23 +00:00
|
|
|
let countToolCallsWithResults = 0;
|
2026-01-23 20:32:35 -05:00
|
|
|
for (const tool of visibleToolCalls) {
|
2025-06-19 20:17:23 +00:00
|
|
|
if (tool.resultDisplay !== undefined && tool.resultDisplay !== '') {
|
|
|
|
|
countToolCallsWithResults++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-23 20:32:35 -05:00
|
|
|
const countOneLineToolCalls =
|
|
|
|
|
visibleToolCalls.length - countToolCallsWithResults;
|
2025-06-19 20:17:23 +00:00
|
|
|
const availableTerminalHeightPerToolMessage = availableTerminalHeight
|
|
|
|
|
? Math.max(
|
|
|
|
|
Math.floor(
|
|
|
|
|
(availableTerminalHeight - staticHeight - countOneLineToolCalls) /
|
|
|
|
|
Math.max(1, countToolCallsWithResults),
|
|
|
|
|
),
|
|
|
|
|
1,
|
|
|
|
|
)
|
|
|
|
|
: undefined;
|
|
|
|
|
|
2026-02-08 00:09:48 -08:00
|
|
|
const contentWidth = terminalWidth - TOOL_MESSAGE_HORIZONTAL_MARGIN;
|
|
|
|
|
|
2026-02-20 16:26:11 -08:00
|
|
|
/*
|
|
|
|
|
* ToolGroupMessage calculates its own overflow state locally and passes
|
|
|
|
|
* it as a prop to ShowMoreLines. This isolates it from global overflow
|
|
|
|
|
* reports in ASB mode, while allowing it to contribute to the global
|
|
|
|
|
* 'Toast' hint in Standard mode.
|
|
|
|
|
*
|
|
|
|
|
* Because of this prop-based isolation and the explicit mode-checks in
|
|
|
|
|
* AppContainer, we do not need to shadow the OverflowProvider here.
|
|
|
|
|
*/
|
|
|
|
|
const hasOverflow = useMemo(() => {
|
|
|
|
|
if (!availableTerminalHeightPerToolMessage) return false;
|
|
|
|
|
return visibleToolCalls.some((tool) => {
|
|
|
|
|
const isShellToolCall = isShellTool(tool.name);
|
|
|
|
|
const isFocused = isThisShellFocused(
|
|
|
|
|
tool.name,
|
|
|
|
|
tool.status,
|
|
|
|
|
tool.ptyId,
|
|
|
|
|
activePtyId,
|
|
|
|
|
embeddedShellFocused,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let maxLines: number | undefined;
|
|
|
|
|
|
|
|
|
|
if (isShellToolCall) {
|
|
|
|
|
maxLines = calculateShellMaxLines({
|
|
|
|
|
status: tool.status,
|
|
|
|
|
isAlternateBuffer,
|
|
|
|
|
isThisShellFocused: isFocused,
|
|
|
|
|
availableTerminalHeight: availableTerminalHeightPerToolMessage,
|
|
|
|
|
constrainHeight,
|
|
|
|
|
isExpandable,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Standard tools and Shell tools both eventually use ToolResultDisplay's logic.
|
|
|
|
|
// ToolResultDisplay uses calculateToolContentMaxLines to find the final line budget.
|
|
|
|
|
const contentMaxLines = calculateToolContentMaxLines({
|
|
|
|
|
availableTerminalHeight: availableTerminalHeightPerToolMessage,
|
|
|
|
|
isAlternateBuffer,
|
|
|
|
|
maxLinesLimit: maxLines,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!contentMaxLines) return false;
|
|
|
|
|
|
|
|
|
|
if (typeof tool.resultDisplay === 'string') {
|
|
|
|
|
const text = tool.resultDisplay;
|
|
|
|
|
const hasTrailingNewline = text.endsWith('\n');
|
|
|
|
|
const contentText = hasTrailingNewline ? text.slice(0, -1) : text;
|
|
|
|
|
const lineCount = contentText.split('\n').length;
|
|
|
|
|
return lineCount > contentMaxLines;
|
|
|
|
|
}
|
|
|
|
|
if (Array.isArray(tool.resultDisplay)) {
|
|
|
|
|
return tool.resultDisplay.length > contentMaxLines;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
}, [
|
|
|
|
|
visibleToolCalls,
|
|
|
|
|
availableTerminalHeightPerToolMessage,
|
|
|
|
|
activePtyId,
|
|
|
|
|
embeddedShellFocused,
|
|
|
|
|
isAlternateBuffer,
|
|
|
|
|
constrainHeight,
|
|
|
|
|
isExpandable,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// If all tools are filtered out (e.g., in-progress AskUser tools, confirming tools),
|
|
|
|
|
// only render if we need to close a border from previous
|
|
|
|
|
// tool groups. borderBottomOverride=true means we must render the closing border;
|
|
|
|
|
// undefined or false means there's nothing to display.
|
|
|
|
|
if (visibleToolCalls.length === 0 && borderBottomOverride !== true) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const content = (
|
2025-04-22 07:48:12 -04:00
|
|
|
<Box
|
|
|
|
|
flexDirection="column"
|
2025-04-25 17:11:08 -07:00
|
|
|
/*
|
2026-02-20 16:26:11 -08:00
|
|
|
This width constraint is highly important and protects us from an Ink rendering bug.
|
|
|
|
|
Since the ToolGroup can typically change rendering states frequently, it can cause
|
|
|
|
|
Ink to render the border of the box incorrectly and span multiple lines and even
|
|
|
|
|
cause tearing.
|
|
|
|
|
*/
|
2025-10-09 19:27:20 -07:00
|
|
|
width={terminalWidth}
|
2026-02-08 00:09:48 -08:00
|
|
|
paddingRight={TOOL_MESSAGE_HORIZONTAL_MARGIN}
|
2025-04-22 07:48:12 -04:00
|
|
|
>
|
2026-01-23 20:32:35 -05:00
|
|
|
{visibleToolCalls.map((tool, index) => {
|
2025-11-12 17:01:16 -08:00
|
|
|
const isFirst = index === 0;
|
2026-01-22 16:02:14 -08:00
|
|
|
const isShellToolCall = isShellTool(tool.name);
|
2025-11-19 15:49:39 -08:00
|
|
|
|
|
|
|
|
const commonProps = {
|
|
|
|
|
...tool,
|
|
|
|
|
availableTerminalHeight: availableTerminalHeightPerToolMessage,
|
2026-02-08 00:09:48 -08:00
|
|
|
terminalWidth: contentWidth,
|
2026-02-11 19:46:58 -05:00
|
|
|
emphasis: 'medium' as const,
|
2026-01-27 16:06:24 -08:00
|
|
|
isFirst:
|
|
|
|
|
borderTopOverride !== undefined
|
|
|
|
|
? borderTopOverride && isFirst
|
|
|
|
|
: isFirst,
|
2025-11-19 15:49:39 -08:00
|
|
|
borderColor,
|
|
|
|
|
borderDimColor,
|
2026-02-20 16:26:11 -08:00
|
|
|
isExpandable,
|
2025-11-19 15:49:39 -08:00
|
|
|
};
|
|
|
|
|
|
2025-05-23 05:28:31 +00:00
|
|
|
return (
|
2025-11-11 07:50:11 -08:00
|
|
|
<Box
|
|
|
|
|
key={tool.callId}
|
|
|
|
|
flexDirection="column"
|
|
|
|
|
minHeight={1}
|
2026-02-08 00:09:48 -08:00
|
|
|
width={contentWidth}
|
2025-11-11 07:50:11 -08:00
|
|
|
>
|
2026-01-22 16:02:14 -08:00
|
|
|
{isShellToolCall ? (
|
2026-02-17 18:41:43 -08:00
|
|
|
<ShellToolMessage {...commonProps} config={config} />
|
2025-11-19 15:49:39 -08:00
|
|
|
) : (
|
|
|
|
|
<ToolMessage {...commonProps} />
|
|
|
|
|
)}
|
2026-02-20 16:26:11 -08:00
|
|
|
{tool.outputFile && (
|
|
|
|
|
<Box
|
|
|
|
|
borderLeft={true}
|
|
|
|
|
borderRight={true}
|
|
|
|
|
borderTop={false}
|
|
|
|
|
borderBottom={false}
|
|
|
|
|
borderColor={borderColor}
|
|
|
|
|
borderDimColor={borderDimColor}
|
|
|
|
|
flexDirection="column"
|
|
|
|
|
borderStyle="round"
|
|
|
|
|
paddingLeft={1}
|
|
|
|
|
paddingRight={1}
|
|
|
|
|
>
|
2025-11-12 17:01:16 -08:00
|
|
|
<Box>
|
|
|
|
|
<Text color={theme.text.primary}>
|
|
|
|
|
Output too long and was saved to: {tool.outputFile}
|
|
|
|
|
</Text>
|
|
|
|
|
</Box>
|
2026-02-20 16:26:11 -08:00
|
|
|
</Box>
|
|
|
|
|
)}
|
2025-05-23 05:28:31 +00:00
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
})}
|
2025-11-13 14:33:48 -08:00
|
|
|
{
|
|
|
|
|
/*
|
2026-02-20 16:26:11 -08:00
|
|
|
We have to keep the bottom border separate so it doesn't get
|
|
|
|
|
drawn over by the sticky header directly inside it.
|
|
|
|
|
*/
|
2026-01-27 16:06:24 -08:00
|
|
|
(visibleToolCalls.length > 0 || borderBottomOverride !== undefined) && (
|
2025-11-13 14:33:48 -08:00
|
|
|
<Box
|
2026-02-10 11:12:40 -08:00
|
|
|
height={0}
|
2026-02-08 00:09:48 -08:00
|
|
|
width={contentWidth}
|
2025-11-13 14:33:48 -08:00
|
|
|
borderLeft={true}
|
|
|
|
|
borderRight={true}
|
|
|
|
|
borderTop={false}
|
2026-01-27 16:06:24 -08:00
|
|
|
borderBottom={borderBottomOverride ?? true}
|
2025-11-13 14:33:48 -08:00
|
|
|
borderColor={borderColor}
|
|
|
|
|
borderDimColor={borderDimColor}
|
|
|
|
|
borderStyle="round"
|
|
|
|
|
/>
|
|
|
|
|
)
|
|
|
|
|
}
|
2026-01-27 16:06:24 -08:00
|
|
|
{(borderBottomOverride ?? true) && visibleToolCalls.length > 0 && (
|
2026-02-20 16:26:11 -08:00
|
|
|
<ShowMoreLines
|
|
|
|
|
constrainHeight={constrainHeight && !!isExpandable}
|
|
|
|
|
isOverflowing={hasOverflow}
|
|
|
|
|
/>
|
2026-01-27 16:06:24 -08:00
|
|
|
)}
|
2025-04-17 18:06:21 -04:00
|
|
|
</Box>
|
|
|
|
|
);
|
2026-02-20 16:26:11 -08:00
|
|
|
|
|
|
|
|
return content;
|
Initial commit of Gemini Code CLI
This commit introduces the initial codebase for the Gemini Code CLI, a command-line interface designed to facilitate interaction with the Gemini API for software engineering tasks.
The code was migrated from a previous git repository as a single squashed commit.
Core Features & Components:
* **Gemini Integration:** Leverages the `@google/genai` SDK to interact with the Gemini models, supporting chat history, streaming responses, and function calling (tools).
* **Terminal UI:** Built with Ink (React for CLIs) providing an interactive chat interface within the terminal, including input prompts, message display, loading indicators, and tool interaction elements.
* **Tooling Framework:** Implements a robust tool system allowing Gemini to interact with the local environment. Includes tools for:
* File system listing (`ls`)
* File reading (`read-file`)
* Content searching (`grep`)
* File globbing (`glob`)
* File editing (`edit`)
* File writing (`write-file`)
* Executing bash commands (`terminal`)
* **State Management:** Handles the streaming state of Gemini responses and manages the conversation history.
* **Configuration:** Parses command-line arguments (`yargs`) and loads environment variables (`dotenv`) for setup.
* **Project Structure:** Organized into `core`, `ui`, `tools`, `config`, and `utils` directories using TypeScript. Includes basic build (`tsc`) and start scripts.
This initial version establishes the foundation for a powerful CLI tool enabling developers to use Gemini for coding assistance directly in their terminal environment.
---
Created by yours truly: __Gemini Code__
2025-04-15 21:41:08 -07:00
|
|
|
};
|