---
title: Core Patterns
description: Fundamental architectural patterns used throughout Kollabor CLI
category: architecture
status: stable
last_updated: 2026-01-24
version: 1.0.0
tags:
  - level: intermediate
  - component: architecture
  - status: stable
---

# Core Patterns

Fundamental architectural patterns used throughout Kollabor CLI. These patterns form the foundation of the application's design.

## Event-Driven Architecture

### Overview
All components communicate through a central event bus with hooks. This enables loose coupling and extensibility.

### Components

**EventBus**: Central coordinator for event emission and hook processing
- Coordinates between specialized components (HookRegistry, HookExecutor, EventProcessor)
- Provides `emit_with_hooks()` for event processing
- Manages pre/post event phase mappings

**HookRegistry**: Manages hook organization and lifecycle
- Registers/unregisters hooks by event type
- Maintains hooks in priority-sorted order
- Provides hook status summaries

**HookExecutor**: Executes individual hooks with safety guards
- Enforces timeouts (default 30s, configurable per hook)
- Implements retry logic with exponential backoff
- Per-hook asyncio locks prevent race conditions

**EventProcessor**: Handles pre/main/post phase processing
- Three-phase processing: PRE → MAIN → POST
- Transforms event data through phases
- Supports event cancellation

### Event Types (68 total)

**User Input (3):** `USER_INPUT_PRE`, `USER_INPUT`, `USER_INPUT_POST`

**Key Press (3):** `KEY_PRESS_PRE`, `KEY_PRESS`, `KEY_PRESS_POST`

**Paste (1):** `PASTE_DETECTED`

**LLM (8):** `LLM_REQUEST_PRE`, `LLM_REQUEST`, `LLM_REQUEST_POST`, `LLM_RESPONSE_PRE`, `LLM_RESPONSE`, `LLM_RESPONSE_POST`, `LLM_THINKING`, `CANCEL_REQUEST`

**Tool (3):** `TOOL_CALL_PRE`, `TOOL_CALL`, `TOOL_CALL_POST`

**MCP (10):** `MCP_SERVER_CONNECT`, `MCP_SERVER_CONNECTED`, `MCP_SERVER_DISCONNECT`, `MCP_SERVER_ERROR`, `MCP_SERVER_DISCOVER`, `MCP_SERVER_DISCOVERED`, `MCP_TOOL_REGISTER`, `MCP_TOOL_UNREGISTER`, `MCP_TOOL_CALL_PRE`, `MCP_TOOL_CALL_POST`

**Permission (4):** `PERMISSION_CHECK`, `PERMISSION_GRANTED`, `PERMISSION_DENIED`, `PERMISSION_CONFIRMATION`

**System (4):** `SYSTEM_STARTUP`, `SYSTEM_READY`, `SYSTEM_SHUTDOWN`, `RENDER_FRAME`

**Input Render (3):** `INPUT_RENDER_PRE`, `INPUT_RENDER`, `INPUT_RENDER_POST`

**Command Menu (7):** `COMMAND_MENU_SHOW`, `COMMAND_MENU_NAVIGATE`, `COMMAND_MENU_SELECT`, `COMMAND_MENU_HIDE`, `COMMAND_MENU_RENDER`, `COMMAND_MENU_FILTER`, `COMMAND_MENU_ACTIVATE`

**Status (3):** `STATUS_VIEW_CHANGED`, `STATUS_CONTENT_UPDATE`, `STATUS_BLOCK_RESIZE`

**Slash Command (4):** `SLASH_COMMAND_DETECTED`, `SLASH_COMMAND_EXECUTE`, `SLASH_COMMAND_COMPLETE`, `SLASH_COMMAND_ERROR`

**Command Output (1):** `COMMAND_OUTPUT_DISPLAY`

**Message Inject (3):** `ADD_MESSAGE`, `PRE_MESSAGE_INJECT`, `POST_MESSAGE_INJECT`

**LLM Control (1):** `TRIGGER_LLM_CONTINUE`

**Modal (8):** `MODAL_TRIGGER`, `STATUS_MODAL_TRIGGER`, `STATUS_MODAL_RENDER`, `LIVE_MODAL_TRIGGER`, `MODAL_COMMAND_SELECTED`, `MODAL_SHOW`, `MODAL_HIDE`, `MODAL_SAVE`

