ccmanager 3.0.0 → 3.1.0

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.
@@ -4,6 +4,7 @@ import SelectInput from 'ink-select-input';
4
4
  import { configurationManager } from '../services/configurationManager.js';
5
5
  import { shortcutManager } from '../services/shortcutManager.js';
6
6
  import ConfigureCustomCommand from './ConfigureCustomCommand.js';
7
+ import ConfigureTimeout from './ConfigureTimeout.js';
7
8
  import CustomCommandSummary from './CustomCommandSummary.js';
8
9
  const ConfigureOther = ({ onComplete }) => {
9
10
  const autoApprovalConfig = configurationManager.getAutoApprovalConfig();
@@ -11,6 +12,8 @@ const ConfigureOther = ({ onComplete }) => {
11
12
  const [autoApprovalEnabled, setAutoApprovalEnabled] = useState(autoApprovalConfig.enabled);
12
13
  const [customCommand, setCustomCommand] = useState(autoApprovalConfig.customCommand ?? '');
13
14
  const [customCommandDraft, setCustomCommandDraft] = useState(customCommand);
15
+ const [timeout, setTimeout] = useState(autoApprovalConfig.timeout ?? 30);
16
+ const [timeoutDraft, setTimeoutDraft] = useState(timeout);
14
17
  useInput((input, key) => {
15
18
  if (shortcutManager.matchesShortcut('cancel', input, key)) {
16
19
  if (view === 'customCommand') {
@@ -18,6 +21,11 @@ const ConfigureOther = ({ onComplete }) => {
18
21
  setView('main');
19
22
  return;
20
23
  }
24
+ if (view === 'timeout') {
25
+ setTimeoutDraft(timeout);
26
+ setView('main');
27
+ return;
28
+ }
21
29
  onComplete();
22
30
  }
23
31
  });
@@ -30,6 +38,10 @@ const ConfigureOther = ({ onComplete }) => {
30
38
  label: '✏️ Edit Custom Command',
31
39
  value: 'customCommand',
32
40
  },
41
+ {
42
+ label: `⏱️ Set Timeout (${timeout}s)`,
43
+ value: 'timeout',
44
+ },
33
45
  {
34
46
  label: '💾 Save Changes',
35
47
  value: 'save',
@@ -48,10 +60,15 @@ const ConfigureOther = ({ onComplete }) => {
48
60
  setCustomCommandDraft(customCommand);
49
61
  setView('customCommand');
50
62
  break;
63
+ case 'timeout':
64
+ setTimeoutDraft(timeout);
65
+ setView('timeout');
66
+ break;
51
67
  case 'save':
52
68
  configurationManager.setAutoApprovalConfig({
53
69
  enabled: autoApprovalEnabled,
54
70
  customCommand: customCommand.trim() || undefined,
71
+ timeout,
55
72
  });
56
73
  onComplete();
57
74
  break;
@@ -71,6 +88,15 @@ const ConfigureOther = ({ onComplete }) => {
71
88
  setView('main');
72
89
  } }));
73
90
  }
91
+ if (view === 'timeout') {
92
+ return (React.createElement(ConfigureTimeout, { value: timeoutDraft, onChange: setTimeoutDraft, onCancel: () => {
93
+ setTimeoutDraft(timeout);
94
+ setView('main');
95
+ }, onSubmit: value => {
96
+ setTimeout(value);
97
+ setView('main');
98
+ } }));
99
+ }
74
100
  return (React.createElement(Box, { flexDirection: "column" },
75
101
  React.createElement(Box, { marginBottom: 1 },
76
102
  React.createElement(Text, { bold: true, color: "green" }, "Other & Experimental Settings")),
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ interface ConfigureTimeoutProps {
3
+ value: number;
4
+ onChange: (value: number) => void;
5
+ onSubmit: (value: number) => void;
6
+ onCancel: () => void;
7
+ }
8
+ declare const ConfigureTimeout: React.FC<ConfigureTimeoutProps>;
9
+ export default ConfigureTimeout;
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ import TextInputWrapper from './TextInputWrapper.js';
4
+ import { shortcutManager } from '../services/shortcutManager.js';
5
+ const ConfigureTimeout = ({ value, onChange, onSubmit, onCancel, }) => {
6
+ const [inputValue, setInputValue] = React.useState(String(value));
7
+ const handleChange = (newValue) => {
8
+ // Only allow numeric input
9
+ const filtered = newValue.replace(/[^0-9]/g, '');
10
+ setInputValue(filtered);
11
+ const parsed = parseInt(filtered, 10);
12
+ if (!isNaN(parsed) && parsed > 0) {
13
+ onChange(parsed);
14
+ }
15
+ };
16
+ const handleSubmit = () => {
17
+ const parsed = parseInt(inputValue, 10);
18
+ if (!isNaN(parsed) && parsed > 0) {
19
+ onSubmit(parsed);
20
+ }
21
+ };
22
+ useInput((input, key) => {
23
+ if (shortcutManager.matchesShortcut('cancel', input, key)) {
24
+ onCancel();
25
+ }
26
+ });
27
+ return (React.createElement(Box, { flexDirection: "column" },
28
+ React.createElement(Box, { marginBottom: 1 },
29
+ React.createElement(Text, { bold: true, color: "green" }, "Auto-Approval Timeout")),
30
+ React.createElement(Box, { marginBottom: 1 },
31
+ React.createElement(Text, null, "Enter timeout in seconds for auto-approval verification:")),
32
+ React.createElement(Box, { marginBottom: 1 },
33
+ React.createElement(TextInputWrapper, { value: inputValue, onChange: handleChange, onSubmit: handleSubmit, placeholder: "e.g. 30", focus: true })),
34
+ React.createElement(Box, { marginBottom: 1 },
35
+ React.createElement(Text, { dimColor: true }, "Must be a positive integer (minimum: 1 second, default: 30 seconds)")),
36
+ React.createElement(Box, null,
37
+ React.createElement(Text, { dimColor: true },
38
+ "Press Enter to save, ",
39
+ shortcutManager.getShortcutDisplay('cancel'),
40
+ " to go back"))));
41
+ };
42
+ export default ConfigureTimeout;
@@ -3,7 +3,12 @@ import { ProcessError } from '../types/errors.js';
3
3
  import { configurationManager } from './configurationManager.js';
4
4
  import { logger } from '../utils/logger.js';
5
5
  import { execFile, spawn, } from 'child_process';
6
- const AUTO_APPROVAL_TIMEOUT_MS = 60000;
6
+ const DEFAULT_TIMEOUT_SECONDS = 30;
7
+ const getTimeoutMs = () => {
8
+ const config = configurationManager.getAutoApprovalConfig();
9
+ const timeoutSeconds = config.timeout ?? DEFAULT_TIMEOUT_SECONDS;
10
+ return timeoutSeconds * 1000;
11
+ };
7
12
  const createAbortError = () => {
8
13
  const error = new Error('Auto-approval verification aborted');
9
14
  error.name = 'AbortError';
@@ -80,15 +85,16 @@ export class AutoApprovalVerifier {
80
85
  return;
81
86
  signal.removeEventListener('abort', abortListener);
82
87
  };
88
+ const timeoutMs = getTimeoutMs();
83
89
  const timeoutId = setTimeout(() => {
84
90
  settle(() => {
85
91
  logger.warn('Auto-approval verification timed out, terminating helper Claude process');
86
92
  if (child?.pid) {
87
93
  child.kill('SIGKILL');
88
94
  }
89
- reject(new Error('Auto-approval verification timed out after 60s'));
95
+ reject(new Error(`Auto-approval verification timed out after ${timeoutMs / 1000}s`));
90
96
  });
91
- }, AUTO_APPROVAL_TIMEOUT_MS);
97
+ }, timeoutMs);
92
98
  if (signal) {
93
99
  if (signal.aborted) {
94
100
  abortListener();
@@ -166,13 +172,14 @@ export class AutoApprovalVerifier {
166
172
  const child = spawn(command, [], spawnOptions);
167
173
  let stdout = '';
168
174
  let stderr = '';
175
+ const timeoutMs = getTimeoutMs();
169
176
  timeoutId = setTimeout(() => {
170
177
  logger.warn('Auto-approval custom command timed out, terminating process');
171
178
  settle(() => {
172
179
  child.kill('SIGKILL');
173
- reject(new Error('Auto-approval verification custom command timed out after 60s'));
180
+ reject(new Error(`Auto-approval verification custom command timed out after ${timeoutMs / 1000}s`));
174
181
  });
175
- }, AUTO_APPROVAL_TIMEOUT_MS);
182
+ }, timeoutMs);
176
183
  if (signal) {
177
184
  if (signal.aborted) {
178
185
  abortListener();
@@ -24,6 +24,7 @@ export declare class ConfigurationManager {
24
24
  getAutoApprovalConfig(): NonNullable<ConfigurationData['autoApproval']>;
25
25
  setAutoApprovalConfig(autoApproval: NonNullable<ConfigurationData['autoApproval']>): void;
26
26
  setAutoApprovalEnabled(enabled: boolean): void;
27
+ setAutoApprovalTimeout(timeout: number): void;
27
28
  getCommandConfig(): CommandConfig;
28
29
  setCommandConfig(commandConfig: CommandConfig): void;
29
30
  private migrateLegacyCommandToPresets;
@@ -102,10 +102,16 @@ export class ConfigurationManager {
102
102
  if (!this.config.autoApproval) {
103
103
  this.config.autoApproval = {
104
104
  enabled: false,
105
+ timeout: 30,
105
106
  };
106
107
  }
107
- else if (!Object.prototype.hasOwnProperty.call(this.config.autoApproval, 'enabled')) {
108
- this.config.autoApproval.enabled = false;
108
+ else {
109
+ if (!Object.prototype.hasOwnProperty.call(this.config.autoApproval, 'enabled')) {
110
+ this.config.autoApproval.enabled = false;
111
+ }
112
+ if (!Object.prototype.hasOwnProperty.call(this.config.autoApproval, 'timeout')) {
113
+ this.config.autoApproval.timeout = 30;
114
+ }
109
115
  }
110
116
  // Migrate legacy command config to presets if needed
111
117
  this.migrateLegacyCommandToPresets();
@@ -174,9 +180,14 @@ export class ConfigurationManager {
174
180
  this.saveConfig();
175
181
  }
176
182
  getAutoApprovalConfig() {
177
- return (this.config.autoApproval || {
183
+ const config = this.config.autoApproval || {
178
184
  enabled: false,
179
- });
185
+ };
186
+ // Default timeout to 30 seconds if not set
187
+ return {
188
+ ...config,
189
+ timeout: config.timeout ?? 30,
190
+ };
180
191
  }
181
192
  setAutoApprovalConfig(autoApproval) {
182
193
  this.config.autoApproval = autoApproval;
@@ -186,6 +197,10 @@ export class ConfigurationManager {
186
197
  const currentConfig = this.getAutoApprovalConfig();
187
198
  this.setAutoApprovalConfig({ ...currentConfig, enabled });
188
199
  }
200
+ setAutoApprovalTimeout(timeout) {
201
+ const currentConfig = this.getAutoApprovalConfig();
202
+ this.setAutoApprovalConfig({ ...currentConfig, timeout });
203
+ }
189
204
  getCommandConfig() {
190
205
  // For backward compatibility, return the default preset as CommandConfig
191
206
  const defaultPreset = this.getDefaultPreset();
@@ -535,10 +550,16 @@ export class ConfigurationManager {
535
550
  if (!config.autoApproval) {
536
551
  config.autoApproval = {
537
552
  enabled: false,
553
+ timeout: 30,
538
554
  };
539
555
  }
540
- else if (!Object.prototype.hasOwnProperty.call(config.autoApproval, 'enabled')) {
541
- config.autoApproval.enabled = false;
556
+ else {
557
+ if (!Object.prototype.hasOwnProperty.call(config.autoApproval, 'enabled')) {
558
+ config.autoApproval.enabled = false;
559
+ }
560
+ if (!Object.prototype.hasOwnProperty.call(config.autoApproval, 'timeout')) {
561
+ config.autoApproval.timeout = 30;
562
+ }
542
563
  }
543
564
  return config;
544
565
  }
@@ -110,6 +110,7 @@ export interface ConfigurationData {
110
110
  autoApproval?: {
111
111
  enabled: boolean;
112
112
  customCommand?: string;
113
+ timeout?: number;
113
114
  };
114
115
  }
115
116
  export interface GitProject {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccmanager",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "TUI application for managing multiple Claude Code sessions across Git worktrees",
5
5
  "license": "MIT",
6
6
  "author": "Kodai Kabasawa",