ccmanager 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -126,13 +126,13 @@ const App = () => {
126
126
  const handleCancelNewWorktree = () => {
127
127
  handleReturnToMenu();
128
128
  };
129
- const handleDeleteWorktrees = async (worktreePaths) => {
129
+ const handleDeleteWorktrees = async (worktreePaths, deleteBranch) => {
130
130
  setView('deleting-worktree');
131
131
  setError(null);
132
132
  // Delete the worktrees
133
133
  let hasError = false;
134
134
  for (const path of worktreePaths) {
135
- const result = worktreeService.deleteWorktree(path);
135
+ const result = worktreeService.deleteWorktree(path, { deleteBranch });
136
136
  if (!result.success) {
137
137
  hasError = true;
138
138
  setError(result.error || 'Failed to delete worktree');
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ interface DeleteConfirmationProps {
3
+ worktrees: Array<{
4
+ path: string;
5
+ branch?: string;
6
+ }>;
7
+ onConfirm: (deleteBranch: boolean) => void;
8
+ onCancel: () => void;
9
+ }
10
+ declare const DeleteConfirmation: React.FC<DeleteConfirmationProps>;
11
+ export default DeleteConfirmation;
@@ -0,0 +1,133 @@
1
+ import React, { useState } from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ import { shortcutManager } from '../services/shortcutManager.js';
4
+ const DeleteConfirmation = ({ worktrees, onConfirm, onCancel, }) => {
5
+ // Check if any worktrees have branches
6
+ const hasAnyBranches = worktrees.some(wt => wt.branch);
7
+ const [deleteBranch, setDeleteBranch] = useState(true);
8
+ const [focusedOption, setFocusedOption] = useState(hasAnyBranches ? 'deleteBranch' : 'confirm');
9
+ // Helper functions for navigation
10
+ const isRadioOption = (option) => option === 'deleteBranch' || option === 'keepBranch';
11
+ const isActionButton = (option) => option === 'confirm' || option === 'cancel';
12
+ const handleUpArrow = () => {
13
+ if (!hasAnyBranches) {
14
+ if (focusedOption === 'cancel')
15
+ setFocusedOption('confirm');
16
+ return;
17
+ }
18
+ const navigationMap = {
19
+ keepBranch: 'deleteBranch',
20
+ confirm: 'keepBranch',
21
+ cancel: 'keepBranch',
22
+ };
23
+ const next = navigationMap[focusedOption];
24
+ if (next)
25
+ setFocusedOption(next);
26
+ };
27
+ const handleDownArrow = () => {
28
+ if (!hasAnyBranches) {
29
+ if (focusedOption === 'confirm')
30
+ setFocusedOption('cancel');
31
+ return;
32
+ }
33
+ const navigationMap = {
34
+ deleteBranch: 'keepBranch',
35
+ keepBranch: 'confirm',
36
+ confirm: 'cancel',
37
+ };
38
+ const next = navigationMap[focusedOption];
39
+ if (next)
40
+ setFocusedOption(next);
41
+ };
42
+ const handleHorizontalArrow = (direction) => {
43
+ if (isActionButton(focusedOption)) {
44
+ setFocusedOption(direction === 'left' ? 'confirm' : 'cancel');
45
+ }
46
+ };
47
+ const handleSelect = () => {
48
+ if (isRadioOption(focusedOption)) {
49
+ setDeleteBranch(focusedOption === 'deleteBranch');
50
+ }
51
+ else if (focusedOption === 'confirm') {
52
+ onConfirm(deleteBranch);
53
+ }
54
+ else if (focusedOption === 'cancel') {
55
+ onCancel();
56
+ }
57
+ };
58
+ useInput((input, key) => {
59
+ if (key.upArrow) {
60
+ handleUpArrow();
61
+ }
62
+ else if (key.downArrow) {
63
+ handleDownArrow();
64
+ }
65
+ else if (key.leftArrow) {
66
+ handleHorizontalArrow('left');
67
+ }
68
+ else if (key.rightArrow) {
69
+ handleHorizontalArrow('right');
70
+ }
71
+ else if (input === ' ' && isRadioOption(focusedOption)) {
72
+ setDeleteBranch(focusedOption === 'deleteBranch');
73
+ }
74
+ else if (key.return) {
75
+ handleSelect();
76
+ }
77
+ else if (shortcutManager.matchesShortcut('cancel', input, key)) {
78
+ onCancel();
79
+ }
80
+ });
81
+ return (React.createElement(Box, { flexDirection: "column" },
82
+ React.createElement(Text, { bold: true, color: "red" }, "\u26A0\uFE0F Delete Confirmation"),
83
+ React.createElement(Box, { marginTop: 1, marginBottom: 1, flexDirection: "column" },
84
+ React.createElement(Text, null, "You are about to delete the following worktrees:"),
85
+ worktrees.length <= 10 ? (worktrees.map(wt => (React.createElement(Text, { key: wt.path, color: "red" },
86
+ "\u2022 ",
87
+ wt.branch ? wt.branch.replace('refs/heads/', '') : 'detached',
88
+ ' ',
89
+ "(",
90
+ wt.path,
91
+ ")")))) : (React.createElement(React.Fragment, null,
92
+ worktrees.slice(0, 8).map(wt => (React.createElement(Text, { key: wt.path, color: "red" },
93
+ "\u2022",
94
+ ' ',
95
+ wt.branch ? wt.branch.replace('refs/heads/', '') : 'detached',
96
+ ' ',
97
+ "(",
98
+ wt.path,
99
+ ")"))),
100
+ React.createElement(Text, { color: "red", dimColor: true },
101
+ "... and ",
102
+ worktrees.length - 8,
103
+ " more worktrees")))),
104
+ hasAnyBranches && (React.createElement(Box, { marginBottom: 1, flexDirection: "column" },
105
+ React.createElement(Text, { bold: true }, "What do you want to do with the associated branches?"),
106
+ React.createElement(Box, { marginTop: 1, flexDirection: "column" },
107
+ React.createElement(Box, null,
108
+ React.createElement(Text, { color: focusedOption === 'deleteBranch' ? 'red' : undefined, inverse: focusedOption === 'deleteBranch' },
109
+ deleteBranch ? '(•)' : '( )',
110
+ " Delete the branches too")),
111
+ React.createElement(Box, null,
112
+ React.createElement(Text, { color: focusedOption === 'keepBranch' ? 'green' : undefined, inverse: focusedOption === 'keepBranch' },
113
+ !deleteBranch ? '(•)' : '( )',
114
+ " Keep the branches"))))),
115
+ React.createElement(Box, { marginTop: 1 },
116
+ React.createElement(Box, { marginRight: 2 },
117
+ React.createElement(Text, { color: focusedOption === 'confirm' ? 'green' : 'white', inverse: focusedOption === 'confirm' },
118
+ ' ',
119
+ "Confirm",
120
+ ' ')),
121
+ React.createElement(Box, null,
122
+ React.createElement(Text, { color: focusedOption === 'cancel' ? 'red' : 'white', inverse: focusedOption === 'cancel' },
123
+ ' ',
124
+ "Cancel",
125
+ ' '))),
126
+ React.createElement(Box, { marginTop: 1 },
127
+ React.createElement(Text, { dimColor: true },
128
+ "Use \u2191\u2193 to navigate options, Space/Enter to select,",
129
+ ' ',
130
+ shortcutManager.getShortcutDisplay('cancel'),
131
+ " to cancel"))));
132
+ };
133
+ export default DeleteConfirmation;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  interface DeleteWorktreeProps {
3
- onComplete: (worktreePaths: string[]) => void;
3
+ onComplete: (worktreePaths: string[], deleteBranch: boolean) => void;
4
4
  onCancel: () => void;
5
5
  }
6
6
  declare const DeleteWorktree: React.FC<DeleteWorktreeProps>;
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useEffect } from 'react';
2
2
  import { Box, Text, useInput } from 'ink';
3
3
  import { WorktreeService } from '../services/worktreeService.js';
4
- import Confirmation from './Confirmation.js';
4
+ import DeleteConfirmation from './DeleteConfirmation.js';
5
5
  import { shortcutManager } from '../services/shortcutManager.js';
6
6
  const DeleteWorktree = ({ onComplete, onCancel, }) => {
7
7
  const [worktrees, setWorktrees] = useState([]);
@@ -63,41 +63,14 @@ const DeleteWorktree = ({ onComplete, onCancel, }) => {
63
63
  }
64
64
  if (confirmMode) {
65
65
  const selectedWorktrees = Array.from(selectedIndices).map(index => worktrees[index]);
66
- const handleConfirm = () => {
66
+ const handleConfirm = (deleteBranch) => {
67
67
  const selectedPaths = Array.from(selectedIndices).map(index => worktrees[index].path);
68
- onComplete(selectedPaths);
68
+ onComplete(selectedPaths, deleteBranch);
69
69
  };
70
70
  const handleCancel = () => {
71
71
  setConfirmMode(false);
72
72
  };
73
- const confirmMessage = (React.createElement(Box, { flexDirection: "column" },
74
- React.createElement(Text, { bold: true, color: "red" }, "\u26A0\uFE0F Delete Confirmation"),
75
- React.createElement(Box, { marginTop: 1, marginBottom: 1, flexDirection: "column" },
76
- React.createElement(Text, null, "You are about to delete the following worktrees:"),
77
- selectedWorktrees.length <= 10 ? (selectedWorktrees.map(wt => (React.createElement(Text, { key: wt.path, color: "red" },
78
- "\u2022",
79
- ' ',
80
- wt.branch ? wt.branch.replace('refs/heads/', '') : 'detached',
81
- ' ',
82
- "(",
83
- wt.path,
84
- ")")))) : (React.createElement(React.Fragment, null,
85
- selectedWorktrees.slice(0, 8).map(wt => (React.createElement(Text, { key: wt.path, color: "red" },
86
- "\u2022",
87
- ' ',
88
- wt.branch
89
- ? wt.branch.replace('refs/heads/', '')
90
- : 'detached',
91
- ' ',
92
- "(",
93
- wt.path,
94
- ")"))),
95
- React.createElement(Text, { color: "red", dimColor: true },
96
- "... and ",
97
- selectedWorktrees.length - 8,
98
- " more worktrees")))),
99
- React.createElement(Text, { bold: true }, "This will also delete their branches. Are you sure?")));
100
- return (React.createElement(Confirmation, { message: confirmMessage, onConfirm: handleConfirm, onCancel: handleCancel }));
73
+ return (React.createElement(DeleteConfirmation, { worktrees: selectedWorktrees, onConfirm: handleConfirm, onCancel: handleCancel }));
101
74
  }
102
75
  return (React.createElement(Box, { flexDirection: "column" },
103
76
  React.createElement(Box, { marginBottom: 1 },
@@ -13,7 +13,9 @@ export declare class WorktreeService {
13
13
  success: boolean;
14
14
  error?: string;
15
15
  };
16
- deleteWorktree(worktreePath: string): {
16
+ deleteWorktree(worktreePath: string, options?: {
17
+ deleteBranch?: boolean;
18
+ }): {
17
19
  success: boolean;
18
20
  error?: string;
19
21
  };
@@ -210,7 +210,7 @@ export class WorktreeService {
210
210
  };
211
211
  }
212
212
  }
213
- deleteWorktree(worktreePath) {
213
+ deleteWorktree(worktreePath, options) {
214
214
  try {
215
215
  // Get the worktree info to find the branch
216
216
  const worktrees = this.getWorktrees();
@@ -232,19 +232,20 @@ export class WorktreeService {
232
232
  cwd: this.rootPath,
233
233
  encoding: 'utf8',
234
234
  });
235
- // Delete the branch if it exists
236
- const branchName = worktree.branch
237
- ? worktree.branch.replace('refs/heads/', '')
238
- : 'detached';
239
- try {
240
- execSync(`git branch -D "${branchName}"`, {
241
- cwd: this.rootPath,
242
- encoding: 'utf8',
243
- });
244
- }
245
- catch {
246
- // Branch might not exist or might be checked out elsewhere
247
- // This is not a fatal error
235
+ // Delete the branch if requested (default to true for backward compatibility)
236
+ const deleteBranch = options?.deleteBranch ?? true;
237
+ if (deleteBranch && worktree.branch) {
238
+ const branchName = worktree.branch.replace('refs/heads/', '');
239
+ try {
240
+ execSync(`git branch -D "${branchName}"`, {
241
+ cwd: this.rootPath,
242
+ encoding: 'utf8',
243
+ });
244
+ }
245
+ catch {
246
+ // Branch might not exist or might be checked out elsewhere
247
+ // This is not a fatal error
248
+ }
248
249
  }
249
250
  return { success: true };
250
251
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccmanager",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "TUI application for managing multiple Claude Code sessions across Git worktrees",
5
5
  "license": "MIT",
6
6
  "author": "Kodai Kabasawa",