Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ All config keys use `gtr.*` prefix and are managed via `git config`. Configurati
- `gtr.worktrees.prefix`: Folder prefix for worktrees (default: `""`)
- `gtr.defaultBranch`: Default branch name (default: auto-detect)
- `gtr.editor.default`: Default editor (cursor, vscode, zed, etc.)
- `gtr.editor.workspace`: Workspace file path for VS Code/Cursor (relative to worktree root, auto-detects if not set)
- `gtr.ai.default`: Default AI tool (aider, claude, codex, etc.)
- `gtr.copy.include`: Multi-valued glob patterns for files to copy
- `gtr.copy.exclude`: Multi-valued glob patterns for files to exclude
Expand All @@ -469,6 +470,7 @@ All config keys use `gtr.*` prefix and are managed via `git config`. Configurati
| `gtr.hook.preRemove` | `hooks.preRemove` |
| `gtr.hook.postRemove` | `hooks.postRemove` |
| `gtr.editor.default` | `defaults.editor` |
| `gtr.editor.workspace` | `editor.workspace` |
| `gtr.ai.default` | `defaults.ai` |

## Environment Variables
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ While `git worktree` is powerful, it's verbose and manual. `git gtr` adds qualit
| Create worktree | `git worktree add ../repo-feature feature` | `git gtr new feature` |
| Create + open | `git worktree add ... && cursor .` | `git gtr new feature --editor` |
| Open in editor | `cd ../repo-feature && cursor .` | `git gtr editor feature` |
| Start AI tool | `cd ../repo-feature && aider` | `git gtr ai feature` |
| Start AI tool | `cd ../repo-feature && claude` | `git gtr ai feature` |
| Copy config files | Manual copy/paste | Auto-copy via `gtr.copy.include` |
| Run build steps | Manual `npm install && npm run build` | Auto-run via `gtr.hook.postCreate` |
| List worktrees | `git worktree list` (shows paths) | `git gtr list` (shows branches + status) |
Expand Down Expand Up @@ -191,7 +191,7 @@ Start AI coding tool (uses `gtr.ai.default` or `--ai` flag).

```bash
git gtr ai my-feature # Uses configured AI tool
git gtr ai my-feature --ai aider # Override with aider
git gtr ai my-feature --ai codex # Override with different tool
git gtr ai my-feature -- --model gpt-4 # Pass arguments to tool
git gtr ai 1 # Use AI in main repo
```
Expand Down Expand Up @@ -296,7 +296,7 @@ All configuration is stored via `git config`. For team settings, create a `.gtrc
# Set your editor (cursor, vscode, zed)
git gtr config set gtr.editor.default cursor

# Set your AI tool (aider, claude, codex, continue, copilot, cursor, gemini, opencode)
# Set your AI tool (claude, codex, copilot, cursor, gemini, opencode, aider, continue)
git gtr config set gtr.ai.default claude

# Copy env files to new worktrees
Expand Down
12 changes: 9 additions & 3 deletions adapters/editor/cursor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ editor_can_open() {
command -v cursor >/dev/null 2>&1
}

# Open a directory in Cursor
# Usage: editor_open path
# Open a directory or workspace file in Cursor
# Usage: editor_open path [workspace_file]
editor_open() {
local path="$1"
local workspace="${2:-}"

if ! editor_can_open; then
log_error "Cursor not found. Install from https://cursor.com or enable the shell command."
return 1
fi

cursor "$path"
# Open workspace file if provided, otherwise open directory
if [ -n "$workspace" ] && [ -f "$workspace" ]; then
cursor "$workspace"
else
cursor "$path"
fi
}
12 changes: 9 additions & 3 deletions adapters/editor/vscode.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ editor_can_open() {
command -v code >/dev/null 2>&1
}

# Open a directory in VS Code
# Usage: editor_open path
# Open a directory or workspace file in VS Code
# Usage: editor_open path [workspace_file]
editor_open() {
local path="$1"
local workspace="${2:-}"

if ! editor_can_open; then
log_error "VS Code 'code' command not found. Install from https://code.visualstudio.com"
return 1
fi

code "$path"
# Open workspace file if provided, otherwise open directory
if [ -n "$workspace" ] && [ -f "$workspace" ]; then
code "$workspace"
else
code "$path"
fi
}
52 changes: 49 additions & 3 deletions bin/gtr
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,18 @@ editor_can_open() {
}

editor_open() {
local path="$1"
local workspace="${2:-}"
local target="$path"

# Use workspace file if provided and exists
if [ -n "$workspace" ] && [ -f "$workspace" ]; then
target="$workspace"
fi

# $GTR_EDITOR_CMD may contain arguments (e.g., "code --wait")
# Using eval here is necessary to handle multi-word commands properly
eval "$GTR_EDITOR_CMD \"\$1\""
eval "$GTR_EDITOR_CMD \"\$target\""
}

# Globals set by load_ai_adapter: GTR_AI_CMD, GTR_AI_CMD_NAME
Expand Down Expand Up @@ -302,8 +311,10 @@ cmd_create() {
editor=$(cfg_default gtr.editor.default GTR_EDITOR_DEFAULT "none" defaults.editor)
if [ "$editor" != "none" ]; then
load_editor_adapter "$editor"
local workspace_file
workspace_file=$(resolve_workspace_file "$worktree_path")
log_step "Opening in $editor..."
editor_open "$worktree_path"
editor_open "$worktree_path" "$workspace_file"
else
open_in_gui "$worktree_path"
log_info "Opened in file browser (no editor configured)"
Expand Down Expand Up @@ -701,8 +712,10 @@ cmd_editor() {
else
# Load editor adapter and open
load_editor_adapter "$editor"
local workspace_file
workspace_file=$(resolve_workspace_file "$worktree_path")
log_step "Opening in $editor..."
editor_open "$worktree_path"
editor_open "$worktree_path" "$workspace_file"
fi
}

Expand Down Expand Up @@ -1303,6 +1316,37 @@ cmd_config() {
esac
}

# Resolve workspace file for VS Code/Cursor editors
# Returns the workspace file path if found, empty otherwise
resolve_workspace_file() {
local worktree_path="$1"

# Check config first (gtr.editor.workspace or editor.workspace in .gtrconfig)
local configured
configured=$(cfg_default gtr.editor.workspace "" "" editor.workspace)

# Opt-out: "none" disables workspace lookup entirely
if [ "$configured" = "none" ]; then
return 0
fi

if [ -n "$configured" ]; then
local full_path="$worktree_path/$configured"
if [ -f "$full_path" ]; then
echo "$full_path"
fi
# Explicit config set - don't fall through to auto-detect
return 0
fi

# Auto-detect: find first .code-workspace in worktree root
local ws_file
ws_file=$(find "$worktree_path" -maxdepth 1 -name "*.code-workspace" -type f 2>/dev/null | head -1)
if [ -n "$ws_file" ]; then
echo "$ws_file"
fi
}

# Load editor adapter
load_editor_adapter() {
local editor="$1"
Expand Down Expand Up @@ -1530,6 +1574,8 @@ CONFIGURATION OPTIONS:
Options: cursor, vscode, zed, idea, pycharm,
webstorm, vim, nvim, emacs, sublime, nano,
atom, none
gtr.editor.workspace Workspace file for VS Code/Cursor
(relative path, auto-detects, or "none")
gtr.ai.default Default AI tool
Options: aider, claude, codex, continue,
copilot, cursor, gemini, opencode, none
Expand Down
15 changes: 15 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ echo "/.worktrees/" >> .gitignore
```bash
# Default editor: cursor, vscode, zed, or none
gtr.editor.default = cursor

