Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
12 changes: 12 additions & 0 deletions .changeset/add-open-in-terminal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"git-work-grove": minor
---

Add Open in Terminal command for worktrees and workspace files

- New context menu and inline button to open a terminal at any item's location
- Terminal CWD resolves to the worktree directory (or parent directory for workspace files)
- Customizable terminal name templates for all 4 item types
- Added "terminal" option to the openBehavior setting
- Leaf worktrees/repository (no workspace files) now respond to clicks via openBehavior
- QuickPick includes "Open in Terminal" and "Always Open in Terminal" options
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Design specs live in `docs/spec/`. These are the **source of truth** for how fea
| `docs/spec/commands.md` | All commands, menu placement, behaviors |
| `docs/spec/workspace-scanning.md` | File discovery, include/exclude patterns, limits |
| `docs/spec/open-behavior.md` | Open modes, URI resolution, click handling |
| `docs/spec/open-in-terminal.md` | CWD resolution, terminal naming, prunable guard |
| `docs/spec/empty-states.md` | Git unavailable, no repository, no worktrees messages |

## The 4 Fundamental Types
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ If VS Code or GitLens ever ships proper `.code-workspace` support for worktrees,
- **Favorites** — Pin any item (repository, worktree, workspace file) to the top with drag-and-drop reordering
- **Current indicator** — Green icon and badge highlight the currently open item
- **Customizable templates** — Full control over labels and descriptions for all 8 item types
- **Open in Terminal** — Right-click to open a terminal at any worktree or workspace file location
Comment thread
VdustR marked this conversation as resolved.
- **Prune** — Clean up stale worktree records
- **Live updates** — FileSystemWatcher detects worktree changes automatically

Expand All @@ -59,6 +60,7 @@ Right-click any item in the tree to access:
|--------|-------------|
| **Open in New Window** | Opens the worktree or workspace file in a new VS Code window |
| **Open in Current Window** | Opens in the current VS Code window |
| **Open in Terminal** | Opens a terminal at the item's location |
| **Reveal in Finder** | Opens the item's location in your OS file manager |
| **Copy Name** | Copy the item's display name to clipboard |
| **Copy Path** | Copy the item's filesystem path to clipboard |
Expand All @@ -67,6 +69,7 @@ Favorite-specific actions appear as inline buttons:

| Button | Description |
|--------|-------------|
| **Open in Terminal** (terminal icon) | Open a terminal at this item's location (non-favorites only) |
| **Add Favorite** (star outline) | Pin this item to the Favorites section |
| **Remove Favorite** (filled star) | Unpin from Favorites |
| **Move Up / Move Down** (chevrons) | Reorder within Favorites |
Expand All @@ -87,9 +90,10 @@ The WORKGROVE panel header provides:

When you click a workspace file, the behavior depends on the `openBehavior` setting:

- **`ask`** (default) — Shows a picker with options: _Open in New Window_, _Open in Current Window_, plus "Always" variants that persist your choice
- **`ask`** (default) — Shows a picker with options: _Open in New Window_, _Open in Current Window_, _Open in Terminal_, plus "Always" variants that persist your choice
- **`newWindow`** — Always opens in a new window
- **`currentWindow`** — Always opens in the current window
- **`terminal`** — Always opens a terminal at the item's location

### Current Indicator

Expand All @@ -106,10 +110,10 @@ Open VS Code Settings (`Cmd+,` / `Ctrl+,`) and search for `git-work-grove`:

| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `git-work-grove.openBehavior` | `ask` \| `newWindow` \| `currentWindow` | `ask` | Default action when opening a workspace |
| `git-work-grove.openBehavior` | `ask` \| `newWindow` \| `currentWindow` \| `terminal` | `ask` | Default action when opening a workspace |
| `git-work-grove.workspaceFile.include` | `string[]` | `["*.code-workspace"]` | Glob patterns for workspace file scanning |
| `git-work-grove.workspaceFile.exclude` | `string[]` | `[]` | Glob patterns to exclude from scanning |
| `git-work-grove.template.*` | `string` | *(varies)* | Display templates — see [Template Customization](https://github.com/vp-tw/vscode-extension-git-work-grove/blob/main/docs/templates.md) |
| `git-work-grove.template.*` | `string` | *(varies)* | Display templates (label, description, terminalName) — see [Template Customization](https://github.com/vp-tw/vscode-extension-git-work-grove/blob/main/docs/templates.md) |
| `git-work-grove.favorites` | `string[]` | `[]` | Ordered list of favorited item paths (managed via the UI) |

### Template Customization
Expand Down Expand Up @@ -139,6 +143,7 @@ These commands appear when right-clicking items in the tree view:
|---------|-------------|
| Open in New Window | Open worktree or workspace file in a new VS Code window |
| Open in Current Window | Open in the current VS Code window |
| Open in Terminal | Open a terminal at the item's location |
| Reveal in Finder | Open the item's location in your OS file manager |
| Copy Name | Copy the item's display name to clipboard |
| Copy Path | Copy the item's filesystem path to clipboard |
Expand All @@ -161,6 +166,7 @@ Design documents for contributors and AI-assisted development:
- [Commands](https://github.com/vp-tw/vscode-extension-git-work-grove/blob/main/docs/spec/commands.md) — All commands, menu placement, behaviors
- [Workspace Scanning](https://github.com/vp-tw/vscode-extension-git-work-grove/blob/main/docs/spec/workspace-scanning.md) — File discovery, include/exclude patterns
- [Open Behavior](https://github.com/vp-tw/vscode-extension-git-work-grove/blob/main/docs/spec/open-behavior.md) — Open modes, URI resolution, click handling
- [Open in Terminal](https://github.com/vp-tw/vscode-extension-git-work-grove/blob/main/docs/spec/open-in-terminal.md) — CWD resolution, terminal naming, prunable guard
- [Empty States](https://github.com/vp-tw/vscode-extension-git-work-grove/blob/main/docs/spec/empty-states.md) — Git unavailable, no repository, no worktrees messages

## Installation
Expand Down
21 changes: 19 additions & 2 deletions docs/spec/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ All commands use the `gitWorkGrove.*` prefix.
| `moveFavoriteDown` | Move Favorite Down | `$(chevron-down)` | `gitWorkGrove.hasRepository` |
| `copyName` | Copy Name | — | `gitWorkGrove.hasRepository` |
| `copyPath` | Copy Path | — | `gitWorkGrove.hasRepository` |
| `openInTerminal` | Open in Terminal | `$(terminal)` | `gitWorkGrove.hasRepository` |
| `revealInOS` | Reveal in Finder | — | `gitWorkGrove.hasRepository` |
| `refresh` | Refresh | `$(refresh)` | `gitWorkGrove.hasRepository` |
| `showOutput` | Show Output | — | *(always)* |
Expand All @@ -37,8 +38,10 @@ All commands use the `gitWorkGrove.*` prefix.
| `openInNewWindow` | `navigation@1` | `viewItem =~ /^worktree\|^workspaceFile\|^repository\|^favorite\./` |
| `openInCurrentWindow` | `navigation@2` | *(same)* |
| `revealInOS` | `navigation@3` | *(same)* |
| `openInTerminal` | `navigation@4` | *(same)* |
Comment thread
VdustR marked this conversation as resolved.
| `copyName` | `5_cutcopypaste@1` | *(same)* |
| `copyPath` | `5_cutcopypaste@2` | *(same)* |
| `openInTerminal` | `inline` | `viewItem =~ /^worktree\|^workspaceFile\|^repository/` AND NOT `viewItem =~ /favorite/` |
| `addFavorite` | `inline` | `viewItem =~ /^worktree\|^workspaceFile\|^repository/` AND NOT `viewItem =~ /favorite/` |
| `removeFavorite` | `inline` | `viewItem =~ /favorite/` |
| `moveFavoriteUp` | `inline` | `viewItem =~ /^favorite\./` |
Expand All @@ -56,16 +59,30 @@ All open commands resolve a URI from the tree item:

`openInNewWindow` always opens in a new window. `openInCurrentWindow` always opens in the current window.

### Open in Terminal

Opens a new VS Code terminal at the item's location. See [Open in Terminal spec](open-in-terminal.md) for full details.

- `WorktreeItem` / `GroupHeaderItem` (repository) → cwd is `worktreeInfo.path`
- `WorkspaceFileItem` → cwd is `dirname(workspaceFileInfo.path)`
- `FavoriteItem` → resolved via duck-typing (same detection order as `resolveUri`)

Terminal name is rendered from `template.*.terminalName` settings (4 templates — favorites reuse non-favorite templates).

Prunable worktree guard: checks `fs.existsSync(cwd)` before creating the terminal. Shows a warning if the directory is missing.

### Default Open (click)

Handled by `treeView.onDidChangeSelection`, not a registered command. Triggers on:

- `FavoriteItem` click (when not current)
- `WorkspaceFileItem` click (when not current)
- `WorktreeItem` click (leaf only — `CollapsibleState.None`, i.e., no workspace files)
- `GroupHeaderItem` (repository) click (leaf only — `CollapsibleState.None`, i.e., no workspace files)

Uses `openBehavior` setting: `"ask"` shows a QuickPick, `"newWindow"` / `"currentWindow"` opens directly. The QuickPick includes "Always" options that persist the choice.
Uses `openBehavior` setting: `"ask"` shows a QuickPick, `"newWindow"` / `"currentWindow"` / `"terminal"` opens directly. The QuickPick includes "Always" options that persist the choice.

WorktreeItem clicks are NOT handled — clicking a worktree expands/collapses it.
WorktreeItem and GroupHeaderItem (repository) clicks with children expand/collapse only (no open).

### Copy Name / Copy Path

Expand Down
20 changes: 15 additions & 5 deletions docs/spec/open-behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ Controls how items are opened when clicked or via context menu commands.

| Setting | Type | Default | Scope |
|---|---|---|---|
| `git-work-grove.openBehavior` | `"ask" \| "newWindow" \| "currentWindow"` | `"ask"` | resource |
| `git-work-grove.openBehavior` | `"ask" \| "newWindow" \| "currentWindow" \| "terminal"` | `"ask"` | resource |

## Modes

### `"ask"` (default)

Shows a QuickPick with 4 options:
Shows a QuickPick with 6 options:

1. **Open in New Window** — opens once in new window
2. **Open in Current Window** — opens once in current window
3. **Always Open in New Window** — persists `"newWindow"` to global settings, then opens
4. **Always Open in Current Window** — persists `"currentWindow"` to global settings, then opens
3. **Open in Terminal** — opens once in terminal
4. **Always Open in New Window** — persists `"newWindow"` to global settings, then opens
5. **Always Open in Current Window** — persists `"currentWindow"` to global settings, then opens
6. **Always Open in Terminal** — persists `"terminal"` to global settings, then opens terminal

The "Always" options update the setting globally, so future clicks use the new mode.

Expand All @@ -29,15 +31,23 @@ Opens the target in a new VS Code window (`forceNewWindow: true`).

Opens the target in the current VS Code window (`forceNewWindow: false`).

### `"terminal"`

Opens a new VS Code terminal at the item's location instead of opening a window. See [Open in Terminal spec](open-in-terminal.md) for CWD resolution and terminal naming details.

## Trigger Points

| Trigger | Behavior |
|---|---|
| Click on `WorkspaceFileItem` | Uses `openBehavior` setting |
| Click on `FavoriteItem` | Uses `openBehavior` setting |
| Click on `WorktreeItem` | Expands/collapses only (no open) |
| Click on `WorktreeItem` (has children) | Expands/collapses only (no open) |
| Click on `WorktreeItem` (leaf, no workspace files) | Uses `openBehavior` setting |
| Click on `GroupHeaderItem` (repository, has children) | Expands/collapses only (no open) |
| Click on `GroupHeaderItem` (repository, leaf, no workspace files) | Uses `openBehavior` setting |
| Context menu "Open in New Window" | Always new window (ignores setting) |
| Context menu "Open in Current Window" | Always current window (ignores setting) |
| Context menu "Open in Terminal" | Always opens terminal (ignores setting) |

## URI Resolution

Expand Down
69 changes: 69 additions & 0 deletions docs/spec/open-in-terminal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Open in Terminal Specification

Opens a new VS Code terminal at the item's filesystem location.

## Command

| Command ID | Title | Icon |
|---|---|---|
| `gitWorkGrove.openInTerminal` | Open in Terminal | `$(terminal)` |

## CWD Resolution

The working directory depends on the item type:

| Item type | CWD |
|---|---|
| `GroupHeaderItem` (repository) | `worktreeInfo.path` |
| `WorktreeItem` | `worktreeInfo.path` |
| `WorkspaceFileItem` | `dirname(workspaceFileInfo.path)` |
| `FavoriteItem` | Resolved via duck-typing (see detection order below) |

### Detection Order (duck-typing)

Same order as `resolveUri` in `open-behavior.md`:

1. `"favoritePath" in item` → resolve to underlying type
2. `"workspaceFileInfo" in item` → `dirname(workspaceFileInfo.path)`
3. `"worktreeInfo" in item && item.worktreeInfo` → `worktreeInfo.path`

## Terminal Naming

The terminal name is rendered from `template.*.terminalName` settings. Only 4 templates exist (one per non-favorite type). Favorites reuse the corresponding non-favorite template.

| Item type | Template setting |
|---|---|
| Repository | `template.repository.terminalName` |
| Worktree | `template.worktree.terminalName` |
| Repository Workspace | `template.repositoryWorkspace.terminalName` |
| Worktree Workspace | `template.worktreeWorkspace.terminalName` |

### Defaults

| Setting | Default |
|---|---|
| `template.repository.terminalName` | `{ref}` |
| `template.worktree.terminalName` | `{ref}` |
| `template.repositoryWorkspace.terminalName` | `{name}` |
| `template.worktreeWorkspace.terminalName` | `{name} ({ref})` |

Templates use the same variables and syntax as label/description templates. See [templates.md](templates.md) for variable reference.

## Prunable Worktree Guard

Before creating the terminal, checks `fs.existsSync(cwd)`. If the directory does not exist (prunable worktree), shows a warning message instead of creating the terminal.

## Error Handling

The `createTerminal` call is wrapped in a try/catch. On failure, shows an error message via `showErrorMessage` and logs the error via `logError`.

## Integration with openBehavior

The `openBehavior` setting now accepts `"terminal"` as a fourth option. When set, clicking an item (at trigger points listed in [open-behavior.md](open-behavior.md)) opens a terminal instead of a window.

Leaf worktree and repository items (`CollapsibleState.None`, i.e., no workspace files found) now trigger `openBehavior` on click. Previously these clicks were inert (only expand/collapse applied, and leaf items had nothing to expand).

## Menu Placement

- **Context menu**: `navigation@4` — appears for all actionable item types
- **Inline button**: `$(terminal)` icon on non-favorite items only (favorites already have 3 inline buttons: remove, move up, move down)
16 changes: 15 additions & 1 deletion docs/spec/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Based on the 4 fundamental cases (repository, worktree, repositoryWorkspace, wor
| 7 | Favorite Repo Workspace | `template.favoriteRepositoryWorkspace` | Favorited workspace file from repository |
| 8 | Favorite Worktree Workspace | `template.favoriteWorktreeWorkspace` | Favorited workspace file from linked worktree |

Each type has `.label` and `.description` settings (16 settings total).
Each type has `.label` and `.description` settings (16 settings total). Additionally, the 4 non-favorite types have a `.terminalName` setting (4 settings, 20 total). Favorite types reuse the corresponding non-favorite `terminalName` template.

## Template Rendering

Expand Down Expand Up @@ -109,6 +109,10 @@ Built by `workspaceFileVars(name, filePath, parent)` where `parent.isMain === fa
| `template.favoriteRepositoryWorkspace.description` | *(empty)* |
| `template.favoriteWorktreeWorkspace.label` | `{name}` |
| `template.favoriteWorktreeWorkspace.description` | `🌲 {worktree}` |
| `template.repository.terminalName` | `Repository` |
| `template.worktree.terminalName` | `{name}` |
| `template.repositoryWorkspace.terminalName` | `{name}` |
| `template.worktreeWorkspace.terminalName` | `{name}` |

Design rationale: Only `favoriteWorktreeWorkspace.description` has a non-empty default because it's the only type that needs disambiguation — a workspace file shortcut in the Favorites section has no visible parent to indicate which worktree it belongs to.

Expand All @@ -134,6 +138,16 @@ else → favoriteWorktreeWorkspace.label / .description

For `type === "repo"` / `"worktree"`: Uses the corresponding `favoriteRepository.*` / `favoriteWorktree.*` templates directly.

### Terminal Name (all types)

Terminal name templates use the non-favorite variant only. Favorites resolve to their underlying type:

```
FavoriteItem (type === "repo") → repository.terminalName
FavoriteItem (type === "worktree") → worktree.terminalName
FavoriteItem (type === "workspaceFile") → repositoryWorkspace.terminalName or worktreeWorkspace.terminalName (based on parent.isMain)
```

### Repository header (GroupHeaderItem)

Always uses `repository.label` / `repository.description` with `worktreeVars(main)`.
Expand Down
4 changes: 4 additions & 0 deletions docs/spec/tree-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ Note: "Has children" means at least one workspace file exists in the worktree di

Green color: `new vscode.ThemeColor("terminal.ansiGreen")`

## Inline Buttons

Non-favorite items show two inline buttons: `$(terminal)` (Open in Terminal) and `$(star-empty)` (Add Favorite). Favorite items show three inline buttons: `$(star-full)` (Remove Favorite), `$(chevron-up)` (Move Up), `$(chevron-down)` (Move Down). See [commands.md](commands.md) for full menu placement details.

## Tooltips

All items use a unified tooltip format via `buildTooltip()` (`src/utils/tooltip.ts`). Fields shown in order:
Expand Down
6 changes: 5 additions & 1 deletion docs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Based on the 4 fundamental cases (repository, worktree, repositoryWorkspace, wor
| 7 | `template.favoriteRepositoryWorkspace` | Favorited workspace file from Repository |
| 8 | `template.favoriteWorktreeWorkspace` | Favorited workspace file from linked worktree |

Each has `.label` and `.description`. Set to empty string to hide.
Each has `.label` and `.description`. Set to empty string to hide. The 4 non-favorite types also have a `.terminalName` setting for the Open in Terminal command. Favorites reuse the corresponding non-favorite `terminalName` template.

## Settings

Expand All @@ -39,6 +39,10 @@ Each has `.label` and `.description`. Set to empty string to hide.
| `template.favoriteRepositoryWorkspace.description` | *(empty)* |
| `template.favoriteWorktreeWorkspace.label` | `{name}` |
| `template.favoriteWorktreeWorkspace.description` | `🌲 {worktree}` |
| `template.repository.terminalName` | `Repository` |
| `template.worktree.terminalName` | `{name}` |
| `template.repositoryWorkspace.terminalName` | `{name}` |
| `template.worktreeWorkspace.terminalName` | `{name}` |

## Variables

Expand Down
Loading
Loading