2025-08-08 11:02:27 -07:00
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
2025-10-08 22:17:58 -07:00
import { useState , useCallback , useEffect , useRef } from 'react' ;
2025-09-06 01:39:02 -04:00
import type { LoadedSettings } from '../../config/settings.js' ;
2025-08-08 11:02:27 -07:00
import { FolderTrustChoice } from '../components/FolderTrustDialog.js' ;
2025-08-14 11:15:48 -07:00
import {
loadTrustedFolders ,
TrustLevel ,
isWorkspaceTrusted ,
} from '../../config/trustedFolders.js' ;
2025-08-25 22:11:27 +02:00
import * as process from 'node:process' ;
2025-10-08 22:17:58 -07:00
import { type HistoryItemWithoutId , MessageType } from '../types.js' ;
2026-02-20 10:21:03 -08:00
import {
coreEvents ,
ExitCodes ,
isHeadlessMode ,
FolderTrustDiscoveryService ,
type FolderDiscoveryResults ,
} from '@google/gemini-cli-core' ;
2025-11-26 08:13:21 +05:30
import { runExitCleanup } from '../../utils/cleanup.js' ;
2025-08-08 11:02:27 -07:00
2025-08-14 11:15:48 -07:00
export const useFolderTrust = (
settings : LoadedSettings ,
onTrustChange : ( isTrusted : boolean | undefined ) = > void ,
2025-10-08 22:17:58 -07:00
addItem : ( item : HistoryItemWithoutId , timestamp : number ) = > number ,
2025-08-14 11:15:48 -07:00
) = > {
const [ isTrusted , setIsTrusted ] = useState < boolean | undefined > ( undefined ) ;
const [ isFolderTrustDialogOpen , setIsFolderTrustDialogOpen ] = useState ( false ) ;
2026-02-20 10:21:03 -08:00
const [ discoveryResults , setDiscoveryResults ] =
useState < FolderDiscoveryResults | null > ( null ) ;
2025-09-08 14:43:49 -07:00
const [ isRestarting , setIsRestarting ] = useState ( false ) ;
2025-10-08 22:17:58 -07:00
const startupMessageSent = useRef ( false ) ;
2025-08-14 11:15:48 -07:00
2026-02-03 17:08:10 -08:00
const folderTrust = settings . merged . security . folderTrust . enabled ? ? true ;
2025-08-27 18:39:45 -07:00
2025-08-14 11:15:48 -07:00
useEffect ( ( ) = > {
2026-02-09 15:46:49 -08:00
let isMounted = true ;
2025-09-22 11:45:02 -07:00
const { isTrusted : trusted } = isWorkspaceTrusted ( settings . merged ) ;
2025-10-08 22:17:58 -07:00
2026-02-20 10:21:03 -08:00
if ( trusted === undefined || trusted === false ) {
void FolderTrustDiscoveryService . discover ( process . cwd ( ) )
. then ( ( results ) = > {
if ( isMounted ) {
setDiscoveryResults ( results ) ;
}
} )
. catch ( ( ) = > {
// Silently ignore discovery errors as they are handled within the service
// and reported via results.discoveryErrors if successful.
} ) ;
}
2026-02-09 15:46:49 -08:00
const showUntrustedMessage = ( ) = > {
if ( trusted === false && ! startupMessageSent . current ) {
addItem (
{
type : MessageType . INFO ,
text : 'This folder is untrusted, project settings, hooks, MCPs, and GEMINI.md files will not be applied for this folder.\nUse the `/permissions` command to change the trust level.' ,
} ,
Date . now ( ) ,
) ;
startupMessageSent . current = true ;
}
} ;
if ( isHeadlessMode ( ) ) {
if ( isMounted ) {
setIsTrusted ( trusted ) ;
setIsFolderTrustDialogOpen ( false ) ;
onTrustChange ( true ) ;
showUntrustedMessage ( ) ;
}
} else if ( isMounted ) {
setIsTrusted ( trusted ) ;
setIsFolderTrustDialogOpen ( trusted === undefined ) ;
onTrustChange ( trusted ) ;
showUntrustedMessage ( ) ;
2025-10-08 22:17:58 -07:00
}
2026-02-09 15:46:49 -08:00
return ( ) = > {
isMounted = false ;
} ;
2025-10-08 22:17:58 -07:00
} , [ folderTrust , onTrustChange , settings . merged , addItem ] ) ;
2025-08-08 11:02:27 -07:00
2025-08-14 11:15:48 -07:00
const handleFolderTrustSelect = useCallback (
2026-02-09 09:16:56 -08:00
async ( choice : FolderTrustChoice ) = > {
2026-01-06 10:09:09 -08:00
const trustLevelMap : Record < FolderTrustChoice , TrustLevel > = {
[ FolderTrustChoice . TRUST_FOLDER ] : TrustLevel . TRUST_FOLDER ,
[ FolderTrustChoice . TRUST_PARENT ] : TrustLevel . TRUST_PARENT ,
[ FolderTrustChoice . DO_NOT_TRUST ] : TrustLevel . DO_NOT_TRUST ,
} ;
2025-08-13 11:06:31 -07:00
2026-01-06 10:09:09 -08:00
const trustLevel = trustLevelMap [ choice ] ;
if ( ! trustLevel ) return ;
2025-09-08 14:43:49 -07:00
2026-01-06 10:09:09 -08:00
const cwd = process . cwd ( ) ;
const trustedFolders = loadTrustedFolders ( ) ;
2025-08-13 11:06:31 -07:00
2025-11-14 11:56:39 -08:00
try {
2026-02-09 09:16:56 -08:00
await trustedFolders . setValue ( cwd , trustLevel ) ;
2025-11-14 11:56:39 -08:00
} catch ( _e ) {
coreEvents . emitFeedback (
'error' ,
'Failed to save trust settings. Exiting Gemini CLI.' ,
) ;
2025-11-26 08:13:21 +05:30
setTimeout ( async ( ) = > {
await runExitCleanup ( ) ;
process . exit ( ExitCodes . FATAL_CONFIG_ERROR ) ;
2025-11-14 11:56:39 -08:00
} , 100 ) ;
return ;
}
2025-09-08 14:43:49 -07:00
const currentIsTrusted =
trustLevel === TrustLevel . TRUST_FOLDER ||
trustLevel === TrustLevel . TRUST_PARENT ;
2026-01-06 10:09:09 -08:00
2025-09-08 14:43:49 -07:00
onTrustChange ( currentIsTrusted ) ;
2026-01-06 10:09:09 -08:00
setIsTrusted ( currentIsTrusted ) ;
const wasTrusted = isTrusted ? ? false ;
2025-09-08 14:43:49 -07:00
2026-01-06 10:09:09 -08:00
if ( wasTrusted !== currentIsTrusted ) {
2025-09-08 14:43:49 -07:00
setIsRestarting ( true ) ;
setIsFolderTrustDialogOpen ( true ) ;
} else {
setIsFolderTrustDialogOpen ( false ) ;
}
2025-08-14 11:15:48 -07:00
} ,
2025-09-08 14:43:49 -07:00
[ onTrustChange , isTrusted ] ,
2025-08-14 11:15:48 -07:00
) ;
2025-08-08 11:02:27 -07:00
return {
2025-08-14 11:15:48 -07:00
isTrusted ,
2025-08-08 11:02:27 -07:00
isFolderTrustDialogOpen ,
2026-02-20 10:21:03 -08:00
discoveryResults ,
2025-08-08 11:02:27 -07:00
handleFolderTrustSelect ,
2025-08-21 00:38:12 -07:00
isRestarting ,
2025-08-08 11:02:27 -07:00
} ;
} ;