Hooks run shell commands or agent prompts automatically when specific events happen in your session - a file is saved, a tool is invoked, or a task completes. You define the trigger and the action; Kiro handles the execution.
| Capability | IDE | CLI | Web | Mobile |
|---|---|---|---|---|
| Event-driven hooks | ✓ | ✓ | — | — |
| Shell command actions | ✓ | ✓ | — | — |
| Agent prompt actions | ✓ | ✓ | — | — |
| Hook creation via chat | ✓ | ✓ | — | — |
A PostFileSave hook that runs ESLint whenever you save a TypeScript file:
{ "version": "v1", "hooks": [{ "name": "Lint on save", "trigger": "PostFileSave", "matcher": "\\.(ts|tsx)$", "action": { "type": "command", "command": "npx eslint --fix" } }] }
This file lives at .kiro/hooks/lint-on-save.json and activates automatically - no manual prompting needed. The hook receives the saved file path and session context via STDIN. See Hook Actions for details on how commands receive event data.
Hook configurations are JSON files stored in .kiro/hooks/. Each file defines one or more hooks with a trigger event, an optional matcher pattern, and an action.
When the trigger event fires, Kiro checks the matcher. If it matches (or no matcher is specified), the action executes:
| Trigger | When it fires | Can block? |
|---|---|---|
PostFileSave | After a file is saved | No |
PostFileCreate | After a new file is created | No |
PostFileDelete | After a file is deleted | No |
PreToolUse | Before a tool is about to execute | Yes |
PostToolUse | After a tool has executed | No |
UserPromptSubmit | When a message is sent to the agent | Yes |
SessionStart | When a new session begins | No |
Stop | When the agent finishes responding | No |
PreTaskExec | Before a spec task starts | Yes |
PostTaskExec | After a spec task completes | No |
See Hook Triggers for detailed descriptions, matcher patterns, and use cases for each trigger type.
Each hook file is a standalone JSON file at .kiro/hooks/<id>.json. The full schema:
{ "version": "v1", "hooks": [ { "name": "example-hook", "trigger": "PostFileSave", "matcher": "\\.(ts|tsx)$", "action": { "type": "command", "command": "npx eslint --fix" } } ] }
| Field | Required | Description |
|---|---|---|
version | Yes | Schema version - currently "v1" |
hooks | Yes | Array of hook definitions |
hooks[].name | Yes | Human-readable identifier for the hook |
hooks[].description | No | Documentation only |
hooks[].trigger | Yes | Event that fires the hook (PascalCase - see triggers table) |
hooks[].matcher | No | Regex pattern to filter which events fire this hook. For PreToolUse/PostToolUse, matches tool name. For file events, matches file path. Defaults to always-match. |
hooks[].action.type | Yes | "command" (shell command) or "agent" (inject prompt) |
hooks[].action.command | Cond. | Shell command to run (required when type is "command") |
hooks[].action.prompt | Cond. | Prompt text to inject (required when type is "agent") |
hooks[].timeout | No | Timeout in seconds for command actions (default: 60). 0 disables the timeout. Ignored for agent actions. |
hooks[].enabled | No | Set false to skip the hook without deleting it (default: true) |
hooks[].confirm | No | Ask for confirmation before a Stop command hook runs. See Confirmation prompts. |
A command hook on the Stop trigger can ask before it runs. Add a confirm block with the question to ask and the options to present:
{ "version": "v1", "hooks": [ { "name": "Submit session results", "trigger": "Stop", "action": { "type": "command", "command": "./submit.sh" }, "confirm": { "question": "Submit this session's results?", "options": [ { "id": "submit", "label": "Yes, submit", "run": true }, { "id": "dismiss", "label": "Not this time", "run": false } ] } } ] }
Each option has an id, a label shown on the button, and a run flag that controls whether the hook's command executes when that option is chosen.
To decide at run time whether and what to ask, add an optional confirmCommand to the confirm block. The command runs before the prompt appears, and its stdout controls the prompt as JSON:
{ "skip": true } suppresses the prompt and skips the hook for this turn{ "question": "...", "options": [...] } replaces the static question and options{ "confirm": { "question": "Submit this session's results?", "confirmCommand": "./confirm-options.sh", "options": [ { "id": "submit", "label": "Yes, submit", "run": true }, { "id": "dismiss", "label": "Not this time", "run": false } ] } }
If confirmCommand exits non-zero, times out, or prints invalid JSON, the static question and options are used as a fallback. This makes it useful for prompts that should only appear under certain conditions - for example, a "don't ask again this session" option that writes a marker file and returns { "skip": true } on later turns.
.kiro/hooks/ in your project root.json filename works - use descriptive kebab-case names (e.g., lint-on-save.json, guard-writes.json)hooks arrayClick the + button in the Agent Hooks section of the Kiro panel and select Ask Kiro to create a hook. Describe what you want in natural language - for example, "run tests after every file save" - and Kiro generates the hook configuration through conversation.
The resulting hook is saved as a JSON file in .kiro/hooks/.
The .kiro/hooks/*.json format was introduced in IDE 1.0 and CLI 3.0. If you're upgrading from an earlier version:
kiro-cli agent migrate to auto-convert, or see CLI 3.0 Hooks migration for the manual mapping.
Hooks