@@ -308,42 +308,57 @@ func getContentWithoutSummary(content, summary string) string {
308308// RenderAskResult displays a complete AskResult from the ask pipeline.
309309// This is the primary rendering function for the `taskwing ask` command.
310310func RenderAskResult (result * app.AskResult , verbose bool ) {
311- titleStyle := lipgloss .NewStyle ().Foreground (lipgloss .Color ("205" )).Bold (true )
312311 sectionStyle := lipgloss .NewStyle ().Foreground (lipgloss .Color ("141" )).Bold (true )
313- metaStyle := lipgloss .NewStyle ().Foreground (lipgloss .Color ("241" ))
314312
315- // Title
313+ // Header with query in a styled box
314+ headerBox := lipgloss .NewStyle ().
315+ Bold (true ).
316+ Foreground (ColorPrimary ).
317+ Padding (0 , 1 ).
318+ Border (lipgloss .RoundedBorder ()).
319+ BorderForeground (ColorSecondary )
320+
321+ fmt .Println ()
316322 if result .Answer != "" {
317- fmt .Println ()
318- fmt .Println (titleStyle .Render (fmt .Sprintf ("📖 %s" , result .Query )))
323+ fmt .Println (headerBox .Render (fmt .Sprintf ("Q: %s" , result .Query )))
319324 } else {
320- fmt .Println (titleStyle .Render (fmt .Sprintf ("🔍 Results for: \" %s \" " , result .Query )))
325+ fmt .Println (headerBox .Render (fmt .Sprintf ("Search: %s " , result .Query )))
321326 }
322327
323- // Pipeline info
324- fmt .Println (metaStyle .Render (fmt .Sprintf (" Pipeline: %s" , result .Pipeline )))
328+ // Pipeline & rewrite as dim metadata
329+ var metaParts []string
330+ metaParts = append (metaParts , result .Pipeline )
325331 if result .RewrittenQuery != "" {
326- fmt .Println (metaStyle .Render (fmt .Sprintf (" Rewritten: %s" , result .RewrittenQuery )))
332+ metaParts = append (metaParts , fmt .Sprintf ("rewritten: %s" , result .RewrittenQuery ))
333+ }
334+ if result .Total > 0 || result .TotalSymbols > 0 {
335+ metaParts = append (metaParts , fmt .Sprintf ("%d knowledge, %d symbols" , result .Total , result .TotalSymbols ))
327336 }
337+ fmt .Println (StyleAskMeta .Render (" " + strings .Join (metaParts , " | " )))
328338
329339 // Warning
330340 if result .Warning != "" {
331341 fmt .Println ()
332342 fmt .Println (RenderWarningPanel ("Warning" , result .Warning ))
333343 }
334344
335- // Answer (only render if not already streamed — streaming writes directly to stdout)
345+ // Answer box with accent border
336346 if result .Answer != "" {
337347 fmt .Println ()
338- fmt .Println (RenderInfoPanel ("Answer" , result .Answer ))
348+ answerBox := lipgloss .NewStyle ().
349+ Border (lipgloss .RoundedBorder ()).
350+ BorderForeground (ColorBlue ).
351+ Padding (1 , 2 ).
352+ Width (80 )
353+ fmt .Println (answerBox .Render (result .Answer ))
339354 }
340355
341- // Knowledge results
356+ // Sources as compact citations
342357 if len (result .Results ) > 0 {
343358 fmt .Println ()
344- fmt .Println (sectionStyle .Render ("📚 Knowledge" ))
359+ fmt .Println (sectionStyle .Render ("Sources" ))
360+ fmt .Println ()
345361
346- // Convert NodeResponse to ScoredNode for the existing panel renderer
347362 scored := nodeResponsesToScoredNodes (result .Results )
348363
349364 var maxScore float32 = 0.01
@@ -354,31 +369,78 @@ func RenderAskResult(result *app.AskResult, verbose bool) {
354369 }
355370
356371 for i , s := range scored {
357- renderScoredNodePanel (i + 1 , s , maxScore , verbose )
372+ renderCitation (i + 1 , s , maxScore )
358373 }
359374 }
360375
361376 // Code symbols
362377 if len (result .Symbols ) > 0 {
363378 fmt .Println ()
364- fmt .Println (sectionStyle .Render ("💻 Code Symbols" ))
379+ fmt .Println (sectionStyle .Render ("Code Symbols" ))
380+ fmt .Println ()
365381
366382 for i , sym := range result .Symbols {
367- renderSymbolPanel (i + 1 , sym , verbose )
383+ renderSymbolCitation (i + 1 , sym )
368384 }
369385 }
370386
371387 // No results
372388 if len (result .Results ) == 0 && len (result .Symbols ) == 0 && result .Answer == "" {
373389 fmt .Println ()
374- fmt .Println (metaStyle .Render (" No results found. Try a different query or run 'taskwing bootstrap' to populate memory." ))
390+ fmt .Println (StyleAskMeta .Render (" No results found. Try a different query or run 'taskwing bootstrap' to populate memory." ))
375391 }
376392
377- // Summary line
378- if result .Total > 0 || result .TotalSymbols > 0 {
379- fmt .Println ()
380- fmt .Println (metaStyle .Render (fmt .Sprintf (" %d knowledge result(s), %d symbol(s)" , result .Total , result .TotalSymbols )))
393+ fmt .Println ()
394+ }
395+
396+ // renderCitation renders a knowledge source as a compact citation line.
397+ func renderCitation (index int , s knowledge.ScoredNode , maxScore float32 ) {
398+ summary := s .Node .Summary
399+ if summary == "" {
400+ runes := []rune (s .Node .Text ())
401+ if len (runes ) > 60 {
402+ summary = string (runes [:60 ]) + "..."
403+ } else {
404+ summary = string (runes )
405+ }
406+ }
407+
408+ badge := CategoryBadge (s .Node .Type )
409+ scoreBar := renderMiniBar (s .Score , maxScore )
410+
411+ id := s .Node .ID
412+ if len (id ) > 8 {
413+ id = id [:8 ]
414+ }
415+
416+ fmt .Printf (" %s %s %s %s\n " ,
417+ StyleCitationBadge .Render (fmt .Sprintf ("[%d]" , index )),
418+ badge ,
419+ lipgloss .NewStyle ().Foreground (ColorText ).Render (summary ),
420+ StyleCitationPath .Render (fmt .Sprintf ("(%s %s)" , id , scoreBar )),
421+ )
422+ }
423+
424+ // renderSymbolCitation renders a code symbol as a compact citation line.
425+ func renderSymbolCitation (index int , sym app.SymbolResponse ) {
426+ icon := symbolKindIcon (sym .Kind )
427+ fmt .Printf (" %s %s %s %s\n " ,
428+ StyleCitationBadge .Render (fmt .Sprintf ("[%d]" , index )),
429+ icon ,
430+ lipgloss .NewStyle ().Foreground (ColorText ).Bold (true ).Render (sym .Name ),
431+ StyleCitationPath .Render (sym .Location ),
432+ )
433+ }
434+
435+ // renderMiniBar renders a compact score indicator.
436+ func renderMiniBar (score , maxScore float32 ) string {
437+ rel := score / maxScore
438+ filled := int (rel * 5 )
439+ if filled < 1 && score > 0 {
440+ filled = 1
381441 }
442+ bar := strings .Repeat ("█" , filled ) + strings .Repeat ("░" , 5 - filled )
443+ return bar
382444}
383445
384446// nodeResponsesToScoredNodes converts NodeResponse slice to ScoredNode slice
0 commit comments