Skip to content

feat: add worktree slash commands for isolated Rust development#364

Merged
pszymkowiak merged 1 commit intodevelopfrom
feature/worktree-commands
Mar 13, 2026
Merged

feat: add worktree slash commands for isolated Rust development#364
pszymkowiak merged 1 commit intodevelopfrom
feature/worktree-commands

Conversation

@FlorianBruniaux
Copy link
Copy Markdown
Collaborator

Summary

Ports 4 worktree management slash commands from MethodeAristote project, adapted for RTK (Rust/cargo instead of pnpm/TypeScript).

New commands:

Command Description
/worktree <branch> Create isolated worktree + background cargo check
/worktree-status <branch> Check background cargo check result
/clean-worktrees Auto-remove all merged worktrees (no interaction)
/clean-worktree Interactive cleanup with confirmation

Key adaptations from source project:

  • pnpm tsc --noEmitcargo check (background by default)
  • No node_modules symlink step (Rust doesn't need it)
  • develop branch → master branch for merge detection
  • macOS-compatible: no timeout (uses gtimeout fallback)
  • Flags: --fast (skip check) and --check (blocking check)

Also fixes: version refs in README, CLAUDE.md, ARCHITECTURE.md (0.26.0 → 0.27.0) — pre-push hook was blocking any push on master.

Test plan

  • /worktree feature/test-cmd — creates .worktrees/feature-test-cmd, starts cargo check in background
  • /worktree-status feature/test-cmd — shows check result
  • /worktree feature/test-fast --fast — instant, no cargo check
  • /clean-worktrees --dry-run — preview mode, no deletions
  • /clean-worktree — interactive, confirms before deleting

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings March 6, 2026 08:26
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Claude Code slash-command docs for creating and cleaning git worktrees in the RTK repo (with optional background cargo check), and updates documentation/version references to match the current RTK release.

Changes:

  • Add /worktree + /worktree-status commands to create isolated worktrees and track a background cargo check.
  • Add /clean-worktrees (automatic) and /clean-worktree (interactive) cleanup commands for merged worktrees.
  • Update docs to reference RTK 0.27.0 and ignore .worktrees/ + .vitals/ directories.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
README.md Updates “verify version” snippet to 0.27.0.
CLAUDE.md Updates “verify version” snippet to 0.27.0.
ARCHITECTURE.md Updates recorded RTK version to 0.27.0.
.gitignore Adds ignores for .vitals/ and .worktrees/.
.claude/commands/worktree.md New command script/doc to create worktrees and run cargo check (background/foreground).
.claude/commands/worktree-status.md New command script/doc to read background cargo check log and report status.
.claude/commands/clean-worktrees.md New automatic cleanup script/doc for merged worktrees (supports --dry-run).
.claude/commands/clean-worktree.md New interactive cleanup script/doc for merged worktrees.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +43 to +56
# Parse flags
RAW_ARGS="$ARGUMENTS"
BRANCH_NAME="$RAW_ARGS"
SKIP_CHECK=false
BLOCKING_CHECK=false

if [[ "$RAW_ARGS" == *"--fast"* ]]; then
SKIP_CHECK=true
BRANCH_NAME="${BRANCH_NAME// --fast/}"
fi
if [[ "$RAW_ARGS" == *"--check"* ]]; then
BLOCKING_CHECK=true
BRANCH_NAME="${BRANCH_NAME// --check/}"
fi
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flag parsing via substring replacement (${BRANCH_NAME// --fast/} / // --check/) is brittle (order-dependent, fails if flag is first, can leave extra whitespace, and doesn't prevent additional unknown args). Consider parsing args properly (e.g., loop over tokens / getopts) and deriving BRANCH_NAME from the remaining positional arg(s).

Suggested change
# Parse flags
RAW_ARGS="$ARGUMENTS"
BRANCH_NAME="$RAW_ARGS"
SKIP_CHECK=false
BLOCKING_CHECK=false
if [[ "$RAW_ARGS" == *"--fast"* ]]; then
SKIP_CHECK=true
BRANCH_NAME="${BRANCH_NAME// --fast/}"
fi
if [[ "$RAW_ARGS" == *"--check"* ]]; then
BLOCKING_CHECK=true
BRANCH_NAME="${BRANCH_NAME// --check/}"
fi
# Parse flags and branch name from $ARGUMENTS
BRANCH_NAME=""
SKIP_CHECK=false
BLOCKING_CHECK=false
# Convert $ARGUMENTS into positional parameters for robust parsing
set -- $ARGUMENTS
while [ "$#" -gt 0 ]; do
case "$1" in
--fast)
SKIP_CHECK=true
;;
--check)
BLOCKING_CHECK=true
;;
-*)
echo "Unknown flag: $1"
exit 1
;;
*)
if [ -n "$BRANCH_NAME" ]; then
echo "Multiple branch names provided: '$BRANCH_NAME' and '$1'"
exit 1
fi
BRANCH_NAME="$1"
;;
esac
shift
done
if [ -z "$BRANCH_NAME" ]; then
echo "Usage: /worktree <branch> [--fast|--check]"
exit 1
fi

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +63
CURRENT_DIR="$(pwd)"

