Files
terdut-tui/CLAUDE.md
T
Niklas Ye e04cfcf433
Release / build (amd64, darwin) (push) Failing after 10s
Release / build (amd64, linux) (push) Failing after 9s
Release / build (arm64, darwin) (push) Failing after 11s
Release / build (arm64, linux) (push) Failing after 9s
Release / release (push) Has been skipped
feat: Last Seen column tracking Alertmanager re-send heartbeat
The alert list showed only Started, which comes from Prometheus and
never changes for the lifetime of an alert instance. A firing alert
that started 12 days ago looked identical whether Alertmanager
refreshed it 30 seconds ago or went silent a week ago.

terdut-server already tracks this: the webhook upsert sets
received_at on every accepted payload, including the periodic
re-sends issued at repeat_interval, and its archiver treats the
field as a liveness heartbeat. The field was already decoded into
api.Alert.ReceivedAt and simply never rendered.

Add a Last Seen column to the alert tables, rendered with the
existing humanAgo helper. The Alerts and Archived tabs share
alertColumns/alertRows, so both pick it up. The width budget is
re-derived for five columns; the slack constant now accounts for
all of bubbles' per-cell padding, so the table lands exactly on
the terminal width instead of overflowing by two columns as it
did with four.

The detail view gains a matching Last Seen line, with the timeline
labels widened to keep values aligned. Since received_at stops
advancing once an alert resolves, also pull through the server's
resolution_source and show it in the status header
(RESOLVED · alertmanager vs RESOLVED · expiry) so a frozen
timestamp is explained.
2026-07-30 08:48:53 +02:00

3.1 KiB

terdut-tui

TUI client for terdut-server, a Prometheus Alertmanager receiver and on-call scheduler.

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/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                  All lipgloss styles
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.

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

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 .

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

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.