auto-update-daily-20260202
  1package output
  2
  3import (
  4	"fmt"
  5	"io"
  6	"sort"
  7	"strings"
  8
  9	"github.com/vdemeester/home/tools/review-tool/internal/activity"
 10)
 11
 12// WriteYAML writes the report in YAML format optimized for LLM consumption.
 13// YAML format provides better accuracy for LLM understanding compared to JSON or XML.
 14func WriteYAML(w io.Writer, report *activity.ReviewReport) error {
 15	fmt.Fprintf(w, "# Activity Review: %s\n", report.TimeRange.Description)
 16	fmt.Fprintf(w, "period:\n")
 17	fmt.Fprintf(w, "  start: %s\n", report.TimeRange.Start.Format("2006-01-02"))
 18	fmt.Fprintf(w, "  end: %s\n", report.TimeRange.End.Format("2006-01-02"))
 19	fmt.Fprintf(w, "generated: %s\n", report.GeneratedAt.Format("2006-01-02 15:04"))
 20	fmt.Fprintln(w)
 21
 22	// Summary
 23	if report.Summary != nil && report.Summary.TotalItems > 0 {
 24		fmt.Fprintln(w, "summary:")
 25		fmt.Fprintf(w, "  total_activities: %d\n", report.Summary.TotalItems)
 26
 27		cats := make([]string, 0, len(report.Summary.ItemsByCategory))
 28		for cat := range report.Summary.ItemsByCategory {
 29			cats = append(cats, cat)
 30		}
 31		sort.Strings(cats)
 32
 33		fmt.Fprintln(w, "  by_category:")
 34		for _, cat := range cats {
 35			fmt.Fprintf(w, "    %s: %d\n", cat, report.Summary.ItemsByCategory[cat])
 36		}
 37		fmt.Fprintln(w)
 38	}
 39
 40	// Activities by source
 41	fmt.Fprintln(w, "activities:")
 42	sourceOrder := []string{"github", "org", "jira", "claude"}
 43	for _, source := range sourceOrder {
 44		act, ok := report.Activities[source]
 45		if !ok || len(act.Items) == 0 {
 46			continue
 47		}
 48
 49		fmt.Fprintf(w, "  %s:\n", source)
 50
 51		if act.Error != "" {
 52			fmt.Fprintf(w, "    error: %q\n", act.Error)
 53			continue
 54		}
 55
 56		// Group by type
 57		byType := groupByType(act.Items)
 58		types := make([]string, 0, len(byType))
 59		for t := range byType {
 60			types = append(types, t)
 61		}
 62		sort.Strings(types)
 63
 64		for _, typ := range types {
 65			items := byType[typ]
 66			fmt.Fprintf(w, "    %s:\n", typ)
 67
 68			// Sort by timestamp descending
 69			sort.Slice(items, func(i, j int) bool {
 70				return items[i].Timestamp.After(items[j].Timestamp)
 71			})
 72
 73			for _, item := range items {
 74				writeYAMLItem(w, item)
 75			}
 76		}
 77	}
 78
 79	return nil
 80}
 81
 82func writeYAMLItem(w io.Writer, item activity.ActivityItem) {
 83	// Escape title for YAML if needed
 84	title := item.Title
 85	if strings.ContainsAny(title, ":\n\"'") {
 86		title = fmt.Sprintf("%q", title)
 87	}
 88
 89	fmt.Fprintf(w, "      - title: %s\n", title)
 90	fmt.Fprintf(w, "        timestamp: %s\n", item.Timestamp.Format("2006-01-02 15:04"))
 91
 92	if item.URL != "" {
 93		fmt.Fprintf(w, "        url: %s\n", item.URL)
 94	}
 95
 96	// Include relevant metadata
 97	if section := item.Metadata["section"]; section != "" {
 98		fmt.Fprintf(w, "        section: %s\n", section)
 99	}
100	if file := item.Metadata["file"]; file != "" {
101		// Shorten archive paths
102		if strings.Contains(file, "/archive/") {
103			parts := strings.Split(file, "/archive/")
104			if len(parts) > 1 {
105				fmt.Fprintf(w, "        source: archive/%s\n", parts[1])
106			}
107		}
108	}
109	if duration := item.Metadata["duration"]; duration != "" {
110		fmt.Fprintf(w, "        duration: %s\n", duration)
111	}
112	if repo := item.Metadata["repo"]; repo != "" {
113		fmt.Fprintf(w, "        repo: %s\n", repo)
114	}
115
116	if item.Description != "" {
117		desc := item.Description
118		if strings.ContainsAny(desc, ":\n\"'") {
119			desc = fmt.Sprintf("%q", desc)
120		}
121		fmt.Fprintf(w, "        description: %s\n", desc)
122	}
123}