This document provides context for AI coding agents working on the sift project.
sift is a lightweight terminal UI for displaying Go test results. It allows developers to traverse verbose Go test logs in their terminal, with the ability to expand and collapse individual tests to focus only on the logs that matter.
The tool was created to address the overwhelming nature of Go's test output, especially for large test suites. When running tests from the command line with go test -v -json, the verbose output can make it difficult to quickly identify and drill into failing or interesting tests. sift provides an interactive, vim-inspired interface for navigating test results without needing an IDE.
- Interactive Test Browsing: Collapse and expand test outputs to reduce visual clutter
- Fuzzy Search/Filter: Case-insensitive fuzzy search to quickly find specific tests
- Vim-Inspired Navigation: Familiar keymaps for developers comfortable with vim motions
- Smart Log Parsing: Automatically detects and prettifies structured logs (slog JSON/text, default logs)
- Two View Modes:
- Interactive mode with alternate screen (default)
- Non-interactive inline mode for CI/CD pipelines
- Auto-Toggle Mode: Automatically opens/closes tests as you navigate
- Test Status Indicators: Visual icons for pass/fail/skip/running states
- Language: Go 1.25.0
- UI Framework: Bubble Tea (TUI framework based on The Elm Architecture)
- Styling: Lipgloss for terminal styling
- UI Components: Bubbles for viewport and text input
- CLI Parsing: Kong
- Search: fuzzysearch for fuzzy matching
- Testing: testify for test assertions
internal/
├── sift/ # Core bubbletea view
├── tests/ # Test manager and data structures
pkg/
├── logparse/ # Log parsing utilities
├── viewbuilder/ # View construction helper
└── helpview/ # Wrapping help view
The sift struct orchestrates three concurrent goroutines using errgroup:
- Stdin Scanner: Reads JSON test output line-by-line from stdin
- Bubble Tea Program: Handles UI rendering and user input
- Frame Loop: Sends periodic frame messages for smooth updates (120 FPS)
Thread-safe manager that:
- Stores test nodes with status, elapsed time, and metadata
- Maintains separate map of test logs (parsed log entries)
- Uses RWMutex for concurrent access from scanner and UI goroutines
- Processes JSON output from
go test -jsoncommand - Filters out redundant Go test output lines (e.g.,
=== RUN,--- PASS)
Implements the Bubble Tea architecture:
- Model: Contains test state, cursor position, viewport, search input
- Update: Handles all user input events and key combinations
- View: Delegates to interactive or inline view renderers
Key state management:
testState: Map of test references to their toggle/viewport statecursor: Tracks currently selected test and log lineautoToggleMode: Automatically expands/collapses tests during navigationsearchInput: Bubble Tea text input for fuzzy search
Attempts to parse logs in order:
- Default structured log format
- Slog JSON format
- Slog text format
- Falls back to raw message
Extracts timestamp, log level, message, and additional fields for prettification.
stdin (go test -json)
↓
Scanner goroutine → TestManager.AddTestOutput()
↓
TestManager (thread-safe storage)
↓
Frame loop → FrameMsg → Model.Update()
↓
Model.View() → interactive/inline view
↓
Terminal output
Each test is uniquely identified by a TestReference struct containing:
Package: The Go package pathTest: The test name (including subtest hierarchy with/separators)
The cursor tracks two positions:
cursor.test: Index of the currently selected testcursor.log: Index of the currently selected log line within that test
- Automatically scrolls to keep cursor visible with a 5-line buffer
- Adjusts height dynamically based on content and footer size
- Syncs with test toggle states to update content
- Uses fuzzy matching on test names (case-insensitive)
- Filters tests in real-time as user types
- Automatically adjusts cursor to nearest visible test
sift consumes the JSON output from Go's test command:
go test ./... -v -json | siftEach line is a JSON object with fields:
time: Timestampaction:run,output,pass,fail,skippackage: Package pathtest: Test name (optional)elapsed: Duration in seconds (optional)output: Log line (optional)
- Unit tests exist for core components (test_manager, logparse, view)
- Uses testify for test assertions
- Sample tests provided in
samples/directory for manual testing - Run demo:
go test ./samples/... -v -json | go run .
The TestManager uses read-write locks extensively since it's accessed concurrently by:
- The scanner goroutine (writes)
- The Bubble Tea update/view methods (reads)
- Frame rate set to 120 FPS for smooth animations
- View builder tracks line counts for efficient viewport positioning
- Logs are stored separately from test nodes to optimize memory