Your AI coding agent writes the code. autogit ships it.
When your agent finishes a turn, autogit stages, commits, and pushes — automatically.
# 1. Install (once per machine)
npm install -g @davidondrej/autogit
autogit setup
# 2. Enable it per repo
cd your-project
autogit onDone. Every agent turn now ends with: stage → secrets scan → commit → push.
From source instead:
git clone https://github.com/davidondrej/autogit && cd autogit && npm link
| Agent | After autogit setup |
|---|---|
| Claude Code | works immediately |
| Cursor | works immediately — interactive local + worktree agents, including repos entered or created after the session starts (agent -p has no stop hook) |
| Pi | works immediately |
| Codex | one-time approval: restart open sessions, then run /hooks in codex and trust autogit (needs ≥ 0.124) — covers the CLI, the Codex desktop app, and the IDE extension |
Hooks fire for interactive local agent sessions. Cursor
agent -p, Cursor cloud agents (no local user hooks), Codex cloud tasks, andcodex execare not covered — upstream limitations. Codex re-asks for/hookstrust whenever autogit updates its hook entries.
autogit setup Wire up agent hooks (once per machine)
autogit on Enable auto-push in this repo
autogit on --agent Agent mode: an LLM reviews each turn before it ships
autogit off Disable auto-push in this repo
autogit ship Stage, scan, commit, push (what the hooks run)
autogit undo Take back the last autogit commit, local + remote
autogit status Show hooks + repo state
autogit update Update autogit to the latest version from npm
autogit --version Print the installed version (-v)
Commit messages: autogit ship -m "message" uses your message. Without -m, the subject is the prompt you gave your agent that turn (so git log reads like your instructions), falling back to a list of changed files. If the prompt looks like it contains a secret (pasted API key, token, etc.), it's never used — the commit gets the file-list subject instead.
Undo: shipped something you regret? autogit undo rewinds the remote branch, removes the commit locally, and leaves the changes uncommitted in your working tree — ready to fix and re-ship. Run it again to peel off earlier autogit commits. It refuses to touch commits it didn't make, or remotes that have since moved on.
Update: autogit update fetches the latest release (npm install -g under the hood) and prints old → new version. Running from a source checkout (npm link)? It refuses and points you at git pull instead.
Status: autogit status reports the version, which agent hooks are wired, whether auto-push is on here, and a busy line. busy: N agent(s) mid-turn — shipping deferred means another agent is still active in this repo, so your change is staged-pending — not lost or failed; the last agent to finish ships everything. If something hasn't pushed yet, check here first: a deferred ship during concurrent agents is normal, not a bug.
Plain autogit ships every turn, subject = your prompt. Agent mode puts an LLM between the turn and the push: one cheap call reviews the staged diff (plus your prompt and recent commit subjects) and decides — ship, or hold?
autogit on --agent # asks for an OpenRouter API key once per machine- Holds work-in-progress and meaningless churn (debug prints, scratch files, half-implemented features). Held changes stay in your working tree, get re-staged next turn, and are re-reviewed with the bigger diff — nothing is ever discarded.
- Ships everything else, with a descriptive commit subject written from the diff — not from your prompt. This fixes the messy-
git logproblem too. - Fails open. No key, API down, 15 s timeout, unusable reply → ships exactly like plain auto mode, with a stderr note. A flaky API can never silently stop your pushes.
autogit statuswarns when the key is missing. - Secrets stay guarded. The secrets scan runs before the review call, so an unscanned diff never leaves your machine; a prompt that looks like it contains a secret isn't sent either.
-mbypasses the gate — an explicit message is human intent, no LLM call spent.- Key never touches the repo — it lives in
~/.autogit/config.json(mode 0600). TheOPENROUTER_API_KEYenv var overrides it. Agents enable withautogit on --agent --key <key>. - Undo works unchanged — agent-shipped commits carry the same trailer.
Model: defaults to z-ai/glm-5.2 (fast, ~free at diff-review volumes). Pin per repo with --model <id> (stored in .autogit.json), or set a machine default (model in ~/.autogit/config.json). A baseUrl there points the calls at any OpenAI-compatible endpoint. Back to plain auto-push: autogit on --auto.
- Opt-in per repo — repos without
autogit onare never touched. - Public-repo check —
autogit onwarns when the repo is public on GitHub (your prompts become public commit messages). Humans confirm with y/N; agents must re-run withautogit on --public-ok. - Multi-account aware — if
ghhas several GitHub accounts logged in,autogit onasks which one pushes this repo and pins it (repo-local git config). Agents pass--account <name>. - One-command undo —
autogit undotakes back the last auto-push, remote included. - Secrets scan — blocks pushes containing API keys, private key blocks,
.envfiles, or JWTs, and unstages everything. Template files (.env.example,*.sample,*.template,*.dist) are fully exempt — filename and contents — they're meant to be committed, and a template's values are placeholders by definition. Obvious placeholder values (your-key-here,AKIA…EXAMPLE,xxxx…) are exempt everywhere else too. Override real blocks with--force-secrets. Commit messages are covered too: a prompt containing a secret never becomes the subject (not overridable). - No noise — nothing changed means nothing shipped. Aborted or errored Cursor turns never ship.
- Remote-moved aware — if something else pushed first (another machine, the GitHub web editor),
shiprebases your commit onto the new remote tip and retries. Real conflicts stop safely: commit kept locally, fix spelled out. - Parallel-agent aware — if another agent is still mid-task in the same repo, autogit waits its turn: the last agent to finish ships everything. (For fully separate commits per agent, use worktrees — autogit handles each independently.)
For contributors, human or AI. The implementation is a reference of product intent, not fixed architecture.
- Single zero-dependency Node.js CLI:
index.js, ESM, Node ≥18. - Commands:
setup,on,off,ship,undo,busy,status,update, plus-v/--version(read frompackage.json, also shown bystatus). autogit update(added 2026-07-04, after users couldn't find how to update): runsnpm install -g @davidondrej/autogit@latest, then reports old → new by asking the fresh binary for its version (the running process is still old code). Source checkouts refuse with agit pullhint — detected by.gitnext toindex.js(npm strips it on publish) — so npm can't bury annpm linksymlink under the registry tarball.- Modes (DECIDED 2026-06-10: auto only; agent added 2026-07-12): auto — ship immediately, no gate. agent — an LLM reviews each turn before it ships (see below). human (terminal y/n) remains on the roadmap.
- Agent mode (DECIDED 2026-07-12): a separate OpenRouter call — this supersedes the 2026-06-09 decision that the currently-running agent should review. Reason: no agent (Claude/Codex/Cursor/Pi) exposes a review channel from a stop hook — the turn is already over, and exit-2 tricks are exactly what the fail-safes forbid. One API call works uniformly for all four; sending the turn's prompt with the diff recovers most task context.
- Gate runs after every local gate (opt-in, busy-defer, secrets, branch) so a blocked turn never burns an LLM call;
-mskips it (human intent). - Fail open on everything (no key, timeout 15 s, HTTP error, unparseable reply): ship with plain auto behavior + stderr note. Rationale: autogit is a backup as much as a publisher — a flaky API must never silently stop pushes. Hold =
git reset, changes stay in the working tree, re-reviewed next turn. - Key in
~/.autogit/config.json(0600;AUTOGIT_HOMErelocates it for tests/dotfiles), never repo-local —.autogit.jsonis often committed.OPENROUTER_API_KEYenv wins. Model precedence: repomodel> globalmodel> built-in default. GlobalbaseUrlsupports any OpenAI-compatible endpoint. - No
response_formatin the API call (per-model support varies) — strict-JSON instructions plus defensive parsing (fence/prose stripping, booleancommitrequired); anything unusable fails open. Diff capped at 50 KB with an explicit truncation note. LLM subjects go through the same 72-char flattening as prompts. on --agentprobes the key (GET/key) while a human is present: definitive 401/403 dies without enabling; network trouble passes — offline must never block setup.
- Gate runs after every local gate (opt-in, busy-defer, secrets, branch) so a blocked turn never burns an LLM call;
- npm name (DECIDED 2026-06-10):
@davidondrej/autogit— unscopedautogit/autogit-clitaken;auto-gitrejected by npm's name-similarity rule. The installed binary staysautogit. Scoped packages neednpm publish --access=public. - Per-repo opt-in is the safety model:
autogit onwrites.autogit.json; without it,shipis a silent no-op (exit 0). Only enable it where aggressive auto-push is OK. - Public-repo guard in
on(added 2026-06-12): parses theoriginURL for agithub.comslug, probeshttps://api.github.com/repos/<slug>anonymously (3 s timeout, explicit User-Agent — GitHub rejects UA-less requests). HTTP 200 = public → TTY asks y/N (Ctrl+C/D = No), non-TTY (an agent) dies pointing at--public-ok. 404/offline/non-GitHub remotes enable silently — best-effort guard, never a gate.--public-okskips the probe entirely. Runs only aton; the ship path is untouched. - Multi-account pin in
on(added 2026-06-12): gh's credential helper only ever serves its active account (cli/cli#11938), so auto-pushes to a second account's repos 403. Detection reads gh'shosts.ymldirectly (instant —gh auth statushits the network for seconds). 2+ accounts on an HTTPS github.com remote → TTY asks which account (number, name, or Enter = active), non-TTY dies pointing at--account <name>. The pin is repo-local git config:credential.username(osxkeychain/GCM route by it) plus an appended last-resortcredential.helperthat servesgh auth token --user <name>. SSH remotes skip the question (keys route those). Re-pin anytime withautogit on --account <name>(works while already on);statusshows the pin;offleaves it in place — it fixes manual pushes too. autogit setupwires lifecycle hooks globally: Claude CodeStop(~/.claude/settings.json), CodexStop(~/.codex/hooks.json, ≥0.124, one-time/hookstrust), Cursorstop(~/.cursor/hooks.json, lowercase events +version: 1), and a Pi extension (~/.pi/agent/extensions/autogit.ts, fires onagent_end). Cursor also imports Claude hooks, so generated Claude commands no-op whenCURSOR_VERSIONis present; only Cursor's native copies perform Git work.- Codex legacy
notifyis NOT used (single-slot, often taken by other tools; an upstream deprecation was attempted and reverted in 0.129). Codex hook commands run in the sessioncwd, unsandboxed, via$SHELL -lc— sogit pushhas network and the user's PATH. - Codex surfaces (verified 2026-06-10): the desktop app and IDE extension run the same CLI core and execute the same
~/.codex/hooks.json; cloud tasks never fire local hooks, andcodex exechook dispatch is broken upstream (openai/codex#26452). Trust is hash-based — any change to the wired commands silently un-trusts them until the user re-runs/hooks; editing hooks.json mid-session disables hooks until Codex restarts (#21160). Esc-interrupted turns fire noStop; that turn's changes ship with the next one (busy-marker TTL self-heals). shipreads an optional JSON payload from stdin (all hook systems pipe one). Cursor repo resolution is strict and ordered: the opted-in repo saved frompostToolUse.cwd, then the current turn's transcriptworking_directory, thenworkspace_rootsas the multi-root fallback. This covers nested repos and repos created after a session starts without recursively scanning folders. Cursorstatusmust becompleted; aborted/errored turns never push. Claude/Codex payloads fall through to cwd behavior.
git add -A → secrets scan on added lines (AWS/OpenAI/Anthropic/GitHub/Slack/Google keys, private key blocks, .env filenames, JWTs; --force-secrets overrides) → agent gate (agent mode only: ship-or-hold + subject) → commit → push to origin/current branch.
Scan false-positive guards (added 2026-07-03, after every-turn blocks in the field; widened 2026-07-04): filenames ending .example/.sample/.template/.dist skip the scan entirely — filename check and content scan. (Contents were scanned at first, but a realistic-looking dummy value is indistinguishable from a real key, so templates kept blocking. Naming a file *.example declares its contents committable — deliberate trade-off: a real key pasted into a template ships.) Elsewhere, a matched token containing an obvious placeholder word (your, example, changeme, xxxx, …) is ignored — the placeholder test runs against the matched token only, never the whole line, so a real key on a line mentioning "example" still blocks. Note a blocked file stays in the working tree: git add -A re-stages it next turn, so a real stray .env re-blocks every turn until removed or gitignored (the error message says so).
Non-fast-forward rejection (remote moved — a push from elsewhere) self-heals: fetch, git rebase FETCH_HEAD, push again once. The tree is always clean at that point (everything was just committed) so the rebase is safe, and the Shipped-by trailer survives it, so undo keeps working. A rebase conflict aborts cleanly: commit kept locally, manual fix printed (git pull --rebase && git push). Any other push failure still dies with the commit kept locally.
Commit subject precedence: -m flag > agent-mode message (LLM-written, from the diff) > the turn's user prompt > the agent's final message (Codex last_assistant_message) > file-list fallback (autogit: update X, Y (+N more)). Prompt-derived subjects are first checked against SECRET_PATTERNS (full text, pre-truncation — the diff scan never sees the message): a match drops to the file-list fallback, with a stderr note. --force-secrets deliberately does not override this. The prompt comes from the session's busy-marker content (see below), a prompt-like field in the stop payload, or the last real user message in the Cursor/Claude/Codex transcript_path JSONL (formats are officially unstable, so parsing is defensive; tool results and <-prefixed noise like <user_instructions> are skipped). Subjects are flattened to one line, capped at 72 chars. Every commit gets a Shipped-by: autogit trailer — that's how undo identifies autogit commits.
Escape hatch for bad auto-pushes; one commit per run, repeatable. Refuses unless the last commit has the Shipped-by: autogit trailer (or legacy autogit: subject prefix). Order matters: it rewinds the remote first (push --force-with-lease of the parent, only if the remote tip still equals the shipped commit), then git reset <parent> (mixed) locally so the changes land back in the working tree uncommitted. Remote tip == parent means the push never landed → local-only undo. Remote moved past the commit → die, undo manually. Works even after autogit off (falls back to default remote origin).
- Problem:
git add -Awould scoop up a second agent's half-finished work when the first agent's turn ends. - Solution: while an agent is mid-turn it holds a marker file in
<git-dir>/autogit-busy/<session-id>.shipclears its own marker, then defers (exit 0, stderr note) if any live other agent's marker survives. The last agent to finish ships everything. No polling, no daemon. - Liveness is the core check (not just freshness). Each marker stores its owning agent's process id, so
shipcan tell a working agent from a ghost.sweepBusyscans every marker (no early return) and deletes any that can't be a live other agent: owned by a dead process (crashed/closed agent), owned by this agent's own pid (a leftover from a churned session id — compaction/clear/resume), or past the TTL backstop. This is why a deferred ship no longer freezes: an orphan marker is reaped on the next ship instead of blocking for the full TTL.- The agent pid is resolved by walking up from the hook's parent past the throwaway shell wrapper (
cd … && autogit …) to the first non-shell ancestor — the long-lived agent. Pi spawnsautogitdirectly, so its parent already is the agent.
- The agent pid is resolved by walking up from the hook's parent past the throwaway shell wrapper (
- Markers are written/refreshed by
autogit busy, wired to: ClaudeUserPromptSubmit+PostToolUse, CodexUserPromptSubmit+PostToolUse, CursorbeforeSubmitPrompt+postToolUse, Piagent_start+tool_execution_end. Tool hooks refresh the marker so long turns stay fresh. Cursor also records the current generation's exact opted-in repo under~/.autogit/cursor-sessions/(0600);stopconsumes and deletes it. - Marker payload is JSON
{ pid, prompt }. Prompt-submit hooks carry the user's prompt, sobusywrites it into the marker; tool hooks carry none, so they only bump mtime (preserving the content).shipreads its own marker before clearing it and uses the prompt as the commit subject. Pi's hooks don't expose the prompt — Pi ships with the file-list fallback. Legacy plain-text markers (pre-pid) are still read as{ pid: null, prompt }and fall back to TTL-only gating. - TTL (
BUSY_TTL_MS, 10 min) is now a backstop, not the primary signal: it reaps legacy pid-less markers and guards against a recycled pid that happens to look alive. Liveness reaps real ghosts immediately. - Markers live under the resolved git dir (
git rev-parse --git-dir), so each worktree has its own set — parallel worktree agents never block each other. autogit busymust stay silent on stdout (some hooks parse stdout). Session ids come from hook payloads (session_id/conversation_id/thread_id/turn_id) or--id(Pi). No id → no marker (an unattributable marker can never be cleared by its owner and would block shipping until stale).- Limit: simultaneous agents in ONE directory still end up in one blended commit (shipped by the last finisher). True isolation = worktrees.
- Hooks must never disturb the agent:
shipexits 0 on every no-op path, and never exits 2 (Claude would block its Stop hook; Codex would treat stderr as instructions and continue the turn). All output goes to stderr — Codex parses Stop-hook stdout as JSON and injects UserPromptSubmit stdout into model context. - Secrets scan blocks the push and fully unstages (
git reset). autogit undoreverses a bad ship — remote rewind + local uncommit, never touches non-autogit commits.- Nothing staged → no commit, no push, no noise.
Owner-gated — don't build these without a go-ahead.
agent mode— shipped 2026-07-12 (see Agent mode above; supersedes the 2026-06-09 running-agent idea).- human mode — terminal y/n prompt on the diff, for production repos. (Existed in the pre-MVP prototype, cut for focus.)
- More agents in
setup(Pi added 2026-06-10; Hermes next:post_llm_callshell hook in~/.hermes/config.yaml+ readingcwdfrom stdin JSON inship+ user consent flow). - Branch strategy: currently current-branch push only; auto-branch + PR flow considered.
MIT