Skip to content

Latest commit

 

History

History
528 lines (407 loc) · 13.7 KB

File metadata and controls

528 lines (407 loc) · 13.7 KB

Terminal Management

Overview

MCP CLI provides comprehensive terminal management utilities for handling terminal state, cleanup, and cross-platform operations. The TerminalManager class offers a unified interface for terminal operations that work across Windows, macOS, and Linux.

Core Features

1. Screen Clearing

Clear the terminal screen using platform-appropriate commands:

from mcp_cli.ui.terminal import clear_screen

# Clear the terminal
clear_screen()

# Or using the manager directly
from mcp_cli.ui.terminal import TerminalManager
TerminalManager.clear()
  • Windows: Uses cls command
  • Unix/Linux/macOS: Uses clear command

2. Terminal Reset

Reset terminal to sane defaults (Unix-like systems only):

from mcp_cli.ui.terminal import reset_terminal

# Reset terminal settings
reset_terminal()

# Useful after programs leave terminal in bad state
  • Runs stty sane on Unix-like systems
  • No-op on Windows
  • Handles errors gracefully with debug logging

3. Terminal Size Detection

Get current terminal dimensions:

from mcp_cli.ui.terminal import get_terminal_size

# Get size as (columns, rows)
cols, rows = get_terminal_size()

print(f"Terminal size: {cols}×{rows}")

# Check terminal size categories
if cols < 80:
    print("Narrow terminal - content may wrap")
elif cols < 120:
    print("Standard terminal width")
else:
    print("Wide terminal - good for side-by-side views")
  • Uses shutil.get_terminal_size() internally
  • Returns default (80, 24) if detection fails

4. Terminal Title

Set the terminal window title:

from mcp_cli.ui.terminal import set_terminal_title

# Set a custom title
set_terminal_title("MCP CLI - Processing...")

# Update title with status
for i in range(100):
    set_terminal_title(f"Progress: {i+1}%")
    # ... do work ...

set_terminal_title("MCP CLI - Complete")
  • Windows: Uses title command
  • Unix/Linux/macOS: Uses ANSI escape sequence \033]0;title\007

5. Color Support Detection

Check terminal color capabilities:

from mcp_cli.ui.terminal import TerminalManager

# Basic color support
if TerminalManager.supports_color():
    print("\033[32mGreen text\033[0m")  # Use ANSI colors
else:
    print("Plain text")  # No colors

# Advanced color detection
if TerminalManager.supports_truecolor():
    print("24-bit RGB colors supported")
elif TerminalManager.supports_256_colors():
    print("256 colors supported")
else:
    print("Basic 16 colors")

# Get color level
color_level = TerminalManager.get_color_level()
# Returns: 'truecolor', '256', '16', or 'mono'
  • supports_color(): Returns True if stdout is a TTY
  • supports_truecolor(): Checks for 24-bit RGB support via COLORTERM
  • supports_256_colors(): Checks for 256 color support via TERM
  • get_color_level(): Returns the highest supported color level

6. Cursor Control

Control cursor visibility and position (Unix-like systems):

from mcp_cli.ui.terminal import (
    hide_cursor, show_cursor,
    TerminalManager
)

# Hide/show cursor
hide_cursor()
# ... do work without visible cursor ...
show_cursor()

# Save and restore position
TerminalManager.save_cursor_position()
print("Line 1")
print("Line 2")
TerminalManager.restore_cursor_position()  # Back to saved position

# Move cursor
TerminalManager.move_cursor_up(2)    # Move up 2 lines
TerminalManager.move_cursor_down(1)  # Move down 1 line
TerminalManager.clear_line()          # Clear current line
  • Cursor control uses ANSI escape sequences
  • No-op on Windows
  • Useful for creating dynamic terminal UIs

7. Hyperlinks

Create clickable hyperlinks in supported terminals:

from mcp_cli.ui.terminal import hyperlink, TerminalManager

# Create hyperlink with TerminalManager
link = TerminalManager.hyperlink("https://example.com")
print(f"Visit: {link}")

# Custom link text
link = TerminalManager.hyperlink("https://docs.example.com", "Documentation")
print(f"Read the {link}")

# Using Rich for hyperlinks (recommended with Rich output)
from mcp_cli.ui.output import get_output
ui = get_output()
ui.print("Visit [link=https://example.com]our website[/link]")

