From b7daa7c7021691e3466c4b629e600b7b3af949f0 Mon Sep 17 00:00:00 2001 From: Seth Troisi Date: Thu, 12 Jun 2025 16:08:27 -0700 Subject: [PATCH] Fixed @ file content not being added or sent to server (#962) --- .../src/ui/hooks/atCommandProcessor.test.ts | 59 +++++++------------ .../cli/src/ui/hooks/atCommandProcessor.ts | 42 ++++--------- 2 files changed, 33 insertions(+), 68 deletions(-) diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.test.ts b/packages/cli/src/ui/hooks/atCommandProcessor.test.ts index 790dac15..dc2b89f3 100644 --- a/packages/cli/src/ui/hooks/atCommandProcessor.test.ts +++ b/packages/cli/src/ui/hooks/atCommandProcessor.test.ts @@ -154,9 +154,7 @@ describe('handleAtCommand', () => { const query = `@${filePath}`; const fileContent = 'This is the file content.'; mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${filePath} --- -${fileContent}`, + llmContent: [`--- ${filePath} ---\n\n${fileContent}\n\n`], returnDisplay: 'Read 1 file.', }); @@ -202,9 +200,7 @@ ${fileContent}`, isDirectory: () => true, } as Stats); mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${resolvedGlob} --- -${fileContent}`, + llmContent: [`--- ${resolvedGlob} ---\n\n${fileContent}\n\n`], returnDisplay: 'Read directory contents.', }); @@ -245,9 +241,7 @@ ${fileContent}`, // Current implementation of read_many_files for images returns base64 in text. const imageFileTextContent = '[base64 image data for path/to/image.png]'; mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${imagePath} --- -${imageFileTextContent}`, + llmContent: [`--- ${imagePath} ---\n\n${imageFileTextContent}\n\n`], returnDisplay: 'Read 1 image.', }); @@ -276,9 +270,7 @@ ${imageFileTextContent}`, const query = `${textBefore}@${filePath}${textAfter}`; const fileContent = 'Markdown content.'; mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${filePath} --- -${fileContent}`, + llmContent: [`--- ${filePath} ---\n\n${fileContent}\n\n`], returnDisplay: 'Read 1 doc.', }); @@ -310,9 +302,7 @@ ${fileContent}`, const query = `@${rawPath}`; const fileContent = 'Content of file with space.'; mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${unescapedPath} --- -${fileContent}`, + llmContent: [`--- ${unescapedPath} ---\n\n${fileContent}\n\n`], returnDisplay: 'Read 1 file.', }); @@ -338,11 +328,10 @@ ${fileContent}`, const query = `@${file1} @${file2}`; mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${file1} --- -${content1} ---- ${file2} --- -${content2}`, + llmContent: [ + `--- ${file1} ---\n\n${content1}\n\n`, + `--- ${file2} ---\n\n${content2}\n\n`, + ], returnDisplay: 'Read 2 files.', }); @@ -381,11 +370,10 @@ ${content2}`, const query = `${text1}@${file1}${text2}@${file2}${text3}`; mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${file1} --- -${content1} ---- ${file2} --- -${content2}`, + llmContent: [ + `--- ${file1} ---\n\n${content1}\n\n`, + `--- ${file2} ---\n\n${content2}\n\n`, + ], returnDisplay: 'Read 2 files.', }); @@ -446,11 +434,10 @@ ${content2}`, }); mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${file1} --- -${content1} ---- ${resolvedFile2} --- -${content2}`, + llmContent: [ + `--- ${file1} ---\n\n${content1}\n\n`, + `--- ${resolvedFile2} ---\n\n${content2}\n\n`, + ], returnDisplay: 'Read 2 files.', }); @@ -537,9 +524,7 @@ ${content2}`, }); mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${queryPath} --- -${fileContent}`, + llmContent: [`--- ${queryPath} ---\n\n${fileContent}\n\n`], returnDisplay: 'Read 1 file.', }); @@ -628,9 +613,7 @@ ${fileContent}`, mockFileDiscoveryService.shouldIgnoreFile.mockReturnValue(false); mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${validFile} --- -${fileContent}`, + llmContent: [`--- ${validFile} ---\n\n${fileContent}\n\n`], returnDisplay: 'Read 1 file.', }); @@ -670,9 +653,7 @@ ${fileContent}`, (path: string) => path === gitIgnoredFile, ); mockReadManyFilesExecute.mockResolvedValue({ - llmContent: ` ---- ${validFile} --- -${fileContent}`, + llmContent: [`--- ${validFile} ---\n\n${fileContent}\n\n`], returnDisplay: 'Read 1 file.', }); diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.ts b/packages/cli/src/ui/hooks/atCommandProcessor.ts index f96f6635..04e64f45 100644 --- a/packages/cli/src/ui/hooks/atCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/atCommandProcessor.ts @@ -366,38 +366,22 @@ export async function handleAtCommand({ confirmationDetails: undefined, }; - if ( - result.llmContent && - typeof result.llmContent === 'string' && - result.llmContent.trim() !== '' - ) { + if (Array.isArray(result.llmContent)) { + const fileContentRegex = /^--- (.*?) ---\n\n([\s\S]*?)\n\n$/; processedQueryParts.push({ text: '\n--- Content from referenced files ---', }); - const fileContentRegex = - /\n--- (.*?) ---\n([\s\S]*?)(?=\n--- .*? ---\n|$)/g; - let match; - const foundContentForSpecs = new Set(); - while ((match = fileContentRegex.exec(result.llmContent)) !== null) { - const filePathSpecInContent = match[1]; // This is a resolved pathSpec - const fileActualContent = match[2].trim(); - if (pathSpecsToRead.includes(filePathSpecInContent)) { - // Ensure we only add content for paths we requested - processedQueryParts.push({ - text: `\nContent from @${filePathSpecInContent}:\n`, - }); - processedQueryParts.push({ text: fileActualContent }); - foundContentForSpecs.add(filePathSpecInContent); - } - } - // Check if any requested pathSpecs didn't yield content in the parsed block, could indicate an issue. - for (const requestedSpec of pathSpecsToRead) { - if (!foundContentForSpecs.has(requestedSpec)) { - onDebugMessage( - `Content for @${requestedSpec} was expected but not found in read_many_files output.`, - ); - // Optionally add a note about missing content for this spec - // processedQueryParts.push({ text: `\nContent for @${requestedSpec} not found or empty.\n` }); + for (const part of result.llmContent) { + if (typeof part === 'string') { + const match = fileContentRegex.exec(part); + if (match) { + const filePathSpecInContent = match[1]; // This is a resolved pathSpec + const fileActualContent = match[2].trim(); + processedQueryParts.push({ + text: `\nContent from @${filePathSpecInContent}:\n`, + }); + processedQueryParts.push({ text: fileActualContent }); + } } } processedQueryParts.push({ text: '\n--- End of content ---' });