From 04e0922d40be38b1b94b0df2ad15d2949e8808ec Mon Sep 17 00:00:00 2001 From: Paul Gebheim <86010+pgebheim@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:21:10 +0000 Subject: [PATCH] fix(rig-worktree,install): resolve script and doc paths per install target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two paths were hardcoded to `.claude/` with no indirection, so on an agents-md-only repo — Codex, Cursor, Gemini CLI, the population #69 and #70 exist to serve — they pointed at files that aren't there. rig-worktree named `.claude/scripts/{setup,remove}-worktree.sh` in four places while `agents-md` installs those scripts to `.rig/scripts/`, and the repo has no `.claude/` at all. The skill told the agent to run a path that doesn't exist, right after insisting it must not reimplement the script inline. It now resolves `` first-hit-wins over `.claude/scripts/` then `.rig/scripts/`, in the spirit of the `` resolver rig-epic/rig-task/rig-issue/rig-doctor/rig-plan already use, and stops with a clear message if neither has the script. The scripts themselves already resolved their own siblings, so only the callers needed fixing. install.sh copied rig.config.example.json verbatim, so every install got `review.patternsFile: .claude/REVIEWER.md` and `tracker.labelMapFile: .claude/label-mapping.md`. Without the claude-code target those docs were installed under `.rig/`, leaving both keys dangling on a fresh install. It now rewrites the two paths when claude-code isn't among the targets. Uses an inline membership test rather than a helper, to stay independent of #70. `.claude/worktrees/` as the default worktree location is deliberate and unchanged — the Claude Code harness only adopts worktrees there (see 41283b3). Documented that it carries no special meaning elsewhere and `--path` overrides. Verified across all three shapes: claude-only keeps `.claude/` paths and resolves to `.claude/scripts`; agents-only gets `.rig/` paths and resolves to `.rig/scripts`; dual keeps `.claude/` paths, both files present. The prescribed resolver snippet was executed as written against each install, and every configured doc path was checked to exist. `bun test scripts/` — 39 pass. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014oVJgHRkCkeCiayKUJ4iSg --- docs/config.md | 4 ++-- install.sh | 10 ++++++++++ scripts/setup-worktree.sh | 2 +- skills/rig-worktree/SKILL.md | 37 ++++++++++++++++++++++++++++-------- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/docs/config.md b/docs/config.md index 23de66a..e54abd1 100644 --- a/docs/config.md +++ b/docs/config.md @@ -50,13 +50,13 @@ Set `provider: "none"` to strip all ticket steps from `ticket`/`sprint`/review f | `team` | — | Linear team name/key or GitHub org. | | `project` | — | Linear project name. | | `ticketPrefix` | — | e.g. `INC-`; detects ticket IDs in branches/PR titles. | -| `labelMapFile` | `.claude/label-mapping.md` | PR/tracker label source of truth. | +| `labelMapFile` | `.claude/label-mapping.md` | PR/tracker label source of truth. On an install without the `claude-code` target the doc lives at `.rig/label-mapping.md`, and the installer writes that path instead. | | `githubIntegration` | `false` | If true, GitHub drives PR/merge transitions (In Progress on PR-open, In Review, Done on merge); skills set only the start-of-work In Progress, which GitHub can't observe before a PR exists. | ## `review` | Key | Default | Meaning | |---|---|---| -| `patternsFile` | `.claude/REVIEWER.md` | The P0–P3 review catalog. | +| `patternsFile` | `.claude/REVIEWER.md` | The P0–P3 review catalog. On an install without the `claude-code` target the catalog lives at `.rig/REVIEWER.md`, and the installer writes that path instead. | | `bot` | `none` | `codex`\|`claude`\|`bugbot`\|`none`. Which PR bot `rig-review fix` polls/re-triggers. | | `botRetrigger` | — | Comment that re-triggers the bot, e.g. `@codex review`. | | `maxRounds` | `5` | Max fix↔re-review rounds before handing to a human. | diff --git a/install.sh b/install.sh index a08c8d4..58a1db1 100644 --- a/install.sh +++ b/install.sh @@ -94,6 +94,9 @@ copy_no_clobber() { } # --- Shared: project profile (agent-agnostic) -------------------------------- +# The example profile carries .claude/ paths for the two support docs. Without +# the claude-code target those files were installed under .rig/, so ship the +# profile pointing where the docs actually are rather than at a dead path. write_profile() { if [[ -e "$TARGET/.rig/config.json" ]]; then echo " skip (exists): .rig/config.json" @@ -101,6 +104,13 @@ write_profile() { mkdir -p "$TARGET/.rig" cp "$RIG_DIR/rig.config.example.json" "$TARGET/.rig/config.json" cp "$RIG_DIR/rig.schema.json" "$TARGET/.rig/schema.json" + if [[ " ${TARGETS[*]} " != *" claude-code "* ]]; then + sed -i.bak -e 's|"\.claude/REVIEWER\.md"|".rig/REVIEWER.md"|' \ + -e 's|"\.claude/label-mapping\.md"|".rig/label-mapping.md"|' \ + "$TARGET/.rig/config.json" + rm -f "$TARGET/.rig/config.json.bak" + echo " (no claude-code target: patternsFile/labelMapFile point at .rig/)" + fi echo " wrote: .rig/config.json (EDIT THIS — it currently holds the example values)" fi } diff --git a/scripts/setup-worktree.sh b/scripts/setup-worktree.sh index e9a82c3..786059c 100755 --- a/scripts/setup-worktree.sh +++ b/scripts/setup-worktree.sh @@ -96,7 +96,7 @@ esac # Optional session name (do it early so any job list updates even if a later # step is slow). set-session-name.sh ships alongside this script; it's # Claude-Code-only and no-ops elsewhere. Resolve it as this script's sibling so -# it works under .claude/scripts/ or rig/scripts/. +# it works under .claude/scripts/ or .rig/scripts/. if [ -n "$SESSION_NAME" ]; then set_name="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/set-session-name.sh" if [ -x "$set_name" ]; then diff --git a/skills/rig-worktree/SKILL.md b/skills/rig-worktree/SKILL.md index fc361e8..703c887 100644 --- a/skills/rig-worktree/SKILL.md +++ b/skills/rig-worktree/SKILL.md @@ -15,8 +15,9 @@ ad-hoc branches, parallel experiments) and that implement-style flows and prune/rig-tidy steps can delegate to. The logic lives in two scripts so the invariants live in one place: -`.claude/scripts/setup-worktree.sh` (create) and -`.claude/scripts/remove-worktree.sh` (teardown). **Creation encodes +`/setup-worktree.sh` (create) and +`/remove-worktree.sh` (teardown), where `` is resolved +per install (see *Configuration*). **Creation encodes three invariants that bite if skipped** — fetch-before-branch (stale local refs silently start you on a pre-merge tip), env symlinks (missing secrets read as flaky/timeout test failures, not "config not found"), @@ -26,6 +27,18 @@ inline; call the scripts. ## Configuration +**Script location (``).** Where the helper scripts live depends on +which target installed them, so resolve — don't assume — from the repo root +(`git rev-parse --show-toplevel`), first hit wins: + +1. `.claude/scripts/` — the `claude-code` target (on a dual-target install this + is a symlink at `.rig/scripts/`, so either entry finds the same file). +2. `.rig/scripts/` — the `agents-md` target (Codex, Cursor, Gemini CLI, …), + where there is no `.claude/` at all. + +If neither holds `setup-worktree.sh`, say so and stop rather than +reimplementing the script inline — its invariants are the whole point. + Reads `.rig/config.json` (missing keys → defaults): | Key | Default | Used for | @@ -62,7 +75,9 @@ only required argument. `.claude/worktrees/` (so `alice/feat-521-foo` → `.claude/worktrees/feat-521-foo`). This is the directory the Claude Code harness manages, so a worktree created here can be adopted natively via - `EnterWorktree`/`ExitWorktree`. + `EnterWorktree`/`ExitWorktree`. The default is unchanged on non-Claude + agents — it's then just a path with no special meaning, so pass `--path` if + you'd rather not have a `.claude/` dir in the repo. - `--reuse` — if the worktree/branch already exists, fetch and hard-reset it to `` instead of failing. Use this for a "reuse a child worktree" path. @@ -93,10 +108,16 @@ only required argument. from the ticket title — fetch it if you have a tracker, else ask). 3. **Run the script** from the repo root, passing the resolved base and - install command: + install command. Resolve `` first (see *Configuration*): ```bash - "$(git rev-parse --show-toplevel)/.claude/scripts/setup-worktree.sh" \ + ROOT="$(git rev-parse --show-toplevel)" + for d in "$ROOT/.claude/scripts" "$ROOT/.rig/scripts"; do + [ -x "$d/setup-worktree.sh" ] && SCRIPTS="$d" && break + done + [ -n "$SCRIPTS" ] || { echo "rig scripts not found under .claude/ or .rig/" >&2; exit 1; } + + "$SCRIPTS/setup-worktree.sh" \ [--base ] [--install-cmd ""] [--reuse] ``` @@ -104,7 +125,7 @@ only required argument. path as the last line of stdout**, so capture it: ```bash - WT=$("$(git rev-parse --show-toplevel)/.claude/scripts/setup-worktree.sh" \ + WT=$("$SCRIPTS/setup-worktree.sh" \ alice/feat-521-rename --base origin/main --install-cmd "bun install" | tail -1) ``` @@ -142,10 +163,10 @@ End with a one-line hint, e.g. *"2 worktrees on MERGED branches — Safely tear a worktree down via the shared script. Dirty worktrees are **skipped, not removed** (a skip is a normal outcome, not an error) — -pass `--force-dirty` to override. +pass `--force-dirty` to override. Resolve `` as in *Create* step 3: ```bash -"$(git rev-parse --show-toplevel)/.claude/scripts/remove-worktree.sh" \ +"$SCRIPTS/remove-worktree.sh" \ [--force-dirty] [--keep-branch] ```