**Rendering (3):** `PAUSE_RENDERING`, `RESUME_RENDERING`, `FULLSCREEN_INPUT`

**Status Takeover (4):** `STATUS_TAKEOVER_START`, `STATUS_TAKEOVER_NAVIGATE`, `STATUS_TAKEOVER_ACTION`, `STATUS_TAKEOVER_END`

**Help (2):** `SHOW_HELP_OVERLAY`, `SHOW_FIRST_RUN_HELP`

**Shell Command (4):** `SHELL_COMMAND_PRE`, `SHELL_COMMAND_POST`, `SHELL_COMMAND_ERROR`, `SHELL_COMMAND_CANCEL`

**Widget (4):** `WIDGET_SELECTED`, `WIDGET_ACTIVATED`, `WIDGET_ACTION_EXECUTED`, `WIDGET_DEACTIVATED`

### Hook Registration Pattern

```python
from kollabor.events import EventType, Hook, HookPriority

async def register_hooks(self):
    """Register plugin hooks with the event bus."""
    hook = Hook(
        name="my_hook",
        plugin_name="MyPlugin",
        event_type=EventType.USER_INPUT_PRE,
        priority=HookPriority.NORMAL,
        callback=self.on_user_input
    )
    await self.event_bus.register_hook(hook)
```

### Related Documentation
- [Event System Architecture](architecture/event-system.md)
- [Hook System SDK](hook-system-sdk.md)
- [Event Types Reference](event-types-reference.md)

---

## Plugin Ecosystem

### Overview
Modular extension system with dynamic discovery, instantiation, and lifecycle management.

### Plugin Lifecycle

1. **Discovery**: Scan `plugins/` directory for `*_plugin.py` files
2. **Loading**: Import modules and extract plugin classes
3. **Instantiation**: Create plugin instances with dependency injection
4. **Initialization**: Call `initialize()` with managers and services
5. **Hook Registration**: Call `register_hooks()` to attach to event bus
6. **Operation**: Plugin active, receiving events via hooks
7. **Shutdown**: Call `shutdown()` for cleanup

### Plugin Discovery

**PluginDiscovery**: File system scanning and module loading
- Scans recursively for `*_plugin.py` files
- Validates plugin names and paths for security
- Loads modules and extracts plugin classes
- Supports both file-based and directory-based plugins

**PluginFactory**: Plugin instantiation with dependencies
- Instantiates plugins with injected dependencies
- Passes event_bus, config, renderer, and other services
- Handles instantiation errors gracefully

**PluginStatusCollector**: Status aggregation from plugin instances
- Collects startup information from plugins
- Aggregates status lines for display
- Provides statistics for monitoring

### Plugin Interface

Plugins implement these methods:

```python
class MyPlugin:
    @staticmethod
    def register_cli_args(parser):
        """Register custom CLI arguments."""
        pass

    @staticmethod
    def handle_early_args(args):
        """Handle args that should exit before app starts."""
        return False

    async def initialize(self, args, event_bus, config, **kwargs):
        """Initialize the plugin with dependencies."""
        pass

    async def register_hooks(self):
        """Register plugin hooks with the event bus."""
        pass

    async def shutdown(self):
        """Cleanup resources before shutdown."""
        pass

    @staticmethod
    def get_default_config():
        """Get default configuration values."""
        return {}

    @staticmethod
    def get_startup_info(config):
        """Get startup information strings."""
        return []

    @staticmethod
    def get_config_widgets():
        """Get configuration widget definitions."""
        return {}
```

### Plugin Contributions

Plugins can contribute:

1. **Hooks**: Register callbacks for 30+ event types
2. **Slash Commands**: Register commands via `SlashCommandRegistry`
3. **Status Widgets**: Register custom widgets via `StatusWidgetRegistry`
4. **Configuration**: Merge default configs via `get_default_config()`
5. **Fullscreen Plugins**: Register fullscreen experiences
6. **System Prompts**: Add to system prompt via `llm_service.set_plugin_instances()`

### Security

- Plugin name validation (alphanumeric + underscores only)
- Path traversal prevention
- Blocked dangerous names
- Shell metacharacter detection
- File permission verification (Unix-only)

### Related Documentation
- [Plugin System Architecture](core-systems/plugin-system.md)
- [Plugin Development Tutorial](plugin-development/plugin-development-tutorial.md)