while IFS= read -r line; do
path=$(echo "$line" | awk '{print $1}')
branch=$(echo "$line" | grep -oE '\[.*\]' | tr -d '[]' || true)

[ -z "$branch" ] && continue
[ "$branch" = "master" ] && continue
[ "$branch" = "main" ] && continue
[ "$path" = "$CURRENT_DIR" ] && continue

Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CURRENT_DIR="$(pwd)" only protects you if you run the command from the worktree root. If you run it from a subdirectory inside a worktree, $CURRENT_DIR won’t equal the worktree path and the script may remove the worktree you’re currently using. Compare realpaths and treat any worktree path that is a prefix of the current directory as “current” (or use git rev-parse --show-toplevel for the current worktree root).

Copilot uses AI. Check for mistakes.
[ "$branch" = "main" ] && continue
[ "$path" = "$CURRENT_DIR" ] && continue

if git branch --merged master | grep -q "^[* ] ${branch}$" 2>/dev/null; then
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Branch merge detection uses grep with an unescaped branch name (grep -q "^[* ] ${branch}$"), so branch names containing regex metacharacters (e.g. ., +, []) can cause false matches and lead to deleting the wrong worktree/branch. Use git branch --merged master --format="%(refname:short)" | grep -Fxq "$branch" (or another exact-match approach) instead of regex matching.

Suggested change
if git branch --merged master | grep -q "^[* ] ${branch}$" 2>/dev/null; then
if git branch --merged master --format="%(refname:short)" | grep -Fxq "$branch" 2>/dev/null; then

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +49
if git branch --merged master | grep -q "^[* ] ${branch}$" 2>/dev/null; then
echo " - $branch (at $path) - MERGED"
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Branch merge detection uses grep with an unescaped branch name (grep -q "^[* ] ${branch}$"), so branch names containing regex metacharacters can cause false positives and list/delete the wrong worktree/branch. Prefer an exact-match check (e.g., git branch --merged master --format="%(refname:short)" | grep -Fxq "$branch").

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +69
while IFS= read -r line; do
path=$(echo "$line" | awk '{print $1}')
branch=$(echo "$line" | grep -oE '\[.*\]' | tr -d '[]' || true)

[ -z "$branch" ] && continue
[ "$branch" = "master" ] && continue
[ "$branch" = "main" ] && continue
[ "$path" = "$CURRENT_DIR" ] && continue

if git branch --merged master | grep -q "^[* ] ${branch}$" 2>/dev/null; then
MERGED_COUNT=$((MERGED_COUNT + 1))
MERGED_BRANCHES+=("$branch|$path")
echo " - $branch (merged)"
fi
done < <(git worktree list)
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing git worktree list with awk '{print $1}' will break if a worktree path contains spaces (it will truncate the path), and later git worktree remove / rm -rf could target the wrong directory. Use git worktree list --porcelain (or -z where available) to parse paths safely.

Suggested change
while IFS= read -r line; do
path=$(echo "$line" | awk '{print $1}')
branch=$(echo "$line" | grep -oE '\[.*\]' | tr -d '[]' || true)
[ -z "$branch" ] && continue
[ "$branch" = "master" ] && continue
[ "$branch" = "main" ] && continue
[ "$path" = "$CURRENT_DIR" ] && continue
if git branch --merged master | grep -q "^[* ] ${branch}$" 2>/dev/null; then
MERGED_COUNT=$((MERGED_COUNT + 1))
MERGED_BRANCHES+=("$branch|$path")
echo " - $branch (merged)"
fi
done < <(git worktree list)
path=""
branch=""
while IFS= read -r line; do
if [[ "$line" == worktree\ * ]]; then
path="${line#worktree }"
elif [[ "$line" == branch\ * ]]; then
ref="${line#branch }"
branch="${ref#refs/heads/}"
elif [[ -z "$line" ]]; then
# End of a worktree record; process collected path and branch
if [ -n "$branch" ]; then
[ "$branch" = "master" ] && { path=""; branch=""; continue; }
[ "$branch" = "main" ] && { path=""; branch=""; continue; }
[ "$path" = "$CURRENT_DIR" ] && { path=""; branch=""; continue; }
if git branch --merged master | grep -q "^[* ] ${branch}$" 2>/dev/null; then
MERGED_COUNT=$((MERGED_COUNT + 1))
MERGED_BRANCHES+=("$branch|$path")
echo " - $branch (merged)"
fi
fi
# Reset for next record
path=""
branch=""
fi
done < <(git worktree list --porcelain)

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +45
# Parse flags
RAW_ARGS="$ARGUMENTS"
BRANCH_NAME="$RAW_ARGS"
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With set -u, reading $ARGUMENTS will abort the script if that variable is unset in the caller environment. Use a default (RAW_ARGS="${ARGUMENTS:-}") and add an explicit check that the branch argument is present (and not just flags) before proceeding.

