@@ -53,6 +53,7 @@ import { calculateApiCost } from "../utils/cost"
5353import { fileExistsAtPath } from "../utils/fs"
5454import { arePathsEqual , getReadablePath } from "../utils/path"
5555import { parseMentions } from "./mentions"
56+ import { RooIgnoreController , LOCK_TEXT_SYMBOL } from "./ignore/RooIgnoreController"
5657import { AssistantMessageContent , parseAssistantMessage , ToolParamName , ToolUseName } from "./assistant-message"
5758import { formatResponse } from "./prompts/responses"
5859import { SYSTEM_PROMPT } from "./prompts/system"
@@ -100,6 +101,7 @@ export class Cline {
100101
101102 apiConversationHistory : ( Anthropic . MessageParam & { ts ?: number } ) [ ] = [ ]
102103 clineMessages : ClineMessage [ ] = [ ]
104+ rooIgnoreController ?: RooIgnoreController
103105 private askResponse ?: ClineAskResponse
104106 private askResponseText ?: string
105107 private askResponseImages ?: string [ ]
@@ -148,6 +150,11 @@ export class Cline {
148150 throw new Error ( "Either historyItem or task/images must be provided" )
149151 }
150152
153+ this . rooIgnoreController = new RooIgnoreController ( cwd )
154+ this . rooIgnoreController . initialize ( ) . catch ( ( error ) => {
155+ console . error ( "Failed to initialize RooIgnoreController:" , error )
156+ } )
157+
151158 this . taskId = historyItem ? historyItem . id : crypto . randomUUID ( )
152159
153160 this . apiConfiguration = apiConfiguration
@@ -791,6 +798,7 @@ export class Cline {
791798 this . terminalManager . disposeAll ( )
792799 this . urlContentFetcher . closeBrowser ( )
793800 this . browserSession . closeBrowser ( )
801+ this . rooIgnoreController ?. dispose ( )
794802
795803 // If we're not streaming then `abortStream` (which reverts the diff
796804 // view changes) won't be called, so we need to revert the changes here.
@@ -942,6 +950,8 @@ export class Cline {
942950 } )
943951 }
944952
953+ const rooIgnoreInstructions = this . rooIgnoreController ?. getInstructions ( )
954+
945955 const {
946956 browserViewportSize,
947957 mode,
@@ -972,6 +982,7 @@ export class Cline {
972982 this . diffEnabled ,
973983 experiments ,
974984 enableMcpServerCreation ,
985+ rooIgnoreInstructions ,
975986 )
976987 } ) ( )
977988
@@ -1346,6 +1357,15 @@ export class Cline {
13461357 // wait so we can determine if it's a new file or editing an existing file
13471358 break
13481359 }
1360+
1361+ const accessAllowed = this . rooIgnoreController ?. validateAccess ( relPath )
1362+ if ( ! accessAllowed ) {
1363+ await this . say ( "rooignore_error" , relPath )
1364+ pushToolResult ( formatResponse . toolError ( formatResponse . rooIgnoreError ( relPath ) ) )
1365+
1366+ break
1367+ }
1368+
13491369 // Check if file exists using cached map or fs.access
13501370 let fileExists : boolean
13511371 if ( this . diffViewProvider . editType !== undefined ) {
@@ -1555,6 +1575,14 @@ export class Cline {
15551575 break
15561576 }
15571577
1578+ const accessAllowed = this . rooIgnoreController ?. validateAccess ( relPath )
1579+ if ( ! accessAllowed ) {
1580+ await this . say ( "rooignore_error" , relPath )
1581+ pushToolResult ( formatResponse . toolError ( formatResponse . rooIgnoreError ( relPath ) ) )
1582+
1583+ break
1584+ }
1585+
15581586 const absolutePath = path . resolve ( cwd , relPath )
15591587 const fileExists = await fileExistsAtPath ( absolutePath )
15601588
@@ -1988,6 +2016,15 @@ export class Cline {
19882016 pushToolResult ( await this . sayAndCreateMissingParamError ( "read_file" , "path" ) )
19892017 break
19902018 }
2019+
2020+ const accessAllowed = this . rooIgnoreController ?. validateAccess ( relPath )
2021+ if ( ! accessAllowed ) {
2022+ await this . say ( "rooignore_error" , relPath )
2023+ pushToolResult ( formatResponse . toolError ( formatResponse . rooIgnoreError ( relPath ) ) )
2024+
2025+ break
2026+ }
2027+
19912028 this . consecutiveMistakeCount = 0
19922029 const absolutePath = path . resolve ( cwd , relPath )
19932030 const completeMessage = JSON . stringify ( {
@@ -2033,7 +2070,12 @@ export class Cline {
20332070 this . consecutiveMistakeCount = 0
20342071 const absolutePath = path . resolve ( cwd , relDirPath )
20352072 const [ files , didHitLimit ] = await listFiles ( absolutePath , recursive , 200 )
2036- const result = formatResponse . formatFilesList ( absolutePath , files , didHitLimit )
2073+ const result = formatResponse . formatFilesList (
2074+ absolutePath ,
2075+ files ,
2076+ didHitLimit ,
2077+ this . rooIgnoreController ,
2078+ )
20372079 const completeMessage = JSON . stringify ( {
20382080 ...sharedMessageProps ,
20392081 content : result ,
@@ -2074,7 +2116,10 @@ export class Cline {
20742116 }
20752117 this . consecutiveMistakeCount = 0
20762118 const absolutePath = path . resolve ( cwd , relDirPath )
2077- const result = await parseSourceCodeForDefinitionsTopLevel ( absolutePath )
2119+ const result = await parseSourceCodeForDefinitionsTopLevel (
2120+ absolutePath ,
2121+ this . rooIgnoreController ,
2122+ )
20782123 const completeMessage = JSON . stringify ( {
20792124 ...sharedMessageProps ,
20802125 content : result ,
@@ -2122,7 +2167,13 @@ export class Cline {
21222167 }
21232168 this . consecutiveMistakeCount = 0
21242169 const absolutePath = path . resolve ( cwd , relDirPath )
2125- const results = await regexSearchFiles ( cwd , absolutePath , regex , filePattern )
2170+ const results = await regexSearchFiles (
2171+ cwd ,
2172+ absolutePath ,
2173+ regex ,
2174+ filePattern ,
2175+ this . rooIgnoreController ,
2176+ )
21262177 const completeMessage = JSON . stringify ( {
21272178 ...sharedMessageProps ,
21282179 content : results ,
@@ -2301,6 +2352,19 @@ export class Cline {
23012352 )
23022353 break
23032354 }
2355+
2356+ const ignoredFileAttemptedToAccess = this . rooIgnoreController ?. validateCommand ( command )
2357+ if ( ignoredFileAttemptedToAccess ) {
2358+ await this . say ( "rooignore_error" , ignoredFileAttemptedToAccess )
2359+ pushToolResult (
2360+ formatResponse . toolError (
2361+ formatResponse . rooIgnoreError ( ignoredFileAttemptedToAccess ) ,
2362+ ) ,
2363+ )
2364+
2365+ break
2366+ }
2367+
23042368 this . consecutiveMistakeCount = 0
23052369
23062370 const didApprove = await askApproval ( "command" , command )
@@ -3161,29 +3225,39 @@ export class Cline {
31613225
31623226 // It could be useful for cline to know if the user went from one or no file to another between messages, so we always include this context
31633227 details += "\n\n# VSCode Visible Files"
3164- const visibleFiles = vscode . window . visibleTextEditors
3228+ const visibleFilePaths = vscode . window . visibleTextEditors
31653229 ?. map ( ( editor ) => editor . document ?. uri ?. fsPath )
31663230 . filter ( Boolean )
3167- . map ( ( absolutePath ) => path . relative ( cwd , absolutePath ) . toPosix ( ) )
3168- . join ( "\n" )
3169- if ( visibleFiles ) {
3170- details += `\n${ visibleFiles } `
3231+ . map ( ( absolutePath ) => path . relative ( cwd , absolutePath ) )
3232+
3233+ // Filter paths through rooIgnoreController
3234+ const allowedVisibleFiles = this . rooIgnoreController
3235+ ? this . rooIgnoreController . filterPaths ( visibleFilePaths )
3236+ : visibleFilePaths . map ( ( p ) => p . toPosix ( ) ) . join ( "\n" )
3237+
3238+ if ( allowedVisibleFiles ) {
3239+ details += `\n${ allowedVisibleFiles } `
31713240 } else {
31723241 details += "\n(No visible files)"
31733242 }
31743243
31753244 details += "\n\n# VSCode Open Tabs"
31763245 const { maxOpenTabsContext } = ( await this . providerRef . deref ( ) ?. getState ( ) ) ?? { }
31773246 const maxTabs = maxOpenTabsContext ?? 20
3178- const openTabs = vscode . window . tabGroups . all
3247+ const openTabPaths = vscode . window . tabGroups . all
31793248 . flatMap ( ( group ) => group . tabs )
31803249 . map ( ( tab ) => ( tab . input as vscode . TabInputText ) ?. uri ?. fsPath )
31813250 . filter ( Boolean )
31823251 . map ( ( absolutePath ) => path . relative ( cwd , absolutePath ) . toPosix ( ) )
31833252 . slice ( 0 , maxTabs )
3184- . join ( "\n" )
3185- if ( openTabs ) {
3186- details += `\n${ openTabs } `
3253+
3254+ // Filter paths through rooIgnoreController
3255+ const allowedOpenTabs = this . rooIgnoreController
3256+ ? this . rooIgnoreController . filterPaths ( openTabPaths )
3257+ : openTabPaths . map ( ( p ) => p . toPosix ( ) ) . join ( "\n" )
3258+
3259+ if ( allowedOpenTabs ) {
3260+ details += `\n${ allowedOpenTabs } `
31873261 } else {
31883262 details += "\n(No open tabs)"
31893263 }
@@ -3342,7 +3416,7 @@ export class Cline {
33423416 details += "(Desktop files not shown automatically. Use list_files to explore if needed.)"
33433417 } else {
33443418 const [ files , didHitLimit ] = await listFiles ( cwd , true , 200 )
3345- const result = formatResponse . formatFilesList ( cwd , files , didHitLimit )
3419+ const result = formatResponse . formatFilesList ( cwd , files , didHitLimit , this . rooIgnoreController )
33463420 details += result
33473421 }
33483422 }
0 commit comments