[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-92235":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":14,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":15,"rankGlobal":9,"rankLanguage":9,"license":16,"archived":17,"fork":17,"defaultBranch":18,"hasWiki":19,"hasPages":17,"topics":20,"createdAt":9,"pushedAt":9,"updatedAt":21,"readmeContent":22,"aiSummary":23,"trendingCount":14,"starSnapshotCount":14,"syncStatus":13,"lastSyncTime":24,"discoverSource":25},92235,"autogit","davidondrej\u002Fautogit","davidondrej","Auto commit & push for AI coding agents, with a safety level you pick.",null,"JavaScript",57,13,2,0,43.44,"MIT License",false,"main",true,[],"2026-07-22 04:02:05","# autogit\n\n\u003C!-- User-facing flow up top; contributor internals below. -->\n\nYour AI coding agent writes the code. **autogit ships it.**\n\nWhen your agent finishes a turn, autogit stages, commits, and pushes — automatically.\n\n## Quick start\n\n```bash\n# 1. Install (once per machine)\nnpm install -g @davidondrej\u002Fautogit\nautogit setup\n\n# 2. Enable it per repo\ncd your-project\nautogit on\n```\n\nDone. Every agent turn now ends with: **stage → secrets scan → commit → push.**\n\n> From source instead: `git clone https:\u002F\u002Fgithub.com\u002Fdavidondrej\u002Fautogit && cd autogit && npm link`\n\n## Supported agents\n\n| Agent | After `autogit setup` |\n| --- | --- |\n| **Claude Code** | works immediately |\n| **Cursor** | works immediately — local + worktree agents (cloud agents don't fire stop hooks yet) |\n| **Pi** | works immediately |\n| **Codex** | one-time approval: restart open sessions, then run `\u002Fhooks` in `codex` and trust autogit (needs ≥ 0.124) — covers the CLI, the Codex desktop app, and the IDE extension |\n\n> Hooks fire for local agent sessions. Delegated\u002Fcloud runs (Cursor cloud agents, Codex cloud tasks) and `codex exec` don't fire them yet — upstream limitations. Codex re-asks for `\u002Fhooks` trust whenever autogit updates its hook entries.\n\n## Commands\n\n```\nautogit setup     Wire up agent hooks (once per machine)\nautogit on        Enable auto-push in this repo\nautogit off       Disable auto-push in this repo\nautogit ship      Stage, scan, commit, push (what the hooks run)\nautogit undo      Take back the last autogit commit, local + remote\nautogit status    Show hooks + repo state\nautogit update    Update autogit to the latest version from npm\nautogit --version Print the installed version (-v)\n```\n\n**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.\n\n**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.\n\n**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.\n\n**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.\n\n## Safety\n\n- **Opt-in per repo** — repos without `autogit on` are never touched.\n- **Public-repo check** — `autogit on` warns when the repo is public on GitHub (your prompts become public commit messages). Humans confirm with y\u002FN; agents must re-run with `autogit on --public-ok`.\n- **Multi-account aware** — if `gh` has several GitHub accounts logged in, `autogit on` asks which one pushes this repo and pins it (repo-local git config). Agents pass `--account \u003Cname>`.\n- **One-command undo** — `autogit undo` takes back the last auto-push, remote included.\n- **Secrets scan** — blocks pushes containing API keys, private key blocks, `.env` files, 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).\n- **No noise** — nothing changed means nothing shipped. Aborted or errored Cursor turns never ship.\n- **Remote-moved aware** — if something else pushed first (another machine, the GitHub web editor), `ship` rebases your commit onto the new remote tip and retries. Real conflicts stop safely: commit kept locally, fix spelled out.\n- **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.)\n\n## Internals\n\nFor contributors, human or AI. The implementation is a reference of product intent, not fixed architecture.\n\n### Design\n\n- Single zero-dependency Node.js CLI: `index.js`, ESM, Node ≥18.\n- Commands: `setup`, `on`, `off`, `ship`, `undo`, `busy`, `status`, `update`, plus `-v`\u002F`--version` (read from `package.json`, also shown by `status`).\n- `autogit update` (added 2026-07-04, after users couldn't find how to update): runs `npm install -g @davidondrej\u002Fautogit@latest`, then reports old → new by asking the fresh binary for its version (the running process is still old code). Source checkouts refuse with a `git pull` hint — detected by `.git` next to `index.js` (npm strips it on publish) — so npm can't bury an `npm link` symlink under the registry tarball.\n- One mode for now (DECIDED 2026-06-10): **auto** — ship immediately, no review gate. Review modes are on the roadmap.\n- npm name (DECIDED 2026-06-10): **`@davidondrej\u002Fautogit`** — unscoped `autogit`\u002F`autogit-cli` taken; `auto-git` rejected by npm's name-similarity rule. The installed binary stays `autogit`. Scoped packages need `npm publish --access=public`.\n- Per-repo opt-in is the safety model: `autogit on` writes `.autogit.json`; without it, `ship` is a silent no-op (exit 0). Only enable it where aggressive auto-push is OK.\n- Public-repo guard in `on` (added 2026-06-12): parses the `origin` URL for a `github.com` slug, probes `https:\u002F\u002Fapi.github.com\u002Frepos\u002F\u003Cslug>` anonymously (3 s timeout, explicit User-Agent — GitHub rejects UA-less requests). HTTP 200 = public → TTY asks y\u002FN (Ctrl+C\u002FD = No), non-TTY (an agent) dies pointing at `--public-ok`. 404\u002Foffline\u002Fnon-GitHub remotes enable silently — best-effort guard, never a gate. `--public-ok` skips the probe entirely. Runs only at `on`; the ship path is untouched.\n- Multi-account pin in `on` (added 2026-06-12): gh's credential helper only ever serves its *active* account (cli\u002Fcli#11938), so auto-pushes to a second account's repos 403. Detection reads gh's `hosts.yml` directly (instant — `gh auth status` hits 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 \u003Cname>`. The pin is repo-local git config: `credential.username` (osxkeychain\u002FGCM route by it) plus an appended last-resort `credential.helper` that serves `gh auth token --user \u003Cname>`. SSH remotes skip the question (keys route those). Re-pin anytime with `autogit on --account \u003Cname>` (works while already on); `status` shows the pin; `off` leaves it in place — it fixes manual pushes too.\n- `autogit setup` wires lifecycle hooks globally: Claude Code `Stop` (`~\u002F.claude\u002Fsettings.json`), Codex `Stop` (`~\u002F.codex\u002Fhooks.json`, ≥0.124, one-time `\u002Fhooks` trust), Cursor `stop` (`~\u002F.cursor\u002Fhooks.json`, lowercase events + `version: 1`), and a Pi extension (`~\u002F.pi\u002Fagent\u002Fextensions\u002Fautogit.ts`, fires on `agent_end`). All JSON configs merge through one helper; Claude\u002FCodex share the same `Stop` entry shape.\n- Codex legacy `notify` is 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 session `cwd`, unsandboxed, via `$SHELL -lc` — so `git push` has network and the user's PATH.\n- Codex surfaces (verified 2026-06-10): the desktop app and IDE extension run the same CLI core and execute the same `~\u002F.codex\u002Fhooks.json`; cloud tasks never fire local hooks, and `codex exec` hook dispatch is broken upstream (openai\u002Fcodex#26452). Trust is hash-based — any change to the wired commands silently un-trusts them until the user re-runs `\u002Fhooks`; editing hooks.json mid-session disables hooks until Codex restarts (#21160). Esc-interrupted turns fire no `Stop`; that turn's changes ship with the next one (busy-marker TTL self-heals).\n- `ship` reads an optional JSON payload from stdin (all hook systems pipe one): Cursor's carries `workspace_roots` (its hooks run from `~\u002F.cursor`, not the project — multi-root workspaces ship every opted-in root) and `status` (`ship` only proceeds on `completed`, so aborted\u002Ferrored turns never push). Claude\u002FCodex payloads lack these fields and fall through to cwd behavior.\n\n### How `ship` works\n\n`git add -A` → secrets scan on added lines (AWS\u002FOpenAI\u002FAnthropic\u002FGitHub\u002FSlack\u002FGoogle keys, private key blocks, `.env` filenames, JWTs; `--force-secrets` overrides) → commit → push to `origin`\u002Fcurrent branch.\n\nScan false-positive guards (added 2026-07-03, after every-turn blocks in the field; widened 2026-07-04): filenames ending `.example`\u002F`.sample`\u002F`.template`\u002F`.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).\n\nNon-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.\n\nCommit subject precedence: `-m` flag > 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), or a `prompt`-like field in the stop payload, or the last real user message in the `transcript_path` JSONL — both Claude transcript and Codex rollout line shapes are parsed (formats are officially unstable, so parsing is defensive; tool results and `\u003C`-prefixed noise like `\u003Cuser_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.\n\n### How `undo` works\n\nEscape 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 \u003Cparent>` (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`).\n\n### Parallel agents (busy markers)\n\n- Problem: `git add -A` would scoop up a second agent's half-finished work when the first agent's turn ends.\n- Solution: while an agent is mid-turn it holds a marker file in `\u003Cgit-dir>\u002Fautogit-busy\u002F\u003Csession-id>`. `ship` clears 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.\n- Liveness is the core check (not just freshness). Each marker stores its owning agent's process id, so `ship` can tell a working agent from a **ghost**. `sweepBusy` scans every marker (no early return) and deletes any that can't be a live other agent: owned by a dead process (crashed\u002Fclosed agent), owned by *this* agent's own pid (a leftover from a churned session id — compaction\u002F`clear`\u002Fresume), 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.\n  - 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 spawns `autogit` directly, so its parent already is the agent.\n- Markers are written\u002Frefreshed by `autogit busy`, wired to: Claude `UserPromptSubmit` + `PostToolUse`, Codex `UserPromptSubmit` + `PostToolUse`, Cursor `beforeSubmitPrompt` + `postToolUse`, Pi `agent_start` + `tool_execution_end`. Tool hooks refresh the marker so long turns stay fresh.\n- Marker payload is JSON `{ pid, prompt }`. Prompt-submit hooks carry the user's prompt, so `busy` writes it into the marker; tool hooks carry none, so they only bump mtime (preserving the content). `ship` reads 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.\n- 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.\n- 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.\n- `autogit busy` must stay silent on stdout (some hooks parse stdout). Session ids come from hook payloads (`session_id`\u002F`conversation_id`\u002F`thread_id`\u002F`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).\n- Limit: simultaneous agents in ONE directory still end up in one blended commit (shipped by the last finisher). True isolation = worktrees.\n\n### Fail-safes\n\n- Hooks must never disturb the agent: `ship` exits 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.\n- Secrets scan blocks the push and fully unstages (`git reset`).\n- `autogit undo` reverses a bad ship — remote rewind + local uncommit, never touches non-autogit commits.\n- Nothing staged → no commit, no push, no noise.\n\n## Roadmap\n\nOwner-gated — don't build these without a go-ahead.\n\n- **agent mode** — an LLM reviews the diff before push, for more serious repos. Owner decision 2026-06-09: the *currently-running* agent should review (it has task context), not a separate OpenRouter call. Mechanics TBD.\n- **human mode** — terminal y\u002Fn prompt on the diff, for production repos. (Existed in the pre-MVP prototype, cut for focus.)\n- More agents in `setup` (Pi added 2026-06-10; Hermes next: `post_llm_call` shell hook in `~\u002F.hermes\u002Fconfig.yaml` + reading `cwd` from stdin JSON in `ship` + user consent flow).\n- Branch strategy: currently current-branch push only; auto-branch + PR flow considered.\n\nMIT\n","autogit 是一个为 AI 编程代理（如 Claude Code、Cursor、Pi、Codex）提供自动化 Git 提交与推送的工具。它在代理完成一轮代码生成后，自动执行文件暂存、敏感信息扫描、提交（支持自定义或基于提示语的智能消息）和远程推送，并提供安全回滚（autogit undo）、状态检查与版本更新等配套命令。项目采用 JavaScript 实现，通过钩子机制集成主流本地 AI 编程环境，强调安全性（如自动过滤含密提示语）与可逆性。适用于 AI 辅助开发流程中需高频、可靠、可审计地同步代码变更的场景，尤其适合本地化运行的 AI 编程工作流。","2026-07-08 04:29:55","CREATED_QUERY"]