@@ -160,177 +160,156 @@ export function parseToolCallDetails(
160
160
} ,
161
161
content : string
162
162
) : ParsedToolCallDetails {
163
+ // Parse arguments once with graceful fallback
163
164
let args : { command ?: string , path ?: string , prDescription ?: string , commitMessage ?: string , view_range ?: unknown } = { } ;
164
- try {
165
- args = toolCall . function . arguments ? JSON . parse ( toolCall . function . arguments ) : { } ;
166
- } catch {
167
- // fallback to empty args
168
- }
165
+ try { args = toolCall . function . arguments ? JSON . parse ( toolCall . function . arguments ) : { } ; } catch { /* ignore */ }
169
166
170
167
const name = toolCall . function . name ;
171
168
172
- if ( name === 'str_replace_editor' ) {
173
- if ( args . command === 'view' ) {
174
- const parsedContent = parseDiff ( content ) ;
175
- const parsedRange = parseRange ( args . view_range ) ;
176
- if ( parsedContent ) {
177
- const file = parsedContent . fileA ?? parsedContent . fileB ;
178
- const fileLabel = file && toFileLabel ( file ) ;
179
- return {
180
- toolName : fileLabel === '' ? 'Read repository' : 'Read' ,
181
- invocationMessage : fileLabel ? ( `Read [](${ fileLabel } )` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) : 'Read repository' ,
182
- pastTenseMessage : fileLabel ? ( `Read [](${ fileLabel } )` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) : 'Read repository' ,
183
- toolSpecificData : fileLabel ? {
184
- command : 'view' ,
185
- filePath : file ,
186
- fileLabel : fileLabel ,
187
- parsedContent : parsedContent ,
188
- viewRange : parsedRange
189
- } : undefined
190
- } ;
191
- } else {
192
- const filePath = args . path ;
193
- let fileLabel = filePath ? toFileLabel ( filePath ) : undefined ;
194
-
195
- if ( fileLabel === undefined ) {
196
- fileLabel = filePath ;
197
-
198
- return {
199
- toolName : fileLabel ? ( `Read ${ fileLabel } ` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) : 'Read repository' ,
200
- invocationMessage : fileLabel ? ( `Read ${ fileLabel } ` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) : 'Read repository' ,
201
- pastTenseMessage : fileLabel ? ( `Read ${ fileLabel } ` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) : 'Read repository' ,
202
- } ;
203
- } else if ( fileLabel === '' ) {
204
- return {
205
- toolName : 'Read repository' ,
206
- invocationMessage : 'Read repository' ,
207
- pastTenseMessage : 'Read repository' ,
208
- } ;
209
- } else {
210
- return {
211
- toolName : `Read` ,
212
- invocationMessage : ( `Read ${ fileLabel } ` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) ,
213
- pastTenseMessage : ( `Read ${ fileLabel } ` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) ,
214
- toolSpecificData : {
215
- command : 'view' ,
216
- filePath : filePath ,
217
- fileLabel : fileLabel ,
218
- viewRange : parsedRange
219
- }
220
- } ;
221
- }
222
- }
223
- } else {
224
- const filePath = args . path ;
225
- const fileLabel = filePath && toFileLabel ( filePath ) ;
226
- const parsedRange = parseRange ( args . view_range ) ;
227
-
228
- return {
229
- toolName : 'Edit' ,
230
- invocationMessage : fileLabel ? ( `Edit [](${ fileLabel } )` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) : 'Edit' ,
231
- pastTenseMessage : fileLabel ? ( `Edit [](${ fileLabel } )` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) : 'Edit' ,
232
- toolSpecificData : fileLabel ? {
233
- command : args . command || 'edit' ,
234
- filePath : filePath ,
235
- fileLabel : fileLabel ,
236
- viewRange : parsedRange
237
- } : undefined
238
- } ;
239
- }
240
- } else if ( name === 'str_replace' ) {
241
- const filePath = args . path ;
169
+ // Small focused helpers to remove duplication while preserving behavior
170
+ const buildReadDetails = ( filePath : string | undefined , parsedRange : { start : number , end : number } | undefined , opts ?: { parsedContent ?: { content : string ; fileA : string | undefined ; fileB : string | undefined ; } } ) : ParsedToolCallDetails => {
242
171
const fileLabel = filePath && toFileLabel ( filePath ) ;
243
-
244
- return {
245
- toolName : 'Edit' ,
246
- invocationMessage : fileLabel ? `Edit [](${ fileLabel } )` : `Edit ${ filePath } ` ,
247
- pastTenseMessage : fileLabel ? `Edit [](${ fileLabel } )` : `Edit ${ filePath } ` ,
248
- toolSpecificData : fileLabel ? {
249
- command : 'str_replace' ,
250
- filePath : filePath ,
251
- fileLabel : fileLabel ,
252
- } : undefined
172
+ if ( fileLabel === undefined || fileLabel === '' ) {
173
+ return { toolName : 'Read repository' , invocationMessage : 'Read repository' , pastTenseMessage : 'Read repository' } ;
253
174
}
254
- } else if ( name === 'create' ) {
255
- const filePath = args . path ;
256
- const fileLabel = filePath && toFileLabel ( filePath ) ;
257
-
175
+ const rangeSuffix = parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ;
176
+ // Default helper returns bracket variant (used for generic view). Plain variant handled separately for str_replace_editor non-diff.
258
177
return {
259
- toolName : 'Create ' ,
260
- invocationMessage : fileLabel ? `Create [](${ fileLabel } )` : `Create File ${ filePath } `,
261
- pastTenseMessage : fileLabel ? `Create [](${ fileLabel } )` : `Create File ${ filePath } `,
262
- toolSpecificData : fileLabel ? {
263
- command : 'create ' ,
178
+ toolName : 'Read ' ,
179
+ invocationMessage : `Read [](${ fileLabel } )${ rangeSuffix } `,
180
+ pastTenseMessage : `Read [](${ fileLabel } )${ rangeSuffix } `,
181
+ toolSpecificData : {
182
+ command : 'view ' ,
264
183
filePath : filePath ,
265
184
fileLabel : fileLabel ,
266
- } : undefined
267
- }
268
- } else if ( name === 'view' ) {
269
- const filePath = args . path ;
185
+ parsedContent : opts ?. parsedContent ,
186
+ viewRange : parsedRange
187
+ }
188
+ } ;
189
+ } ;
190
+
191
+ const buildEditDetails = ( filePath : string | undefined , command : string , parsedRange : { start : number , end : number } | undefined , opts ?: { defaultName ?: string } ) : ParsedToolCallDetails => {
270
192
const fileLabel = filePath && toFileLabel ( filePath ) ;
271
- const parsedRange = parseRange ( args . view_range ) ;
193
+ const rangeSuffix = parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ;
194
+ let invocationMessage : string ;
195
+ let pastTenseMessage : string ;
196
+ if ( fileLabel ) {
197
+ invocationMessage = `Edit [](${ fileLabel } )${ rangeSuffix } ` ;
198
+ pastTenseMessage = `Edit [](${ fileLabel } )${ rangeSuffix } ` ;
199
+ } else {
200
+ if ( opts ?. defaultName === 'Create' ) {
201
+ invocationMessage = pastTenseMessage = `Create File ${ filePath } ` ;
202
+ } else {
203
+ invocationMessage = pastTenseMessage = ( opts ?. defaultName || 'Edit' ) ;
204
+ }
205
+ invocationMessage += rangeSuffix ;
206
+ pastTenseMessage += rangeSuffix ;
207
+ }
272
208
273
209
return {
274
- toolName : fileLabel === '' ? 'Read repository' : 'Read ',
275
- invocationMessage : fileLabel ? ( `Read []( ${ fileLabel } )` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) : 'Read repository' ,
276
- pastTenseMessage : fileLabel ? ( `Read []( ${ fileLabel } )` + ( parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ) ) : 'Read repository' ,
210
+ toolName : opts ?. defaultName || 'Edit ',
211
+ invocationMessage,
212
+ pastTenseMessage,
277
213
toolSpecificData : fileLabel ? {
278
- command : 'view' ,
214
+ command : command || ( opts ?. defaultName === 'Create' ? 'create' : ( command || 'edit' ) ) ,
279
215
filePath : filePath ,
280
216
fileLabel : fileLabel ,
281
217
viewRange : parsedRange
282
218
} : undefined
283
219
} ;
284
- } else if ( name === 'think' ) {
285
- const thought = ( args as unknown as { thought ?: string } ) . thought || content || 'Thought' ;
220
+ } ;
221
+
222
+ const buildStrReplaceDetails = ( filePath : string | undefined ) : ParsedToolCallDetails => {
223
+ const fileLabel = filePath && toFileLabel ( filePath ) ;
224
+ const message = fileLabel ? `Edit [](${ fileLabel } )` : `Edit ${ filePath } ` ;
286
225
return {
287
- toolName : 'think' ,
288
- invocationMessage : thought ,
226
+ toolName : 'Edit' ,
227
+ invocationMessage : message ,
228
+ pastTenseMessage : message ,
229
+ toolSpecificData : fileLabel ? { command : 'str_replace' , filePath, fileLabel } : undefined
289
230
} ;
290
- } else if ( name === 'report_progress' ) {
291
- const details : ParsedToolCallDetails = {
292
- toolName : 'Progress Update' ,
293
- invocationMessage : `${ args . prDescription } ` || content || 'Progress Update'
231
+ } ;
232
+
233
+ const buildCreateDetails = ( filePath : string | undefined ) : ParsedToolCallDetails => {
234
+ const fileLabel = filePath && toFileLabel ( filePath ) ;
235
+ const message = fileLabel ? `Create [](${ fileLabel } )` : `Create File ${ filePath } ` ;
236
+ return {
237
+ toolName : 'Create' ,
238
+ invocationMessage : message ,
239
+ pastTenseMessage : message ,
240
+ toolSpecificData : fileLabel ? { command : 'create' , filePath, fileLabel } : undefined
294
241
} ;
295
- if ( args . commitMessage ) {
296
- details . originMessage = `Commit: ${ args . commitMessage } ` ;
297
- }
242
+ } ;
243
+
244
+ const buildBashDetails = ( bashArgs : typeof args , contentStr : string ) : ParsedToolCallDetails => {
245
+ const command = bashArgs . command ? `$ ${ bashArgs . command } ` : undefined ;
246
+ const bashContent = [ command , contentStr ] . filter ( Boolean ) . join ( '\n' ) ;
247
+ const details : ParsedToolCallDetails = { toolName : 'Run Bash command' , invocationMessage : bashContent || 'Run Bash command' } ;
248
+ if ( bashArgs . command ) { details . toolSpecificData = { commandLine : { original : bashArgs . command } , language : 'bash' } ; }
298
249
return details ;
299
- } else if ( name === 'bash' ) {
300
- const command = args . command ? `$ ${ args . command } ` : undefined ;
301
- const bashContent = [ command , content ] . filter ( Boolean ) . join ( '\n' ) ;
302
- const details : ParsedToolCallDetails = {
303
- toolName : 'Run Bash command' ,
304
- invocationMessage : bashContent || 'Run Bash command' ,
305
- } ;
250
+ } ;
306
251
307
- // Use the terminal-specific data for bash commands
308
- if ( args . command ) {
309
- const bashToolData : BashToolData = {
310
- commandLine : {
311
- original : args . command ,
312
- } ,
313
- language : 'bash'
314
- } ;
315
- details . toolSpecificData = bashToolData ;
252
+ switch ( name ) {
253
+ case 'str_replace_editor' : {
254
+ if ( args . command === 'view' ) {
255
+ const parsedContent = parseDiff ( content ) ;
256
+ const parsedRange = parseRange ( args . view_range ) ;
257
+ if ( parsedContent ) {
258
+ const file = parsedContent . fileA ?? parsedContent . fileB ;
259
+ const fileLabel = file && toFileLabel ( file ) ;
260
+ if ( fileLabel === '' ) {
261
+ return { toolName : 'Read repository' , invocationMessage : 'Read repository' , pastTenseMessage : 'Read repository' } ;
262
+ } else if ( fileLabel === undefined ) {
263
+ return { toolName : 'Read' , invocationMessage : 'Read repository' , pastTenseMessage : 'Read repository' } ;
264
+ } else {
265
+ const rangeSuffix = parsedRange ? `, lines ${ parsedRange . start } to ${ parsedRange . end } ` : '' ;
266
+ return {
267
+ toolName : 'Read' ,
268
+ invocationMessage : `Read [](${ fileLabel } )${ rangeSuffix } ` ,
269
+ pastTenseMessage : `Read [](${ fileLabel } )${ rangeSuffix } ` ,
270
+ toolSpecificData : { command : 'view' , filePath : file , fileLabel, parsedContent, viewRange : parsedRange }
271
+ } ;
272
+ }
273
+ }
274
+ // No diff parsed: use PLAIN (non-bracket) variant for str_replace_editor views
275
+ const plainRange = parseRange ( args . view_range ) ;
276
+ const fp = args . path ; const fl = fp && toFileLabel ( fp ) ;
277
+ if ( fl === undefined || fl === '' ) {
278
+ return { toolName : 'Read repository' , invocationMessage : 'Read repository' , pastTenseMessage : 'Read repository' } ;
279
+ }
280
+ const suffix = plainRange ? `, lines ${ plainRange . start } to ${ plainRange . end } ` : '' ;
281
+ return {
282
+ toolName : 'Read' ,
283
+ invocationMessage : `Read ${ fl } ${ suffix } ` ,
284
+ pastTenseMessage : `Read ${ fl } ${ suffix } ` ,
285
+ toolSpecificData : { command : 'view' , filePath : fp , fileLabel : fl , viewRange : plainRange }
286
+ } ;
287
+ }
288
+ return buildEditDetails ( args . path , args . command || 'edit' , parseRange ( args . view_range ) ) ;
316
289
}
317
- return details ;
318
- } else if ( name === 'read_bash' ) {
319
- return {
320
- toolName : 'read_bash' ,
321
- invocationMessage : 'Read logs from Bash session'
290
+ case 'str_replace' :
291
+ return buildStrReplaceDetails ( args . path ) ;
292
+ case 'create' :
293
+ return buildCreateDetails ( args . path ) ;
294
+ case 'view' :
295
+ return buildReadDetails ( args . path , parseRange ( args . view_range ) ) ; // generic view always bracket variant
296
+ case 'think' : {
297
+ const thought = ( args as unknown as { thought ?: string } ) . thought || content || 'Thought' ;
298
+ return { toolName : 'think' , invocationMessage : thought } ;
322
299
}
323
- } else if ( name === 'stop_bash' ) {
324
- return {
325
- toolName : 'stop_bash' ,
326
- invocationMessage : 'Stop Bash session'
300
+ case 'report_progress' : {
301
+ const details : ParsedToolCallDetails = { toolName : 'Progress Update' , invocationMessage : ` ${ args . prDescription } ` || content || 'Progress Update' } ;
302
+ if ( args . commitMessage ) { details . originMessage = `Commit: ${ args . commitMessage } ` ; }
303
+ return details ;
327
304
}
328
- } else {
329
- // Unknown tool type
330
- return {
331
- toolName : name || 'unknown' ,
332
- invocationMessage : content || name || 'unknown'
333
- } ;
305
+ case 'bash' :
306
+ return buildBashDetails ( args , content ) ;
307
+ case 'read_bash' :
308
+ return { toolName : 'read_bash' , invocationMessage : 'Read logs from Bash session' } ;
309
+ case 'stop_bash' :
310
+ return { toolName : 'stop_bash' , invocationMessage : 'Stop Bash session' } ;
311
+ default :
312
+ return { toolName : name || 'unknown' , invocationMessage : content || name || 'unknown' } ;
334
313
}
335
314
}
336
315
0 commit comments