Files
terdut-tui/CLAUDE.md
T
Niklas Ye 4a579bdbc6
CI / test (pull_request) Successful in 4s
Colour themes, defaulting to gruvbox dark
Every colour was a 256-colour ANSI index hardcoded in styles.go, so changing
the palette meant editing the styles themselves. This puts a semantic token set
between the two: styles name roles, a theme supplies the colours.

internal/theme holds the twelve tokens, the two built-ins (gruvbox-dark, the
new default, and gruvbox-light) and the loader for user themes in
~/.config/terdut-tui/themes/. A user file may 'extends:' a built-in and
override only what it cares about, and may shadow a built-in name to tweak it
in place. Unknown keys, malformed colours and incomplete themes are refused
with a message naming what went wrong.

Colours are truecolor hex now: lipgloss downsamples for 256- and 16-colour
terminals and honours NO_COLOR, so themes carry no fallbacks of their own.
An ANSI index is still accepted for anyone who would rather follow their
terminal's own palette.

The 21 package-level style vars become a Styles struct on the Model, which is
what rule 3 asked for all along; the four free functions in view.go take one as
their first argument. The embedded bubbles components are restyled from the
same tokens — otherwise a theme would leave a pink selected row and grey help
text behind. Note that the table's Cell style deliberately keeps no foreground:
bubbles renders cells before wrapping the row in Selected, so a colour there
cuts the selection highlight short.
2026-08-20 11:06:36 +02:00

4.5 KiB

terdut-tui

TUI client for terdut-server, a Prometheus Alertmanager receiver and incident manager. Requires server v0.4.0+.

Domain model

The server splits alerts from incidents, and this client mirrors it:

  • Alert — Alertmanager's record. Firing or resolved, read-only, no workflow state.
  • Incident — the work item: triggered → acknowledged → resolved, with an assignee, snooze, notes and an append-only timeline. Many alerts to one incident, correlated by Alertmanager's groupKey.

All user actions target incidents. Two server behaviours the UI has to respect: manual resolve is terminal (hence the confirmation prompt), and snooze is the non-destructive "not now" alternative.

Tech stack

  • Go 1.25+
  • Bubbletea — TUI framework (strict Elm architecture)
  • Lipgloss — styles (all in internal/tui/styles.go, never inline)
  • Bubbles — table, textinput, help components

Project layout

main.go                      CLI entry point: flags, config load, health check, start TUI
internal/api/client.go       REST API client — one method per endpoint
internal/config/config.go    Config loader (~/.config/terdut-tui/config.yaml)
internal/theme/              Colour themes: semantic tokens, built-ins, user file loader
internal/tui/                Bubbletea UI
  model.go                   Model struct, mode/section constants, Init(), tea.Cmd constructors
  update.go                  Update() — dispatch only, no API calls inline
  view.go                    View() — pure rendering
  keys.go                    keyMap (bubbles/key pattern)
  styles.go                  Styles struct — every lipgloss style, built from a theme
internal/updater/updater.go  Self-update via GitHub Releases

Architecture rules

  1. Never call API inside Update() — return tea.Cmd instead; the runtime runs it async.
  2. View() is pure — no side effects, no state mutations.
  3. All state in Model — no globals.
  4. All styles in styles.go — never use lipgloss inline in view.go. Styles live on Model.styles, built once by newStyles(theme.Theme); the handful of free functions in view.go take a Styles as their first argument. No colour literal appears outside internal/theme.

Config

Location: ~/.config/terdut-tui/config.yaml

server_url: https://terdut.example.com
api_key: <64-char hex key>
refresh_interval: 30  # seconds, optional, default 30
theme: gruvbox-dark   # optional, default gruvbox-dark

Built-in themes are gruvbox-dark and gruvbox-light; user themes are YAML files in ~/.config/terdut-tui/themes/, optionally extends:-ing a built-in. See the README for the token list.

The API key is a one-time secret generated by terdut-server (POST /api/users/{id}/api-keys).

Running

go run .
go run . --version
go run . --self-update

Building

go build -ldflags="-X main.version=v0.1.0" -o terdut-tui .

Sections

Incidents (the queue, and the default) · Alerts (raw read-only feed) · Stats (MTTA/MTTR and alert frequency charts) · Archived (archived incidents) · Schedule · Users

Development stages

Stage Feature
1 Scaffold, config, health check, placeholder TUI
2 Alert dashboard with auto-refresh and stats
3 Alert detail: acknowledge, comment, statistics charts
4 On-call schedule calendar view
5 User management and API key lifecycle
6 Incidents: queue, timeline, ack/assign/snooze/resolve, MTTA/MTTR

Memory (GrayMatter)

This project has persistent agent memory via the graymatter MCP tools:

  • memory_search (agent_id, query) — call at the start of a task when prior context might matter.
  • memory_add (agent_id, text) — call whenever you learn something durable: user preferences, decisions, conventions, gotchas.
  • memory_reflect (action, agent, text/target) — update or forget stale facts. ⚠ takes agent, not agent_id.
  • checkpoint_save / checkpoint_resume (agent_id) — snapshot/restore session state before major refactors or across restarts.

Use a stable agent_id of the form <project>-<role> (e.g. myapp-backend). Store conclusions, not conversation logs. Err on the side of remembering.