Skip to content

Commit 7ce37ed

Browse files
committed
feat: tone down worktrees UI in TUI implementation
Skip the branch warning dialog entirely when there are no conflicts (not on a protected branch and no other PRD running in the same directory). For protected branches, recommend "Create branch only" instead of worktrees. Remove the "(current directory)" label from the picker for entries without branches. https://claude.ai/code/session_01V27fcymZwAuZxwfqDtUxM8
1 parent 1e9dd39 commit 7ce37ed

5 files changed

Lines changed: 30 additions & 66 deletions

File tree

internal/tui/app.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -629,16 +629,19 @@ func (a App) startLoopForPRD(prdName string) (tea.Model, tea.Cmd) {
629629
isProtected := git.IsProtectedBranch(branch)
630630
anotherRunningInSameDir := a.isAnotherPRDRunningInSameDir(prdName)
631631

632+
if !isProtected && !anotherRunningInSameDir {
633+
// No conflicts: skip the dialog entirely and start the loop directly
634+
return a.doStartLoop(prdName, prdDir)
635+
}
636+
632637
var dialogCtx DialogContext
633638
if isProtected {
634639
dialogCtx = DialogProtectedBranch
635-
} else if anotherRunningInSameDir {
636-
dialogCtx = DialogAnotherPRDRunning
637640
} else {
638-
dialogCtx = DialogNoConflicts
641+
dialogCtx = DialogAnotherPRDRunning
639642
}
640643

641-
// Show the enhanced dialog
644+
// Show the dialog only for protected branch or another PRD running
642645
a.branchWarning.SetSize(a.width, a.height)
643646
a.branchWarning.SetContext(branch, prdName, relWorktreePath)
644647
a.branchWarning.SetDialogContext(dialogCtx)

internal/tui/branch_warning.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ func (b *BranchWarning) buildOptions() {
8484
case DialogProtectedBranch:
8585
b.options = []dialogOption{
8686
{
87-
label: "Create worktree + branch",
88-
hint: b.worktreePath,
87+
label: "Create branch only",
88+
hint: "./ (current directory)",
8989
recommended: true,
90-
option: BranchOptionCreateWorktree,
90+
option: BranchOptionCreateBranch,
9191
},
9292
{
93-
label: "Create branch only",
94-
hint: "./ (current directory)",
95-
option: BranchOptionCreateBranch,
93+
label: "Create worktree + branch",
94+
hint: b.worktreePath,
95+
option: BranchOptionCreateWorktree,
9696
},
9797
{
9898
label: fmt.Sprintf("Continue on %s", b.currentBranch),
@@ -289,7 +289,7 @@ func (b *BranchWarning) renderHeader(content *strings.Builder, modalWidth int) {
289289
messageStyle := lipgloss.NewStyle().Foreground(TextColor)
290290
content.WriteString(messageStyle.Render(fmt.Sprintf("You are on the '%s' branch.", b.currentBranch)))
291291
content.WriteString("\n")
292-
content.WriteString(messageStyle.Render("It's recommended to isolate work in a worktree."))
292+
content.WriteString(messageStyle.Render("It's recommended to create a separate branch."))
293293
content.WriteString("\n\n")
294294

295295
case DialogAnotherPRDRunning:

internal/tui/branch_warning_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,22 @@ func TestBranchWarningProtectedBranch(t *testing.T) {
1616
t.Fatalf("expected 4 options for protected branch, got %d", len(bw.options))
1717
}
1818

19-
// First option should be "Create worktree + branch" (recommended)
20-
if bw.options[0].option != BranchOptionCreateWorktree {
21-
t.Errorf("expected first option to be CreateWorktree, got %v", bw.options[0].option)
19+
// First option should be "Create branch only" (recommended)
20+
if bw.options[0].option != BranchOptionCreateBranch {
21+
t.Errorf("expected first option to be CreateBranch, got %v", bw.options[0].option)
2222
}
2323
if !bw.options[0].recommended {
2424
t.Error("expected first option to be recommended")
2525
}
2626

2727
// Default selection should be first option
28-
if bw.GetSelectedOption() != BranchOptionCreateWorktree {
29-
t.Errorf("expected default selection to be CreateWorktree, got %v", bw.GetSelectedOption())
28+
if bw.GetSelectedOption() != BranchOptionCreateBranch {
29+
t.Errorf("expected default selection to be CreateBranch, got %v", bw.GetSelectedOption())
3030
}
3131

32-
// Second option should be "Create branch only"
33-
if bw.options[1].option != BranchOptionCreateBranch {
34-
t.Errorf("expected second option to be CreateBranch, got %v", bw.options[1].option)
32+
// Second option should be "Create worktree + branch"
33+
if bw.options[1].option != BranchOptionCreateWorktree {
34+
t.Errorf("expected second option to be CreateWorktree, got %v", bw.options[1].option)
3535
}
3636

3737
// Third option should be "Continue on main"
@@ -209,11 +209,11 @@ func TestBranchWarningPathHints(t *testing.T) {
209209
bw.SetDialogContext(DialogProtectedBranch)
210210

211211
// Check that options have correct path hints
212-
if bw.options[0].hint != ".chief/worktrees/auth/" {
213-
t.Errorf("expected worktree path hint, got %q", bw.options[0].hint)
212+
if bw.options[0].hint != "./ (current directory)" {
213+
t.Errorf("expected current dir hint for branch only, got %q", bw.options[0].hint)
214214
}
215-
if bw.options[1].hint != "./ (current directory)" {
216-
t.Errorf("expected current dir hint for branch only, got %q", bw.options[1].hint)
215+
if bw.options[1].hint != ".chief/worktrees/auth/" {
216+
t.Errorf("expected worktree path hint, got %q", bw.options[1].hint)
217217
}
218218
if bw.options[2].hint != "./ (current directory)" {
219219
t.Errorf("expected current dir hint for continue, got %q", bw.options[2].hint)

internal/tui/picker.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -604,13 +604,6 @@ func (p *PRDPicker) renderEntry(entry PRDEntry, selected bool, width int) string
604604
infoStr := p.formatBranchPath(branchStr, pathStr, remaining)
605605
line.WriteString(branchPathStyle.Render(infoStr))
606606
}
607-
} else if entry.Branch == "" && p.hasAnyBranch() {
608-
// If other entries have branches, show "(current directory)" for alignment
609-
branchPathStyle := lipgloss.NewStyle().Foreground(MutedColor)
610-
remaining := width - 32
611-
if remaining > 20 {
612-
line.WriteString(branchPathStyle.Render(" (current directory)"))
613-
}
614607
}
615608
}
616609

@@ -673,16 +666,6 @@ func (p *PRDPicker) formatBranchPath(branch, path string, maxWidth int) string {
673666
return prefix + string(branchRunes)
674667
}
675668

676-
// hasAnyBranch returns true if any entry has a branch set.
677-
func (p *PRDPicker) hasAnyBranch() bool {
678-
for _, entry := range p.entries {
679-
if entry.Branch != "" {
680-
return true
681-
}
682-
}
683-
return false
684-
}
685-
686669
// renderLoopStateIndicator renders a visual indicator for the loop state.
687670
func (p *PRDPicker) renderLoopStateIndicator(entry PRDEntry) string {
688671
switch entry.LoopState {

internal/tui/picker_test.go

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func TestRenderEntryNoBranch(t *testing.T) {
6464
}
6565
}
6666

67-
func TestRenderEntryNoBranchShowsCurrentDirectoryWhenOthersHaveBranch(t *testing.T) {
67+
func TestRenderEntryNoBranchOmitsCurrentDirectoryLabel(t *testing.T) {
6868
p := &PRDPicker{
6969
basePath: "/project",
7070
currentPRD: "",
@@ -87,10 +87,10 @@ func TestRenderEntryNoBranchShowsCurrentDirectoryWhenOthersHaveBranch(t *testing
8787
},
8888
}
8989

90-
// Render the entry without a branch — should show "(current directory)" since another has a branch
90+
// Render the entry without a branch — should NOT show "(current directory)" to keep worktrees low-key
9191
result := p.renderEntry(p.entries[0], false, 80)
92-
if !containsText(result, "(current directory)") {
93-
t.Errorf("expected '(current directory)' for branchless entry when others have branches, got: %s", result)
92+
if containsText(result, "(current directory)") {
93+
t.Errorf("expected no '(current directory)' label for branchless entry, got: %s", result)
9494
}
9595
}
9696

@@ -176,28 +176,6 @@ func TestWorktreeDisplayPathWithoutWorktree(t *testing.T) {
176176
}
177177
}
178178

179-
func TestHasAnyBranch(t *testing.T) {
180-
p := &PRDPicker{
181-
entries: []PRDEntry{
182-
{Name: "a", Branch: ""},
183-
{Name: "b", Branch: "chief/b"},
184-
},
185-
}
186-
if !p.hasAnyBranch() {
187-
t.Error("expected hasAnyBranch() to return true when one entry has a branch")
188-
}
189-
190-
p2 := &PRDPicker{
191-
entries: []PRDEntry{
192-
{Name: "a", Branch: ""},
193-
{Name: "c", Branch: ""},
194-
},
195-
}
196-
if p2.hasAnyBranch() {
197-
t.Error("expected hasAnyBranch() to return false when no entries have branches")
198-
}
199-
}
200-
201179
func TestRenderEntryWithLoadError(t *testing.T) {
202180
p := &PRDPicker{
203181
basePath: "/project",

0 commit comments

Comments
 (0)