# 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`: ```bash git_cmd.sh [--repo ] [-- ] ``` - **No `cd` needed.** Omit `--repo` and 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 ` 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`, arbitrary `git 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: 1. 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). 2. Add exactly one entry to your Claude Code permissions: `Bash(:*)` — nothing broader. 3. 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 ```bash # 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`: ```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 permit `git 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 --force` is technically reachable (it's just `push` with extra args) since it's a normal, if consequential, part of a git workflow — it isn't specially gated beyond the normal `push` approval.