diff --git a/packages/cli/src/config/trustedFolders.test.ts b/packages/cli/src/config/trustedFolders.test.ts index 892cd86e4b..a57a946d54 100644 --- a/packages/cli/src/config/trustedFolders.test.ts +++ b/packages/cli/src/config/trustedFolders.test.ts @@ -457,6 +457,54 @@ describe('Trusted Folders', () => { const folders = loadTrustedFolders(); expect(folders.isPathTrusted('/any-untrusted-path')).toBe(true); }); + + it('should return true when a prompt flag is detected in process.argv', async () => { + const geminiCore = await import('@google/gemini-cli-core'); + vi.spyOn(geminiCore, 'isHeadlessMode').mockReturnValue(false); + + const originalArgv = process.argv; + process.argv = ['node', 'gemini', '--prompt', 'test']; + + try { + expect(isWorkspaceTrusted(mockSettings).isTrusted).toBe(true); + const folders = loadTrustedFolders(); + expect(folders.isPathTrusted('/any-path')).toBe(true); + } finally { + process.argv = originalArgv; + } + }); + + it('should return true when a positional argument (query/command) is detected', async () => { + const geminiCore = await import('@google/gemini-cli-core'); + vi.spyOn(geminiCore, 'isHeadlessMode').mockReturnValue(false); + + const originalArgv = process.argv; + process.argv = ['node', 'gemini', '/pickle']; + + try { + expect(isWorkspaceTrusted(mockSettings).isTrusted).toBe(true); + } finally { + process.argv = originalArgv; + } + }); + + it('should return false (or check config) when no headless indicators are present', async () => { + const geminiCore = await import('@google/gemini-cli-core'); + vi.spyOn(geminiCore, 'isHeadlessMode').mockReturnValue(false); + + const originalArgv = process.argv; + process.argv = ['node', 'gemini']; + + try { + const config = { '/projectA': TrustLevel.DO_NOT_TRUST }; + fs.writeFileSync(trustedFoldersPath, JSON.stringify(config), 'utf-8'); + expect(isWorkspaceTrusted(mockSettings, '/projectA').isTrusted).toBe( + false, + ); + } finally { + process.argv = originalArgv; + } + }); }); describe('Trusted Folders Caching', () => { diff --git a/packages/cli/src/config/trustedFolders.ts b/packages/cli/src/config/trustedFolders.ts index 2a808c4c02..50082d7244 100644 --- a/packages/cli/src/config/trustedFolders.ts +++ b/packages/cli/src/config/trustedFolders.ts @@ -104,6 +104,28 @@ function getRealPath(location: string): string { return realPath; } +function isActuallyHeadless(): boolean { + if (isHeadlessMode()) { + return true; + } + + // Sniff for headless mode from process.argv if not already detected by core. + // This helps identify "headless" sessions (e.g. running a specific command or query) + // that don't need interactive trust prompts. + const args = process.argv.slice(2); + const doubleDashIndex = args.indexOf('--'); + const relevantArgs = + doubleDashIndex === -1 ? args : args.slice(0, doubleDashIndex); + + return ( + relevantArgs.includes('-p') || + relevantArgs.includes('--prompt') || + relevantArgs.includes('-q') || + relevantArgs.includes('--query') || + (relevantArgs.length > 0 && !relevantArgs[0].startsWith('-')) + ); +} + export class LoadedTrustedFolders { constructor( readonly user: TrustedFoldersFile, @@ -128,7 +150,7 @@ export class LoadedTrustedFolders { location: string, config?: Record, ): boolean | undefined { - if (isHeadlessMode()) { + if (isActuallyHeadless()) { return true; } const configToUse = config ?? this.user.config; @@ -358,7 +380,7 @@ export function isWorkspaceTrusted( workspaceDir: string = process.cwd(), trustConfig?: Record, ): TrustResult { - if (isHeadlessMode()) { + if (isActuallyHeadless()) { return { isTrusted: true, source: undefined }; }