From 71e3831e64e8ad34bca3e03e389df8a974a7b857 Mon Sep 17 00:00:00 2001 From: Fraser Isbester Date: Thu, 30 Apr 2026 00:26:52 -0700 Subject: [PATCH 1/2] feat: update logo in header to a new style and adjust width --- README.md | 3 +++ internal/tui/app.go | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bd51746..5a94382 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +[![CI](https://github.com/Fraser-Isbester/tusk/actions/workflows/ci.yml/badge.svg)](https://github.com/Fraser-Isbester/tusk/actions/workflows/ci.yml) +[![Go Report Card](https://goreportcard.com/badge/github.com/fraser-isbester/tusk)](https://goreportcard.com/report/github.com/fraser-isbester/tusk) + # Tusk A k9s-style terminal UI for real-time PostgreSQL monitoring and management. Not a database client — a tool for observing and acting on live database activity. diff --git a/internal/tui/app.go b/internal/tui/app.go index 632d738..bf4616a 100644 --- a/internal/tui/app.go +++ b/internal/tui/app.go @@ -375,15 +375,16 @@ func (a *App) updateHeader() { y := "[#FFD700]" r := "[#FF5F5F]" - // TUSK in big open letters (figlet "big" style) - o := "[#D78700]" logo := []string{ - o + tview.Escape(" _______ _ _ _____ _ __") + "[-]", - o + tview.Escape(" |__ __|| | | | / ____|| |/ /") + "[-]", - o + tview.Escape(" | | | | | || (___ | ' / ") + "[-]", - o + tview.Escape(" | | | | | | \\___ \\ | < ") + "[-]", - o + tview.Escape(" | | | |__| | ____) || . \\ ") + "[-]", - o + tview.Escape(" |_| \\____/ |_____/ |_|\\_\\") + "[-]", + "", + "", + "", + c + ` _______ _ _ _______ _ _` + "[-]", + c + ` | | | |______ |____/ ` + "[-]", + c + ` | |_____| ______| | \_` + "[-]", + "", + "", + "", } uptimeStr := "--" @@ -436,7 +437,7 @@ func (a *App) updateHeader() { if headerWidth <= 0 { headerWidth = 120 } - logoWidth := 33 // visual width of the big letters + logoWidth := 34 // visual width of cyberlarge logo var lines []string for i := 0; i < len(infoLines) || i < len(logo); i++ { From b2eb93e13369cdc32d8e0ee83c1230ce818a93e3 Mon Sep 17 00:00:00 2001 From: Fraser Isbester Date: Thu, 30 Apr 2026 09:39:16 -0700 Subject: [PATCH 2/2] docs: add GitHub Pages site with Hugo, Hextra, and gomarkdoc API reference --- .github/workflows/docs.yml | 69 +++++++++++++++++++ .gitignore | 13 ++++ docs/content/_index.md | 28 ++++++++ docs/content/docs/_index.md | 8 +++ docs/content/docs/development.md | 59 ++++++++++++++++ docs/content/docs/getting-started.md | 98 +++++++++++++++++++++++++++ docs/content/docs/keybindings.md | 58 ++++++++++++++++ docs/content/docs/reference/_index.md | 6 ++ docs/content/docs/rules.md | 73 ++++++++++++++++++++ docs/content/docs/views.md | 45 ++++++++++++ docs/go.mod | 5 ++ docs/go.sum | 2 + docs/hugo.yaml | 29 ++++++++ internal/config/config.go | 1 + internal/db/pool.go | 1 + internal/rules/engine.go | 1 + internal/tui/app.go | 1 + internal/tui/theme/theme.go | 1 + internal/tui/views/queries.go | 1 + 19 files changed, 499 insertions(+) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/content/_index.md create mode 100644 docs/content/docs/_index.md create mode 100644 docs/content/docs/development.md create mode 100644 docs/content/docs/getting-started.md create mode 100644 docs/content/docs/keybindings.md create mode 100644 docs/content/docs/reference/_index.md create mode 100644 docs/content/docs/rules.md create mode 100644 docs/content/docs/views.md create mode 100644 docs/go.mod create mode 100644 docs/go.sum create mode 100644 docs/hugo.yaml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ba92d34 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,69 @@ +name: Docs + +on: push + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: go.mod + + - name: Install tools + run: | + go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@v1.1.0 + go install github.com/gohugoio/hugo@v0.161.1 + + - name: Generate API reference + run: | + mkdir -p docs/content/docs/reference + gomarkdoc --output docs/content/docs/reference/config.md ./internal/config/ + gomarkdoc --output docs/content/docs/reference/db.md ./internal/db/ + gomarkdoc --output docs/content/docs/reference/rules.md ./internal/rules/ + gomarkdoc --output docs/content/docs/reference/tui.md ./internal/tui/ + gomarkdoc --output docs/content/docs/reference/theme.md ./internal/tui/theme/ + gomarkdoc --output docs/content/docs/reference/views.md ./internal/tui/views/ + + - name: Add front matter to generated docs + run: | + for f in docs/content/docs/reference/*.md; do + name=$(basename "$f" .md) + if [ "$name" = "_index" ]; then continue; fi + tmp=$(mktemp) + printf -- '---\ntitle: %s\n---\n\n' "$name" > "$tmp" + cat "$f" >> "$tmp" + mv "$tmp" "$f" + done + + - name: Build site + run: cd docs && hugo --minify + + - name: Upload artifact + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 + with: + path: docs/public/ + + deploy: + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/.gitignore b/.gitignore index 1274ebd..70e61b6 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,19 @@ dist/ # env file .env +# Hugo +docs/public/ +docs/resources/ +docs/.hugo_build.lock + +# Generated API reference docs +docs/content/docs/reference/config.md +docs/content/docs/reference/db.md +docs/content/docs/reference/rules.md +docs/content/docs/reference/tui.md +docs/content/docs/reference/theme.md +docs/content/docs/reference/views.md + # Editor/IDE # .idea/ # .vscode/ diff --git a/docs/content/_index.md b/docs/content/_index.md new file mode 100644 index 0000000..eac009a --- /dev/null +++ b/docs/content/_index.md @@ -0,0 +1,28 @@ +--- +title: Tusk +layout: hextra-home +--- + +
+{{< hextra/hero-headline >}} + Tusk +{{< /hextra/hero-headline >}} +
+ +
+{{< hextra/hero-subtitle >}} + A k9s-style terminal UI for real-time PostgreSQL monitoring and management +{{< /hextra/hero-subtitle >}} +
+ +
+{{< hextra/hero-button text="Get Started" link="docs/getting-started" >}} +
+ +## Features + +{{< cards >}} + {{< card link="docs/views" title="Live Views" subtitle="Queries, transactions, sessions, locks, tables, indexes with 2s auto-refresh" >}} + {{< card link="docs/rules" title="Rules Engine" subtitle="CEL-based policy rules evaluated against live database state" >}} + {{< card link="docs/keybindings" title="Keyboard-Driven" subtitle="k9s-inspired navigation with column sorting, filtering, and drill-down" >}} +{{< /cards >}} diff --git a/docs/content/docs/_index.md b/docs/content/docs/_index.md new file mode 100644 index 0000000..1b1eaec --- /dev/null +++ b/docs/content/docs/_index.md @@ -0,0 +1,8 @@ +--- +title: Documentation +next: getting-started +sidebar: + open: true +--- + +Tusk is a terminal UI for real-time PostgreSQL monitoring. It connects to live Postgres instances and provides interactive views of database activity, with a rules engine for policy enforcement. diff --git a/docs/content/docs/development.md b/docs/content/docs/development.md new file mode 100644 index 0000000..c63b2bd --- /dev/null +++ b/docs/content/docs/development.md @@ -0,0 +1,59 @@ +--- +title: Development +weight: 5 +--- + +## Architecture + +``` +cmd/tusk/main.go Entry point, CLI flags, profile resolution, engine setup +internal/config/ YAML config with profiles, connection strings, rule definitions +internal/db/ PostgreSQL connectivity (pgx pool), query methods, data types +internal/rules/ CEL rules engine, violation store, action executors +internal/tui/ TUI application (tview), views, detail panes, navigation +internal/tui/theme/ Color palette and styles +internal/tui/views/ Individual view implementations (queries, transactions, etc.) +scripts/ Load testing and seed data +``` + +## Key types + +- `db.ResourceBase` -- shared fields for all `pg_stat_activity` resources (PID, User, App, Database, State, BackendStart) +- `db.Query` -- active query with duration, wait events, SQLcommenter tags, BlockedBy, QueryID, QueryStart +- `db.Transaction` -- active transaction with XactStart, XactDuration, QueryDuration, LockCount +- `rules.Violation` -- rule violation with timestamped event log (detected, action, cooldown, closed) +- `rules.Engine` -- evaluates CEL rules against snapshots every 2s, manages violation store + +## Conventions + +- Views implement the `tui.View` interface: `Table()`, `Start()`, `Stop()`, `ItemCount()`, `SetFilter()` +- Detail views return `*tview.Flex` with split panes (Info, Query/Queries, Activity) +- Navigation uses a view stack with `Esc` to go back; `Navigator` callback for drill-down +- `Tab` / `Shift+Tab` cycles focus between panes in detail views +- All table views save/restore selection across refresh cycles +- `Stop()` methods guard against double-close with `v.done = nil` after close +- Query identity: `(PID, BackendStart)` for sessions, `(PID, XactStart)` for transactions +- SQL formatting is display-only via `formatSQL()` before `highlightSQL()` + +## Build and test + +```bash +task build # go build -o tusk ./cmd/tusk +task run # build + run with dev profile +task db:up # start local Postgres in Docker +task loadtest # generate realistic load +task test # run all tests with Ginkgo +task check # run vet + fmt check + tests +./tusk -P # run with a named profile +./tusk '' # run with a direct connection string +``` + +## Dependencies + +| Package | Purpose | +|---------|---------| +| `rivo/tview` + `gdamore/tcell` | TUI framework | +| `jackc/pgx/v5` | PostgreSQL driver and connection pool | +| `google/cel-go` | Common Expression Language for rule evaluation | +| `spf13/cobra` | CLI framework | +| `gopkg.in/yaml.v3` | Configuration parsing | diff --git a/docs/content/docs/getting-started.md b/docs/content/docs/getting-started.md new file mode 100644 index 0000000..401a380 --- /dev/null +++ b/docs/content/docs/getting-started.md @@ -0,0 +1,98 @@ +--- +title: Getting Started +weight: 1 +--- + +## Install + +### From source + +```bash +go install github.com/fraser-isbester/tusk/cmd/tusk@latest +``` + +### Pre-built binaries + +Download from [GitHub Releases](https://github.com/fraser-isbester/tusk/releases). + +## Usage + +```bash +# Direct connection +tusk 'postgres://user:pass@localhost:5432/mydb' + +# Using a profile +tusk -P production +``` + +## Configuration + +Tusk reads its configuration from `~/.config/tusk/config.yaml`. A configuration file defines one or more connection profiles, each with optional rules. + +```yaml +default_profile: dev + +profiles: + dev: + url: "postgres://postgres:postgres@localhost:5432/mydb?sslmode=disable" + rules: + - name: kill-idle-in-txn + resource: transaction + when: "state == 'idle in transaction' && xact_duration > duration('5m')" + action: terminate + cooldown: 5m + dry_run: true + + - name: long-queries + resource: query + when: "state == 'active' && duration > duration('30s')" + action: cancel + cooldown: 1m + dry_run: true + + production: + host: db.example.com + port: 5432 + user: monitor + password: 'secret' + database: prod + readonly: true + rules: + - name: idle-in-txn-5m + resource: transaction + when: "state == 'idle in transaction' && xact_duration > duration('5m')" + action: terminate + cooldown: 5m + dry_run: true +``` + +### Profile fields + +Profiles can specify a connection either as a full `url` or with individual fields (`host`, `port`, `user`, `password`, `database`, `sslmode`). Additional profile-level options: + +| Field | Description | +|-------|-------------| +| `readonly` | Forces all rules in this profile to dry-run mode | +| `color` | Profile accent color for the header | +| `refresh_interval` | Polling interval (default `2s`) | + +### Rule fields + +| Field | Description | +|-------|-------------| +| `name` | Human-readable identifier for the rule | +| `resource` | `query`, `transaction`, or `lock` | +| `when` | CEL expression evaluated against the resource fields | +| `action` | `terminate`, `cancel`, or `log` | +| `cooldown` | Minimum interval between action firings per PID | +| `dry_run` | Record violations but don't execute the action | + +### CEL expression fields + +Each resource type exposes different fields to CEL expressions: + +**Query**: `pid`, `user`, `app`, `database`, `state`, `duration`, `wait_event_type`, `wait_event`, `query`, `blocked_by`, `query_id`, `route`, `controller`, `action_name`, `framework` + +**Transaction**: `pid`, `user`, `app`, `database`, `state`, `xact_duration`, `query_duration`, `query`, `lock_count` + +**Lock**: `blocked_pid`, `blocking_pid`, `blocked_user`, `blocking_user`, `blocked_app`, `blocking_app`, `lock_type`, `mode`, `wait_duration` diff --git a/docs/content/docs/keybindings.md b/docs/content/docs/keybindings.md new file mode 100644 index 0000000..89890fe --- /dev/null +++ b/docs/content/docs/keybindings.md @@ -0,0 +1,58 @@ +--- +title: Key Bindings +weight: 4 +--- + +Tusk uses keyboard-driven navigation inspired by k9s and vim. + +## Global keys + +| Key | Action | +|-----|--------| +| `Left` / `Right` | Switch between views | +| `:` | Open command prompt (type a view name, e.g. `:queries`) | +| `/` | Open filter input | +| `h` | Show help overlay | +| `q` | Quit | +| `Ctrl+C` | Quit | + +## Table views + +| Key | Action | +|-----|--------| +| `Enter` | Drill into detail view for the selected row | +| `Esc` | Go back to the previous view / clear filter | +| `Shift+letter` | Sort by column (e.g. `Shift+D` for duration) | + +## Query and transaction views + +| Key | Action | +|-----|--------| +| `c` | Cancel query (`pg_cancel_backend`) | +| `t` | Terminate backend (`pg_terminate_backend`) | + +## Lock view + +| Key | Action | +|-----|--------| +| `t` | Terminate the blocking backend | + +## Detail views + +| Key | Action | +|-----|--------| +| `Tab` | Cycle focus to the next pane | +| `Shift+Tab` | Cycle focus to the previous pane | +| `Esc` | Return to the parent list view | +| `c` | Cancel query (in query/transaction detail) | +| `t` | Terminate backend (in query/transaction detail) | + +## Filter + +| Key | Action | +|-----|--------| +| `/` | Open filter input | +| `Enter` | Apply filter and return focus to the table | +| `Esc` | Clear filter and close filter input | + +Filter text is matched case-insensitively across all visible columns. The filter persists until cleared with `Esc`. diff --git a/docs/content/docs/reference/_index.md b/docs/content/docs/reference/_index.md new file mode 100644 index 0000000..84694b1 --- /dev/null +++ b/docs/content/docs/reference/_index.md @@ -0,0 +1,6 @@ +--- +title: API Reference +weight: 10 +--- + +These pages are auto-generated from Go source using [gomarkdoc](https://github.com/princjef/gomarkdoc). They are rebuilt on every push by the docs CI workflow and should not be edited by hand. diff --git a/docs/content/docs/rules.md b/docs/content/docs/rules.md new file mode 100644 index 0000000..7cc1932 --- /dev/null +++ b/docs/content/docs/rules.md @@ -0,0 +1,73 @@ +--- +title: Rules Engine +weight: 3 +--- + +Tusk includes a CEL-based rules engine that evaluates policy rules against live database state every 2 seconds. Rules are defined per-profile in the YAML configuration. + +## Rule fields + +| Field | Description | +|-------|-------------| +| `name` | Human-readable identifier for the rule | +| `resource` | `query`, `transaction`, or `lock` | +| `when` | CEL expression evaluated against the resource fields | +| `action` | `terminate`, `cancel`, or `log` | +| `cooldown` | Minimum interval between action firings per PID | +| `dry_run` | Record violations but don't execute the action | + +## CEL expression fields + +Each resource type exposes a different set of fields to CEL expressions. + +### Query + +`pid`, `user`, `app`, `database`, `state`, `duration`, `wait_event_type`, `wait_event`, `query`, `blocked_by`, `query_id`, `route`, `controller`, `action_name`, `framework` + +### Transaction + +`pid`, `user`, `app`, `database`, `state`, `xact_duration`, `query_duration`, `query`, `lock_count` + +### Lock + +`blocked_pid`, `blocking_pid`, `blocked_user`, `blocking_user`, `blocked_app`, `blocking_app`, `lock_type`, `mode`, `wait_duration` + +## Example rules + +```yaml +rules: + - name: kill-idle-in-txn + resource: transaction + when: "state == 'idle in transaction' && xact_duration > duration('5m')" + action: terminate + cooldown: 5m + dry_run: true + + - name: long-queries + resource: query + when: "state == 'active' && duration > duration('30s')" + action: cancel + cooldown: 1m + dry_run: true +``` + +## Violation lifecycle + +When a rule's CEL expression matches a resource, a violation is recorded and progresses through a lifecycle: + +1. **Detected** -- The rule expression matched a resource. A violation is created with a timestamped event. +2. **Action** -- The configured action is triggered (or logged if `dry_run: true`). +3. **Cooldown** -- If a `cooldown` is set, no further actions fire for that PID until the cooldown expires. The violation remains visible. +4. **Closed** -- When the PID is no longer present in the database snapshot, the violation is marked as closed. + +Violations are visible in the `:violations` view with their full event timeline. + +## Dry-run and copilot mode + +All rules default to `dry_run: true`. In this mode, violations are recorded and displayed but no action is executed against the database. This allows you to observe what would happen before enabling automatic actions. + +From the Activity pane in query or transaction detail views, you can manually fire an action by pressing `Enter` on a violation row. This "copilot" workflow lets you review each violation before taking action. + +## Readonly profiles + +Setting `readonly: true` at the profile level forces all rules in that profile to dry-run mode, regardless of their individual `dry_run` setting. This is useful for monitoring profiles that should never modify the database. diff --git a/docs/content/docs/views.md b/docs/content/docs/views.md new file mode 100644 index 0000000..d0573b2 --- /dev/null +++ b/docs/content/docs/views.md @@ -0,0 +1,45 @@ +--- +title: Views +weight: 2 +--- + +Tusk provides eight resource views, each accessible via the command prompt (`:`) or arrow-key navigation. + +## Resource views + +| Command | Description | +|---------|-------------| +| `:queries` | Active queries with duration, wait events, blocking info, and rule violation indicators | +| `:transactions` | Active transactions sorted by age with lock counts and transaction duration | +| `:sessions` | Connections grouped by user, application, and state | +| `:tables` | Table sizes, row counts, dead tuple percentages, and vacuum statistics | +| `:locks` | Blocked/blocking lock pairs with wait duration and lock type | +| `:indexes` | Index scan counts, sizes, and usage statistics | +| `:rules` | Configured rules with violation counts and current status | +| `:violations` | Violation audit log with timestamped event timeline | + +All views auto-refresh every 2 seconds. The active view is highlighted in the tab bar, and the item count is shown in parentheses. + +## Detail views + +Pressing `Enter` on a row in most views opens a detail view with split panes showing additional information. + +### Query detail + +Shows formatted and syntax-highlighted SQL, query metadata (PID, user, app, database, state, duration, wait events), and an Activity pane displaying lock contention and rule violations for the query's PID. Press `c` to cancel the query or `t` to terminate the backend. + +### Transaction detail + +Displays transaction metadata (PID, user, state, transaction duration, query duration, lock count) alongside the current query text. Includes a query history pane showing previous queries executed within the same transaction, and an Activity pane with violations. + +### Lock detail + +Shows the blocked and blocking backends side-by-side with their respective queries, users, applications, and lock information (lock type, mode, wait duration). + +### Table detail + +Displays detailed table statistics including size, row estimates, dead tuples, sequential vs. index scan ratios, and vacuum/analyze timestamps. + +## Pane navigation + +Detail views contain multiple panes. Use `Tab` to move focus to the next pane and `Shift+Tab` to move to the previous pane. The currently focused pane is highlighted with a brighter border. diff --git a/docs/go.mod b/docs/go.mod new file mode 100644 index 0000000..7bdc745 --- /dev/null +++ b/docs/go.mod @@ -0,0 +1,5 @@ +module github.com/fraser-isbester/tusk/docs + +go 1.26.2 + +require github.com/imfing/hextra v0.12.2 // indirect diff --git a/docs/go.sum b/docs/go.sum new file mode 100644 index 0000000..583c3df --- /dev/null +++ b/docs/go.sum @@ -0,0 +1,2 @@ +github.com/imfing/hextra v0.12.2 h1:qa+cHQ1LC/7ys9EhRNnHrRBAHu83Tm8rhh1oWO2a7cc= +github.com/imfing/hextra v0.12.2/go.mod h1:vi+yhpq8YPp/aghvJlNKVnJKcPJ/VyAEcfC1BSV9ARo= diff --git a/docs/hugo.yaml b/docs/hugo.yaml new file mode 100644 index 0000000..615676e --- /dev/null +++ b/docs/hugo.yaml @@ -0,0 +1,29 @@ +baseURL: https://fraser-isbester.github.io/tusk/ +title: Tusk +languageCode: en-us + +module: + imports: + - path: github.com/imfing/hextra + +menu: + main: + - name: Docs + pageRef: /docs + weight: 1 + - name: GitHub + url: https://github.com/Fraser-Isbester/tusk + weight: 2 + params: + icon: github + +params: + description: A k9s-style terminal UI for PostgreSQL monitoring + navbar: + displayTitle: true + displayLogo: false + footer: + displayPoweredBy: false + editURL: + enable: true + base: https://github.com/Fraser-Isbester/tusk/edit/main/docs/content diff --git a/internal/config/config.go b/internal/config/config.go index 7382643..d68c9dd 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,3 +1,4 @@ +// Package config handles YAML profile loading and connection string resolution. package config import ( diff --git a/internal/db/pool.go b/internal/db/pool.go index 341c0dc..3e6613a 100644 --- a/internal/db/pool.go +++ b/internal/db/pool.go @@ -1,3 +1,4 @@ +// Package db provides PostgreSQL connectivity, query execution, and data types. package db import ( diff --git a/internal/rules/engine.go b/internal/rules/engine.go index 0d0d7d1..70f00da 100644 --- a/internal/rules/engine.go +++ b/internal/rules/engine.go @@ -1,3 +1,4 @@ +// Package rules implements a CEL-based policy engine with violation tracking and action execution. package rules import ( diff --git a/internal/tui/app.go b/internal/tui/app.go index bf4616a..826ebea 100644 --- a/internal/tui/app.go +++ b/internal/tui/app.go @@ -1,3 +1,4 @@ +// Package tui is the root terminal UI application built on tview. package tui import ( diff --git a/internal/tui/theme/theme.go b/internal/tui/theme/theme.go index 1bd7674..c472d42 100644 --- a/internal/tui/theme/theme.go +++ b/internal/tui/theme/theme.go @@ -1,3 +1,4 @@ +// Package theme defines the color palette and styles for the TUI. package theme import ( diff --git a/internal/tui/views/queries.go b/internal/tui/views/queries.go index 924c861..a783f05 100644 --- a/internal/tui/views/queries.go +++ b/internal/tui/views/queries.go @@ -1,3 +1,4 @@ +// Package views implements the individual resource views for the TUI. package views import (