Generated: 2026-01-05 Context: Python 3.12+, Textual TUI, AsyncIO, AI Integration
Null Terminal is a cyber-noir TUI that blends a standard shell with AI capabilities (Ollama, OpenAI, Anthropic). Built on Textual, it uses a "Block" architecture where command outputs and AI responses are distinct, interactive widgets.
.
├── main.py # Entry point (uv run main.py)
├── app.py # NullApp orchestrator & event loop
├── nullrc.py # Local config manager (.nullrc)
├── executor.py # PTY/Process execution engine
├── ai/ # AI Providers (Ollama, OpenAI, etc.)
├── commands/ # Slash command implementations
├── config/ # Settings, Storage (SQLite), Encryption
├── handlers/ # Input & Execution logic (Complexity Hotspot)
├── screens/ # Modal UI screens (Settings, Providers)
├── styles/ # TCSS stylesheets (main.tcss)
├── widgets/ # UI Components
│ └── blocks/ # Core Block architecture
└── tests/ # Pytest suite (Unit + Integration)
| Task | Location | Notes |
|---|---|---|
| New Slash Command | commands/ |
Prefix cmd_, register in handler.py |
| New AI Provider | ai/ |
Inherit LLMProvider, register in factory.py |
| UI Styling | styles/main.tcss |
Use variables ($primary), avoid hardcoding |
| AI logic/flow | handlers/execution.py |
Tool calling loop, streaming, agent mode |
| Configuration | config/ |
ai.py (settings), storage.py (DB) |
| Terminal Compat | utils/terminal.py |
Adapters for Kitty, WezTerm, etc. |
| Symbol | Type | Location | Role |
|---|---|---|---|
NullApp |
Class | app.py |
Main TUI application, global state |
LLMProvider |
ABC | ai/base.py |
Base for all AI integrations |
BaseBlockWidget |
Class | widgets/blocks/base.py |
Parent of all output blocks |
ExecutionHandler |
Class | handlers/execution.py |
Manages AI/Command execution flow |
SlashCommandHandler |
Class | commands/handler.py |
Routes /commands to methods |
- Flat Layout: No
src/directory. Root contains app modules. - Async First: Extensive use of
asyncio. Tests usepytest-asyncio. - Textual Reactivity: UI state driven by
reactiveproperties. - Tool Use: AI tools defined in
commands/mcp.pyor internal handlers. - Testing:
mock_homefixture is MANDATORY to protect user config.
- No Global Config: Do NOT touch
~/.nullin tests; use fixtures. - No Synchronous AI: All AI calls must be async/streaming.
- No Direct Styles: Do NOT hardcode colors in Python; use TCSS classes.
- No Print: Use
self.notify()orself.log(); stdout is captured. - No NPM in Core: npm/npx are only for MCP servers (external processes), not app logic.
uv run main.py # Run App
uv run pytest tests/ # Run Tests
uv run ruff check . # Lint
uv run textual console # Debug ConsoleNull Terminal implements the Model Context Protocol for extensible tool integration. MCP servers are external processes that provide tools and resources the AI can use.
mcp/
├── manager.py # MCPManager - multi-server lifecycle
├── client.py # MCPClient - per-server connection
├── config.py # Server configuration (mcp.json)
└── catalog.py # Pre-configured server catalog
| Component | Role |
|---|---|
MCPManager |
Manages all server connections, aggregates tools |
MCPClient |
Single server connection via JSON-RPC over stdio |
MCPConfig |
Loads/saves ~/.null/mcp.json |
MCPTool |
Tool definition (name, description, schema) |
{
"servers": {
"brave-search": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-server-brave"],
"env": {"BRAVE_API_KEY": "..."},
"enabled": true
}
}
}MCPManager.initialize()- Connects to all enabled serversget_all_tools()- Aggregates tools from all servers- AI generates
tool_callwith MCP tool name AIExecutorroutes toMCPManager.call_tool()- Result fed back to AI
Built-in (tools/builtin.py) |
MCP (mcp/) |
|---|---|
run_command, read_file, write_file |
External server tools |
| Always available | Require server connection |
| No prefix | Usually no prefix (merged) |
# Via command
/mcp add
# Via catalog
/mcp catalogManagers handle stateful subsystems independently from UI.
| Manager | Location | Responsibility |
|---|---|---|
AIManager |
ai/manager.py |
Provider lifecycle, model discovery |
MCPManager |
mcp/manager.py |
MCP server connections |
ProcessManager |
managers/process.py |
Background PTY processes |
AgentManager |
managers/agent.py |
Agent session state/history |
BranchManager |
managers/branch.py |
Conversation branching |
Managers are initialized in NullApp.__init__() and accessed via self.app.*_manager.
handlers/ai_executor.pyis the complexity hotspot. Modify with care.- Integration tests use
pilotpattern (Textual) and requiremock_ai_components. - See
docs/ARCHITECTURE.mdfor detailed system diagrams.