seil is a tool for managing lifecycle hooks across AI-native development workflows.
It lets you define setup, teardown, and post-edit automation once in .seil.yml and reuse it across integrations.
go install github.com/sushichan044/seil/cmd/seil@latestmise install github:sushichan044/seilDownload the latest release binaries from Releases.
- One config file for AI agent lifecycle hooks.
- Human-readable output by default, JSON output when automation needs structured results
post-edithooks can be filtered by glob and skipped for.gitignored files
You can route file edit events through seil post-edit instead of embedding formatter or lint commands directly in Claude Code hooks.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs -I {} env AI_AGENT=claude seil post-edit {} || exit 2"
}
]
}
]
}
}This keeps Claude Code responsible for detecting edits, while .seil.yml owns what should happen after the edit.
You can also connect workspace lifecycle events to setup and teardown.
[wt]
basedir = ".git/wt"
hook = "seil setup"
deletehook = "seil teardown"This works well when a worktree should run initialization on create and cleanup on delete.
Create .seil.yml in your repository:
setup:
jobs:
- name: tidy
run: go mod tidy
post_edit:
jobs:
- name: go-format
glob: "**/*.go"
run: mise run lint && mise run fmt
teardown:
jobs:
- name: cleanup
run: echo "done"# Run setup hooks
seil setup
# Run post-edit hooks for a file
seil post-edit internal/config/config.go
# Run teardown hooks
seil teardownUse --reporter json when another tool needs structured results.
seil --reporter json post-edit internal/config/config.goThe JSON result is grouped into failure, success, and skipped.
seil uses .seil.yml.
- If
-c, --config <path>is provided, that file is used. - Otherwise,
seilsearches from the current directory upward until the filesystem root. - If
.seil.ymlis not found during auto-discovery,seiltreats it as a no-op and exits successfully. - To use a different filename such as
seil.yml, pass it with-c, --config.
setup:
jobs:
- name: optional-name
run: required shell command
post_edit:
jobs:
- name: optional-name
glob: optional doublestar pattern
run: required shell command
teardown:
jobs:
- name: optional-name
run: required shell commandrunis executed throughsh -c.- Hook commands run with the directory containing
.seil.ymlas their working directory. nameis optional. If omitted,seilderives a normalized name fromrun.post_edit.jobs[].globuses doublestar matching such as**/*.go.post-editreceives the edited file path and makes it available to command templating.
Note
run commands use the directory containing the loaded .seil.yml as cwd.
This is true both when seil finds the config automatically and when you pass it with -c.
setupandteardownrun all configured jobs and preserve result order from the config.post-editskips jobs when theglobdoes not match the file path.post-editalso skips jobs when the file is matched by.gitignore.- If no config file is found during auto-discovery, all commands return an empty result set and exit with status code
0. - The default human-readable output includes grouped results, status, log path, and a short summary.
- If any hook fails,
seilexits with status code1for the default and JSON reporters, and2for the Claude reporter.
--reporter autois the default.AI_AGENToverrides automatic agent detection when--reporter autois used.seilcan auto-detectclaude,cursor,devin,replit,gemini,codex,auggie,opencode,kiro,goose, andpi.--reporter autoselects an agent-specific reporter when one exists.--reporter jsonalways keeps the JSON reporter, even when an agent is detected.--reporter defaultforces the default human-readable reporter.--reporter claudeforces the Claude reporter.- Agents without a dedicated reporter fall back to the default human-readable output.
- Go 1.26+
- mise for project tasks
mise run fmt
mise run lint:fix
mise run testMIT