Copilot uses AI. Check for mistakes.
#!/bin/bash
set -euo pipefail

BRANCH_NAME="$ARGUMENTS"
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With set -u, BRANCH_NAME="$ARGUMENTS" will fail if ARGUMENTS is unset. Use BRANCH_NAME="${ARGUMENTS:-}" and validate it's non-empty (and not just whitespace) before building the log file path.

Suggested change
BRANCH_NAME="$ARGUMENTS"
BRANCH_NAME="${ARGUMENTS:-}"
# Ensure branch name is provided and not just whitespace before using it in a path
if [[ -z "${BRANCH_NAME//[[:space:]]/}" ]]; then
echo "Error: branch name must be provided."
echo "Usage: /worktree-status <branch-name>"
exit 1
fi

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +45
LOG_CONTENT=$(head -n 500 "$LOG_FILE")

if echo "$LOG_CONTENT" | grep -q "^PASSED"; then
TIMESTAMP=$(echo "$LOG_CONTENT" | grep "^PASSED" | sed 's/PASSED at //')
echo "cargo check passed"
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOG_CONTENT=$(head -n 500 ...) can miss the PASSED/FAILED sentinel lines because those are appended at the end of the log, and cargo check output can easily exceed 500 lines. Prefer checking tail (or scanning the whole file) for the completion marker and timestamp so completed checks aren’t reported as “still running/unknown”.

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +47
CURRENT_DIR="$(pwd)"

while IFS= read -r line; do
path=$(echo "$line" | awk '{print $1}')
branch=$(echo "$line" | grep -oE '\[.*\]' | tr -d '[]' || true)
[ -z "$branch" ] && continue
[ "$branch" = "master" ] && continue
[ "$branch" = "main" ] && continue
[ "$path" = "$CURRENT_DIR" ] && continue

Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CURRENT_DIR="$(pwd)" only matches the worktree root. If the script is executed from a subdirectory inside a worktree, $CURRENT_DIR won’t equal the worktree path and the script can end up offering to delete the worktree you’re currently in. Consider comparing realpaths with prefix checks, or compute the current worktree root via git rev-parse --show-toplevel and exclude that worktree path.

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +43
while IFS= read -r line; do
path=$(echo "$line" | awk '{print $1}')
branch=$(echo "$line" | grep -oE '\[.*\]' | tr -d '[]' || true)
[ -z "$branch" ] && continue
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing git worktree list with awk '{print $1}' will truncate paths containing spaces, and that can lead to deleting the wrong directory when calling git worktree remove / rm -rf. Prefer git worktree list --porcelain (or another machine-readable format) and parse the worktree <path> lines instead.

Copilot uses AI. Check for mistakes.
@pszymkowiak
Copy link
Copy Markdown
Collaborator

Hi! Thanks for the contribution! Since March 6, all PRs should target the develop branch instead of master (see CONTRIBUTING.md).

Could you update the base branch? Click Edit at the top right of this PR and change it from master to develop.

Thanks!

Adds 4 Claude Code slash commands for git worktree management, adapted
from MethodeAristote project (Rust/cargo instead of pnpm/TypeScript).

Commands:
- /worktree <branch>: creates worktree + background cargo check
  - --fast: skip cargo check (instant)
  - --check: blocking cargo check before ready
- /worktree-status <branch>: check background cargo check result
- /clean-worktrees: auto-remove all merged worktrees (--dry-run support)
- /clean-worktree: interactive cleanup with confirmation

Also adds .worktrees/ to .gitignore (required by /worktree command).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@pszymkowiak pszymkowiak force-pushed the feature/worktree-commands branch from 685fc4a to 0abcc02 Compare March 13, 2026 08:34
@pszymkowiak pszymkowiak changed the base branch from master to develop March 13, 2026 08:34
@pszymkowiak pszymkowiak merged commit ab83e79 into develop Mar 13, 2026
ahundt pushed a commit to ahundt/rtk that referenced this pull request Mar 14, 2026
Adds 4 Claude Code slash commands for git worktree management, adapted
from MethodeAristote project (Rust/cargo instead of pnpm/TypeScript).

Commands:
- /worktree <branch>: creates worktree + background cargo check
  - --fast: skip cargo check (instant)
  - --check: blocking cargo check before ready
- /worktree-status <branch>: check background cargo check result
- /clean-worktrees: auto-remove all merged worktrees (--dry-run support)
- /clean-worktree: interactive cleanup with confirmation

Also adds .worktrees/ to .gitignore (required by /worktree command).

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants