Tools
Tools: Track Your GitHub Copilot Pro from the Terminal ✨
2026-02-14
0 views
admin
From Invisible to Visible ## What I Built : Copilot-usage ## The Problem That Drove Me Crazy ## The Solution: Copilot Usage ## Key Features ## Architecture Deep Dive ## Async Event Loop Pattern ## Technical Decisions ## 1. Modular Component Architecture ## 2. State Machine Pattern ## 3. Constant Warning/Error Colors ## 4. Compact Layout Philosophy ## 5. Never Exit TUI Philosophy ## 📸 Screenshots ## Dashboard ## TokyoNight ## Theme Selector ## Waybar Integration ## Refresh ## My Experience with GitHub Copilot CLI ## How I Used Copilot CLI ## Edge Cases: Error handling, terminal cleanup on panic, XDG directory resolution - Copilot handled these gracefully. ## Installation ( Only tested on LINUX ) ## Using install script (Recommended) ## Manual installation ## Keybindings (in TUI) ## Configuration ## Required GitHub Permissions ## - NOT "Copilot Requests" - different permission! ## What's Next? ## - Good first issues: new themes, documentation, tests ## Credits & Thanks GitHub Copilot CLI Challenge Submission This is a submission for the GitHub Copilot CLI Challenge Author: Cesar Castillo
Repository: https://github.com/CGCM070/copilot-usage_tui Stop guessing. Start knowing. GitHub Copilot Pro gives you 300 requests per month… but having to go to the site, navigate through menus, and dig into a specific section just to check your usage? That’s tedious I built a terminal-based TUI that transforms your Copilot usage from an invisible mystery into a beautiful, real-time dashboard. Because developers deserve to know exactly how they're using their AI pair programmer. A Rust-based CLI tool with an interactive terminal UI that tracks GitHub Copilot Pro usage in real-time, featuring progress bars, per-model statistics, and seamless Waybar integration for Hyprland users. If you're a GitHub Copilot Pro user, you know the drill:
You get 300 premium requests per month
There is a page where you can check usage…
But you have to go to the site, open the right section, and dig through settings to find it
So you’re never quite sure where you stand until you manually go look
300 requests. Visibility — technically. Convenience? Not so much. I built a terminal dashboard that gives you complete visibility into your Copilot consumption with zero friction. 📊 Beautiful Terminal Dashboard 🔧 Interactive Modals 📈 Waybar Integration This isn't just a simple API wrapper. It's a full async TUI application with clean separation of concerns: The heart of the application is a non-blocking event loop: Each UI component is a pure function with zero side effects: AppState enum models all UI states explicitly: No boolean flags. No hidden state. Every state is explicit and handled. Across all 9 themes, warning and error colors remain constant: Users instantly recognize status regardless of theme. Inspired by system monitors like btop: All operations happen via modals: The TUI stays alive. No jarring exits. No lost context. This project was built with extensive help from GitHub Copilot CLI, turning natural language into idiomatic Rust code. 1. Async Architecture Design 2. State Machine Pattern 4. Progress Bar Segmentation Copilot suggested the modular component pattern that made the codebase maintainable and testable. File: ~/.config/copilot-usage/config.toml Notification Support - Alert when usage > 80%
Historical Graphs - Usage trends over time
🤝 Open Source
This project is open source. Want to contribute? devchallenge
githubchallenge
cli
githubcopilot
rust Templates let you quickly answer FAQs or store snippets for re-use. Very clean and reliable. Great work! Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
┌─────────────────────────────────────────────────────────┐
│ Terminal UI (ratatui) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ UI Components │ │
│ │ • Header (user info) │ │
│ │ • Usage Progress Bar │ │
│ │ • Model Usage Table │ │
│ │ • Modal Dialogs │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Event Loop (50ms poll) │ │
│ │ • Keyboard input handling │ │
│ │ • Async result processing │ │
│ │ • Spinner animation updates │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Async Handler (tokio) │ │
│ │ • Background API calls │ │
│ │ • Cache operations │ │
│ │ • mpsc channels for results │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘ ↓
┌─────────────────────────────────────────────────────────┐
│ GitHub API │
│ • /users/{username}/settings/billing/... │
│ • 5000 req/hour rate limit │
│ • Fine-grained PAT with Plan (Read) │
└─────────────────────────────────────────────────────────┘ Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
┌─────────────────────────────────────────────────────────┐
│ Terminal UI (ratatui) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ UI Components │ │
│ │ • Header (user info) │ │
│ │ • Usage Progress Bar │ │
│ │ • Model Usage Table │ │
│ │ • Modal Dialogs │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Event Loop (50ms poll) │ │
│ │ • Keyboard input handling │ │
│ │ • Async result processing │ │
│ │ • Spinner animation updates │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Async Handler (tokio) │ │
│ │ • Background API calls │ │
│ │ • Cache operations │ │
│ │ • mpsc channels for results │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘ ↓
┌─────────────────────────────────────────────────────────┐
│ GitHub API │
│ • /users/{username}/settings/billing/... │
│ • 5000 req/hour rate limit │
│ • Fine-grained PAT with Plan (Read) │
└─────────────────────────────────────────────────────────┘ CODE_BLOCK:
┌─────────────────────────────────────────────────────────┐
│ Terminal UI (ratatui) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ UI Components │ │
│ │ • Header (user info) │ │
│ │ • Usage Progress Bar │ │
│ │ • Model Usage Table │ │
│ │ • Modal Dialogs │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Event Loop (50ms poll) │ │
│ │ • Keyboard input handling │ │
│ │ • Async result processing │ │
│ │ • Spinner animation updates │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Async Handler (tokio) │ │
│ │ • Background API calls │ │
│ │ • Cache operations │ │
│ │ • mpsc channels for results │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘ ↓
┌─────────────────────────────────────────────────────────┐
│ GitHub API │
│ • /users/{username}/settings/billing/... │
│ • 5000 req/hour rate limit │
│ • Fine-grained PAT with Plan (Read) │
└─────────────────────────────────────────────────────────┘ COMMAND_BLOCK:
loop { // 1. Handle user input (non-blocking) if event::poll(Duration::from_millis(50))? { // Process keyboard events } // 2. Animate spinner during loading if matches!(app.state, LoadingRefresh | LoadingCache) { app.advance_spinner(); } // 3. Check for async results if let Some(result) = async_handler.try_recv() { match result { RefreshComplete(Ok(stats)) => update_dashboard(stats), RefreshComplete(Err(e)) => show_error_modal(e), CacheInfoReady(info) => show_cache_modal(info), } } // 4. Render UI (20 FPS) terminal.draw(|f| render_ui(f, &app, &stats, theme))?;
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
loop { // 1. Handle user input (non-blocking) if event::poll(Duration::from_millis(50))? { // Process keyboard events } // 2. Animate spinner during loading if matches!(app.state, LoadingRefresh | LoadingCache) { app.advance_spinner(); } // 3. Check for async results if let Some(result) = async_handler.try_recv() { match result { RefreshComplete(Ok(stats)) => update_dashboard(stats), RefreshComplete(Err(e)) => show_error_modal(e), CacheInfoReady(info) => show_cache_modal(info), } } // 4. Render UI (20 FPS) terminal.draw(|f| render_ui(f, &app, &stats, theme))?;
} COMMAND_BLOCK:
loop { // 1. Handle user input (non-blocking) if event::poll(Duration::from_millis(50))? { // Process keyboard events } // 2. Animate spinner during loading if matches!(app.state, LoadingRefresh | LoadingCache) { app.advance_spinner(); } // 3. Check for async results if let Some(result) = async_handler.try_recv() { match result { RefreshComplete(Ok(stats)) => update_dashboard(stats), RefreshComplete(Err(e)) => show_error_modal(e), CacheInfoReady(info) => show_cache_modal(info), } } // 4. Render UI (20 FPS) terminal.draw(|f| render_ui(f, &app, &stats, theme))?;
} CODE_BLOCK:
// Pure rendering function - no state mutations
pub fn render_usage_overall( f: &mut Frame, area: Rect, stats: &UsageStats, colors: &ThemeColors
) { // Calculate progress bar segments // Render with theme colors // No state changes, no events handled
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
// Pure rendering function - no state mutations
pub fn render_usage_overall( f: &mut Frame, area: Rect, stats: &UsageStats, colors: &ThemeColors
) { // Calculate progress bar segments // Render with theme colors // No state changes, no events handled
} CODE_BLOCK:
// Pure rendering function - no state mutations
pub fn render_usage_overall( f: &mut Frame, area: Rect, stats: &UsageStats, colors: &ThemeColors
) { // Calculate progress bar segments // Render with theme colors // No state changes, no events handled
} CODE_BLOCK:
pub enum AppState { Dashboard, // Main view LoadingRefresh, // Spinner during API call ShowCacheInfo(CacheInfo), // Cache status modal ShowError { // Error with debug toggle message: String, debug_message: String, show_debug: bool, }, // ... more states
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
pub enum AppState { Dashboard, // Main view LoadingRefresh, // Spinner during API call ShowCacheInfo(CacheInfo), // Cache status modal ShowError { // Error with debug toggle message: String, debug_message: String, show_debug: bool, }, // ... more states
} CODE_BLOCK:
pub enum AppState { Dashboard, // Main view LoadingRefresh, // Spinner during API call ShowCacheInfo(CacheInfo), // Cache status modal ShowError { // Error with debug toggle message: String, debug_message: String, show_debug: bool, }, // ... more states
} COMMAND_BLOCK:
# Asked: "Best way to structure async TUI in Rust?"
# Copilot suggested: tokio + mpsc channels + non-blocking event loop
# Result: Smooth 20 FPS UI with background API calls Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Asked: "Best way to structure async TUI in Rust?"
# Copilot suggested: tokio + mpsc channels + non-blocking event loop
# Result: Smooth 20 FPS UI with background API calls COMMAND_BLOCK:
# Asked: "Best way to structure async TUI in Rust?"
# Copilot suggested: tokio + mpsc channels + non-blocking event loop
# Result: Smooth 20 FPS UI with background API calls COMMAND_BLOCK:
# Asked: "How to model TUI states without boolean flags?"
# Copilot proposed: AppState enum with exhaustive match
# Result: Clean state transitions, no hidden bugs Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Asked: "How to model TUI states without boolean flags?"
# Copilot proposed: AppState enum with exhaustive match
# Result: Clean state transitions, no hidden bugs COMMAND_BLOCK:
# Asked: "How to model TUI states without boolean flags?"
# Copilot proposed: AppState enum with exhaustive match
# Result: Clean state transitions, no hidden bugs COMMAND_BLOCK:
# Asked: "Implement 9 color themes with constant warning/error colors"
# Copilot generated: ThemeColors struct + theme implementations
# Result: 9 gorgeous themes with consistent UX Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Asked: "Implement 9 color themes with constant warning/error colors"
# Copilot generated: ThemeColors struct + theme implementations
# Result: 9 gorgeous themes with consistent UX COMMAND_BLOCK:
# Asked: "Implement 9 color themes with constant warning/error colors"
# Copilot generated: ThemeColors struct + theme implementations
# Result: 9 gorgeous themes with consistent UX COMMAND_BLOCK:
# Asked: "Create gradient progress bars (green→orange→red)"
# Copilot designed: Segmented bar with zone calculations
# Result: Visual feedback at a glance Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Asked: "Create gradient progress bars (green→orange→red)"
# Copilot designed: Segmented bar with zone calculations
# Result: Visual feedback at a glance COMMAND_BLOCK:
# Asked: "Create gradient progress bars (green→orange→red)"
# Copilot designed: Segmented bar with zone calculations
# Result: Visual feedback at a glance COMMAND_BLOCK:
# Asked: "Implement file-based cache with TTL in Rust"
# Copilot built: Cache with CacheStatus enum (Fresh/Expired/Missing)
# Result: Fast cached responses, automatic invalidation Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Asked: "Implement file-based cache with TTL in Rust"
# Copilot built: Cache with CacheStatus enum (Fresh/Expired/Missing)
# Result: Fast cached responses, automatic invalidation COMMAND_BLOCK:
# Asked: "Implement file-based cache with TTL in Rust"
# Copilot built: Cache with CacheStatus enum (Fresh/Expired/Missing)
# Result: Fast cached responses, automatic invalidation COMMAND_BLOCK:
git clone https://github.com/CGCM070/copilot-usage.git
cd copilot-usage/
./install.sh Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
git clone https://github.com/CGCM070/copilot-usage.git
cd copilot-usage/
./install.sh COMMAND_BLOCK:
git clone https://github.com/CGCM070/copilot-usage.git
cd copilot-usage/
./install.sh CODE_BLOCK:
cargo install --path . --force Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
cargo install --path . --force CODE_BLOCK:
cargo install --path . --force COMMAND_BLOCK:
# First run - interactive setup
copilot-usage # Dashboard (uses cache if available)
copilot-usage # Force refresh from API
copilot-usage --refresh # Waybar JSON output
copilot-usage --waybar # Change theme temporarily
copilot-usage --theme nord # Reconfigure token/theme
copilot-usage reconfigure # Check cache status
copilot-usage --cache-status Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# First run - interactive setup
copilot-usage # Dashboard (uses cache if available)
copilot-usage # Force refresh from API
copilot-usage --refresh # Waybar JSON output
copilot-usage --waybar # Change theme temporarily
copilot-usage --theme nord # Reconfigure token/theme
copilot-usage reconfigure # Check cache status
copilot-usage --cache-status COMMAND_BLOCK:
# First run - interactive setup
copilot-usage # Dashboard (uses cache if available)
copilot-usage # Force refresh from API
copilot-usage --refresh # Waybar JSON output
copilot-usage --waybar # Change theme temporarily
copilot-usage --theme nord # Reconfigure token/theme
copilot-usage reconfigure # Check cache status
copilot-usage --cache-status CODE_BLOCK:
token = "ghp_..."
theme = "dark"
cache_ttl_minutes = 5
waybar_format = "{}"
username = "octocat" Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
token = "ghp_..."
theme = "dark"
cache_ttl_minutes = 5
waybar_format = "{}"
username = "octocat" CODE_BLOCK:
token = "ghp_..."
theme = "dark"
cache_ttl_minutes = 5
waybar_format = "{}"
username = "octocat" - Real-time progress bars with color-coded zones (green → orange → red)
- Per-model usage breakdown (GPT-4, Claude 3.5, Gemini, etc.)
- Compact, density-focused layout inspired by btop
- 9 gorgeous color themes: Dark, Nord, Monokai, Gruvbox, Catppuccin, OneDark, TokyoNight, Solarized, Kanagawa - Non-blocking UI with tokio async runtime
- Smooth spinner animations during API calls (20 FPS)
- Cancelable operations with Escape key
- Background refresh while you keep working - Instant theme switching without exiting TUI
- Persistent theme configuration
- Consistent warning (orange) and error (red) colors across all themes
- Segmented progress bars for visual feedback - Local cache with 5-minute TTL (configurable)
- Cache status indicator (Fresh/Expired/Missing)
- Manual refresh with 'r' key
- Automatic cache invalidation on --refresh flag - Command menu (press /) for quick actions
- Theme selector with live preview
- Help dialog with all keybindings (press ?)
- Error dialogs with debug mode toggle (press d)
- Cache info modal showing last update time - JSON output for Waybar status bar
- Customizable format strings
- Shows percentage in your status bar
- Perfect for Hyprland/wayland users - Interactive first-run wizard
- Automatic GitHub username detection
- Fine-grained PAT or classic token support
- XDG-compliant config directory - API calls don't freeze the UI
- Spinner animations stay smooth
- Users can cancel operations anytime
- Background tasks complete even after cancel - Easy to test (input → output)
- No hidden state mutations
- Components compose naturally
- Theme changes apply instantly - Warning: rgb(255, 184, 108) (orange)
- Error: rgb(255, 85, 85) (red) - Dynamic height: Model table resizes based on data (N + 3 rows)
- Compact width: 50% of terminal for readability
- Minimal padding: Information density over whitespace
- Rounded borders: Modern, polished feel - Reconfigure token? Modal dialog
- Change theme? Theme selector modal
- Refresh data? Loading spinner modal
- Error occurred? Error modal with debug info - Fine-grained PAT with Plan (Read) permission
- Classic tokens need read:user scope - PRs welcome - GitHub for Copilot Pro - the AI that powers our coding
- GitHub Copilot CLI for helping build this tool faster than ever <3
- DEV.to for hosting this challenge - Location United States
- Pronouns He/Him
- Work Open to Work
- Joined Jan 14, 2026
how-totutorialguidedev.toaimlgptlinuxswitchgitgithub