@@ -14,6 +14,7 @@ import { ClineProvider } from "../../webview/ClineProvider"
1414import { ApiStreamChunk } from "../../../api/transform/stream"
1515import { ContextProxy } from "../../config/ContextProxy"
1616import { processUserContentMentions } from "../../mentions/processUserContentMentions"
17+ import { extractFileMentions , hasFileMentions } from "../../mentions/extractFileMentions"
1718import { MultiSearchReplaceDiffStrategy } from "../../diff/strategies/multi-search-replace"
1819import { MultiFileSearchReplaceDiffStrategy } from "../../diff/strategies/multi-file-search-replace"
1920import { EXPERIMENT_IDS } from "../../../shared/experiments"
@@ -72,6 +73,19 @@ vi.mock("p-wait-for", () => ({
7273 default : vi . fn ( ) . mockImplementation ( async ( ) => Promise . resolve ( ) ) ,
7374} ) )
7475
76+ vi . mock ( "../../mentions/processUserContentMentions" , async ( importOriginal ) => {
77+ const actual = ( await importOriginal ( ) ) as any
78+ return {
79+ ...actual ,
80+ processUserContentMentions : vi . fn ( ) . mockImplementation ( actual . processUserContentMentions ) ,
81+ }
82+ } )
83+
84+ vi . mock ( "../../mentions/extractFileMentions" , ( ) => ( {
85+ extractFileMentions : vi . fn ( ) ,
86+ hasFileMentions : vi . fn ( ) ,
87+ } ) )
88+
7589vi . mock ( "vscode" , ( ) => {
7690 const mockDisposable = { dispose : vi . fn ( ) }
7791 const mockEventEmitter = { event : vi . fn ( ) , fire : vi . fn ( ) }
@@ -883,19 +897,19 @@ describe("Cline", () => {
883897 const userContent = [
884898 {
885899 type : "text" ,
886- text : "Regular text with 'some/path' (see below for file content) " ,
900+ text : "Regular text with 'some/path'" ,
887901 } as const ,
888902 {
889903 type : "text" ,
890- text : "<task>Text with 'some/path' (see below for file content) in task tags</task>" ,
904+ text : "<task>Text with 'some/path' in task tags</task>" ,
891905 } as const ,
892906 {
893907 type : "tool_result" ,
894908 tool_use_id : "test-id" ,
895909 content : [
896910 {
897911 type : "text" ,
898- text : "<feedback>Check 'some/path' (see below for file content) </feedback>" ,
912+ text : "<feedback>Check 'some/path'</feedback>" ,
899913 } ,
900914 ] ,
901915 } as Anthropic . ToolResultBlockParam ,
@@ -905,7 +919,7 @@ describe("Cline", () => {
905919 content : [
906920 {
907921 type : "text" ,
908- text : "Regular tool result with 'path' (see below for file content) " ,
922+ text : "Regular tool result with 'path'" ,
909923 } ,
910924 ] ,
911925 } as Anthropic . ToolResultBlockParam ,
@@ -919,30 +933,26 @@ describe("Cline", () => {
919933 } )
920934
921935 // Regular text should not be processed
922- expect ( ( processedContent [ 0 ] as Anthropic . TextBlockParam ) . text ) . toBe (
923- "Regular text with 'some/path' (see below for file content)" ,
924- )
936+ expect ( ( processedContent [ 0 ] as Anthropic . TextBlockParam ) . text ) . toBe ( "Regular text with 'some/path'" )
925937
926938 // Text within task tags should be processed
927939 expect ( ( processedContent [ 1 ] as Anthropic . TextBlockParam ) . text ) . toBeDefined ( )
928940 expect ( ( processedContent [ 1 ] as Anthropic . TextBlockParam ) . text ) . toContain (
929- "<task>Text with 'some/path' (see below for file content) in task tags</task>" ,
941+ "<task>Text with 'some/path' in task tags</task>" ,
930942 )
931943
932944 // Feedback tag content should be processed
933945 const toolResult1 = processedContent [ 2 ] as Anthropic . ToolResultBlockParam
934946 const content1 = Array . isArray ( toolResult1 . content ) ? toolResult1 . content [ 0 ] : toolResult1 . content
935947 expect ( ( processedContent [ 1 ] as Anthropic . TextBlockParam ) . text ) . toBeDefined ( )
936948 expect ( ( content1 as Anthropic . TextBlockParam ) . text ) . toContain (
937- "<feedback>Check 'some/path' (see below for file content) </feedback>" ,
949+ "<feedback>Check 'some/path'</feedback>" ,
938950 )
939951
940952 // Regular tool result should not be processed
941953 const toolResult2 = processedContent [ 3 ] as Anthropic . ToolResultBlockParam
942954 const content2 = Array . isArray ( toolResult2 . content ) ? toolResult2 . content [ 0 ] : toolResult2 . content
943- expect ( ( content2 as Anthropic . TextBlockParam ) . text ) . toBe (
944- "Regular tool result with 'path' (see below for file content)" ,
945- )
955+ expect ( ( content2 as Anthropic . TextBlockParam ) . text ) . toBe ( "Regular tool result with 'path'" )
946956
947957 await cline . abortTask ( true )
948958 await task . catch ( ( ) => { } )
@@ -1510,19 +1520,23 @@ describe("Cline", () => {
15101520 apiMessages . push ( message )
15111521 } )
15121522
1523+ // Mock extractFileMentions to return a file mention
1524+ vi . mocked ( extractFileMentions ) . mockReturnValue ( [ { mention : "@src/main.ts" , path : "src/main.ts" } ] )
1525+ vi . mocked ( hasFileMentions ) . mockReturnValue ( true )
1526+
15131527 // Mock processUserContentMentions to return processed content
15141528 vi . mocked ( processUserContentMentions ) . mockResolvedValue ( [
15151529 {
15161530 type : "text" ,
1517- text : "Please analyze 'src/main.ts' (see below for file content) and provide feedback\n\n<file_content path=\"src/main.ts\">\nfile content here\n</file_content>" ,
1531+ text : "Please analyze 'src/main.ts' and provide feedback\n\n<file_content path=\"src/main.ts\">\nfile content here\n</file_content>" ,
15181532 } ,
15191533 ] )
15201534
15211535 // Start the task
15221536 await ( task as any ) . startTask ( "Please analyze @/src/main.ts and provide feedback" )
15231537
1524- // Verify the API conversation history has 3 messages
1525- expect ( apiMessages ) . toHaveLength ( 3 )
1538+ // Verify the API conversation history has 4 messages (including assistant response)
1539+ expect ( apiMessages ) . toHaveLength ( 4 )
15261540
15271541 // First message: Original user message (unprocessed)
15281542 expect ( apiMessages [ 0 ] . role ) . toBe ( "user" )
@@ -1546,6 +1560,17 @@ describe("Cline", () => {
15461560 startTask : false ,
15471561 } )
15481562
1563+ // Mock hasFileMentions to return false (no file mentions)
1564+ vi . mocked ( hasFileMentions ) . mockReturnValue ( false )
1565+
1566+ // Mock processUserContentMentions to return the content wrapped in task tags
1567+ vi . mocked ( processUserContentMentions ) . mockResolvedValue ( [
1568+ {
1569+ type : "text" ,
1570+ text : "<task>\nPlease help me write a function\n</task>" ,
1571+ } ,
1572+ ] )
1573+
15491574 // Mock the API conversation history to track messages
15501575 const apiMessages : any [ ] = [ ]
15511576 vi . spyOn ( task as any , "addToApiConversationHistory" ) . mockImplementation ( async ( message ) => {
@@ -1575,11 +1600,18 @@ describe("Cline", () => {
15751600 apiMessages . push ( message )
15761601 } )
15771602
1603+ // Mock extractFileMentions to return multiple file mentions
1604+ vi . mocked ( extractFileMentions ) . mockReturnValue ( [
1605+ { mention : "@/src/index.ts" , path : "/src/index.ts" } ,
1606+ { mention : "@/src/utils.ts" , path : "/src/utils.ts" } ,
1607+ ] )
1608+ vi . mocked ( hasFileMentions ) . mockReturnValue ( true )
1609+
15781610 // Mock processUserContentMentions
15791611 vi . mocked ( processUserContentMentions ) . mockResolvedValue ( [
15801612 {
15811613 type : "text" ,
1582- text : "Compare 'src/index.ts' (see below for file content) with 'src/utils.ts' (see below for file content) \n\n<file_content path=\"src/index.ts\">\nindex content\n</file_content>\n\n<file_content path=\"src/utils.ts\">\nutils content\n</file_content>" ,
1614+ text : "Compare 'src/index.ts' with 'src/utils.ts'\n\n<file_content path=\"src/index.ts\">\nindex content\n</file_content>\n\n<file_content path=\"src/utils.ts\">\nutils content\n</file_content>" ,
15831615 } ,
15841616 ] )
15851617
@@ -1588,8 +1620,8 @@ describe("Cline", () => {
15881620
15891621 // Verify synthetic assistant message has multiple read_file calls
15901622 expect ( apiMessages [ 1 ] . role ) . toBe ( "assistant" )
1591- expect ( apiMessages [ 1 ] . content [ 0 ] . text ) . toContain ( "<path>src/index.ts</path>" )
1592- expect ( apiMessages [ 1 ] . content [ 0 ] . text ) . toContain ( "<path>src/utils.ts</path>" )
1623+ expect ( apiMessages [ 1 ] . content [ 0 ] . text ) . toContain ( "<path>/ src/index.ts</path>" )
1624+ expect ( apiMessages [ 1 ] . content [ 0 ] . text ) . toContain ( "<path>/ src/utils.ts</path>" )
15931625 expect ( apiMessages [ 1 ] . content [ 0 ] . text ) . toMatch ( / r e a d _ f i l e .* r e a d _ f i l e / s) // Multiple read_file blocks
15941626 } )
15951627
@@ -1603,17 +1635,32 @@ describe("Cline", () => {
16031635
16041636 // Spy on say method which saves to task history
16051637 const sayMessages : any [ ] = [ ]
1638+ const originalSay = ( task as any ) . say . bind ( task )
16061639 vi . spyOn ( task as any , "say" ) . mockImplementation ( async ( type , text , images ) => {
16071640 sayMessages . push ( { type, text, images } )
1641+ // Call the original implementation to maintain clineMessages
1642+ return originalSay ( type , text , images )
16081643 } )
16091644
1645+ // Mock extractFileMentions to return a file mention
1646+ vi . mocked ( extractFileMentions ) . mockReturnValue ( [ { mention : "@/large-file.ts" , path : "/large-file.ts" } ] )
1647+ vi . mocked ( hasFileMentions ) . mockReturnValue ( true )
1648+
1649+ // Mock processUserContentMentions
1650+ vi . mocked ( processUserContentMentions ) . mockResolvedValue ( [
1651+ {
1652+ type : "text" ,
1653+ text : "Analyze '/large-file.ts'\n\n<file_content path=\"/large-file.ts\">\nfile content here\n</file_content>" ,
1654+ } ,
1655+ ] )
1656+
16101657 // Start the task
16111658 await ( task as any ) . startTask ( "Analyze @/large-file.ts" )
16121659
16131660 // Verify task history only contains the original message
1614- expect ( sayMessages ) . toHaveLength ( 1 )
1615- expect ( sayMessages [ 0 ] . type ) . toBe ( "text" )
1616- expect ( sayMessages [ 0 ] . text ) . toBe ( "Analyze @/large-file.ts" )
1661+ const textMessages = sayMessages . filter ( ( m ) => m . type === "text" )
1662+ expect ( textMessages ) . toHaveLength ( 1 )
1663+ expect ( textMessages [ 0 ] . text ) . toBe ( "Analyze @/large-file.ts" )
16171664 // Should NOT contain processed file content
16181665 expect ( sayMessages [ 0 ] . text ) . not . toContain ( "file_content" )
16191666 } )
0 commit comments