all repos — llm_aggregator @ 7a6aa2c8274fa914984b3ebfbf1e5f55189451c2

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

internal/cli/args.go (view raw)

  1package cli
  2
  3import (
  4	"fmt"
  5	"os"
  6	"strings"
  7
  8	"github.com/alexflint/go-arg"
  9)
 10
 11var (
 12	Version   string
 13	BuildDate string
 14)
 15
 16// Args represents command-line arguments.
 17type Args struct {
 18	FeedsFile string `arg:"--feeds-file,required" help:"Path to file containing RSS feed URLs (one per line)"`
 19	Prompt    string `arg:"--prompt,required" help:"User prompt for summarisation/analysis"`
 20
 21	// Feed aggregation options
 22	MaxArticlesPerFeed int `arg:"--max-articles-per-feed" help:"Maximum articles to fetch from each feed" default:"10"`
 23	MaxDaysOld         int `arg:"--max-days-old" help:"Only include articles from the last N days (0 for all)" default:"7"`
 24	MaxTotalArticles   int `arg:"--max-total-articles" help:"Maximum total articles to process" default:"20"`
 25
 26	// Content filtering
 27	IncludeKeywords string `arg:"--include-keywords" help:"Comma-separated list of keywords to include (case-insensitive)"`
 28	ExcludeKeywords string `arg:"--exclude-keywords" help:"Comma-separated list of keywords to exclude (case-insensitive)"`
 29
 30	// LLM API options
 31	APIKey      string  `arg:"--api-key" help:"OpenAI-compatible API key (default: read from LLM_AGGREGATOR_API_KEY env var)"`
 32	BaseURL     string  `arg:"--base-url" help:"API base URL" default:"https://api.deepseek.com"`
 33	Model       string  `arg:"--model" help:"LLM model to use" default:"deepseek-chat"`
 34	MaxTokens   int     `arg:"--max-tokens" help:"Maximum tokens in response" default:"4000"`
 35	Temperature float64 `arg:"--temperature" help:"Sampling temperature (0.0 to 1.0)" default:"0.7"`
 36
 37	// Output options
 38	Output          string `arg:"--output" help:"Output format" default:"text" choice:"text,json,markdown"`
 39	OutputFile      string `arg:"--output-file" help:"Write output to file (default: stdout)"`
 40	IncludeArticles bool   `arg:"--include-articles" help:"Include original articles in JSON output"`
 41
 42	// System options
 43	SystemPrompt string `arg:"--system-prompt" help:"Custom system prompt for LLM"`
 44	TUI          bool   `arg:"--tui" help:"Enable TUI interface with progress bar"`
 45	Verbose      bool   `arg:"-v,--verbose" help:"Show verbose output"`
 46	ShowVersion  bool   `arg:"--version" help:"Show version"`
 47}
 48
 49// Version returns the version string.
 50func (Args) Version() string {
 51	return fmt.Sprintf("llm_aggregator v%s (built %s)", Version, BuildDate)
 52}
 53
 54// Description returns the program description.
 55func (Args) Description() string {
 56	return `LLM Aggregator - Aggregate RSS feeds and summarise with LLM API
 57
 58Examples:
 59  # Basic usage with prompts
 60  llm_aggregator --feeds-file feeds.txt --prompt "What are the latest trends in free software?"
 61  
 62  # With custom LLM model
 63  llm_aggregator --feeds-file feeds.txt --prompt "Summarise tech news" --model deepseek-coder
 64  
 65  # Output to JSON file
 66  llm_aggregator --feeds-file feeds.txt --prompt "Analyse AI developments" --output json --output-file analysis.json
 67  
 68  # Filter by keywords
 69  llm_aggregator --feeds-file feeds.txt --prompt "Linux news" --include-keywords linux,opensource
 70
 71Environment Variables:
 72  LLM_AGGREGATOR_API_KEY: Your LLM API key (required if not provided via --api-key)`
 73}
 74
 75// ParseKeywords parses comma-separated keywords string into list.
 76func ParseKeywords(keywordString string) []string {
 77	if keywordString == "" {
 78		return nil
 79	}
 80	keywords := strings.Split(keywordString, ",")
 81	result := make([]string, 0, len(keywords))
 82	for _, kw := range keywords {
 83		if trimmed := strings.TrimSpace(kw); trimmed != "" {
 84			result = append(result, trimmed)
 85		}
 86	}
 87	return result
 88}
 89
 90// ParseArgs parses command line arguments.
 91func ParseArgs() (*Args, error) {
 92	var args Args
 93	parser, err := arg.NewParser(arg.Config{
 94		Program: "llm_aggregator",
 95	}, &args)
 96	if err != nil {
 97		return nil, err
 98	}
 99
100	// Handle help and version flags before checking required fields
101	if len(os.Args) > 1 {
102		if os.Args[1] == "-h" || os.Args[1] == "--help" {
103			parser.WriteHelp(os.Stdout)
104			os.Exit(0)
105		}
106		if os.Args[1] == "--version" {
107			fmt.Printf("llm_aggregator v%s (built %s)", Version, BuildDate)
108			os.Exit(0)
109		}
110	}
111
112	err = parser.Parse(os.Args[1:])
113	if err != nil {
114		return nil, err
115	}
116
117	return &args, nil
118}
119
120// ToViperMap converts Args to a map for binding to Viper.
121// Only non-empty/non-zero values are included.
122func (a *Args) ToViperMap() map[string]any {
123	return map[string]any{
124		"max_articles_per_feed": a.MaxArticlesPerFeed,
125		"max_days_old":         a.MaxDaysOld,
126		"max_total_articles":   a.MaxTotalArticles,
127		"include_keywords":     a.IncludeKeywords,
128		"exclude_keywords":     a.ExcludeKeywords,
129		"api_key":              a.APIKey,
130		"base_url":             a.BaseURL,
131		"model":                a.Model,
132		"max_tokens":           a.MaxTokens,
133		"temperature":          a.Temperature,
134		"system_prompt":        a.SystemPrompt,
135		"output":               a.Output,
136		"output_file":          a.OutputFile,
137		"include_articles":     a.IncludeArticles,
138	}
139}