---

## Slash Command System

### Overview
Command-based workflow control with multiple interaction modes and event integration.

### Components

**SlashCommandRegistry**: Manages command registration and lookup
- Registers commands with conflict detection
- Tracks aliases and categories
- Organizes commands by plugin and category
- Provides search with priority matching

**SlashCommandExecutor**: Handles command execution
- Parses and validates commands
- Executes handlers (sync/async)
- Emits lifecycle events
- Handles errors and displays results

**SlashCommandParser**: Parses user input into commands
- Detects slash prefix
- Extracts command name and arguments
- Validates command format

### Command Modes

| Mode | Description | Example |
|------|-------------|---------|
| `INSTANT` | Executes immediately, no UI | `/clear` |
| `MENU_POPUP` | Shows command menu overlay | `/` |
| `STATUS_TAKEOVER` | Takes over status area | `/agents` |
| `STATUS_MODAL` | Modal within status area | `/config` |
| `INLINE_INPUT` | Inline parameters | `/save filename.md` |
| `MODAL` | Full modal overlay | `/profile edit` |
| `LIVE_MODAL` | Live-updating modal | `/t view dev` |

### Command Categories

- `SYSTEM`: Core commands (config, help, quit)
- `CONVERSATION`: Conversation management (save, resume, clear)
- `AGENT`: Agent management (create, select, skill)
- `DEVELOPMENT`: Development tools (debug, profile)
- `FILE`: File operations (read, write, delete)
- `TASK`: Task management (run, list, status)
- `CUSTOM`: Plugin-provided commands

### Command Registration Pattern

```python
from kollabor.commands import CommandDefinition, CommandCategory, CommandMode

def register_commands(self):
    """Register slash commands."""
    command = CommandDefinition(
        name="my_command",
        description="My custom command",
        handler=self.handle_my_command,
        plugin_name="MyPlugin",
        aliases=["mc", "mycmd"],
        mode=CommandMode.INSTANT,
        category=CommandCategory.CUSTOM,
        parameters=[
            ParameterDefinition(
                name="param1",
                type="string",
                description="Parameter description",
                required=True
            )
        ]
    )
    self.command_registry.register_command(command)
```

### Event Integration

Commands emit events:
- `SLASH_COMMAND_DETECTED`: When command detected
- `SLASH_COMMAND_EXECUTE`: Before execution
- `SLASH_COMMAND_COMPLETE`: After execution
- `SLASH_COMMAND_ERROR`: On error

### Related Documentation
- [Slash Commands Guide](slash-commands-guide.md)

---

## Configuration System

### Overview
Layered configuration management with hot reload, file watching, and multi-source merging.

### Configuration Layers (Priority: High → Low)

1. **Environment Variables**: Runtime overrides (highest priority)
2. **Local Project Config**: `.kollabor-cli/config.json` (project-specific)
3. **Global Config**: `~/.kollabor-cli/config.json` (user-specific)
4. **Plugin Defaults**: Default configs from plugins
5. **System Defaults**: Base application defaults (lowest priority)

### Components

**ConfigService**: High-level API for configuration operations
- Coordinates ConfigManager and ConfigLoader
- Provides dot notation access (`get("terminal.render_fps")`)
- Handles hot reload and file watching
- Validates configuration on changes

**ConfigManager**: File operations and in-memory storage
- Reads/writes JSON configuration files
- Implements dot notation for nested keys
- Manages configuration in memory
- Provides backup/restore functionality

**ConfigLoader**: Multi-source configuration loading
- Loads from multiple sources
- Merges configurations using deep merge
- Handles plugin config injection
- Validates merged configuration

**LLMTaskConfig**: Task-specific configuration
- Manages LLM-specific settings
- Handles profile configurations
- Provides task-based config access

### Hot Reload

Configuration automatically reloads when file changes:
- File watching via `watchdog` library
- Debouncing (500ms) to prevent rapid reloads
- Validation before applying changes
- Callback notification system

### Configuration Access Pattern

```python
# Get configuration value
render_fps = self.config.get("terminal.render_fps", 20)

# Set configuration (in-memory only)
self.config.set("terminal.render_fps", 30)

# Persist to disk
self.config.save_config()

# Reload from file
self.config.reload()

# Register reload callback
self.config.register_reload_callback(self._on_config_reload)
```

