---
name: rhiza-completions
description: Install shell tab-completion for make targets, so `make <TAB>` lists a project's targets. Writes to the user's data directory, not to the repo. Works in any make project.
---

> **Portable copy of `plugin/skills/completions/SKILL.md`, generated by
> `plugin/scripts/build_bundle.py`. Edit the source and run `make bundle` —
> an edit here is overwritten.**
>
> - **`${RHIZA_ROOT}`** is your `rhiza-claude` checkout. Export it, or
>   substitute the path wherever it appears. Any remark below about the
>   variable being empty in a source checkout is Claude Code's spelling of the
>   same idea — `${RHIZA_ROOT}` replaces it, and the repo-relative fallbacks do
>   not apply, because you are working in the *user's* repo, not in this one.
> - **`/rhiza:<name>`** names another skill in this bundle, `rhiza-<name>`.
>   Invoke it however your client invokes skills.
> - **Tools.** `Read`, `Edit` and `Write` read and write files; `Grep` and
>   `Glob` search; `Bash` is a shell in the user's repo. Use your equivalents.
> - **`AskUserQuestion`** is a multiple-choice question put to the user. With no
>   such tool, ask in plain text, number the options, and **wait for a reply**:
>   where the procedure says nothing is created without an explicit selection,
>   that holds however the question was asked.
> - **Arguments:** [bash, zsh or both]  (optional; defaults to both).
>   The text below writes `$ARGUMENTS` for what the user passed;
>   substitute it yourself if your client does not.
> - **Runs:** `uv`. Nothing here enforces that list, where the
>   plugin's frontmatter did — your client has to permit them.
> - **Never run this on your own initiative** — only when the user
>   asks for it by name.

You are running `/completions`. Goal: install the bundled make tab-completion for the
user's shell, so `make <TAB>` lists the targets of whatever project their shell is
sitting in.

**Two things make this command unlike every other one here.** Read both before you run
anything.

1. **It writes outside the repo.** The destinations are under
   `${XDG_DATA_HOME:-$HOME/.local/share}` — the user's home, not the working tree. There
   is nothing to review in a PR afterwards and nothing a `git checkout` undoes. That is
   why the user has to name this command: it is not model-invocable.
2. **It is not repo-scoped at all.** The completion is generic make completion — it
   discovers targets by parsing the make database in the current directory — so it works
   in every make project on the machine, rhiza-managed or not. **Install it once per
   machine**, not once per repo. If the user asks you to run it "for this repo", say that
   and install it once anyway; a second run is a no-op that reports `already up to date`.

This replaces the `make install-completions` target the rhiza template used to sync into
every managed repo, where N repos each carried an identical copy of a script that
installs to one shared path. If the user's repo still has that target, either one works
today and they do the same thing — prefer this one, and say why.

Argument (optional): `$ARGUMENTS` — `bash`, `zsh` or `both`. Default `both`.

## 1. Work out which shell

**Do not guess from `$SHELL` and install only that one.** Installing an unused
completion file is inert; installing the wrong single one leaves the user with no
completion and no error. So:

- `$ARGUMENTS` names a shell → pass it through.
- `$ARGUMENTS` is empty → `both`, and say so in the report.

`fish` and anything else is unsupported. The script rejects it with argparse's own
message; relay that rather than improvising a fish completion.

## 2. Install

`${RHIZA_ROOT}/plugin` resolves at runtime (**keep the quotes**); in a source
checkout it's empty, so fall back to the repo-relative path
`plugin/scripts/install_completions.py`.

```bash
uv run --python 3.12 --no-project python "${RHIZA_ROOT}/plugin/scripts/install_completions.py" --shell both
```

Swap `both` for the shell from step 1. Two flags exist for the cases below and
should not be passed speculatively:

- `--dry-run` — print the destinations and the verdict per shell, write nothing. Use it
  when the user asks *where* this would install, or wants to see the plan first.
- `--force` — replace a completion at the destination that this plugin did not write.
  **Never pass it on the first run.** See step 3.

`--json` gives the same summary as one object if you need to read a field rather than
show a line.

## 3. Handle the one blocking case

Exit **3** means a destination already holds a completion **this plugin did not write** —
the filenames are `make` and `_make`, the generic names both shells resolve for the
`make` command, so a completion from Homebrew, a distro package or the user's own dotfiles
lands in exactly that spot. The script has changed nothing and named the path.

**Stop and ask.** Show the path and offer the two ways forward:

- re-run with `--force`, which overwrites that file with ours; or
- leave it alone and wire ours up by hand from
  `"${RHIZA_ROOT}/plugin/scripts/completions/"` — the docs page carries the manual
  setup, and the header comment of each script repeats it.

Do **not** re-run with `--force` on your own initiative, and do not delete the file to
make the un-forced run succeed. Overwriting a completion the user installed deliberately
is precisely the surprise the exit code exists to prevent.

## 4. Report

Relay the script's lines as printed — one per shell, each with its destination and its
follow-up step. The follow-up is the half that matters and the half users skip:

- **bash** — start a new shell, or `source` the installed file.
- **zsh** — if completion doesn't activate, the destination has to be on `fpath` before
  `compinit` runs. The line to add to `~/.zshrc` is in the output; show it verbatim.

`already up to date` means a previous run installed the same bytes. That is a success,
not a no-op to apologise for — say so in one line and stop.

Nothing here needs a repo, a git remote or a network, so there is no state to check
afterwards and nothing to commit.
