# Hook System SDK Documentation

The Kollabor CLI application provides a comprehensive hook system that allows plugins to intercept, modify, and respond to every aspect of the application's operation. This SDK documentation covers everything you need to build powerful plugins using the hook system.

## Table of Contents

- [Overview](#overview)
- [Core Concepts](#core-concepts)
- [Hook Architecture](#hook-architecture)
- [Event Types](#event-types)
- [Hook Priorities](#hook-priorities)
- [Creating Hooks](#creating-hooks)
- [Plugin Integration](#plugin-integration)
- [Plugin Communication & Service Discovery](#plugin-communication--service-discovery)
- [Core Extension Points](#core-extension-points)
  - [DisplayFilterRegistry](#displayfilterregistry---message-content-transformation)
  - [force_continue](#force_continue---llm-continuation-control)
  - [Event Timing](#event-timing---pre-display-hooks)
- [Advanced Patterns](#advanced-patterns)
- [Best Practices](#best-practices)
- [Troubleshooting](#troubleshooting)

## Overview

The hook system is built on the principle **"everything has a hook"**. Every significant operation in the application triggers events that plugins can intercept:

- **User input processing**
- **LLM request/response cycles**
- **Terminal rendering**
- **Tool execution**
- **System lifecycle events**

This provides maximum customization - even core LLM behavior can be completely replaced or enhanced through plugins.

## Core Concepts

### Event-Driven Architecture

The system uses a **three-phase processing model**:

```
PRE Phase → MAIN Phase → POST Phase
```

1. **PRE Phase**: Preprocessing, validation, data transformation
2. **MAIN Phase**: Core event processing
3. **POST Phase**: Post-processing, cleanup, logging, display updates

### Hook Lifecycle

```
Plugin Registration → Hook Creation → Event Bus Registration → Event Triggered → Hook Execution
```

### Error Handling & Cancellation

- **Event Cancellation**: Any hook can cancel the entire event chain
- **Error Actions**: `"continue"` (default) or `"stop"` on hook failure
- **Timeouts**: Configurable per-hook execution timeouts
- **Status Tracking**: Real-time monitoring of hook execution status

## Hook Architecture

### Core Components

The hook system consists of specialized components in `kollabor/events/`:

- **`EventBus`**: Central coordinator for hook registration and event processing
- **`HookRegistry`**: Manages hook organization by event type and priority
- **`HookExecutor`**: Executes individual hooks with timeout/error handling
- **`EventProcessor`**: Handles pre/main/post event processing phases

### Data Models

```python
@dataclass
class Hook:
    name: str                    # Unique hook name
    plugin_name: str            # Owning plugin name
    event_type: EventType       # Event type to listen for
    priority: int               # Execution order (higher = earlier)
    callback: Callable          # Async function to execute
    enabled: bool = True        # Can be toggled on/off
    timeout: int = 30           # Max execution time in seconds
    error_action: str = "continue"  # "continue" or "stop" on error
    status: HookStatus = HookStatus.PENDING
```

## Event Types

### User Interaction Events

```python
# User input processing
USER_INPUT_PRE      # Before input processing
USER_INPUT          # Main input processing
USER_INPUT_POST     # After input processing

# Keyboard events
KEY_PRESS_PRE       # Before key processing
KEY_PRESS           # Main key processing
KEY_PRESS_POST      # After key processing

# Paste detection
PASTE_DETECTED      # When paste operation detected
```

### LLM Operation Events

```python
# Request lifecycle
LLM_REQUEST_PRE     # Before LLM request
LLM_REQUEST         # Main LLM request processing
LLM_REQUEST_POST    # After LLM request

# Response lifecycle
LLM_RESPONSE_PRE    # Before LLM response processing
LLM_RESPONSE        # Main LLM response processing (fires BEFORE display)
LLM_RESPONSE_POST   # After LLM response processing

# Special LLM events
LLM_THINKING        # During LLM thinking/processing
CANCEL_REQUEST      # When LLM request cancelled
```

### Tool & System Events

```python
# Tool execution
TOOL_CALL_PRE       # Before tool execution
TOOL_CALL           # Main tool execution
TOOL_CALL_POST      # After tool execution

# System lifecycle
SYSTEM_STARTUP      # Application startup
SYSTEM_SHUTDOWN     # Application shutdown
RENDER_FRAME        # Terminal render frame
```

### UI & Rendering Events

```python
# Input rendering
INPUT_RENDER_PRE    # Before input rendering
INPUT_RENDER        # Main input rendering
INPUT_RENDER_POST   # After input rendering

# Rendering control
PAUSE_RENDERING     # Pause render loop
RESUME_RENDERING    # Resume render loop
FULLSCREEN_INPUT    # Fullscreen input mode

# Note: Use DisplayFilterRegistry for message content transformation (see Core Extension Points)
```

### Command Menu Events

```python
# Basic menu events
COMMAND_MENU_SHOW      # Menu display
COMMAND_MENU_NAVIGATE  # Menu navigation
COMMAND_MENU_SELECT    # Menu selection
COMMAND_MENU_HIDE      # Menu hiding
COMMAND_MENU_RENDER    # Menu rendering
COMMAND_MENU_FILTER    # Menu filtering
COMMAND_MENU_ACTIVATE  # Menu activation
```

### Slash Command Events

```python
SLASH_COMMAND_DETECTED  # Slash command detected in input
SLASH_COMMAND_EXECUTE   # Slash command execution
SLASH_COMMAND_COMPLETE  # Slash command completed
SLASH_COMMAND_ERROR     # Slash command error
COMMAND_OUTPUT_DISPLAY  # Command output display
```

### Modal Events

```python
MODAL_TRIGGER           # Modal triggered
MODAL_SHOW              # Modal shown
MODAL_HIDE              # Modal hidden
MODAL_SAVE              # Modal save action
MODAL_COMMAND_SELECTED  # Command selected in modal

# Status modal events
STATUS_MODAL_TRIGGER    # Status modal triggered
STATUS_MODAL_RENDER     # Status modal rendering
LIVE_MODAL_TRIGGER      # Live-updating modal (e.g., tmux view)
```

### Status Area Events

```python
# Status display
STATUS_VIEW_CHANGED     # Status view changed
STATUS_CONTENT_UPDATE   # Status content updated
STATUS_BLOCK_RESIZE     # Status block resized

# Status takeover (plugin takes over status area)
STATUS_TAKEOVER_START    # Takeover started
STATUS_TAKEOVER_NAVIGATE # Navigation during takeover
STATUS_TAKEOVER_ACTION   # Action during takeover
STATUS_TAKEOVER_END      # Takeover ended
```

### Message Injection Events (SDK Extension Points)

These are core extension points that any plugin can use for message injection and LLM continuation:

```python
ADD_MESSAGE             # Add message to conversation
PRE_MESSAGE_INJECT      # Before message injection (hook opportunity)
POST_MESSAGE_INJECT     # After message injection (hook opportunity)
TRIGGER_LLM_CONTINUE    # Trigger LLM to continue after injection
```

### Plugin-Specific Events (String Literals)

Plugins can emit custom events using string literals instead of EventType enum values.
This keeps the core clean while allowing plugins to define their own event types.

```python
# Example: Plugin emits custom events
await self.event_bus.emit_with_hooks(
    "agent_spawned",  # String literal, not EventType
    {"name": agent.name, "task": agent.task},
    "my_plugin"
)

# Other plugins can subscribe to these using string matching
# The event_bus accepts both EventType enum and string event types
```

**Benefits:**
- Core EventType stays minimal and generic
- Plugins can define unlimited custom events
- No core changes needed for plugin-specific events
- Cross-plugin event subscription still works

## Hook Priorities

The system uses a structured priority system to ensure proper execution order:

```python
class HookPriority(Enum):
    SYSTEM = 1000          # Core system operations
    SECURITY = 900         # Security validation, authorization
    PREPROCESSING = 500    # Data transformation, validation
    LLM = 100             # LLM model inference
    POSTPROCESSING = 50   # Response formatting, logging
    DISPLAY = 10          # UI updates, rendering
```

**Higher numbers execute first**. Use appropriate priorities for your hook's purpose.

## Creating Hooks

### Basic Hook Creation

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

# Create a hook
my_hook = Hook(
    name="my_custom_hook",
    plugin_name="my_plugin",
    event_type=EventType.USER_INPUT,
    priority=HookPriority.PREPROCESSING.value,
    callback=self._handle_user_input,
    timeout=10,
    error_action="continue"
)

# Hook callback function
async def _handle_user_input(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
    """Process user input."""
    logger.info(f"Processing user input: {data.get('message', '')}")

    # Modify event data if needed
    if "urgent" in data.get('message', '').lower():
        event.data['priority'] = 'high'

    return {
        "status": "processed",
        "modifications": ["priority_check"]
    }
```

### Data Transformation Hook

```python
async def _transform_input(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
    """Transform input data."""

    # Transform the data
    if isinstance(data.get('message'), str):
        # Add timestamp
        data['timestamp'] = datetime.now().isoformat()

        # Transform message format
        data['message'] = f"[{self.plugin_name}] {data['message']}"

    # Return modified data to pass to next hooks
    return {
        "data": data,  # This updates the event data
        "status": "transformed"
    }
```

### Event Cancellation Hook

```python
async def _validate_input(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
    """Validate input and potentially cancel event."""

    message = data.get('message', '')

    # Check for spam or inappropriate content
    if self._is_spam(message):
        event.cancelled = True  # Cancel the entire event chain
        logger.warning(f"Cancelled event due to spam detection: {message[:50]}")

        return {
            "status": "cancelled",
            "reason": "spam_detected"
        }

    return {"status": "validated"}
```

## Plugin Integration

### Complete Plugin Example

```python
"""Example plugin demonstrating comprehensive hook usage."""

import datetime
import logging
from typing import Any, Dict, List
from kollabor.events import Event, EventType, Hook, HookPriority

logger = logging.getLogger(__name__)

class ExamplePlugin:
    """Example plugin with comprehensive hook integration."""

    def __init__(self, name: str, state_manager, event_bus, renderer, config):
        self.name = name
        self.state_manager = state_manager
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config

        # Plugin state
        self.message_count = 0
        self.processed_messages = []

        # Create hooks
        self.hooks = self._create_hooks()

    def _create_hooks(self) -> List[Hook]:
        """Create all plugin hooks."""
        timeout = self.config.get('plugins.example.hook_timeout', 30)

        return [
            # Input processing hooks
            Hook(
                name="validate_input",
                plugin_name=self.name,
                event_type=EventType.USER_INPUT_PRE,
                priority=HookPriority.PREPROCESSING.value,
                callback=self._validate_input,
                timeout=timeout
            ),
            Hook(
                name="enhance_input",
                plugin_name=self.name,
                event_type=EventType.USER_INPUT,
                priority=HookPriority.PREPROCESSING.value,
                callback=self._enhance_input,
                timeout=timeout
            ),
            Hook(
                name="log_input",
                plugin_name=self.name,
                event_type=EventType.USER_INPUT_POST,
                priority=HookPriority.POSTPROCESSING.value,
                callback=self._log_input,
                timeout=timeout
            ),

            # LLM hooks
            Hook(
                name="prepare_llm_request",
                plugin_name=self.name,
                event_type=EventType.LLM_REQUEST_PRE,
                priority=HookPriority.PREPROCESSING.value,
                callback=self._prepare_llm_request,
                timeout=timeout
            ),
            Hook(
                name="process_llm_response",
                plugin_name=self.name,
                event_type=EventType.LLM_RESPONSE_POST,
                priority=HookPriority.POSTPROCESSING.value,
                callback=self._process_llm_response,
                timeout=timeout
            )
        ]

    async def _validate_input(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
        """Validate user input."""
        message = data.get('message', '')

        if len(message.strip()) == 0:
            event.cancelled = True
            return {"status": "cancelled", "reason": "empty_message"}

        if len(message) > 10000:  # Arbitrary limit
            event.cancelled = True
            return {"status": "cancelled", "reason": "message_too_long"}

        return {"status": "validated"}

    async def _enhance_input(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
        """Enhance user input with additional context."""
        message = data.get('message', '')

        # Add message metadata
        enhanced_data = data.copy()
        enhanced_data.update({
            'message_id': self.message_count,
            'plugin_processed': True,
            'enhancement_timestamp': datetime.now().isoformat()
        })

        self.message_count += 1

        return {
            "data": enhanced_data,
            "status": "enhanced"
        }

    async def _log_input(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
        """Log processed input."""
        message = data.get('message', '')
        message_id = data.get('message_id', 'unknown')

        log_entry = {
            'id': message_id,
            'message': message[:100],  # Truncate for logging
            'timestamp': datetime.now().isoformat()
        }

        self.processed_messages.append(log_entry)

        # Keep only recent messages
        if len(self.processed_messages) > 100:
            self.processed_messages = self.processed_messages[-100:]

        logger.info(f"Logged input message {message_id}")
        return {"status": "logged"}

    async def _prepare_llm_request(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
        """Prepare LLM request with plugin context."""
        # Add plugin context to LLM request
        if 'context' not in data:
            data['context'] = {}

        data['context']['plugin_name'] = self.name
        data['context']['message_count'] = self.message_count

        return {
            "data": data,
            "status": "prepared"
        }

    async def _process_llm_response(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
        """Process LLM response."""
        response = data.get('response', '')

        # Log response statistics
        word_count = len(response.split())
        logger.info(f"LLM response processed: {word_count} words")

        return {
            "status": "processed",
            "word_count": word_count
        }

    # Required plugin methods
    async def initialize(self):
        """Initialize the plugin."""
        logger.info(f"Initializing {self.name} plugin")

    async def register_hooks(self):
        """Register all plugin hooks."""
        for hook in self.hooks:
            await self.event_bus.register_hook(hook)
        logger.info(f"Registered {len(self.hooks)} hooks for {self.name}")

    async def shutdown(self):
        """Shutdown the plugin."""
        logger.info(f"Shutting down {self.name} - processed {self.message_count} messages")

    def get_status_lines(self) -> Dict[str, List[str]]:
        """Get plugin status for display organized by area."""
        return {
            "A": [],  # Core system status
            "B": [    # Plugin-specific metrics
                f"Messages: {self.message_count}",
                f"Recent: {len(self.processed_messages)}"
            ],
            "C": []   # Detailed information
        }

    @staticmethod
    def get_default_config() -> Dict[str, Any]:
        """Get default plugin configuration."""
        return {
            "plugins": {
                "example": {
                    "enabled": True,
                    "hook_timeout": 30,
                    "log_level": "INFO"
                }
            }
        }
```

## Plugin Communication & Service Discovery

The Kollabor CLI system provides a sophisticated plugin ecosystem where plugins can discover, communicate with, and call services from other plugins. This enables powerful cross-plugin integration like JavaScript extension plugins working with coding aid plugins.

### Core Components for Plugin Communication

#### 1. Plugin Factory Methods

The `PluginFactory` provides essential methods for plugin discovery:

```python
# Access the plugin factory from your plugin
factory = self.get_factory()  # Available in plugin context

# Get a specific plugin instance
js_plugin = factory.get_instance("JavaScriptExtensionPlugin")

# Get all plugin instances for discovery
all_plugins = factory.get_all_instances()
# Returns: {"PluginName": plugin_instance, ...}

# Check instantiation status
errors = factory.get_instantiation_errors()
```

#### 2. KollaborPluginSDK Service Registration

Plugins can register services that other plugins can discover and use:

```python
from kollabor.llm.plugin_sdk import KollaborPluginSDK

class JavaScriptExtensionPlugin:
    def __init__(self, name: str, state_manager, event_bus, renderer, config):
        self.name = name
        self.sdk = KollaborPluginSDK()
        # ... other initialization

    async def initialize(self):
        """Register this plugin's services."""
        # Register analysis service
        self.sdk.register_custom_tool({
            "name": "analyze_javascript",
            "description": "Analyze JavaScript code for syntax errors and suggestions",
            "handler": self.analyze_code,
            "parameters": {
                "code": {"type": "string", "description": "JavaScript code to analyze"}
            },
            "plugin": self.name,
            "enabled": True
        })

        # Register formatting service
        self.sdk.register_custom_tool({
            "name": "format_javascript",
            "description": "Format JavaScript code with proper styling",
            "handler": self.format_code,
            "parameters": {
                "code": {"type": "string", "description": "JavaScript code to format"},
                "style": {"type": "string", "description": "Formatting style (default: 'standard')"}
            },
            "plugin": self.name
        })

    async def analyze_code(self, params: Dict[str, Any]) -> Dict[str, Any]:
        """Analyze JavaScript code."""
        code = params.get("code", "")

        # Perform analysis (example implementation)
        issues = []
        suggestions = []

        if "var " in code:
            issues.append("Use 'let' or 'const' instead of 'var'")

        if code.count(";") < code.count("\n"):
            suggestions.append("Add semicolons for clarity")

        return {
            "syntax_valid": True,
            "issues": issues,
            "suggestions": suggestions,
            "complexity_score": len(code.split("\n"))
        }

    async def format_code(self, params: Dict[str, Any]) -> Dict[str, Any]:
        """Format JavaScript code."""
        code = params.get("code", "")
        style = params.get("style", "standard")

        # Format code (example implementation)
        formatted = code.replace("\t", "  ").strip()

        return {
            "formatted_code": formatted,
            "style_applied": style,
            "changes_made": ["normalized indentation"]
        }
```

#### 3. Service Discovery and Usage

Other plugins can discover and use these services:

```python
class CodingAidPlugin:
    async def initialize(self):
        """Discover available coding services."""
        # Method 1: Direct plugin instance access
        factory = self.get_factory()
        self.js_plugin = factory.get_instance("JavaScriptExtensionPlugin")

        # Method 2: SDK service discovery
        self.sdk = KollaborPluginSDK()
        self.available_tools = self.sdk.list_custom_tools()

        # Log discovered services
        js_tools = [tool for tool in self.available_tools if tool["plugin"] == "JavaScriptExtensionPlugin"]
        logger.info(f"Discovered {len(js_tools)} JavaScript services")

    async def process_code_request(self, code: str, language: str) -> str:
        """Process code analysis request."""

        if language.lower() == "javascript":
            # Method 1: Direct plugin method call
            if self.js_plugin:
                result = await self.js_plugin.analyze_code({"code": code})
                return f"Analysis: {len(result['issues'])} issues found"

            # Method 2: SDK service execution
            result = await self.sdk.execute_custom_tool("analyze_javascript", {"code": code})
            if result.get("status") == "success":
                analysis = result["result"]
                return f"Issues: {analysis['issues']}, Suggestions: {analysis['suggestions']}"

        return f"No analyzer available for {language}"

    async def format_code_request(self, code: str, language: str, style: str = "standard") -> str:
        """Format code using appropriate service."""

        if language.lower() == "javascript":
            result = await self.sdk.execute_custom_tool("format_javascript", {
                "code": code,
                "style": style
            })

            if result.get("status") == "success":
                return result["result"]["formatted_code"]

        return code  # Return unchanged if no formatter available
```

### 4. Event Bus Direct Messaging

Plugins can send direct messages to each other via the event bus:

```python
class SecurityPlugin:
    async def scan_complete_handler(self, scan_results: Dict[str, Any]):
        """Send scan results to other interested plugins."""

        # Send direct message to LLM plugin
        await self.event_bus.send_message(
            target_plugin="llm_plugin",
            data={
                "alert": "security_scan_complete",
                "results": scan_results,
                "priority": "high" if scan_results["threats_found"] > 0 else "normal"
            }
        )

        # Send to monitoring plugin
        await self.event_bus.send_message(
            target_plugin="hook_monitoring_plugin",
            data={
                "metric_update": {
                    "security_scans": scan_results["scan_count"],
                    "threats_detected": scan_results["threats_found"]
                }
            }
        )

class LLMPlugin:
    async def handle_direct_message(self, message: Dict[str, Any], sender: str):
        """Handle direct messages from other plugins."""

        if message.get("alert") == "security_scan_complete":
            priority = message.get("priority", "normal")
            results = message.get("results", {})

            if priority == "high":
                # Inject security context into next LLM request
                self.add_security_context(f"Security scan found {results['threats_found']} threats")

            logger.info(f"Received security update from {sender}")
```

### 5. Dynamic Service Discovery Patterns

For building flexible plugin ecosystems:

```python
class PluginDiscoveryMixin:
    """Mixin for plugins that need to discover services dynamically."""

    def discover_services_by_type(self, service_type: str) -> List[Dict[str, Any]]:
        """Find all plugins providing a specific service type."""
        factory = self.get_factory()
        all_plugins = factory.get_all_instances()

        services = []
        for plugin_name, plugin_instance in all_plugins.items():
            if hasattr(plugin_instance, 'get_services'):
                plugin_services = plugin_instance.get_services()
                for service_name, service_info in plugin_services.items():
                    if service_info.get('type') == service_type:
                        services.append({
                            'plugin': plugin_name,
                            'service': service_name,
                            'info': service_info
                        })

        return services

    async def call_best_service(self, service_type: str, params: Dict[str, Any]) -> Any:
        """Call the best available service for a given type."""
        services = self.discover_services_by_type(service_type)

        if not services:
            raise ServiceNotFoundError(f"No services found for type: {service_type}")

        # Sort by priority or other criteria
        best_service = max(services, key=lambda s: s['info'].get('priority', 0))

        # Call the service
        plugin = self.get_factory().get_instance(best_service['plugin'])
        service_method = getattr(plugin, best_service['service'])

        return await service_method(params)

class AdvancedCodingAidPlugin(PluginDiscoveryMixin):
    async def analyze_any_language(self, code: str, language: str) -> Dict[str, Any]:
        """Analyze code in any supported language."""

        try:
            # Try to find a specific analyzer
            result = await self.call_best_service(f"code_analysis_{language.lower()}", {
                "code": code
            })
            return result

        except ServiceNotFoundError:
            # Fall back to generic analysis
            services = self.discover_services_by_type("code_analysis_generic")
            if services:
                return await self.call_best_service("code_analysis_generic", {
                    "code": code,
                    "language": language
                })

            return {"error": f"No analyzer available for {language}"}
```

### Real-World Example: JavaScript Extension Ecosystem

Here's how a complete JavaScript extension would integrate:

```python
# plugins/javascript_extension_plugin.py
class JavaScriptExtensionPlugin:
    """Advanced JavaScript analysis and tooling plugin."""

    @staticmethod
    def get_default_config():
        return {
            "plugins": {
                "javascript_extension": {
                    "enabled": True,
                    "eslint_enabled": True,
                    "prettier_enabled": True,
                    "complexity_analysis": True,
                    "service_priority": 100
                }
            }
        }

    def get_services(self) -> Dict[str, Dict[str, Any]]:
        """Register services this plugin provides."""
        return {
            "analyze_javascript": {
                "type": "code_analysis_javascript",
                "priority": self.config.get("plugins.javascript_extension.service_priority", 100),
                "description": "Advanced JavaScript analysis with ESLint integration",
                "method": self.analyze_javascript
            },
            "format_javascript": {
                "type": "code_formatting_javascript",
                "priority": 100,
                "description": "Format JavaScript with Prettier",
                "method": self.format_javascript
            },
            "suggest_improvements": {
                "type": "code_improvement_javascript",
                "priority": 90,
                "description": "Suggest JavaScript best practices",
                "method": self.suggest_improvements
            }
        }

# plugins/coding_aid_plugin.py
class CodingAidPlugin(PluginDiscoveryMixin):
    """Universal coding assistance plugin."""

    async def handle_code_request(self, code: str, language: str, task: str) -> str:
        """Handle any coding-related request."""

        service_type = f"code_{task}_{language.lower()}"

        try:
            result = await self.call_best_service(service_type, {"code": code})

            return self.format_response(result, task, language)

        except ServiceNotFoundError:
            return f"Sorry, I don't have {task} capabilities for {language} yet. " \
                   f"Available services: {self.list_available_services()}"

    def list_available_services(self) -> str:
        """List all available coding services."""
        all_services = []
        for service_type in ["analysis", "formatting", "improvement"]:
            services = self.discover_services_by_type(f"code_{service_type}")
            for service in services:
                lang = service['service'].split('_')[-1]
                all_services.append(f"{lang} {service_type}")

        return ", ".join(set(all_services))
```

This ecosystem allows:
- **Dynamic service discovery** - coding aid finds all available language extensions
- **Graceful degradation** - falls back gracefully when services aren't available
- **Easy extension** - new language plugins just implement the service interface
- **Cross-plugin communication** - plugins can notify each other of analysis results
- **Priority-based selection** - best available service is automatically chosen

## Core Extension Points

Beyond hooks, the system provides additional extension points for common plugin needs.

### DisplayFilterRegistry - Message Content Transformation

Plugins can register filters to transform message content before display. This is useful for
stripping plugin-specific XML commands or transforming output.

```python
from kollabor.io.message_renderer import DisplayFilterRegistry, MessageType

class MyPlugin:
    async def initialize(self):
        # Register a display filter
        DisplayFilterRegistry.register(
            name="my_plugin",                    # Unique identifier
            filter_fn=self._transform_content,   # Transform function
            message_types=[MessageType.ASSISTANT],  # Which messages to filter (None = all)
            priority=100,                        # Higher = runs first
        )

    def shutdown(self):
        # Clean up on shutdown
        DisplayFilterRegistry.unregister("my_plugin")

    def _transform_content(self, content: str, message_type: MessageType) -> str:
        """Transform message content before display.

        Args:
            content: Original message content.
            message_type: Type of message being displayed.

        Returns:
            Transformed content.
        """
        import re
        # Strip plugin-specific XML commands (they're handled separately)
        content = re.sub(r"<my-command>.*?</my-command>", "", content, flags=re.DOTALL)
        return content
```

**Key Features:**
- Filters run synchronously (no async) for performance
- Priority ordering (higher = runs first)
- Message type filtering (ASSISTANT, USER, SYSTEM, etc.)
- Error handling - failing filters are logged but don't crash
- Multiple plugins can register filters that chain together

**Use Cases:**
- Strip XML commands that are displayed as tool indicators
- Transform markdown to terminal-friendly format
- Inject or remove content from responses
- Sanitize output

### force_continue - LLM Continuation Control

Plugins can request the LLM to continue processing after their hook completes. This is useful
when you inject content and want the LLM to respond to it.

```python
async def _on_llm_response(self, context: dict) -> dict:
    """Process LLM response and optionally continue."""
    response = context.get("response_text", "")

    # Check for commands in response
    if self._has_commands(response):
        # Execute commands
        result = await self._execute_commands(response)

        # Inject result back into conversation
        await self._inject_result(result)

        # Request LLM to continue (see the injected result)
        context["force_continue"] = True

    return context
```

**How it works:**
1. Plugin sets `context["force_continue"] = True` in any LLM_RESPONSE hook phase
2. LLM service checks for this flag after all hooks complete
3. If set, `turn_completed` stays False, triggering another LLM turn
4. LLM sees the injected content and can respond to it

**Important Notes:**
- Works in PRE, MAIN, or POST phases
- The flag is checked after display, so injected content appears first
- Be careful of infinite loops - only continue when there's new content to process

### Event Timing - Pre-Display Hooks

The LLM_RESPONSE event fires BEFORE the response is displayed. This allows plugins to:
- Show tool indicators before the prose response
- Transform the response before display
- Inject content that appears before the response

```python
async def _on_llm_response(self, context: dict) -> dict:
    """Hook runs BEFORE response is displayed."""
    response = context.get("response_text", "")

    # Parse commands from response
    commands = self._parse_commands(response)

    # Display tool indicator BEFORE the response text
    for cmd in commands:
        self._display_tool_indicator(cmd)  # Shows: ⏺ my_command(args)

    # When this hook returns, the response is displayed
    return context
```

**Timing Flow:**
```
LLM generates response
    ↓
LLM_RESPONSE_PRE hooks run
    ↓
LLM_RESPONSE hooks run (MAIN)
    ↓
LLM_RESPONSE_POST hooks run
    ↓
Response is displayed (after all hooks)
    ↓
force_continue checked
```

## Advanced Patterns

### Conditional Hook Registration

```python
def _create_hooks(self) -> List[Hook]:
    """Create hooks based on configuration."""
    hooks = []

    # Always include basic hooks
    hooks.append(self._create_basic_hook())

    # Conditional hooks based on config
    if self.config.get('plugins.example.enable_validation', True):
        hooks.append(self._create_validation_hook())

    if self.config.get('plugins.example.enable_enhancement', False):
        hooks.append(self._create_enhancement_hook())

    return hooks
```

### Hook Performance Monitoring

```python
import time

async def _monitored_hook(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
    """Hook with performance monitoring."""
    start_time = time.time()

    try:
        # Main hook logic
        result = await self._process_data(data, event)

        # Log performance
        execution_time = (time.time() - start_time) * 1000
        if execution_time > 100:  # Warn on slow hooks
            logger.warning(f"Slow hook execution: {execution_time:.1f}ms")

        return result

    except Exception as e:
        execution_time = (time.time() - start_time) * 1000
        logger.error(f"Hook failed after {execution_time:.1f}ms: {e}")
        raise
```

### Dynamic Hook Modification

```python
async def _adaptive_hook(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
    """Hook that adapts based on runtime conditions."""

    # Adapt behavior based on system load
    current_load = self._get_system_load()

    if current_load > 0.8:
        # Simplified processing under high load
        return await self._fast_processing(data, event)
    else:
        # Full processing under normal load
        return await self._full_processing(data, event)
```

## Best Practices

### 1. Hook Design

- **Single Responsibility**: Each hook should have one clear purpose
- **Idempotent Operations**: Hooks should be safe to run multiple times
- **Graceful Degradation**: Handle missing data gracefully
- **Resource Management**: Clean up resources properly

### 2. Error Handling

```python
async def _robust_hook(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
    """Example of robust error handling."""
    try:
        # Validate input data
        if not isinstance(data, dict):
            logger.warning("Invalid data type received")
            return {"status": "error", "reason": "invalid_data_type"}

        required_fields = ['message']
        for field in required_fields:
            if field not in data:
                logger.warning(f"Missing required field: {field}")
                return {"status": "error", "reason": f"missing_{field}"}

        # Main processing
        result = self._process_safely(data)

        return {"status": "success", "result": result}

    except Exception as e:
        logger.error(f"Hook execution failed: {e}", exc_info=True)
        return {"status": "error", "reason": str(e)}
```

### 3. Performance Considerations

- **Timeout Management**: Set appropriate timeouts for your hooks
- **Resource Limits**: Avoid memory leaks and excessive resource usage
- **Async/Await**: Use proper async patterns for non-blocking execution
- **Caching**: Cache expensive operations when appropriate

### 4. Logging & Debugging

```python
import logging

logger = logging.getLogger(__name__)

# Use structured logging
logger.info("Hook executed", extra={
    'hook_name': self.name,
    'event_type': event.type.value,
    'execution_time_ms': execution_time,
    'data_size': len(str(data))
})

# Include context in error messages
logger.error(f"Hook {self.name} failed for event {event.type.value}: {e}")
```

## Troubleshooting

### Common Issues

1. **Hook Not Executing**
   - Check if hook is properly registered
   - Verify event type matches expected events
   - Check if plugin is enabled in configuration

2. **Event Cancellation**
   - Check if earlier hooks are cancelling events
   - Review hook priorities and execution order
   - Enable debug logging to trace event flow

3. **Performance Issues**
   - Check hook execution times in logs
   - Review hook timeouts and adjust if needed
   - Use the Hook Monitoring Plugin for detailed analysis

4. **Data Not Flowing**
   - Ensure hooks return `{"data": modified_data}` for transformations
   - Check if hooks are modifying event.data directly
   - Verify hook execution order and priorities

5. **Plugin Communication Issues**
   - Verify factory methods are accessible: `factory = self.get_factory()`
   - Check if target plugins are properly instantiated
   - Ensure service registration happens in `initialize()` method
   - Validate service names and plugin names match exactly

### Debug Tools

Use the Hook Monitoring Plugin (`plugins/hook_monitoring_plugin.py`) for comprehensive debugging:

```python
# Enable detailed monitoring
config = {
    "plugins": {
        "hook_monitoring": {
            "enabled": True,
            "log_all_events": True,
            "log_performance": True,
            "performance_threshold_ms": 50,
            "debug_logging": True,
            "show_status": True
        }
    }
}
```

### Log Analysis

Monitor `.kollabor-cli/logs/kollabor.log` for hook execution details:

```bash
# Filter hook-related logs
tail -f .kollabor-cli/logs/kollabor.log | grep "HOOK"

# Monitor performance issues
tail -f .kollabor-cli/logs/kollabor.log | grep "SLOW HOOK"
```

## Conclusion

The hook system provides powerful extensibility for the Kollabor CLI application. By following this SDK documentation and best practices, you can create sophisticated plugins that enhance, modify, or completely replace core application behavior.

For additional examples and implementation details, refer to:
- `plugins/enhanced_input_plugin.py` - UI enhancement example
- `plugins/hook_monitoring_plugin.py` - Comprehensive monitoring example
- `kollabor/events/` - Core hook system implementation

The hook system's "everything has a hook" philosophy ensures maximum customization while maintaining clean separation of concerns and robust error handling.