diff --git a/.claude/commands/worktree-ls.md b/.claude/commands/worktree-ls.md new file mode 100644 index 00000000..84709b1f --- /dev/null +++ b/.claude/commands/worktree-ls.md @@ -0,0 +1,45 @@ +--- +description: List all active git worktrees with status +argument-hint: "" +--- + +# List Git Worktrees + +## Instructions + +### 1. Get Worktree List + +Use porcelain format for reliable, machine-parseable output: + +```bash +git worktree list --porcelain +``` + +### 2. Parse and Check Status + +Parse the porcelain output into records. Each worktree is a block of lines +separated by a blank line, with fields: +- `worktree ` — the absolute path +- `HEAD ` — the full commit hash +- `branch refs/heads/` — the branch (or `detached` if HEAD is detached) + +For each worktree path, check for uncommitted changes: + +```bash +git -C "$WORKTREE_PATH" status --porcelain | wc -l +``` + +Quote `$WORKTREE_PATH` in all commands to handle paths with spaces. + +### 3. Display Results + +Show a table with: +- **Path** +- **Branch** +- **Commit** (short hash — first 7 characters of HEAD) +- **Status**: "clean" or "N uncommitted changes" + +If there's only the main worktree, add: +``` +No additional worktrees. Use /worktree-new to create one. +``` diff --git a/.claude/commands/worktree-new.md b/.claude/commands/worktree-new.md new file mode 100644 index 00000000..0bb52283 --- /dev/null +++ b/.claude/commands/worktree-new.md @@ -0,0 +1,125 @@ +--- +description: Create a new git worktree with full dev environment for parallel work +argument-hint: " [base-branch]" +--- + +# Create Git Worktree + +Create an isolated worktree for parallel development. Arguments: $ARGUMENTS + +## Instructions + +### 1. Parse Arguments + +Parse `$ARGUMENTS` to extract: +- **name** (required): First argument — used as both directory suffix and branch name +- **base-ref** (optional): Second argument — existing branch, tag, or ref to branch + from (creates branch `` starting at that ref) + +If no name is provided, abort with: +``` +Error: Name required. Usage: /worktree-new [base-branch] +Example: /worktree-new feature-bacon-fix +``` + +Validate that **name** starts with a letter or digit, followed by `[a-zA-Z0-9._-]`. +If it starts with `-` or contains spaces, slashes, or other shell metacharacters, abort: +``` +Error: Name must start with a letter or digit and contain only letters, digits, dots, hyphens, and underscores. +Got: +``` + +If **base-ref** is provided, apply the same character validation (must match +`^[a-zA-Z0-9][a-zA-Z0-9._/-]*$` — slashes are allowed for refs like `origin/main`). +Then verify the ref exists: + +```bash +git rev-parse --verify --quiet "$BASE_REF" +``` + +If verification fails, abort: +``` +Error: Ref not found: +Available branches: + +``` + +### 2. Resolve Paths + +Derive paths dynamically (do NOT hardcode the repo name): + +```bash +MAIN_ROOT="$(git worktree list --porcelain | head -1 | sed 's/^worktree //')" +REPO_NAME="$(basename "$MAIN_ROOT")" +PARENT_DIR="$(dirname "$MAIN_ROOT")" +WORKTREE_PATH="${PARENT_DIR}/${REPO_NAME}-" +``` + +Use `$WORKTREE_PATH` (the absolute path) for all subsequent commands. + +### 3. Validate + +```bash +git worktree list +``` + +- If a worktree already exists at `$WORKTREE_PATH`, abort with an error. +- If a branch named `` already exists and no base-ref was given: + - First check if the branch is already checked out in a worktree + (parse `git worktree list --porcelain` for a `branch refs/heads/` line). + - If checked out elsewhere, abort: + ``` + Error: Branch '' is already checked out in worktree at . + Use a different name or remove that worktree first. + ``` + - Otherwise, ask the user whether to check out that existing branch + or pick a different name. If the user chooses to use it: + ```bash + git worktree add -- "$WORKTREE_PATH" "" + ``` + Then skip step 4 and continue to step 5. + +### 4. Create the Worktree + +```bash +# If base-ref provided (create new branch starting at base-ref): +git worktree add -b "" -- "$WORKTREE_PATH" "$BASE_REF" + +# If no base-ref (create new branch from current HEAD): +git worktree add -b "" -- "$WORKTREE_PATH" +``` + +### 5. Set Up Python Environment + +Note to user: dependency installation may take a moment on a fresh venv. + +```bash +python3 -m venv "$WORKTREE_PATH/.venv" +"$WORKTREE_PATH/.venv/bin/pip" install --upgrade pip +"$WORKTREE_PATH/.venv/bin/pip" install -e "$WORKTREE_PATH[dev]" +``` + +Do NOT use `-q` — let pip output stream so the user sees progress. + +### 6. Build Rust Backend (best-effort) + +Use `--manifest-path` to avoid changing directories: + +```bash +"$WORKTREE_PATH/.venv/bin/maturin" develop --manifest-path "$WORKTREE_PATH/Cargo.toml" +``` + +If maturin is not installed in the venv or the build fails, note that pure-Python +mode will be used and continue. This is not an error. + +### 7. Report + +Print: + +``` +Worktree ready: $WORKTREE_PATH +Branch: + +To start working: + cd $WORKTREE_PATH && source .venv/bin/activate && claude +``` diff --git a/.claude/commands/worktree-rm.md b/.claude/commands/worktree-rm.md new file mode 100644 index 00000000..5102ddcc --- /dev/null +++ b/.claude/commands/worktree-rm.md @@ -0,0 +1,100 @@ +--- +description: Remove a git worktree and optionally delete its branch +argument-hint: "" +--- + +# Remove Git Worktree + +Remove an existing worktree. Arguments: $ARGUMENTS + +## Instructions + +### 1. Parse Arguments + +Extract **name** from `$ARGUMENTS`. If empty, abort: +``` +Error: Name required. Usage: /worktree-rm +Tip: Run /worktree-ls to see active worktrees. +``` + +Validate that **name** starts with a letter or digit, followed by `[a-zA-Z0-9._-]`. +If it starts with `-` or contains invalid characters, abort: +``` +Error: Name must start with a letter or digit and contain only letters, digits, dots, hyphens, and underscores. +Got: +``` + +### 2. Resolve Paths + +```bash +MAIN_ROOT="$(git worktree list --porcelain | head -1 | sed 's/^worktree //')" +REPO_NAME="$(basename "$MAIN_ROOT")" +PARENT_DIR="$(dirname "$MAIN_ROOT")" +WORKTREE_PATH="${PARENT_DIR}/${REPO_NAME}-" +``` + +### 3. Validate + +```bash +git worktree list +``` + +If no worktree exists at `$WORKTREE_PATH`, abort: +``` +Error: No worktree found at $WORKTREE_PATH +Active worktrees: +``` + +### 4. Check for Uncommitted Work + +Capture the branch name first (needed for step 6, before the worktree is removed): + +```bash +BRANCH=$(git -C "$WORKTREE_PATH" rev-parse --abbrev-ref HEAD) +``` + +Then check for uncommitted changes: + +```bash +git -C "$WORKTREE_PATH" status --porcelain +``` + +If there are uncommitted changes, warn the user with AskUserQuestion: +- Option 1: "Abort — I have unsaved work" +- Option 2: "Remove anyway — discard changes" + +**If the user chooses "Abort", stop immediately. Do NOT continue to step 5.** + +### 5. Remove the Worktree + +If the worktree had uncommitted changes and the user chose "Remove anyway": +```bash +git worktree remove "$WORKTREE_PATH" --force +``` + +If the worktree was clean (step 4 found no changes): +```bash +git worktree remove "$WORKTREE_PATH" +``` + +### 6. Try to Delete the Branch + +Only attempt branch deletion if `$BRANCH` equals `` (meaning we created it +via `/worktree-new ` without a base-ref). If the branch is something else +(e.g., `feature/existing-branch`), skip deletion — the user didn't create it. + +```bash +git branch -d -- "$BRANCH" +``` + +Let the output print naturally: +- If the branch was merged, it will be deleted and git prints a confirmation. +- If not fully merged, git prints a warning — relay that to the user + (suggest `git branch -D -- "$BRANCH"` if they want to force-delete). + +### 7. Report + +``` +Removed worktree: $WORKTREE_PATH +[Branch status from step 6] +```