Steering files
A steering file is a Markdown file with your project’s rules for the AI: coding style, how the API is laid out, what to never touch. The header at the top of the file says when a chat gets it. The format is Kiro’s, so steering files you already wrote for Kiro work here without changes.
Off by default. Turn it on in settings.json, in your profile or
in the workspace’s .local-workflows/settings.json for the whole team:
{
"features": {
"steering": true
}
}
While it is off, no steering file is looked for, no chat gets the
steering tool, and #name in a compose box is plain text.
Where they can live
Anywhere in the folders the chat works in: the working folder, and the
workspace folders open in the window. .kiro/steering/ works, and so
does src/api/rules.md.
Not read:
- Anything git ignores, and anything in
.gitornode_modules. - Your home folder’s
~/.kiro/steering/.
The header
The header is the block between two --- lines at the very top of the
file. A file is a steering file only if its header has inclusion:.
A README.md, a docs page, or a file with no header at all is never
used. So is a file with tags: and no inclusion:: blog posts and docs
pages carry tags: too, and they are not rules for the AI.
---
inclusion: always
---
# How we write C#
- One class per file.
- ...
| Header | What happens |
|---|---|
inclusion: always |
Attached to the first message of every chat. |
inclusion: fileMatch |
Attached to an SDD implement wave whose Files: lines match its pattern. See fileMatch. |
inclusion: manual + tags: [api, dotnet] |
Not attached. The AI loads it by tag when its work needs it. See Tags. |
inclusion: manual |
Attached to one message when you type #name in a compose box. See #name. |
inclusion: auto |
Not used. Add tags: to it instead. |
No header, or no inclusion: |
Not used. |
“Every chat” means SDD phases, each SDD implement wave, ai@1 steps in
a workflow, and every compose box - the AI chat
sidebar and its editor tab included. Follow-up messages and continued
chats do not get the files again: the chat already has them.
A file with a header this build cannot read, such as
inclusion: sometimes, or fileMatch with no pattern, is skipped, and
the run log says which file and why.
fileMatch
---
inclusion: fileMatch
fileMatchPattern: "src/api/**"
---
# How our API controllers look
...
fileMatchPattern is one pattern or a list: ["*.ts", "*.tsx"].
- Where it works: SDD implement only. When a wave starts, its
tasks’
Files:lines are checked against every pattern, and each file that matches is attached to that wave’s chat. - Where it does not: everywhere else. Other SDD phases, workflow steps and the compose box do not know which files the chat will touch before it starts, and a file the AI happens to open later never pulls one in. That is on purpose: it is how Kiro ends up filling chats with rules nobody needed.
- For the rest, add
tags:too. AfileMatchfile with tags is attached when a wave’s files match, and offered through thesteeringtool everywhere else.
Patterns are relative to the repository:
| Pattern | Matches |
|---|---|
src/api/** |
everything under src/api/, at any depth |
src/**/*.ts |
every .ts file under src/ |
src/*.ts |
.ts files directly in src/, not deeper |
*.tsx |
every .tsx file, at any depth. A pattern with no / matches the file name alone. |
src/{api,web}/*.ts |
.ts files directly in src/api/ or src/web/ |
src/?.ts |
src/a.ts, src/b.ts: ? is one character |
Case does not matter. [abc] character sets are not supported.
#name
Type # and a manual steering file’s name in any compose box, and
that file is attached to that one message. The name is the file name
without .md, case ignored: #api-rules finds api-rules.md in any
folder.
- Works in every compose box: an SDD phase, an implement wave’s chat, a workflow run’s AI Session tab, and the AI chat sidebar or its editor tab.
- Only
manualfiles. Analwaysfile is already in the chat. - The message is sent exactly as you typed it,
#api-rulesincluded. - Two files with the same name are both attached.
- A
#wordthat names nomanualfile is left alone, and the log saysNo manual steering file named #word.#12andissue#12are not read as names.
Tags
An always file is read in every chat, even when the work has nothing
to do with it. Tags are for everything else.
---
inclusion: manual
tags: [api, errors]
---
# How our REST errors look
...
Each chat gets a steering tool. Its description lists every tag in
the project and the files under each one. The AI calls it with the
tags that fit its work, and gets those files’ text back. Until it asks,
a tagged file costs one line in the tool’s description.
- Tags are your own words. Pick words that say what the rules are
about, like
api,payments,database. The AI sees the file names next to each tag, but a vague tag likemiscis still hard for it to choose. - Case does not matter:
APIandapiare the same tag. - A file with
inclusion: alwaysand tags is attached, and the tool does not offer it again. - A project with no tagged files gets no
steeringtool at all. - Every call goes in the run log: the tags the AI asked for, and the
files it got. If a tag is never asked for, rename it or make the
file
always.
Write tagged files as inclusion: manual plus tags:. With
manual, Kiro itself loads the file only when someone types #name,
and this extension offers it through the tool.
A file with tags: and no inclusion: is not a steering file here at
all, so the header needs both.
Which providers
| Provider | always and fileMatch files |
steering tool |
|---|---|---|
ghcp |
Yes | Yes |
claude |
Yes | Yes |
ACP providers (kiro-cli, gemini-cli, …) |
Yes | No |
ACP agents get attachments but cannot be given the engine’s own tools, so tagged files do not reach them.
How this differs from Kiro
- No header means not used. Kiro attaches a file with no header to every chat.
- Any folder, not only
.kiro/steering/. A steering file outside.kiro/steering/works here, and Kiro itself ignores it. tags:is this extension’s own key, not Kiro’s.fileMatchworks in SDD implement only, against the wave’sFiles:lines. Kiro matches files as the chat opens them.manualworks through#nameonly. Kiro also offers it as a slash command.autois not used. Addtags:instead.