Merge tag 'v0.3.0' into chore/sync-gemini-cli-v0.3.0

This commit is contained in:
mingholy.lmh
2025-09-10 21:01:40 +08:00
583 changed files with 30160 additions and 10770 deletions

View File

@@ -4,60 +4,59 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type { Content } from '@google/genai';
import * as path from 'node:path';
import process from 'node:process';
import { GeminiClient } from '../core/client.js';
import type { ContentGeneratorConfig } from '../core/contentGenerator.js';
import {
AuthType,
ContentGeneratorConfig,
createContentGeneratorConfig,
} from '../core/contentGenerator.js';
import { IdeClient } from '../ide/ide-client.js';
import type { MCPOAuthConfig } from '../mcp/oauth-provider.js';
import { PromptRegistry } from '../prompts/prompt-registry.js';
import { ToolRegistry } from '../tools/tool-registry.js';
import { LSTool } from '../tools/ls.js';
import { ReadFileTool } from '../tools/read-file.js';
import { GrepTool } from '../tools/grep.js';
import { GlobTool } from '../tools/glob.js';
import { EditTool } from '../tools/edit.js';
import { ShellTool } from '../tools/shell.js';
import { WriteFileTool } from '../tools/write-file.js';
import { WebFetchTool } from '../tools/web-fetch.js';
import { ReadManyFilesTool } from '../tools/read-many-files.js';
import {
MemoryTool,
setGeminiMdFilename,
GEMINI_CONFIG_DIR as GEMINI_DIR,
} from '../tools/memoryTool.js';
import { TodoWriteTool } from '../tools/todoWrite.js';
import { WebSearchTool } from '../tools/web-search.js';
import { GeminiClient } from '../core/client.js';
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
import { GitService } from '../services/gitService.js';
import { getProjectTempDir } from '../utils/paths.js';
import {
initializeTelemetry,
DEFAULT_TELEMETRY_TARGET,
type FileSystemService,
StandardFileSystemService,
} from '../services/fileSystemService.js';
import { GitService } from '../services/gitService.js';
import type { TelemetryTarget } from '../telemetry/index.js';
import {
DEFAULT_OTLP_ENDPOINT,
TelemetryTarget,
DEFAULT_TELEMETRY_TARGET,
initializeTelemetry,
StartSessionEvent,
} from '../telemetry/index.js';
import { logCliConfiguration, logIdeConnection } from '../telemetry/loggers.js';
import { IdeConnectionEvent, IdeConnectionType } from '../telemetry/types.js';
import { EditTool } from '../tools/edit.js';
import { GlobTool } from '../tools/glob.js';
import { GrepTool } from '../tools/grep.js';
import { LSTool } from '../tools/ls.js';
import { MemoryTool, setGeminiMdFilename } from '../tools/memoryTool.js';
import { ReadFileTool } from '../tools/read-file.js';
import { ReadManyFilesTool } from '../tools/read-many-files.js';
import { RipGrepTool } from '../tools/ripGrep.js';
import { ShellTool } from '../tools/shell.js';
import { TodoWriteTool } from '../tools/todoWrite.js';
import { ToolRegistry } from '../tools/tool-registry.js';
import type { AnyToolInvocation } from '../tools/tools.js';
import { WebFetchTool } from '../tools/web-fetch.js';
import { WebSearchTool } from '../tools/web-search.js';
import { WriteFileTool } from '../tools/write-file.js';
import { shouldAttemptBrowserLaunch } from '../utils/browser.js';
import { FileExclusions } from '../utils/ignorePatterns.js';
import { WorkspaceContext } from '../utils/workspaceContext.js';
import {
DEFAULT_GEMINI_EMBEDDING_MODEL,
DEFAULT_GEMINI_FLASH_MODEL,
} from './models.js';
import { shouldAttemptBrowserLaunch } from '../utils/browser.js';
import { MCPOAuthConfig } from '../mcp/oauth-provider.js';
import { IdeClient } from '../ide/ide-client.js';
import type { Content } from '@google/genai';
import {
FileSystemService,
StandardFileSystemService,
} from '../services/fileSystemService.js';
import { logCliConfiguration, logIdeConnection } from '../telemetry/loggers.js';
import { IdeConnectionEvent, IdeConnectionType } from '../telemetry/types.js';
import { Storage } from './storage.js';
// Re-export OAuth config type
export type { MCPOAuthConfig };
import { WorkspaceContext } from '../utils/workspaceContext.js';
export type { AnyToolInvocation, MCPOAuthConfig };
export enum ApprovalMode {
DEFAULT = 'default',
@@ -67,6 +66,7 @@ export enum ApprovalMode {
export interface AccessibilitySettings {
disableLoadingPhrases?: boolean;
screenReader?: boolean;
}
export interface BugCommandSettings {
@@ -169,6 +169,7 @@ export interface ConfigParameters {
question?: string;
fullContext?: boolean;
coreTools?: string[];
allowedTools?: string[];
excludeTools?: string[];
toolDiscoveryCommand?: string;
toolCallCommand?: string;
@@ -187,6 +188,7 @@ export interface ConfigParameters {
respectGitIgnore?: boolean;
respectGeminiIgnore?: boolean;
enableRecursiveFileSearch?: boolean;
disableFuzzySearch?: boolean;
};
checkpointing?: boolean;
proxy?: string;
@@ -229,8 +231,11 @@ export interface ConfigParameters {
chatCompression?: ChatCompressionSettings;
interactive?: boolean;
trustedFolder?: boolean;
useRipgrep?: boolean;
shouldUseNodePtyShell?: boolean;
skipNextSpeakerCheck?: boolean;
extensionManagement?: boolean;
enablePromptCompletion?: boolean;
}
export class Config {
@@ -247,6 +252,7 @@ export class Config {
private readonly question: string | undefined;
private readonly fullContext: boolean;
private readonly coreTools: string[] | undefined;
private readonly allowedTools: string[] | undefined;
private readonly excludeTools: string[] | undefined;
private readonly toolDiscoveryCommand: string | undefined;
private readonly toolCallCommand: string | undefined;
@@ -265,6 +271,7 @@ export class Config {
respectGitIgnore: boolean;
respectGeminiIgnore: boolean;
enableRecursiveFileSearch: boolean;
disableFuzzySearch: boolean;
};
private fileDiscoveryService: FileDiscoveryService | null = null;
private gitService: GitService | undefined = undefined;
@@ -278,7 +285,7 @@ export class Config {
private readonly folderTrustFeature: boolean;
private readonly folderTrust: boolean;
private ideMode: boolean;
private ideClient: IdeClient;
private ideClient!: IdeClient;
private inFallbackMode = false;
private readonly systemPromptMappings?: Array<{
baseUrls?: string[];
@@ -313,9 +320,14 @@ export class Config {
private readonly chatCompression: ChatCompressionSettings | undefined;
private readonly interactive: boolean;
private readonly trustedFolder: boolean | undefined;
private readonly useRipgrep: boolean;
private readonly shouldUseNodePtyShell: boolean;
private readonly skipNextSpeakerCheck: boolean;
private readonly extensionManagement: boolean;
private readonly enablePromptCompletion: boolean = false;
private initialized: boolean = false;
readonly storage: Storage;
private readonly fileExclusions: FileExclusions;
constructor(params: ConfigParameters) {
this.sessionId = params.sessionId;
@@ -332,6 +344,7 @@ export class Config {
this.question = params.question;
this.fullContext = params.fullContext ?? false;
this.coreTools = params.coreTools;
this.allowedTools = params.allowedTools;
this.excludeTools = params.excludeTools;
this.toolDiscoveryCommand = params.toolDiscoveryCommand;
this.toolCallCommand = params.toolCallCommand;
@@ -362,6 +375,7 @@ export class Config {
respectGeminiIgnore: params.fileFiltering?.respectGeminiIgnore ?? true,
enableRecursiveFileSearch:
params.fileFiltering?.enableRecursiveFileSearch ?? true,
disableFuzzySearch: params.fileFiltering?.disableFuzzySearch ?? false,
};
this.checkpointing = params.checkpointing ?? false;
this.proxy = params.proxy;
@@ -382,7 +396,6 @@ export class Config {
this.folderTrustFeature = params.folderTrustFeature ?? false;
this.folderTrust = params.folderTrust ?? false;
this.ideMode = params.ideMode ?? false;
this.ideClient = IdeClient.getInstance();
this.systemPromptMappings = params.systemPromptMappings;
this.authType = params.authType;
this.enableOpenAILogging = params.enableOpenAILogging ?? false;
@@ -394,11 +407,16 @@ export class Config {
this.chatCompression = params.chatCompression;
this.interactive = params.interactive ?? false;
this.trustedFolder = params.trustedFolder;
this.shouldUseNodePtyShell = params.shouldUseNodePtyShell ?? false;
this.skipNextSpeakerCheck = params.skipNextSpeakerCheck ?? false;
// Web search
this.tavilyApiKey = params.tavilyApiKey;
this.useRipgrep = params.useRipgrep ?? false;
this.shouldUseNodePtyShell = params.shouldUseNodePtyShell ?? false;
this.skipNextSpeakerCheck = params.skipNextSpeakerCheck ?? false;
this.extensionManagement = params.extensionManagement ?? false;
this.storage = new Storage(this.targetDir);
this.enablePromptCompletion = params.enablePromptCompletion ?? false;
this.fileExclusions = new FileExclusions(this);
if (params.contextFileName) {
setGeminiMdFilename(params.contextFileName);
@@ -419,6 +437,7 @@ export class Config {
throw Error('Config was already initialized');
}
this.initialized = true;
this.ideClient = await IdeClient.getInstance();
// Initialize centralized FileDiscoveryService
this.getFileService();
if (this.getCheckpointingEnabled()) {
@@ -426,6 +445,7 @@ export class Config {
}
this.promptRegistry = new PromptRegistry();
this.toolRegistry = await this.createToolRegistry();
logCliConfiguration(this, new StartSessionEvent(this, this.toolRegistry));
}
async refreshAuth(authMethod: AuthType) {
@@ -577,6 +597,10 @@ export class Config {
return this.coreTools;
}
getAllowedTools(): string[] | undefined {
return this.allowedTools;
}
getExcludeTools(): string[] | undefined {
return this.excludeTools;
}
@@ -618,6 +642,11 @@ export class Config {
}
setApprovalMode(mode: ApprovalMode): void {
if (this.isTrustedFolder() === false && mode !== ApprovalMode.DEFAULT) {
throw new Error(
'Cannot enable privileged approval modes in an untrusted folder.',
);
}
this.approvalMode = mode;
}
@@ -661,18 +690,14 @@ export class Config {
return this.geminiClient;
}
getGeminiDir(): string {
return path.join(this.targetDir, GEMINI_DIR);
}
getProjectTempDir(): string {
return getProjectTempDir(this.getProjectRoot());
}
getEnableRecursiveFileSearch(): boolean {
return this.fileFiltering.enableRecursiveFileSearch;
}
getFileFilteringDisableFuzzySearch(): boolean {
return this.fileFiltering.disableFuzzySearch;
}
getFileFilteringRespectGitIgnore(): boolean {
return this.fileFiltering.respectGitIgnore;
}
@@ -687,6 +712,21 @@ export class Config {
};
}
/**
* Gets custom file exclusion patterns from configuration.
* TODO: This is a placeholder implementation. In the future, this could
* read from settings files, CLI arguments, or environment variables.
*/
getCustomExcludes(): string[] {
// Placeholder implementation - returns empty array for now
// Future implementation could read from:
// - User settings file
// - Project-specific configuration
// - Environment variables
// - CLI arguments
return [];
}
getCheckpointingEnabled(): boolean {
return this.checkpointing;
}
@@ -726,6 +766,10 @@ export class Config {
return this.listExtensions;
}
getExtensionManagement(): boolean {
return this.extensionManagement;
}
getExtensions(): GeminiCLIExtension[] {
return this._extensions;
}
@@ -849,6 +893,10 @@ export class Config {
return this.interactive;
}
getUseRipgrep(): boolean {
return this.useRipgrep;
}
getShouldUseNodePtyShell(): boolean {
return this.shouldUseNodePtyShell;
}
@@ -857,14 +905,26 @@ export class Config {
return this.skipNextSpeakerCheck;
}
getScreenReader(): boolean {
return this.accessibility.screenReader ?? false;
}
getEnablePromptCompletion(): boolean {
return this.enablePromptCompletion;
}
async getGitService(): Promise<GitService> {
if (!this.gitService) {
this.gitService = new GitService(this.targetDir);
this.gitService = new GitService(this.targetDir, this.storage);
await this.gitService.initialize();
}
return this.gitService;
}
getFileExclusions(): FileExclusions {
return this.fileExclusions;
}
async createToolRegistry(): Promise<ToolRegistry> {
const registry = new ToolRegistry(this);
@@ -874,12 +934,10 @@ export class Config {
const className = ToolClass.name;
const toolName = ToolClass.Name || className;
const coreTools = this.getCoreTools();
const excludeTools = this.getExcludeTools();
const excludeTools = this.getExcludeTools() || [];
let isEnabled = false;
if (coreTools === undefined) {
isEnabled = true;
} else {
let isEnabled = true; // Enabled by default if coreTools is not set.
if (coreTools) {
isEnabled = coreTools.some(
(tool) =>
tool === className ||
@@ -889,10 +947,11 @@ export class Config {
);
}
if (
excludeTools?.includes(className) ||
excludeTools?.includes(toolName)
) {
const isExcluded = excludeTools.some(
(tool) => tool === className || tool === toolName,
);
if (isExcluded) {
isEnabled = false;
}
@@ -903,7 +962,13 @@ export class Config {
registerCoreTool(LSTool, this);
registerCoreTool(ReadFileTool, this);
registerCoreTool(GrepTool, this);
if (this.getUseRipgrep()) {
registerCoreTool(RipGrepTool, this);
} else {
registerCoreTool(GrepTool, this);
}
registerCoreTool(GlobTool, this);
registerCoreTool(EditTool, this);
registerCoreTool(WriteFileTool, this);