Repository navigation
feat: ship the branch guard and formatter hooks in the arjs plugin - #5
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…commands Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rg repositories Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wire guard-protected-branches (PreToolUse, Bash|PowerShell) and format-on-edit (PostToolUse, Edit|Write|MultiEdit) through plugins/arjs/hooks/hooks.json, document them with the repository settings snippet, and release arjs 0.3.0. The engines field now matches what the locked vitest requires (Node >= 22.12). Refs #1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uard
- A `git switch` or `git checkout` earlier in the same command sets the
branch that later commits and pushes in that directory run on, so the
release skill's `git switch dev && ... && git push` is no longer taken
for a push to main.
- Braces that open a block split segments, and shell keywords (`then`,
`do`, `else`, `elif`, `!`, `if`, `while`, `until`) are skipped before
git, so PowerShell's `if ($?) { git commit }` and Bash `then git commit`
are checked. `HEAD@{0}` and quoted braces are left alone.
- `git push --tags` without refspecs pushes tags alone: no branch target.
- Git is spawned only when the verdict needs it: the branch for a commit,
a bare push or a HEAD refspec; the scope check only before blocking.
Both are cached per directory.
- Malformed stdin exits 0 quietly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lint "<pkg> is not installed; run npm install" is reported only when the repository's root package.json declares the tool in dependencies or devDependencies. An undeclared, uninstalled tool is skipped silently; an installed one still runs. Malformed stdin exits 0 quietly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ADME - AGENTS.md: what the repository is, the Git flow, the plugin version rule and how to run the tests; the guard's message points to it. - The release skill pushes `origin dev` explicitly when re-syncing dev. - README: the guard refuses a push to main; it is a guard-rail against mistakes, not an access control. - test.yml: read-only contents permission. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A branch recorded from `git switch`/`git checkout` now holds only while
every separator since the switch is `&&`. After `;`, `||`, `|`, a newline
or a brace, the switch may have failed and the next command still run, so
recorded branches other than `main` are dropped and the guard reads the
branch from git again. A recorded `main` is kept across any separator.
`git switch missing; git commit` on main is blocked again.
Braces inside a word (`HEAD@{0}`, `${VAR}`) are counted separately, so
their `}` no longer closes an enclosing block.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Linux a backslash is a filename character, so `cd "<dir>\"` names a missing directory and the guard rightly lets the commit through; the CI ubuntu job failed on that. The path assertions now run on Windows only, and a platform-neutral one checks the parse itself: in PowerShell the quote after `C:\dir\` closes, so the push that follows is seen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 10, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #1. This is the plugin side. The three repositories drop their copies in follow-up PRs.
The two Claude Code hooks copied into arjs-plugin-artoolkit, AR.js-next and arjs-plugin-threejs now ship once, in the
arjsplugin. They are wired byplugins/arjs/hooks/hooks.jsonthrough${CLAUDE_PLUGIN_ROOT}.Hooks
guard-protected-branches.mjs(PreToolUse,Bash|PowerShell) refusesgit commitonmainand pushes tomain. It is imported from arjs-plugin-artoolkit@f8a6604 and changed as follows:scope.mjs): anyremote.<name>.urlundergithub.com[:/]AR-js-org/, so forks with anupstreamremote count too. A plugin hook runs in every repository, so outside this scope the guard does nothing.Set-Location/sl/Push-Locationwith-Path/-LiteralPath.if ($?) { … }(the PowerShell 5.1 chaining idiom) and Bashthen/do/elsebodies.git switch/git checkout [-c|-b]within one command, but only across&&. A switch tomainholds across any separator. Without this, thereleaseskill'sdevre-sync was falsely blocked.git push --tagsis no longer blocked. It spawns git only when a verdict needs it, and it exits 0 silently on malformed stdin.format-on-edit.mjs(PostToolUse,Edit|Write|MultiEdit) runs the repository's own prettier and eslint--fixon the edited file. Changes:package.jsondeclares the tool. Repositories that don't use prettier get no noise.Repository
package.json(Vitest, Node ≥ 22.12 for the tests; the hooks use only Node built-ins) and CI on ubuntu and windows.AGENTS.md. The guard's message points to it, and this repository is protected by the guard too.extraKnownMarketplaces/enabledPluginssnippet), migration note, tests.releaseskill: the step 3 re-sync pushesorigin devexplicitly.0.3.0.Known limits
The guard is a guard-rail against mistakes. GitHub branch protection stays the enforcement. It lets through, by design: subshells,
bash -c, git aliases,Pop-Location,-Path:"x", and a recordedmainoverwritten by a later failed switch. It blocks, fail-closed, a non-main switch followed by;(use&&).Migration
Project hooks and plugin hooks are not deduplicated. Each repository must drop
.claude/hooks/in the same PR that enables the plugin. Those PRs (artoolkit, AR.js-next, and threejs#6) follow this one.Verification
npm test: 48/48 on Windows. CI runs ubuntu and windows.claude plugin validate: passes.hooks.jsoncommands against an AR-js-org repository and a foreign repository.claude --plugin-dir ./plugins/arjs, in a throwaway clone of arjs-plugin-artoolkit onmainwith its own.claude/hooksremoved:/hookslists[Plugin] Bash|PowerShelland[Plugin] Edit|Write|MultiEditfromarjs@inline, and no project hooks.git commit --allow-empty -m testonmainis blocked with the plugin's message.git logshows no new commit.git add -A; if ($?) { git commit -m test }is blocked.git statusshows nothing staged, because the whole call was refused.git switch -c test/x && git commit --allow-empty -m testfrommaingoes through, because the guard follows the switch.# Prova formattertoREADME.mdends up as# Prova formatter, with a blank line before it, from the repository's own prettier.enabledPluginsroute (trusted folder, no--plugin-dir).🤖 Generated with Claude Code