git-manager
Run git commands across one or more repositories — your code, an Obsidian
vault kept under version control, whatever else — without Claude Code
needing to cd into a directory or chain shell commands together. That
matters because chained commands (cd path && git ..., git add && git commit && git push) are exactly what breaks Bash permission whitelists:
either the whole chain needs separate approval every time, or — depending
on the Claude Code version — a chain can slip through on a rule that was
only meant to cover one piece of it. This plugin sidesteps the problem
instead of fighting it: there's simply never a reason to cd or chain.
What it does
All git operations go through a single script, git_cmd.sh:
git_cmd.sh <subcommand> [--repo <name>] [-- <git-args...>]
- No
cdneeded. Omit--repoand it operates on whatever git repository your current working directory is in — which, in a normal Claude Code session, is already the right repo. Use--repo <name>to target a different, pre-registered repository (e.g. your notes vault) without leaving your current directory. - No chaining needed. Each git operation is its own call to the script. Want to add, commit, and push? That's three separate calls, not one chained command — which is exactly what keeps every single one of them covered by the whitelist.
- A fixed set of allowed subcommands:
status,log,diff,show,fetch,remote,branch,checkout,add,commit,push,pull. Anything else (reset,rebase,filter-branch, arbitrarygit config, ...) is rejected by the script itself, before git ever runs — not because those operations are inherently dangerous, but because they're consequential enough that they should go through a normal confirmation prompt rather than being auto-approved.
Setup
After installing the plugin:
/git-manager:setup
This will:
- Ask you (optionally) to register named repositories — useful if you work with more than one repo in the same session, e.g. a code repo plus a separate Obsidian vault repo. Skip this if you're fine relying on the default (current working directory).
- Add exactly one entry to your Claude Code permissions:
Bash(<path-to-git_cmd.sh>:*)— nothing broader. - Ask whether you want an additional safety net installed: a hook that
forces a normal confirmation prompt (not a silent block, not a
silent pass-through) any time a Bash command contains shell chaining or
substitution characters (
&&,;,|,`,$(...)) — see "The anti-chaining hook" below.
Usage examples
# Current repo (no --repo needed)
git_cmd.sh status
git_cmd.sh log -- --oneline -10
git_cmd.sh add -- -A
git_cmd.sh commit -- -m "Update notes"
git_cmd.sh push -- origin main
# A different, pre-registered repo — still no cd
git_cmd.sh status --repo homelab-notes
git_cmd.sh push --repo homelab-notes -- origin main
Everything after -- is passed to git as literal arguments (not
re-parsed by a shell), so quoting in commit messages etc. is safe.
Registering repositories
Repositories are looked up by name in
~/.agent-skills/git-manager/config.json:
{
"repos": [
{"name": "homelab-notes", "path": "/absolute/path/to/vault"},
{"name": "dwh-pipeline", "path": "/absolute/path/to/repo"}
]
}
This file is entirely optional. Without any entries, --repo simply isn't
available and every call operates on the current directory. Add entries any
time by asking Claude to register a new repo, or by editing the file
directly — writing to this file is never in the permissions whitelist, so
you'll always see (and can decline) that change.
The anti-chaining hook
Optional, offered during /git-manager:setup. If installed, it watches
every Bash command Claude Code is about to run (not just git ones) and,
if the command contains a chaining or substitution operator, forces the
normal permission prompt instead of letting whitelist matching decide.
It never silently blocks anything and never silently approves anything —
worst case, you get one extra confirmation prompt you didn't strictly need;
best case, it catches a chained command before it runs unreviewed.
With git_cmd.sh in place, chained commands involving git shouldn't come
up in the first place — this hook is a backstop for the rare cases they do
anyway (or for other tools/skills in your setup that aren't as careful
about it).
What's deliberately not included
- No
Bash(git:*)— that would also permitgit reset --hard,git clean -fdx,git filter-branch, etc. - No
Bash(cd:*)— removed as a category, not just discouraged. - No auto-approved writes to the config file — registering a repo is always a visible, confirmed step.
push --forceis technically reachable (it's justpushwith extra args) since it's a normal, if consequential, part of a git workflow — it isn't specially gated beyond the normalpushapproval.