The hook that refuses a clone or install naming a blocked repository before it runs — what it sees, what it does not, and how to install it.
Everything above is advisory: the agent asks, and what it does with
the answer is up to it. Claude Code has a hook mechanism, so there the
answer can be enforced. The RepoGates plugin runs a
PreToolUse hook before every Bash tool call, sends
the command line to POST /v1/preflight — parsed on the
server, never executed, with URL credentials and
NAME=value assignments redacted first — and acts on the
decision. It also adds the MCP server and the vetting skill.
/plugin marketplace add https://repogates.com/plugin/marketplace.json
/plugin install repogates@repogates
The plugin is published from this site and nowhere else. That
marketplace file names one plugin, the zip it comes from and the zip's
sha256, so what installs is exactly what we built; /plugin
update picks up a new version when the file changes. Prefer a
folder you can read first? Download
repogates-claude-plugin.zip,
unzip it, and add the folder as a local marketplace:
/plugin marketplace add ./repogates-claude-plugin.
Then create a personal API token on your account page and save it from a terminal, not through an agent:
umask 077; mkdir -p ~/.config/repogates; pbpaste > ~/.config/repogates/token
Run /repogates:setup to check it: it exercises the hook
against a permanently blocked canary repository without ever printing
the token. On a REVIEW, /repogates:scan owner/repo scans
the files before you decide — priced in credits by size, and the command asks
first.
/repogates:fix-my-repo owner/repo runs the same scan on a
public repository you maintain and prints what to fix first: the
findings ranked, each with where it is, why it matters, what to change
and how to confirm it, and what changed since your previous scan of it.
The price and the asking are the scan command's. It prints the list and
does not edit your code; RepoGates never does. An agent may start a scan
only for a repository whose ordinary verdict is REVIEW or that could not
be assessed; one that passes, or is blocked, is scanned from your
account page, and the command then reads
that scan's plan and spends nothing. A scan reads the code and never
runs it, a list with nothing left on it is not an audit, and private
repositories are not scanned.
/repogates:checkup inventories what this machine's
AI-agent setup already has — installed plugins and their marketplaces,
skills, MCP servers, hooks, permission rules — and scores it with
RepoGates. It runs the repogates-checkup CLI, a wheel
served from this site at a version the command
pins, reads the configuration on the machine and sends RepoGates
identifiers, never contents. The check-up reads; it never runs a
program, never changes a file, and never blocks anything. What it
reads, what leaves, the score, the exit codes and what it cannot see:
the check-up page. Part of Personal,
Premium and Team.
| RepoGates says | The hook does |
|---|---|
| An active malware campaign, or a finding your policy profile blocks — hidden Unicode in agent rules, a nested bare repository, any CRIT the profile does not downgrade | Refuses the command, with the reason and a report link. |
| A finding your policy warns on — every REVIEW, and the CRITs the shipped profiles downgrade (a devcontainer, an install script); could not be assessed; the repository is a variable or a subshell; hourly cap reached; not answered in time | Asks you, with the reason and what decided it. In a headless run an ask cannot be answered, so the command does not run. In auto mode it does not reach you (below). |
| PASS | Adds a one-line note. Claude Code's own permission prompt still applies to the rest of the line — a PASS is never a permission. |
| Nothing — the line names no GitHub repository | Says nothing, and no request leaves the machine. |
| No answer — no token, an unknown or revoked token (401), no paid plan (402), rate limited (429), the service unreachable or answering 5xx, a missing tool, an interrupted hook | Asks you, naming the cause and the fix. No answer is not permission. |
Auto mode. In Claude Code's auto mode a RepoGates ask does not reach you: auto mode decides it without a prompt, and a refusal still holds. On 27 September 2026 auto mode approved a clone RepoGates had rated REVIEW, with no prompt; the same session's refusals held. Where a warning must reach a person, leave auto mode off.
Lines it recognises: git clone in every URL form,
git submodule add, git remote add,
gh repo clone, gh release download -R,
npm/pnpm/yarn/bun
installs with github:, o/r or
git+ specs, npx degit,
pip/uv/poetry with
git+ specs, go get, cargo --git,
and any github.com or githubusercontent.com
URL whatever the verb — curl …/raw/o/r/… | sh included —
also inside bash -c, eval, sudo,
chains and the heredocs a shell or a language runtime reads. A heredoc
that is only printed, or written to a note (.md,
.txt, .log…) and not run on the same line, is
text, not a fetch. Registry packages too: npm install
express, pip install flask, npx,
yarn add, uv add — RepoGates asks npm or PyPI
which source repository the package declares and assesses that one. A
package that declares none is allowed, with a note; a registry that
does not answer is asked about. Direct dependencies named on the command line only; transitive dependencies and lockfiles are not resolved.
The repository is assessed, not the artifact: the tarball or wheel the
registry serves for the version you install is not fetched or compared
with it.
Adding an MCP server starts its command later, every time the server
runs, so the hook reads that command when the server is added.
claude mcp add srv -- npx -y some-package and
claude mcp add-json srv '{"command":"npx","args":[…]}' are
read as if npx -y some-package had been typed: a package
or repository on the campaign list, or that your policy blocks, is
refused; one that cannot be assessed is asked about; one that passes
gets a note. A registry set in the server's environment
(-e npm_config_registry=…) is asked about, as on any other
line. A remote server (--transport http or
sse, or a URL where the command goes) is asked about:
remote MCP servers are not assessed yet. Listing, getting and removing
servers sends nothing. Not seen: a server added by editing a
configuration file, or through the /mcp menu.
Before a package manager runs, the shell can turn one word into
others: npm i ev*l matches files in the current folder,
pip install {fl,x}ask becomes two names, and an unquoted
requests[socks] is a pattern too. The name on the line is
then not the name that is installed, so the hook asks about such a
line instead of looking up the name as written. Quoted, the word is
left alone and read as the package it names
(pip install 'requests[socks]').
A second hook runs before Claude Code's Write, Edit, MultiEdit and
NotebookEdit tools. It reads only the text about to be written, never
the file already on disk, and compares it with about fifty key shapes
that have a distinctive prefix: GitHub, GitLab, Anthropic, OpenAI,
AWS, Google, Slack, Hugging Face, npm, PyPI, Stripe, Replicate, Groq,
xAI, OpenRouter, private-key headers and others. On a match it asks
you before the file is written, naming the provider, the file and the
line, never the value. The check runs on your machine and makes no
network call. A placeholder in a key's shape (ghp_XXXX…,
or one that names itself EXAMPLE, dummy or
your_…) is not asked about, and text over 2 MB, or any failure of the guard itself,
is a note, never an ask. It matches these patterns; a key in a shape it
does not know is not seen, and it does not scan files already on disk
(repogates-checkup secrets
does that, offline). From plugin 0.9.21.
What it does not cover. Anything that is not a Bash tool call:
Read, MCP git tools, a
human at a terminal, another agent. Edit and Write are read for one
thing only — an API key about to be saved — checked on the machine
and never sent; that guard asks, it never denies. A command whose repository is not
literally on the line (git clone $REPO,
git submodule update, a manifest such as
package.json) — those are asked about, not allowed. The
dependency tree: a named package is resolved to its own repository, the
packages it pulls in are not.
Sessions with hooks disabled. A hook killed at its 15-second timeout is
treated by Claude Code as "proceed", which is why the hook's own network
budget is 12 seconds at most. Windows is best effort. Verified against
Claude Code 2.1.251 on 2026-09-07: a refusal holds in headless runs and
inside subagents, and an "ask" that cannot be answered stops the
command; on 2026-09-09, with the plugin installed from the public
marketplace, the same refusal held under
--dangerously-skip-permissions.
What it does not see. RepoGates gates downloads made through the browser. It does not see git clone, package managers or curl — outside Claude Code with the RepoGates plugin, whose hook refuses a clone or install that names a blocked repository on the command line, before it runs. The hook sees Bash tool calls in that one client and nothing else. Edit and Write are read for one thing only — an API key about to be saved — checked on the machine and never sent; that guard asks, it never denies. VS Code and Docker Desktop extensions and agent skills are answered through the MCP server and the plugin, not in the browser.