1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import yargs from 'yargs/yargs';
import { hideBin } from 'yargs/helpers';
import process from 'node:process';
import {
Config,
loadEnvironment,
createServerConfig,
loadServerHierarchicalMemory,
} from '@gemini-code/server';
import { Settings } from './settings.js';
import { readPackageUp } from 'read-package-up';
// 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),
};
const DEFAULT_GEMINI_MODEL = 'gemini-2.5-pro-preview-05-06';
interface CliArgs {
model: string | undefined;
sandbox: boolean | string | undefined;
debug: boolean | undefined;
prompt: string | undefined;
all_files: boolean | undefined;
}
async function parseArguments(): Promise<CliArgs> {
const argv = await yargs(hideBin(process.argv))
.option('model', {
alias: 'm',
type: 'string',
description: `Model`,
default: process.env.GEMINI_MODEL || DEFAULT_GEMINI_MODEL,
})
.option('prompt', {
alias: 'p',
type: 'string',
description: 'Prompt. Appended to input on stdin (if any).',
})
.option('sandbox', {
alias: 's',
type: 'boolean',
description: 'Run in sandbox?',
})
.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,
})
.help()
.alias('h', 'help')
.strict().argv;
const finalArgv: CliArgs = {
...argv,
sandbox: argv.sandbox,
};
return finalArgv;
}
// 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,
debugMode: boolean,
): Promise<{ memoryContent: string; fileCount: number }> {
if (debugMode) {
logger.debug(
`CLI: Delegating hierarchical memory load to server for CWD: ${currentWorkingDirectory}`,
);
}
// Directly call the server function.
// The server function will use its own homedir() for the global path.
return loadServerHierarchicalMemory(currentWorkingDirectory, debugMode);
}
export async function loadCliConfig(settings: Settings): Promise<Config> {
loadEnvironment();
const geminiApiKey = process.env.GEMINI_API_KEY;
const googleApiKey = process.env.GOOGLE_API_KEY;
const googleCloudProject = process.env.GOOGLE_CLOUD_PROJECT;
const googleCloudLocation = process.env.GOOGLE_CLOUD_LOCATION;
const hasGeminiApiKey = !!geminiApiKey;
const hasGoogleApiKey = !!googleApiKey;
const hasVertexProjectLocationConfig =
!!googleCloudProject && !!googleCloudLocation;
if (!hasGeminiApiKey && !hasGoogleApiKey && !hasVertexProjectLocationConfig) {
logger.error(
'No valid API authentication configuration found. Please set ONE of the following combinations in your environment variables or .env file:\n' +
'1. GEMINI_API_KEY (for Gemini API access).\n' +
'2. GOOGLE_API_KEY (for Gemini API or Vertex AI Express Mode access).\n' +
'3. GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION (for Vertex AI access).\n\n' +
'For Gemini API keys, visit: https://ai.google.dev/gemini-api/docs/api-key\n' +
'For Vertex AI authentication, visit: https://cloud.google.com/vertex-ai/docs/start/authentication\n' +
'The GOOGLE_GENAI_USE_VERTEXAI environment variable can also be set to true/false to influence service selection when ambiguity exists.',
);
process.exit(1);
}
const argv = await parseArguments();
const debugMode = argv.debug || false;
// Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version
const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory(
process.cwd(),
debugMode,
);
const userAgent = await createUserAgent();
const apiKeyForServer = geminiApiKey || googleApiKey || '';
return createServerConfig(
apiKeyForServer,
argv.model || DEFAULT_GEMINI_MODEL,
argv.sandbox ?? settings.sandbox ?? false,
process.cwd(),
debugMode,
argv.prompt || '',
argv.all_files || false,
settings.coreTools || undefined,
settings.toolDiscoveryCommand,
settings.toolCallCommand,
settings.mcpServerCommand,
settings.mcpServers,
userAgent,
memoryContent,
fileCount,
);
}
async function createUserAgent(): Promise<string> {
try {
const packageJsonInfo = await readPackageUp({ cwd: import.meta.url });
const cliVersion = packageJsonInfo?.packageJson.version || 'unknown';
return `GeminiCLI/${cliVersion} Node.js/${process.version} (${process.platform}; ${process.arch})`;
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
logger.warn(
`Could not determine package version for User-Agent: ${message}`,
);
return `GeminiCLI/unknown Node.js/${process.version} (${process.platform}; ${process.arch})`;
}
}
|