Files
skill-repo/vendor/claude-code/plugins/git-manager/commands/setup.md
T
Henner M. Kruse 9f39a898c4 Fix setup.md: version-discovery fragility and unused argument-hint
Observed live: a stale plugin-cache version dir (1.0.0) coexisted with
the active one (1.1.0). setup.md's 'try the hardcoded version path
first' advice picked the stale one, its --install-raw-git-hook flag
failed silently, and Claude went on an unscripted forensic dig (chained
test&&echo, --help probing, grep/cat across both version dirs, cat'ing
plugin.json, stat'ing .in_use mtimes) to figure out which version was
actually active.

Fix, in both plugins' commands/setup.md:
- Never hardcode a version number. Always do one bounded
  find -maxdepth 1 -type d first; disambiguate multiple hits via the
  .in_use marker Claude Code itself writes, falling back to highest
  plugin.json version. Explicitly rule out the exploration that
  happened here (stat/mtime, --help, reading source, diffing versions)
  since setup.sh has no --help and unrecognized flags just error.
- Wire up the declared but previously-unused argument-hint
  ([repo-name] [repo-path] / [vault-path] [mode]): if the user already
  supplied it on the command line, skip asking the question that would
  just re-collect the same info.
2026-08-05 18:17:09 +00:00

134 lines
7.1 KiB
Markdown

---
description: Set up the git-manager skill (permissions whitelist, optional anti-chaining hook)
argument-hint: [repo-name] [repo-path]
---
Set up the `git-manager` skill. As part of the `git-manager` plugin, this
command is automatically namespaced by Claude Code and invoked as
`/git-manager:setup`.
Almost everything about this setup — reading/writing the skill's config,
merging permissions and the hook into settings.json, setting executable
bits — happens inside one bundled script, `scripts/setup.sh`, which you run
as a **single** Bash call. That script also copies the wrapper script(s) it
needs to a stable, self-controlled location
(`~/.agent-skills/git-manager/bin/`) before writing any permissions, so the
permission rule never has to change again across future plugin updates —
only this first run (and, optionally, a re-run after a plugin update) needs
to locate the plugin itself.
## 1. Ask the user — three selectable questions first, repo path last
**1a. Before asking anything, check for arguments.** This command's
`argument-hint` is `[repo-name] [repo-path]`. If the user invoked it with
both (e.g. `/git-manager:setup homelab-vault /home/hmk/projects/homelab`),
that already answers "whether to register a repo" and "which repo" — use
the given `name=path` pair directly as the `--repo` flag in step 2, and
skip both the "register any repos" option below and step 1c entirely,
without asking about either. If no arguments (or only one of the two) were
given, proceed normally: nothing here is decided yet, ask 1b and, if
needed, 1c below.
**1b. First turn: questions via the selection UI:**
- Whether to register any repos at all: yes/no. **Omit this question if
1a already determined a repo was given as an argument** — in that case
there's nothing to ask, it's already yes.
- Settings scope: `project` (`.claude/settings.json` in the current
project) or `user` (`~/.claude/settings.json`). Mention: if they work
across many separate repos/projects, `user` avoids repeating setup per
project.
- Anti-chaining hook: yes/no, after explaining it never silently blocks or
silently allows anything, it only forces the *normal* confirmation
prompt when a Bash command contains shell chaining operators (`&&`, `;`,
`|`, backticks, `$(...)`), as a safety net given Claude Code's Bash
allow-list matching has had bugs around compound commands (see
`hooks/force-ask-on-chaining.sh` for details).
- Ask-before-raw-git hook: yes/no, after explaining it never silently
blocks or silently allows anything either — it only forces the normal
confirmation prompt when a Bash command runs raw `git` instead of the
`git_cmd.sh` wrapper. It never fully blocks raw git (that stays a valid
fallback for subcommands the wrapper deliberately doesn't support, e.g.
`stash`/`merge`/`rebase`/`reset`, or for repos that aren't registered
yet) — it just makes sure raw git use is always a visible, confirmed
choice rather than something that slips through unnoticed (see
`hooks/force-ask-on-raw-git.sh` for details). Independent of the
anti-chaining hook — either, both, or neither can be installed.
**1c. Only if the answer to "register any repos" was yes AND 1a didn't
already supply a repo: a second turn, plain chat message, no tool call.**
Ask exactly: "Which repos should git-manager know about? Provide as
`name=path` pairs." End your turn right after asking this, with nothing
else queued up, so the user's reply is what actually gets collected before
you continue. (Only a tool call reliably pauses for a reply in this
environment; plain text followed immediately by a tool call in the same
turn does not wait — that's what caused this question to appear skipped in
an earlier version of this command.) Skip this sub-step entirely — don't
ask it at all — if the answer to "register any repos" was no, or if 1a
already supplied a repo.
## 2. Locate the plugin, then run the setup script — one command
Claude Code installs plugins added from a local marketplace at
`~/.claude/plugins/cache/<marketplace-name>/<plugin-name>/<plugin-version>/`.
**Never hardcode or guess a version number** — it changes on every release
of this plugin, and a stale directory from a previous version can still be
sitting on disk right alongside the current one (an old version won't
recognize newer flags like `--install-raw-git-hook`, so running it by
mistake fails in a confusing way). Always start with exactly one bounded,
read-only lookup instead:
```bash
find ~/.claude/plugins/cache/skill-repo/git-manager -maxdepth 1 -type d 2>/dev/null
```
- **Exactly one version directory found:** use it.
- **More than one found:** prefer whichever one contains a `.in_use`
marker file (Claude Code writes this into the currently-active version's
directory). If none of them has one, or more than one does, fall back to
whichever directory's `.claude-plugin/plugin.json` reports the highest
`version`. Don't disambiguate any other way — no `stat`/mtime
comparisons, no diffing scripts between versions, no probing with
`--help` (there is no `--help`; unrecognized flags just print `Error:
unknown argument`). That kind of exploration is exactly what this
bounded lookup exists to avoid.
- **None found:** the plugin likely isn't installed under this name or
marketplace — say so and stop rather than guessing at a different path.
Then run, as a **single** Bash call (no `test -f ... &&` pre-check, no
piping through `grep`/`head` to inspect it first):
```bash
~/.claude/plugins/cache/skill-repo/git-manager/<version>/scripts/setup.sh \
--settings-scope <project|user> \
[--project-dir <path>] \
[--repo <name>=<path> ...] \
[--install-hook] \
[--install-raw-git-hook]
```
Include `--repo name=path` once per repo from step 1, `--install-hook` and
`--install-raw-git-hook` only for the ones the user opted into (they're
independent flags), and `--project-dir` only if `--settings-scope
project` and the project isn't the current working directory. If this one
call errors, that means the version picked above was wrong — re-run the
`find` above (and re-check `.in_use`/`plugin.json`) rather than falling
back to reading source files or trying more flags. This is expected to
prompt for approval the first time — there's no way to pre-whitelist it
before the setup that establishes the whitelist has run.
## 3. Relay the result
The script prints a JSON summary (stable script location, settings file
written, config file, whether the hook was installed, repos registered,
the exact permission rules added, and a note about session reload).
Present that summary to the user in plain language — don't re-read any of
those files yourself to "double check"; the script's own output already
reflects exactly what's on disk.
Do **not** follow this with a test invocation of `git_cmd.sh` — Claude Code
loads permissions at session start and doesn't always pick up changes made
by its own file edits within the same session, so an immediate test can
prompt (or not) in a way that doesn't reliably indicate success either way.
If a real command later still prompts, the fix is a fresh session, not
re-running this setup — the configuration is already correctly saved.