2025-04-18 17:44:24 -07:00
/ * *
* @license
* Copyright 2025 Google LLC
* SPDX - License - Identifier : Apache - 2.0
* /
2025-08-25 22:11:27 +02:00
import * as fs from 'node:fs' ;
import * as path from 'node:path' ;
2025-08-02 03:52:17 +05:30
import { homedir } from 'node:os' ;
2025-04-18 11:12:18 -07:00
import yargs from 'yargs/yargs' ;
import { hideBin } from 'yargs/helpers' ;
import process from 'node:process' ;
2025-08-06 11:52:29 -04:00
import { mcpCommand } from '../commands/mcp.js' ;
2025-08-26 00:04:53 +02:00
import type {
FileFilteringOptions ,
MCPServerConfig ,
2025-09-11 05:19:47 +09:00
OutputFormat ,
2025-08-26 00:04:53 +02:00
} from '@google/gemini-cli-core' ;
2025-08-25 17:02:10 +00:00
import { extensionsCommand } from '../commands/extensions.js' ;
2025-04-19 19:45:42 +01:00
import {
Config ,
2025-05-23 08:53:22 -07:00
loadServerHierarchicalMemory ,
2025-05-31 12:49:28 -07:00
setGeminiMdFilename as setServerGeminiMdFilename ,
getCurrentGeminiMdFilename ,
2025-06-02 22:05:45 +02:00
ApprovalMode ,
2025-06-13 01:25:42 -07:00
DEFAULT_GEMINI_MODEL ,
2025-09-12 15:57:07 -04:00
DEFAULT_GEMINI_MODEL_AUTO ,
2025-06-13 01:25:42 -07:00
DEFAULT_GEMINI_EMBEDDING_MODEL ,
2025-07-20 00:55:33 -07:00
DEFAULT_MEMORY_FILE_FILTERING_OPTIONS ,
2025-06-13 20:25:59 -04:00
FileDiscoveryService ,
2025-08-07 14:19:06 -07:00
ShellTool ,
EditTool ,
WriteFileTool ,
2025-09-23 03:23:06 +09:00
resolveTelemetrySettings ,
FatalConfigError ,
2025-06-25 05:41:11 -07:00
} from '@google/gemini-cli-core' ;
2025-08-26 00:04:53 +02:00
import type { Settings } from './settings.js' ;
2025-06-19 16:52:22 -07:00
2025-08-26 00:04:53 +02:00
import type { Extension } from './extension.js' ;
import { annotateActiveExtensions } from './extension.js' ;
2025-06-16 01:13:39 -05:00
import { getCliVersion } from '../utils/version.js' ;
2025-06-18 10:01:00 -07:00
import { loadSandboxConfig } from './sandboxConfig.js' ;
2025-08-06 02:01:01 +09:00
import { resolvePath } from '../utils/resolvePath.js' ;
2025-08-28 21:53:56 +02:00
import { appEvents } from '../utils/events.js' ;
2025-04-18 11:12:18 -07:00
2025-08-13 11:06:31 -07:00
import { isWorkspaceTrusted } from './trustedFolders.js' ;
2025-09-18 13:44:23 -07:00
import { createPolicyEngineConfig } from './policy.js' ;
2025-09-29 06:53:19 -07:00
import type { ExtensionEnablementManager } from './extensions/extensionEnablement.js' ;
2025-08-13 11:06:31 -07:00
2025-05-14 12:37:17 -07:00
// Simple console logger for now - replace with actual logger if available
const logger = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
debug : ( . . . args : any [ ] ) = > console . debug ( '[DEBUG]' , . . . args ) ,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
warn : ( . . . args : any [ ] ) = > console . warn ( '[WARN]' , . . . args ) ,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error : ( . . . args : any [ ] ) = > console . error ( '[ERROR]' , . . . args ) ,
} ;
2025-07-11 16:52:56 -07:00
export interface CliArgs {
2025-04-18 11:12:18 -07:00
model : string | undefined ;
2025-05-02 08:15:46 -07:00
sandbox : boolean | string | undefined ;
2025-07-08 16:56:12 -04:00
sandboxImage : string | undefined ;
2025-05-15 11:38:33 -07:00
debug : boolean | undefined ;
prompt : string | undefined ;
2025-07-11 16:52:56 -07:00
promptInteractive : string | undefined ;
2025-07-08 16:56:12 -04:00
allFiles : boolean | undefined ;
showMemoryUsage : boolean | undefined ;
2025-06-02 22:05:45 +02:00
yolo : boolean | undefined ;
2025-08-12 15:10:22 -07:00
approvalMode : string | undefined ;
2025-06-05 16:04:25 -04:00
telemetry : boolean | undefined ;
2025-06-20 00:39:15 -04:00
checkpointing : boolean | undefined ;
Add telemetry command and refactor telemetry settings (#1060)
#750
### Telemetry Settings
Refactors telemetry configuration to use a nested `telemetry` object in `settings.json`, for example:
```json
{
"telemetry": {
"enabled": true,
"target": "gcp"
"log-prompts": "true"
},
"sandbox": false
}
```
The above includes
- Centralized telemetry settings under a `telemetry` object in `settings.json`.
- CLI flags for the `gemini` command to override all telemetry sub-settings:
- `--telemetry` / `--no-telemetry`
- `--telemetry-target <local|gcp>`
- `--telemetry-otlp-endpoint <URL>`
- `--telemetry-log-prompts` / `--no-telemetry-log-prompts`
- Updates `packages/cli/src/config/config.ts` and `packages/core/src/config/config.ts` to read from the new settings structure and respect the new CLI flags.
- Modifies `scripts/handle-telemetry.js`, `scripts/local_telemetry.js`, and `scripts/telemetry_utils.js` to align with the new settings structure.
- Updates `docs/core/telemetry.md` to reflect the new settings structure, CLI flags, and order of precedence.
- Renames `logUserPromptsEnabled` to `logPrompts` for brevity.
### `npm run telemetry`
Add a new `npm run telemetry` command that uses `scripts/telemetry.js`, automates the entire process of setting up a local and GCP telemetry pipelines, including configuring the necessary settings in the `.gemini/settings.json` workspace file and installing required binaries (e.g. `otelcol-contrib`).
---
```shell
$ npm run telemetry -- --target=gcp
> gemini-cli@0.1.0 telemetry
> node scripts/telemetry.js --target=gcp
⚙️ Using command-line target: gcp
🚀 Running telemetry script for target: gcp.
✨ Starting Local Telemetry Exporter for Google Cloud ✨
⚙️ Enabled telemetry in workspace settings.
🔧 Set telemetry OTLP endpoint to http://localhost:4317.
🎯 Set telemetry target to gcp.
✅ Workspace settings updated.
✅ Using Google Cloud Project ID: foo-bar
🔑 Please ensure you are authenticated with Google Cloud:
- Run `gcloud auth application-default login` OR ensure `GOOGLE_APPLICATION_CREDENTIALS` environment variable points to a valid service account key.
- The account needs "Cloud Trace Agent", "Monitoring Metric Writer", and "Logs Writer" roles.
✅ otelcol-contrib already exists at /Users/jerop/github/gemini-cli/.gemini/otel/bin/otelcol-contrib
🧹 Cleaning up old processes and logs...
✅ Deleted old GCP collector log.
📄 Wrote OTEL collector config to /Users/jerop/github/gemini-cli/.gemini/otel/collector-gcp.yaml
🚀 Starting OTEL collector for GCP... Logs: /Users/jerop/github/gemini-cli/.gemini/otel/collector-gcp.log
⏳ Waiting for OTEL collector to start (PID: 17013)...
✅ OTEL collector started successfully on port 4317.
✨ Local OTEL collector for GCP is running.
🚀 To send telemetry, run the Gemini CLI in a separate terminal window.
📄 Collector logs are being written to: /Users/jerop/github/gemini-cli/.gemini/otel/collector-gcp.log
📊 View your telemetry data in Google Cloud Console:
- Logs: https://console.cloud.google.com/logs/query;query=logName%3D%22projects%2Ffoo-bar%2Flogs%2Fgemini_cli%22?project=foo-bar
- Metrics: https://console.cloud.google.com/monitoring/metrics-explorer?project=foo-bar
- Traces: https://console.cloud.google.com/traces/list?project=foo-bar
Press Ctrl+C to exit.
^C
👋 Shutting down...
⚙️ Disabled telemetry in workspace settings.
🔧 Cleared telemetry OTLP endpoint.
🎯 Cleared telemetry target.
✅ Workspace settings updated.
🛑 Stopping otelcol-contrib (PID: 17013)...
✅ otelcol-contrib stopped.
```
2025-06-15 00:47:32 -04:00
telemetryTarget : string | undefined ;
telemetryOtlpEndpoint : string | undefined ;
2025-08-15 18:10:21 -07:00
telemetryOtlpProtocol : string | undefined ;
Add telemetry command and refactor telemetry settings (#1060)
#750
### Telemetry Settings
Refactors telemetry configuration to use a nested `telemetry` object in `settings.json`, for example:
```json
{
"telemetry": {
"enabled": true,
"target": "gcp"
"log-prompts": "true"
},
"sandbox": false
}
```
The above includes
- Centralized telemetry settings under a `telemetry` object in `settings.json`.
- CLI flags for the `gemini` command to override all telemetry sub-settings:
- `--telemetry` / `--no-telemetry`
- `--telemetry-target <local|gcp>`
- `--telemetry-otlp-endpoint <URL>`
- `--telemetry-log-prompts` / `--no-telemetry-log-prompts`
- Updates `packages/cli/src/config/config.ts` and `packages/core/src/config/config.ts` to read from the new settings structure and respect the new CLI flags.
- Modifies `scripts/handle-telemetry.js`, `scripts/local_telemetry.js`, and `scripts/telemetry_utils.js` to align with the new settings structure.
- Updates `docs/core/telemetry.md` to reflect the new settings structure, CLI flags, and order of precedence.
- Renames `logUserPromptsEnabled` to `logPrompts` for brevity.
### `npm run telemetry`
Add a new `npm run telemetry` command that uses `scripts/telemetry.js`, automates the entire process of setting up a local and GCP telemetry pipelines, including configuring the necessary settings in the `.gemini/settings.json` workspace file and installing required binaries (e.g. `otelcol-contrib`).
---
```shell
$ npm run telemetry -- --target=gcp
> gemini-cli@0.1.0 telemetry
> node scripts/telemetry.js --target=gcp
⚙️ Using command-line target: gcp
🚀 Running telemetry script for target: gcp.
✨ Starting Local Telemetry Exporter for Google Cloud ✨
⚙️ Enabled telemetry in workspace settings.
🔧 Set telemetry OTLP endpoint to http://localhost:4317.
🎯 Set telemetry target to gcp.
✅ Workspace settings updated.
✅ Using Google Cloud Project ID: foo-bar
🔑 Please ensure you are authenticated with Google Cloud:
- Run `gcloud auth application-default login` OR ensure `GOOGLE_APPLICATION_CREDENTIALS` environment variable points to a valid service account key.
- The account needs "Cloud Trace Agent", "Monitoring Metric Writer", and "Logs Writer" roles.
✅ otelcol-contrib already exists at /Users/jerop/github/gemini-cli/.gemini/otel/bin/otelcol-contrib
🧹 Cleaning up old processes and logs...
✅ Deleted old GCP collector log.
📄 Wrote OTEL collector config to /Users/jerop/github/gemini-cli/.gemini/otel/collector-gcp.yaml
🚀 Starting OTEL collector for GCP... Logs: /Users/jerop/github/gemini-cli/.gemini/otel/collector-gcp.log
⏳ Waiting for OTEL collector to start (PID: 17013)...
✅ OTEL collector started successfully on port 4317.
✨ Local OTEL collector for GCP is running.
🚀 To send telemetry, run the Gemini CLI in a separate terminal window.
📄 Collector logs are being written to: /Users/jerop/github/gemini-cli/.gemini/otel/collector-gcp.log
📊 View your telemetry data in Google Cloud Console:
- Logs: https://console.cloud.google.com/logs/query;query=logName%3D%22projects%2Ffoo-bar%2Flogs%2Fgemini_cli%22?project=foo-bar
- Metrics: https://console.cloud.google.com/monitoring/metrics-explorer?project=foo-bar
- Traces: https://console.cloud.google.com/traces/list?project=foo-bar
Press Ctrl+C to exit.
^C
👋 Shutting down...
⚙️ Disabled telemetry in workspace settings.
🔧 Cleared telemetry OTLP endpoint.
🎯 Cleared telemetry target.
✅ Workspace settings updated.
🛑 Stopping otelcol-contrib (PID: 17013)...
✅ otelcol-contrib stopped.
```
2025-06-15 00:47:32 -04:00
telemetryLogPrompts : boolean | undefined ;
2025-07-23 17:48:24 -04:00
telemetryOutfile : string | undefined ;
2025-07-09 11:38:38 -07:00
allowedMcpServerNames : string [ ] | undefined ;
2025-08-27 02:17:43 +10:00
allowedTools : string [ ] | undefined ;
2025-07-17 16:25:23 -06:00
experimentalAcp : boolean | undefined ;
2025-07-08 12:57:34 -04:00
extensions : string [ ] | undefined ;
listExtensions : boolean | undefined ;
2025-07-18 02:57:37 +08:00
proxy : string | undefined ;
2025-07-31 05:38:20 +09:00
includeDirectories : string [ ] | undefined ;
2025-08-21 22:29:15 +00:00
screenReader : boolean | undefined ;
2025-08-29 15:45:39 -04:00
useSmartEdit : boolean | undefined ;
2025-09-20 06:01:02 -07:00
useWriteTodos : boolean | undefined ;
2025-09-03 15:48:54 -07:00
promptWords : string [ ] | undefined ;
2025-09-11 05:19:47 +09:00
outputFormat : string | undefined ;
2025-04-18 11:12:18 -07:00
}
2025-08-25 17:02:10 +00:00
export async function parseArguments ( settings : Settings ) : Promise < CliArgs > {
2025-07-09 09:02:59 +02:00
const yargsInstance = yargs ( hideBin ( process . argv ) )
2025-08-23 12:43:03 +08:00
. locale ( 'en' )
2025-07-07 21:13:31 -04:00
. scriptName ( 'gemini' )
. usage (
2025-08-06 11:52:29 -04:00
'Usage: gemini [options] [command]\n\nGemini CLI - Launch an interactive CLI, use -p/--prompt for non-interactive mode' ,
2025-07-07 21:13:31 -04:00
)
2025-09-18 23:13:44 -07:00
. option ( 'telemetry' , {
type : 'boolean' ,
description :
'Enable telemetry? This flag specifically controls if telemetry is sent. Other --telemetry-* flags set specific values but do not enable telemetry on their own.' ,
} )
. option ( 'telemetry-target' , {
type : 'string' ,
choices : [ 'local' , 'gcp' ] ,
description :
'Set the telemetry target (local or gcp). Overrides settings files.' ,
} )
. option ( 'telemetry-otlp-endpoint' , {
type : 'string' ,
description :
'Set the OTLP endpoint for telemetry. Overrides environment variables and settings files.' ,
} )
. option ( 'telemetry-otlp-protocol' , {
type : 'string' ,
choices : [ 'grpc' , 'http' ] ,
description :
'Set the OTLP protocol for telemetry (grpc or http). Overrides settings files.' ,
} )
. option ( 'telemetry-log-prompts' , {
type : 'boolean' ,
description :
'Enable or disable logging of user prompts for telemetry. Overrides settings files.' ,
} )
. option ( 'telemetry-outfile' , {
type : 'string' ,
description : 'Redirect all telemetry output to the specified file.' ,
} )
. deprecateOption (
'telemetry' ,
'Use the "telemetry.enabled" setting in settings.json instead. This flag will be removed in a future version.' ,
)
. deprecateOption (
'telemetry-target' ,
'Use the "telemetry.target" setting in settings.json instead. This flag will be removed in a future version.' ,
)
. deprecateOption (
'telemetry-otlp-endpoint' ,
'Use the "telemetry.otlpEndpoint" setting in settings.json instead. This flag will be removed in a future version.' ,
)
. deprecateOption (
'telemetry-otlp-protocol' ,
'Use the "telemetry.otlpProtocol" setting in settings.json instead. This flag will be removed in a future version.' ,
)
. deprecateOption (
'telemetry-log-prompts' ,
'Use the "telemetry.logPrompts" setting in settings.json instead. This flag will be removed in a future version.' ,
)
. deprecateOption (
'telemetry-outfile' ,
'Use the "telemetry.outfile" setting in settings.json instead. This flag will be removed in a future version.' ,
)
. option ( 'debug' , {
alias : 'd' ,
type : 'boolean' ,
description : 'Run in debug mode?' ,
default : false ,
} )
. option ( 'proxy' , {
type : 'string' ,
description :
'Proxy for gemini client, like schema://user:password@host:port' ,
} )
. deprecateOption (
'proxy' ,
'Use the "proxy" setting in settings.json instead. This flag will be removed in a future version.' ,
)
2025-09-03 15:48:54 -07:00
. command ( '$0 [promptWords...]' , 'Launch Gemini CLI' , ( yargsInstance ) = >
2025-08-06 11:52:29 -04:00
yargsInstance
. option ( 'model' , {
alias : 'm' ,
type : 'string' ,
description : ` Model ` ,
} )
. option ( 'prompt' , {
alias : 'p' ,
type : 'string' ,
description : 'Prompt. Appended to input on stdin (if any).' ,
} )
. option ( 'prompt-interactive' , {
alias : 'i' ,
type : 'string' ,
description :
'Execute the provided prompt and continue in interactive mode' ,
} )
. option ( 'sandbox' , {
alias : 's' ,
type : 'boolean' ,
description : 'Run in sandbox?' ,
} )
. option ( 'sandbox-image' , {
type : 'string' ,
description : 'Sandbox image URI.' ,
} )
. option ( 'all-files' , {
alias : [ 'a' ] ,
type : 'boolean' ,
description : 'Include ALL files in context?' ,
default : false ,
} )
. option ( 'show-memory-usage' , {
type : 'boolean' ,
description : 'Show memory usage in status bar' ,
default : false ,
} )
. option ( 'yolo' , {
alias : 'y' ,
type : 'boolean' ,
description :
'Automatically accept all actions (aka YOLO mode, see https://www.youtube.com/watch?v=xvFZjo5PgG0 for more details)?' ,
default : false ,
} )
2025-08-12 15:10:22 -07:00
. option ( 'approval-mode' , {
type : 'string' ,
choices : [ 'default' , 'auto_edit' , 'yolo' ] ,
description :
'Set the approval mode: default (prompt for approval), auto_edit (auto-approve edit tools), yolo (auto-approve all tools)' ,
} )
2025-08-06 11:52:29 -04:00
. option ( 'checkpointing' , {
alias : 'c' ,
type : 'boolean' ,
description : 'Enables checkpointing of file edits' ,
default : false ,
} )
. option ( 'experimental-acp' , {
type : 'boolean' ,
description : 'Starts the agent in ACP mode' ,
} )
. option ( 'allowed-mcp-server-names' , {
type : 'array' ,
string : true ,
description : 'Allowed MCP server names' ,
2025-09-20 12:49:50 -07:00
coerce : ( mcpServerNames : string [ ] ) = >
// Handle comma-separated values
mcpServerNames . flatMap ( ( mcpServerName ) = >
mcpServerName . split ( ',' ) . map ( ( m ) = > m . trim ( ) ) ,
) ,
2025-08-06 11:52:29 -04:00
} )
2025-08-27 02:17:43 +10:00
. option ( 'allowed-tools' , {
type : 'array' ,
string : true ,
description : 'Tools that are allowed to run without confirmation' ,
2025-09-19 14:50:59 -07:00
coerce : ( tools : string [ ] ) = >
// Handle comma-separated values
tools . flatMap ( ( tool ) = > tool . split ( ',' ) . map ( ( t ) = > t . trim ( ) ) ) ,
2025-08-27 02:17:43 +10:00
} )
2025-08-06 11:52:29 -04:00
. option ( 'extensions' , {
alias : 'e' ,
type : 'array' ,
string : true ,
description :
'A list of extensions to use. If not provided, all extensions are used.' ,
2025-09-20 12:49:50 -07:00
coerce : ( extensions : string [ ] ) = >
// Handle comma-separated values
extensions . flatMap ( ( extension ) = >
extension . split ( ',' ) . map ( ( e ) = > e . trim ( ) ) ,
) ,
2025-08-06 11:52:29 -04:00
} )
. option ( 'list-extensions' , {
alias : 'l' ,
type : 'boolean' ,
description : 'List all available extensions and exit.' ,
} )
. option ( 'include-directories' , {
type : 'array' ,
string : true ,
description :
'Additional directories to include in the workspace (comma-separated or multiple --include-directories)' ,
coerce : ( dirs : string [ ] ) = >
// Handle comma-separated values
dirs . flatMap ( ( dir ) = > dir . split ( ',' ) . map ( ( d ) = > d . trim ( ) ) ) ,
} )
2025-08-21 22:29:15 +00:00
. option ( 'screen-reader' , {
type : 'boolean' ,
description : 'Enable screen reader mode for accessibility.' ,
} )
2025-09-11 05:19:47 +09:00
. option ( 'output-format' , {
2025-09-16 15:45:08 -07:00
alias : 'o' ,
2025-09-11 05:19:47 +09:00
type : 'string' ,
description : 'The format of the CLI output.' ,
choices : [ 'text' , 'json' ] ,
} )
2025-08-28 16:22:59 -07:00
. deprecateOption (
'show-memory-usage' ,
2025-09-07 12:23:57 -07:00
'Use the "ui.showMemoryUsage" setting in settings.json instead. This flag will be removed in a future version.' ,
2025-08-28 16:22:59 -07:00
)
. deprecateOption (
'sandbox-image' ,
2025-09-07 12:23:57 -07:00
'Use the "tools.sandbox" setting in settings.json instead. This flag will be removed in a future version.' ,
2025-08-28 16:22:59 -07:00
)
. deprecateOption (
'checkpointing' ,
2025-09-07 12:23:57 -07:00
'Use the "general.checkpointing.enabled" setting in settings.json instead. This flag will be removed in a future version.' ,
2025-08-28 16:22:59 -07:00
)
. deprecateOption (
'all-files' ,
'Use @ includes in the application instead. This flag will be removed in a future version.' ,
)
2025-09-03 15:48:54 -07:00
. deprecateOption (
'prompt' ,
'Use the positional prompt instead. This flag will be removed in a future version.' ,
)
2025-08-06 11:52:29 -04:00
. check ( ( argv ) = > {
2025-09-03 15:48:54 -07:00
const promptWords = argv [ 'promptWords' ] as string [ ] | undefined ;
if ( argv [ 'prompt' ] && promptWords && promptWords . length > 0 ) {
throw new Error (
'Cannot use both a positional prompt and the --prompt (-p) flag together' ,
) ;
}
if ( argv [ 'prompt' ] && argv [ 'promptInteractive' ] ) {
2025-08-06 11:52:29 -04:00
throw new Error (
'Cannot use both --prompt (-p) and --prompt-interactive (-i) together' ,
) ;
}
2025-08-17 12:43:21 -04:00
if ( argv . yolo && argv [ 'approvalMode' ] ) {
2025-08-12 15:10:22 -07:00
throw new Error (
'Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead.' ,
) ;
}
2025-08-06 11:52:29 -04:00
return true ;
} ) ,
2025-07-08 16:56:12 -04:00
)
2025-08-06 11:52:29 -04:00
// Register MCP subcommands
2025-08-25 17:02:10 +00:00
. command ( mcpCommand ) ;
2025-09-05 11:44:41 -07:00
if ( settings ? . experimental ? . extensionManagement ? ? true ) {
2025-08-25 17:02:10 +00:00
yargsInstance . command ( extensionsCommand ) ;
}
yargsInstance
2025-06-18 09:57:17 -07:00
. version ( await getCliVersion ( ) ) // This will enable the --version flag based on package.json
2025-06-16 01:13:39 -05:00
. alias ( 'v' , 'version' )
2025-04-18 11:12:18 -07:00
. help ( )
. alias ( 'h' , 'help' )
2025-07-11 16:52:56 -07:00
. strict ( )
2025-08-06 11:52:29 -04:00
. demandCommand ( 0 , 0 ) ; // Allow base command to run with no subcommands
2025-05-14 12:37:17 -07:00
2025-07-09 09:02:59 +02:00
yargsInstance . wrap ( yargsInstance . terminalWidth ( ) ) ;
2025-08-06 11:52:29 -04:00
const result = await yargsInstance . parse ( ) ;
// Handle case where MCP subcommands are executed - they should exit the process
// and not return to main CLI logic
2025-08-25 17:02:10 +00:00
if (
result . _ . length > 0 &&
( result . _ [ 0 ] === 'mcp' || result . _ [ 0 ] === 'extensions' )
) {
2025-08-06 11:52:29 -04:00
// MCP commands handle their own execution and process exit
process . exit ( 0 ) ;
}
2025-07-31 22:06:50 +05:30
// The import format is now only controlled by settings.memoryImportFormat
// We no longer accept it as a CLI argument
2025-08-06 11:52:29 -04:00
return result as unknown as CliArgs ;
2025-05-14 12:37:17 -07:00
}
2025-05-23 08:53:22 -07:00
// This function is now a thin wrapper around the server's implementation.
// It's kept in the CLI for now as App.tsx directly calls it for memory refresh.
// TODO: Consider if App.tsx should get memory via a server call or if Config should refresh itself.
2025-05-14 12:37:17 -07:00
export async function loadHierarchicalGeminiMemory (
currentWorkingDirectory : string ,
2025-08-06 02:01:01 +09:00
includeDirectoriesToReadGemini : readonly string [ ] = [ ] ,
2025-05-14 12:37:17 -07:00
debugMode : boolean ,
2025-06-13 20:25:59 -04:00
fileService : FileDiscoveryService ,
2025-07-23 14:48:35 -07:00
settings : Settings ,
2025-06-11 13:34:35 -07:00
extensionContextFilePaths : string [ ] = [ ] ,
2025-08-29 14:12:36 -04:00
folderTrust : boolean ,
2025-07-31 22:06:50 +05:30
memoryImportFormat : 'flat' | 'tree' = 'tree' ,
2025-07-20 00:55:33 -07:00
fileFilteringOptions? : FileFilteringOptions ,
2025-05-14 15:19:45 -07:00
) : Promise < { memoryContent : string ; fileCount : number } > {
2025-08-02 03:52:17 +05:30
// FIX: Use real, canonical paths for a reliable comparison to handle symlinks.
const realCwd = fs . realpathSync ( path . resolve ( currentWorkingDirectory ) ) ;
const realHome = fs . realpathSync ( path . resolve ( homedir ( ) ) ) ;
const isHomeDirectory = realCwd === realHome ;
// If it is the home directory, pass an empty string to the core memory
// function to signal that it should skip the workspace search.
const effectiveCwd = isHomeDirectory ? '' : currentWorkingDirectory ;
2025-05-23 08:53:22 -07:00
if ( debugMode ) {
2025-05-14 12:37:17 -07:00
logger . debug (
2025-07-31 22:06:50 +05:30
` CLI: Delegating hierarchical memory load to server for CWD: ${ currentWorkingDirectory } (memoryImportFormat: ${ memoryImportFormat } ) ` ,
2025-05-14 12:37:17 -07:00
) ;
}
2025-07-20 00:55:33 -07:00
2025-08-02 03:52:17 +05:30
// Directly call the server function with the corrected path.
2025-06-11 13:34:35 -07:00
return loadServerHierarchicalMemory (
2025-08-02 03:52:17 +05:30
effectiveCwd ,
2025-08-06 02:01:01 +09:00
includeDirectoriesToReadGemini ,
2025-06-11 13:34:35 -07:00
debugMode ,
2025-06-13 20:25:59 -04:00
fileService ,
2025-06-11 13:34:35 -07:00
extensionContextFilePaths ,
2025-08-29 14:12:36 -04:00
folderTrust ,
2025-07-31 22:06:50 +05:30
memoryImportFormat ,
2025-07-20 00:55:33 -07:00
fileFilteringOptions ,
2025-08-27 18:39:45 -07:00
settings . context ? . discoveryMaxDirs ,
2025-06-11 13:34:35 -07:00
) ;
2025-04-18 11:12:18 -07:00
}
2025-09-22 19:48:25 -07:00
export function isDebugMode ( argv : CliArgs ) : boolean {
return (
argv . debug ||
[ process . env [ 'DEBUG' ] , process . env [ 'DEBUG_MODE' ] ] . some (
( v ) = > v === 'true' || v === '1' ,
)
) ;
}
2025-06-02 13:55:54 -07:00
export async function loadCliConfig (
settings : Settings ,
2025-06-13 13:57:00 -07:00
extensions : Extension [ ] ,
2025-09-29 06:53:19 -07:00
extensionEnablementManager : ExtensionEnablementManager ,
2025-06-11 04:46:39 +00:00
sessionId : string ,
2025-07-11 16:52:56 -07:00
argv : CliArgs ,
2025-08-13 12:58:26 -03:00
cwd : string = process . cwd ( ) ,
2025-06-07 11:12:30 -07:00
) : Promise < Config > {
2025-09-22 19:48:25 -07:00
const debugMode = isDebugMode ( argv ) ;
2025-08-27 18:39:45 -07:00
const memoryImportFormat = settings . context ? . importFormat || 'tree' ;
2025-07-14 12:04:08 -04:00
2025-08-27 18:39:45 -07:00
const ideMode = settings . ide ? . enabled ? ? false ;
2025-07-25 17:46:55 +00:00
2025-09-02 12:01:22 -04:00
const folderTrust = settings . security ? . folderTrust ? . enabled ? ? false ;
2025-09-22 11:45:02 -07:00
const trustedFolder = isWorkspaceTrusted ( settings ) ? . isTrusted ? ? true ;
2025-08-06 15:27:21 -07:00
2025-07-18 20:45:00 +02:00
const allExtensions = annotateActiveExtensions (
2025-07-08 12:57:34 -04:00
extensions ,
2025-08-26 14:36:55 +00:00
cwd ,
2025-09-29 06:53:19 -07:00
extensionEnablementManager ,
2025-07-08 12:57:34 -04:00
) ;
2025-07-18 20:45:00 +02:00
const activeExtensions = extensions . filter (
( _ , i ) = > allExtensions [ i ] . isActive ,
) ;
2025-05-31 12:49:28 -07:00
// Set the context filename in the server's memoryTool module BEFORE loading memory
// TODO(b/343434939): This is a bit of a hack. The contextFileName should ideally be passed
// directly to the Config constructor in core, and have core handle setGeminiMdFilename.
// However, loadHierarchicalGeminiMemory is called *before* createServerConfig.
2025-08-27 18:39:45 -07:00
if ( settings . context ? . fileName ) {
setServerGeminiMdFilename ( settings . context . fileName ) ;
2025-05-31 12:49:28 -07:00
} else {
// Reset to default if not provided in settings.
setServerGeminiMdFilename ( getCurrentGeminiMdFilename ( ) ) ;
}
2025-07-08 12:57:34 -04:00
const extensionContextFilePaths = activeExtensions . flatMap (
( e ) = > e . contextFiles ,
) ;
2025-06-11 13:34:35 -07:00
2025-08-13 12:58:26 -03:00
const fileService = new FileDiscoveryService ( cwd ) ;
2025-07-20 00:55:33 -07:00
const fileFiltering = {
. . . DEFAULT_MEMORY_FILE_FILTERING_OPTIONS ,
2025-08-27 18:39:45 -07:00
. . . settings . context ? . fileFiltering ,
2025-07-20 00:55:33 -07:00
} ;
2025-08-27 18:39:45 -07:00
const includeDirectories = ( settings . context ? . includeDirectories || [ ] )
2025-08-06 02:01:01 +09:00
. map ( resolvePath )
. concat ( ( argv . includeDirectories || [ ] ) . map ( resolvePath ) ) ;
2025-05-23 08:53:22 -07:00
// Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version
2025-05-14 15:19:45 -07:00
const { memoryContent , fileCount } = await loadHierarchicalGeminiMemory (
2025-08-13 12:58:26 -03:00
cwd ,
2025-08-27 18:39:45 -07:00
settings . context ? . loadMemoryFromIncludeDirectories
? includeDirectories
: [ ] ,
2025-05-14 12:37:17 -07:00
debugMode ,
2025-06-13 20:25:59 -04:00
fileService ,
2025-07-23 14:48:35 -07:00
settings ,
2025-06-11 13:34:35 -07:00
extensionContextFilePaths ,
2025-08-29 14:12:36 -04:00
trustedFolder ,
2025-07-31 22:06:50 +05:30
memoryImportFormat ,
2025-07-20 00:55:33 -07:00
fileFiltering ,
2025-05-14 12:37:17 -07:00
) ;
2025-04-19 19:45:42 +01:00
2025-07-08 12:57:34 -04:00
let mcpServers = mergeMcpServers ( settings , activeExtensions ) ;
2025-09-03 15:48:54 -07:00
const question =
argv . promptInteractive || argv . prompt || ( argv . promptWords || [ ] ) . join ( ' ' ) ;
2025-08-12 15:10:22 -07:00
// Determine approval mode with backward compatibility
let approvalMode : ApprovalMode ;
if ( argv . approvalMode ) {
// New --approval-mode flag takes precedence
switch ( argv . approvalMode ) {
case 'yolo' :
approvalMode = ApprovalMode . YOLO ;
break ;
case 'auto_edit' :
approvalMode = ApprovalMode . AUTO_EDIT ;
break ;
case 'default' :
approvalMode = ApprovalMode . DEFAULT ;
break ;
default :
throw new Error (
` Invalid approval mode: ${ argv . approvalMode } . Valid values are: yolo, auto_edit, default ` ,
) ;
}
} else {
// Fallback to legacy --yolo flag behavior
approvalMode =
argv . yolo || false ? ApprovalMode.YOLO : ApprovalMode.DEFAULT ;
}
2025-08-25 17:30:04 -07:00
// Force approval mode to default if the folder is not trusted.
if ( ! trustedFolder && approvalMode !== ApprovalMode . DEFAULT ) {
logger . warn (
` Approval mode overridden to "default" because the current folder is not trusted. ` ,
) ;
approvalMode = ApprovalMode . DEFAULT ;
}
2025-09-23 03:23:06 +09:00
let telemetrySettings ;
try {
telemetrySettings = await resolveTelemetrySettings ( {
argv ,
env : process.env as unknown as Record < string , string | undefined > ,
settings : settings.telemetry ,
} ) ;
} catch ( err ) {
if ( err instanceof FatalConfigError ) {
throw new FatalConfigError (
` Invalid telemetry configuration: ${ err . message } . ` ,
) ;
}
throw err ;
}
2025-09-18 13:44:23 -07:00
const policyEngineConfig = createPolicyEngineConfig ( settings , approvalMode ) ;
2025-09-22 06:16:21 +09:00
// Fix: If promptWords are provided, always use non-interactive mode
const hasPromptWords = argv . promptWords && argv . promptWords . length > 0 ;
2025-08-07 14:19:06 -07:00
const interactive =
2025-09-22 06:16:21 +09:00
! ! argv . promptInteractive ||
( process . stdin . isTTY && ! hasPromptWords && ! argv . prompt ) ;
2025-08-12 15:10:22 -07:00
// In non-interactive mode, exclude tools that require a prompt.
const extraExcludes : string [ ] = [ ] ;
2025-08-13 12:58:26 -03:00
if ( ! interactive && ! argv . experimentalAcp ) {
2025-08-12 15:10:22 -07:00
switch ( approvalMode ) {
case ApprovalMode . DEFAULT :
// In default non-interactive mode, all tools that require approval are excluded.
2025-09-26 16:22:02 -07:00
extraExcludes . push ( ShellTool . Name , EditTool . Name , WriteFileTool . Name ) ;
2025-08-12 15:10:22 -07:00
break ;
case ApprovalMode . AUTO_EDIT :
// In auto-edit non-interactive mode, only tools that still require a prompt are excluded.
2025-09-26 16:22:02 -07:00
extraExcludes . push ( ShellTool . Name ) ;
2025-08-12 15:10:22 -07:00
break ;
case ApprovalMode . YOLO :
// No extra excludes for YOLO mode.
break ;
default :
// This should never happen due to validation earlier, but satisfies the linter
break ;
}
}
2025-08-07 14:19:06 -07:00
const excludeTools = mergeExcludeTools (
settings ,
activeExtensions ,
2025-08-12 15:10:22 -07:00
extraExcludes . length > 0 ? extraExcludes : undefined ,
2025-08-07 14:19:06 -07:00
) ;
2025-07-18 20:45:00 +02:00
const blockedMcpServers : Array < { name : string ; extensionName : string } > = [ ] ;
2025-06-10 15:48:39 -07:00
2025-07-15 20:45:24 +00:00
if ( ! argv . allowedMcpServerNames ) {
2025-08-27 18:39:45 -07:00
if ( settings . mcp ? . allowed ) {
2025-08-07 16:42:17 -07:00
mcpServers = allowedMcpServers (
mcpServers ,
2025-08-27 18:39:45 -07:00
settings . mcp . allowed ,
2025-08-07 16:42:17 -07:00
blockedMcpServers ,
) ;
2025-07-15 20:45:24 +00:00
}
2025-08-27 18:39:45 -07:00
if ( settings . mcp ? . excluded ) {
const excludedNames = new Set ( settings . mcp . excluded . filter ( Boolean ) ) ;
2025-07-15 20:45:24 +00:00
if ( excludedNames . size > 0 ) {
mcpServers = Object . fromEntries (
Object . entries ( mcpServers ) . filter ( ( [ key ] ) = > ! excludedNames . has ( key ) ) ,
) ;
}
}
}
2025-07-08 16:56:12 -04:00
if ( argv . allowedMcpServerNames ) {
2025-08-07 16:42:17 -07:00
mcpServers = allowedMcpServers (
mcpServers ,
argv . allowedMcpServerNames ,
blockedMcpServers ,
) ;
2025-07-07 09:45:58 -07:00
}
2025-09-24 17:33:14 -04:00
const useModelRouter = settings . experimental ? . useModelRouter ? ? true ;
2025-09-12 15:57:07 -04:00
const defaultModel = useModelRouter
? DEFAULT_GEMINI_MODEL_AUTO
: DEFAULT_GEMINI_MODEL ;
const resolvedModel : string =
argv . model ||
process . env [ 'GEMINI_MODEL' ] ||
settings . model ? . name ||
defaultModel ;
2025-06-18 10:01:00 -07:00
const sandboxConfig = await loadSandboxConfig ( settings , argv ) ;
2025-08-21 22:29:15 +00:00
const screenReader =
2025-08-28 20:52:14 +00:00
argv . screenReader !== undefined
? argv . screenReader
: ( settings . ui ? . accessibility ? . screenReader ? ? false ) ;
2025-06-07 13:49:00 -07:00
return new Config ( {
2025-06-11 04:46:39 +00:00
sessionId ,
2025-06-07 13:38:05 -07:00
embeddingModel : DEFAULT_GEMINI_EMBEDDING_MODEL ,
2025-06-18 10:01:00 -07:00
sandbox : sandboxConfig ,
2025-08-13 12:58:26 -03:00
targetDir : cwd ,
2025-08-06 02:01:01 +09:00
includeDirectories ,
loadMemoryFromIncludeDirectories :
2025-08-27 18:39:45 -07:00
settings . context ? . loadMemoryFromIncludeDirectories || false ,
2025-05-14 12:37:17 -07:00
debugMode ,
2025-08-07 14:19:06 -07:00
question ,
2025-08-25 20:04:35 -04:00
fullContext : argv.allFiles || false ,
2025-08-27 18:39:45 -07:00
coreTools : settings.tools?.core || undefined ,
2025-09-26 16:22:02 -07:00
allowedTools : argv.allowedTools || settings . tools ? . allowed || undefined ,
2025-09-18 13:44:23 -07:00
policyEngineConfig ,
2025-07-01 16:13:46 -07:00
excludeTools ,
2025-08-27 18:39:45 -07:00
toolDiscoveryCommand : settings.tools?.discoveryCommand ,
toolCallCommand : settings.tools?.callCommand ,
mcpServerCommand : settings.mcp?.serverCommand ,
2025-06-10 15:48:39 -07:00
mcpServers ,
2025-05-29 20:51:17 +00:00
userMemory : memoryContent ,
geminiMdFileCount : fileCount ,
2025-08-07 14:19:06 -07:00
approvalMode ,
2025-08-27 18:39:45 -07:00
showMemoryUsage :
argv . showMemoryUsage || settings . ui ? . showMemoryUsage || false ,
2025-08-21 22:29:15 +00:00
accessibility : {
2025-08-27 18:39:45 -07:00
. . . settings . ui ? . accessibility ,
2025-08-21 22:29:15 +00:00
screenReader ,
} ,
2025-09-23 03:23:06 +09:00
telemetry : telemetrySettings ,
2025-08-27 18:39:45 -07:00
usageStatisticsEnabled : settings.privacy?.usageStatisticsEnabled ? ? true ,
2025-09-03 10:25:52 -07:00
fileFiltering : settings.context?.fileFiltering ,
2025-08-27 18:39:45 -07:00
checkpointing :
argv . checkpointing || settings . general ? . checkpointing ? . enabled ,
2025-06-12 17:17:29 -07:00
proxy :
2025-07-18 02:57:37 +08:00
argv . proxy ||
2025-08-17 12:43:21 -04:00
process . env [ 'HTTPS_PROXY' ] ||
process . env [ 'https_proxy' ] ||
process . env [ 'HTTP_PROXY' ] ||
process . env [ 'http_proxy' ] ,
2025-08-13 12:58:26 -03:00
cwd ,
2025-06-13 20:25:59 -04:00
fileDiscoveryService : fileService ,
2025-08-27 18:39:45 -07:00
bugCommand : settings.advanced?.bugCommand ,
2025-09-12 15:57:07 -04:00
model : resolvedModel ,
2025-06-22 16:17:05 -07:00
extensionContextFilePaths ,
2025-08-27 18:39:45 -07:00
maxSessionTurns : settings.model?.maxSessionTurns ? ? - 1 ,
2025-08-13 12:58:26 -03:00
experimentalZedIntegration : argv.experimentalAcp || false ,
2025-07-08 12:57:34 -04:00
listExtensions : argv.listExtensions || false ,
2025-07-18 20:45:00 +02:00
extensions : allExtensions ,
blockedMcpServers ,
2025-08-17 12:43:21 -04:00
noBrowser : ! ! process . env [ 'NO_BROWSER' ] ,
2025-08-27 18:39:45 -07:00
summarizeToolOutput : settings.model?.summarizeToolOutput ,
2025-07-14 12:04:08 -04:00
ideMode ,
2025-08-27 18:39:45 -07:00
chatCompression : settings.model?.chatCompression ,
2025-08-07 14:06:17 -07:00
folderTrust ,
2025-08-08 11:02:27 -07:00
interactive ,
2025-08-13 11:06:31 -07:00
trustedFolder ,
2025-08-27 18:39:45 -07:00
useRipgrep : settings.tools?.useRipgrep ,
2025-09-18 13:05:01 -07:00
shouldUseNodePtyShell : settings.tools?.shell?.enableInteractiveShell ,
2025-08-27 18:39:45 -07:00
skipNextSpeakerCheck : settings.model?.skipNextSpeakerCheck ,
enablePromptCompletion : settings.general?.enablePromptCompletion ? ? false ,
2025-09-05 15:37:29 -07:00
truncateToolOutputThreshold : settings.tools?.truncateToolOutputThreshold ,
truncateToolOutputLines : settings.tools?.truncateToolOutputLines ,
2025-09-09 13:01:25 -07:00
enableToolOutputTruncation : settings.tools?.enableToolOutputTruncation ,
2025-08-28 21:53:56 +02:00
eventEmitter : appEvents ,
2025-08-29 15:45:39 -04:00
useSmartEdit : argv.useSmartEdit ? ? settings . useSmartEdit ,
2025-09-20 06:01:02 -07:00
useWriteTodos : argv.useWriteTodos ? ? settings . useWriteTodos ,
2025-09-11 05:19:47 +09:00
output : {
format : ( argv . outputFormat ? ? settings . output ? . format ) as OutputFormat ,
} ,
2025-09-12 15:57:07 -04:00
useModelRouter ,
2025-09-22 12:03:20 -07:00
enableMessageBusIntegration :
settings . tools ? . enableMessageBusIntegration ? ? false ,
2025-06-07 13:49:00 -07:00
} ) ;
2025-04-18 11:12:18 -07:00
}
2025-08-07 16:42:17 -07:00
function allowedMcpServers (
mcpServers : { [ x : string ] : MCPServerConfig } ,
allowMCPServers : string [ ] ,
blockedMcpServers : Array < { name : string ; extensionName : string } > ,
) {
const allowedNames = new Set ( allowMCPServers . filter ( Boolean ) ) ;
if ( allowedNames . size > 0 ) {
mcpServers = Object . fromEntries (
Object . entries ( mcpServers ) . filter ( ( [ key , server ] ) = > {
const isAllowed = allowedNames . has ( key ) ;
if ( ! isAllowed ) {
blockedMcpServers . push ( {
name : key ,
extensionName : server.extensionName || '' ,
} ) ;
}
return isAllowed ;
} ) ,
) ;
} else {
blockedMcpServers . push (
. . . Object . entries ( mcpServers ) . map ( ( [ key , server ] ) = > ( {
name : key ,
extensionName : server.extensionName || '' ,
} ) ) ,
) ;
mcpServers = { } ;
}
return mcpServers ;
}
2025-06-07 16:17:27 -07:00
2025-06-13 13:57:00 -07:00
function mergeMcpServers ( settings : Settings , extensions : Extension [ ] ) {
2025-06-13 14:51:29 -07:00
const mcpServers = { . . . ( settings . mcpServers || { } ) } ;
2025-06-10 15:48:39 -07:00
for ( const extension of extensions ) {
2025-06-13 13:57:00 -07:00
Object . entries ( extension . config . mcpServers || { } ) . forEach (
( [ key , server ] ) = > {
if ( mcpServers [ key ] ) {
logger . warn (
` Skipping extension MCP config for server with key " ${ key } " as it already exists. ` ,
) ;
return ;
}
2025-07-18 20:45:00 +02:00
mcpServers [ key ] = {
. . . server ,
extensionName : extension.config.name ,
} ;
2025-06-13 13:57:00 -07:00
} ,
) ;
2025-06-10 15:48:39 -07:00
}
return mcpServers ;
}
2025-07-01 16:13:46 -07:00
function mergeExcludeTools (
settings : Settings ,
extensions : Extension [ ] ,
2025-08-07 14:19:06 -07:00
extraExcludes? : string [ ] | undefined ,
2025-07-01 16:13:46 -07:00
) : string [ ] {
2025-08-07 14:19:06 -07:00
const allExcludeTools = new Set ( [
2025-08-27 18:39:45 -07:00
. . . ( settings . tools ? . exclude || [ ] ) ,
2025-08-07 14:19:06 -07:00
. . . ( extraExcludes || [ ] ) ,
] ) ;
2025-07-01 16:13:46 -07:00
for ( const extension of extensions ) {
for ( const tool of extension . config . excludeTools || [ ] ) {
allExcludeTools . add ( tool ) ;
}
}
return [ . . . allExcludeTools ] ;
}