A language-agnostic push gate for regular git push workflows. An installed pre-push hook delegates into a managed Pushgate runner so local checks and AI review can fit the normal git push flow before changes reach the next layer of review.
git push
│
▼
┌─────────────────────────────────────┐
│ Changed files vs target branch │
│ (ignore_paths filtering applied) │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Run configured tools │
│ (linters, type checkers, tests) │
│ ✗ any failure → push blocked │
└──────────────┬──────────────────────┘
│ all pass
▼
┌─────────────────────────────────────┐
│ AI review via Claude Code CLI │
│ (diff sent, findings returned) │
│ BLOCK → push blocked │
│ PASS → push proceeds │
└─────────────────────────────────────┘
git push stays the main entry point. Pushgate plugs into it through the installed pre-push hook; pushgate push is an optional friendly wrapper for the same workflow.
Local deterministic checks can block a push. Local AI supports blocking, advisory, and off modes; blocking is the default, matching the review gate shown above. CI and PR checks remain the final enforcement point for policy that must survive local hook skips.
.pushgate.yml is the primary project config. .push-review.yml belongs to migration compatibility rather than the public config contract.
The current M1 runner boundary is intentionally thin: the installer wires the
hook to the managed pushgate command, the command accepts Git pre-push
context, and policy execution lands in the changed-file, deterministic-check,
and AI runner work that follows.
# Default (base template — no tools pre-configured, fully documented)
curl -fsSL https://raw.githubusercontent.com/rootstrap/ai-pushgate/main/install.sh | bash
# Node.js
curl -fsSL https://raw.githubusercontent.com/rootstrap/ai-pushgate/main/install.sh | bash -s -- --template node
# TypeScript
curl -fsSL https://raw.githubusercontent.com/rootstrap/ai-pushgate/main/install.sh | bash -s -- --template typescript
# Next.js
curl -fsSL https://raw.githubusercontent.com/rootstrap/ai-pushgate/main/install.sh | bash -s -- --template nextjs
# Ruby
curl -fsSL https://raw.githubusercontent.com/rootstrap/ai-pushgate/main/install.sh | bash -s -- --template ruby
# Ruby on Rails
curl -fsSL https://raw.githubusercontent.com/rootstrap/ai-pushgate/main/install.sh | bash -s -- --template railsThe installer:
- Installs the
pushgatecommand used by the hook - Downloads and validates
hook/pre-push→.git/hooks/pre-push - Backs up any existing
pre-pushhook before overwriting - Downloads the template config →
.pushgate.yml(only on first install — never overwrites) - Checks the Node.js runtime required by the managed command
Git is required. Pushgate plugs into its pre-push hook path.
Node.js is required by the installer-managed pushgate command.
AI providers depend on the configured mode. For example, Claude feedback requires Claude Code CLI:
npm install -g @anthropic-ai/claude-code
claude /loginConfigured tool runtimes depend on the tools you configure:
| Runtime | Required by |
|---|---|
| Node.js | node, typescript, nextjs templates |
| Ruby | ruby, rails templates |
| Python | Python tools (manual config) |
| Go | Go tools (manual config) |
After install, edit .pushgate.yml in your project root:
version: 2
ai:
# Supported modes: blocking (default), advisory, off.
mode: blocking
provider: claude
providers:
claude:
# Provider-specific settings live below the selected provider block.
model: claude-sonnet-4-20250514
review:
target_branch: main # local ref for git diff <target_branch>...HEAD
context_lines: 10 # surrounding context lines included in the diff
max_lines_for_full_file: 300 # below this threshold, full file contents are sent
# instead of just the diff for richer context
# Tools to run before AI review — first failure blocks the push immediately
tools:
- name: eslint
# Commands are argv arrays. {changed_files} is expanded by the runner.
command: ["npx", "eslint", "{changed_files}"]
extensions: [".js", ".jsx", ".ts", ".tsx"]
- name: brakeman
command: ["bundle", "exec", "brakeman", "--no-pager", "--quiet"]
# no {changed_files} → runs on the whole project
# Gitignore-like repo-relative paths excluded from tool checks and AI review
ignore_paths:
- "*.lock"
- "dist/**"
- "coverage/**"V2 configs must declare version: 2. Core config sections are strict, provider-specific config belongs below ai.providers.<provider>, and tool commands are argv arrays rather than shell strings. Reviewer focus and default finding-category instructions live with the built-in review prompt rather than the v2 config surface. See docs/v2-config-schema.md for the schema boundary, changed-file policy, and migration behavior for .push-review.yml.
--template |
Stack | Tools pre-configured |
|---|---|---|
base |
Any | None (fully-documented reference config) |
node |
Node.js | ESLint, Prettier, Jest |
typescript |
TypeScript | tsc, ESLint, Prettier, Jest |
nextjs |
Next.js | tsc, next lint, Prettier, Jest |
ruby |
Ruby | RuboCop, Reek, RSpec |
rails |
Ruby on Rails | RuboCop, Reek, Brakeman, RSpec |
To bypass the hook for a single push:
git push --no-verifyScoped one-push skip controls are the v2 contract for the runner work that follows. They use Git's temporary config channel:
git -c pushgate.skip-ai-check=true push
git -c pushgate.skip-all-checks=true pushThe planned optional wrapper maps friendly flags to the same one-push config:
pushgate push --skip-ai-check
pushgate push --skip-all-checksRe-run the installer to update the managed command and hook script. Your .pushgate.yml is never overwritten — it stays exactly as you've configured it.
curl -fsSL https://raw.githubusercontent.com/rootstrap/ai-pushgate/main/install.sh | bashTo also reset your config to a template, delete it first:
rm .pushgate.yml
curl -fsSL https://raw.githubusercontent.com/rootstrap/ai-pushgate/main/install.sh | bash -s -- --template <name>To add a new template:
- Add
templates/<name>.ymlfollowing the structure of an existing template (e.g.ruby.yml) - Add a row to the Available templates table in this README
- Open a pull request
Templates should include sensible ignore_paths defaults and pre-configured tools for the common tools in that stack. The base.yml template is the reference for all available config options.