How Kimi Code Lifecycle Hooks Work: Events, Execution, and Configuration

Kimi Code lifecycle hooks map session events like SessionStart or PreToolUse to shell commands that receive JSON payloads via stdin, execute with configurable timeouts, and use exit codes (0 to allow, 2 to block) to control workflow flow without stopping execution on errors.

Kimi Code provides a generic hook system that allows you to intercept and react to key moments in an AI session's lifecycle. Implemented in packages/agent-core-v2/src/hooks.ts, these lifecycle hooks enable everything from audit logging to security blocking by running external scripts when specific events fire. Understanding how this system processes events and handles responses is essential for customizing your Kimi Code workflow safely.

What Are Lifecycle Hooks in Kimi Code?

A lifecycle hook is a rule that binds a specific event—such as SessionStart, UserPromptSubmit, or PreToolUse—to a shell command. When the event occurs, Kimi Code executes the command asynchronously, passing contextual data as JSON.

The core implementation resides in packages/agent-core-v2/src/hooks.ts, which exports OrderedHookSlot and createHooks to manage hook registration and execution. Hooks are defined in your ~/.kimi-code/config.toml under the [[hooks]] array, making them configurable without modifying the source code.

How Lifecycle Hooks Execute

When an event triggers, Kimi Code follows a strict execution protocol:

  1. Payload delivery – The CLI collects event-specific data (session ID, working directory, tool inputs) and writes it as JSON to the hook command's stdin.
  2. Timeout enforcement – Commands run with a configurable timeout that defaults to 30 seconds.
  3. Exit code interpretation:
    • 0: Allow the operation to proceed
    • 2: Block the operation (for blockable events)
    • Any other non-zero: Allow (fail-open behavior)
  4. Stdout parsing – The CLI can read structured JSON responses from stdout to modify behavior.

Because the system is fail-open, timeouts or errors in hook scripts never halt the main Kimi Code session; they are logged and the workflow continues. This design prioritizes availability while still permitting security-oriented interception.

Available Lifecycle Events

Kimi Code supports distinct events categorized by whether they can block operations or only observe them:

  • UserPromptSubmit – Fires when text is entered. Blockable; use to amend or filter user input before the model processes it.
  • PreToolUse – Fires before a tool executes. Blockable; use to validate dangerous operations like shell commands.
  • Stop – Intercepts the model's stop signal. Blockable; allows injection of follow-up messages.
  • SessionStart / SessionEnd – Fires on startup, resume, or exit. Observation-only; use for logging and resource initialization.
  • PostToolUse / PostToolUseFailure – Fires after tool execution. Observation-only; use for telemetry and notifications.

Additional events like Notification (for background task state changes) are documented in docs/en/customization/hooks.md.

Configuring Lifecycle Hooks

Define hooks in ~/.kimi-code/config.toml using TOML array syntax. Each hook requires an event, optional matcher for filtering, command to execute, and timeout in seconds.

[[hooks]]
event   = "SessionStart"
matcher = "startup"
command = "node ~/.kimi-code/hooks/log-session.mjs"
timeout = 30

The matcher field filters events when the event type supports it (e.g., matching specific tool names for PreToolUse). Omitting it catches all instances of that event.

Practical Lifecycle Hook Examples

Sending Desktop Notifications on Task Completion

Fire a macOS notification when background tasks complete using the Notification event:

[[hooks]]
event   = "Notification"
matcher = "task\\.completed"
command = "terminal-notifier -title Kimi -message 'Task done'"
timeout = 30

Blocking Dangerous Shell Commands with PreToolUse

Create a security gate that prevents rm -rf execution by exiting with code 2:

[[hooks]]
event   = "PreToolUse"
matcher = "Bash"
command = "node ~/.kimi-code/hooks/block-dangerous-bash.mjs"
timeout = 5
// block-dangerous-bash.mjs
let input = '';
process.stdin.on('data', chunk => (input += chunk));
process.stdin.on('end', () => {
  const payload = JSON.parse(input);
  const cmd = payload.tool_input?.command ?? '';
  
  if (cmd.includes('rm -rf')) {
    console.error('Dangerous command detected, blocked');
    process.exit(2);  // Block operation
  }
  process.exit(0);      // Allow operation
});

Logging Session Start and End Events

The test suite in packages/agent-core/test/session/lifecycle-hooks.test.ts demonstrates logging by capturing JSON payloads:

// Helper script that records payloads to a file
const scriptPath = join(dir, 'record-hook.cjs');
await writeFile(
  scriptPath,
  [
    "const { appendFileSync } = require('node:fs');",
    "let input = '';",
    "process.stdin.on('data', chunk => { input += chunk; });",
    "process.stdin.on('end', () => {",
    "  appendFileSync(process.argv[2], input.trim() + '\\n');",
    "});",
  ].join('\n'),
  'utf-8',
);

// Session configuration with hooks
const session = new Session({
  hooks: [
    { event: 'SessionStart', matcher: 'startup', command: scriptPath, timeout: 5 },
    { event: 'SessionEnd',   matcher: 'exit',    command: scriptPath, timeout: 5 },
  ],
});

The resulting JSON payload contains:

{
  "hook_event_name": "SessionStart",
  "session_id": "session-123",
  "cwd": "/workspace",
  "source": "startup"
}

When to Use Lifecycle Hooks

Choose hooks based on your operational requirements:

  • Audit and logging – Use SessionStart and PostToolUse to write immutable logs of all AI activity.
  • Security validation – Use PreToolUse with exit code 2 to block dangerous operations before execution.
  • Context enrichment – Use UserPromptSubmit to inject Git branch names or environment metadata into prompts.
  • System integration – Use Notification to trigger external alerts when long-running tasks complete.
  • Resource management – Use SessionEnd to clean up temporary files or terminate background processes.

Summary

  • Lifecycle hooks in Kimi Code connect session events to shell commands via packages/agent-core-v2/src/hooks.ts.
  • Commands receive JSON payloads on stdin and communicate decisions through exit codes (0 allow, 2 block).
  • The system is fail-open; script failures or timeouts never crash the main session.
  • Configure hooks in ~/.kimi-code/config.toml using the [[hooks]] array syntax.
  • Blockable events (PreToolUse, UserPromptSubmit) can halt operations, while observation events provide telemetry without interrupting flow.

Frequently Asked Questions

What happens if a lifecycle hook script crashes or times out?

If a hook exits with a non-zero code other than 2, or exceeds its configured timeout (default 30 seconds), Kimi Code logs the failure and allows the operation to proceed. This fail-open design ensures that hook errors never disrupt your coding session.

Can I modify the JSON payload before it reaches the model?

Yes. The UserPromptSubmit event allows you to intercept user input via stdin, and you can write a modified JSON object to stdout. However, the specific implementation details for payload mutation depend on the event type; blocking events typically check exit codes while transformation events parse stdout.

Where are lifecycle hooks configured?

Hooks are defined in your user configuration file at ~/.kimi-code/config.toml under the [[hooks]] TOML array. This location keeps user customizations separate from the Kimi Code source code in packages/agent-core-v2/src/hooks.ts.

Which events can actually block operations versus just observe them?

According to the source code documentation in docs/en/customization/hooks.md, blockable events include UserPromptSubmit, PreToolUse, and Stop. Events like SessionStart, SessionEnd, PostToolUse, and PostToolUseFailure are observation-only and cannot halt execution.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →