@@ -11,20 +11,38 @@ const ANSI = {
1111 reset : "\x1b[0m" ,
1212 bold : "\x1b[1m" ,
1313 dim : "\x1b[2m" ,
14+ italic : "\x1b[3m" ,
1415 red : "\x1b[31m" ,
1516 green : "\x1b[32m" ,
1617 yellow : "\x1b[33m" ,
1718 blue : "\x1b[34m" ,
19+ magenta : "\x1b[35m" ,
1820 cyan : "\x1b[36m" ,
21+ gray : "\x1b[90m" ,
1922 clearLine : "\r\x1b[K" ,
2023}
2124
25+ /** Symbols for visual indicators */
26+ const SYMBOLS = {
27+ thinking : "💭" ,
28+ tool : "🔧" ,
29+ result : " →" ,
30+ success : "✓" ,
31+ error : "✗" ,
32+ arrow : "▶" ,
33+ spinner : [ "⠋" , "⠙" , "⠹" , "⠸" , "⠼" , "⠴" , "⠦" , "⠧" , "⠇" , "⠏" ] ,
34+ }
35+
2236export class Logger {
2337 private paths : Paths
2438 private verbose : boolean
2539 private cycleLogFile ?: string
2640 private logBuffer : string [ ] = [ ]
2741 private readonly BUFFER_SIZE = 2048
42+ private spinnerInterval ?: ReturnType < typeof setInterval >
43+ private spinnerIndex = 0
44+ private currentSpinnerMessage = ""
45+ private isSpinning = false
2846
2947 constructor ( paths : Paths , verbose : boolean ) {
3048 this . paths = paths
@@ -145,28 +163,62 @@ export class Logger {
145163 * Log a tool call
146164 */
147165 toolCall ( name : string , input ?: unknown ) : void {
148- const inputStr = input ? `: ${ JSON . stringify ( input ) } ` : ""
149- const truncated = inputStr . length > 100 ? `${ inputStr . slice ( 0 , 100 ) } ...` : inputStr
150- console . log ( `${ ANSI . cyan } [TOOL] ${ name } ${ truncated } ${ ANSI . reset } ` )
166+ this . stopSpinner ( )
167+ const inputStr = input ? this . formatToolInput ( input ) : ""
168+ console . log (
169+ `${ ANSI . cyan } ${ SYMBOLS . tool } ${ ANSI . bold } ${ name } ${ ANSI . reset } ${ ANSI . dim } ${ inputStr } ${ ANSI . reset } ` ,
170+ )
151171 this . writeToBuffer ( this . formatForFile ( `[TOOL] ${ name } ${ inputStr } ` ) )
152172 }
153173
174+ /**
175+ * Format tool input for display
176+ */
177+ private formatToolInput ( input : unknown ) : string {
178+ if ( typeof input === "string" ) {
179+ return input . length > 80 ? ` ${ input . slice ( 0 , 80 ) } ...` : ` ${ input } `
180+ }
181+ if ( typeof input === "object" && input !== null ) {
182+ const obj = input as Record < string , unknown >
183+ // Show key parameters for common tools
184+ if ( "filePath" in obj ) return ` ${ obj . filePath } `
185+ if ( "path" in obj ) return ` ${ obj . path } `
186+ if ( "pattern" in obj ) return ` ${ obj . pattern } `
187+ if ( "command" in obj ) {
188+ const cmd = String ( obj . command )
189+ return cmd . length > 60 ? ` ${ cmd . slice ( 0 , 60 ) } ...` : ` ${ cmd } `
190+ }
191+ if ( "query" in obj ) return ` "${ obj . query } "`
192+ // Fallback: stringify and truncate
193+ const str = JSON . stringify ( input )
194+ return str . length > 80 ? ` ${ str . slice ( 0 , 80 ) } ...` : ` ${ str } `
195+ }
196+ return ""
197+ }
198+
154199 /**
155200 * Log a tool result
156201 */
157202 toolResult ( output : string ) : void {
158- const truncated = output . length > 200 ? `${ output . slice ( 0 , 200 ) } ...` : output
159- console . log ( `${ ANSI . dim } [RESULT] ${ truncated } ${ ANSI . reset } ` )
203+ // Only show tool results in verbose mode - they can be noisy
204+ if ( this . verbose ) {
205+ const truncated = output . length > 200 ? `${ output . slice ( 0 , 200 ) } ...` : output
206+ const firstLine = truncated . split ( "\n" ) [ 0 ] || truncated
207+ console . log ( `${ ANSI . gray } ${ SYMBOLS . result } ${ firstLine } ${ ANSI . reset } ` )
208+ }
160209 this . writeToBuffer ( this . formatForFile ( `[RESULT] ${ output } ` ) )
161210 }
162211
163212 /**
164- * Log thinking/reasoning (only in verbose mode to console)
213+ * Log thinking/reasoning - shown by default for visibility
165214 */
166215 thinking ( text : string ) : void {
167- if ( this . verbose ) {
168- console . log ( `${ ANSI . dim } [THINKING] ${ text } ${ ANSI . reset } ` )
169- }
216+ this . stopSpinner ( )
217+ // Show thinking in a visually distinct way
218+ const lines = text . split ( "\n" )
219+ const firstLine = lines [ 0 ] || text
220+ const display = firstLine . length > 100 ? `${ firstLine . slice ( 0 , 100 ) } ...` : firstLine
221+ console . log ( `${ ANSI . magenta } ${ SYMBOLS . thinking } ${ ANSI . italic } ${ display } ${ ANSI . reset } ` )
170222 this . writeToBuffer ( this . formatForFile ( `[THINKING] ${ text } ` ) )
171223 }
172224
@@ -181,6 +233,60 @@ export class Logger {
181233 this . writeToBuffer ( this . formatForFile ( msg ) )
182234 }
183235
236+ /**
237+ * Start a spinner with a message (for long-running operations)
238+ */
239+ startSpinner ( message : string ) : void {
240+ if ( this . isSpinning ) {
241+ this . stopSpinner ( )
242+ }
243+
244+ this . isSpinning = true
245+ this . currentSpinnerMessage = message
246+ this . spinnerIndex = 0
247+
248+ this . spinnerInterval = setInterval ( ( ) => {
249+ const frame = SYMBOLS . spinner [ this . spinnerIndex % SYMBOLS . spinner . length ]
250+ process . stdout . write (
251+ `${ ANSI . clearLine } ${ ANSI . cyan } ${ frame } ${ ANSI . reset } ${ this . currentSpinnerMessage } ` ,
252+ )
253+ this . spinnerIndex ++
254+ } , 80 )
255+ }
256+
257+ /**
258+ * Update spinner message without stopping it
259+ */
260+ updateSpinner ( message : string ) : void {
261+ if ( this . isSpinning ) {
262+ this . currentSpinnerMessage = message
263+ }
264+ }
265+
266+ /**
267+ * Stop the spinner and clear the line
268+ */
269+ stopSpinner ( ) : void {
270+ if ( this . spinnerInterval ) {
271+ clearInterval ( this . spinnerInterval )
272+ this . spinnerInterval = undefined
273+ }
274+ if ( this . isSpinning ) {
275+ process . stdout . write ( ANSI . clearLine )
276+ this . isSpinning = false
277+ }
278+ }
279+
280+ /**
281+ * Log activity indicator (brief visual feedback)
282+ */
283+ activity ( action : string , detail ?: string ) : void {
284+ this . stopSpinner ( )
285+ const detailStr = detail ? ` ${ ANSI . dim } ${ detail } ${ ANSI . reset } ` : ""
286+ console . log ( `${ ANSI . blue } ${ SYMBOLS . arrow } ${ action } ${ detailStr } ${ ANSI . reset } ` )
287+ this . writeToBuffer ( this . formatForFile ( `[ACTIVITY] ${ action } ${ detail || "" } ` ) )
288+ }
289+
184290 /**
185291 * Format message for file output with timestamp
186292 */
0 commit comments