diff --git a/src/core/mentions/index.ts b/src/core/mentions/index.ts index d53a3ff3ed..8ae4f7f131 100644 --- a/src/core/mentions/index.ts +++ b/src/core/mentions/index.ts @@ -17,6 +17,8 @@ import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher" import { FileContextTracker } from "../context-tracking/FileContextTracker" +import { RooIgnoreController } from "../ignore/RooIgnoreController" + export async function openMention(mention?: string): Promise { if (!mention) { return @@ -50,6 +52,8 @@ export async function parseMentions( cwd: string, urlContentFetcher: UrlContentFetcher, fileContextTracker?: FileContextTracker, + rooIgnoreController?: RooIgnoreController, + showRooIgnoredFiles: boolean = true, ): Promise { const mentions: Set = new Set() let parsedText = text.replace(mentionRegexGlobal, (match, mention) => { @@ -102,12 +106,11 @@ export async function parseMentions( } else if (mention.startsWith("/")) { const mentionPath = mention.slice(1) try { - const content = await getFileOrFolderContent(mentionPath, cwd) + const content = await getFileOrFolderContent(mentionPath, cwd, rooIgnoreController, showRooIgnoredFiles) if (mention.endsWith("/")) { parsedText += `\n\n\n${content}\n` } else { parsedText += `\n\n\n${content}\n` - // Track that this file was mentioned and its content was included if (fileContextTracker) { await fileContextTracker.trackFileContext(mentionPath, "file_mentioned") } @@ -161,8 +164,12 @@ export async function parseMentions( return parsedText } -async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise { - // Unescape spaces in the path before resolving it +async function getFileOrFolderContent( + mentionPath: string, + cwd: string, + rooIgnoreController?: any, + showRooIgnoredFiles: boolean = true, +): Promise { const unescapedPath = unescapeSpaces(mentionPath) const absPath = path.resolve(cwd, unescapedPath) @@ -170,6 +177,9 @@ async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise const stats = await fs.stat(absPath) if (stats.isFile()) { + if (rooIgnoreController && !rooIgnoreController.validateAccess(absPath)) { + return `(File ${mentionPath} is ignored by .rooignore)` + } try { const content = await extractTextFromFile(absPath) return content @@ -180,33 +190,51 @@ async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise const entries = await fs.readdir(absPath, { withFileTypes: true }) let folderContent = "" const fileContentPromises: Promise[] = [] - entries.forEach((entry, index) => { + const LOCK_SYMBOL = "🔒" + + for (let index = 0; index < entries.length; index++) { + const entry = entries[index] const isLast = index === entries.length - 1 const linePrefix = isLast ? "└── " : "├── " + const entryPath = path.join(absPath, entry.name) + + let isIgnored = false + if (rooIgnoreController) { + isIgnored = !rooIgnoreController.validateAccess(entryPath) + } + + if (isIgnored && !showRooIgnoredFiles) { + continue + } + + const displayName = isIgnored ? `${LOCK_SYMBOL} ${entry.name}` : entry.name + if (entry.isFile()) { - folderContent += `${linePrefix}${entry.name}\n` - const filePath = path.join(mentionPath, entry.name) - const absoluteFilePath = path.resolve(absPath, entry.name) - fileContentPromises.push( - (async () => { - try { - const isBinary = await isBinaryFile(absoluteFilePath).catch(() => false) - if (isBinary) { + folderContent += `${linePrefix}${displayName}\n` + if (!isIgnored) { + const filePath = path.join(mentionPath, entry.name) + const absoluteFilePath = path.resolve(absPath, entry.name) + fileContentPromises.push( + (async () => { + try { + const isBinary = await isBinaryFile(absoluteFilePath).catch(() => false) + if (isBinary) { + return undefined + } + const content = await extractTextFromFile(absoluteFilePath) + return `\n${content}\n` + } catch (error) { return undefined } - const content = await extractTextFromFile(absoluteFilePath) - return `\n${content}\n` - } catch (error) { - return undefined - } - })(), - ) + })(), + ) + } } else if (entry.isDirectory()) { - folderContent += `${linePrefix}${entry.name}/\n` + folderContent += `${linePrefix}${displayName}/\n` } else { - folderContent += `${linePrefix}${entry.name}\n` + folderContent += `${linePrefix}${displayName}\n` } - }) + } const fileContents = (await Promise.all(fileContentPromises)).filter((content) => content) return `${folderContent}\n${fileContents.join("\n\n")}`.trim() } else { diff --git a/src/core/mentions/processUserContentMentions.ts b/src/core/mentions/processUserContentMentions.ts index 2b69486a86..3f131a1c05 100644 --- a/src/core/mentions/processUserContentMentions.ts +++ b/src/core/mentions/processUserContentMentions.ts @@ -11,11 +11,15 @@ export async function processUserContentMentions({ cwd, urlContentFetcher, fileContextTracker, + rooIgnoreController, + showRooIgnoredFiles = true, }: { userContent: Anthropic.Messages.ContentBlockParam[] cwd: string urlContentFetcher: UrlContentFetcher fileContextTracker: FileContextTracker + rooIgnoreController?: any + showRooIgnoredFiles?: boolean }) { // Process userContent array, which contains various block types: // TextBlockParam, ImageBlockParam, ToolUseBlockParam, and ToolResultBlockParam. @@ -35,7 +39,14 @@ export async function processUserContentMentions({ if (shouldProcessMentions(block.text)) { return { ...block, - text: await parseMentions(block.text, cwd, urlContentFetcher, fileContextTracker), + text: await parseMentions( + block.text, + cwd, + urlContentFetcher, + fileContextTracker, + rooIgnoreController, + showRooIgnoredFiles, + ), } } @@ -45,7 +56,14 @@ export async function processUserContentMentions({ if (shouldProcessMentions(block.content)) { return { ...block, - content: await parseMentions(block.content, cwd, urlContentFetcher, fileContextTracker), + content: await parseMentions( + block.content, + cwd, + urlContentFetcher, + fileContextTracker, + rooIgnoreController, + showRooIgnoredFiles, + ), } } @@ -61,6 +79,8 @@ export async function processUserContentMentions({ cwd, urlContentFetcher, fileContextTracker, + rooIgnoreController, + showRooIgnoredFiles, ), } } diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index c9f2b4a100..6b20376431 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1096,11 +1096,15 @@ export class Task extends EventEmitter { }), ) + const { showRooIgnoredFiles = true } = (await this.providerRef.deref()?.getState()) ?? {} + const parsedUserContent = await processUserContentMentions({ userContent, cwd: this.cwd, urlContentFetcher: this.urlContentFetcher, fileContextTracker: this.fileContextTracker, + rooIgnoreController: this.rooIgnoreController, + showRooIgnoredFiles, }) const environmentDetails = await getEnvironmentDetails(this, includeFileDetails)