# opencode-worktree Isolated OpenCode sessions via git worktrees. ## Overview Each OpenCode session gets its own git worktree with a unique branch. This prevents: - Clashes with parallel sessions on the same repo - Accidental overwrites from multiple agents - Confusion from work-in-progress across contexts ## Prerequisites - Git - opencode installed and configured ## Installation ### Option 1: Source directly (Recommended) ```bash . skills/opencode-worktree/opencode-worktree.sh ``` ### Option 2: Copy to PATH ```bash cp skills/opencode-worktree/opencode-worktree.sh ~/.local/bin/opencode-worktree chmod +x ~/.local/bin/opencode-worktree ``` ## Usage ### Create new session ```bash . opencode-worktree.sh # session-20260327-a1b2c3 . opencode-worktree.sh refactor-auth # session-20260327-a1b2c3-refactor-auth ``` ### Cleanup ```bash . opencode-worktree.sh --cleanup # remove all session-* worktrees . opencode-worktree.sh --cleanup # remove specific worktree ``` ## How It Works 1. **Cleanup** - On every launch, removes all stale `session-*` worktrees and their branches 2. **Create** - Creates new worktree based on `main` with unique name: `session-{timestamp}-{random6}[-{purpose}]` 3. **Launch** - Changes into worktree and launches opencode 4. **Exit** - When opencode exits, you return to your original directory (worktree remains for review) ## Example Workflow ```bash # Start session for refactoring auth . opencode-worktree.sh refactor-auth # ... do work in opencode ... # Exit opencode (worktree with your changes still exists) # Later, resume or cleanup . opencode-worktree.sh --cleanup session-20260327-a1b2c3-refactor-auth ``` --- ## Script Source ```bash #!/bin/bash # opencode-worktree - Isolated OpenCode sessions via git worktrees set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WORKTREE_BASE="$PWD/.git/worktrees" PURPOSE="" CLEANUP_ONLY=false CLEANUP_NAME="" usage() { cat < Remove specific worktree by name Examples: $(basename "$0") # session-20260327-a1b2c3 $(basename "$0") refactor-auth # session-20260327-a1b2c3-refactor-auth $(basename "$0") --cleanup # remove all session-* worktrees EOF } parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --cleanup) CLEANUP_ONLY=true if [[ $# -gt 1 && ! "$2" =~ ^-- ]]; then CLEANUP_NAME="$2" shift fi ;; -h|--help) usage exit 0 ;; *) if [[ -z "$PURPOSE" ]]; then PURPOSE="$1" fi ;; esac shift done } cleanup_stale() { if [[ ! -d "$WORKTREE_BASE" ]]; then return fi for wt in "$WORKTREE_BASE"/session-*; do [[ -d "$wt" ]] || continue branch=$(git -C "$wt" rev-parse --abbrev-ref HEAD 2>/dev/null) || continue echo "Removing stale worktree: $(basename "$wt")" git worktree remove "$wt" --force 2>/dev/null || true if [[ -n "$branch" && "$branch" != "HEAD" ]]; then git branch -D "$branch" 2>/dev/null || true fi done } cleanup_single() { local name="$1" local wt_path="$WORKTREE_BASE/$name" if [[ ! -d "$wt_path" ]]; then echo "Worktree '$name' not found" return fi branch=$(git -C "$wt_path" rev-parse --abbrev-ref HEAD 2>/dev/null) || branch="" echo "Removing worktree: $name" git worktree remove "$wt_path" --force 2>/dev/null || true if [[ -n "$branch" && "$branch" != "HEAD" ]]; then git branch -D "$branch" 2>/dev/null || true fi } create_worktree() { local timestamp=$(date +%Y%m%d-%H%M%S) local random=$(head -c 3 /dev/urandom | xxd -p | head -c 6) local worktree_name="session-${timestamp}-${random}" local branch_name="$worktree_name" if [[ -n "$PURPOSE" ]]; then worktree_name="${worktree_name}-${PURPOSE}" branch_name="$worktree_name" fi local worktree_path="$WORKTREE_BASE/$worktree_name" # Cleanup any existing with same name if [[ -d "$worktree_path" ]]; then echo "Removing existing worktree: $worktree_name" git worktree remove "$worktree_path" --force 2>/dev/null || true fi # Ensure main exists and is up to date if ! git show-ref --quiet refs/heads/main 2>/dev/null; then echo "Error: 'main' branch does not exist" exit 1 fi # Create worktree from main echo "Creating worktree: $worktree_name" git worktree add -b "$branch_name" "$worktree_path" main # Launch opencode in worktree echo "Entering worktree and launching opencode..." cd "$worktree_path" exec opencode } main() { # Verify we're in a git repo if ! git rev-parse --is-inside-work-tree 2>/dev/null; then echo "Error: Must be run inside a git repository" exit 1 fi if [[ "$CLEANUP_ONLY" == true ]]; then if [[ -n "$CLEANUP_NAME" ]]; then cleanup_single "$CLEANUP_NAME" else cleanup_stale fi exit 0 fi cleanup_stale create_worktree } parse_args "$@" main ```