Claude Code plugin

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.

The Claude Code plugin — the one hook that refuses

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.

The agent check-up

/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 saysThe 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

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.

Names the shell rewrites

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]').

The write guard

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.