2025-04-18 17:44:24 -07:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
Initial commit of Gemini Code CLI
This commit introduces the initial codebase for the Gemini Code CLI, a command-line interface designed to facilitate interaction with the Gemini API for software engineering tasks.
The code was migrated from a previous git repository as a single squashed commit.
Core Features & Components:
* **Gemini Integration:** Leverages the `@google/genai` SDK to interact with the Gemini models, supporting chat history, streaming responses, and function calling (tools).
* **Terminal UI:** Built with Ink (React for CLIs) providing an interactive chat interface within the terminal, including input prompts, message display, loading indicators, and tool interaction elements.
* **Tooling Framework:** Implements a robust tool system allowing Gemini to interact with the local environment. Includes tools for:
* File system listing (`ls`)
* File reading (`read-file`)
* Content searching (`grep`)
* File globbing (`glob`)
* File editing (`edit`)
* File writing (`write-file`)
* Executing bash commands (`terminal`)
* **State Management:** Handles the streaming state of Gemini responses and manages the conversation history.
* **Configuration:** Parses command-line arguments (`yargs`) and loads environment variables (`dotenv`) for setup.
* **Project Structure:** Organized into `core`, `ui`, `tools`, `config`, and `utils` directories using TypeScript. Includes basic build (`tsc`) and start scripts.
This initial version establishes the foundation for a powerful CLI tool enabling developers to use Gemini for coding assistance directly in their terminal environment.
---
Created by yours truly: __Gemini Code__
2025-04-15 21:41:08 -07:00
|
|
|
import React from 'react';
|
|
|
|
|
import { render } from 'ink';
|
2025-06-08 18:01:02 -04:00
|
|
|
import { AppWrapper } from './ui/App.js';
|
2025-07-11 16:52:56 -07:00
|
|
|
import { loadCliConfig, parseArguments, CliArgs } from './config/config.js';
|
2025-04-21 17:41:44 -07:00
|
|
|
import { readStdin } from './utils/readStdin.js';
|
2025-06-10 18:34:36 -07:00
|
|
|
import { basename } from 'node:path';
|
2025-06-24 21:18:55 +00:00
|
|
|
import v8 from 'node:v8';
|
|
|
|
|
import os from 'node:os';
|
|
|
|
|
import { spawn } from 'node:child_process';
|
2025-06-18 10:01:00 -07:00
|
|
|
import { start_sandbox } from './utils/sandbox.js';
|
2025-06-20 01:36:33 -07:00
|
|
|
import {
|
|
|
|
|
LoadedSettings,
|
|
|
|
|
loadSettings,
|
2025-07-09 00:10:36 +02:00
|
|
|
SettingScope,
|
2025-06-20 01:36:33 -07:00
|
|
|
} from './config/settings.js';
|
2025-05-01 10:34:07 -07:00
|
|
|
import { themeManager } from './ui/themes/theme-manager.js';
|
2025-05-14 00:12:04 +00:00
|
|
|
import { getStartupWarnings } from './utils/startupWarnings.js';
|
2025-07-05 23:27:00 -07:00
|
|
|
import { getUserStartupWarnings } from './utils/userStartupWarnings.js';
|
2025-06-01 16:11:37 -07:00
|
|
|
import { runNonInteractive } from './nonInteractiveCli.js';
|
2025-06-13 13:57:00 -07:00
|
|
|
import { loadExtensions, Extension } from './config/extension.js';
|
2025-07-12 15:42:47 -07:00
|
|
|
import { cleanupCheckpoints, registerCleanup } from './utils/cleanup.js';
|
2025-07-11 13:43:57 -07:00
|
|
|
import { getCliVersion } from './utils/version.js';
|
2025-06-01 16:11:37 -07:00
|
|
|
import {
|
2025-06-02 15:35:03 -07:00
|
|
|
ApprovalMode,
|
|
|
|
|
Config,
|
2025-06-01 16:11:37 -07:00
|
|
|
EditTool,
|
|
|
|
|
ShellTool,
|
2025-06-08 16:20:43 -07:00
|
|
|
WriteFileTool,
|
2025-06-11 04:46:39 +00:00
|
|
|
sessionId,
|
2025-06-13 03:44:17 -04:00
|
|
|
logUserPrompt,
|
2025-06-19 16:52:22 -07:00
|
|
|
AuthType,
|
2025-07-10 18:59:02 -07:00
|
|
|
getOauthClient,
|
2025-06-25 05:41:11 -07:00
|
|
|
} from '@google/gemini-cli-core';
|
2025-06-19 16:52:22 -07:00
|
|
|
import { validateAuthMethod } from './config/auth.js';
|
2025-06-20 05:27:03 +00:00
|
|
|
import { setMaxSizedBoxDebugging } from './ui/components/shared/MaxSizedBox.js';
|
2025-07-22 10:52:40 -04:00
|
|
|
import { validateNonInteractiveAuth } from './validateNonInterActiveAuth.js';
|
2025-07-25 17:35:26 -07:00
|
|
|
import { appEvents, AppEvent } from './utils/events.js';
|
2025-04-23 22:00:40 +00:00
|
|
|
|
2025-06-24 21:18:55 +00:00
|
|
|
function getNodeMemoryArgs(config: Config): string[] {
|
|
|
|
|
const totalMemoryMB = os.totalmem() / (1024 * 1024);
|
|
|
|
|
const heapStats = v8.getHeapStatistics();
|
|
|
|
|
const currentMaxOldSpaceSizeMb = Math.floor(
|
|
|
|
|
heapStats.heap_size_limit / 1024 / 1024,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Set target to 50% of total memory
|
|
|
|
|
const targetMaxOldSpaceSizeInMB = Math.floor(totalMemoryMB * 0.5);
|
|
|
|
|
if (config.getDebugMode()) {
|
|
|
|
|
console.debug(
|
|
|
|
|
`Current heap size ${currentMaxOldSpaceSizeMb.toFixed(2)} MB`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (process.env.GEMINI_CLI_NO_RELAUNCH) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (targetMaxOldSpaceSizeInMB > currentMaxOldSpaceSizeMb) {
|
|
|
|
|
if (config.getDebugMode()) {
|
|
|
|
|
console.debug(
|
|
|
|
|
`Need to relaunch with more memory: ${targetMaxOldSpaceSizeInMB.toFixed(2)} MB`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return [`--max-old-space-size=${targetMaxOldSpaceSizeInMB}`];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function relaunchWithAdditionalArgs(additionalArgs: string[]) {
|
|
|
|
|
const nodeArgs = [...additionalArgs, ...process.argv.slice(1)];
|
|
|
|
|
const newEnv = { ...process.env, GEMINI_CLI_NO_RELAUNCH: 'true' };
|
|
|
|
|
|
|
|
|
|
const child = spawn(process.execPath, nodeArgs, {
|
|
|
|
|
stdio: 'inherit',
|
|
|
|
|
env: newEnv,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await new Promise((resolve) => child.on('close', resolve));
|
|
|
|
|
process.exit(0);
|
|
|
|
|
}
|
2025-07-17 16:25:23 -06:00
|
|
|
import { runAcpPeer } from './acp/acpPeer.js';
|
2025-06-24 21:18:55 +00:00
|
|
|
|
2025-07-25 17:35:26 -07:00
|
|
|
export function setupUnhandledRejectionHandler() {
|
|
|
|
|
let unhandledRejectionOccurred = false;
|
|
|
|
|
process.on('unhandledRejection', (reason, _promise) => {
|
|
|
|
|
const errorMessage = `=========================================
|
|
|
|
|
This is an unexpected error. Please file a bug report using the /bug tool.
|
|
|
|
|
CRITICAL: Unhandled Promise Rejection!
|
|
|
|
|
=========================================
|
|
|
|
|
Reason: ${reason}${
|
|
|
|
|
reason instanceof Error && reason.stack
|
|
|
|
|
? `
|
|
|
|
|
Stack trace:
|
|
|
|
|
${reason.stack}`
|
|
|
|
|
: ''
|
|
|
|
|
}`;
|
|
|
|
|
appEvents.emit(AppEvent.LogError, errorMessage);
|
|
|
|
|
if (!unhandledRejectionOccurred) {
|
|
|
|
|
unhandledRejectionOccurred = true;
|
|
|
|
|
appEvents.emit(AppEvent.OpenDebugConsole);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 09:56:45 -07:00
|
|
|
export async function main() {
|
2025-07-25 17:35:26 -07:00
|
|
|
setupUnhandledRejectionHandler();
|
2025-06-10 11:54:51 +08:00
|
|
|
const workspaceRoot = process.cwd();
|
|
|
|
|
const settings = loadSettings(workspaceRoot);
|
|
|
|
|
|
2025-06-11 15:33:09 -04:00
|
|
|
await cleanupCheckpoints();
|
2025-06-06 09:56:45 -07:00
|
|
|
if (settings.errors.length > 0) {
|
|
|
|
|
for (const error of settings.errors) {
|
|
|
|
|
let errorMessage = `Error in ${error.path}: ${error.message}`;
|
|
|
|
|
if (!process.env.NO_COLOR) {
|
|
|
|
|
errorMessage = `\x1b[31m${errorMessage}\x1b[0m`;
|
|
|
|
|
}
|
|
|
|
|
console.error(errorMessage);
|
|
|
|
|
console.error(`Please fix ${error.path} and try again.`);
|
|
|
|
|
}
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 16:52:56 -07:00
|
|
|
const argv = await parseArguments();
|
2025-06-10 15:48:39 -07:00
|
|
|
const extensions = loadExtensions(workspaceRoot);
|
2025-07-11 16:52:56 -07:00
|
|
|
const config = await loadCliConfig(
|
|
|
|
|
settings.merged,
|
|
|
|
|
extensions,
|
|
|
|
|
sessionId,
|
|
|
|
|
argv,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (argv.promptInteractive && !process.stdin.isTTY) {
|
|
|
|
|
console.error(
|
|
|
|
|
'Error: The --prompt-interactive flag is not supported when piping input from stdin.',
|
|
|
|
|
);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
2025-06-02 13:55:54 -07:00
|
|
|
|
2025-07-08 12:57:34 -04:00
|
|
|
if (config.getListExtensions()) {
|
|
|
|
|
console.log('Installed extensions:');
|
|
|
|
|
for (const extension of extensions) {
|
|
|
|
|
console.log(`- ${extension.config.name}`);
|
|
|
|
|
}
|
|
|
|
|
process.exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-09 00:10:36 +02:00
|
|
|
// Set a default auth type if one isn't set.
|
2025-07-07 15:02:13 -07:00
|
|
|
if (!settings.merged.selectedAuthType) {
|
2025-07-09 00:10:36 +02:00
|
|
|
if (process.env.CLOUD_SHELL === 'true') {
|
2025-07-07 15:02:13 -07:00
|
|
|
settings.setValue(
|
|
|
|
|
SettingScope.User,
|
|
|
|
|
'selectedAuthType',
|
|
|
|
|
AuthType.CLOUD_SHELL,
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-06-20 01:36:33 -07:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 05:27:03 +00:00
|
|
|
setMaxSizedBoxDebugging(config.getDebugMode());
|
|
|
|
|
|
2025-07-07 15:01:59 -07:00
|
|
|
await config.initialize();
|
Ignore folders files (#651)
# Add .gitignore-Aware File Filtering to gemini-cli
This pull request introduces .gitignore-based file filtering to the gemini-cli, ensuring that git-ignored files are automatically excluded from file-related operations and suggestions throughout the CLI. The update enhances usability, reduces noise from build artifacts and dependencies, and provides new configuration options for fine-tuning file discovery.
Key Improvements
.gitignore File Filtering
All @ (at) commands, file completions, and core discovery tools now honor .gitignore patterns by default.
Git-ignored files (such as node_modules/, dist/, .env, and .git) are excluded from results unless explicitly overridden.
The behavior can be customized via a new fileFiltering section in settings.json, including options for:
Turning .gitignore respect on/off.
Adding custom ignore patterns.
Allowing or excluding build artifacts.
Configuration & Documentation Updates
settings.json schema extended with fileFiltering options.
Documentation updated to explain new filtering controls and usage patterns.
Testing
New and updated integration/unit tests for file filtering logic, configuration merging, and edge cases.
Test coverage ensures .gitignore filtering works as intended across different workflows.
Internal Refactoring
Core file discovery logic refactored for maintainability and extensibility.
Underlying tools (ls, glob, read-many-files) now support git-aware filtering out of the box.
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-06-03 21:40:46 -07:00
|
|
|
|
2025-07-20 16:51:18 +09:00
|
|
|
// Load custom themes from settings
|
|
|
|
|
themeManager.loadCustomThemes(settings.merged.customThemes);
|
|
|
|
|
|
2025-05-02 08:15:46 -07:00
|
|
|
if (settings.merged.theme) {
|
2025-05-09 10:20:08 -07:00
|
|
|
if (!themeManager.setActiveTheme(settings.merged.theme)) {
|
2025-05-08 20:56:46 -07:00
|
|
|
// If the theme is not found during initial load, log a warning and continue.
|
|
|
|
|
// The useThemeCommand hook in App.tsx will handle opening the dialog.
|
2025-05-09 10:20:08 -07:00
|
|
|
console.warn(`Warning: Theme "${settings.merged.theme}" not found.`);
|
2025-05-08 20:56:46 -07:00
|
|
|
}
|
2025-05-01 10:34:07 -07:00
|
|
|
}
|
|
|
|
|
|
2025-04-29 10:52:05 -07:00
|
|
|
// hop into sandbox if we are outside and sandboxing is enabled
|
|
|
|
|
if (!process.env.SANDBOX) {
|
2025-07-07 15:01:59 -07:00
|
|
|
const memoryArgs = settings.merged.autoConfigureMaxOldSpaceSize
|
|
|
|
|
? getNodeMemoryArgs(config)
|
|
|
|
|
: [];
|
2025-06-18 10:01:00 -07:00
|
|
|
const sandboxConfig = config.getSandbox();
|
|
|
|
|
if (sandboxConfig) {
|
2025-06-19 16:52:22 -07:00
|
|
|
if (settings.merged.selectedAuthType) {
|
|
|
|
|
// Validate authentication here because the sandbox will interfere with the Oauth2 web redirect.
|
2025-06-26 08:27:20 -07:00
|
|
|
try {
|
|
|
|
|
const err = validateAuthMethod(settings.merged.selectedAuthType);
|
|
|
|
|
if (err) {
|
|
|
|
|
throw new Error(err);
|
|
|
|
|
}
|
|
|
|
|
await config.refreshAuth(settings.merged.selectedAuthType);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Error authenticating:', err);
|
2025-06-19 16:52:22 -07:00
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-24 21:18:55 +00:00
|
|
|
await start_sandbox(sandboxConfig, memoryArgs);
|
2025-04-29 10:52:05 -07:00
|
|
|
process.exit(0);
|
2025-06-24 21:18:55 +00:00
|
|
|
} else {
|
|
|
|
|
// Not in a sandbox and not entering one, so relaunch with additional
|
|
|
|
|
// arguments to control memory usage if needed.
|
|
|
|
|
if (memoryArgs.length > 0) {
|
|
|
|
|
await relaunchWithAdditionalArgs(memoryArgs);
|
|
|
|
|
process.exit(0);
|
|
|
|
|
}
|
2025-04-29 10:52:05 -07:00
|
|
|
}
|
2025-04-24 08:58:47 -07:00
|
|
|
}
|
2025-07-10 18:59:02 -07:00
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
settings.merged.selectedAuthType === AuthType.LOGIN_WITH_GOOGLE &&
|
2025-07-21 16:23:28 -07:00
|
|
|
config.isBrowserLaunchSuppressed()
|
2025-07-10 18:59:02 -07:00
|
|
|
) {
|
|
|
|
|
// Do oauth before app renders to make copying the link possible.
|
|
|
|
|
await getOauthClient(settings.merged.selectedAuthType, config);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 16:25:23 -06:00
|
|
|
if (config.getExperimentalAcp()) {
|
|
|
|
|
return runAcpPeer(config, settings);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-02 08:15:46 -07:00
|
|
|
let input = config.getQuestion();
|
2025-07-05 23:27:00 -07:00
|
|
|
const startupWarnings = [
|
|
|
|
|
...(await getStartupWarnings()),
|
|
|
|
|
...(await getUserStartupWarnings(workspaceRoot)),
|
|
|
|
|
];
|
2025-05-02 08:15:46 -07:00
|
|
|
|
2025-07-11 16:52:56 -07:00
|
|
|
const shouldBeInteractive =
|
|
|
|
|
!!argv.promptInteractive || (process.stdin.isTTY && input?.length === 0);
|
|
|
|
|
|
2025-04-22 18:32:03 -07:00
|
|
|
// Render UI, passing necessary config values. Check that there is no command line question.
|
2025-07-11 16:52:56 -07:00
|
|
|
if (shouldBeInteractive) {
|
2025-07-11 13:43:57 -07:00
|
|
|
const version = await getCliVersion();
|
2025-06-18 11:40:15 -07:00
|
|
|
setWindowTitle(basename(workspaceRoot), settings);
|
2025-07-12 15:42:47 -07:00
|
|
|
const instance = render(
|
2025-05-27 21:40:46 +00:00
|
|
|
<React.StrictMode>
|
2025-06-08 18:01:02 -04:00
|
|
|
<AppWrapper
|
2025-05-27 21:40:46 +00:00
|
|
|
config={config}
|
|
|
|
|
settings={settings}
|
|
|
|
|
startupWarnings={startupWarnings}
|
2025-07-11 13:43:57 -07:00
|
|
|
version={version}
|
2025-05-27 21:40:46 +00:00
|
|
|
/>
|
|
|
|
|
</React.StrictMode>,
|
2025-05-30 19:36:52 -07:00
|
|
|
{ exitOnCtrlC: false },
|
2025-04-21 17:41:44 -07:00
|
|
|
);
|
2025-07-12 15:42:47 -07:00
|
|
|
|
|
|
|
|
registerCleanup(() => instance.unmount());
|
2025-04-22 11:01:09 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2025-04-22 18:32:03 -07:00
|
|
|
// If not a TTY, read from stdin
|
|
|
|
|
// This is for cases where the user pipes input directly into the command
|
2025-07-05 08:27:22 -07:00
|
|
|
if (!process.stdin.isTTY && !input) {
|
2025-04-22 18:32:03 -07:00
|
|
|
input += await readStdin();
|
|
|
|
|
}
|
2025-04-22 11:01:09 -07:00
|
|
|
if (!input) {
|
|
|
|
|
console.error('No input provided via stdin.');
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
2025-04-21 17:41:44 -07:00
|
|
|
|
2025-07-10 00:19:30 +05:30
|
|
|
const prompt_id = Math.random().toString(16).slice(2);
|
2025-06-13 03:44:17 -04:00
|
|
|
logUserPrompt(config, {
|
2025-06-22 09:26:48 -05:00
|
|
|
'event.name': 'user_prompt',
|
|
|
|
|
'event.timestamp': new Date().toISOString(),
|
2025-06-13 03:44:17 -04:00
|
|
|
prompt: input,
|
2025-07-10 00:19:30 +05:30
|
|
|
prompt_id,
|
2025-07-11 14:08:49 -07:00
|
|
|
auth_type: config.getContentGeneratorConfig()?.authType,
|
2025-06-13 03:44:17 -04:00
|
|
|
prompt_length: input.length,
|
|
|
|
|
});
|
|
|
|
|
|
2025-06-01 16:11:37 -07:00
|
|
|
// Non-interactive mode handled by runNonInteractive
|
2025-06-10 15:48:39 -07:00
|
|
|
const nonInteractiveConfig = await loadNonInteractiveConfig(
|
|
|
|
|
config,
|
|
|
|
|
extensions,
|
|
|
|
|
settings,
|
2025-07-11 16:52:56 -07:00
|
|
|
argv,
|
2025-06-10 15:48:39 -07:00
|
|
|
);
|
2025-06-02 15:35:03 -07:00
|
|
|
|
2025-07-10 00:19:30 +05:30
|
|
|
await runNonInteractive(nonInteractiveConfig, input, prompt_id);
|
2025-06-02 15:35:03 -07:00
|
|
|
process.exit(0);
|
Initial commit of Gemini Code CLI
This commit introduces the initial codebase for the Gemini Code CLI, a command-line interface designed to facilitate interaction with the Gemini API for software engineering tasks.
The code was migrated from a previous git repository as a single squashed commit.
Core Features & Components:
* **Gemini Integration:** Leverages the `@google/genai` SDK to interact with the Gemini models, supporting chat history, streaming responses, and function calling (tools).
* **Terminal UI:** Built with Ink (React for CLIs) providing an interactive chat interface within the terminal, including input prompts, message display, loading indicators, and tool interaction elements.
* **Tooling Framework:** Implements a robust tool system allowing Gemini to interact with the local environment. Includes tools for:
* File system listing (`ls`)
* File reading (`read-file`)
* Content searching (`grep`)
* File globbing (`glob`)
* File editing (`edit`)
* File writing (`write-file`)
* Executing bash commands (`terminal`)
* **State Management:** Handles the streaming state of Gemini responses and manages the conversation history.
* **Configuration:** Parses command-line arguments (`yargs`) and loads environment variables (`dotenv`) for setup.
* **Project Structure:** Organized into `core`, `ui`, `tools`, `config`, and `utils` directories using TypeScript. Includes basic build (`tsc`) and start scripts.
This initial version establishes the foundation for a powerful CLI tool enabling developers to use Gemini for coding assistance directly in their terminal environment.
---
Created by yours truly: __Gemini Code__
2025-04-15 21:41:08 -07:00
|
|
|
}
|
|
|
|
|
|
2025-06-10 11:54:51 +08:00
|
|
|
function setWindowTitle(title: string, settings: LoadedSettings) {
|
|
|
|
|
if (!settings.merged.hideWindowTitle) {
|
2025-07-08 12:34:17 -07:00
|
|
|
const windowTitle = (process.env.CLI_TITLE || `Gemini - ${title}`).replace(
|
|
|
|
|
// eslint-disable-next-line no-control-regex
|
|
|
|
|
/[\x00-\x1F\x7F]/g,
|
|
|
|
|
'',
|
|
|
|
|
);
|
|
|
|
|
process.stdout.write(`\x1b]2;${windowTitle}\x07`);
|
2025-06-10 11:54:51 +08:00
|
|
|
|
|
|
|
|
process.on('exit', () => {
|
|
|
|
|
process.stdout.write(`\x1b]2;\x07`);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-02 15:35:03 -07:00
|
|
|
async function loadNonInteractiveConfig(
|
|
|
|
|
config: Config,
|
2025-06-13 13:57:00 -07:00
|
|
|
extensions: Extension[],
|
2025-06-02 15:35:03 -07:00
|
|
|
settings: LoadedSettings,
|
2025-07-11 16:52:56 -07:00
|
|
|
argv: CliArgs,
|
2025-06-02 15:35:03 -07:00
|
|
|
) {
|
2025-06-19 16:52:22 -07:00
|
|
|
let finalConfig = config;
|
|
|
|
|
if (config.getApprovalMode() !== ApprovalMode.YOLO) {
|
|
|
|
|
// Everything is not allowed, ensure that only read-only tools are configured.
|
|
|
|
|
const existingExcludeTools = settings.merged.excludeTools || [];
|
|
|
|
|
const interactiveTools = [
|
|
|
|
|
ShellTool.Name,
|
|
|
|
|
EditTool.Name,
|
|
|
|
|
WriteFileTool.Name,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const newExcludeTools = [
|
|
|
|
|
...new Set([...existingExcludeTools, ...interactiveTools]),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const nonInteractiveSettings = {
|
|
|
|
|
...settings.merged,
|
|
|
|
|
excludeTools: newExcludeTools,
|
|
|
|
|
};
|
|
|
|
|
finalConfig = await loadCliConfig(
|
|
|
|
|
nonInteractiveSettings,
|
|
|
|
|
extensions,
|
|
|
|
|
config.getSessionId(),
|
2025-07-11 16:52:56 -07:00
|
|
|
argv,
|
2025-06-19 16:52:22 -07:00
|
|
|
);
|
2025-07-08 23:44:56 -07:00
|
|
|
await finalConfig.initialize();
|
2025-06-02 15:35:03 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-22 10:52:40 -04:00
|
|
|
return await validateNonInteractiveAuth(
|
2025-06-19 16:52:22 -07:00
|
|
|
settings.merged.selectedAuthType,
|
|
|
|
|
finalConfig,
|
|
|
|
|
);
|
|
|
|
|
}
|