Understanding Kimi Code Lifecycle Hooks: Event-Driven Automation and Security
Kimi Code lifecycle hooks are asynchronous shell command triggers configured in ~/.kimi-code/config.toml that execute at specific session events—passing JSON payloads via stdin and using exit codes to allow (0), block (2), or ignore failures—enabling users to audit, intercept, or modify AI interactions without modifying the core codebase.
The MoonshotAI/kimi-code repository provides a generic hook system in packages/agent-core-v2/src/hooks.ts that exposes session lifecycle events to external scripts. These Kimi Code lifecycle hooks bridge the CLI's internal state with user-defined automation, supporting everything from security policy enforcement to desktop notifications through a fail-open architecture that prioritizes workflow continuity.
Architecture of the Kimi Code Hook System
The core implementation resides in packages/agent-core-v2/src/hooks.ts, which defines OrderedHookSlot and the createHooks factory. When an event fires, the CLI:
- Collects payload data (session ID, cwd, event-specific fields) as JSON
- Writes the payload to stdin of the configured command
- Executes with a timeout (default 30 seconds, configurable per hook)
- Interprets exit codes using fail-open semantics:
0allows continuation,2explicitly blocks the operation, and any other non-zero exit allows continuation but logs the error
This fail-open design ensures that hook failures or timeouts never interrupt the main session flow, making the system safe for production observability while still permitting strict security controls when scripts intentionally exit with code 2.
Available Lifecycle Events and When to Use Them
The CLI exposes distinct event types as documented in docs/en/customization/hooks.md. Each event carries a specific payload structure and blocking capability:
Session Management Events
- SessionStart: Fires during
startuporresume. Non-blocking. Typical for initializing resources or logging session creation with metadata includingsession_idandcwd. - SessionEnd: Fires on
exit. Non-blocking. Used for cleanup scripts, flushing logs, or terminating background processes.
User Interaction Hooks
- UserPromptSubmit: Intercepts text entered by the user before model processing. Blockable (exit code 2). Ideal for input validation, auto-appending Git branch context, or preventing prompt injection attacks.
Tool Execution Hooks
- PreToolUse: Executes before any tool invocation. Blockable. Receives
tool_nameandtool_inputin the JSON payload. Critical for security policies that prevent dangerous shell commands. - PostToolUse: Fires after successful tool execution. Non-blocking. Used for success logging, metrics collection, or firing notification events.
- PostToolUseFailure: Captures tool execution errors. Non-blocking. Enables failure alerting and debugging workflows.
Control Flow Interception
- Stop: Intercepts the model's stop signal. Blockable. Allows injection of follow-up messages or preventing session termination.
Configuring Hooks in config.toml
Hooks are defined in the TOML array [[hooks]] within ~/.kimi-code/config.toml. Each entry requires:
event: The lifecycle event name (e.g.,"PreToolUse")matcher: Optional filter (e.g., tool name"Bash"or regex pattern like"task\\.completed")command: Shell command or script path to executetimeout: Execution limit in seconds (optional, defaults to 30)
Practical Implementation Examples
Desktop Notifications on Task Completion
Configure a non-blocking notification when background tasks finish:
# ~/.kimi-code/config.toml
[[hooks]]
event = "Notification"
matcher = "task\\.completed"
command = "terminal-notifier -title Kimi -message 'Task done'"
timeout = 30
Security: Blocking Dangerous Bash Commands
Implement a safety guard using PreToolUse with a Node.js validator:
[[hooks]]
event = "PreToolUse"
matcher = "Bash"
command = "node ~/.kimi-code/hooks/block-dangerous-bash.mjs"
timeout = 5
The corresponding script reads the JSON payload from stdin:
// ~/.kimi-code/hooks/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); // Exit 2 blocks execution
}
process.exit(0); // Exit 0 allows execution
});
Session Audit Logging
Capture session boundaries for compliance tracking using the test suite pattern from packages/agent-core/test/session/lifecycle-hooks.test.ts:
// Script: record-hook.cjs
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`);
});
Configure the hooks to log to a specific file:
[[hooks]]
event = "SessionStart"
matcher = "startup"
command = "node ~/.kimi-code/hooks/record-hook.cjs ~/logs/kimi-sessions.jsonl"
timeout = 5
[[hooks]]
event = "SessionEnd"
matcher = "exit"
command = "node ~/.kimi-code/hooks/record-hook.cjs ~/logs/kimi-sessions.jsonl"
timeout = 5
The resulting JSON Lines file contains structured payloads:
{"hook_event_name": "SessionStart", "session_id": "session-123", "cwd": "/work", "source": "startup"}
{"hook_event_name": "SessionEnd", "session_id": "session-123", "cwd": "/work", "reason": "exit"}
Summary
- Kimi Code lifecycle hooks execute shell commands asynchronously at specific session events defined in
packages/agent-core-v2/src/hooks.ts. - The system uses JSON payloads via stdin and exit codes (
0allow,2block, other non-zero allow with logging) to communicate between the CLI and external scripts. - Fail-open architecture ensures hook timeouts or crashes never disrupt the main session, suitable for both observability and security use cases.
- Configure hooks in
~/.kimi-code/config.tomlunder the[[hooks]]array usingevent,matcher,command, andtimeoutfields. - Blockable events (
UserPromptSubmit,PreToolUse,Stop) enable security policies and input validation, while observability events (SessionStart,PostToolUse) support auditing and notifications.
Frequently Asked Questions
Can I modify the user prompt before it reaches the model using hooks?
Yes. The UserPromptSubmit event fires before the model processes input. While you cannot directly mutate the payload by writing to stdout, you can implement validation logic that inspects the JSON payload from stdin and uses exit code 2 to block disallowed inputs. For enrichment workflows, you would typically handle this through client-side integrations rather than the hook system itself, though hooks excel at validation and blocking.
What happens if my hook script exceeds the timeout or crashes?
The Kimi Code hook system operates on a fail-open basis. If your script exceeds the configured timeout (default 30 seconds) or exits with any non-zero code other than 2, the CLI logs the error but allows the main operation to proceed. Only exit code 2 explicitly blocks execution, ensuring that infrastructure failures don't accidentally freeze AI sessions while still permitting intentional security blocks.
How do I prevent specific dangerous commands like rm -rf / from executing?
Use the PreToolUse event with a matcher for the Bash tool. Create a validation script—such as the block-dangerous-bash.mjs example—that parses the JSON payload from stdin, inspects payload.tool_input.command for dangerous patterns, and exits with code 2 to block execution. This pattern is documented in docs/en/customization/hooks.md and tested in the packages/agent-core test suite.
Are lifecycle hooks available in all Kimi Code editions?
The hook system is part of the open-source kimi-code core in packages/agent-core-v2, meaning it is available in standard CLI deployments configured via ~/.kimi-code/config.toml. The system is validated by packages/agent-core/test/session/lifecycle-hooks.test.ts, confirming its availability in the open-source distribution and standard desktop builds.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →