2025-07-22 19:59:07 +08:00
/ * *
* @license
* Copyright 2025 Google LLC
* SPDX - License - Identifier : Apache - 2.0
* /
2025-08-02 03:52:17 +05:30
import * as fs from 'fs' ;
import * as path from 'path' ;
import { homedir } from 'node:os' ;
2025-07-22 19:59:07 +08: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-07-22 19:59:07 +08:00
import {
Config ,
loadServerHierarchicalMemory ,
setGeminiMdFilename as setServerGeminiMdFilename ,
getCurrentGeminiMdFilename ,
ApprovalMode ,
DEFAULT_GEMINI_MODEL ,
DEFAULT_GEMINI_EMBEDDING_MODEL ,
2025-07-20 00:55:33 -07:00
DEFAULT_MEMORY_FILE_FILTERING_OPTIONS ,
2025-07-22 19:59:07 +08:00
FileDiscoveryService ,
TelemetryTarget ,
2025-07-20 00:55:33 -07:00
FileFilteringOptions ,
2025-08-07 14:19:06 -07:00
ShellTool ,
EditTool ,
WriteFileTool ,
2025-08-07 16:42:17 -07:00
MCPServerConfig ,
2025-08-18 19:55:46 +08:00
ConfigParameters ,
2025-07-23 00:27:14 +08:00
} from '@qwen-code/qwen-code-core' ;
2025-07-22 19:59:07 +08:00
import { Settings } from './settings.js' ;
2025-07-18 20:45:00 +02:00
import { Extension , annotateActiveExtensions } from './extension.js' ;
2025-07-22 19:59:07 +08:00
import { getCliVersion } from '../utils/version.js' ;
import { loadSandboxConfig } from './sandboxConfig.js' ;
2025-08-06 02:01:01 +09:00
import { resolvePath } from '../utils/resolvePath.js' ;
2025-07-22 19:59:07 +08:00
2025-08-13 11:06:31 -07:00
import { isWorkspaceTrusted } from './trustedFolders.js' ;
2025-07-22 19:59:07 +08: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 ) ,
} ;
export interface CliArgs {
model : string | undefined ;
sandbox : boolean | string | undefined ;
sandboxImage : string | undefined ;
debug : boolean | undefined ;
prompt : string | undefined ;
promptInteractive : string | undefined ;
allFiles : boolean | undefined ;
all_files : boolean | undefined ;
showMemoryUsage : boolean | undefined ;
show_memory_usage : boolean | undefined ;
yolo : boolean | undefined ;
2025-08-12 15:10:22 -07:00
approvalMode : string | undefined ;
2025-07-22 19:59:07 +08:00
telemetry : boolean | undefined ;
checkpointing : boolean | undefined ;
telemetryTarget : string | undefined ;
telemetryOtlpEndpoint : string | undefined ;
telemetryLogPrompts : boolean | undefined ;
2025-07-23 17:48:24 -04:00
telemetryOutfile : string | undefined ;
2025-07-22 19:59:07 +08:00
allowedMcpServerNames : string [ ] | undefined ;
2025-07-17 16:25:23 -06:00
experimentalAcp : boolean | undefined ;
2025-07-22 19:59:07 +08:00
extensions : string [ ] | undefined ;
listExtensions : boolean | undefined ;
openaiLogging : boolean | undefined ;
openaiApiKey : string | undefined ;
openaiBaseUrl : string | 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-14 21:20:23 +08:00
tavilyApiKey : string | undefined ;
2025-07-22 19:59:07 +08:00
}
export async function parseArguments ( ) : Promise < CliArgs > {
const yargsInstance = yargs ( hideBin ( process . argv ) )
2025-08-25 18:34:41 +08:00
// Set locale to English for consistent output, especially in tests
. locale ( 'en' )
2025-07-22 19:59:07 +08:00
. scriptName ( 'qwen' )
. usage (
2025-08-18 19:55:46 +08:00
'Usage: qwen [options] [command]\n\nQwen Code - Launch an interactive CLI, use -p/--prompt for non-interactive mode' ,
2025-07-22 19:59:07 +08:00
)
2025-08-18 19:55:46 +08:00
. command ( '$0' , 'Launch Qwen Code' , ( yargsInstance ) = >
2025-08-06 11:52:29 -04:00
yargsInstance
. option ( 'model' , {
alias : 'm' ,
type : 'string' ,
description : ` Model ` ,
default : process . env . GEMINI_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 ( 'debug' , {
alias : 'd' ,
type : 'boolean' ,
description : 'Run in debug mode?' ,
default : false ,
} )
. option ( 'all-files' , {
alias : [ 'a' ] ,
type : 'boolean' ,
description : 'Include ALL files in context?' ,
default : false ,
} )
. option ( 'all_files' , {
type : 'boolean' ,
description : 'Include ALL files in context?' ,
default : false ,
} )
. deprecateOption (
'all_files' ,
'Use --all-files instead. We will be removing --all_files in the coming weeks.' ,
)
. option ( 'show-memory-usage' , {
type : 'boolean' ,
description : 'Show memory usage in status bar' ,
default : false ,
} )
. option ( 'show_memory_usage' , {
type : 'boolean' ,
description : 'Show memory usage in status bar' ,
default : false ,
} )
. deprecateOption (
'show_memory_usage' ,
'Use --show-memory-usage instead. We will be removing --show_memory_usage in the coming weeks.' ,
)
. 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 ( '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-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.' ,
} )
. 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' ,
} )
. option ( 'extensions' , {
alias : 'e' ,
type : 'array' ,
string : true ,
description :
'A list of extensions to use. If not provided, all extensions are used.' ,
} )
. option ( 'list-extensions' , {
alias : 'l' ,
type : 'boolean' ,
description : 'List all available extensions and exit.' ,
} )
. option ( 'proxy' , {
type : 'string' ,
description :
'Proxy for gemini client, like schema://user:password@host:port' ,
} )
. 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-18 19:55:46 +08:00
. option ( 'openai-logging' , {
type : 'boolean' ,
description :
'Enable logging of OpenAI API calls for debugging and analysis' ,
} )
. option ( 'openai-api-key' , {
type : 'string' ,
description : 'OpenAI API key to use for authentication' ,
} )
. option ( 'openai-base-url' , {
type : 'string' ,
description : 'OpenAI base URL (for custom endpoints)' ,
} )
. option ( 'tavily-api-key' , {
type : 'string' ,
description : 'Tavily API key for web search functionality' ,
} )
2025-08-06 11:52:29 -04:00
. check ( ( argv ) = > {
if ( argv . prompt && argv . promptInteractive ) {
throw new Error (
'Cannot use both --prompt (-p) and --prompt-interactive (-i) together' ,
) ;
}
2025-08-12 15:10:22 -07:00
if ( argv . yolo && argv . approvalMode ) {
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-22 19:59:07 +08:00
)
2025-08-06 11:52:29 -04:00
// Register MCP subcommands
. command ( mcpCommand )
2025-07-22 19:59:07 +08:00
. version ( await getCliVersion ( ) ) // This will enable the --version flag based on package.json
. alias ( 'v' , 'version' )
. help ( )
. alias ( 'h' , 'help' )
. strict ( )
2025-08-06 11:52:29 -04:00
. demandCommand ( 0 , 0 ) ; // Allow base command to run with no subcommands
2025-07-22 19:59:07 +08: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
if ( result . _ . length > 0 && result . _ [ 0 ] === 'mcp' ) {
// 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-07-22 19:59:07 +08: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.
export async function loadHierarchicalGeminiMemory (
currentWorkingDirectory : string ,
2025-08-06 02:01:01 +09:00
includeDirectoriesToReadGemini : readonly string [ ] = [ ] ,
2025-07-22 19:59:07 +08:00
debugMode : boolean ,
fileService : FileDiscoveryService ,
2025-07-23 14:48:35 -07:00
settings : Settings ,
2025-07-22 19:59:07 +08:00
extensionContextFilePaths : string [ ] = [ ] ,
2025-07-31 22:06:50 +05:30
memoryImportFormat : 'flat' | 'tree' = 'tree' ,
2025-07-20 00:55:33 -07:00
fileFilteringOptions? : FileFilteringOptions ,
2025-07-22 19:59:07 +08: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-07-22 19:59:07 +08:00
if ( debugMode ) {
logger . debug (
2025-07-31 22:06:50 +05:30
` CLI: Delegating hierarchical memory load to server for CWD: ${ currentWorkingDirectory } (memoryImportFormat: ${ memoryImportFormat } ) ` ,
2025-07-22 19:59:07 +08: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-07-22 19:59:07 +08:00
return loadServerHierarchicalMemory (
2025-08-02 03:52:17 +05:30
effectiveCwd ,
2025-08-06 02:01:01 +09:00
includeDirectoriesToReadGemini ,
2025-07-22 19:59:07 +08:00
debugMode ,
fileService ,
extensionContextFilePaths ,
2025-07-31 22:06:50 +05:30
memoryImportFormat ,
2025-07-20 00:55:33 -07:00
fileFilteringOptions ,
2025-07-23 14:48:35 -07:00
settings . memoryDiscoveryMaxDirs ,
2025-07-22 19:59:07 +08:00
) ;
}
export async function loadCliConfig (
settings : Settings ,
extensions : Extension [ ] ,
sessionId : string ,
argv : CliArgs ,
2025-08-13 12:58:26 -03:00
cwd : string = process . cwd ( ) ,
2025-07-22 19:59:07 +08:00
) : Promise < Config > {
const debugMode =
argv . debug ||
[ process . env . DEBUG , process . env . DEBUG_MODE ] . some (
( v ) = > v === 'true' || v === '1' ,
2025-07-31 22:06:50 +05:30
) ||
false ;
const memoryImportFormat = settings . memoryImportFormat || 'tree' ;
2025-07-22 19:59:07 +08:00
2025-08-05 18:52:58 -04:00
const ideMode = settings . ideMode ? ? false ;
2025-07-25 17:46:55 +00:00
2025-08-06 15:27:21 -07:00
const folderTrustFeature = settings . folderTrustFeature ? ? false ;
2025-08-13 11:06:31 -07:00
const folderTrustSetting = settings . folderTrust ? ? true ;
2025-08-07 14:06:17 -07:00
const folderTrust = folderTrustFeature && folderTrustSetting ;
2025-08-13 11:06:31 -07:00
const trustedFolder = folderTrust ? isWorkspaceTrusted ( ) : true ;
2025-08-06 15:27:21 -07:00
2025-07-18 20:45:00 +02:00
const allExtensions = annotateActiveExtensions (
2025-07-22 19:59:07 +08:00
extensions ,
argv . extensions || [ ] ,
) ;
2025-07-18 20:45:00 +02:00
const activeExtensions = extensions . filter (
( _ , i ) = > allExtensions [ i ] . isActive ,
) ;
2025-07-22 19:59:07 +08:00
// Handle OpenAI API key from command line
if ( argv . openaiApiKey ) {
process . env . OPENAI_API_KEY = argv . openaiApiKey ;
}
// Handle OpenAI base URL from command line
if ( argv . openaiBaseUrl ) {
process . env . OPENAI_BASE_URL = argv . openaiBaseUrl ;
}
2025-08-14 21:20:23 +08:00
// Handle Tavily API key from command line
if ( argv . tavilyApiKey ) {
process . env . TAVILY_API_KEY = argv . tavilyApiKey ;
}
2025-07-22 19:59:07 +08: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.
if ( settings . contextFileName ) {
setServerGeminiMdFilename ( settings . contextFileName ) ;
} else {
// Reset to default if not provided in settings.
setServerGeminiMdFilename ( getCurrentGeminiMdFilename ( ) ) ;
}
const extensionContextFilePaths = activeExtensions . flatMap (
( e ) = > e . contextFiles ,
) ;
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 ,
. . . settings . fileFiltering ,
} ;
2025-08-06 02:01:01 +09:00
const includeDirectories = ( settings . includeDirectories || [ ] )
. map ( resolvePath )
. concat ( ( argv . includeDirectories || [ ] ) . map ( resolvePath ) ) ;
2025-07-22 19:59:07 +08:00
// Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version
const { memoryContent , fileCount } = await loadHierarchicalGeminiMemory (
2025-08-13 12:58:26 -03:00
cwd ,
2025-08-06 02:01:01 +09:00
settings . loadMemoryFromIncludeDirectories ? includeDirectories : [ ] ,
2025-07-22 19:59:07 +08:00
debugMode ,
fileService ,
2025-07-23 14:48:35 -07:00
settings ,
2025-07-22 19:59:07 +08:00
extensionContextFilePaths ,
2025-07-31 22:06:50 +05:30
memoryImportFormat ,
2025-07-20 00:55:33 -07:00
fileFiltering ,
2025-07-22 19:59:07 +08:00
) ;
let mcpServers = mergeMcpServers ( settings , activeExtensions ) ;
2025-08-07 14:19:06 -07:00
const question = argv . promptInteractive || argv . prompt || '' ;
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-07 14:19:06 -07:00
const interactive =
! ! argv . promptInteractive || ( process . stdin . isTTY && question . length === 0 ) ;
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.
extraExcludes . push ( ShellTool . Name , EditTool . Name , WriteFileTool . Name ) ;
break ;
case ApprovalMode . AUTO_EDIT :
// In auto-edit non-interactive mode, only tools that still require a prompt are excluded.
extraExcludes . push ( ShellTool . Name ) ;
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 ) {
if ( settings . allowMCPServers ) {
2025-08-07 16:42:17 -07:00
mcpServers = allowedMcpServers (
mcpServers ,
settings . allowMCPServers ,
blockedMcpServers ,
) ;
2025-07-15 20:45:24 +00:00
}
if ( settings . excludeMCPServers ) {
const excludedNames = new Set ( settings . excludeMCPServers . filter ( Boolean ) ) ;
if ( excludedNames . size > 0 ) {
mcpServers = Object . fromEntries (
Object . entries ( mcpServers ) . filter ( ( [ key ] ) = > ! excludedNames . has ( key ) ) ,
) ;
}
}
}
2025-07-22 19:59:07 +08:00
if ( argv . allowedMcpServerNames ) {
2025-08-07 16:42:17 -07:00
mcpServers = allowedMcpServers (
mcpServers ,
argv . allowedMcpServerNames ,
blockedMcpServers ,
) ;
2025-07-22 19:59:07 +08:00
}
const sandboxConfig = await loadSandboxConfig ( settings , argv ) ;
2025-08-12 21:00:17 +08:00
const cliVersion = await getCliVersion ( ) ;
2025-07-22 19:59:07 +08:00
return new Config ( {
sessionId ,
embeddingModel : DEFAULT_GEMINI_EMBEDDING_MODEL ,
sandbox : sandboxConfig ,
2025-08-13 12:58:26 -03:00
targetDir : cwd ,
2025-08-06 02:01:01 +09:00
includeDirectories ,
loadMemoryFromIncludeDirectories :
2025-08-10 04:50:53 +09:00
settings . loadMemoryFromIncludeDirectories || false ,
2025-07-22 19:59:07 +08:00
debugMode ,
2025-08-07 14:19:06 -07:00
question ,
2025-07-22 19:59:07 +08:00
fullContext : argv.allFiles || argv . all_files || false ,
coreTools : settings.coreTools || undefined ,
excludeTools ,
toolDiscoveryCommand : settings.toolDiscoveryCommand ,
toolCallCommand : settings.toolCallCommand ,
mcpServerCommand : settings.mcpServerCommand ,
mcpServers ,
userMemory : memoryContent ,
geminiMdFileCount : fileCount ,
2025-08-07 14:19:06 -07:00
approvalMode ,
2025-07-22 19:59:07 +08:00
showMemoryUsage :
argv . showMemoryUsage ||
argv . show_memory_usage ||
settings . showMemoryUsage ||
false ,
accessibility : settings.accessibility ,
telemetry : {
enabled : argv.telemetry ? ? settings . telemetry ? . enabled ,
target : ( argv . telemetryTarget ? ?
settings . telemetry ? . target ) as TelemetryTarget ,
otlpEndpoint :
argv . telemetryOtlpEndpoint ? ?
process . env . OTEL_EXPORTER_OTLP_ENDPOINT ? ?
settings . telemetry ? . otlpEndpoint ,
logPrompts : argv.telemetryLogPrompts ? ? settings . telemetry ? . logPrompts ,
2025-07-23 17:48:24 -04:00
outfile : argv.telemetryOutfile ? ? settings . telemetry ? . outfile ,
2025-07-22 19:59:07 +08:00
} ,
usageStatisticsEnabled : settings.usageStatisticsEnabled ? ? true ,
// Git-aware file filtering settings
fileFiltering : {
respectGitIgnore : settings.fileFiltering?.respectGitIgnore ,
2025-07-20 00:55:33 -07:00
respectGeminiIgnore : settings.fileFiltering?.respectGeminiIgnore ,
2025-07-22 19:59:07 +08:00
enableRecursiveFileSearch :
settings . fileFiltering ? . enableRecursiveFileSearch ,
} ,
checkpointing : argv.checkpointing || settings . checkpointing ? . enabled ,
proxy :
2025-07-18 02:57:37 +08:00
argv . proxy ||
2025-07-22 19:59:07 +08: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-07-22 19:59:07 +08:00
fileDiscoveryService : fileService ,
bugCommand : settings.bugCommand ,
2025-08-04 16:41:58 -04:00
model : argv.model || settings . model || DEFAULT_GEMINI_MODEL ,
2025-07-22 19:59:07 +08:00
extensionContextFilePaths ,
maxSessionTurns : settings.maxSessionTurns ? ? - 1 ,
2025-08-08 09:48:31 +08:00
sessionTokenLimit : settings.sessionTokenLimit ? ? - 1 ,
2025-08-13 12:58:26 -03:00
experimentalZedIntegration : argv.experimentalAcp || false ,
2025-07-22 19:59:07 +08:00
listExtensions : argv.listExtensions || false ,
2025-07-18 20:45:00 +02:00
extensions : allExtensions ,
blockedMcpServers ,
2025-07-22 19:59:07 +08:00
noBrowser : ! ! process . env . NO_BROWSER ,
2025-07-15 10:22:31 -07:00
summarizeToolOutput : settings.summarizeToolOutput ,
2025-07-22 19:59:07 +08:00
ideMode ,
enableOpenAILogging :
( typeof argv . openaiLogging === 'undefined'
? settings . enableOpenAILogging
: argv . openaiLogging ) ? ? false ,
2025-08-18 19:55:46 +08:00
systemPromptMappings : ( settings . systemPromptMappings ? ? [
2025-07-29 13:11:41 +08:00
{
baseUrls : [
'https://dashscope.aliyuncs.com/compatible-mode/v1/' ,
'https://dashscope-intl.aliyuncs.com/compatible-mode/v1/' ,
] ,
modelNames : [ 'qwen3-coder-plus' ] ,
template :
'SYSTEM_TEMPLATE:{"name":"qwen3_coder","params":{"is_git_repository":{RUNTIME_VARS_IS_GIT_REPO},"sandbox":"{RUNTIME_VARS_SANDBOX}"}}' ,
2025-08-18 20:01:54 +08:00
} ,
2025-08-18 19:55:46 +08:00
] ) as ConfigParameters [ 'systemPromptMappings' ] ,
2025-08-02 14:47:37 +08:00
contentGenerator : settings.contentGenerator ,
2025-08-12 21:00:17 +08:00
cliVersion ,
2025-08-14 21:20:23 +08:00
tavilyApiKey :
argv . tavilyApiKey || settings . tavilyApiKey || process . env . TAVILY_API_KEY ,
2025-08-07 07:34:40 -07:00
chatCompression : settings.chatCompression ,
2025-08-06 15:27:21 -07:00
folderTrustFeature ,
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-07-22 19:59:07 +08: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-07-22 19:59:07 +08:00
function mergeMcpServers ( settings : Settings , extensions : Extension [ ] ) {
const mcpServers = { . . . ( settings . mcpServers || { } ) } ;
for ( const extension of extensions ) {
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-07-22 19:59:07 +08:00
} ,
) ;
}
return mcpServers ;
}
function mergeExcludeTools (
settings : Settings ,
extensions : Extension [ ] ,
2025-08-07 14:19:06 -07:00
extraExcludes? : string [ ] | undefined ,
2025-07-22 19:59:07 +08:00
) : string [ ] {
2025-08-07 14:19:06 -07:00
const allExcludeTools = new Set ( [
. . . ( settings . excludeTools || [ ] ) ,
. . . ( extraExcludes || [ ] ) ,
] ) ;
2025-07-22 19:59:07 +08:00
for ( const extension of extensions ) {
for ( const tool of extension . config . excludeTools || [ ] ) {
allExcludeTools . add ( tool ) ;
}
}
return [ . . . allExcludeTools ] ;
}