# Workspace file for VS Code/Cursor (relative path from worktree root)
# If set, opens the workspace file instead of the folder
# If not set, auto-detects *.code-workspace files in worktree root
# Set to "none" to disable workspace lookup entirely
gtr.editor.workspace = project.code-workspace
```

**Setup editors:**
Expand All @@ -114,6 +120,15 @@ gtr.editor.default = cursor
- **VS Code**: Install from [code.visualstudio.com](https://code.visualstudio.com), enable `code` command
- **Zed**: Install from [zed.dev](https://zed.dev), `zed` command available automatically

**Workspace files:**

VS Code and Cursor support `.code-workspace` files for multi-root workspaces, custom settings, and recommended extensions. When opening a worktree:

1. If `gtr.editor.workspace` is set to a path, opens that file (relative to worktree root)
2. If set to `none`, disables workspace lookup (always opens folder)
3. Otherwise, auto-detects any `*.code-workspace` file in the worktree root
4. Falls back to opening the folder if no workspace file is found

---

## AI Tool Settings
Expand Down
2 changes: 2 additions & 0 deletions lib/config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ cfg_map_to_file_key() {
gtr.hook.preRemove) echo "hooks.preRemove" ;;
gtr.hook.postRemove) echo "hooks.postRemove" ;;
gtr.editor.default) echo "defaults.editor" ;;
gtr.editor.workspace) echo "editor.workspace" ;;
gtr.ai.default) echo "defaults.ai" ;;
gtr.worktrees.dir) echo "worktrees.dir" ;;
gtr.worktrees.prefix) echo "worktrees.prefix" ;;
Expand Down Expand Up @@ -306,6 +307,7 @@ cfg_list() {
hooks.preRemove) mapped_key="gtr.hook.preRemove" ;;
hooks.postRemove) mapped_key="gtr.hook.postRemove" ;;
defaults.editor) mapped_key="gtr.editor.default" ;;
editor.workspace) mapped_key="gtr.editor.workspace" ;;
defaults.ai) mapped_key="gtr.ai.default" ;;
defaults.branch) mapped_key="gtr.defaultBranch" ;;
worktrees.dir) mapped_key="gtr.worktrees.dir" ;;
Expand Down
8 changes: 8 additions & 0 deletions templates/.gtrconfig.example
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,11 @@
# Default AI tool for 'git gtr ai' command
# Available: aider, claude, codex, gemini, or custom via GTR_AI_CMD
# ai = claude

[editor]
# Workspace file for VS Code/Cursor (relative path from worktree root)
# If set, opens the workspace file instead of the folder
# If not set, auto-detects *.code-workspace files in worktree root
# Set to "none" to disable workspace lookup entirely
# workspace = project.code-workspace
# workspace = none