CHANGELOG.md (view raw)
1# Changelog
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a
6Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
7[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
9## [0.14.0] - 2026-04-27
10
11### Added
12
13- **Signal handling for graceful shutdown**: `llm_aggregator` now handles
14 `SIGINT`, `SIGTERM`, and `SIGHUP`. When a signal arrives during execution:
15 - The context is cancelled, stopping in-progress operations as soon as possible
16 - Any partial LLM summary already received is written to stdout (or the output file)
17 - The process exits with code 130 (128 + signal number) instead of a generic error
18 - In TUI mode, `q`/`Ctrl+C` also triggers clean shutdown via `RequestExit()`
19- **`Runtime.Interrupted` and `Runtime.WasInterrupted()`**: New state field and
20 query method allow callers to detect signal-caused termination.
21
22## [0.13.0] - 2026-04-26
23
24### Added
25
26- **`--stdin` flag**: Read raw RSS/Atom XML directly from standard input,
27 composable with `-f/--feeds-file` to collate articles from both sources into
28 a single LLM summary. Stdin articles appear before feeds-file articles in the
29 collated output.
30 - `curl -s $URL | llm_aggregator --stdin -p "Summarise"`
31 - `llm_aggregator -f feeds.txt --stdin -p "Summarise all"`
32 - Environment variable: `LLM_AGGREGATOR_STDIN`
33- **`--feeds-file` is no longer required**: Either `--feeds-file` or `--stdin`
34 (or both) must be provided. A clear error is returned if neither is specified.
35
36### Changed
37
38- Refactored `ParseFeedsFromFile` to delegate to a new `ParseFeedFromReader`
39 method that accepts any `io.Reader`, enabling both stdin and unit test usage
40 with `strings.Reader`.
41
42## [0.12.0] - 2026-04-26
43
44### Added
45
46- **`-P/--plain` flag**: Output the raw LLM summary without any formatting or
47 metadata. Useful when the summary is to be consumed by downstream scripts.
48
49## [0.11.0] - 2026-04-23
50
51- Progress bar now tracks estimated token count (via tiktoken) before sending
52 to LLM, then updates with actual completion tokens from the API response.
53 Gives a more accurate picture of how much content is being sent.
54
55## [0.10.0] - 2026-04-23
56
57### Added
58
59- **Short command-line flags**: Single-character shortcuts for the most
60 commonly used options:
61 - `-f` / `--feeds-file` — feeds file path
62 - `-p` / `--prompt` — summarisation prompt
63 - `-n` / `--max-articles-per-feed` — articles per feed limit
64 - `-d` / `--max-days-old` — article age filter
65 - `-i` / `--include-keywords` — keyword include filter
66 - `-e` / `--exclude-keywords` — keyword exclude filter
67 - `-m` / `--model` — LLM model selection
68 - `-o` / `--output` — output format
69 - `-t` / `--tui` — enable TUI
70 - `-D` / `--dry-run` — dry run mode
71- **Styled help output**: Custom lipgloss-styled help text at
72 `internal/cli/help.go` replaces go-arg's default plain-text output. Flags are
73 rendered in cyan, section headers in bold, with consistent column alignment.
74 Respects `$NO_COLOR`.
75
76## [0.9.0] - 2026-04-23
77
78### Added
79
80- **Markdown rendering in TUI**: Implemented `charmbracelet/glamour` to
81 render LLM summary output with proper markdown styling. Headers, bold,
82 italic, code blocks, lists, and other markdown elements are now styled
83 for better readability in the terminal.
84- **Formalised infobar keybinds**: TUI keybinds are now defined as
85 dedicated style variables at the top of the model for easier maintenance.
86 Keybinds are bold while separators and action text are subtle but
87 distinguishable.
88- **Thinking indicator as separate element**: The `[💭 Thinking: ON/OFF]`
89 toggle indicator is now on its own line above the keybinds, using a
90 distinct style (magenta+bold when ON, subtle+italic when OFF).
91- **Dynamic text wrapping on resize**: TUI summary text now re-wraps when
92 the terminal window size or font changes, with proper markdown re-rendering.
93
94## [0.8.0] - 2026-04-23
95
96### Added
97
98- **Thinking tags toggle in TUI**: LLMs that output `<think>`/`</think>`
99 thinking tags now have those sections automatically removed from the summary
100 display by default. Users can press `t` to toggle the thinking section back
101 on, displayed in gray at the head of the output with XML tags stripped for
102 cleaner presentation. A status indicator shows whether thinking is currently
103 visible.
104
105## [0.7.1] - 2026-04-22
106
107### Fixed
108
109- Environment variables were correctly overriding config file values, but CLI
110 arguments without explicit flags were also overriding config file values
111 unintentionally.
112 - Root cause: `go-arg` was populating struct fields with default values
113 from `default:` tags even when those CLI flags weren't provided
114 - Fix: Removed `default:` tags from Args struct fields for LLM options
115 (`model`, `base-url`, `api-key`, `max-tokens`, `temperature`);
116 Viper's own default handling is sufficient
117 - Fix: Changed Args struct fields from value types to pointer types
118 (`*string`, `*int`, `*float64`) so that "not provided on CLI" can be
119 distinguished from "provided but zero/empty"
120 - Fix: Updated `BindCLIArgs()` and `isZero()` to properly handle pointer
121 types and distinguish nil pointers from zero values
122- `isZero()` with compound type switch cases: Compound type cases like `case
123 *int, *int8, *int16, *int32, *int64` don't properly handle nil comparison when
124 stored in an interface variable. Fixed by splitting into individual type cases.
125- `isZero()` missing nil interface handling: When a nil pointer is stored
126 in an `any` interface, `value != nil` returns `true` (the interface itself
127 is not nil). Added early `if v == nil { return true }` check.
128
129## [0.7.0] - 2026-04-22
130
131### Added
132
133- **`--dry-run` option**: Validate configuration and preview what would happen
134 without making any LLM API calls. Useful for validating feeds file and
135 configuration, checking article counts and filtering results and anything
136 else that can be done "offline".
137- **Styled terminal output using `lipgloss`**: All CLI output now uses ANSI
138 colour codes for better readability:
139- **`$NO_COLOR` environment variable support**: Respects the ["no
140color"](https://no-color.org/) standard. When `NO_COLOR` is set, all styling is
141disabled and plain text is output instead.
142
143### Changed
144
145- API key validation now only occurs when not using `--dry-run` mode
146
147## [0.6.0] - 2026-04-22
148
149### Added
150
151- **Comprehensive unit test suite**, including CLI argument tests
152 (`--feeds-file`, `--prompt`, `--api-key`, `--model`, `--base-url`),
153 configuration tests, aggregator tests, output formatter tests, processor
154 tests and defaults tests.
155- **`--base-url` CLI option**: configure custom API endpoints for different LLM
156 providers. Should support any OpenAI-compatible provider.
157 - Environment variable: `LLM_AGGREGATOR_BASE_URL`
158- `internal/defaults` package
159- `docs/TESTING.md` which contains a comprehensive testing guide with test
160 descriptions
161
162### Changed
163
164- **Configuration defaults**: All packages now use `defaults.Default*` constants
165 - `internal/config`: Uses `defaults.Default*` for default values
166 - `internal/runtime`: Uses `defaults.Default*` for runtime defaults
167 - `internal/llm`: Uses `defaults.Default*` for LLM client defaults
168
169### Fixed
170
171- `nil` pointer safety in aggregator
172 - Added nil checks for `progressCtx` across all log statements
173 - `FeedAggregator` now works correctly when progress context is nil
174 - Tests use `NewFeedAggregatorWithProgress` with explicit progress context
175
176## [0.5.0] - 2026-04-22
177
178### Added
179
180- **Accurate token counting with tiktoken**
181 - New `internal/tokeniser` package using OpenAI's tiktoken-go library
182 - BPE tokenisation for accurate token counting vs rough character estimation
183 - Encoding caching to avoid repeated initialisation overhead
184 - Model-aware encoding selection (cl100k_base, o200k_base, etc.)
185 - `CountTokens()` and `CountMessagesTokens()` functions
186 - Fallback to rough estimation when tiktoken initialisation fails
187- **Concurrent feed fetching**: Feeds are now fetched concurrently using
188 `golang.org/x/sync/errgroup`
189 - Semaphore limits concurrent requests to 10 to avoid server overload
190 - Mutex-protected shared state for thread-safe article collection
191 - Partial failures don't stop other feeds from being processed
192- **Scrollable summary in TUI**
193 - Bubble Tea viewport component for browsing long summaries
194 - Keyboard navigation: j/k or arrows (scroll), Space/B (page), g/G
195 (start/end)
196 - Mouse wheel scrolling support
197 - Scroll progress indicator showing position and total lines
198
199### Changed
200
201- **Streamlined configuration management**: Viper now uses global instance with
202automatic precedence handling. Simplified configuration flow: Parse → GetViper
203→ BindCLIArgs → ViperToRuntime
204- **TUI colour scheme**: All hex colour codes replaced with ANSI 256-colour
205 palette
206
207### Fixed
208
209- Viewport now properly handles scroll events via `Update()`. Summary content
210 pre-wrapped to viewport width before display
211- Fixed string literal colour names to use actual variables
212
213## [0.4.0] - 2026-04-21
214
215### Changed
216
217- **TUI is now fully functional and production-ready**: Replaced the
218work-in-progress TUI implementation with a robust Bubble Tea command pattern:
219 - Runtime execution now uses native `tea.Cmd` instead of manual goroutine
220 orchestration.
221 - Added real-time progress updates via `progress.Progress` interface with
222 stage, substage, and article count messages.
223 - TUI displays a spinner, elapsed time, and final summary inline.
224 - Removed signal handling and channel synchronisation in favour of Bubble
225 Tea's built-in lifecycle.
226 - `TUIProgress` now only sends messages to an existing program, simplifying
227 integration.
228 - Both verbose mode (`SimpleLogger`) and TUI mode share the same
229 `progress.Progress` interface.
230
231## [0.3.0] - 2026-04-21
232
233### Added
234
235- **Configuration file support via TOML** (XDG-compliant and cross-platform)
236 - Load settings from `~/.config/llm_aggregator/config.toml`
237 - Supports all aggregation, API, and output options
238 - Example configuration file at `configs/config.example.toml`
239- **Environment variable support with `LLM_AGGREGATOR_` prefix**
240 - All configuration options can be set via environment variables
241 - Precedence order: CLI arguments > environment variables > config file >
242 built‑in defaults
243- New `internal/config` package with Viper integration
244 - `Load()` and `Save()` methods for configuration management
245 - `GetConfigPath()` for XDG‑compliant config file location
246 - `ConfigExists()` to check for existing configuration
247- Comprehensive unit tests for configuration loading, saving, and environment
248 variable precedence (`internal/config/config_test.go`)
249
250### Changed
251
252- **Environment variable renamed:** `DEEPSEEK_API_KEY` →
253 `LLM_AGGREGATOR_API_KEY`
254- **CLI help text** now reflects the new API key environment variable and
255 general configuration options
256- **Command‑line argument precedence** implemented in `cmd/llm_aggregator.go`
257via `applyConfiguration()`
258- Updated all help text, documentation, and code references
259- `README.md` completely revised with a new "Configuration" section
260
261### Removed
262
263- Global `QuietMode` and `VerboseMode` variables from
264`internal/config/config.go`. Replaced by runtime‑specific configuration in
265`runtime.Runtime` and CLI arguments
266- Hardcoded system prompt in `runtime.NewRuntime()`. Now left empty, allowing
267configuration or client default to be used
268- CLI flags now correctly override config file and environment variables
269
270## [0.2.0] - 2026-04-21
271
272### Changed
273
274- Moved all progress and status messages behind `-v/--verbose` flag
275- Default CLI mode is now silent except for final output and errors
276- Components (aggregator, processor, LLM client) use logger interface
277controlled by verbose flag
278
279## [0.1.0] - 2026-04-21
280
281### Added
282
283- Complete translation from Python prototype to Go implementation
284- RSS/Atom feed parsing using `gofeed` library
285- DeepSeek API integration via `openai-go` client, configured for `/chat/completions` endpoint
286- Content filtering and processing with keyword‑based include/exclude, date filtering, and sorting
287- Command‑line interface using `go‑arg` with automatic help and version flags
288- Multiple output formats: plain text, GitHub‑flavoured markdown, and JSON
289- Terminal user interface (TUI) built with `bubbletea` featuring:
290 - Animated progress bar with gradient colours (`#FF7CCB` to `#FDFF8C`)
291 - Live article counters (aggregated/processed)
292 - Elapsed time display
293 - Coloured status indicators using `lipgloss` styling
294 - Keyboard controls (q/Ctrl+C to quit)
295- Web content extraction fallback using `goquery` when feed descriptions are minimal
296- Configurable limits: articles per feed, maximum age, total articles
297- Token estimation and API usage logging
298- Environment variable support (`LLM_AGGREGATOR_API_KEY`) for authentication
299- Example feeds file with technology, programming, and free software sources
300
301### Changed
302
303- Program structure organised into standard Go layout: `cmd/`, `internal/`, `pkg/`
304- English spelling conventions maintained throughout (colour, initialise, summarise)
305- Error handling improved with specific messages for common API failures (invalid key, rate limits, etc.)
306- Progress reporting unified through a `ProgressContext` interface for both TUI and CLI modes
307
308### Fixed
309
310- Initial API endpoint mismatch (using `/responses` instead of `/chat/completions`)
311- Nil pointer dereferences when handling optional feed metadata (author, dates)
312- String repetition syntax errors in Go (replaced `"="*80` with `strings.Repeat`)
313- CLI help/version flag handling to show information without requiring other arguments
314- Type compatibility issues with `openai-go` v3 API (message parameters, token usage fields)