headless-terminal
Headless Terminal Implementation
Overview
This skill guides the implementation of headless terminal interfaces—programmatic wrappers that control shell sessions without a visible terminal UI. These implementations typically involve spawning shell processes, sending input (keystrokes, commands), and capturing output.
Approach
Step 1: Understand the Interface Contract
Before implementing, thoroughly read and understand the interface to be implemented:
- Identify all required methods and their signatures
- Note return types and expected behaviors
- Check for optional methods or features
- Look for existing implementations or tests that clarify expected behavior
Step 2: Select the Underlying Library
Common libraries for terminal emulation in Python:
| Library | Best For | Trade-offs |
|---|---|---|
pexpect |
Interactive terminal emulation, pattern matching | Higher-level API, good for expect/send patterns |
pty |
Low-level pseudo-terminal control | More control, but more boilerplate |
subprocess |
Simple non-interactive commands | Limited for true terminal emulation |
Decision criteria to document:
- Does the task require interactive input (passwords, prompts)? →
pexpect - Does the task need low-level terminal control? →
pty - Is it purely non-interactive command execution? →
subprocessmay suffice
Document the choice explicitly with reasoning for future maintainability.
Step 3: Handle Shell Configuration
When spawning a shell process, consider these configuration options:
Interactive mode (-i flag):
- Ensures startup files (
.bashrc,.bash_profile) are sourced - Affects signal handling behavior
- Required if tests verify startup file sourcing
Echo behavior:
echo=Falsein pexpect prevents input from appearing in output- Consider whether the use case needs to see echoed input
Terminal dimensions:
- Default
(24, 80)is standard terminal size - May affect output formatting for commands that detect terminal width
- Document why specific dimensions are chosen
Step 4: Implement Core Functionality
Focus on implementing exactly what the interface requires:
- Process lifecycle management - Spawn, check liveness, terminate
- Input handling - Send keystrokes, handle special keys (Ctrl+C, etc.)
- Output capture - Read available output, handle buffering
- Resource cleanup - Proper termination, context manager support
Step 5: Address Edge Cases
Explicitly handle these scenarios:
| Edge Case | Handling Strategy |
|---|---|
| Shell exits unexpectedly | Check process liveness before operations |
| Long-running commands | Configurable timeouts, background execution support |
| Non-ASCII characters | Explicit encoding handling (UTF-8 typically) |
| Race conditions | Appropriate delays between send and read operations |
| Command cancellation | Proper signal handling (SIGINT for Ctrl+C) |
Verification Strategies
Testing Approach
Create focused tests for each capability:
- Basic functionality - Import, instantiation
- Non-interactive commands - Simple command execution and output
- Interactive commands - Commands requiring input
- Signal handling - Ctrl+C cancellation
- Shell state - Variable persistence, environment
- Startup configuration - Verify startup files are sourced
- Background execution - Long-running command handling
Verification Guidelines
- Run the test suite to verify all functionality
- Avoid redundant manual verification after tests pass
- If a single comprehensive test file exists, use it rather than creating separate verification scripts
Common Pitfalls
Scope Management
- Implement only what is required - Avoid adding unrequested features like extra utility methods
- If additional methods seem useful, note them but don't implement unless asked
- Context manager support is often expected but verify it's in the interface
Efficiency
- Avoid redundant searches - If searching for a class, one targeted search is sufficient
- Don't duplicate verification - If tests pass, additional manual checks are unnecessary
- Don't create example files unless explicitly requested
Documentation Gaps
Common decisions that should be documented but often aren't:
- Why the chosen library over alternatives
- Why specific shell flags are used
- Why specific default values were selected
- Thread safety considerations (or explicit statement that it's not thread-safe)
Error Handling
Consider and document handling for:
- Invalid keystroke sequences
- Terminal process termination
- Timeout scenarios
- Permission issues
Implementation Checklist
Before considering the implementation complete:
- All interface methods are implemented
- Library choice is documented with reasoning
- Configuration decisions are justified
- Edge cases are explicitly handled or documented as out of scope
- Tests cover all required functionality
- No unrequested features were added
- Resource cleanup is properly implemented