AI agents¶
An AI agent (Claude Code, opencode…) can change a protected branch, work without a ticket or bypass the hooks. Git hooks stop it only at commit time and CI only at the PR: the work done until then is lost. The guard stops it before each action, with the repowarden command to run instead.
repowarden agents init # Claude Code and opencode, in the repository (to commit)
git add .claude/settings.json .opencode/plugins/repowarden-garde.js
What is refused¶
Only in a repowarden repository (.repowarden.conf, a repowarden.* setting or active repowarden hooks); anywhere else, everything is allowed. Same settings as the hooks.
| Agent action | Refused when | Instead |
|---|---|---|
| Change a file | detached HEAD; protected branch (protectedBranches, integrationBranch); if the repository tracks its tickets: branch that is not a ticket's branch (type/<n>-topic) |
repowarden ticket <n> (without ticket tracking: git switch -c <type>/<topic>) |
Create a branch (git checkout -b, git switch -c, git branch <name>, git worktree add -b) |
the repository tracks its tickets | repowarden ticket <n>: branches come from tickets |
git commit |
always, except --amend --no-edit |
repowarden cc -m "…" --refs "Ticket : #<n>" (or git cc) |
repowarden cc, git cc |
protected branch: the commit would be refused there, and without a terminal cc does not create a branch for the agent |
git switch -c <type>/<topic> (changes follow); if the repository tracks its tickets: repowarden ticket <n> |
any commit (git commit --amend --no-edit included) |
repowarden hooks inactive in the repository (no core.hooksPath to the repowarden hooks): messages, secrets and formatting would not be checked |
repowarden install (or --global) |
--no-verify (and git commit -n) |
always | run it again without, fix what the hooks report |
git add ., -A, --all |
always | git add <files> |
forced git push: --force, -f (-uf…), --force-with-lease[=…], --force-if-includes, +branch refspec |
always: it rewrites the history already pushed | fix it with a new commit (repowarden cc …), then git push without forcing |
gh issue create |
always | repowarden ticket nouveau "<title>" |
gh issue comment, gh issue edit |
always | repowarden ticket comment <n> "…", repowarden ticket label <n> <label> |
gh pr create |
always: the draft PR is opened by the bot on the first push | git push -u origin HEAD |
gh run rerun |
always | repowarden run rerun <id> |
gh pr ready, gh pr merge, gh pr review --approve, writing gh api …/pending_deployments |
always, even with a ticket: the maintainer's decision | say the PR is ready, the maintainer decides |
any other gh command (refused by default): writing gh api (-X other than GET, -f/-F/--input without -X GET, graphql mutation), gh repo edit, gh issue close, gh pr comment, gh pr review without --approve, gh release create, gh label create, gh gist, unknown subcommand… |
without a REPOWARDEN_TICKET=<n> prefix of an open and validated ticket (read with gh issue view <n>, in the current repository or the -R one); unreadable ticket: refused (a write without proof of a ticket) |
create the ticket (repowarden ticket nouveau), get it validated, then REPOWARDEN_TICKET=<n> gh … |
Only known gh reads are allowed without a ticket: view, list, status, checks, diff, watch, gh search, gh browse, read-only gh api, gh auth status, help (--help), plus gh pr edit (PR description). Anything that is not a read is refused without a ticket, forgotten or future subcommands included. Every change, GitHub settings included, starts from a ticket created beforehand.
Help and preview (--help, -h, --dry-run of repowarden cc, git cc, git commit) are always allowed: nothing is committed. Each command of a shell line is checked (&&, ;, |…), in the right repository (cd, git -C). A refusal applies to the whole line: none of it runs, not even the other commands (git add a.txt && git commit … adds nothing), and the message says so. A refusal gives the reason and the command:
✖ repowarden : change refused: "main" is a protected branch.
→ Work on a ticket's branch: repowarden ticket <n>
Supported tools¶
| Tool | Wiring | Project (--projet, default) |
User (--global) |
|---|---|---|---|
| Claude Code | PreToolUse hook (Edit, Write, MultiEdit, NotebookEdit, Bash tools): repowarden garde --format claude |
.claude/settings.json |
$CLAUDE_CONFIG_DIR/settings.json (if set), else ~/.claude/settings.json |
| opencode | plugin, tool.execute.before hook (edit, write, apply_patch, bash tools) |
.opencode/plugins/repowarden-garde.js |
$OPENCODE_CONFIG_DIR/plugins/ (if set), else ${XDG_CONFIG_HOME:-~/.config}/opencode/plugins/ |
repowarden agents init --claude or --opencode for a single tool. Re-runnable: an existing settings.json is completed (nothing if the hook is already there), a modified plugin is kept as is and reported. The guard follows repowarden updates: a plugin written by repowarden (first line // Garde-fou repowarden pour opencode…, the former standalone plugin included) or a repowarden garde… hook in a form other than the current one is replaced by the template, other hooks staying intact ("guard updated"). A personal file (without that header) is never overwritten. repowarden statut reports an outdated guard, in the repository or for the user, with repowarden agents init (--global for the user); repowarden install updates, without asking, the user guard already wired. repowarden hooks inactive (repository, or --global without repowarden install --global): reported, with the command, since the guard will then refuse commits. repowarden statut and repowarden install report an agent present (claude or opencode in the PATH, or their configuration folder) with the guard wired neither in the repository nor for the user, with repowarden agents init; install offers it in a terminal.
Removing the guard¶
Same options as init (--claude, --opencode, --projet, --global). Only what init wrote goes: in settings.json, the repowarden garde commands (other hooks and settings stay; the file is deleted if nothing is left); the opencode plugin if identical to the template (modified, it is kept and reported). Run again, nothing to remove, no error.
Checker: repowarden garde¶
One checker for all tools: it reads the action on standard input (JSON) and answers with its exit code.
| Code | Meaning | Effect |
|---|---|---|
| 0 | allowed | the action runs |
| 2 | refused | Claude Code blocks the call and passes the message to the agent; the opencode plugin throws an error with this message |
| other | guard out of order (jq missing, unreadable JSON) | nothing is blocked: a warning only |
Formats: --format claude (tool_name, tool_input, cwd: the PreToolUse hook input), --format opencode (default: tool, args, cwd).
Code instructions¶
Every new function has, right above it, one or two lines saying what it does and why, without paraphrasing the code: features documented in the code, without big comment blocks. Copy it into the project instructions (AGENTS.md, CLAUDE.md…); the pre-commit hook and the CI report omissions (functions without a comment).
Limitations¶
- Safety net, not a wall: git hooks and the
ticketcheck in CI remain the protection. A change made through the shell (sed -i,>redirection, script) is not seen before the commit, where the hooks check it. - GitHub writes: only
ghones are seen, and only from a repowarden repository:gh -R …run outside such a repository is not seen; neither are the web interface andcurlto the API. - Reading a shell line: simple splitting (quotes and subshells are not interpreted); a command hidden in
bash -c "…"or in a variable is not seen. - Requirements:
repowardenon thePATH(npm install -g repowarden) and jq. Otherwise nothing is blocked (the guard never blocks an agent because of a failure). - opencode: the plugin API is evolving; the guard uses the documented
tool.execute.beforehook. The new{ id, setup }API (@opencode-ai/pluginpackage, v2) does not expose tools yet. - Other tools (Cursor, Gemini CLI…): not supported yet; only hooks and CI apply.