in packages/cli/src/gemini.tsx [84:210]
export async function main() {
const workspaceRoot = process.cwd();
const settings = loadSettings(workspaceRoot);
await cleanupCheckpoints();
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);
}
const extensions = loadExtensions(workspaceRoot);
const config = await loadCliConfig(settings.merged, extensions, sessionId);
// set default fallback to gemini api key
// this has to go after load cli because thats where the env is set
if (!settings.merged.selectedAuthType && process.env.GEMINI_API_KEY) {
settings.setValue(
SettingScope.User,
'selectedAuthType',
AuthType.USE_GEMINI,
);
}
setMaxSizedBoxDebugging(config.getDebugMode());
// Initialize centralized FileDiscoveryService
config.getFileService();
if (config.getCheckpointingEnabled()) {
try {
await config.getGitService();
} catch {
// For now swallow the error, later log it.
}
}
if (settings.merged.theme) {
if (!themeManager.setActiveTheme(settings.merged.theme)) {
// 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.
console.warn(`Warning: Theme "${settings.merged.theme}" not found.`);
}
}
const memoryArgs = settings.merged.autoConfigureMaxOldSpaceSize
? getNodeMemoryArgs(config)
: [];
// hop into sandbox if we are outside and sandboxing is enabled
if (!process.env.SANDBOX) {
const sandboxConfig = config.getSandbox();
if (sandboxConfig) {
if (settings.merged.selectedAuthType) {
// Validate authentication here because the sandbox will interfere with the Oauth2 web redirect.
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);
process.exit(1);
}
}
await start_sandbox(sandboxConfig, memoryArgs);
process.exit(0);
} 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);
}
}
}
let input = config.getQuestion();
const startupWarnings = await getStartupWarnings();
// Render UI, passing necessary config values. Check that there is no command line question.
if (process.stdin.isTTY && input?.length === 0) {
setWindowTitle(basename(workspaceRoot), settings);
render(
<React.StrictMode>
<AppWrapper
config={config}
settings={settings}
startupWarnings={startupWarnings}
/>
</React.StrictMode>,
{ exitOnCtrlC: false },
);
return;
}
// If not a TTY, read from stdin
// This is for cases where the user pipes input directly into the command
if (!process.stdin.isTTY) {
input += await readStdin();
}
if (!input) {
console.error('No input provided via stdin.');
process.exit(1);
}
logUserPrompt(config, {
'event.name': 'user_prompt',
'event.timestamp': new Date().toISOString(),
prompt: input,
prompt_length: input.length,
});
// Non-interactive mode handled by runNonInteractive
const nonInteractiveConfig = await loadNonInteractiveConfig(
config,
extensions,
settings,
);
await runNonInteractive(nonInteractiveConfig, input);
process.exit(0);
}