### Related Documentation
- [Configuration Management System](config-management-system.md)

---

## Async Patterns

### Overview
Async/await patterns throughout all services for responsive performance.

### Key Patterns

**Constructor Async**: Async initialization methods
- `async def initialize()` for plugins
- `async def start()` for application
- `async def shutdown()` for cleanup

**Background Tasks**: Tracked async tasks with cleanup
```python
task = self.create_background_task(
    self._render_loop(),
    "render_loop"
)
```

**Proper Cleanup**: Shutdown methods await all resources
```python
async def shutdown(self):
    """Clean up all async resources."""
    await self.input_handler.stop()
    await self.llm_service.shutdown()
    await self.conversation_logger.shutdown()
```

**Error Handling**: Try/except with logging everywhere
```python
try:
    result = await some_async_operation()
except Exception as e:
    logger.error(f"Operation failed: {e}")
    raise
```

**Task Tracking**: Prevent orphaned tasks
- All background tasks tracked in `self._background_tasks`
- Tasks cancelled on shutdown
- Cleanup callbacks remove completed tasks

### Related Documentation
- [Event System Architecture](architecture/event-system.md)

---

## Dependency Injection

### Overview
Components receive dependencies at initialization for testability and flexibility.

### Injection Points

**Constructor Injection**: Dependencies passed to `__init__`
```python
class MyService:
    def __init__(self, event_bus, config, renderer):
        self.event_bus = event_bus
        self.config = config
        self.renderer = renderer
```

**Late Binding**: Circular dependencies resolved after init
```python
# In application.py
self.input_handler.command_registry = self.command_registry
self.system_commands.llm_service = self.llm_service
```

**Global State**: Terminal state set globally for access
```python
from kollabor.io.terminal_state import set_global_terminal_state, get_global_width
set_global_terminal_state(terminal_state)
```

### Benefits

- **Testability**: Mock dependencies for unit tests
- **Flexibility**: Swap implementations without changing code
- **Loose Coupling**: Components don't create their own dependencies
- **Clear Interfaces**: Dependencies explicit in constructor

---

## Terminal Rendering System

### Overview
Modular rendering system with visual effects, layout management, and message coordination.

### Components

**TerminalRenderer**: Main rendering coordinator
- Manages render loop timing (20 FPS target)
- Handles render caching to prevent unnecessary writes
- Coordinates with message display system
- Manages thinking animations

**LayoutManager**: Screen layout and sizing
- Calculates layout areas (input, status, messages)
- Handles terminal resize events
- Manages thinking animation positioning

**MessageRenderer**: Message formatting and display
- Renders markdown and code blocks
- Applies visual effects (gradients, colors)
- Handles message wrapping and truncation

**VisualEffects**: Centralized visual styling
- Generates gradients
- Manages shimmer animations
- Applies color themes

**MessageDisplayCoordinator**: Unified message handling
- Coordinates message rendering across components
- Handles hook messages and system messages
- Manages message display queue

### Render Loop Pattern

```python
async def _render_loop(self):
    """Main rendering loop for status updates."""
    while self.running:
        try:
            # Render active area (status views)
            await self.renderer.render_active_area()
            
            # Use configured FPS for timing
            render_fps = self.config.get("terminal.render_fps", 20)
            await asyncio.sleep(1.0 / render_fps)
        except Exception as e:
            logger.error(f"Render loop error: {e}")
            await asyncio.sleep(0.1)  # Error delay
```

### Related Documentation
- [Terminal Rendering System](core-systems/terminal-rendering.md)
- [Event-Driven Rendering](event-driven-rendering.md)

---

## LLM Service Architecture

### Overview
Orchestration layer for language model communication, tool execution, and conversation management.

### Components

**LLMService**: Core orchestrator
- Manages conversation flow
- Coordinates tool execution
- Handles streaming responses
- Integrates with event bus

**APICommunicationService**: HTTP communication
- Async API calls to LLM providers
- Connection pooling
- Error handling and retry logic
- Streaming response handling

**ToolExecutor**: Tool orchestration
- Native tool calling (terminal, file operations)
- MCP tool execution
- Permission gates for destructive operations
- Batch tool execution

