gh-agent-viz

Architecture Decisions

This document captures the key decisions made when building gh-agent-viz.

Why a gh Extension and Not a Copilot CLI Plugin

Copilot CLI Plugin System

The GitHub Copilot CLI (github/copilot-cli) has a plugin system that can be accessed via /plugin install. Plugins can provide several capabilities:

The official plugin repository is github/copilot-plugins, with examples like:

The Limitation

Plugins operate within the agent conversation model. They give the agent new knowledge and tools, but provide zero control over the terminal UI. There is no plugin API for:

A plugin could answer “what are my agent sessions?” conversationally, but it cannot render an interactive dashboard.

The Solution

For interactive visualization, a gh CLI extension is the correct approach because:

Architecture Decisions

Language: Go

Decision: Use Go as the primary language.

Rationale:

TUI Framework: Bubble Tea + Lip Gloss + Bubbles

Decision: Use the Charmbracelet stack for the TUI.

Rationale:

CLI Framework: Cobra

Decision: Use Cobra for command-line argument parsing.

Rationale:

Data Source: Direct Copilot API with CLI Fallback

Decision: Fetch data primarily via direct HTTP calls to the Copilot API (api.githubcopilot.com), falling back to gh agent-task CLI commands if CAPI auth fails.

Primary — Copilot API (CAPI):

Fallback — CLI:

Rationale:

Distribution: gh extension install

Decision: Distribute as a gh extension.

Installation:

gh extension install maxbeizer/gh-agent-viz

Usage:

gh agent-viz
gh agent-viz --repo owner/repo

Rationale:

Reference Architecture: gh-dash

Decision: Model architecture on dlvhdr/gh-dash.

Rationale:

Key Go Dependencies

Package Purpose
github.com/charmbracelet/bubbletea TUI framework (Elm architecture)
github.com/charmbracelet/lipgloss Terminal styling and layout
github.com/charmbracelet/bubbles Pre-built UI components
github.com/spf13/cobra CLI argument parsing
github.com/cli/go-gh/v2 GitHub auth context and API client
gopkg.in/yaml.v3 Configuration file parsing

What Was Ruled Out

Copilot CLI Plugin

Why not: No API for custom UI rendering. Plugins can only provide conversational responses within the agent’s chat interface.

When it makes sense: If we wanted to add a conversational interface to query agent sessions (“show me my last 5 agent runs”), a plugin could complement the TUI.

Standalone Binary Outside gh Ecosystem

Why not: Would require separate authentication setup and wouldn’t integrate with the gh CLI workflow.

When it makes sense: If we needed to support users who don’t use gh CLI at all.

Direct REST API Calls

Why not (standalone): The Copilot API is now the primary data source (see above), but a CLI fallback is retained for auth-edge cases.

When it makes sense: Future enhancement when GitHub provides a proper API endpoint.

Future Considerations

When a REST API Becomes Available

The primary data path now uses the Copilot API (api.githubcopilot.com) directly. If GitHub introduces a separate dedicated REST API endpoint for agent sessions, we should evaluate migrating to it for broader compatibility.

Live Log Streaming

The --follow flag on gh agent-task view <id> --log enables live log streaming. This could be implemented using:

Companion Copilot CLI Plugin

We could create a plugin that provides:

Configuration Enhancements

Future config file options could include:

Project Structure

gh-agent-viz/
├── gh-agent-viz.go          # Entry point
├── cmd/
│   └── root.go              # Cobra root command
├── internal/
│   ├── data/
│   │   ├── agentapi.go      # Data fetching layer (orchestrates CAPI + CLI fallback)
│   │   └── capi/            # Direct Copilot API client
│   ├── config/
│   │   └── config.go        # Configuration parsing
│   └── tui/
│       ├── ui.go            # Main Bubble Tea model
│       ├── context.go       # Shared program context
│       ├── theme.go         # Lip Gloss styles
│       ├── keys.go          # Key bindings
│       └── components/
│           ├── header/
│           ├── footer/
│           ├── tasklist/
│           ├── taskdetail/
│           └── logview/
├── docs/
│   └── DECISIONS.md         # This file
├── .goreleaser.yaml         # Cross-platform builds
├── .gitignore               # Go ignores
└── README.md                # Install and usage docs

Key Patterns from gh-dash

Data Fetching via Copilot API (Primary) with CLI Fallback

// Primary: direct CAPI call (internal/data/capi/)
client := capi.NewClient(token)
sessions, err := client.ListSessions(ctx)

// Fallback: shell out to gh agent-task
cmd := exec.Command("gh", "agent-task", "list", "--json")
output, err := cmd.Output()

Bubble Tea Model Pattern

type Model struct {
    // State fields
}

func (m Model) Init() tea.Cmd { /* ... */ }
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { /* ... */ }
func (m Model) View() string { /* ... */ }

Component Organization

Each UI component lives in its own package with:

Centralized Key Bindings

Define all key bindings in one place for:

Theme-Based Styling

Lip Gloss styles defined in a theme package: