all repos — llm_aggregator @ 7a6aa2c8274fa914984b3ebfbf1e5f55189451c2

A CLI tool to aggregate RSS feeds and summarise them with LLMs

cmd/llm_aggregator.go (view raw)

  1package main
  2
  3import (
  4	"context"
  5	"fmt"
  6	"os"
  7
  8	tea "github.com/charmbracelet/bubbletea"
  9	"llm_aggregator/internal/cli"
 10	"llm_aggregator/internal/config"
 11	"llm_aggregator/internal/progress"
 12	"llm_aggregator/internal/runtime"
 13	"llm_aggregator/internal/tui"
 14)
 15
 16var (
 17	version   string
 18	buildDate string
 19)
 20
 21func main() {
 22	cli.BuildDate = buildDate
 23	cli.Version = version
 24
 25	// Parse command line arguments
 26	args, err := cli.ParseArgs()
 27	if err != nil {
 28		fmt.Fprintf(os.Stderr, "Error parsing arguments: %v\n", err)
 29		os.Exit(1)
 30	}
 31
 32	// Get viper instance with config file + environment variables + defaults
 33	v := config.GetViper()
 34
 35	// Bind CLI args to viper (highest precedence)
 36	config.BindCLIArgs(v, args.ToViperMap())
 37
 38	// Validate API key
 39	if v.GetString("api_key") == "" {
 40		fmt.Fprintln(os.Stderr, "Error: OpenAI-compatible API key is required. Set via --api-key, LLM_AGGREGATOR_API_KEY environment variable, or config file.")
 41		os.Exit(1)
 42	}
 43
 44	// Create runtime directly from viper configuration
 45	rt := config.ViperToRuntime(v, args.FeedsFile, args.Prompt)
 46
 47	// Run with TUI if requested
 48	if args.TUI {
 49		runWithTUI(rt)
 50	} else {
 51		runWithoutTUI(rt, args.Verbose)
 52	}
 53}
 54
 55func runWithTUI(rt *runtime.Runtime) {
 56	// 1. Create the model and pass the runtime to it.
 57	model := tui.New(rt)
 58
 59	// 2. Create the tea.Program.
 60	p := tea.NewProgram(model, tea.WithAltScreen())
 61
 62	// 3. Create the TUIProgress handler and give it the program instance.
 63	//    This is the bridge that allows the runtime to send messages to the TUI.
 64	tp := tui.NewTUIProgress(p)
 65
 66	// 4. Inject the progress handler into the runtime.
 67	rt.Progress = tp
 68
 69	// 5. Run the program. This is a blocking call.
 70	if _, err := p.Run(); err != nil {
 71		fmt.Fprintf(os.Stderr, "TUI Error: %v\n", err)
 72		os.Exit(1)
 73	}
 74}
 75
 76func runWithoutTUI(rt *runtime.Runtime, verbose bool) {
 77	// Setup logger based on verbose flag and inject it into the runtime
 78	if verbose {
 79		rt.Progress = progress.NewSimpleLogger(os.Stdout, true)
 80	} else {
 81		// Default is already NoopLogger, but we can be explicit
 82		rt.Progress = &progress.NoopLogger{}
 83	}
 84
 85	// Execute the runtime
 86	err := rt.Execute(context.Background())
 87	if err != nil {
 88		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
 89		os.Exit(1)
 90	}
 91
 92	// Write output
 93	if rt.OutputFile != "" {
 94		if err := rt.WriteOutputToFile(); err != nil {
 95			fmt.Fprintf(os.Stderr, "Error writing output: %v\n", err)
 96			os.Exit(1)
 97		}
 98	} else {
 99		if err := rt.WriteOutput(os.Stdout); err != nil {
100			fmt.Fprintf(os.Stderr, "Error writing output: %v\n", err)
101			os.Exit(1)
102		}
103	}
104}