**MCPIntegration**: Model Context Protocol
- MCP server management
- Tool discovery and registration
- Resource access via MCP

**ConversationManager**: Conversation state
- Message history with UUID tracking
- Context window management
- Conversation persistence
- Resume functionality

**ProfileManager**: LLM profiles
- Multiple LLM provider support
- Profile configuration
- Dynamic profile switching
- Environment variables per profile

**AgentManager**: Agent system
- Multiple agent support
- Agent discovery and loading
- Agent-specific system prompts
- Skill-based agent composition

### Permission System

Risk-based approval for destructive operations:
- **RiskAssessor**: Evaluates operation risk level
- **PermissionManager**: Manages approval workflow
- **PermissionHook**: Integrates with event system
- UI integration via modal prompts

### Related Documentation
- [LLM Service Architecture](llm-integration/llm-service.md)
- [LLM Message Flow](llm-message-flow.md)

---

## Status Widget System

### Overview
Dynamic widget-based status area with plugin extensibility and script support.

### Components

**StatusWidgetRegistry**: Widget registration
- Registers widgets by ID
- Manages widget metadata
- Provides widget lookup

**StatusLayoutManager**: Widget layout arrangement
- Manages widget positions
- Handles layout persistence
- Supports dynamic layouts

**StatusLayoutRenderer**: Widget rendering
- Renders widgets in status area
- Handles widget updates
- Manages widget interactions

**ScriptWidgetManager**: Script-based widgets
- Manages widget scripts in `~/.kollabor-cli/status-widgets/`
- Handles refresh scheduling
- Supports custom widget implementations

### Widget API

Plugins can register custom widgets:

```python
widget_api = self.app.get_widget_api()
if widget_api:
    widget_api.register_widget(
        id="my-widget",
        name="My Widget",
        description="Shows custom information",
        render_fn=self._render_widget,
    )
```

### Related Documentation
- [Widget Status System](widget-status-system.md)
- [Widget Development Guide](../guides/widget-development-guide.md)

---

## Code Organization

### Directory Structure

```
kollabor/
├── kollabor/                    # Core application modules
│   ├── application.py        # Main orchestrator
│   ├── cli.py              # CLI entry point
│   ├── commands/            # Slash command system
│   ├── config/             # Configuration management
│   ├── events/             # Event bus and hooks
│   ├── io/                 # Terminal I/O
│   ├── llm/                # LLM services
│   ├── plugins/            # Plugin system
│   ├── ui/                 # UI components
│   └── utils/              # Utilities
├── plugins/                # Plugin implementations
├── agents/                 # Agent definitions
├── docs/                   # Documentation
└── tests/                  # Test suite
```

### File Organization

- **325 Python files** across all subsystems
- Each subsystem has clear responsibilities
- Heavy use of dataclasses for models (Event, Hook, CommandDefinition)
- Type hints throughout with `typing` module

### Entry Points

- `main.py`: Delegates to `kollabor/cli.py` (development)
- `kollabor/cli.py`: `cli_main()` - argument parsing and initialization
- `kollabor/application.py`: `TerminalLLMChat` - main orchestrator

---

## Type Hints

### Overview
Extensive use of Python typing for better IDE support and runtime validation.

### Common Patterns

```python
from typing import Dict, List, Optional, Union, Callable, Any

# Dictionary with string keys and any values
def get_config(self) -> Dict[str, Any]:
    pass

# Optional parameter
def process(self, value: Optional[str]) -> None:
    pass

# Union types
event_type: Union[EventType, str]

# Callable type
callback: Callable[[Dict[str, Any]], Dict[str, Any]]

# List of specific type
hooks: List[Hook]

# Optional with default
timeout: Optional[int] = None
```

### Type Checking

Supported type checking with mypy:
```bash
python -m mypy kollabor/ plugins/
```

---

## Design Principles

### Separation of Concerns
Each component has a single, well-defined responsibility.

### Loose Coupling
Components communicate through events and well-defined interfaces.

### Extensibility
Plugins can extend functionality without modifying core code.

### Testability
Dependency injection and event-driven design enable easy testing.

### Performance
Async/await patterns throughout, caching where appropriate.

### Safety
Validation, error handling, and permission gates everywhere.

---

**Last Updated**: 2026-01-24  
**Status**: Stable  
**Version**: 1.0.0
