@@ -994,6 +994,63 @@ function formatToolCallAsDetails(options) {
994994 return `<details>\n<summary>${ fullSummary } </summary>\n\n${ detailsContent } \n</details>\n\n` ;
995995}
996996
997+ /**
998+ * Formats a tool result content into a preview string showing the first 2 non-empty lines.
999+ * Uses tree-branch characters (├, └) for visual hierarchy in copilot-cli style.
1000+ *
1001+ * Examples:
1002+ * 1 line: " └ result text"
1003+ * 2 lines: " ├ line 1\n └ line 2"
1004+ * 3+ lines: " ├ line 1\n └ line 2 (+ 1 more)"
1005+ *
1006+ * @param {string } resultText - The result text to preview
1007+ * @param {number } [maxLineLength=80] - Maximum characters per preview line
1008+ * @returns {string } Formatted preview string, or empty string if no content
1009+ */
1010+ function formatResultPreview ( resultText , maxLineLength = 80 ) {
1011+ if ( ! resultText ) return "" ;
1012+
1013+ // Scan line-by-line to avoid building a full array for large outputs.
1014+ // Normalize CRLF by stripping trailing \r from each line.
1015+ let firstLine = "" ;
1016+ let secondLine = "" ;
1017+ let nonEmptyLineCount = 0 ;
1018+ let start = 0 ;
1019+
1020+ while ( start <= resultText . length ) {
1021+ const newlineIndex = resultText . indexOf ( "\n" , start ) ;
1022+ const end = newlineIndex === - 1 ? resultText . length : newlineIndex ;
1023+ // Strip trailing \r to handle Windows CRLF line endings
1024+ const rawLine = resultText . substring ( start , end ) . replace ( / \r $ / , "" ) ;
1025+
1026+ if ( rawLine . trim ( ) ) {
1027+ nonEmptyLineCount += 1 ;
1028+ if ( nonEmptyLineCount === 1 ) {
1029+ const truncated = rawLine . substring ( 0 , maxLineLength ) ;
1030+ firstLine = rawLine . length > maxLineLength ? truncated + "..." : truncated ;
1031+ } else if ( nonEmptyLineCount === 2 ) {
1032+ const truncated = rawLine . substring ( 0 , maxLineLength ) ;
1033+ secondLine = rawLine . length > maxLineLength ? truncated + "..." : truncated ;
1034+ }
1035+ }
1036+
1037+ if ( newlineIndex === - 1 ) {
1038+ break ;
1039+ }
1040+ start = newlineIndex + 1 ;
1041+ }
1042+
1043+ if ( nonEmptyLineCount === 0 ) return "" ;
1044+ if ( nonEmptyLineCount === 1 ) {
1045+ return ` └ ${ firstLine } ` ;
1046+ }
1047+ if ( nonEmptyLineCount === 2 ) {
1048+ return ` ├ ${ firstLine } \n └ ${ secondLine } ` ;
1049+ }
1050+
1051+ return ` ├ ${ firstLine } \n └ ${ secondLine } (+ ${ nonEmptyLineCount - 2 } more)` ;
1052+ }
1053+
9971054/**
9981055 * Generates a lightweight plain text summary optimized for raw text rendering.
9991056 * This is designed for console output (core.info) instead of markdown step summaries.
@@ -1065,14 +1122,15 @@ function generatePlainTextSummary(logEntries, options = {}) {
10651122 displayText = displayText . substring ( 0 , MAX_AGENT_TEXT_LENGTH ) + `... [truncated: showing first ${ MAX_AGENT_TEXT_LENGTH } of ${ text . length } chars]` ;
10661123 }
10671124
1068- // Split into lines and add Agent prefix
1125+ // Split into lines: first line gets "◆ " prefix, continuation lines are indented
10691126 const textLines = displayText . split ( "\n" ) ;
1070- for ( const line of textLines ) {
1127+ for ( let i = 0 ; i < textLines . length ; i ++ ) {
10711128 if ( conversationLineCount >= MAX_CONVERSATION_LINES ) {
10721129 conversationTruncated = true ;
10731130 break ;
10741131 }
1075- lines . push ( `Agent: ${ line } ` ) ;
1132+ const prefix = i === 0 ? "◆ " : " " ;
1133+ lines . push ( `${ prefix } ${ textLines [ i ] } ` ) ;
10761134 conversationLineCount ++ ;
10771135 }
10781136 lines . push ( "" ) ; // Add blank line after agent response
@@ -1100,38 +1158,28 @@ function generatePlainTextSummary(logEntries, options = {}) {
11001158 const cmd = formatBashCommand ( input . command || "" ) ;
11011159 displayName = `$ ${ cmd } ` ;
11021160
1103- // Show result preview if available
1161+ // Show first 2 lines of result using copilot-cli tree-branch style
11041162 if ( toolResult && toolResult . content ) {
11051163 const resultText = typeof toolResult . content === "string" ? toolResult . content : String ( toolResult . content ) ;
1106- const resultLines = resultText . split ( "\n" ) . filter ( l => l . trim ( ) ) ;
1107- if ( resultLines . length > 0 ) {
1108- const previewLine = resultLines [ 0 ] . substring ( 0 , 80 ) ;
1109- if ( resultLines . length > 1 ) {
1110- resultPreview = ` └ ${ resultLines . length } lines...` ;
1111- } else if ( previewLine ) {
1112- resultPreview = ` └ ${ previewLine } ` ;
1113- }
1114- }
1164+ resultPreview = formatResultPreview ( resultText ) ;
11151165 }
11161166 } else if ( toolName . startsWith ( "mcp__" ) ) {
11171167 // Format MCP tool names like github-list_pull_requests
11181168 const formattedName = formatMcpName ( toolName ) . replace ( "::" , "-" ) ;
11191169 displayName = formatToolDisplayName ( formattedName , input ) ;
11201170
1121- // Show result preview if available
1171+ // Show first 2 lines of result using copilot-cli tree-branch style
11221172 if ( toolResult && toolResult . content ) {
11231173 const resultText = typeof toolResult . content === "string" ? toolResult . content : JSON . stringify ( toolResult . content ) ;
1124- const truncated = resultText . length > 80 ? resultText . substring ( 0 , 80 ) + "..." : resultText ;
1125- resultPreview = ` └ ${ truncated } ` ;
1174+ resultPreview = formatResultPreview ( resultText ) ;
11261175 }
11271176 } else {
11281177 displayName = formatToolDisplayName ( toolName , input ) ;
11291178
1130- // Show result preview if available
1179+ // Show first 2 lines of result using copilot-cli tree-branch style
11311180 if ( toolResult && toolResult . content ) {
11321181 const resultText = typeof toolResult . content === "string" ? toolResult . content : String ( toolResult . content ) ;
1133- const truncated = resultText . length > 80 ? resultText . substring ( 0 , 80 ) + "..." : resultText ;
1134- resultPreview = ` └ ${ truncated } ` ;
1182+ resultPreview = formatResultPreview ( resultText ) ;
11351183 }
11361184 }
11371185
@@ -1140,7 +1188,7 @@ function generatePlainTextSummary(logEntries, options = {}) {
11401188
11411189 if ( resultPreview ) {
11421190 lines . push ( resultPreview ) ;
1143- conversationLineCount ++ ;
1191+ conversationLineCount += resultPreview . split ( "\n" ) . length ;
11441192 }
11451193
11461194 lines . push ( "" ) ; // Add blank line after tool execution
@@ -1279,14 +1327,15 @@ function generateCopilotCliStyleSummary(logEntries, options = {}) {
12791327 displayText = displayText . substring ( 0 , MAX_AGENT_TEXT_LENGTH ) + `... [truncated: showing first ${ MAX_AGENT_TEXT_LENGTH } of ${ text . length } chars]` ;
12801328 }
12811329
1282- // Split into lines and add Agent prefix
1330+ // Split into lines: first line gets "◆ " prefix, continuation lines are indented
12831331 const textLines = displayText . split ( "\n" ) ;
1284- for ( const line of textLines ) {
1332+ for ( let i = 0 ; i < textLines . length ; i ++ ) {
12851333 if ( conversationLineCount >= MAX_CONVERSATION_LINES ) {
12861334 conversationTruncated = true ;
12871335 break ;
12881336 }
1289- lines . push ( `Agent: ${ line } ` ) ;
1337+ const prefix = i === 0 ? "◆ " : " " ;
1338+ lines . push ( `${ prefix } ${ textLines [ i ] } ` ) ;
12901339 conversationLineCount ++ ;
12911340 }
12921341 lines . push ( "" ) ; // Add blank line after agent response
@@ -1314,38 +1363,28 @@ function generateCopilotCliStyleSummary(logEntries, options = {}) {
13141363 const cmd = formatBashCommand ( input . command || "" ) ;
13151364 displayName = `$ ${ cmd } ` ;
13161365
1317- // Show result preview if available
1366+ // Show first 2 lines of result using copilot-cli tree-branch style
13181367 if ( toolResult && toolResult . content ) {
13191368 const resultText = typeof toolResult . content === "string" ? toolResult . content : String ( toolResult . content ) ;
1320- const resultLines = resultText . split ( "\n" ) . filter ( l => l . trim ( ) ) ;
1321- if ( resultLines . length > 0 ) {
1322- const previewLine = resultLines [ 0 ] . substring ( 0 , 80 ) ;
1323- if ( resultLines . length > 1 ) {
1324- resultPreview = ` └ ${ resultLines . length } lines...` ;
1325- } else if ( previewLine ) {
1326- resultPreview = ` └ ${ previewLine } ` ;
1327- }
1328- }
1369+ resultPreview = formatResultPreview ( resultText ) ;
13291370 }
13301371 } else if ( toolName . startsWith ( "mcp__" ) ) {
13311372 // Format MCP tool names like github-list_pull_requests
13321373 const formattedName = formatMcpName ( toolName ) . replace ( "::" , "-" ) ;
13331374 displayName = formatToolDisplayName ( formattedName , input ) ;
13341375
1335- // Show result preview if available
1376+ // Show first 2 lines of result using copilot-cli tree-branch style
13361377 if ( toolResult && toolResult . content ) {
13371378 const resultText = typeof toolResult . content === "string" ? toolResult . content : JSON . stringify ( toolResult . content ) ;
1338- const truncated = resultText . length > 80 ? resultText . substring ( 0 , 80 ) + "..." : resultText ;
1339- resultPreview = ` └ ${ truncated } ` ;
1379+ resultPreview = formatResultPreview ( resultText ) ;
13401380 }
13411381 } else {
13421382 displayName = formatToolDisplayName ( toolName , input ) ;
13431383
1344- // Show result preview if available
1384+ // Show first 2 lines of result using copilot-cli tree-branch style
13451385 if ( toolResult && toolResult . content ) {
13461386 const resultText = typeof toolResult . content === "string" ? toolResult . content : String ( toolResult . content ) ;
1347- const truncated = resultText . length > 80 ? resultText . substring ( 0 , 80 ) + "..." : resultText ;
1348- resultPreview = ` └ ${ truncated } ` ;
1387+ resultPreview = formatResultPreview ( resultText ) ;
13491388 }
13501389 }
13511390
@@ -1354,7 +1393,7 @@ function generateCopilotCliStyleSummary(logEntries, options = {}) {
13541393
13551394 if ( resultPreview ) {
13561395 lines . push ( resultPreview ) ;
1357- conversationLineCount ++ ;
1396+ conversationLineCount += resultPreview . split ( "\n" ) . length ;
13581397 }
13591398
13601399 lines . push ( "" ) ; // Add blank line after tool execution
@@ -1628,6 +1667,7 @@ module.exports = {
16281667 formatToolUse,
16291668 parseLogEntries,
16301669 formatToolCallAsDetails,
1670+ formatResultPreview,
16311671 generatePlainTextSummary,
16321672 generateCopilotCliStyleSummary,
16331673 wrapAgentLogInSection,
0 commit comments