<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: teefayb</title>
    <description>The latest articles on DEV Community by teefayb (@teefayb).</description>
    <link>https://dev.to/teefayb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4192036%2Ffd676f63-312d-49b2-8de5-d61d42e52da0.jpg</url>
      <title>DEV Community: teefayb</title>
      <link>https://dev.to/teefayb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/teefayb"/>
    <language>en</language>
    <item>
      <title>I tried 22 ways to force-push past my team's Claude Code rules. 7 were hard-blocked.</title>
      <dc:creator>teefayb</dc:creator>
      <pubDate>Sun, 18 Oct 2026 13:09:08 +0000</pubDate>
      <link>https://dev.to/teefayb/i-tried-22-ways-to-force-push-past-my-teams-claude-code-rules-7-were-hard-blocked-2b5d</link>
      <guid>https://dev.to/teefayb/i-tried-22-ways-to-force-push-past-my-teams-claude-code-rules-7-were-hard-blocked-2b5d</guid>
      <description>&lt;p&gt;If your team uses Claude Code, someone has probably put this in &lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push --force *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push -f *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push * main)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks like it closes the door. I wanted to know how far, so I listed 22 realistic ways&lt;br&gt;
to force-push or push to &lt;code&gt;main&lt;/code&gt; and ran each one against a real Claude Code session. The&lt;br&gt;
repo was a team demo with a full shared setup: deny rules, ask rules, hooks, skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7 of 22 were hard-blocked. The other 15 got past the deny rules.&lt;/strong&gt; Then I added one&lt;br&gt;
PreToolUse hook, and all 22 were blocked while normal feature-branch pushes still went&lt;br&gt;
through.&lt;/p&gt;