Supported terminals include:

  • iTerm2 (macOS)
  • Kitty
  • WezTerm
  • Hyper
  • Most modern terminal emulators with OSC 8 support

For unsupported terminals, falls back to "text (url)" format.

8. Terminal Bell

Sound the terminal bell/beep:

from mcp_cli.ui.terminal import bell

# Sound the bell
bell()

# Multiple bells with delay
import time
for i in range(3):
    bell()
    time.sleep(1)
  • Uses ASCII bell character (\a)
  • May be disabled in terminal settings
  • Some terminals flash instead of beeping

9. Alternate Screen Buffer

Use alternate screen like vim, less, or htop:

from mcp_cli.ui.terminal import alternate_screen

# Using context manager (recommended)
with alternate_screen():
    # Now in alternate screen
    print("This is in alternate screen")
    print("Original screen content is preserved")
    input("Press Enter to return...")
# Automatically returns to main screen

# Manual control
from mcp_cli.ui.terminal import TerminalManager
TerminalManager.enter_alternate_screen()
# ... work in alternate screen ...
TerminalManager.exit_alternate_screen()

The alternate screen:

  • Preserves main screen content
  • Automatically hides cursor in context manager
  • Used by full-screen applications
  • Clears when exiting

10. Terminal Information

Get comprehensive terminal information:

from mcp_cli.ui.terminal import get_terminal_info, TerminalManager

# Get all terminal info
info = get_terminal_info()
print(f"Terminal: {info['program']}")
print(f"Type: {info['type']}")
print(f"Size: {info['size']['columns']}×{info['size']['rows']}")
print(f"Encoding: {info['encoding']}")

# Check specific environments
if TerminalManager.is_tmux():
    print("Running in tmux")
if TerminalManager.is_ssh():
    print("Connected via SSH")
if TerminalManager.is_screen():
    print("Running in GNU screen")

# Get terminal program
program = TerminalManager.get_terminal_program()
# Returns: 'iTerm.app', 'Terminal.app', 'unknown', etc.

11. Progress in Title

Show progress in the terminal title bar:

from mcp_cli.ui.terminal import TerminalManager

# Basic progress
for i in range(101):
    TerminalManager.set_title_progress(i)
    # ... do work ...
    time.sleep(0.1)

# Custom prefix
for i in range(101):
    TerminalManager.set_title_progress(i, "Downloading")
    # Shows: "Downloading: [████░░░░░░] 40%"

Creates a visual progress bar in the title with percentage.

12. Full Terminal Restoration

Restore terminal and clean up all resources:

from mcp_cli.ui.terminal import restore_terminal

try:
    # Your application code
    run_app()
finally:
    # Always restore on exit
    restore_terminal()

The restore_terminal() function performs:

  1. Terminal reset (stty sane on Unix)
  2. Asyncio cleanup (cancels tasks, closes event loops)
  3. Garbage collection

Asyncio Cleanup

The terminal manager provides comprehensive asyncio cleanup to prevent resource leaks:

from mcp_cli.ui.terminal import TerminalManager

# Cleanup asyncio resources
TerminalManager.cleanup_asyncio()

This function:

  • Detects running vs non-running event loops
  • Cancels pending tasks gracefully
  • Shuts down async generators
  • Closes event loops properly
  • Handles all errors without raising exceptions

Cleanup Process

  1. Loop Detection: Checks for running or existing event loops
  2. Task Cancellation: Only cancels tasks that aren't done
  3. Graceful Shutdown: Gives tasks time to cancel cleanly
  4. Generator Cleanup: Shuts down async generators
  5. Loop Closure: Closes the event loop if not running

Usage Examples

Basic Terminal Operations

from mcp_cli.ui.terminal import (
    clear_screen,
    get_terminal_size,
    set_terminal_title,
    TerminalManager
)

# Clear and setup
clear_screen()
set_terminal_title("My Application")

# Get and display size
cols, rows = get_terminal_size()
print(f"Working with {cols}×{rows} terminal")

# Check capabilities
if TerminalManager.supports_color():
    print("✅ Color support detected")
else:
    print("No color support")

Application Lifecycle Management

import asyncio
from mcp_cli.ui.terminal import restore_terminal, set_terminal_title

async def main():
    try:
        set_terminal_title("App Running")
        
        # Create background tasks
        tasks = [
            asyncio.create_task(background_work()),
            asyncio.create_task(monitor_input())
        ]
        
        # Run application
        await run_application()
        
    finally:
        # Always restore terminal on exit
        restore_terminal()
        print("Terminal restored successfully")

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        restore_terminal()

