Skip to content

Releases: yybd/repo-guards

RepoGuards 0.3.1

Choose a tag to compare

@yybd yybd released this 14 Oct 17:04

A patch, from a simulated-stranger re-test of 0.3.0. No blockers were found; these are the places where the guards and the README still disagreed.

The push refusal shows what it would have published

A refused foreign push now lists the commits it would have published — up to eight, then a count. When the branch is level with its upstream it says nothing is ahead; with no upstream it says git cannot tell.

  • Before: the refusal pointed at git log @{u}..HEAD and left the looking to the agent — the one step a session about to push is least likely to take.
  • Four new pipeline checks cover the three cases.

README

  • Ground Rule 4 quotes the Stop guard's real heading, "Work left open outside this session's own git status", instead of a sentence it never printed.
  • Registering by hand: the entry goes inside the existing projects object, beside the _example_* entries, which are ignored.
  • What deploy writes outside the repo: one env variable per place in ~/.claude/settings.json, and nothing else. The enabledPlugins and extraKnownMarketplaces entries there are Claude Code's own.
  • One $BASE for every command, including the try-it block.
  • Why claude plugin list shows a commit hash rather than a version: plugin.json declares none on purpose; releases are git tags.
  • "Upgrading from Grove" moved below "Known limits".

Also

  • The shared rules say what $SITE and $KEYS_ROOT are, and when each is absent.
  • deploy.py lists the registered places by name.
  • Comments no longer name one machine's paths.

RepoGuards 0.3.0

Choose a tag to compare

@yybd yybd released this 14 Oct 14:25

This release fixes what a stranger-install test found when it followed the README to the letter.

The guard now protects every repo

Any git repository other than the one the session is standing in now counts as foreign, whether or not it is registered.

  • Before: only repos in the registry counted. A push, commit -a or add . into a fresh, unregistered repo went through without a word, even though the README said the guards run everywhere.
  • Now: git decides. The target is foreign when its working tree (git rev-parse --show-toplevel) differs from the session's. The registry only gives the repo a name.
  • Eight new pipeline checks cover unregistered repos.

Also

  • Guard messages: they no longer quote the author's own incidents.
  • deploy.py:
    • when it fixes something, it now marks the fix "✓ done" instead of leaving a line that reads like a fault;
    • it lists the hooks the next session will load;
    • it counts anchor variables separately from repos;
    • it writes $GROVE only on a machine that already had it, and $KEYS_ROOT only once that folder exists.
  • close.py: prints its own full path.
  • register-project: no longer tells you to commit the registry, which is gitignored machine data.
  • README:
    • a prerequisites line;
    • a "Try it in 60 seconds" section that runs the real hook with no session;
    • how to register a repo by hand, as a three-line registry entry;
    • what registration actually writes;
    • a link to the owns.json format.
  • Shared rules: the author's dated incidents are gone.

Upgrading

git -C <clone> pull
python3 <clone>/tools/deploy.py

Then open a new session.

RepoGuards 0.2.0 — formerly Grove

Choose a tag to compare

@yybd yybd released this 14 Oct 13:38

Grove is now RepoGuards. The product is RepoGuards. Every identifier is repo-guards:

  • the repository, yybd/repo-guards
  • the marketplace and the plugin, repo-guards@repo-guards
  • the commands, /repo-guards:deploy and /repo-guards:register-project

The old GitHub address redirects.

Upgrading from 0.1.0

The marketplace and the plugin have new names, so an installed grove@grove is replaced rather than updated in place:

git -C <your clone> pull
claude plugin marketplace remove grove
claude plugin marketplace add "<your clone, absolute path>"
python3 <your clone>/tools/deploy.py

Then open a new session.

Renamed

Before Now
$GROVE $REPO_GUARDS
GROVE_* REPO_GUARDS_*
grove_repo guards_repo
~/.claude/.grove/ ~/.claude/.repo-guards/

For one release, the old names keep working:

  • deploy still writes $GROVE
  • every GROVE_* variable is still read as a fallback
  • grove_repo re-exports guards_repo

App Factory 0.4.0 relies on these, and App Factory 0.4.1 no longer needs them.

Also

  • The install instructions use an absolute path for marketplace add. A relative path is read as a GitHub owner/repo, and the install fails. A clean-HOME install test hit this.
  • A personal ~/.claude/CLAUDE.md can import the shared rules with an @path line instead of being a symlink to them. The staleness check accepts both forms.

grove 0.1.0

Choose a tag to compare

@yybd yybd released this 14 Oct 13:20

The first tagged release of grove: cross-repo guards for Claude Code.

What is in it

  • Seven guards. Two of them block.
    • honor_ownership refuses a write to a path that another repo declared in its .claude/owns.json. It checks the writing tools and shell lines alike: redirections, sed -i, cp, mv, tee and rm.
    • scope_foreign_commits refuses commit -a, add . and push into a repo the session is not standing in.
    • The other five report and do not block. For example: when a write into a foreign repo is allowed, it hands back that repo's git status, and at the end of a turn it says what was left open elsewhere.
  • tools/close.py reports every place the session touched. With --commit it commits in each of them only the paths this session wrote.
  • tools/deploy.py is the whole install. It writes the registry from the template, installs the plugin, and checks that everything is in place.

Install

git clone https://github.com/yybd/cross-repo-guards.git <base>/cross-repo-guards
claude plugin marketplace add <base>/cross-repo-guards
python3 <base>/cross-repo-guards/tools/deploy.py

Then open a new session.

Known limits

These are stated in the README, and the tests check that each one is still true:

  • Writes a script makes from the inside are out of reach.
  • A target the shell computes at run time is allowed.
  • Every guard fails open.

Built for Claude Code, and tested only there. CI runs on macOS and Linux, with Python 3.9, 3.12 and 3.13.