Skip to content

Commit 836e6cb

Browse files
Release v1.0.2 with multi-IDE support and improved worktree list layout.
Add VS Code, Cursor, and Claude Code open buttons with install detection, flex column layout for branch/path, and branded IDE icons.
1 parent d751bc3 commit 836e6cb

17 files changed

Lines changed: 742 additions & 76 deletions

FyneApp.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ Website = "https://github.com/jigarthacker24/git-worktree-manager"
44
Icon = "Icon.png"
55
Name = "Git Worktree Manager"
66
ID = "io.github.jigarthacker24.gitworktreemanager"
7-
Version = "1.0.1"
8-
Build = 3
7+
Version = "1.0.2"
8+
Build = 4
99

1010
[Development]
1111
HelperText = "Requires git in PATH"

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ Repository: [github.com/jigarthacker24/git-worktree-manager](https://github.com/
77
## Features
88

99
- Open a cloned repository by path (with recent repos)
10-
- List worktrees: branch, folder name, path
10+
- List worktrees: directory, branch, path
1111
- Pin up to 3 worktrees per repository
1212
- Copy branch name or worktree path
13-
- Open a worktree in Cursor IDE
13+
- Open a worktree in VS Code, Cursor, or Claude Code (icons disabled when not installed)
1414
- Create worktrees from an existing branch (searchable) or a new branch
1515
- Remove worktrees (with confirmation and optional force)
1616

@@ -25,7 +25,10 @@ Repository: [github.com/jigarthacker24/git-worktree-manager](https://github.com/
2525
### Run packaged app
2626

2727
- `git` in your PATH
28-
- On macOS: Cursor at `/Applications/Cursor.app` or `cursor` in PATH
28+
- Optional IDEs (install any you want to use; unavailable IDEs show as disabled icons):
29+
- **VS Code:** `code` in PATH or `/Applications/Visual Studio Code.app` (macOS)
30+
- **Cursor:** `cursor` in PATH or `/Applications/Cursor.app` (macOS)
31+
- **Claude Code:** `claude` CLI and/or `claude-cli://` URL handler (installed by the [native installer](https://claude.ai/install)). The Claude desktop chat app alone does not include Code on the free plan (Pro/Max required for desktop Code).
2932

3033
## Install
3134

internal/ide/cursor.go

Lines changed: 0 additions & 37 deletions
This file was deleted.

internal/ide/detect.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package ide
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"runtime"
7+
)
8+
9+
func Detect() Availability {
10+
cli := hasClaudeCodeRuntime()
11+
return Availability{
12+
VSCode: detectVSCode(),
13+
Cursor: detectCursor(),
14+
Claude: cli,
15+
ClaudeDesktopApp: hasClaudeDesktop(),
16+
}
17+
}
18+
19+
func detectVSCode() bool {
20+
if hasCLI("code") {
21+
return true
22+
}
23+
if runtime.GOOS == "darwin" {
24+
return appExists("/Applications/Visual Studio Code.app")
25+
}
26+
return false
27+
}
28+
29+
func detectCursor() bool {
30+
if hasCLI("cursor") {
31+
return true
32+
}
33+
if runtime.GOOS == "darwin" {
34+
return appExists("/Applications/Cursor.app")
35+
}
36+
return false
37+
}
38+
39+
// hasClaudeCodeRuntime reports whether Claude Code can open a worktree (CLI or URL handler).
40+
// Claude.app alone is the chat client; desktop Code requires Pro/Max and is not counted here.
41+
func hasClaudeCodeRuntime() bool {
42+
if len(claudeCLIPaths()) > 0 {
43+
return true
44+
}
45+
if hasClaudeCLIHandler() {
46+
return true
47+
}
48+
for _, p := range claudeCodeAppPaths() {
49+
if appExists(p) {
50+
return true
51+
}
52+
}
53+
return false
54+
}
55+
56+
// claudeCLIPaths returns likely claude binary locations (PATH and common install dirs).
57+
// GUI apps often launch without ~/.local/bin on PATH.
58+
func claudeCLIPaths() []string {
59+
seen := make(map[string]struct{})
60+
var paths []string
61+
add := func(p string) {
62+
if p == "" {
63+
return
64+
}
65+
if _, ok := seen[p]; ok {
66+
return
67+
}
68+
if !isExecutable(p) {
69+
return
70+
}
71+
seen[p] = struct{}{}
72+
paths = append(paths, p)
73+
}
74+
75+
if p, err := execLookPath("claude"); err == nil {
76+
add(p)
77+
}
78+
home, err := os.UserHomeDir()
79+
if err != nil {
80+
return paths
81+
}
82+
add(filepath.Join(home, ".local", "bin", "claude"))
83+
add("/opt/homebrew/bin/claude")
84+
add("/usr/local/bin/claude")
85+
return paths
86+
}
87+
88+
func claudeCodeAppPaths() []string {
89+
var paths []string
90+
if runtime.GOOS == "darwin" {
91+
if home, err := os.UserHomeDir(); err == nil {
92+
paths = append(paths,
93+
filepath.Join(home, "Applications", "Claude Code URL Handler.app"),
94+
filepath.Join(home, "Applications", "Claude Code.app"),
95+
filepath.Join(home, ".local", "share", "claude", "ClaudeCode.app"),
96+
)
97+
}
98+
}
99+
return paths
100+
}
101+
102+
func hasClaudeCLIHandler() bool {
103+
if runtime.GOOS != "darwin" {
104+
return false
105+
}
106+
home, err := os.UserHomeDir()
107+
if err != nil {
108+
return false
109+
}
110+
return appExists(filepath.Join(home, "Applications", "Claude Code URL Handler.app"))
111+
}
112+
113+
func hasClaudeDesktop() bool {
114+
switch runtime.GOOS {
115+
case "darwin":
116+
return appExists("/Applications/Claude.app")
117+
default:
118+
return false
119+
}
120+
}
121+
122+
func hasCLI(name string) bool {
123+
_, err := execLookPath(name)
124+
return err == nil
125+
}
126+
127+
func appExists(path string) bool {
128+
info, err := os.Stat(path)
129+
return err == nil && info.IsDir()
130+
}
131+
132+
func isExecutable(path string) bool {
133+
info, err := os.Stat(path)
134+
if err != nil {
135+
return false
136+
}
137+
if info.IsDir() {
138+
return false
139+
}
140+
return info.Mode()&0111 != 0
141+
}

internal/ide/exec.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package ide
2+
3+
import "os/exec"
4+
5+
func execLookPath(name string) (string, error) {
6+
return exec.LookPath(name)
7+
}
8+
9+
func start(name string, args ...string) error {
10+
cmd := exec.Command(name, args...)
11+
return cmd.Start()
12+
}

internal/ide/ide.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package ide
2+
3+
// Kind identifies a supported IDE.
4+
type Kind int
5+
6+
const (
7+
VSCode Kind = iota
8+
Cursor
9+
Claude
10+
)
11+
12+
// Availability reports which IDEs appear installed on this machine.
13+
type Availability struct {
14+
VSCode bool
15+
Cursor bool
16+
Claude bool // Claude Code runtime (CLI or handler) — can open a worktree
17+
ClaudeDesktopApp bool // Claude.app chat/desktop client (Code tab needs Pro/Max)
18+
}
19+
20+
// ClaudeHint returns the hover text for the Claude Code toolbar button.
21+
func ClaudeHint(a Availability) string {
22+
if a.Claude {
23+
return "Open in Claude Code"
24+
}
25+
if a.ClaudeDesktopApp {
26+
return "Claude Code requires Pro/Max on desktop, or install the CLI: https://claude.ai/install"
27+
}
28+
return "Claude Code not installed"
29+
}
30+
31+
// Name returns a human-readable IDE name.
32+
func (k Kind) Name() string {
33+
switch k {
34+
case VSCode:
35+
return "VS Code"
36+
case Cursor:
37+
return "Cursor"
38+
case Claude:
39+
return "Claude Code"
40+
default:
41+
return "IDE"
42+
}
43+
}

0 commit comments

Comments
 (0)