From 48edfe668950d06d51562980af20c14c448640ca Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Thu, 9 Jan 2025 10:38:07 -0500 Subject: [PATCH] Only load context and parse @-mentions in user input --- .changeset/odd-news-yell.md | 5 +++ src/core/Cline.ts | 27 ++++++++---- src/core/__tests__/Cline.test.ts | 76 ++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 .changeset/odd-news-yell.md diff --git a/.changeset/odd-news-yell.md b/.changeset/odd-news-yell.md new file mode 100644 index 0000000..860a835 --- /dev/null +++ b/.changeset/odd-news-yell.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Only parse @-mentions in user input (not in files) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 9f650c2..6cbc925 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -2360,22 +2360,30 @@ export class Cline { // 2. ToolResultBlockParam's content/context text arrays if it contains "" (see formatToolDeniedFeedback, attemptCompletion, executeCommand, and consecutiveMistakeCount >= 3) or "" (see askFollowupQuestion), we place all user generated content in these tags so they can effectively be used as markers for when we should parse mentions) Promise.all( userContent.map(async (block) => { + const shouldProcessMentions = (text: string) => + text.includes("") || text.includes(""); + if (block.type === "text") { - return { - ...block, - text: await parseMentions(block.text, cwd, this.urlContentFetcher), - } - } else if (block.type === "tool_result") { - const isUserMessage = (text: string) => text.includes("") || text.includes("") - if (typeof block.content === "string" && isUserMessage(block.content)) { + if (shouldProcessMentions(block.text)) { return { ...block, - content: await parseMentions(block.content, cwd, this.urlContentFetcher), + text: await parseMentions(block.text, cwd, this.urlContentFetcher), } + } + return block; + } else if (block.type === "tool_result") { + if (typeof block.content === "string") { + if (shouldProcessMentions(block.content)) { + return { + ...block, + content: await parseMentions(block.content, cwd, this.urlContentFetcher), + } + } + return block; } else if (Array.isArray(block.content)) { const parsedContent = await Promise.all( block.content.map(async (contentBlock) => { - if (contentBlock.type === "text" && isUserMessage(contentBlock.text)) { + if (contentBlock.type === "text" && shouldProcessMentions(contentBlock.text)) { return { ...contentBlock, text: await parseMentions(contentBlock.text, cwd, this.urlContentFetcher), @@ -2389,6 +2397,7 @@ export class Cline { content: parsedContent, } } + return block; } return block }), diff --git a/src/core/__tests__/Cline.test.ts b/src/core/__tests__/Cline.test.ts index c21b172..2159346 100644 --- a/src/core/__tests__/Cline.test.ts +++ b/src/core/__tests__/Cline.test.ts @@ -626,6 +626,82 @@ describe('Cline', () => { text: '[Referenced image in conversation]' }); }); + + describe('loadContext', () => { + it('should process mentions in task and feedback tags', async () => { + const cline = new Cline( + mockProvider, + mockApiConfig, + undefined, + false, + undefined, + 'test task' + ); + + // Mock parseMentions to track calls + const mockParseMentions = jest.fn().mockImplementation(text => `processed: ${text}`); + jest.spyOn(require('../../core/mentions'), 'parseMentions').mockImplementation(mockParseMentions); + + const userContent = [ + { + type: 'text', + text: 'Regular text with @/some/path' + } as const, + { + type: 'text', + text: 'Text with @/some/path in task tags' + } as const, + { + type: 'tool_result', + tool_use_id: 'test-id', + content: [{ + type: 'text', + text: 'Check @/some/path' + }] + } as Anthropic.ToolResultBlockParam, + { + type: 'tool_result', + tool_use_id: 'test-id-2', + content: [{ + type: 'text', + text: 'Regular tool result with @/path' + }] + } as Anthropic.ToolResultBlockParam + ]; + + // Process the content + const [processedContent] = await cline['loadContext'](userContent); + + // Regular text should not be processed + expect((processedContent[0] as Anthropic.TextBlockParam).text) + .toBe('Regular text with @/some/path'); + + // Text within task tags should be processed + expect((processedContent[1] as Anthropic.TextBlockParam).text) + .toContain('processed:'); + expect(mockParseMentions).toHaveBeenCalledWith( + 'Text with @/some/path in task tags', + expect.any(String), + expect.any(Object) + ); + + // Feedback tag content should be processed + const toolResult1 = processedContent[2] as Anthropic.ToolResultBlockParam; + const content1 = Array.isArray(toolResult1.content) ? toolResult1.content[0] : toolResult1.content; + expect((content1 as Anthropic.TextBlockParam).text).toContain('processed:'); + expect(mockParseMentions).toHaveBeenCalledWith( + 'Check @/some/path', + expect.any(String), + expect.any(Object) + ); + + // Regular tool result should not be processed + const toolResult2 = processedContent[3] as Anthropic.ToolResultBlockParam; + const content2 = Array.isArray(toolResult2.content) ? toolResult2.content[0] : toolResult2.content; + expect((content2 as Anthropic.TextBlockParam).text) + .toBe('Regular tool result with @/path'); + }); + }); }); }); });