2025-04-18 17:44:24 -07:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
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-25 17:02:10 +00:00
|
|
|
import { extensionsCommand } from '../commands/extensions.js';
|
2026-01-04 20:40:21 -08:00
|
|
|
import { skillsCommand } from '../commands/skills.js';
|
2025-12-03 10:01:57 -08:00
|
|
|
import { hooksCommand } from '../commands/hooks.js';
|
2025-04-19 19:45:42 +01:00
|
|
|
import {
|
|
|
|
|
Config,
|
2025-05-31 12:49:28 -07:00
|
|
|
setGeminiMdFilename as setServerGeminiMdFilename,
|
|
|
|
|
getCurrentGeminiMdFilename,
|
2025-06-02 22:05:45 +02:00
|
|
|
ApprovalMode,
|
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-10-24 13:20:17 -07:00
|
|
|
DEFAULT_FILE_FILTERING_OPTIONS,
|
2025-07-20 00:55:33 -07:00
|
|
|
DEFAULT_MEMORY_FILE_FILTERING_OPTIONS,
|
2025-06-13 20:25:59 -04:00
|
|
|
FileDiscoveryService,
|
2025-10-09 00:33:31 -04:00
|
|
|
WRITE_FILE_TOOL_NAME,
|
2025-10-06 12:15:21 -07:00
|
|
|
SHELL_TOOL_NAMES,
|
2025-10-17 21:07:26 -04:00
|
|
|
SHELL_TOOL_NAME,
|
2025-09-23 03:23:06 +09:00
|
|
|
resolveTelemetrySettings,
|
|
|
|
|
FatalConfigError,
|
2025-10-17 09:07:18 -07:00
|
|
|
getPty,
|
2025-10-19 20:53:53 -04:00
|
|
|
EDIT_TOOL_NAME,
|
2025-10-20 18:16:47 -04:00
|
|
|
debugLogger,
|
2025-11-07 09:07:25 -08:00
|
|
|
loadServerHierarchicalMemory,
|
2025-12-01 22:43:14 +05:30
|
|
|
WEB_FETCH_TOOL_NAME,
|
2025-12-09 16:38:33 -08:00
|
|
|
getVersion,
|
2025-12-17 09:43:21 -08:00
|
|
|
PREVIEW_GEMINI_MODEL_AUTO,
|
2025-12-23 16:10:46 -05:00
|
|
|
type HookDefinition,
|
|
|
|
|
type HookEventName,
|
|
|
|
|
type OutputFormat,
|
2026-01-23 11:45:46 -05:00
|
|
|
coreEvents,
|
2026-01-14 19:07:51 -05:00
|
|
|
GEMINI_MODEL_ALIAS_AUTO,
|
2026-01-30 13:05:22 -05:00
|
|
|
getAdminErrorMessage,
|
2025-06-25 05:41:11 -07:00
|
|
|
} from '@google/gemini-cli-core';
|
2026-01-15 09:26:10 -08:00
|
|
|
import {
|
|
|
|
|
type Settings,
|
|
|
|
|
type MergedSettings,
|
|
|
|
|
saveModelChange,
|
|
|
|
|
loadSettings,
|
|
|
|
|
} from './settings.js';
|
2025-06-19 16:52:22 -07:00
|
|
|
|
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-11-19 09:22:17 -07:00
|
|
|
import { RESUME_LATEST } from '../utils/sessionUtils.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-10-28 09:04:30 -07:00
|
|
|
import { ExtensionManager } from './extension-manager.js';
|
2026-01-22 15:38:06 -08:00
|
|
|
import { McpServerEnablementManager } from './mcp/mcpServerEnablement.js';
|
2025-11-07 09:07:25 -08:00
|
|
|
import type { ExtensionEvents } from '@google/gemini-cli-core/src/utils/extensionLoader.js';
|
2025-10-28 09:04:30 -07:00
|
|
|
import { requestConsentNonInteractive } from './extensions/consent.js';
|
|
|
|
|
import { promptForSetting } from './extensions/extensionSettings.js';
|
2025-11-04 07:51:18 -08:00
|
|
|
import type { EventEmitter } from 'node:stream';
|
2025-11-20 10:44:02 -08:00
|
|
|
import { runExitCleanup } from '../utils/cleanup.js';
|
2025-08-13 11:06:31 -07:00
|
|
|
|
2025-07-11 16:52:56 -07:00
|
|
|
export interface CliArgs {
|
2025-09-30 21:18:04 -04:00
|
|
|
query: string | undefined;
|
2025-04-18 11:12:18 -07:00
|
|
|
model: string | undefined;
|
2025-05-02 08:15:46 -07:00
|
|
|
sandbox: boolean | 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-10-16 12:09:21 -07:00
|
|
|
|
2025-06-02 22:05:45 +02:00
|
|
|
yolo: boolean | undefined;
|
2025-08-12 15:10:22 -07:00
|
|
|
approvalMode: 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-11-19 09:22:17 -07:00
|
|
|
resume: string | typeof RESUME_LATEST | undefined;
|
2025-11-10 18:31:00 -07:00
|
|
|
listSessions: boolean | undefined;
|
|
|
|
|
deleteSession: 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-09-20 06:01:02 -07:00
|
|
|
useWriteTodos: boolean | undefined;
|
2025-09-11 05:19:47 +09:00
|
|
|
outputFormat: string | undefined;
|
2025-10-23 16:10:43 -07:00
|
|
|
fakeResponses: string | undefined;
|
2025-10-28 12:13:45 -07:00
|
|
|
recordResponses: string | undefined;
|
2026-01-23 05:08:53 +05:30
|
|
|
startupMessages?: string[];
|
2026-01-20 23:58:37 -05:00
|
|
|
rawOutput: boolean | undefined;
|
|
|
|
|
acceptRawOutputRisk: boolean | undefined;
|
2026-01-21 12:38:20 -05:00
|
|
|
isCommand: boolean | undefined;
|
2025-04-18 11:12:18 -07:00
|
|
|
}
|
|
|
|
|
|
2026-01-15 09:26:10 -08:00
|
|
|
export async function parseArguments(
|
|
|
|
|
settings: MergedSettings,
|
|
|
|
|
): Promise<CliArgs> {
|
2025-09-30 21:18:04 -04:00
|
|
|
const rawArgv = hideBin(process.argv);
|
2026-01-23 05:08:53 +05:30
|
|
|
const startupMessages: string[] = [];
|
2025-09-30 21:18:04 -04:00
|
|
|
const yargsInstance = yargs(rawArgv)
|
2025-08-23 12:43:03 +08:00
|
|
|
.locale('en')
|
2025-07-07 21:13:31 -04:00
|
|
|
.scriptName('gemini')
|
|
|
|
|
.usage(
|
2026-01-23 05:08:53 +05:30
|
|
|
'Usage: gemini [options] [command]\n\nGemini CLI - Defaults to interactive mode. Use -p/--prompt for non-interactive (headless) mode.',
|
2025-07-07 21:13:31 -04:00
|
|
|
)
|
2025-09-18 23:13:44 -07:00
|
|
|
.option('debug', {
|
|
|
|
|
alias: 'd',
|
|
|
|
|
type: 'boolean',
|
2026-01-14 08:48:52 -05:00
|
|
|
description: 'Run in debug mode (open debug console with F12)',
|
2025-09-18 23:13:44 -07:00
|
|
|
default: false,
|
|
|
|
|
})
|
2025-09-30 21:18:04 -04:00
|
|
|
.command('$0 [query..]', 'Launch Gemini CLI', (yargsInstance) =>
|
2025-08-06 11:52:29 -04:00
|
|
|
yargsInstance
|
2025-09-30 21:18:04 -04:00
|
|
|
.positional('query', {
|
|
|
|
|
description:
|
2026-01-23 05:08:53 +05:30
|
|
|
'Initial prompt. Runs in interactive mode by default; use -p/--prompt for non-interactive.',
|
2025-09-30 21:18:04 -04:00
|
|
|
})
|
2025-08-06 11:52:29 -04:00
|
|
|
.option('model', {
|
|
|
|
|
alias: 'm',
|
|
|
|
|
type: 'string',
|
2025-10-15 15:00:23 -07:00
|
|
|
nargs: 1,
|
2025-08-06 11:52:29 -04:00
|
|
|
description: `Model`,
|
|
|
|
|
})
|
|
|
|
|
.option('prompt', {
|
|
|
|
|
alias: 'p',
|
|
|
|
|
type: 'string',
|
2025-10-15 15:00:23 -07:00
|
|
|
nargs: 1,
|
2026-01-23 05:08:53 +05:30
|
|
|
description:
|
|
|
|
|
'Run in non-interactive (headless) mode with the given prompt. Appended to input on stdin (if any).',
|
2025-08-06 11:52:29 -04:00
|
|
|
})
|
|
|
|
|
.option('prompt-interactive', {
|
|
|
|
|
alias: 'i',
|
|
|
|
|
type: 'string',
|
2025-10-15 15:00:23 -07:00
|
|
|
nargs: 1,
|
2025-08-06 11:52:29 -04:00
|
|
|
description:
|
|
|
|
|
'Execute the provided prompt and continue in interactive mode',
|
|
|
|
|
})
|
|
|
|
|
.option('sandbox', {
|
|
|
|
|
alias: 's',
|
|
|
|
|
type: 'boolean',
|
|
|
|
|
description: 'Run in sandbox?',
|
|
|
|
|
})
|
2025-10-16 12:09:21 -07:00
|
|
|
|
2025-08-06 11:52:29 -04:00
|
|
|
.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',
|
2025-10-15 15:00:23 -07:00
|
|
|
nargs: 1,
|
2026-01-15 17:00:19 -05:00
|
|
|
choices: ['default', 'auto_edit', 'yolo', 'plan'],
|
2025-08-12 15:10:22 -07:00
|
|
|
description:
|
2026-01-15 17:00:19 -05:00
|
|
|
'Set the approval mode: default (prompt for approval), auto_edit (auto-approve edit tools), yolo (auto-approve all tools), plan (read-only mode)',
|
2025-08-12 15:10:22 -07:00
|
|
|
})
|
2025-08-06 11:52:29 -04:00
|
|
|
.option('experimental-acp', {
|
|
|
|
|
type: 'boolean',
|
|
|
|
|
description: 'Starts the agent in ACP mode',
|
|
|
|
|
})
|
|
|
|
|
.option('allowed-mcp-server-names', {
|
|
|
|
|
type: 'array',
|
|
|
|
|
string: true,
|
2025-10-15 15:00:23 -07:00
|
|
|
nargs: 1,
|
2025-08-06 11:52:29 -04:00
|
|
|
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,
|
2025-10-15 15:00:23 -07:00
|
|
|
nargs: 1,
|
2025-08-27 02:17:43 +10:00
|
|
|
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,
|
2025-10-09 05:32:05 +09:00
|
|
|
nargs: 1,
|
2025-08-06 11:52:29 -04:00
|
|
|
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.',
|
|
|
|
|
})
|
2025-11-10 18:31:00 -07:00
|
|
|
.option('resume', {
|
|
|
|
|
alias: 'r',
|
|
|
|
|
type: 'string',
|
|
|
|
|
// `skipValidation` so that we can distinguish between it being passed with a value, without
|
|
|
|
|
// one, and not being passed at all.
|
|
|
|
|
skipValidation: true,
|
|
|
|
|
description:
|
|
|
|
|
'Resume a previous session. Use "latest" for most recent or index number (e.g. --resume 5)',
|
|
|
|
|
coerce: (value: string): string => {
|
|
|
|
|
// When --resume passed with a value (`gemini --resume 123`): value = "123" (string)
|
|
|
|
|
// When --resume passed without a value (`gemini --resume`): value = "" (string)
|
|
|
|
|
// When --resume not passed at all: this `coerce` function is not called at all, and
|
|
|
|
|
// `yargsInstance.argv.resume` is undefined.
|
|
|
|
|
if (value === '') {
|
2025-11-19 09:22:17 -07:00
|
|
|
return RESUME_LATEST;
|
2025-11-10 18:31:00 -07:00
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
.option('list-sessions', {
|
|
|
|
|
type: 'boolean',
|
|
|
|
|
description:
|
|
|
|
|
'List available sessions for the current project and exit.',
|
|
|
|
|
})
|
|
|
|
|
.option('delete-session', {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description:
|
|
|
|
|
'Delete a session by index number (use --list-sessions to see available sessions).',
|
|
|
|
|
})
|
2025-08-06 11:52:29 -04:00
|
|
|
.option('include-directories', {
|
|
|
|
|
type: 'array',
|
|
|
|
|
string: true,
|
2025-10-15 15:00:23 -07:00
|
|
|
nargs: 1,
|
2025-08-06 11:52:29 -04:00
|
|
|
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',
|
2025-10-15 15:00:23 -07:00
|
|
|
nargs: 1,
|
2025-09-11 05:19:47 +09:00
|
|
|
description: 'The format of the CLI output.',
|
2025-10-15 13:55:37 -07:00
|
|
|
choices: ['text', 'json', 'stream-json'],
|
2025-09-11 05:19:47 +09:00
|
|
|
})
|
2025-10-23 16:10:43 -07:00
|
|
|
.option('fake-responses', {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: 'Path to a file with fake model responses for testing.',
|
2025-10-28 12:13:45 -07:00
|
|
|
hidden: true,
|
|
|
|
|
})
|
|
|
|
|
.option('record-responses', {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: 'Path to a file to record model responses for testing.',
|
|
|
|
|
hidden: true,
|
2026-01-20 23:58:37 -05:00
|
|
|
})
|
|
|
|
|
.option('raw-output', {
|
|
|
|
|
type: 'boolean',
|
|
|
|
|
description:
|
|
|
|
|
'Disable sanitization of model output (e.g. allow ANSI escape sequences). WARNING: This can be a security risk if the model output is untrusted.',
|
|
|
|
|
})
|
|
|
|
|
.option('accept-raw-output-risk', {
|
|
|
|
|
type: 'boolean',
|
|
|
|
|
description: 'Suppress the security warning when using --raw-output.',
|
2026-01-15 04:39:27 +05:30
|
|
|
}),
|
2025-07-08 16:56:12 -04:00
|
|
|
)
|
2025-08-06 11:52:29 -04:00
|
|
|
// Register MCP subcommands
|
2025-11-20 10:44:02 -08:00
|
|
|
.command(mcpCommand)
|
|
|
|
|
// Ensure validation flows through .fail() for clean UX
|
|
|
|
|
.fail((msg, err) => {
|
|
|
|
|
if (err) throw err;
|
|
|
|
|
throw new Error(msg);
|
|
|
|
|
})
|
|
|
|
|
.check((argv) => {
|
|
|
|
|
// The 'query' positional can be a string (for one arg) or string[] (for multiple).
|
|
|
|
|
// This guard safely checks if any positional argument was provided.
|
|
|
|
|
const query = argv['query'] as string | string[] | undefined;
|
|
|
|
|
const hasPositionalQuery = Array.isArray(query)
|
|
|
|
|
? query.length > 0
|
|
|
|
|
: !!query;
|
|
|
|
|
|
|
|
|
|
if (argv['prompt'] && hasPositionalQuery) {
|
|
|
|
|
return 'Cannot use both a positional prompt and the --prompt (-p) flag together';
|
|
|
|
|
}
|
|
|
|
|
if (argv['prompt'] && argv['promptInteractive']) {
|
|
|
|
|
return 'Cannot use both --prompt (-p) and --prompt-interactive (-i) together';
|
|
|
|
|
}
|
|
|
|
|
if (argv['yolo'] && argv['approvalMode']) {
|
|
|
|
|
return 'Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead.';
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
argv['outputFormat'] &&
|
|
|
|
|
!['text', 'json', 'stream-json'].includes(
|
|
|
|
|
argv['outputFormat'] as string,
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
return `Invalid values:\n Argument: output-format, Given: "${argv['outputFormat']}", Choices: "text", "json", "stream-json"`;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
2025-08-25 17:02:10 +00:00
|
|
|
|
2026-01-16 15:10:55 -08:00
|
|
|
if (settings.experimental?.extensionManagement) {
|
2025-08-25 17:02:10 +00:00
|
|
|
yargsInstance.command(extensionsCommand);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-27 23:56:04 -05:00
|
|
|
if (settings.skills?.enabled ?? true) {
|
2026-01-04 20:40:21 -08:00
|
|
|
yargsInstance.command(skillsCommand);
|
|
|
|
|
}
|
2025-12-03 10:01:57 -08:00
|
|
|
// Register hooks command if hooks are enabled
|
2026-01-30 10:15:48 -05:00
|
|
|
if (settings.hooksConfig.enabled) {
|
2025-12-03 10:01:57 -08:00
|
|
|
yargsInstance.command(hooksCommand);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-25 17:02:10 +00:00
|
|
|
yargsInstance
|
2025-12-09 16:38:33 -08:00
|
|
|
.version(await getVersion()) // 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-11-20 10:44:02 -08:00
|
|
|
.demandCommand(0, 0) // Allow base command to run with no subcommands
|
|
|
|
|
.exitProcess(false);
|
2025-05-14 12:37:17 -07:00
|
|
|
|
2025-07-09 09:02:59 +02:00
|
|
|
yargsInstance.wrap(yargsInstance.terminalWidth());
|
2025-11-20 10:44:02 -08:00
|
|
|
let result;
|
|
|
|
|
try {
|
|
|
|
|
result = await yargsInstance.parse();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
const msg = e instanceof Error ? e.message : String(e);
|
|
|
|
|
debugLogger.error(msg);
|
|
|
|
|
yargsInstance.showHelp();
|
|
|
|
|
await runExitCleanup();
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle help and version flags manually since we disabled exitProcess
|
|
|
|
|
if (result['help'] || result['version']) {
|
|
|
|
|
await runExitCleanup();
|
|
|
|
|
process.exit(0);
|
|
|
|
|
}
|
2025-08-06 11:52:29 -04:00
|
|
|
|
2025-09-30 21:18:04 -04:00
|
|
|
// Normalize query args: handle both quoted "@path file" and unquoted @path file
|
|
|
|
|
const queryArg = (result as { query?: string | string[] | undefined }).query;
|
|
|
|
|
const q: string | undefined = Array.isArray(queryArg)
|
|
|
|
|
? queryArg.join(' ')
|
|
|
|
|
: queryArg;
|
|
|
|
|
|
2026-01-23 05:08:53 +05:30
|
|
|
// -p/--prompt forces non-interactive mode; positional args default to interactive in TTY
|
2025-09-30 21:18:04 -04:00
|
|
|
if (q && !result['prompt']) {
|
2026-01-23 05:08:53 +05:30
|
|
|
if (process.stdin.isTTY) {
|
|
|
|
|
startupMessages.push(
|
|
|
|
|
'Positional arguments now default to interactive mode. To run in non-interactive mode, use the --prompt (-p) flag.',
|
|
|
|
|
);
|
2025-09-30 21:18:04 -04:00
|
|
|
result['promptInteractive'] = q;
|
|
|
|
|
} else {
|
|
|
|
|
result['prompt'] = q;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Keep CliArgs.query as a string for downstream typing
|
|
|
|
|
(result as Record<string, unknown>)['query'] = q || undefined;
|
2026-01-23 05:08:53 +05:30
|
|
|
(result as Record<string, unknown>)['startupMessages'] = startupMessages;
|
2025-09-30 21:18:04 -04:00
|
|
|
|
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-10-06 12:15:21 -07:00
|
|
|
/**
|
|
|
|
|
* Creates a filter function to determine if a tool should be excluded.
|
|
|
|
|
*
|
|
|
|
|
* In non-interactive mode, we want to disable tools that require user
|
|
|
|
|
* interaction to prevent the CLI from hanging. This function creates a predicate
|
|
|
|
|
* that returns `true` if a tool should be excluded.
|
|
|
|
|
*
|
|
|
|
|
* A tool is excluded if it's not in the `allowedToolsSet`. The shell tool
|
|
|
|
|
* has a special case: it's not excluded if any of its subcommands
|
|
|
|
|
* are in the `allowedTools` list.
|
|
|
|
|
*
|
|
|
|
|
* @param allowedTools A list of explicitly allowed tool names.
|
|
|
|
|
* @param allowedToolsSet A set of explicitly allowed tool names for quick lookups.
|
|
|
|
|
* @returns A function that takes a tool name and returns `true` if it should be excluded.
|
|
|
|
|
*/
|
|
|
|
|
function createToolExclusionFilter(
|
|
|
|
|
allowedTools: string[],
|
|
|
|
|
allowedToolsSet: Set<string>,
|
|
|
|
|
) {
|
|
|
|
|
return (tool: string): boolean => {
|
2025-10-17 21:07:26 -04:00
|
|
|
if (tool === SHELL_TOOL_NAME) {
|
2025-10-06 12:15:21 -07:00
|
|
|
// If any of the allowed tools is ShellTool (even with subcommands), don't exclude it.
|
|
|
|
|
return !allowedTools.some((allowed) =>
|
|
|
|
|
SHELL_TOOL_NAMES.some((shellName) => allowed.startsWith(shellName)),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return !allowedToolsSet.has(tool);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
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-12-23 16:10:46 -05:00
|
|
|
export interface LoadCliConfigOptions {
|
|
|
|
|
cwd?: string;
|
|
|
|
|
projectHooks?: { [K in HookEventName]?: HookDefinition[] } & {
|
|
|
|
|
disabled?: string[];
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-02 13:55:54 -07:00
|
|
|
export async function loadCliConfig(
|
2026-01-15 09:26:10 -08:00
|
|
|
settings: MergedSettings,
|
2025-06-11 04:46:39 +00:00
|
|
|
sessionId: string,
|
2025-07-11 16:52:56 -07:00
|
|
|
argv: CliArgs,
|
2025-12-23 16:10:46 -05:00
|
|
|
options: LoadCliConfigOptions = {},
|
2025-06-07 11:12:30 -07:00
|
|
|
): Promise<Config> {
|
2025-12-23 16:10:46 -05:00
|
|
|
const { cwd = process.cwd(), projectHooks } = options;
|
2025-09-22 19:48:25 -07:00
|
|
|
const debugMode = isDebugMode(argv);
|
|
|
|
|
|
2025-12-23 19:53:43 +05:30
|
|
|
const loadedSettings = loadSettings(cwd);
|
|
|
|
|
|
2025-10-24 14:23:39 -07:00
|
|
|
if (argv.sandbox) {
|
|
|
|
|
process.env['GEMINI_SANDBOX'] = 'true';
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2026-02-03 00:54:10 -05:00
|
|
|
const trustedFolder = isWorkspaceTrusted(settings, cwd)?.isTrusted ?? false;
|
2025-08-06 15:27:21 -07:00
|
|
|
|
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-08-13 12:58:26 -03:00
|
|
|
const fileService = new FileDiscoveryService(cwd);
|
2025-07-20 00:55:33 -07:00
|
|
|
|
2025-10-24 13:20:17 -07:00
|
|
|
const memoryFileFiltering = {
|
2025-07-20 00:55:33 -07:00
|
|
|
...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-10-24 13:20:17 -07:00
|
|
|
const fileFiltering = {
|
|
|
|
|
...DEFAULT_FILE_FILTERING_OPTIONS,
|
|
|
|
|
...settings.context?.fileFiltering,
|
|
|
|
|
};
|
|
|
|
|
|
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-10-28 09:04:30 -07:00
|
|
|
const extensionManager = new ExtensionManager({
|
|
|
|
|
settings,
|
|
|
|
|
requestConsent: requestConsentNonInteractive,
|
|
|
|
|
requestSetting: promptForSetting,
|
|
|
|
|
workspaceDir: cwd,
|
|
|
|
|
enabledExtensionOverrides: argv.extensions,
|
2026-01-23 11:45:46 -05:00
|
|
|
eventEmitter: coreEvents as EventEmitter<ExtensionEvents>,
|
2026-01-20 22:01:18 +00:00
|
|
|
clientVersion: await getVersion(),
|
2025-10-28 09:04:30 -07:00
|
|
|
});
|
2025-10-28 14:48:50 -04:00
|
|
|
await extensionManager.loadExtensions();
|
2025-10-28 09:04:30 -07:00
|
|
|
|
2025-12-19 07:04:03 -10:00
|
|
|
const experimentalJitContext = settings.experimental?.jitContext ?? false;
|
|
|
|
|
|
|
|
|
|
let memoryContent = '';
|
|
|
|
|
let fileCount = 0;
|
|
|
|
|
let filePaths: string[] = [];
|
|
|
|
|
|
|
|
|
|
if (!experimentalJitContext) {
|
|
|
|
|
// Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version
|
|
|
|
|
const result = await loadServerHierarchicalMemory(
|
2025-10-02 17:46:54 -04:00
|
|
|
cwd,
|
2026-01-21 09:58:23 -08:00
|
|
|
settings.context?.loadMemoryFromIncludeDirectories || false
|
|
|
|
|
? includeDirectories
|
|
|
|
|
: [],
|
2025-10-02 17:46:54 -04:00
|
|
|
debugMode,
|
|
|
|
|
fileService,
|
2025-10-28 09:04:30 -07:00
|
|
|
extensionManager,
|
2025-10-02 17:46:54 -04:00
|
|
|
trustedFolder,
|
|
|
|
|
memoryImportFormat,
|
2025-10-24 13:20:17 -07:00
|
|
|
memoryFileFiltering,
|
2025-11-07 09:07:25 -08:00
|
|
|
settings.context?.discoveryMaxDirs,
|
2025-10-02 17:46:54 -04:00
|
|
|
);
|
2025-12-19 07:04:03 -10:00
|
|
|
memoryContent = result.memoryContent;
|
|
|
|
|
fileCount = result.fileCount;
|
|
|
|
|
filePaths = result.filePaths;
|
|
|
|
|
}
|
2025-04-19 19:45:42 +01:00
|
|
|
|
2025-09-30 21:18:04 -04:00
|
|
|
const question = argv.promptInteractive || argv.prompt || '';
|
2025-08-12 15:10:22 -07:00
|
|
|
|
|
|
|
|
// Determine approval mode with backward compatibility
|
|
|
|
|
let approvalMode: ApprovalMode;
|
2026-01-23 18:14:11 -05:00
|
|
|
const rawApprovalMode =
|
|
|
|
|
argv.approvalMode ||
|
|
|
|
|
(argv.yolo ? 'yolo' : undefined) ||
|
|
|
|
|
((settings.tools?.approvalMode as string) !== 'yolo'
|
|
|
|
|
? settings.tools.approvalMode
|
|
|
|
|
: undefined);
|
|
|
|
|
|
|
|
|
|
if (rawApprovalMode) {
|
|
|
|
|
switch (rawApprovalMode) {
|
2025-08-12 15:10:22 -07:00
|
|
|
case 'yolo':
|
|
|
|
|
approvalMode = ApprovalMode.YOLO;
|
|
|
|
|
break;
|
|
|
|
|
case 'auto_edit':
|
|
|
|
|
approvalMode = ApprovalMode.AUTO_EDIT;
|
|
|
|
|
break;
|
2026-01-15 17:00:19 -05:00
|
|
|
case 'plan':
|
|
|
|
|
if (!(settings.experimental?.plan ?? false)) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
'Approval mode "plan" is only available when experimental.plan is enabled.',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
approvalMode = ApprovalMode.PLAN;
|
|
|
|
|
break;
|
2025-08-12 15:10:22 -07:00
|
|
|
case 'default':
|
|
|
|
|
approvalMode = ApprovalMode.DEFAULT;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new Error(
|
2026-01-23 18:14:11 -05:00
|
|
|
`Invalid approval mode: ${rawApprovalMode}. Valid values are: yolo, auto_edit, plan, default`,
|
2025-08-12 15:10:22 -07:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2026-01-23 18:14:11 -05:00
|
|
|
approvalMode = ApprovalMode.DEFAULT;
|
2025-08-12 15:10:22 -07:00
|
|
|
}
|
|
|
|
|
|
2025-10-22 11:57:10 -07:00
|
|
|
// Override approval mode if disableYoloMode is set.
|
2026-01-06 16:38:07 -05:00
|
|
|
if (settings.security?.disableYoloMode || settings.admin?.secureModeEnabled) {
|
2025-10-22 11:57:10 -07:00
|
|
|
if (approvalMode === ApprovalMode.YOLO) {
|
2026-01-06 16:38:07 -05:00
|
|
|
if (settings.admin?.secureModeEnabled) {
|
|
|
|
|
debugLogger.error(
|
|
|
|
|
'YOLO mode is disabled by "secureModeEnabled" setting.',
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
debugLogger.error(
|
|
|
|
|
'YOLO mode is disabled by the "disableYolo" setting.',
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-10-22 11:57:10 -07:00
|
|
|
throw new FatalConfigError(
|
2026-01-30 13:05:22 -05:00
|
|
|
getAdminErrorMessage('YOLO mode', undefined /* config */),
|
2025-10-22 11:57:10 -07:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else if (approvalMode === ApprovalMode.YOLO) {
|
|
|
|
|
debugLogger.warn(
|
|
|
|
|
'YOLO mode is enabled. All tool calls will be automatically approved.',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-25 17:30:04 -07:00
|
|
|
// Force approval mode to default if the folder is not trusted.
|
|
|
|
|
if (!trustedFolder && approvalMode !== ApprovalMode.DEFAULT) {
|
2025-10-20 18:16:47 -04:00
|
|
|
debugLogger.warn(
|
2025-08-25 17:30:04 -07:00
|
|
|
`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({
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-23 05:08:53 +05:30
|
|
|
// -p/--prompt forces non-interactive (headless) mode
|
|
|
|
|
// -i/--prompt-interactive forces interactive mode with an initial prompt
|
2025-08-07 14:19:06 -07:00
|
|
|
const interactive =
|
2025-09-22 06:16:21 +09:00
|
|
|
!!argv.promptInteractive ||
|
2025-11-20 15:28:37 +00:00
|
|
|
!!argv.experimentalAcp ||
|
2026-01-23 05:08:53 +05:30
|
|
|
(process.stdin.isTTY && !argv.query && !argv.prompt && !argv.isCommand);
|
2026-01-04 00:19:00 -05:00
|
|
|
|
|
|
|
|
const allowedTools = argv.allowedTools || settings.tools?.allowed || [];
|
|
|
|
|
const allowedToolsSet = new Set(allowedTools);
|
|
|
|
|
|
2025-08-12 15:10:22 -07:00
|
|
|
// In non-interactive mode, exclude tools that require a prompt.
|
|
|
|
|
const extraExcludes: string[] = [];
|
2025-11-20 15:28:37 +00:00
|
|
|
if (!interactive) {
|
2025-10-09 00:33:31 -04:00
|
|
|
const defaultExcludes = [
|
2025-10-17 21:07:26 -04:00
|
|
|
SHELL_TOOL_NAME,
|
2025-10-19 20:53:53 -04:00
|
|
|
EDIT_TOOL_NAME,
|
2025-10-09 00:33:31 -04:00
|
|
|
WRITE_FILE_TOOL_NAME,
|
2025-12-01 22:43:14 +05:30
|
|
|
WEB_FETCH_TOOL_NAME,
|
2025-10-09 00:33:31 -04:00
|
|
|
];
|
2025-10-17 21:07:26 -04:00
|
|
|
const autoEditExcludes = [SHELL_TOOL_NAME];
|
2025-10-06 12:15:21 -07:00
|
|
|
|
|
|
|
|
const toolExclusionFilter = createToolExclusionFilter(
|
|
|
|
|
allowedTools,
|
|
|
|
|
allowedToolsSet,
|
|
|
|
|
);
|
|
|
|
|
|
2025-08-12 15:10:22 -07:00
|
|
|
switch (approvalMode) {
|
2026-01-15 17:00:19 -05:00
|
|
|
case ApprovalMode.PLAN:
|
|
|
|
|
// In plan non-interactive mode, all tools that require approval are excluded.
|
|
|
|
|
// TODO(#16625): Replace this default exclusion logic with specific rules for plan mode.
|
|
|
|
|
extraExcludes.push(...defaultExcludes.filter(toolExclusionFilter));
|
|
|
|
|
break;
|
2025-08-12 15:10:22 -07:00
|
|
|
case ApprovalMode.DEFAULT:
|
|
|
|
|
// In default non-interactive mode, all tools that require approval are excluded.
|
2025-10-06 12:15:21 -07:00
|
|
|
extraExcludes.push(...defaultExcludes.filter(toolExclusionFilter));
|
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-10-06 12:15:21 -07:00
|
|
|
extraExcludes.push(...autoEditExcludes.filter(toolExclusionFilter));
|
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
|
|
|
|
2026-01-15 09:26:10 -08:00
|
|
|
const excludeTools = mergeExcludeTools(settings, extraExcludes);
|
2025-07-07 09:45:58 -07:00
|
|
|
|
2026-01-04 00:19:00 -05:00
|
|
|
// Create a settings object that includes CLI overrides for policy generation
|
|
|
|
|
const effectiveSettings: Settings = {
|
|
|
|
|
...settings,
|
|
|
|
|
tools: {
|
|
|
|
|
...settings.tools,
|
|
|
|
|
allowed: allowedTools,
|
|
|
|
|
exclude: excludeTools,
|
|
|
|
|
},
|
|
|
|
|
mcp: {
|
|
|
|
|
...settings.mcp,
|
|
|
|
|
allowed: argv.allowedMcpServerNames ?? settings.mcp?.allowed,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const policyEngineConfig = await createPolicyEngineConfig(
|
|
|
|
|
effectiveSettings,
|
|
|
|
|
approvalMode,
|
|
|
|
|
);
|
|
|
|
|
policyEngineConfig.nonInteractive = !interactive;
|
|
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
const defaultModel = settings.general?.previewFeatures
|
|
|
|
|
? PREVIEW_GEMINI_MODEL_AUTO
|
|
|
|
|
: DEFAULT_GEMINI_MODEL_AUTO;
|
2026-01-14 19:07:51 -05:00
|
|
|
const specifiedModel =
|
|
|
|
|
argv.model || process.env['GEMINI_MODEL'] || settings.model?.name;
|
2025-09-12 15:57:07 -04:00
|
|
|
|
2026-01-14 19:07:51 -05:00
|
|
|
const resolvedModel =
|
|
|
|
|
specifiedModel === GEMINI_MODEL_ALIAS_AUTO
|
|
|
|
|
? defaultModel
|
|
|
|
|
: specifiedModel || 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-10-17 09:07:18 -07:00
|
|
|
|
|
|
|
|
const ptyInfo = await getPty();
|
|
|
|
|
|
2026-01-06 16:38:07 -05:00
|
|
|
const mcpEnabled = settings.admin?.mcp?.enabled ?? true;
|
2026-01-08 12:59:30 -05:00
|
|
|
const extensionsEnabled = settings.admin?.extensions?.enabled ?? true;
|
2026-01-13 23:40:23 -08:00
|
|
|
const adminSkillsEnabled = settings.admin?.skills?.enabled ?? true;
|
2026-01-06 16:38:07 -05:00
|
|
|
|
2026-01-22 15:38:06 -08:00
|
|
|
// Create MCP enablement manager and callbacks
|
|
|
|
|
const mcpEnablementManager = McpServerEnablementManager.getInstance();
|
|
|
|
|
const mcpEnablementCallbacks = mcpEnabled
|
|
|
|
|
? mcpEnablementManager.getEnablementCallbacks()
|
|
|
|
|
: undefined;
|
|
|
|
|
|
2025-06-07 13:49:00 -07:00
|
|
|
return new Config({
|
2025-06-11 04:46:39 +00:00
|
|
|
sessionId,
|
2026-01-20 22:01:18 +00:00
|
|
|
clientVersion: await getVersion(),
|
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-11-18 12:01:16 -05:00
|
|
|
previewFeatures: settings.general?.previewFeatures,
|
2025-10-16 12:09:21 -07:00
|
|
|
|
2025-08-27 18:39:45 -07:00
|
|
|
coreTools: settings.tools?.core || undefined,
|
2025-10-06 12:15:21 -07:00
|
|
|
allowedTools: allowedTools.length > 0 ? allowedTools : 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,
|
2026-01-06 16:38:07 -05:00
|
|
|
mcpServerCommand: mcpEnabled ? settings.mcp?.serverCommand : undefined,
|
|
|
|
|
mcpServers: mcpEnabled ? settings.mcpServers : {},
|
2026-01-22 15:38:06 -08:00
|
|
|
mcpEnablementCallbacks,
|
2026-01-06 16:38:07 -05:00
|
|
|
mcpEnabled,
|
2026-01-08 12:59:30 -05:00
|
|
|
extensionsEnabled,
|
2026-01-13 12:16:02 -08:00
|
|
|
agents: settings.agents,
|
2026-01-13 23:40:23 -08:00
|
|
|
adminSkillsEnabled,
|
2026-01-06 16:38:07 -05:00
|
|
|
allowedMcpServers: mcpEnabled
|
|
|
|
|
? (argv.allowedMcpServerNames ?? settings.mcp?.allowed)
|
|
|
|
|
: undefined,
|
|
|
|
|
blockedMcpServers: mcpEnabled
|
|
|
|
|
? argv.allowedMcpServerNames
|
|
|
|
|
? undefined
|
|
|
|
|
: settings.mcp?.excluded
|
|
|
|
|
: undefined,
|
2025-12-22 19:18:27 -08:00
|
|
|
blockedEnvironmentVariables:
|
|
|
|
|
settings.security?.environmentVariableRedaction?.blocked,
|
|
|
|
|
enableEnvironmentVariableRedaction:
|
|
|
|
|
settings.security?.environmentVariableRedaction?.enabled,
|
2025-05-29 20:51:17 +00:00
|
|
|
userMemory: memoryContent,
|
|
|
|
|
geminiMdFileCount: fileCount,
|
2025-10-02 17:46:54 -04:00
|
|
|
geminiMdFilePaths: filePaths,
|
2025-08-07 14:19:06 -07:00
|
|
|
approvalMode,
|
2026-01-06 16:38:07 -05:00
|
|
|
disableYoloMode:
|
|
|
|
|
settings.security?.disableYoloMode || settings.admin?.secureModeEnabled,
|
2025-10-17 08:58:00 -07:00
|
|
|
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,
|
2026-01-05 15:00:20 -05:00
|
|
|
usageStatisticsEnabled: settings.privacy?.usageStatisticsEnabled,
|
2025-10-10 12:04:15 -04:00
|
|
|
fileFiltering,
|
2025-10-17 08:58:00 -07:00
|
|
|
checkpointing: settings.general?.checkpointing?.enabled,
|
2025-06-12 17:17:29 -07:00
|
|
|
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,
|
2026-01-05 15:00:20 -05:00
|
|
|
maxSessionTurns: settings.model?.maxSessionTurns,
|
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-11-10 18:31:00 -07:00
|
|
|
listSessions: argv.listSessions || false,
|
|
|
|
|
deleteSession: argv.deleteSession,
|
2025-10-20 16:15:23 -07:00
|
|
|
enabledExtensions: argv.extensions,
|
2025-10-28 09:04:30 -07:00
|
|
|
extensionLoader: extensionManager,
|
2025-10-30 11:05:49 -07:00
|
|
|
enableExtensionReloading: settings.experimental?.extensionReloading,
|
2025-12-03 12:53:06 -08:00
|
|
|
enableAgents: settings.experimental?.enableAgents,
|
2026-01-14 19:55:10 -05:00
|
|
|
plan: settings.experimental?.plan,
|
2026-01-19 22:59:30 -05:00
|
|
|
enableEventDrivenScheduler:
|
|
|
|
|
settings.experimental?.enableEventDrivenScheduler,
|
2026-01-27 23:56:04 -05:00
|
|
|
skillsSupport: settings.skills?.enabled ?? true,
|
2025-12-30 13:35:52 -08:00
|
|
|
disabledSkills: settings.skills?.disabled,
|
2025-12-03 04:09:46 +08:00
|
|
|
experimentalJitContext: settings.experimental?.jitContext,
|
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,
|
2026-02-02 10:13:20 -08:00
|
|
|
disableLoopDetection: settings.model?.disableLoopDetection,
|
2025-10-30 16:03:58 -07:00
|
|
|
compressionThreshold: settings.model?.compressionThreshold,
|
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,
|
2026-01-26 15:23:54 -08:00
|
|
|
useBackgroundColor: settings.ui?.useBackgroundColor,
|
2025-08-27 18:39:45 -07:00
|
|
|
useRipgrep: settings.tools?.useRipgrep,
|
2026-01-05 15:00:20 -05:00
|
|
|
enableInteractiveShell: settings.tools?.shell?.enableInteractiveShell,
|
2025-11-26 13:43:33 -08:00
|
|
|
shellToolInactivityTimeout: settings.tools?.shell?.inactivityTimeout,
|
2025-12-27 03:07:33 +08:00
|
|
|
enableShellOutputEfficiency:
|
|
|
|
|
settings.tools?.shell?.enableShellOutputEfficiency ?? true,
|
2025-08-27 18:39:45 -07:00
|
|
|
skipNextSpeakerCheck: settings.model?.skipNextSpeakerCheck,
|
2026-01-05 15:00:20 -05:00
|
|
|
enablePromptCompletion: settings.general?.enablePromptCompletion,
|
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,
|
2026-01-23 11:45:46 -05:00
|
|
|
eventEmitter: coreEvents,
|
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-10-23 16:10:43 -07:00
|
|
|
fakeResponses: argv.fakeResponses,
|
2025-10-28 12:13:45 -07:00
|
|
|
recordResponses: argv.recordResponses,
|
2026-01-05 15:00:20 -05:00
|
|
|
retryFetchErrors: settings.general?.retryFetchErrors,
|
2025-10-17 09:07:18 -07:00
|
|
|
ptyInfo: ptyInfo?.name,
|
2026-01-13 09:26:53 +08:00
|
|
|
disableLLMCorrection: settings.tools?.disableLLMCorrection,
|
2026-01-20 23:58:37 -05:00
|
|
|
rawOutput: argv.rawOutput,
|
|
|
|
|
acceptRawOutputRisk: argv.acceptRawOutputRisk,
|
2025-11-05 17:18:42 -08:00
|
|
|
modelConfigServiceConfig: settings.modelConfigs,
|
2025-11-02 11:49:16 -08:00
|
|
|
// TODO: loading of hooks based on workspace trust
|
2026-01-30 10:15:48 -05:00
|
|
|
enableHooks: settings.hooksConfig.enabled,
|
|
|
|
|
enableHooksUI: settings.hooksConfig.enabled,
|
2025-11-02 11:49:16 -08:00
|
|
|
hooks: settings.hooks || {},
|
2026-01-20 14:47:31 -08:00
|
|
|
disabledHooks: settings.hooksConfig?.disabled || [],
|
2025-12-23 16:10:46 -05:00
|
|
|
projectHooks: projectHooks || {},
|
2025-12-23 19:53:43 +05:30
|
|
|
onModelChange: (model: string) => saveModelChange(loadedSettings, model),
|
2026-01-05 15:12:51 -08:00
|
|
|
onReload: async () => {
|
|
|
|
|
const refreshedSettings = loadSettings(cwd);
|
|
|
|
|
return {
|
2026-01-15 09:26:10 -08:00
|
|
|
disabledSkills: refreshedSettings.merged.skills.disabled,
|
2026-01-14 19:30:17 -05:00
|
|
|
agents: refreshedSettings.merged.agents,
|
2026-01-05 15:12:51 -08:00
|
|
|
};
|
|
|
|
|
},
|
2025-06-07 13:49:00 -07:00
|
|
|
});
|
2025-04-18 11:12:18 -07:00
|
|
|
}
|
2025-08-07 16:42:17 -07:00
|
|
|
|
2025-07-01 16:13:46 -07:00
|
|
|
function mergeExcludeTools(
|
2026-01-15 09:26:10 -08:00
|
|
|
settings: MergedSettings,
|
|
|
|
|
extraExcludes: string[] = [],
|
2025-07-01 16:13:46 -07:00
|
|
|
): string[] {
|
2025-08-07 14:19:06 -07:00
|
|
|
const allExcludeTools = new Set([
|
2026-01-15 09:26:10 -08:00
|
|
|
...(settings.tools.exclude || []),
|
|
|
|
|
...extraExcludes,
|
2025-08-07 14:19:06 -07:00
|
|
|
]);
|
2026-01-15 09:26:10 -08:00
|
|
|
return Array.from(allExcludeTools);
|
2025-07-01 16:13:46 -07:00
|
|
|
}
|