@@ -11,13 +11,17 @@ export function codeSearch({
1111 pattern,
1212 flags,
1313 cwd,
14- maxResults = 30 ,
14+ maxResults = 15 ,
15+ globalMaxResults = 250 ,
16+ maxOutputStringLength = 20_000 ,
1517} : {
1618 projectPath : string
1719 pattern : string
1820 flags ?: string
1921 cwd ?: string
2022 maxResults ?: number
23+ globalMaxResults ?: number
24+ maxOutputStringLength ?: number
2125} ) : Promise < CodebuffToolOutput < 'code_search' > > {
2226 return new Promise ( ( resolve ) => {
2327 let stdout = ''
@@ -59,27 +63,106 @@ export function codeSearch({
5963 } )
6064
6165 childProcess . on ( 'close' , ( code ) => {
62- // Limit results to maxResults
63- const lines = stdout . split ( '\n' )
64- const limitedLines = lines . slice ( 0 , maxResults )
65- const limitedStdout = limitedLines . join ( '\n' )
66- const formattedStdout = formatCodeSearchOutput ( limitedStdout )
66+ const lines = stdout . split ( '\n' ) . filter ( ( line ) => line . trim ( ) )
67+
68+ // Group results by file
69+ const fileGroups = new Map < string , string [ ] > ( )
70+ let currentFile : string | null = null
71+
72+ for ( const line of lines ) {
73+ // Ripgrep output format: filename:line_number:content or filename:content
74+ const colonIndex = line . indexOf ( ':' )
75+ if ( colonIndex === - 1 ) {
76+ // This shouldn't happen with standard ripgrep output
77+ if ( currentFile ) {
78+ fileGroups . get ( currentFile ) ! . push ( line )
79+ }
80+ continue
81+ }
82+
83+ const filename = line . substring ( 0 , colonIndex )
84+
85+ // Check if this is a new file
86+ if ( filename && ! filename . includes ( '\t' ) && ! filename . startsWith ( ' ' ) ) {
87+ currentFile = filename
88+ if ( ! fileGroups . has ( currentFile ) ) {
89+ fileGroups . set ( currentFile , [ ] )
90+ }
91+ fileGroups . get ( currentFile ) ! . push ( line )
92+ } else if ( currentFile ) {
93+ // Continuation of previous result
94+ fileGroups . get ( currentFile ) ! . push ( line )
95+ }
96+ }
97+
98+ // Limit results per file and globally
99+ const limitedLines : string [ ] = [ ]
100+ let totalOriginalCount = 0
101+ let totalLimitedCount = 0
102+ const truncatedFiles : string [ ] = [ ]
103+ let globalLimitReached = false
104+ const skippedFiles : string [ ] = [ ]
105+
106+ for ( const [ filename , fileLines ] of fileGroups ) {
107+ totalOriginalCount += fileLines . length
108+
109+ // Check if we've hit the global limit
110+ if ( totalLimitedCount >= globalMaxResults ) {
111+ globalLimitReached = true
112+ skippedFiles . push ( filename )
113+ continue
114+ }
115+
116+ // Calculate how many results we can take from this file
117+ const remainingGlobalSpace = globalMaxResults - totalLimitedCount
118+ const resultsToTake = Math . min (
119+ maxResults ,
120+ fileLines . length ,
121+ remainingGlobalSpace ,
122+ )
123+ const limited = fileLines . slice ( 0 , resultsToTake )
124+ totalLimitedCount += limited . length
125+ limitedLines . push ( ...limited )
126+
127+ if ( fileLines . length > resultsToTake ) {
128+ truncatedFiles . push (
129+ `${ filename } : ${ fileLines . length } results (showing ${ resultsToTake } )` ,
130+ )
131+ }
132+ }
133+
134+ let limitedStdout = limitedLines . join ( '\n' )
67135
68136 // Add truncation message if results were limited
69- const finalStdout =
70- lines . length > maxResults
71- ? formattedStdout +
72- `\n\n[Results limited to ${ maxResults } of ${ lines . length } total matches]`
73- : formattedStdout
137+ const truncationMessages : string [ ] = [ ]
138+
139+ if ( truncatedFiles . length > 0 ) {
140+ truncationMessages . push (
141+ `Results limited to ${ maxResults } per file. Truncated files:\n${ truncatedFiles . join ( '\n' ) } ` ,
142+ )
143+ }
144+
145+ if ( globalLimitReached ) {
146+ truncationMessages . push (
147+ `Global limit of ${ globalMaxResults } results reached. ${ skippedFiles . length } file(s) skipped:\n${ skippedFiles . join ( '\n' ) } ` ,
148+ )
149+ }
150+
151+ if ( truncationMessages . length > 0 ) {
152+ limitedStdout += `\n\n[${ truncationMessages . join ( '\n\n' ) } ]`
153+ }
154+
155+ const formattedStdout = formatCodeSearchOutput ( limitedStdout )
156+ const finalStdout = formattedStdout
74157
75158 // Truncate output to prevent memory issues
76- const maxLength = 10000
77159 const truncatedStdout =
78- finalStdout . length > maxLength
79- ? finalStdout . substring ( 0 , maxLength ) + '\n\n[Output truncated]'
160+ finalStdout . length > maxOutputStringLength
161+ ? finalStdout . substring ( 0 , maxOutputStringLength ) +
162+ '\n\n[Output truncated]'
80163 : finalStdout
81164
82- const maxErrorLength = 1000
165+ const maxErrorLength = maxOutputStringLength / 5
83166 const truncatedStderr =
84167 stderr . length > maxErrorLength
85168 ? stderr . substring ( 0 , maxErrorLength ) + '\n\n[Error output truncated]'
0 commit comments