Creating Kiro Hooks from Scratch
Hooks are Kiro’s automation engine. They let Kiro automatically perform actions — fix lint errors, run tests, update docs, generate PR descriptions — whenever a specific event happens in your workspace, without you having to type a prompt.
Hooks vs MCP: Hooks automate what Kiro does. MCP controls what Kiro knows. See MCP vs Hooks for a full comparison.
What Is a Hook?
Section titled “What Is a Hook?”A hook is a YAML file that answers two questions:
- When? — Which event should trigger this hook (file saved, test failed, PR opened…)
- What? — What should Kiro do when that event fires (a natural-language prompt)
When the event occurs, Kiro reads the prompt, picks up the context from the event (the file path, the test output, the PR diff), and performs the action autonomously.
Folder Structure
Section titled “Folder Structure”All hooks live in .kiro/hooks/ at the root of your project:
your-project/├── .kiro/│ ├── hooks/│ │ ├── auto-lint.yaml ← fix lint on save│ │ ├── update-docs.yaml ← keep docs in sync with code│ │ ├── run-tests.yaml ← run tests when services change│ │ └── pr-description.yaml ← auto-generate PR descriptions│ ├── steering/│ └── mcp.json├── src/└── package.jsonThe .kiro/ directory should be committed to source control so the whole team shares the same hooks.
Step-by-Step: Create Your First Hook
Section titled “Step-by-Step: Create Your First Hook”Step 1 — Create the hooks directory
Section titled “Step 1 — Create the hooks directory”If it doesn’t exist yet:
mkdir -p .kiro/hooksStep 2 — Create a YAML file
Section titled “Step 2 — Create a YAML file”Name the file something descriptive. The name becomes the hook’s identity in Kiro’s UI.
touch .kiro/hooks/auto-lint.yamlStep 3 — Write the hook YAML
Section titled “Step 3 — Write the hook YAML”Open the file and write the hook definition:
name: Auto-fix lint errors on savetrigger: event: file_saved filePattern: "**/*.{ts,tsx,js,jsx}"action: prompt: | The file {{file}} was just saved. Run ESLint on it and automatically fix any lint errors. Do not change any logic — only fix lint rule violations. If there are no lint errors, do nothing.Step 4 — Reload Kiro
Section titled “Step 4 — Reload Kiro”Kiro picks up new hooks automatically when you save the YAML file. No restart needed. You’ll see the hook appear in Kiro’s Hooks panel.
Step 5 — Trigger the event to test it
Section titled “Step 5 — Trigger the event to test it”For the example above, save any .ts or .tsx file. Kiro should automatically fix any lint errors in that file.
Full YAML Schema
Section titled “Full YAML Schema”# Required: display name shown in Kiro's Hooks panelname: <string>
# Required: what fires this hooktrigger: event: <event-name> # see event list below filePattern: <glob> # optional — only for file_* events
# Required: what Kiro should doaction: prompt: | <natural language instructions for Kiro> Use {{variable}} placeholders for dynamic values.
# Optional: conditions that must be true for the hook to runconditions: - <condition expression>
# Optional: prevent hook from firing more often than thisdebounce: <milliseconds>
# Optional: set to false to temporarily disable without deleting the fileenabled: <true|false>All Available Trigger Events
Section titled “All Available Trigger Events”File Events
Section titled “File Events”| Event | Fires when | Useful filePattern examples |
|---|---|---|
file_saved | A file is saved in the editor | **/*.ts, src/**/*.py, **/*.{js,jsx} |
file_created | A new file is created | src/components/**/*.tsx, **/*.test.ts |
file_deleted | A file is deleted | **/*.md |
file_renamed | A file is renamed or moved | **/* |
Test Events
Section titled “Test Events”| Event | Fires when |
|---|---|
test_failed | Any test in the test suite fails |
test_passed | All tests pass after a previously failing run |
Git / CI Events
Section titled “Git / CI Events”| Event | Fires when |
|---|---|
pull_request_opened | A PR or MR is opened |
pull_request_updated | New commits are pushed to an open PR |
code_merged | A branch is merged |
build_failed | A CI build step fails |
build_passed | A build that was failing now passes |
IDE Events
Section titled “IDE Events”| Event | Fires when |
|---|---|
session_started | Kiro session opens (project loaded) |
terminal_error | A terminal command exits with a non-zero code |
Template Variables
Section titled “Template Variables”Use {{variable}} placeholders in your action.prompt. Kiro substitutes them with real values at runtime.
| Variable | Available in | Value |
|---|---|---|
{{file}} | file_* events | Full path of the file that triggered the hook |
{{file_name}} | file_* events | Filename only (no path) |
{{file_extension}} | file_* events | Extension (e.g. ts, py) |
{{changed_files}} | Git events | List of files changed in the commit/PR |
{{test_output}} | test_failed | Full output from the test runner |
{{error_message}} | terminal_error, build_failed | Error text from the terminal/build |
{{branch_name}} | Git events | Current git branch |
{{pr_title}} | PR events | Title of the pull request |
{{pr_diff}} | PR events | Full diff of the PR |
Practical Hook Examples
Section titled “Practical Hook Examples”1 — Auto-fix lint errors on save
Section titled “1 — Auto-fix lint errors on save”name: Fix lint errors on savetrigger: event: file_saved filePattern: "**/*.{ts,tsx,js,jsx}"action: prompt: | {{file}} was just saved. Run ESLint and fix any violations automatically. Only fix lint issues — do not change any logic or formatting beyond what ESLint requires.2 — Keep documentation in sync with code
Section titled “2 — Keep documentation in sync with code”name: Update JSDoc when function changestrigger: event: file_saved filePattern: "src/**/*.{ts,js}"action: prompt: | The file {{file}} was just saved. Review all exported functions and classes in the file. If any have changed signatures or behaviour since their JSDoc comment, update the JSDoc. If a public function has no JSDoc at all, add one. Do not modify any implementation code.3 — Run related tests when a service file changes
Section titled “3 — Run related tests when a service file changes”name: Run tests when service changestrigger: event: file_saved filePattern: "src/services/**/*.ts"debounce: 3000action: prompt: | The service file {{file}} was just saved. Identify the test file(s) that cover this service. Run those tests. If any fail, show me the failure output and fix the implementation to make the tests pass — do not modify the tests themselves.4 — Auto-generate PR description
Section titled “4 — Auto-generate PR description”name: Generate PR description on opentrigger: event: pull_request_openedaction: prompt: | A pull request was just opened on branch {{branch_name}}. Read the diff in {{pr_diff}} and write a professional PR description that includes: - A one-line summary of what changed - Why the change was made (infer from the code and branch name) - A bullet list of specific changes - How to test this change manually - Any potential side effects or things reviewers should pay attention to Post this as the PR body.5 — Fix failing tests automatically
Section titled “5 — Fix failing tests automatically”name: Auto-fix failing teststrigger: event: test_failedaction: prompt: | Tests just failed. Here is the output: {{test_output}}
Analyse the failures. For each failing test: 1. Determine whether the test is correct and the implementation is wrong, or vice versa. 2. If the implementation is wrong, fix it. 3. If the test assertion is outdated due to intentional code changes, update the test. Do not delete failing tests — fix the underlying issue.6 — Generate a summary on session start
Section titled “6 — Generate a summary on session start”name: Morning standup summarytrigger: event: session_startedaction: prompt: | Kiro just started. Run `git log --since="yesterday" --oneline` and summarise what was committed yesterday. Also list any TODO comments added in the last 24 hours. Format this as a brief standup-style summary.7 — Catch secrets accidentally committed
Section titled “7 — Catch secrets accidentally committed”name: Warn about secrets in new filestrigger: event: file_created filePattern: "**/*.{env,json,yaml,yml,ts,js,py}"action: prompt: | A new file was created: {{file}}. Scan it for accidentally included secrets — API keys, tokens, passwords, connection strings. Common patterns: strings starting with "sk-", "ghp_", "AKIA", anything with "password" or "secret" as a key. If you find any, warn me immediately and suggest moving them to environment variables. If nothing suspicious is found, do nothing.Using Conditions
Section titled “Using Conditions”Conditions let you narrow when a hook fires beyond just the event type:
name: Only lint TypeScript files in src/trigger: event: file_saved filePattern: "**/*.ts"conditions: - "{{file}} starts with src/"action: prompt: | Fix lint errors in {{file}}.Combining Hooks with MCP
Section titled “Combining Hooks with MCP”Hooks become significantly more powerful when Kiro can also read external context via MCP. The hook triggers the action; MCP provides the data:
name: Verify implementation matches Jira tickettrigger: event: pull_request_openedaction: prompt: | A PR was opened for branch {{branch_name}}. Extract the Jira ticket key from the branch name (format: PROJ-123). If a ticket key is found: 1. Read the Jira ticket using the Jira MCP connection. 2. Compare the acceptance criteria in the ticket against the changes in {{pr_diff}}. 3. List any acceptance criteria that are NOT addressed by the PR changes. 4. Post a comment on the PR with the gap analysis. If no ticket key is found, skip.This requires the Atlassian MCP integration to be configured.
Debugging Hooks
Section titled “Debugging Hooks”Hook not firing?
- Check the
.kiro/hooks/directory is at your project root (same level as.git/) - Verify the YAML is valid — use a YAML linter. A single indentation error disables the whole file.
- Confirm
enabledis not set tofalse - Check that
filePatternmatches the file you’re saving (test with**/*temporarily)
Hook fires but does nothing useful?
- Open Kiro’s Hooks panel to see the last run log
- Make the prompt more specific — “Run ESLint” is better than “check the code”
- Add context: tell Kiro which tool to run, what to look for, and what not to change
Hook fires too often?
- Add
debounce: 2000(2 seconds) to avoid re-triggering on every keystroke during rapid saves
Hook interferes with your work?
- Set
enabled: falsetemporarily or delete the file — hooks are cheap to recreate
Best Practices
Section titled “Best Practices”| Do | Don’t |
|---|---|
| One concern per hook file | Put unrelated automations in one file |
| Be explicit in prompts: name the tool, the scope, the constraint | Write vague prompts like “improve this file” |
Add debounce to file_saved hooks | Let file_saved hooks fire on every single keystroke |
Commit .kiro/hooks/ to git | Add it to .gitignore |
Test with a safe scope first (filePattern) | Apply to all files without testing |
| Say “do nothing if no issues found” | Let Kiro make unnecessary edits |
Hook File Template (Copy & Use)
Section titled “Hook File Template (Copy & Use)”Save this as .kiro/hooks/my-hook.yaml and fill in the blanks:
name: <Describe what this hook does in one line>trigger: event: <event-name> filePattern: "**/*.<extension>" # remove if not a file eventdebounce: 2000 # remove if not neededenabled: trueaction: prompt: | <Tell Kiro exactly what happened: {{file}} was saved / tests failed / a PR was opened.> <Tell Kiro what to do: run X tool, fix Y issue, generate Z output.> <Tell Kiro what NOT to do: don't change logic, don't modify tests, only fix lint.> <Tell Kiro what to do if nothing needs doing: if no issues found, do nothing.>