2025-04-18 17:44:24 -07:00
/ * *
* @license
* Copyright 2025 Google LLC
* SPDX - License - Identifier : Apache - 2.0
* /
2025-04-21 13:29:36 -07:00
import { FunctionDeclaration } from '@google/genai' ;
2025-05-03 19:57:28 -07:00
import { Tool , ToolResult , BaseTool } from './tools.js' ;
import { Config } from '../config/config.js' ;
2025-05-16 16:29:03 -07:00
import { parse } from 'shell-quote' ;
2025-05-03 19:57:28 -07:00
import { spawn , execSync } from 'node:child_process' ;
2025-05-05 07:50:05 -07:00
// TODO: remove this dependency once MCP support is built into genai SDK
2025-05-04 12:11:19 -07:00
import { Client } from '@modelcontextprotocol/sdk/client/index.js' ;
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js' ;
2025-05-03 19:57:28 -07:00
type ToolParams = Record < string , unknown > ;
2025-05-17 16:53:22 -07:00
const MCP_TOOL_DEFAULT_TIMEOUT_MSEC = 10 * 60 * 1000 ; // default to 10 minutes
2025-05-03 19:57:28 -07:00
export class DiscoveredTool extends BaseTool < ToolParams , ToolResult > {
constructor (
private readonly config : Config ,
readonly name : string ,
readonly description : string ,
readonly parameterSchema : Record < string , unknown > ,
) {
const discoveryCmd = config . getToolDiscoveryCommand ( ) ! ;
const callCommand = config . getToolCallCommand ( ) ! ;
description += `
2025-05-04 16:26:20 -07:00
2025-05-03 19:57:28 -07:00
This tool was discovered from the project by executing the command \ ` ${ discoveryCmd } \` on project root.
When called , this tool will execute the command \ ` ${ callCommand } ${ name } \` on project root.
2025-05-16 16:29:03 -07:00
Tool discovery and call commands can be configured in project or user settings .
2025-05-03 19:57:28 -07:00
When called , the tool call command is executed as a subprocess .
On success , tool output is returned as a json string .
Otherwise , the following information is returned :
Stdout : Output on stdout stream . Can be \ ` (empty) \` or partial.
Stderr : Output on stderr stream . Can be \ ` (empty) \` or partial.
Error : Error or \ ` (none) \` if no error was reported for the subprocess.
Exit Code : Exit code or \ ` (none) \` if terminated by signal.
Signal : Signal number or \ ` (none) \` if no signal was received.
` ;
super ( name , name , description , parameterSchema ) ;
}
async execute ( params : ToolParams ) : Promise < ToolResult > {
const callCommand = this . config . getToolCallCommand ( ) ! ;
const child = spawn ( callCommand , [ this . name ] ) ;
child . stdin . write ( JSON . stringify ( params ) ) ;
child . stdin . end ( ) ;
let stdout = '' ;
let stderr = '' ;
child . stdout . on ( 'data' , ( data ) = > {
stdout += data . toString ( ) ;
} ) ;
child . stderr . on ( 'data' , ( data ) = > {
stderr += data . toString ( ) ;
} ) ;
let error : Error | null = null ;
child . on ( 'error' , ( err : Error ) = > {
error = err ;
} ) ;
let code : number | null = null ;
let signal : NodeJS.Signals | null = null ;
child . on (
'close' ,
( _code : number | null , _signal : NodeJS.Signals | null ) = > {
code = _code ;
signal = _signal ;
} ,
) ;
await new Promise ( ( resolve ) = > child . on ( 'close' , resolve ) ) ;
// if there is any error, non-zero exit code, signal, or stderr, return error details instead of stdout
if ( error || code !== 0 || signal || stderr ) {
const llmContent = [
` Stdout: ${ stdout || '(empty)' } ` ,
` Stderr: ${ stderr || '(empty)' } ` ,
` Error: ${ error ? ? '(none)' } ` ,
` Exit Code: ${ code ? ? '(none)' } ` ,
` Signal: ${ signal ? ? '(none)' } ` ,
] . join ( '\n' ) ;
return {
llmContent ,
returnDisplay : llmContent ,
} ;
}
return {
llmContent : stdout ,
returnDisplay : stdout ,
} ;
}
}
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
2025-05-04 12:11:19 -07:00
export class DiscoveredMCPTool extends BaseTool < ToolParams , ToolResult > {
constructor (
private readonly mcpClient : Client ,
readonly name : string ,
readonly description : string ,
readonly parameterSchema : Record < string , unknown > ,
2025-05-17 14:13:27 -07:00
readonly serverToolName : string ,
2025-05-17 16:53:22 -07:00
readonly timeout? : number ,
2025-05-04 12:11:19 -07:00
) {
description += `
2025-05-04 16:26:20 -07:00
2025-05-04 12:11:19 -07:00
This MCP tool was discovered from a local MCP server using JSON RPC 2.0 over stdio transport protocol .
When called , this tool will invoke the \ ` tools/call \` method for tool name \` ${ name } \` .
2025-05-16 16:29:03 -07:00
MCP servers can be configured in project or user settings .
2025-05-04 12:11:19 -07:00
Returns the MCP server response as a json string .
` ;
super ( name , name , description , parameterSchema ) ;
}
async execute ( params : ToolParams ) : Promise < ToolResult > {
2025-05-17 16:53:22 -07:00
const result = await this . mcpClient . callTool (
{
name : this.serverToolName ,
arguments : params ,
} ,
undefined , // skip resultSchema to specify options (RequestOptions)
{
timeout : this.timeout ? ? MCP_TOOL_DEFAULT_TIMEOUT_MSEC ,
} ,
) ;
2025-05-04 12:11:19 -07:00
return {
llmContent : JSON.stringify ( result , null , 2 ) ,
returnDisplay : JSON.stringify ( result , null , 2 ) ,
} ;
}
}
2025-04-21 12:59:31 -07:00
export class ToolRegistry {
2025-04-17 18:06:21 -04:00
private tools : Map < string , Tool > = new Map ( ) ;
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
2025-05-03 19:57:28 -07:00
constructor ( private readonly config : Config ) { }
2025-04-17 18:06:21 -04:00
/ * *
* Registers a tool definition .
* @param tool - The tool object containing schema and execution logic .
* /
registerTool ( tool : Tool ) : void {
if ( this . tools . has ( tool . name ) ) {
// Decide on behavior: throw error, log warning, or allow overwrite
console . warn (
` Tool with name " ${ tool . name } " is already registered. Overwriting. ` ,
) ;
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
}
2025-04-17 18:06:21 -04:00
this . tools . set ( tool . name , tool ) ;
}
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
2025-05-03 19:57:28 -07:00
/ * *
* Discovers tools from project , if a discovery command is configured .
* Can be called multiple times to update discovered tools .
* /
discoverTools ( ) : void {
// remove any previously discovered tools
for ( const tool of this . tools . values ( ) ) {
if ( tool instanceof DiscoveredTool ) {
this . tools . delete ( tool . name ) ;
}
}
2025-05-04 12:11:19 -07:00
// discover tools using discovery command, if configured
const discoveryCmd = this . config . getToolDiscoveryCommand ( ) ;
if ( discoveryCmd ) {
// execute discovery command and extract function declarations
const functions : FunctionDeclaration [ ] = [ ] ;
for ( const tool of JSON . parse ( execSync ( discoveryCmd ) . toString ( ) . trim ( ) ) ) {
functions . push ( . . . tool [ 'function_declarations' ] ) ;
}
// register each function as a tool
for ( const func of functions ) {
this . registerTool (
new DiscoveredTool (
this . config ,
func . name ! ,
func . description ! ,
func . parameters ! as Record < string , unknown > ,
) ,
) ;
}
2025-05-03 19:57:28 -07:00
}
2025-05-16 16:29:03 -07:00
// discover tools using MCP servers, if configured
// convert mcpServerCommand (if any) to StdioServerParameters
const mcpServers = this . config . getMcpServers ( ) || { } ;
if ( this . config . getMcpServerCommand ( ) ) {
const cmd = this . config . getMcpServerCommand ( ) ! ;
const args = parse ( cmd , process . env ) as string [ ] ;
if ( args . some ( ( arg ) = > typeof arg !== 'string' ) ) {
throw new Error ( 'failed to parse mcpServerCommand: ' + cmd ) ;
}
// use generic server name 'mcp'
mcpServers [ 'mcp' ] = {
command : args [ 0 ] ,
args : args.slice ( 1 ) ,
} ;
}
2025-05-17 16:53:22 -07:00
for ( const [ mcpServerName , mcpServerConfig ] of Object . entries ( mcpServers ) ) {
2025-05-04 12:11:19 -07:00
( async ( ) = > {
2025-05-16 16:29:03 -07:00
const mcpClient = new Client ( {
name : 'mcp-client' ,
version : '0.0.1' ,
} ) ;
const transport = new StdioClientTransport ( {
2025-05-17 16:53:22 -07:00
. . . mcpServerConfig ,
2025-05-16 16:29:03 -07:00
env : {
. . . process . env ,
2025-05-17 16:53:22 -07:00
. . . ( mcpServerConfig . env || { } ) ,
2025-05-16 16:29:03 -07:00
} as Record < string , string > ,
stderr : 'pipe' ,
} ) ;
try {
await mcpClient . connect ( transport ) ;
} catch ( error ) {
console . error (
` failed to start or connect to MCP server ' ${ mcpServerName } ' ` +
2025-05-17 16:53:22 -07:00
` ${ JSON . stringify ( mcpServerConfig ) } ; \ n ${ error } ` ,
2025-05-16 16:29:03 -07:00
) ;
2025-05-16 16:39:28 -07:00
// Do not re-throw, let other MCP servers be discovered.
return ; // Exit this async IIFE if connection failed
2025-05-04 12:11:19 -07:00
}
2025-05-16 16:29:03 -07:00
mcpClient . onerror = ( error ) = > {
console . error ( 'MCP ERROR' , error . toString ( ) ) ;
} ;
if ( ! transport . stderr ) {
throw new Error ( 'transport missing stderr stream' ) ;
}
transport . stderr . on ( 'data' , ( data ) = > {
// filter out INFO messages logged for each request received
if ( ! data . toString ( ) . includes ( '] INFO' ) ) {
2025-05-16 21:17:51 -07:00
console . debug ( 'MCP STDERR' , data . toString ( ) ) ;
2025-05-16 16:29:03 -07:00
}
} ) ;
const result = await mcpClient . listTools ( ) ;
2025-05-04 12:11:19 -07:00
for ( const tool of result . tools ) {
2025-05-16 22:09:24 -07:00
// Recursively remove additionalProperties and $schema from the inputSchema
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- This function recursively navigates a deeply nested and potentially heterogeneous JSON schema object. Using 'any' is a pragmatic choice here to avoid overly complex type definitions for all possible schema variations.
const removeSchemaProps = ( obj : any ) = > {
if ( typeof obj !== 'object' || obj === null ) {
return ;
}
if ( Array . isArray ( obj ) ) {
obj . forEach ( removeSchemaProps ) ;
} else {
delete obj . additionalProperties ;
delete obj . $schema ;
Object . values ( obj ) . forEach ( removeSchemaProps ) ;
}
} ;
removeSchemaProps ( tool . inputSchema ) ;
2025-05-04 12:11:19 -07:00
this . registerTool (
new DiscoveredMCPTool (
2025-05-16 16:29:03 -07:00
mcpClient ,
Object . keys ( mcpServers ) . length > 1
? mcpServerName + '__' + tool . name
: tool . name ,
2025-05-04 12:11:19 -07:00
tool . description ? ? '' ,
tool . inputSchema ,
2025-05-17 14:13:27 -07:00
tool . name ,
2025-05-17 16:53:22 -07:00
mcpServerConfig . timeout ,
2025-05-04 12:11:19 -07:00
) ,
) ;
}
} ) ( ) ;
2025-05-03 19:57:28 -07:00
}
}
2025-04-17 18:06:21 -04:00
/ * *
2025-04-19 19:45:42 +01:00
* Retrieves the list of tool schemas ( FunctionDeclaration array ) .
* Extracts the declarations from the ToolListUnion structure .
2025-05-03 19:57:28 -07:00
* Includes discovered ( vs registered ) tools if configured .
2025-04-19 19:45:42 +01:00
* @returns An array of FunctionDeclarations .
2025-04-17 18:06:21 -04:00
* /
2025-04-19 19:45:42 +01:00
getFunctionDeclarations ( ) : FunctionDeclaration [ ] {
2025-04-17 18:06:21 -04:00
const declarations : FunctionDeclaration [ ] = [ ] ;
this . tools . forEach ( ( tool ) = > {
declarations . push ( tool . schema ) ;
} ) ;
2025-04-19 19:45:42 +01:00
return declarations ;
}
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
2025-04-19 19:45:42 +01:00
/ * *
2025-05-03 19:57:28 -07:00
* Returns an array of all registered and discovered tool instances .
2025-04-19 19:45:42 +01:00
* /
getAllTools ( ) : Tool [ ] {
return Array . from ( this . tools . values ( ) ) ;
}
2025-04-17 18:06:21 -04:00
/ * *
* Get the definition of a specific tool .
* /
getTool ( name : string ) : Tool | undefined {
return this . tools . get ( name ) ;
}
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
}