ccmanager 3.6.9 → 3.6.10
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/ConfigureOther.js +10 -1
- package/dist/services/config/configReader.d.ts +1 -0
- package/dist/services/config/configReader.js +4 -0
- package/dist/services/config/globalConfigManager.js +4 -0
- package/dist/services/sessionManager.js +7 -0
- package/dist/services/sessionManager.statePersistence.test.js +1 -0
- package/dist/services/sessionManager.test.js +84 -0
- package/dist/types/index.d.ts +2 -0
- package/package.json +6 -6
|
@@ -18,6 +18,7 @@ const ConfigureOther = ({ onComplete }) => {
|
|
|
18
18
|
const [customCommandDraft, setCustomCommandDraft] = useState(customCommand);
|
|
19
19
|
const [timeout, setTimeout] = useState(autoApprovalConfig.timeout ?? 30);
|
|
20
20
|
const [timeoutDraft, setTimeoutDraft] = useState(timeout);
|
|
21
|
+
const [clearHistoryOnClear, setClearHistoryOnClear] = useState(autoApprovalConfig.clearHistoryOnClear ?? false);
|
|
21
22
|
// Show if inheriting from global (for project scope)
|
|
22
23
|
const isInheriting = scope === 'project' && !configEditor.hasProjectOverride('autoApproval');
|
|
23
24
|
useInput((input, key) => {
|
|
@@ -48,6 +49,10 @@ const ConfigureOther = ({ onComplete }) => {
|
|
|
48
49
|
label: `⏱️ Set Timeout (${timeout}s)`,
|
|
49
50
|
value: 'timeout',
|
|
50
51
|
},
|
|
52
|
+
{
|
|
53
|
+
label: `Clear History on Screen Clear: ${clearHistoryOnClear ? '✅ Enabled' : '❌ Disabled'}`,
|
|
54
|
+
value: 'toggleClearHistory',
|
|
55
|
+
},
|
|
51
56
|
{
|
|
52
57
|
label: '💾 Save Changes',
|
|
53
58
|
value: 'save',
|
|
@@ -70,11 +75,15 @@ const ConfigureOther = ({ onComplete }) => {
|
|
|
70
75
|
setTimeoutDraft(timeout);
|
|
71
76
|
setView('timeout');
|
|
72
77
|
break;
|
|
78
|
+
case 'toggleClearHistory':
|
|
79
|
+
setClearHistoryOnClear(!clearHistoryOnClear);
|
|
80
|
+
break;
|
|
73
81
|
case 'save':
|
|
74
82
|
configEditor.setAutoApprovalConfig({
|
|
75
83
|
enabled: autoApprovalEnabled,
|
|
76
84
|
customCommand: customCommand.trim() || undefined,
|
|
77
85
|
timeout,
|
|
86
|
+
clearHistoryOnClear,
|
|
78
87
|
});
|
|
79
88
|
onComplete();
|
|
80
89
|
break;
|
|
@@ -104,6 +113,6 @@ const ConfigureOther = ({ onComplete }) => {
|
|
|
104
113
|
} }));
|
|
105
114
|
}
|
|
106
115
|
const scopeLabel = scope === 'project' ? 'Project' : 'Global';
|
|
107
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { bold: true, color: "green", children: ["Other & Experimental Settings (", scopeLabel, ")"] }) }), isInheriting && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { backgroundColor: "cyan", color: "black", children: [' ', "\uD83D\uDCCB Inheriting from global configuration", ' '] }) })), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Toggle experimental capabilities and other miscellaneous options." }) }), _jsx(CustomCommandSummary, { command: customCommand }), _jsx(SelectInput, { items: menuItems, onSelect: handleSelect, isFocused: true }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Press ", shortcutManager.getShortcutDisplay('cancel'), " to return without saving"] }) })] }));
|
|
116
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { bold: true, color: "green", children: ["Other & Experimental Settings (", scopeLabel, ")"] }) }), isInheriting && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { backgroundColor: "cyan", color: "black", children: [' ', "\uD83D\uDCCB Inheriting from global configuration", ' '] }) })), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Toggle experimental capabilities and other miscellaneous options." }) }), _jsx(CustomCommandSummary, { command: customCommand }), clearHistoryOnClear && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Clear History: When enabled, session output history is cleared when a screen clear escape sequence is detected (e.g., /clear command). This prevents excessive scrolling during session restoration." }) })), _jsx(SelectInput, { items: menuItems, onSelect: handleSelect, isFocused: true }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Press ", shortcutManager.getShortcutDisplay('cancel'), " to return without saving"] }) })] }));
|
|
108
117
|
};
|
|
109
118
|
export default ConfigureOther;
|
|
@@ -17,6 +17,7 @@ export declare class ConfigReader implements IConfigReader {
|
|
|
17
17
|
getConfiguration(): ConfigurationData;
|
|
18
18
|
getAutoApprovalConfig(): NonNullable<ConfigurationData['autoApproval']>;
|
|
19
19
|
isAutoApprovalEnabled(): boolean;
|
|
20
|
+
isClearHistoryOnClearEnabled(): boolean;
|
|
20
21
|
getDefaultPreset(): CommandPreset;
|
|
21
22
|
getSelectPresetOnStart(): boolean;
|
|
22
23
|
getPresetByIdEffect(id: string): Either.Either<CommandPreset, ValidationError>;
|
|
@@ -86,6 +86,10 @@ export class ConfigReader {
|
|
|
86
86
|
isAutoApprovalEnabled() {
|
|
87
87
|
return this.getAutoApprovalConfig().enabled;
|
|
88
88
|
}
|
|
89
|
+
// Check if clear history on clear is enabled
|
|
90
|
+
isClearHistoryOnClearEnabled() {
|
|
91
|
+
return this.getAutoApprovalConfig().clearHistoryOnClear ?? false;
|
|
92
|
+
}
|
|
89
93
|
// Command Preset methods - delegate to global config for modifications
|
|
90
94
|
getDefaultPreset() {
|
|
91
95
|
const presets = this.getCommandPresets();
|
|
@@ -76,6 +76,7 @@ class GlobalConfigManager {
|
|
|
76
76
|
this.config.autoApproval = {
|
|
77
77
|
enabled: false,
|
|
78
78
|
timeout: 30,
|
|
79
|
+
clearHistoryOnClear: false,
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
82
|
else {
|
|
@@ -85,6 +86,9 @@ class GlobalConfigManager {
|
|
|
85
86
|
if (!Object.prototype.hasOwnProperty.call(this.config.autoApproval, 'timeout')) {
|
|
86
87
|
this.config.autoApproval.timeout = 30;
|
|
87
88
|
}
|
|
89
|
+
if (!Object.prototype.hasOwnProperty.call(this.config.autoApproval, 'clearHistoryOnClear')) {
|
|
90
|
+
this.config.autoApproval.clearHistoryOnClear = false;
|
|
91
|
+
}
|
|
88
92
|
}
|
|
89
93
|
// Migrate legacy command config to presets if needed
|
|
90
94
|
this.ensureDefaultPresets();
|
|
@@ -277,6 +277,13 @@ export class SessionManager extends EventEmitter {
|
|
|
277
277
|
session.process.onData((data) => {
|
|
278
278
|
// Write data to virtual terminal
|
|
279
279
|
session.terminal.write(data);
|
|
280
|
+
// Check for screen clear escape sequence (e.g., from /clear command)
|
|
281
|
+
// When enabled and detected, clear the output history to prevent replaying old content on restore
|
|
282
|
+
// This helps avoid excessive scrolling when restoring sessions with large output history
|
|
283
|
+
if (configReader.isClearHistoryOnClearEnabled() &&
|
|
284
|
+
data.includes('\x1B[2J')) {
|
|
285
|
+
session.outputHistory = [];
|
|
286
|
+
}
|
|
280
287
|
// Store in output history as Buffer
|
|
281
288
|
const buffer = Buffer.from(data, 'utf8');
|
|
282
289
|
session.outputHistory.push(buffer);
|
|
@@ -41,6 +41,7 @@ vi.mock('./config/configReader.js', () => ({
|
|
|
41
41
|
getWorktreeLastOpened: vi.fn(() => ({})),
|
|
42
42
|
isAutoApprovalEnabled: vi.fn(() => false),
|
|
43
43
|
setAutoApprovalEnabled: vi.fn(),
|
|
44
|
+
isClearHistoryOnClearEnabled: vi.fn(() => false),
|
|
44
45
|
},
|
|
45
46
|
}));
|
|
46
47
|
describe('SessionManager - State Persistence', () => {
|
|
@@ -30,6 +30,7 @@ vi.mock('./config/configReader.js', () => ({
|
|
|
30
30
|
getWorktreeLastOpened: vi.fn(() => ({})),
|
|
31
31
|
isAutoApprovalEnabled: vi.fn(() => false),
|
|
32
32
|
setAutoApprovalEnabled: vi.fn(),
|
|
33
|
+
isClearHistoryOnClearEnabled: vi.fn(() => false),
|
|
33
34
|
},
|
|
34
35
|
}));
|
|
35
36
|
// Mock Terminal
|
|
@@ -694,6 +695,89 @@ describe('SessionManager', () => {
|
|
|
694
695
|
expect(session.isPrimaryCommand).toBe(false);
|
|
695
696
|
});
|
|
696
697
|
});
|
|
698
|
+
describe('clearHistoryOnClear', () => {
|
|
699
|
+
it('should clear output history when screen clear escape sequence is detected and setting is enabled', async () => {
|
|
700
|
+
// Setup
|
|
701
|
+
vi.mocked(configReader.getDefaultPreset).mockReturnValue({
|
|
702
|
+
id: '1',
|
|
703
|
+
name: 'Main',
|
|
704
|
+
command: 'claude',
|
|
705
|
+
});
|
|
706
|
+
vi.mocked(configReader.isClearHistoryOnClearEnabled).mockReturnValue(true);
|
|
707
|
+
vi.mocked(spawn).mockReturnValue(mockPty);
|
|
708
|
+
// Create session
|
|
709
|
+
const session = await Effect.runPromise(sessionManager.createSessionWithPresetEffect('/test/worktree'));
|
|
710
|
+
// Simulate some data output
|
|
711
|
+
mockPty.emit('data', 'Hello World');
|
|
712
|
+
mockPty.emit('data', 'More data');
|
|
713
|
+
// Verify output history has data
|
|
714
|
+
expect(session.outputHistory.length).toBe(2);
|
|
715
|
+
// Simulate screen clear escape sequence
|
|
716
|
+
mockPty.emit('data', '\x1B[2J');
|
|
717
|
+
// Verify output history was cleared and only contains the clear sequence
|
|
718
|
+
expect(session.outputHistory.length).toBe(1);
|
|
719
|
+
expect(session.outputHistory[0]?.toString()).toBe('\x1B[2J');
|
|
720
|
+
});
|
|
721
|
+
it('should not clear output history when screen clear escape sequence is detected but setting is disabled', async () => {
|
|
722
|
+
// Setup
|
|
723
|
+
vi.mocked(configReader.getDefaultPreset).mockReturnValue({
|
|
724
|
+
id: '1',
|
|
725
|
+
name: 'Main',
|
|
726
|
+
command: 'claude',
|
|
727
|
+
});
|
|
728
|
+
vi.mocked(configReader.isClearHistoryOnClearEnabled).mockReturnValue(false);
|
|
729
|
+
vi.mocked(spawn).mockReturnValue(mockPty);
|
|
730
|
+
// Create session
|
|
731
|
+
const session = await Effect.runPromise(sessionManager.createSessionWithPresetEffect('/test/worktree'));
|
|
732
|
+
// Simulate some data output
|
|
733
|
+
mockPty.emit('data', 'Hello World');
|
|
734
|
+
mockPty.emit('data', 'More data');
|
|
735
|
+
// Verify output history has data
|
|
736
|
+
expect(session.outputHistory.length).toBe(2);
|
|
737
|
+
// Simulate screen clear escape sequence
|
|
738
|
+
mockPty.emit('data', '\x1B[2J');
|
|
739
|
+
// Verify output history was NOT cleared
|
|
740
|
+
expect(session.outputHistory.length).toBe(3);
|
|
741
|
+
});
|
|
742
|
+
it('should not clear output history for normal data when setting is enabled', async () => {
|
|
743
|
+
// Setup
|
|
744
|
+
vi.mocked(configReader.getDefaultPreset).mockReturnValue({
|
|
745
|
+
id: '1',
|
|
746
|
+
name: 'Main',
|
|
747
|
+
command: 'claude',
|
|
748
|
+
});
|
|
749
|
+
vi.mocked(configReader.isClearHistoryOnClearEnabled).mockReturnValue(true);
|
|
750
|
+
vi.mocked(spawn).mockReturnValue(mockPty);
|
|
751
|
+
// Create session
|
|
752
|
+
const session = await Effect.runPromise(sessionManager.createSessionWithPresetEffect('/test/worktree'));
|
|
753
|
+
// Simulate normal data output without screen clear
|
|
754
|
+
mockPty.emit('data', 'Hello World');
|
|
755
|
+
mockPty.emit('data', 'More data');
|
|
756
|
+
mockPty.emit('data', 'Even more data');
|
|
757
|
+
// Verify output history contains all data
|
|
758
|
+
expect(session.outputHistory.length).toBe(3);
|
|
759
|
+
});
|
|
760
|
+
it('should clear history when screen clear is part of larger data chunk', async () => {
|
|
761
|
+
// Setup
|
|
762
|
+
vi.mocked(configReader.getDefaultPreset).mockReturnValue({
|
|
763
|
+
id: '1',
|
|
764
|
+
name: 'Main',
|
|
765
|
+
command: 'claude',
|
|
766
|
+
});
|
|
767
|
+
vi.mocked(configReader.isClearHistoryOnClearEnabled).mockReturnValue(true);
|
|
768
|
+
vi.mocked(spawn).mockReturnValue(mockPty);
|
|
769
|
+
// Create session
|
|
770
|
+
const session = await Effect.runPromise(sessionManager.createSessionWithPresetEffect('/test/worktree'));
|
|
771
|
+
// Simulate some data output
|
|
772
|
+
mockPty.emit('data', 'Hello World');
|
|
773
|
+
mockPty.emit('data', 'More data');
|
|
774
|
+
// Simulate screen clear as part of larger data chunk (e.g., from /clear command)
|
|
775
|
+
mockPty.emit('data', 'prefix\x1B[2Jsuffix');
|
|
776
|
+
// Verify output history was cleared and only contains the new chunk
|
|
777
|
+
expect(session.outputHistory.length).toBe(1);
|
|
778
|
+
expect(session.outputHistory[0]?.toString()).toBe('prefix\x1B[2Jsuffix');
|
|
779
|
+
});
|
|
780
|
+
});
|
|
697
781
|
describe('static methods', () => {
|
|
698
782
|
describe('getSessionCounts', () => {
|
|
699
783
|
// Helper to create mock session with stateMutex
|
package/dist/types/index.d.ts
CHANGED
|
@@ -113,6 +113,7 @@ export interface ConfigurationData {
|
|
|
113
113
|
enabled: boolean;
|
|
114
114
|
customCommand?: string;
|
|
115
115
|
timeout?: number;
|
|
116
|
+
clearHistoryOnClear?: boolean;
|
|
116
117
|
};
|
|
117
118
|
}
|
|
118
119
|
export type ConfigScope = 'project' | 'global';
|
|
@@ -120,6 +121,7 @@ export interface AutoApprovalConfig {
|
|
|
120
121
|
enabled: boolean;
|
|
121
122
|
customCommand?: string;
|
|
122
123
|
timeout?: number;
|
|
124
|
+
clearHistoryOnClear?: boolean;
|
|
123
125
|
}
|
|
124
126
|
export interface ProjectConfigurationData {
|
|
125
127
|
shortcuts?: ShortcutConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccmanager",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.10",
|
|
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.6.
|
|
45
|
-
"@kodaikabasawa/ccmanager-darwin-x64": "3.6.
|
|
46
|
-
"@kodaikabasawa/ccmanager-linux-arm64": "3.6.
|
|
47
|
-
"@kodaikabasawa/ccmanager-linux-x64": "3.6.
|
|
48
|
-
"@kodaikabasawa/ccmanager-win32-x64": "3.6.
|
|
44
|
+
"@kodaikabasawa/ccmanager-darwin-arm64": "3.6.10",
|
|
45
|
+
"@kodaikabasawa/ccmanager-darwin-x64": "3.6.10",
|
|
46
|
+
"@kodaikabasawa/ccmanager-linux-arm64": "3.6.10",
|
|
47
|
+
"@kodaikabasawa/ccmanager-linux-x64": "3.6.10",
|
|
48
|
+
"@kodaikabasawa/ccmanager-win32-x64": "3.6.10"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@eslint/js": "^9.28.0",
|