Dynamic Terminal Monitoring

import asyncio
from mcp_cli.ui.terminal import get_terminal_size

async def monitor_terminal_size():
    """Monitor and react to terminal size changes."""
    last_size = (0, 0)
    
    while True:
        current_size = get_terminal_size()
        
        if current_size != last_size:
            cols, rows = current_size
            print(f"Terminal resized to {cols}×{rows}")
            
            # Adjust UI layout based on new size
            if cols < 80:
                use_compact_layout()
            else:
                use_full_layout()
            
            last_size = current_size
        
        await asyncio.sleep(0.5)

Platform Compatibility

Feature Windows macOS Linux Notes
Clear Screen cls clear clear Platform-specific commands
Terminal Reset stty sane stty sane Unix-only feature
Size Detection Falls back to 80×24
Set Title title ✅ ANSI ✅ ANSI May not work in all terminals
Progress in Title Uses set title functionality
Color Support Based on TTY detection
256/True Color Detection Via environment variables
Cursor Control ✅ ANSI ✅ ANSI Unix-only, uses escape sequences
Hyperlinks (OSC 8) ⚠️ Terminal-dependent support
Terminal Bell May be disabled in settings
Alternate Screen ✅ ANSI ✅ ANSI Unix-only feature
Terminal Info Environment detection
Asyncio Cleanup Cross-platform

Best Practices

1. Always Restore on Exit

try:
    run_application()
finally:
    restore_terminal()

2. Handle Non-TTY Environments

import sys

if not sys.stdout.isatty():
    print("Warning: Not running in a terminal")
    # Disable color output, interactive features

3. Test Terminal Capabilities

def setup_ui():
    if TerminalManager.supports_color():
        enable_rich_output()
    else:
        use_plain_text_mode()
    
    cols, rows = get_terminal_size()
    if cols < 80:
        use_narrow_layout()

4. Graceful Degradation

try:
    set_terminal_title("My App")
except Exception:
    # Some terminals don't support title changes
    pass

5. Write Theme-Agnostic Code

The UI system automatically handles theme differences. Never check themes in application code:

# ✅ GOOD - Theme-agnostic
from rich.table import Table
from mcp_cli.ui.output import get_output

ui = get_output()

# Create table normally
table = Table(show_header=True, title="Status")
table.add_column("Property")
table.add_column("Value")
table.add_row("Size", f"{cols}×{rows}")
table.add_row("Color", "Supported" if has_color else "Not supported")

# Let the UI system handle theme-appropriate rendering
ui.print_table(table)  # Automatically converts to text for minimal/terminal themes

# ❌ BAD - Theme-aware (don't do this!)
if theme.name == "minimal":
    print(f"Size: {cols}×{rows}")
else:
    # Create rich table
    ...

The output system automatically:

  • Renders Rich tables with full formatting for default/dark/light themes
  • Converts tables to aligned plain text for minimal/terminal themes
  • Handles all theme-specific formatting internally

Demo Script

A comprehensive terminal demo script is available:

# Run the terminal management demo
uv run examples/ui_terminal_demo.py

The demo includes:

  1. Terminal Information - Display comprehensive terminal details
  2. Clear Screen - Platform-specific screen clearing
  3. Terminal Title - Dynamic title updates
  4. Terminal Reset - Reset to sane defaults (Unix)
  5. Size Detection - Monitor terminal resizing
  6. Color Support - Check color capabilities (16/256/truecolor)
  7. Cursor Control - Hide/show, move, save/restore cursor position
  8. Hyperlinks - Clickable links in supported terminals
  9. Terminal Bell - System beep/flash demonstration
  10. Alternate Screen - Full-screen buffer like vim/less
  11. Asyncio Tasks - Task management and cleanup
  12. Theme Integration - Terminal features with themes
  13. Stress Test - Rapid terminal operations

Error Handling

All terminal operations handle errors gracefully:

  • Missing TTY: Operations degrade gracefully
  • Permission Errors: Logged but don't crash
  • Platform Limitations: Fallback behaviors provided
  • Asyncio Errors: Caught and logged during cleanup

Related Documentation

  • UI Themes - Theme system that works with terminal features
  • Output System - Rich terminal output
  • Prompts - Interactive terminal prompts
  • Testing - Terminal management tests (99% coverage)