&lt;p&gt;Most Claude Code safety posts are about one developer on one laptop. This one is about the&lt;br&gt;
settings file a whole team shares, where one person's "yes, allow" can land on everyone's&lt;br&gt;
&lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I tested (without hurting anything)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every variant ran in a throwaway clone whose &lt;code&gt;origin&lt;/code&gt; was a local bare repo in a temp
folder. The real GitHub remote was never involved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules layer:&lt;/strong&gt; for each command, a headless session
(&lt;code&gt;claude -p --model haiku --setting-sources project&lt;/code&gt;) was asked to run it verbatim. I
read the tool result: "has been denied" means a deny rule matched. "Requires approval"
means it fell through to a permission prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hook layer:&lt;/strong&gt; a PreToolUse hook is just a script that reads JSON on stdin, so I piped
each command straight into it and checked the exit code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why does a prompt count as "got past"? A deny rule is a wall. A prompt is one click from&lt;br&gt;
running, and in &lt;code&gt;bypassPermissions&lt;/code&gt; mode, or with a broad &lt;code&gt;allow&lt;/code&gt;, there is no click at all.&lt;br&gt;
For something your team has decided to never allow, anything short of a hard block is a&lt;br&gt;
gap.&lt;/p&gt;
&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Deny rules only&lt;/th&gt;
&lt;th&gt;Rules + hook&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git push --force origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git push -f origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git push --force-with-lease origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git push origin +main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git push origin HEAD:main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git push origin HEAD:refs/heads/main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git push origin main:main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;git push&lt;/code&gt; (while on &lt;code&gt;main&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git push --mirror&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git push --all --force&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git -C . push --force origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git -c core.askPass=true push -f origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GIT_TRACE=0 git push -f origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/bin/git push -f origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cd . &amp;amp;&amp;amp; git push --force origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git status; git push -f origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sh -c "git push -f origin main"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bash -lc 'git push --force origin main'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eval "git push -f origin main"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git -c alias.p=push p -f origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;`echo main \&lt;/td&gt;
&lt;td&gt;xargs -I{} git push -f origin {}`&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;bash ship.sh&lt;/code&gt; (a script with a force push)&lt;/td&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Credit where it's due: Claude Code's matcher got some things right. It splits &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;;&lt;/code&gt;&lt;br&gt;
and checks each part, and it strips env-var prefixes, so #13, #15 and #16 were denied. The&lt;br&gt;
&lt;a href="https://code.claude.com/docs/en/permissions" rel="noopener noreferrer"&gt;permissions docs&lt;/a&gt; are upfront about the&lt;br&gt;
rest. They say a Bash rule "isn't a security boundary around the program", and they list&lt;br&gt;
&lt;code&gt;git -C . push&lt;/code&gt; and &lt;code&gt;sh -c&lt;/code&gt; as forms a &lt;code&gt;git push *&lt;/code&gt; rule won't match.&lt;/p&gt;

&lt;p&gt;What slipped through falls into four groups:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Refspecs.&lt;/strong&gt; &lt;code&gt;+main&lt;/code&gt;, &lt;code&gt;HEAD:main&lt;/code&gt; and &lt;code&gt;main:main&lt;/code&gt; don't end in &lt;code&gt;main&lt;/code&gt;, so
&lt;code&gt;git push * main&lt;/code&gt; never fires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;git's own options.&lt;/strong&gt; &lt;code&gt;git -C&lt;/code&gt;, &lt;code&gt;git -c&lt;/code&gt;, and aliases put words between &lt;code&gt;git&lt;/code&gt; and &lt;code&gt;push&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other ways to run a program.&lt;/strong&gt; &lt;code&gt;/usr/bin/git&lt;/code&gt;, &lt;code&gt;sh -c&lt;/code&gt;, &lt;code&gt;bash -lc&lt;/code&gt;, &lt;code&gt;eval&lt;/code&gt;, &lt;code&gt;xargs -I&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripts.&lt;/strong&gt; The rule sees &lt;code&gt;bash ship.sh&lt;/code&gt;, not what's inside it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Six of the 15 still prompted only because the team also had a broad &lt;code&gt;ask: git push *&lt;/code&gt; rule.&lt;br&gt;
Without that rule, #4–#9 would have been one auto-approve away from running.&lt;/p&gt;
&lt;h2&gt;
  
  
  The fix: a PreToolUse hook that reads the whole command
&lt;/h2&gt;

&lt;p&gt;Hooks run before permission rules. If a PreToolUse hook exits with code 2, the call is&lt;br&gt;
blocked, even when an allow rule would let it through. So the hook can look at the full&lt;br&gt;
command text and decide.&lt;/p&gt;

&lt;p&gt;It works in three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Split&lt;/strong&gt; the command into simple commands on &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, newlines, &lt;code&gt;$(...)&lt;/code&gt;
and backticks, keeping quoted strings together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unwrap&lt;/strong&gt; each one: drop &lt;code&gt;FOO=bar&lt;/code&gt; prefixes and wrappers (&lt;code&gt;env&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;xargs&lt;/code&gt;,
&lt;code&gt;nohup&lt;/code&gt;, &lt;code&gt;exec&lt;/code&gt;...), and reduce &lt;code&gt;/usr/bin/git&lt;/code&gt; to &lt;code&gt;git&lt;/code&gt;. Recurse into &lt;code&gt;sh -c&lt;/code&gt;/&lt;code&gt;bash -c&lt;/code&gt;
strings, &lt;code&gt;eval&lt;/code&gt; arguments and local script files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check&lt;/strong&gt; any &lt;code&gt;git push&lt;/code&gt;: skip git's global options (&lt;code&gt;-C&lt;/code&gt;, &lt;code&gt;-c&lt;/code&gt;, and pick up
&lt;code&gt;-c alias.x=push&lt;/code&gt;), then look at the flags and refspecs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the part that makes the call (trimmed from the real file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;push&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;positional&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;force&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;everything&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PUSH_OPTS_WITH_VALUE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^--force&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;-with-lease|-if-includes&lt;/span&gt;&lt;span class="se"&gt;)?(&lt;/span&gt;&lt;span class="sr"&gt;=|$&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="sr"&gt;/^-&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*f&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;force&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--mirror&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;everything&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--mirror&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--all&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--branches&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;everything&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;force&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;positional&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;force&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force pushes are blocked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;everything&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;git push &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;everything&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt; would push protected branches`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refspecs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;positional&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;refspecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;currentBranch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gitCwd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isProtected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;git push&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt; with no branch pushes the current branch (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;refspecs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`"&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" is a force push`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;$`*&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`can't verify the target of "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dst&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HEAD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dst&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;currentBranch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gitCwd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isProtected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`pushing to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;an unknown branch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For text it can't parse as a git call, like &lt;code&gt;python3 -c "subprocess.run(['git','push','-f',...])"&lt;/code&gt;,&lt;br&gt;
it falls back to a regex: &lt;code&gt;git&lt;/code&gt; + &lt;code&gt;push&lt;/code&gt; + a force flag or a protected branch name means&lt;br&gt;
block.&lt;/p&gt;

&lt;p&gt;Wire it up next to your deny rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${CLAUDE_PROJECT_DIR}/.claude/hooks/guard-git-push.mjs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the deny rules. They're the first layer, and they still work if someone disables hooks.&lt;br&gt;
I added a few after this test: &lt;code&gt;Bash(git push * +*)&lt;/code&gt;, &lt;code&gt;Bash(git push * *:main)&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;Bash(git push * *:refs/heads/main)&lt;/code&gt;, the &lt;code&gt;--mirror&lt;/code&gt; forms, and &lt;code&gt;master&lt;/code&gt;/&lt;code&gt;production&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The hook ships with 52 Vitest cases (39 should-block, 12 should-allow, plus a current-branch&lt;br&gt;
check), and they run with the team's normal &lt;code&gt;npm test&lt;/code&gt;. Tests matter here: the easy way to&lt;br&gt;
break a guard like this is a false positive, and if it blocks&lt;br&gt;
&lt;code&gt;git push origin feature/main-menu&lt;/code&gt;, people will just turn it off. These all pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git push origin feature-x
git push -u origin feature-x
git push origin feature/main-menu
git status &amp;amp;&amp;amp; git push origin feature-x
git push                (while on feature-x)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gotchas I hit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;claude -p&lt;/code&gt; ignores your allow rules in a folder it hasn't trusted.&lt;/strong&gt; It prints a
warning on stderr. Deny and ask rules still apply, so the test was still valid, but don't
read a headless run as "how my team's setup behaves" without checking that warning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In &lt;code&gt;-p&lt;/code&gt; mode, a prompt quietly turns into a no.&lt;/strong&gt; The tool result says "requires
approval" and nothing runs. That's why the before column says "Prompt", not "Ran": in my
tests nothing reached the remote. In an interactive or auto-approve session, those 15 are
one step away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;git rev-parse --abbrev-ref HEAD&lt;/code&gt; fails in a repo with no commits.&lt;/strong&gt; My first version
couldn't find the branch and blocked a plain &lt;code&gt;git push&lt;/code&gt; on a feature branch.
&lt;code&gt;git symbolic-ref --short HEAD&lt;/code&gt; works on an unborn branch. When the branch really can't be
known (detached HEAD), the hook blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Running this test in bypass mode didn't work.&lt;/strong&gt; Claude Code's safety classifier
wouldn't start an unattended session in &lt;code&gt;bypassPermissions&lt;/code&gt; mode for it. Fair enough. The
hook doesn't need a live session to test; piping JSON in is the documented way to check it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it doesn't protect against
&lt;/h2&gt;

&lt;p&gt;Be honest with your team about this part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Anything outside Claude's Bash tool:&lt;/strong&gt; your own terminal, a git GUI, CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indirection it can't read:&lt;/strong&gt; &lt;code&gt;npm run release&lt;/code&gt;, a Makefile target, a compiled binary, a
script that builds the command at runtime, or a script edited after the check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pushes without the git CLI:&lt;/strong&gt; &lt;code&gt;gh api&lt;/code&gt; or &lt;code&gt;curl&lt;/code&gt; against the GitHub API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anyone who edits the hook.&lt;/strong&gt; It's code in the repo, and it changes through a PR like
everything else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The real backstop is still &lt;strong&gt;branch protection on &lt;code&gt;main&lt;/code&gt;&lt;/strong&gt;: required review, no force&lt;br&gt;
pushes. The hook makes the safe path the default inside Claude Code. The server makes it the&lt;br&gt;
only path.&lt;/p&gt;




&lt;p&gt;The full setup is in the free demo repo, with the hook, tests, deny rules, team playbook and&lt;br&gt;
raw results: &lt;a href="https://github.com/teefayb/claude-code-setup-demo" rel="noopener noreferrer"&gt;https://github.com/teefayb/claude-code-setup-demo&lt;/a&gt;. If you'd rather have this&lt;br&gt;
fitted to your team's repo, I do that here:&lt;br&gt;
&lt;a href="https://contra.com/s/vXvWUYYB-claude-code-setup-for-dev-teams" rel="noopener noreferrer"&gt;https://contra.com/s/vXvWUYYB-claude-code-setup-for-dev-teams&lt;/a&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>devtools</category>
      <category>security</category>
    </item>
  </channel>
</rss>
