auto-commit
post_tool_call for write_file, edit_file, and exec.What it does
When the agent successfully writes a file inside a directory you've allow-listed, auto-commit:
- Walks up looking for a
.gitdirectory; bails if none is found - Verifies the file's mtime is fresh (was actually written, not just read)
- Runs
git add <file> - Runs
git commit -m "agent: <verb> <filename>"with--no-verify --no-gpg-sign - Logs the result to an audit JSONL
The agent never knows about it. From the user's perspective, file changes the agent makes inside watched repos just show up in git log.
Setup
Two steps:
- Initialize git in the directories you want watched. auto-commit only runs in actual git repos:bash
cd /Users/me/projects/my-app git init git config user.email "you@example.com" git config user.name "You" git commit --allow-empty -m "initial" - Add the directory to the allow-list:Or to add several:text
/auto-commit add /Users/me/projects/my-apptext/auto-commit add /Users/me/projects /auto-commit add /Users/me/notes
Changes apply immediately — no gateway restart needed. Verify:
/auto-commit status
→ auto-commit is ENABLED (source: config.json). Allowed roots:
• /Users/me/projects/my-app (git repo)Environment variable override
For one-off scenarios — CI, test scripts, admin debugging — you can override the persistent allow-list with an env var:
FLOWLY_AUTO_COMMIT_PATHS=/tmp/test-repo:/tmp/other-repo \
flowly gatewayWhen set, the env var fully replaces the config-file list for that gateway session. /auto-commit status reports(source: env var FLOWLY_AUTO_COMMIT_PATHS).
Slash commands
/auto-commit status Show enabled state and allowed roots
/auto-commit add <path> Add a path to the persistent allow-list
/auto-commit remove <path> Remove a path from the allow-list
/auto-commit log [count] Show recent commit events (default: 20)
/auto-commit help Usage helpadd warns if the path doesn't exist or isn't a git repo, but still saves it — so you can pre-configure a directory before git init.
log output looks like:
Last 5 events:
2026-04-29T01:10:42 ✓ test4.py (exec)
2026-04-29T01:10:45 ✗ no_staged_changes test4.py
2026-04-29T01:11:02 ✓ helper.py (write_file)The ✓ rows are commits; ✗ are skip events (no changes, file outside repo, debounce hit, etc.).
How it works
The hook runs through five gates, in order. Failing any gate ends silently with an audit log entry:
- Tool filter — only
write_file,edit_file, andexecare considered. - Path extraction — for
write_file/edit_file, thepathparam. Forexec,shlex.splittokens that look absolute or~-relative. - Allow-list filter — path must resolve under one of the allowed roots.
- Git repo discovery — walk up looking for
.git, bail if none found. - Freshness check (exec only) — file mtime must be within the last 10 seconds. This filters read-only commands like
cat /repo/filethat mention paths but don't modify them. - Per-file debounce — same file can't commit twice in 30 seconds. Prevents commit storms when the agent edits the same file rapidly.
After all gates pass: git add <rel-path>, check git diff --cached --name-only to skip .gitignore'd or unchanged files, then git commit.
Commit messages
agent: create hello.py # write_file
agent: edit handler.py # edit_file or execThe verb is create for write_file, edit for the rest. There's no body — the one-line summary is enough for filtering with git log --oneline | grep agent:.
--no-verify --no-gpg-sign so a slow pre-commit hook can't stall the agent loop and a missing signing key doesn't fail the commit. If you want signed commits, use a separate post-commit script that runs git commit --amend -S --no-edit on demand.Safety guarantees
- Opt-in by directory — files outside the allow-list are never touched.
- Git repo required — the plugin walks up looking for
.git; non-repo directories are silently ignored even if allow-listed. - Debounce — same file commits at most once per 30 seconds.
- Audit trail — every attempt (success, skip, error) is appended to
$FLOWLY_HOME/auto-commit/log.jsonl. - Error containment — git failures don't propagate. The hook catches everything, logs it, and returns. Agent loop never sees the error.
Known limitations
- One commit per file write. If the agent writes 10 files in a turn, you get 10 commits. Consider a follow-up rebase if this gets noisy.
- No commit grouping. Related changes the agent makes in sequence end up as separate commits, not a single one.
- shlex parsing edge cases. The exec branch uses
shlex.splitto extract paths from command lines. Heredocs work; very unusual quoting may fail to parse, in which case a regex fallback runs. - .gitignore'd files are silently skipped.
git addignores them and the diff is empty, so ano_staged_changesevent is logged instead ofcommitted. This is the desired behavior but can be surprising.
State files
$FLOWLY_HOME/auto-commit/
├── config.json Allow-list ({"paths": [...]})
└── log.jsonl Append-only audit (committed, hook_fired, errors)Both files are managed by the plugin. You can edit config.json directly, but the slash commands do atomic writes and validation — prefer those.
flowly/plugins_bundled/auto-commit/. Reference implementation of the configuration + audit log + slash command patterns documented elsewhere in this guide.