Writing rules
Your rules live in stele.md, at the root of your project. A rule can be as simple as one sentence:
Never put credentials in code, config or tests.
That’s a complete rule. It applies to every file the agent writes.
Several rules
Put a line with only --- between rules:
Never put credentials in code, config or tests.
---
Keep functions under 50 lines.
Apply a rule to some files only
Add a scope line, then a blank line, then the rule:
scope: migrations/
Never edit a migration that has been merged. Add a new one instead.
Now the agent sees this rule only when it writes under migrations/.
A few examples of scopes:
| You write | It matches |
|---|---|
*.go | Go files, in any folder |
**/*.go | Go files, in any folder |
/*.go | Go files at the root only |
migrations/ | Everything in migrations/ |
src/**/*.{go,ts} | Go and TypeScript files under src/ |
["internal/**/*.go", "!**/*_test.go"] | Go files in internal/, except tests |
Scope as much as you can. A rule with no scope is shown for every file, and too many of those is just noise.
Have stele check a rule
So far, these rules are only guidance. The agent reads them, but nothing checks that it listened.
To check a rule, give stele a pattern that spots a break:
scope: ["**/*.go"]
check: regex
pattern: fmt\.Print(f|ln)?\(
message: log through internal/log, not fmt.Print
Log through the shared logger in internal/log. Never print with fmt.Print,
Printf or Println.
If the agent writes a line that matches, stele sends it back with your message, and CI fails too.
Patterns use Go’s regular expressions. Two things tend to surprise people:
- The pattern looks at text, so it also matches inside comments and strings.
- Quotes count.
pattern: "net/http"looks fornet/httpwith the quotes around it.
Warn instead of block
A checked rule blocks by default. To get a warning instead, add this line:
blocking: false
After you change a rule
Run stele compile, and commit stele.lock and .claude/rules/stele-rules.md along with stele.md.
If you forget to compile, the hook does it for you on the next write, so your edit still takes effect. You just still have those two files to commit.
If something looks wrong
If a rule can’t be used, stele compile tells you which one and why. The other rules keep working.
stele won’t catch a misspelled key, though. If you write scopes: instead of scope:, the line is ignored and the rule applies to every file.