@@ -109,6 +109,7 @@ import { FsWrite, FsWriteParams } from './tools/fsWrite'
109
109
import { ExecuteBash , ExecuteBashParams } from './tools/executeBash'
110
110
import { ExplanatoryParams , ToolApprovalException } from './tools/toolShared'
111
111
import { FileSearch , FileSearchParams } from './tools/fileSearch'
112
+ import { GrepSearch , SanitizedRipgrepOutput } from './tools/grepSearch'
112
113
import { loggingUtils } from '@aws/lsp-core'
113
114
import { diffLines } from 'diff'
114
115
import {
@@ -917,13 +918,15 @@ export class AgenticChatController implements ChatHandlers {
917
918
case 'fsRead' :
918
919
case 'listDirectory' :
919
920
case 'fileSearch' :
921
+ case 'grepSearch' :
920
922
case 'fsWrite' :
921
923
case 'executeBash' : {
922
924
const toolMap = {
923
925
fsRead : { Tool : FsRead } ,
924
926
listDirectory : { Tool : ListDirectory } ,
925
927
fsWrite : { Tool : FsWrite } ,
926
928
executeBash : { Tool : ExecuteBash } ,
929
+ grepSearch : { Tool : GrepSearch } ,
927
930
fileSearch : { Tool : FileSearch } ,
928
931
}
929
932
@@ -1033,8 +1036,11 @@ export class AgenticChatController implements ChatHandlers {
1033
1036
// no need to write tool result for listDir and fsRead into chat stream
1034
1037
// executeBash will stream the output instead of waiting until the end
1035
1038
break
1036
- case 'codeSearch' :
1037
- // no need to write tool result for code search.
1039
+ case 'grepSearch' :
1040
+ const grepSearchResult = this . #processGrepSearchResult( toolUse , result , chatResultStream )
1041
+ if ( grepSearchResult ) {
1042
+ await chatResultStream . writeResultBlock ( grepSearchResult )
1043
+ }
1038
1044
break
1039
1045
case 'fsWrite' :
1040
1046
const input = toolUse . input as unknown as FsWriteParams
@@ -1707,6 +1713,72 @@ export class AgenticChatController implements ChatHandlers {
1707
1713
}
1708
1714
}
1709
1715
1716
+ /**
1717
+ * Process grep search results and format them for display in the chat UI
1718
+ */
1719
+ #processGrepSearchResult(
1720
+ toolUse : ToolUse ,
1721
+ result : any ,
1722
+ chatResultStream : AgenticChatResultStream
1723
+ ) : ChatMessage | undefined {
1724
+ if ( toolUse . name !== 'grepSearch' ) {
1725
+ return undefined
1726
+ }
1727
+
1728
+ let messageIdToUpdate = toolUse . toolUseId !
1729
+ const currentId = chatResultStream . getMessageIdToUpdateForTool ( toolUse . name ! )
1730
+
1731
+ if ( currentId ) {
1732
+ messageIdToUpdate = currentId
1733
+ } else {
1734
+ chatResultStream . setMessageIdToUpdateForTool ( toolUse . name ! , messageIdToUpdate )
1735
+ }
1736
+
1737
+ // Extract search results from the tool output
1738
+ const output = result . output . content as SanitizedRipgrepOutput
1739
+ if ( ! output || ! output . fileMatches || ! Array . isArray ( output . fileMatches ) ) {
1740
+ return {
1741
+ type : 'tool' ,
1742
+ messageId : messageIdToUpdate ,
1743
+ body : 'No search results found.' ,
1744
+ }
1745
+ }
1746
+
1747
+ // Process the matches into a structured format
1748
+ const matches = output . fileMatches
1749
+ const fileDetails : Record < string , FileDetails > = { }
1750
+
1751
+ // Create file details directly from matches
1752
+ for ( const match of matches ) {
1753
+ const filePath = match . filePath
1754
+ if ( ! filePath ) continue
1755
+
1756
+ fileDetails [ `${ filePath } (${ match . matches . length } ${ match . matches . length <= 1 ? 'result' : 'results' } )` ] = {
1757
+ description : filePath ,
1758
+ lineRanges : [ { first : - 1 , second : - 1 } ] ,
1759
+ }
1760
+ }
1761
+
1762
+ // Create sorted array of file paths
1763
+ const sortedFilePaths = Object . keys ( fileDetails )
1764
+
1765
+ // Create the context list for display
1766
+ const query = ( toolUse . input as any ) ?. query || 'search term'
1767
+
1768
+ const contextList : FileList = {
1769
+ rootFolderTitle : `Grepped for "${ query } ", ${ output . matchCount } ${ output . matchCount <= 1 ? 'result' : 'results' } found` ,
1770
+ filePaths : sortedFilePaths ,
1771
+ details : fileDetails ,
1772
+ }
1773
+
1774
+ return {
1775
+ type : 'tool' ,
1776
+ fileList : contextList ,
1777
+ messageId : messageIdToUpdate ,
1778
+ body : '' ,
1779
+ }
1780
+ }
1781
+
1710
1782
/**
1711
1783
* Updates the request input with tool results for the next iteration
1712
1784
*/
@@ -1724,7 +1796,7 @@ export class AgenticChatController implements ChatHandlers {
1724
1796
updatedRequestInput . conversationState ! . currentMessage ! . userInputMessage ! . content = content
1725
1797
1726
1798
for ( const toolResult of toolResults ) {
1727
- this . #debug( `ToolResult: ${ JSON . stringify ( toolResult ) } ` )
1799
+ this . #debug( `ToolResult: ${ JSON . stringify ( toolResult ) } ` )
1728
1800
updatedRequestInput . conversationState ! . currentMessage ! . userInputMessage ! . userInputMessageContext ! . toolResults . push (
1729
1801
{
1730
1802
...toolResult ,
@@ -1854,31 +1926,33 @@ export class AgenticChatController implements ChatHandlers {
1854
1926
}
1855
1927
1856
1928
if ( authFollowType ) {
1857
- this . #log( `Q auth error: ${ getErrorMessage ( err ) } ` )
1929
+ this . #log( `Q auth error: ${ getErrorMessage ( err ) } ` )
1858
1930
1859
1931
return createAuthFollowUpResult ( authFollowType )
1860
1932
}
1861
1933
1862
1934
if ( customerFacingErrorCodes . includes ( err . code ) ) {
1863
- this . #features. logging . error ( `${ loggingUtils . formatErr ( err ) } ` )
1935
+ this . #features. logging . error ( `${ loggingUtils . formatErr ( err ) } ` )
1864
1936
if ( err . code === 'InputTooLong' ) {
1865
1937
// Clear the chat history in the database for this tab
1866
1938
this . #chatHistoryDb. clearTab ( tabId )
1867
1939
}
1868
1940
1869
1941
const errorBody =
1870
- err . code === 'QModelResponse' && requestID ? `${ err . message } \n\nRequest ID: ${ requestID } ` : err . message
1942
+ err . code === 'QModelResponse' && requestID
1943
+ ? `${ err . message } \n\nRequest ID: ${ requestID } `
1944
+ : err . message
1871
1945
return new ResponseError < ChatResult > ( LSPErrorCodes . RequestFailed , err . message , {
1872
1946
type : 'answer' ,
1873
1947
body : errorBody ,
1874
1948
messageId : errorMessageId ,
1875
1949
buttons : [ ] ,
1876
1950
} )
1877
1951
}
1878
- this . #features. logging . error ( `Unknown Error: ${ loggingUtils . formatErr ( err ) } ` )
1952
+ this . #features. logging . error ( `Unknown Error: ${ loggingUtils . formatErr ( err ) } ` )
1879
1953
return new ResponseError < ChatResult > ( LSPErrorCodes . RequestFailed , err . message , {
1880
1954
type : 'answer' ,
1881
- body : requestID ? `${ genericErrorMsg } \n\nRequest ID: ${ requestID } ` : genericErrorMsg ,
1955
+ body : requestID ? `${ genericErrorMsg } \n\nRequest ID: ${ requestID } ` : genericErrorMsg ,
1882
1956
messageId : errorMessageId ,
1883
1957
buttons : [ ] ,
1884
1958
} )
@@ -1914,10 +1988,10 @@ export class AgenticChatController implements ChatHandlers {
1914
1988
this . #log( 'Response for inline chat' , JSON . stringify ( response . $metadata ) , JSON . stringify ( response ) )
1915
1989
} catch ( err ) {
1916
1990
if ( err instanceof AmazonQServicePendingSigninError || err instanceof AmazonQServicePendingProfileError ) {
1917
- this . #log( `Q Inline Chat SSO Connection error: ${ getErrorMessage ( err ) } ` )
1991
+ this . #log( `Q Inline Chat SSO Connection error: ${ getErrorMessage ( err ) } ` )
1918
1992
return new ResponseError < ChatResult > ( LSPErrorCodes . RequestFailed , err . message )
1919
1993
}
1920
- this . #log( `Q api request error ${ err instanceof Error ? JSON . stringify ( err ) : 'unknown' } ` )
1994
+ this . #log( `Q api request error ${ err instanceof Error ? JSON . stringify ( err ) : 'unknown' } ` )
1921
1995
return new ResponseError < ChatResult > (
1922
1996
LSPErrorCodes . RequestFailed ,
1923
1997
err instanceof Error ? err . message : 'Unknown request error'
@@ -1963,7 +2037,7 @@ export class AgenticChatController implements ChatHandlers {
1963
2037
if ( ! params . code ) missingParams . push ( 'code' )
1964
2038
1965
2039
this . #log(
1966
- `Q Chat server failed to insert code. Missing required parameters for insert code: ${ missingParams . join ( ', ' ) } `
2040
+ `Q Chat server failed to insert code.Missing required parameters for insert code: ${ missingParams . join ( ', ' ) } `
1967
2041
)
1968
2042
1969
2043
return
@@ -2030,7 +2104,7 @@ export class AgenticChatController implements ChatHandlers {
2030
2104
this . #telemetryController. enqueueCodeDiffEntry ( { ...params , code : textWithIndent } )
2031
2105
} else {
2032
2106
this . #log(
2033
- `Q Chat server failed to insert code: ${ applyResult . failureReason ?? 'No failure reason provided' } `
2107
+ `Q Chat server failed to insert code: ${ applyResult . failureReason ?? 'No failure reason provided' } `
2034
2108
)
2035
2109
}
2036
2110
}
@@ -2195,9 +2269,9 @@ export class AgenticChatController implements ChatHandlers {
2195
2269
return path . join ( getUserPromptsDirectory ( ) , relativePath )
2196
2270
}
2197
2271
2198
- this . #features. logging . error ( `File not found: ${ relativePath } ` )
2272
+ this . #features. logging . error ( `File not found: ${ relativePath } ` )
2199
2273
} catch ( e : any ) {
2200
- this . #features. logging . error ( `Error resolving absolute path: ${ e . message } ` )
2274
+ this . #features. logging . error ( `Error resolving absolute path: ${ e . message } ` )
2201
2275
}
2202
2276
2203
2277
return undefined
0 commit comments