ccmanager 3.7.2 → 3.7.3
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.
package/dist/components/Menu.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
3
|
import { Box, Text, useInput, useStdout } from 'ink';
|
|
4
4
|
import SelectInput from 'ink-select-input';
|
|
@@ -35,6 +35,8 @@ const Menu = ({ sessionManager, worktreeService, onSelectWorktree, onSelectRecen
|
|
|
35
35
|
const [sessions, setSessions] = useState([]);
|
|
36
36
|
const [items, setItems] = useState([]);
|
|
37
37
|
const [recentProjects, setRecentProjects] = useState([]);
|
|
38
|
+
const [highlightedWorktreePath, setHighlightedWorktreePath] = useState(null);
|
|
39
|
+
const [autoApprovalToggleCounter, setAutoApprovalToggleCounter] = useState(0);
|
|
38
40
|
const { stdout } = useStdout();
|
|
39
41
|
const fixedRows = 6;
|
|
40
42
|
// Use the search mode hook
|
|
@@ -137,7 +139,10 @@ const Menu = ({ sessionManager, worktreeService, onSelectWorktree, onSelectRecen
|
|
|
137
139
|
: items;
|
|
138
140
|
// Build menu items with proper alignment
|
|
139
141
|
const menuItems = filteredItems.map((item, index) => {
|
|
140
|
-
const
|
|
142
|
+
const baseLabel = assembleWorktreeLabel(item, columnPositions);
|
|
143
|
+
const aaDisabled = configReader.isAutoApprovalEnabled() &&
|
|
144
|
+
sessionManager.isAutoApprovalDisabledForWorktree(item.worktree.path);
|
|
145
|
+
const label = baseLabel + (aaDisabled ? ' [Auto Approval Off]' : '');
|
|
141
146
|
// Only show numbers for worktrees (0-9) when not in search mode
|
|
142
147
|
const numberPrefix = !isSearchMode && index < 10 ? `${index} ❯ ` : '❯ ';
|
|
143
148
|
return {
|
|
@@ -255,6 +260,16 @@ const Menu = ({ sessionManager, worktreeService, onSelectWorktree, onSelectRecen
|
|
|
255
260
|
}
|
|
256
261
|
}
|
|
257
262
|
setItems(menuItems);
|
|
263
|
+
// Ensure highlighted worktree path is valid for hotkey support
|
|
264
|
+
// (e.g., on initial render or when returning from a session view)
|
|
265
|
+
setHighlightedWorktreePath(prev => {
|
|
266
|
+
if (prev &&
|
|
267
|
+
menuItems.some(item => item.type === 'worktree' && item.value === prev)) {
|
|
268
|
+
return prev;
|
|
269
|
+
}
|
|
270
|
+
const first = menuItems.find(item => item.type === 'worktree');
|
|
271
|
+
return first && first.type === 'worktree' ? first.worktree.path : null;
|
|
272
|
+
});
|
|
258
273
|
}, [
|
|
259
274
|
worktrees,
|
|
260
275
|
sessions,
|
|
@@ -264,6 +279,8 @@ const Menu = ({ sessionManager, worktreeService, onSelectWorktree, onSelectRecen
|
|
|
264
279
|
recentProjects,
|
|
265
280
|
searchQuery,
|
|
266
281
|
isSearchMode,
|
|
282
|
+
autoApprovalToggleCounter,
|
|
283
|
+
sessionManager,
|
|
267
284
|
]);
|
|
268
285
|
// Handle hotkeys
|
|
269
286
|
useInput((input, _key) => {
|
|
@@ -309,6 +326,13 @@ const Menu = ({ sessionManager, worktreeService, onSelectWorktree, onSelectRecen
|
|
|
309
326
|
return;
|
|
310
327
|
}
|
|
311
328
|
switch (keyPressed) {
|
|
329
|
+
case 'a':
|
|
330
|
+
// Toggle auto-approval for the currently highlighted worktree
|
|
331
|
+
if (configReader.isAutoApprovalEnabled() && highlightedWorktreePath) {
|
|
332
|
+
sessionManager.toggleAutoApprovalForWorktree(highlightedWorktreePath);
|
|
333
|
+
setAutoApprovalToggleCounter(c => c + 1);
|
|
334
|
+
}
|
|
335
|
+
break;
|
|
312
336
|
case 'n':
|
|
313
337
|
// Trigger new worktree action
|
|
314
338
|
onSelectWorktree({
|
|
@@ -487,10 +511,15 @@ const Menu = ({ sessionManager, worktreeService, onSelectWorktree, onSelectRecen
|
|
|
487
511
|
};
|
|
488
512
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsxs(Text, { bold: true, color: "green", children: ["CCManager - Claude Code Worktree Manager v", version] }), projectName && (_jsx(Text, { bold: true, color: "green", children: projectName }))] }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Select a worktree to start or resume a Claude Code session:" }) }), isSearchMode && (_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { children: "Search: " }), _jsx(TextInputWrapper, { value: searchQuery, onChange: setSearchQuery, focus: true, placeholder: "Type to filter worktrees..." })] })), isSearchMode && items.length === 0 ? (_jsx(Box, { children: _jsx(Text, { color: "yellow", children: "No worktrees match your search" }) })) : isSearchMode ? (
|
|
489
513
|
// In search mode, show the items as a list without SelectInput
|
|
490
|
-
_jsx(Box, { flexDirection: "column", children: items.slice(0, limit).map((item, index) => (_jsxs(Text, { color: index === selectedIndex ? 'green' : undefined, children: [index === selectedIndex ? '❯ ' : ' ', item.label] }, item.value))) })) : (_jsx(SelectInput, { items: items, onSelect: item => handleSelect(item),
|
|
514
|
+
_jsx(Box, { flexDirection: "column", children: items.slice(0, limit).map((item, index) => (_jsxs(Text, { color: index === selectedIndex ? 'green' : undefined, children: [index === selectedIndex ? '❯ ' : ' ', item.label] }, item.value))) })) : (_jsx(SelectInput, { items: items, onSelect: item => handleSelect(item), onHighlight: item => {
|
|
515
|
+
const menuItem = item;
|
|
516
|
+
if (menuItem.type === 'worktree') {
|
|
517
|
+
setHighlightedWorktreePath(menuItem.worktree.path);
|
|
518
|
+
}
|
|
519
|
+
}, isFocused: !error, initialIndex: selectedIndex, limit: limit })), (error || loadError) && (_jsx(Box, { marginTop: 1, paddingX: 1, borderStyle: "round", borderColor: "red", children: _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "red", bold: true, children: ["Error: ", error || loadError] }), _jsx(Text, { color: "gray", dimColor: true, children: "Press any key to dismiss" })] }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Status: ", STATUS_ICONS.BUSY, " ", STATUS_LABELS.BUSY, ' ', STATUS_ICONS.WAITING, " ", STATUS_LABELS.WAITING, " ", STATUS_ICONS.IDLE, ' ', STATUS_LABELS.IDLE, configReader.isAutoApprovalEnabled() && (_jsxs(_Fragment, { children: [' | ', _jsx(Text, { color: "green", children: "Auto Approval Enabled" })] }))] }), _jsx(Text, { dimColor: true, children: isSearchMode
|
|
491
520
|
? 'Search Mode: Type to filter, Enter to exit search, ESC to exit search'
|
|
492
521
|
: searchQuery
|
|
493
|
-
? `Filtered: "${searchQuery}" | ↑↓ Navigate Enter Select | /-Search ESC-Clear 0-9 Quick Select N-New M-Merge D-Delete ${multiProject ? 'C-Config' : 'P-ProjConfig C-GlobalConfig'} ${projectName ? 'B-Back' : 'Q-Quit'}`
|
|
494
|
-
: `Controls: ↑↓ Navigate Enter Select | Hotkeys: 0-9 Quick Select /-Search N-New M-Merge D-Delete ${multiProject ? 'C-Config' : 'P-ProjConfig C-GlobalConfig'} ${projectName ? 'B-Back' : 'Q-Quit'}` })] })] }));
|
|
522
|
+
? `Filtered: "${searchQuery}" | ↑↓ Navigate Enter Select | /-Search ESC-Clear 0-9 Quick Select N-New M-Merge D-Delete ${configReader.isAutoApprovalEnabled() ? 'A-AutoApproval ' : ''}${multiProject ? 'C-Config' : 'P-ProjConfig C-GlobalConfig'} ${projectName ? 'B-Back' : 'Q-Quit'}`
|
|
523
|
+
: `Controls: ↑↓ Navigate Enter Select | Hotkeys: 0-9 Quick Select /-Search N-New M-Merge D-Delete ${configReader.isAutoApprovalEnabled() ? 'A-AutoApproval ' : ''}${multiProject ? 'C-Config' : 'P-ProjConfig C-GlobalConfig'} ${projectName ? 'B-Back' : 'Q-Quit'}` })] })] }));
|
|
495
524
|
};
|
|
496
525
|
export default Menu;
|
|
@@ -84,6 +84,8 @@ describe('Menu - Recent Projects', () => {
|
|
|
84
84
|
createSessionWithPreset: vi.fn(),
|
|
85
85
|
createSessionWithDevcontainer: vi.fn(),
|
|
86
86
|
destroy: vi.fn(),
|
|
87
|
+
isAutoApprovalDisabledForWorktree: vi.fn().mockReturnValue(false),
|
|
88
|
+
toggleAutoApprovalForWorktree: vi.fn().mockReturnValue(false),
|
|
87
89
|
};
|
|
88
90
|
mockWorktreeService = {
|
|
89
91
|
getWorktreesEffect: vi.fn().mockReturnValue(Effect.succeed([
|
|
@@ -14,6 +14,7 @@ export declare class SessionManager extends EventEmitter implements ISessionMana
|
|
|
14
14
|
sessions: Map<string, Session>;
|
|
15
15
|
private waitingWithBottomBorder;
|
|
16
16
|
private busyTimers;
|
|
17
|
+
private autoApprovalDisabledWorktrees;
|
|
17
18
|
private spawn;
|
|
18
19
|
detectTerminalState(session: Session): SessionState;
|
|
19
20
|
detectBackgroundTask(session: Session): number;
|
|
@@ -66,6 +67,8 @@ export declare class SessionManager extends EventEmitter implements ISessionMana
|
|
|
66
67
|
getSession(worktreePath: string): Session | undefined;
|
|
67
68
|
setSessionActive(worktreePath: string, active: boolean): void;
|
|
68
69
|
cancelAutoApproval(worktreePath: string, reason?: string): void;
|
|
70
|
+
toggleAutoApprovalForWorktree(worktreePath: string): boolean;
|
|
71
|
+
isAutoApprovalDisabledForWorktree(worktreePath: string): boolean;
|
|
69
72
|
destroySession(worktreePath: string): void;
|
|
70
73
|
/**
|
|
71
74
|
* Terminate session and cleanup resources using Effect-based error handling
|
|
@@ -22,6 +22,7 @@ export class SessionManager extends EventEmitter {
|
|
|
22
22
|
sessions;
|
|
23
23
|
waitingWithBottomBorder = new Map();
|
|
24
24
|
busyTimers = new Map();
|
|
25
|
+
autoApprovalDisabledWorktrees = new Set();
|
|
25
26
|
async spawn(command, args, worktreePath) {
|
|
26
27
|
const spawnOptions = {
|
|
27
28
|
name: 'xterm-256color',
|
|
@@ -38,7 +39,8 @@ export class SessionManager extends EventEmitter {
|
|
|
38
39
|
// If auto-approval is enabled and state is waiting_input, convert to pending_auto_approval
|
|
39
40
|
if (detectedState === 'waiting_input' &&
|
|
40
41
|
configReader.isAutoApprovalEnabled() &&
|
|
41
|
-
!stateData.autoApprovalFailed
|
|
42
|
+
!stateData.autoApprovalFailed &&
|
|
43
|
+
!this.autoApprovalDisabledWorktrees.has(session.worktreePath)) {
|
|
42
44
|
return 'pending_auto_approval';
|
|
43
45
|
}
|
|
44
46
|
return detectedState;
|
|
@@ -483,6 +485,20 @@ export class SessionManager extends EventEmitter {
|
|
|
483
485
|
}));
|
|
484
486
|
}
|
|
485
487
|
}
|
|
488
|
+
toggleAutoApprovalForWorktree(worktreePath) {
|
|
489
|
+
if (this.autoApprovalDisabledWorktrees.has(worktreePath)) {
|
|
490
|
+
this.autoApprovalDisabledWorktrees.delete(worktreePath);
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
this.autoApprovalDisabledWorktrees.add(worktreePath);
|
|
495
|
+
this.cancelAutoApproval(worktreePath, 'Auto-approval disabled for worktree');
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
isAutoApprovalDisabledForWorktree(worktreePath) {
|
|
500
|
+
return this.autoApprovalDisabledWorktrees.has(worktreePath);
|
|
501
|
+
}
|
|
486
502
|
destroySession(worktreePath) {
|
|
487
503
|
const session = this.sessions.get(worktreePath);
|
|
488
504
|
if (session) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccmanager",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.3",
|
|
4
4
|
"description": "TUI application for managing multiple Claude Code sessions across Git worktrees",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Kodai Kabasawa",
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"bin"
|
|
42
42
|
],
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"@kodaikabasawa/ccmanager-darwin-arm64": "3.7.
|
|
45
|
-
"@kodaikabasawa/ccmanager-darwin-x64": "3.7.
|
|
46
|
-
"@kodaikabasawa/ccmanager-linux-arm64": "3.7.
|
|
47
|
-
"@kodaikabasawa/ccmanager-linux-x64": "3.7.
|
|
48
|
-
"@kodaikabasawa/ccmanager-win32-x64": "3.7.
|
|
44
|
+
"@kodaikabasawa/ccmanager-darwin-arm64": "3.7.3",
|
|
45
|
+
"@kodaikabasawa/ccmanager-darwin-x64": "3.7.3",
|
|
46
|
+
"@kodaikabasawa/ccmanager-linux-arm64": "3.7.3",
|
|
47
|
+
"@kodaikabasawa/ccmanager-linux-x64": "3.7.3",
|
|
48
|
+
"@kodaikabasawa/ccmanager-win32-x64": "3.7.3"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@eslint/js": "^9.28.0",
|