(name: string, result: string, isError: boolean)
| 25 | |
| 26 | /** Build a compact display for a settled tool result. */ |
| 27 | export function summarizeToolResult(name: string, result: string, isError: boolean): ToolDisplay { |
| 28 | const raw = (result ?? '').replace(/\s+$/, ''); |
| 29 | const allLines = raw.length ? raw.split('\n') : []; |
| 30 | const n = norm(name); |
| 31 | |
| 32 | // Errors: surface the message (short), no headline metric. |
| 33 | if (isError) { |
| 34 | const lines = allLines.slice(0, 5); |
| 35 | const more = allLines.length - lines.length; |
| 36 | if (more > 0) lines.push(`… +${more} more line(s)`); |
| 37 | return { headline: '', lines }; |
| 38 | } |
| 39 | |
| 40 | // Reads: never echo the file body. The point of the complaint. Just the size. |
| 41 | if (n === 'read_file' || n === 'pdf_read' || n === 'read') { |
| 42 | const m = raw.match(/(\d[\d,]*)\s+lines/i); // read_file's own "— 541 lines" header |
| 43 | const count = m ? m[1] : String(allLines.length); |
| 44 | return { headline: `${count} lines`, lines: [] }; |
| 45 | } |
| 46 | |
| 47 | // Listings & searches: a count plus a few entries, then "+N more". |
| 48 | if ( |
| 49 | n === 'ls' || n === 'list_files' || n === 'glob' || n === 'find' || |
| 50 | n === 'grep' || n === 'search' || n === 'search_code' || n === 'codebase_search' |
| 51 | ) { |
| 52 | const entries = allLines.filter(l => l.trim().length); |
| 53 | const preview = entries.slice(0, MAX_PREVIEW_LINES); |
| 54 | const more = entries.length - preview.length; |
| 55 | if (more > 0) preview.push(`… +${more} more`); |
| 56 | const noun = (n === 'grep' || n.includes('search')) ? 'match(es)' : 'item(s)'; |
| 57 | return { headline: `${entries.length} ${noun}`, lines: preview }; |
| 58 | } |
| 59 | |
| 60 | // Shell: exit code headline + the tail of the output (where errors live). |
| 61 | if (n === 'shell' || n === 'bash' || n === 'run_shell' || n === 'run') { |
| 62 | const exit = raw.match(/\[exit code:\s*(-?\d+)\]/i); |
| 63 | const body = allLines.filter(l => !/^\s*\[exit code:/i.test(l)); |
| 64 | const tail = body.slice(-MAX_PREVIEW_LINES); |
| 65 | const more = body.length - tail.length; |
| 66 | const lines = more > 0 ? [`… +${more} earlier line(s)`, ...tail] : tail; |
| 67 | return { headline: exit ? `exit ${exit[1]}` : '', lines }; |
| 68 | } |
| 69 | |
| 70 | // Default: cap the preview so nothing dumps a wall of text. |
| 71 | const preview = allLines.slice(0, MAX_PREVIEW_LINES); |
| 72 | const more = allLines.length - preview.length; |
| 73 | if (more > 0) preview.push(`… +${more} more line(s)`); |
| 74 | return { headline: '', lines: preview }; |
| 75 | } |
no test coverage detected