# How to Write Custom Hooks to Trigger on SessionStart and SessionEnd Events in Kimi CLI

> Learn to write custom hooks for SessionStart and SessionEnd events in Kimi CLI. Add custom Python logic to your CLI workflow seamlessly. Integrate event-driven actions easily.

- Repository: [Moonshot AI/kimi-cli](https://github.com/MoonshotAI/kimi-cli)
- Tags: how-to-guide
- Published: 2026-07-22

---

**To write custom hooks that trigger on SessionStart and SessionEnd events, create a Python module with functions that accept the corresponding Pydantic event models, place it in an importable location (e.g., `~/.kimi/hooks/`), and register the hook definitions in `~/.kimi/config.toml` specifying the event type, module path, and callable name.**

The MoonshotAI/kimi-cli repository provides a lightweight hook engine that lets you execute custom Python code during key session lifecycle transitions. By leveraging the `SessionStart` and `SessionEnd` events defined in the hooks system, you can automate setup tasks, logging, analytics, or cleanup operations without modifying the core CLI source code.

## Understanding the Hook Architecture

The hook framework is implemented across several modules under `src/kimi_cli/hooks/`:

- **[`events.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/events.py)** – Defines the Pydantic models `SessionStart` and `SessionEnd` that are passed as payloads to your hook functions when a session begins or ends.
- **[`config.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/config.py)** – Contains the `HookDef` class (schema for individual hooks) and `HookConfig` class (manages loading and validation from user configuration).
- **[`engine.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/engine.py)** – Houses the `HookEngine` class responsible for loading hook definitions, matching them to fired events, and executing the registered callables in sequence.
- **[`runner.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/runner.py)** – Provides the `HookResult` wrapper that captures execution success, return values, and exception details.

When you launch the CLI, `KimiCLI.create()` in [`src/kimi_cli/app.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/app.py) instantiates a `KimiSession` and immediately fires the `SessionStart` event through the hook engine. Conversely, `KimiCLI.run()` registers a signal handler that triggers the `SessionEnd` event via the engine just before the process exits, as managed in [`src/kimi_cli/soul/kimisoul.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/soul/kimisoul.py).

## Step-by-Step Implementation Guide

### 1. Create Your Hook Functions

Write a Python file containing functions that accept the appropriate event model as their first argument. The function signatures must match the event types defined in [`src/kimi_cli/hooks/events.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/hooks/events.py).

```python

# ~/.kimi/hooks/lifecycle_hooks.py

from kimi_cli.hooks.events import SessionStart, SessionEnd
from loguru import logger
import datetime

def initialize_workspace(event: SessionStart) -> None:
    """
    Runs immediately after a new KimiSession is created.
    Access session metadata via the event payload.
    """
    logger.info(f"Session {event.session_id} started at {event.timestamp}")
    # Example: Create a temporary working directory for this session

    print(f"🚀 Initializing environment for session {event.session_id}")

def generate_summary(event: SessionEnd) -> None:
    """
    Runs just before the CLI exits and the session is torn down.
    """
    duration = datetime.datetime.now() - event.start_time
    logger.info(f"Session {event.session_id} ending. Duration: {duration}")
    # Example: Write analytics or clean up resources

    print(f"📊 Session lasted {duration.total_seconds()}s")

```

### 2. Register Hooks in config.toml

Add hook definitions to your user configuration file at `~/.kimi/config.toml`. The `HookConfig` class parses this table and validates entries against the `HookDef` schema.

```toml

# ~/.kimi/config.toml

[[hooks]]
name = "workspace_initializer"
event = "session_start"
module = "lifecycle_hooks"
callable = "initialize_workspace"

[[hooks]]
name = "summary_generator"
event = "session_end"
module = "lifecycle_hooks"
callable = "generate_summary"

```

Each hook definition requires:
- **name** – Unique identifier for the hook.
- **event** – Either `session_start` or `session_end`.
- **module** – Dotted Python import path (relative to directories in `PYTHONPATH`).
- **callable** – Exact function name within the module.

### 3. Verify Hook Execution

Start a new CLI session to trigger the hooks:

```bash
$ kimi chat
🚀 Initializing environment for session a1b2c3d4
> Hello Kimi
...
^C
📊 Session lasted 45.2s

```

The `HookEngine` in [`src/kimi_cli/hooks/engine.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/hooks/engine.py) automatically invokes registered callables when the corresponding event fires. Errors are captured in `HookResult` objects and logged without crashing the CLI.

## Advanced Configuration: Passing Arguments

You can supply static arguments to your hook functions via the `args` dictionary in the configuration. The engine passes these as keyword arguments when calling your function.

```toml
[[hooks]]
name = "detailed_logger"
event = "session_end"
module = "lifecycle_hooks"
callable = "log_to_file"
args = { log_level = "INFO", destination = "/tmp/kimi_audit.log" }

```

Corresponding function signature:

```python
def log_to_file(event: SessionEnd, log_level: str, destination: str) -> None:
    """
    Receives the SessionEnd event plus config-defined arguments.
    """
    # Implementation using log_level and destination

    pass

```

## Key Source Files Reference

| File | Purpose | Key Components |
|------|---------|----------------|
| [`src/kimi_cli/hooks/events.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/hooks/events.py) | Event model definitions | `SessionStart`, `SessionEnd` Pydantic models |
| [`src/kimi_cli/hooks/config.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/hooks/config.py) | Configuration parsing | `HookDef`, `HookConfig.load()` |
| [`src/kimi_cli/hooks/engine.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/hooks/engine.py) | Hook execution logic | `HookEngine` class, `run_hook()` method |
| [`src/kimi_cli/hooks/runner.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/hooks/runner.py) | Result wrapping | `HookResult` dataclass |
| [`src/kimi_cli/app.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/app.py) | Session initialization | `KimiCLI.create()` fires `SessionStart` |
| [`src/kimi_cli/soul/kimisoul.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/soul/kimisoul.py) | Shutdown handling | Signal handlers fire `SessionEnd` |

## Summary

- **Event Models**: `SessionStart` and `SessionEnd` in [`src/kimi_cli/hooks/events.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/hooks/events.py) provide structured payloads containing session metadata.
- **Function Signature**: Hook callables must accept the event object as the first positional argument.
- **Configuration**: Define hooks in `~/.kimi/config.toml` with `event`, `module`, and `callable` fields; the `HookConfig` class validates these at startup.
- **Execution**: The `HookEngine` automatically triggers registered functions when `KimiCLI` creates or destroys a session.
- **Extensibility**: Use the `args` dictionary in config to pass static parameters to your hooks without code changes.

## Frequently Asked Questions

### What data is available in the SessionStart and SessionEnd event payloads?

Both events are Pydantic models defined in [`src/kimi_cli/hooks/events.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/hooks/events.py). `SessionStart` typically includes `session_id`, `timestamp`, and the session configuration object. `SessionEnd` includes `session_id`, `start_time`, and `end_time`, allowing you to calculate duration and access final session state.

### Where should I place my custom hook Python files?

Place your modules in any directory accessible via Python's import system. The standard convention is `~/.kimi/hooks/`, but you can use any location provided you adjust the `module` path in [`config.toml`](https://github.com/MoonshotAI/kimi-cli/blob/main/config.toml) accordingly (e.g., `my_package.my_hooks` if installed via pip, or relative paths if the directory is added to `PYTHONPATH`).

### How do I debug a hook that is not firing?

First, verify your `~/.kimi/config.toml` syntax is valid TOML and that the `hooks` table is properly formatted. Check the CLI logs for validation errors from `HookConfig.load()`. If the hook registers but fails during execution, the `HookEngine` captures exceptions in `HookResult` objects; enable verbose logging (`--verbose` flag) to see stack traces without crashing the session.

### Can I register multiple hooks for the same session event?

Yes. The `HookEngine` supports multiple hook definitions targeting the same event type. They execute in the order defined in the configuration file. If one hook raises an exception, subsequent hooks in the chain still run unless the engine is configured otherwise, with errors recorded individually in their respective `HookResult` instances.