ccmanager 3.3.2 → 3.5.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.
Files changed (92) hide show
  1. package/README.md +11 -5
  2. package/dist/components/App.js +17 -3
  3. package/dist/components/App.test.js +5 -5
  4. package/dist/components/Configuration.d.ts +2 -0
  5. package/dist/components/Configuration.js +6 -2
  6. package/dist/components/ConfigureCommand.js +34 -11
  7. package/dist/components/ConfigureOther.js +18 -4
  8. package/dist/components/ConfigureOther.test.js +48 -12
  9. package/dist/components/ConfigureShortcuts.js +27 -85
  10. package/dist/components/ConfigureStatusHooks.js +19 -4
  11. package/dist/components/ConfigureStatusHooks.test.js +46 -12
  12. package/dist/components/ConfigureWorktree.js +18 -4
  13. package/dist/components/ConfigureWorktreeHooks.js +19 -4
  14. package/dist/components/ConfigureWorktreeHooks.test.js +49 -14
  15. package/dist/components/Menu.js +72 -14
  16. package/dist/components/Menu.recent-projects.test.js +2 -0
  17. package/dist/components/Menu.test.js +2 -0
  18. package/dist/components/NewWorktree.js +2 -2
  19. package/dist/components/NewWorktree.test.js +6 -6
  20. package/dist/components/PresetSelector.js +2 -2
  21. package/dist/constants/statusIcons.d.ts +4 -1
  22. package/dist/constants/statusIcons.js +10 -1
  23. package/dist/constants/statusIcons.test.js +42 -0
  24. package/dist/contexts/ConfigEditorContext.d.ts +21 -0
  25. package/dist/contexts/ConfigEditorContext.js +25 -0
  26. package/dist/services/autoApprovalVerifier.js +3 -3
  27. package/dist/services/autoApprovalVerifier.test.js +2 -2
  28. package/dist/services/config/configEditor.d.ts +46 -0
  29. package/dist/services/{configurationManager.effect.test.js → config/configEditor.effect.test.js} +46 -49
  30. package/dist/services/config/configEditor.js +101 -0
  31. package/dist/services/{configurationManager.selectPresetOnStart.test.js → config/configEditor.selectPresetOnStart.test.js} +27 -19
  32. package/dist/services/config/configEditor.test.d.ts +1 -0
  33. package/dist/services/{configurationManager.test.js → config/configEditor.test.js} +60 -132
  34. package/dist/services/config/configReader.d.ts +28 -0
  35. package/dist/services/config/configReader.js +95 -0
  36. package/dist/services/config/configReader.multiProject.test.d.ts +1 -0
  37. package/dist/services/config/configReader.multiProject.test.js +136 -0
  38. package/dist/services/config/globalConfigManager.d.ts +30 -0
  39. package/dist/services/config/globalConfigManager.js +216 -0
  40. package/dist/services/config/index.d.ts +13 -0
  41. package/dist/services/config/index.js +13 -0
  42. package/dist/services/config/projectConfigManager.d.ts +41 -0
  43. package/dist/services/config/projectConfigManager.js +181 -0
  44. package/dist/services/config/projectConfigManager.test.d.ts +1 -0
  45. package/dist/services/config/projectConfigManager.test.js +105 -0
  46. package/dist/services/config/testUtils.d.ts +81 -0
  47. package/dist/services/config/testUtils.js +351 -0
  48. package/dist/services/sessionManager.autoApproval.test.js +9 -6
  49. package/dist/services/sessionManager.d.ts +2 -0
  50. package/dist/services/sessionManager.effect.test.js +27 -18
  51. package/dist/services/sessionManager.js +43 -40
  52. package/dist/services/sessionManager.statePersistence.test.js +5 -4
  53. package/dist/services/sessionManager.test.js +71 -49
  54. package/dist/services/shortcutManager.d.ts +0 -1
  55. package/dist/services/shortcutManager.js +5 -16
  56. package/dist/services/shortcutManager.test.js +2 -2
  57. package/dist/services/stateDetector/base.d.ts +1 -0
  58. package/dist/services/stateDetector/claude.d.ts +1 -0
  59. package/dist/services/stateDetector/claude.js +8 -0
  60. package/dist/services/stateDetector/claude.test.js +102 -0
  61. package/dist/services/stateDetector/cline.d.ts +1 -0
  62. package/dist/services/stateDetector/cline.js +3 -0
  63. package/dist/services/stateDetector/codex.d.ts +1 -0
  64. package/dist/services/stateDetector/codex.js +3 -0
  65. package/dist/services/stateDetector/cursor.d.ts +1 -0
  66. package/dist/services/stateDetector/cursor.js +3 -0
  67. package/dist/services/stateDetector/gemini.d.ts +1 -0
  68. package/dist/services/stateDetector/gemini.js +3 -0
  69. package/dist/services/stateDetector/github-copilot.d.ts +1 -0
  70. package/dist/services/stateDetector/github-copilot.js +3 -0
  71. package/dist/services/stateDetector/opencode.d.ts +1 -0
  72. package/dist/services/stateDetector/opencode.js +3 -0
  73. package/dist/services/stateDetector/types.d.ts +1 -0
  74. package/dist/services/worktreeService.d.ts +12 -0
  75. package/dist/services/worktreeService.js +24 -4
  76. package/dist/services/worktreeService.sort.test.js +105 -109
  77. package/dist/services/worktreeService.test.js +5 -5
  78. package/dist/types/index.d.ts +47 -7
  79. package/dist/utils/gitUtils.d.ts +8 -0
  80. package/dist/utils/gitUtils.js +32 -0
  81. package/dist/utils/hookExecutor.js +2 -2
  82. package/dist/utils/hookExecutor.test.js +13 -12
  83. package/dist/utils/mutex.d.ts +1 -0
  84. package/dist/utils/mutex.js +1 -0
  85. package/dist/utils/worktreeUtils.js +3 -2
  86. package/dist/utils/worktreeUtils.test.js +2 -1
  87. package/package.json +7 -7
  88. package/dist/services/configurationManager.d.ts +0 -121
  89. package/dist/services/configurationManager.js +0 -597
  90. /package/dist/{services/configurationManager.effect.test.d.ts → constants/statusIcons.test.d.ts} +0 -0
  91. /package/dist/services/{configurationManager.selectPresetOnStart.test.d.ts → config/configEditor.effect.test.d.ts} +0 -0
  92. /package/dist/services/{configurationManager.test.d.ts → config/configEditor.selectPresetOnStart.test.d.ts} +0 -0
@@ -1,597 +0,0 @@
1
- import { homedir } from 'os';
2
- import { join } from 'path';
3
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
4
- import { Effect, Either } from 'effect';
5
- import { DEFAULT_SHORTCUTS, } from '../types/index.js';
6
- import { FileSystemError, ConfigError, ValidationError, } from '../types/errors.js';
7
- export class ConfigurationManager {
8
- constructor() {
9
- Object.defineProperty(this, "configPath", {
10
- enumerable: true,
11
- configurable: true,
12
- writable: true,
13
- value: void 0
14
- });
15
- Object.defineProperty(this, "legacyShortcutsPath", {
16
- enumerable: true,
17
- configurable: true,
18
- writable: true,
19
- value: void 0
20
- });
21
- Object.defineProperty(this, "configDir", {
22
- enumerable: true,
23
- configurable: true,
24
- writable: true,
25
- value: void 0
26
- });
27
- Object.defineProperty(this, "config", {
28
- enumerable: true,
29
- configurable: true,
30
- writable: true,
31
- value: {}
32
- });
33
- Object.defineProperty(this, "worktreeLastOpened", {
34
- enumerable: true,
35
- configurable: true,
36
- writable: true,
37
- value: new Map()
38
- });
39
- // Determine config directory based on platform
40
- const homeDir = homedir();
41
- this.configDir =
42
- process.platform === 'win32'
43
- ? join(process.env['APPDATA'] || join(homeDir, 'AppData', 'Roaming'), 'ccmanager')
44
- : join(homeDir, '.config', 'ccmanager');
45
- // Ensure config directory exists
46
- if (!existsSync(this.configDir)) {
47
- mkdirSync(this.configDir, { recursive: true });
48
- }
49
- this.configPath = join(this.configDir, 'config.json');
50
- this.legacyShortcutsPath = join(this.configDir, 'shortcuts.json');
51
- this.loadConfig();
52
- }
53
- loadConfig() {
54
- // Try to load the new config file
55
- if (existsSync(this.configPath)) {
56
- try {
57
- const configData = readFileSync(this.configPath, 'utf-8');
58
- this.config = JSON.parse(configData);
59
- }
60
- catch (error) {
61
- console.error('Failed to load configuration:', error);
62
- this.config = {};
63
- }
64
- }
65
- else {
66
- // If new config doesn't exist, check for legacy shortcuts.json
67
- this.migrateLegacyShortcuts();
68
- }
69
- // Check if shortcuts need to be loaded from legacy file
70
- // This handles the case where config.json exists but doesn't have shortcuts
71
- if (!this.config.shortcuts && existsSync(this.legacyShortcutsPath)) {
72
- this.migrateLegacyShortcuts();
73
- }
74
- // Ensure default values
75
- if (!this.config.shortcuts) {
76
- this.config.shortcuts = DEFAULT_SHORTCUTS;
77
- }
78
- if (!this.config.statusHooks) {
79
- this.config.statusHooks = {};
80
- }
81
- if (!this.config.worktreeHooks) {
82
- this.config.worktreeHooks = {};
83
- }
84
- if (!this.config.worktree) {
85
- this.config.worktree = {
86
- autoDirectory: false,
87
- copySessionData: true,
88
- sortByLastSession: false,
89
- };
90
- }
91
- if (!Object.prototype.hasOwnProperty.call(this.config.worktree, 'copySessionData')) {
92
- this.config.worktree.copySessionData = true;
93
- }
94
- if (!Object.prototype.hasOwnProperty.call(this.config.worktree, 'sortByLastSession')) {
95
- this.config.worktree.sortByLastSession = false;
96
- }
97
- if (!this.config.command) {
98
- this.config.command = {
99
- command: 'claude',
100
- };
101
- }
102
- if (!this.config.autoApproval) {
103
- this.config.autoApproval = {
104
- enabled: false,
105
- timeout: 30,
106
- };
107
- }
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
- }
115
- }
116
- // Migrate legacy command config to presets if needed
117
- this.migrateLegacyCommandToPresets();
118
- }
119
- migrateLegacyShortcuts() {
120
- if (existsSync(this.legacyShortcutsPath)) {
121
- try {
122
- const shortcutsData = readFileSync(this.legacyShortcutsPath, 'utf-8');
123
- const shortcuts = JSON.parse(shortcutsData);
124
- // Validate that it's a valid shortcuts config
125
- if (shortcuts && typeof shortcuts === 'object') {
126
- this.config.shortcuts = shortcuts;
127
- // Save to new config format
128
- this.saveConfig();
129
- console.log('Migrated shortcuts from legacy shortcuts.json to config.json');
130
- }
131
- }
132
- catch (error) {
133
- console.error('Failed to migrate legacy shortcuts:', error);
134
- }
135
- }
136
- }
137
- saveConfig() {
138
- try {
139
- writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
140
- }
141
- catch (error) {
142
- console.error('Failed to save configuration:', error);
143
- }
144
- }
145
- getShortcuts() {
146
- return this.config.shortcuts || DEFAULT_SHORTCUTS;
147
- }
148
- setShortcuts(shortcuts) {
149
- this.config.shortcuts = shortcuts;
150
- this.saveConfig();
151
- }
152
- getStatusHooks() {
153
- return this.config.statusHooks || {};
154
- }
155
- setStatusHooks(hooks) {
156
- this.config.statusHooks = hooks;
157
- this.saveConfig();
158
- }
159
- getWorktreeHooks() {
160
- return this.config.worktreeHooks || {};
161
- }
162
- setWorktreeHooks(hooks) {
163
- this.config.worktreeHooks = hooks;
164
- this.saveConfig();
165
- }
166
- getConfiguration() {
167
- return this.config;
168
- }
169
- setConfiguration(config) {
170
- this.config = config;
171
- this.saveConfig();
172
- }
173
- getWorktreeConfig() {
174
- return (this.config.worktree || {
175
- autoDirectory: false,
176
- });
177
- }
178
- setWorktreeConfig(worktreeConfig) {
179
- this.config.worktree = worktreeConfig;
180
- this.saveConfig();
181
- }
182
- getAutoApprovalConfig() {
183
- const config = this.config.autoApproval || {
184
- enabled: false,
185
- };
186
- // Default timeout to 30 seconds if not set
187
- return {
188
- ...config,
189
- timeout: config.timeout ?? 30,
190
- };
191
- }
192
- setAutoApprovalConfig(autoApproval) {
193
- this.config.autoApproval = autoApproval;
194
- this.saveConfig();
195
- }
196
- setAutoApprovalEnabled(enabled) {
197
- const currentConfig = this.getAutoApprovalConfig();
198
- this.setAutoApprovalConfig({ ...currentConfig, enabled });
199
- }
200
- setAutoApprovalTimeout(timeout) {
201
- const currentConfig = this.getAutoApprovalConfig();
202
- this.setAutoApprovalConfig({ ...currentConfig, timeout });
203
- }
204
- getCommandConfig() {
205
- // For backward compatibility, return the default preset as CommandConfig
206
- const defaultPreset = this.getDefaultPreset();
207
- return {
208
- command: defaultPreset.command,
209
- args: defaultPreset.args,
210
- fallbackArgs: defaultPreset.fallbackArgs,
211
- };
212
- }
213
- setCommandConfig(commandConfig) {
214
- this.config.command = commandConfig;
215
- // Also update the default preset for backward compatibility
216
- if (this.config.commandPresets) {
217
- const defaultPreset = this.config.commandPresets.presets.find(p => p.id === this.config.commandPresets.defaultPresetId);
218
- if (defaultPreset) {
219
- defaultPreset.command = commandConfig.command;
220
- defaultPreset.args = commandConfig.args;
221
- defaultPreset.fallbackArgs = commandConfig.fallbackArgs;
222
- }
223
- }
224
- this.saveConfig();
225
- }
226
- migrateLegacyCommandToPresets() {
227
- // Only migrate if we have legacy command config but no presets
228
- if (this.config.command && !this.config.commandPresets) {
229
- const defaultPreset = {
230
- id: '1',
231
- name: 'Main',
232
- command: this.config.command.command,
233
- args: this.config.command.args,
234
- fallbackArgs: this.config.command.fallbackArgs,
235
- };
236
- this.config.commandPresets = {
237
- presets: [defaultPreset],
238
- defaultPresetId: '1',
239
- };
240
- this.saveConfig();
241
- }
242
- // Ensure default presets if none exist
243
- if (!this.config.commandPresets) {
244
- this.config.commandPresets = {
245
- presets: [
246
- {
247
- id: '1',
248
- name: 'Main',
249
- command: 'claude',
250
- },
251
- ],
252
- defaultPresetId: '1',
253
- };
254
- }
255
- }
256
- getCommandPresets() {
257
- if (!this.config.commandPresets) {
258
- this.migrateLegacyCommandToPresets();
259
- }
260
- return this.config.commandPresets;
261
- }
262
- setCommandPresets(presets) {
263
- this.config.commandPresets = presets;
264
- this.saveConfig();
265
- }
266
- getDefaultPreset() {
267
- const presets = this.getCommandPresets();
268
- const defaultPreset = presets.presets.find(p => p.id === presets.defaultPresetId);
269
- // If default preset not found, return the first one
270
- return defaultPreset || presets.presets[0];
271
- }
272
- getPresetById(id) {
273
- const presets = this.getCommandPresets();
274
- return presets.presets.find(p => p.id === id);
275
- }
276
- addPreset(preset) {
277
- const presets = this.getCommandPresets();
278
- // Replace if exists, otherwise add
279
- const existingIndex = presets.presets.findIndex(p => p.id === preset.id);
280
- if (existingIndex >= 0) {
281
- presets.presets[existingIndex] = preset;
282
- }
283
- else {
284
- presets.presets.push(preset);
285
- }
286
- this.setCommandPresets(presets);
287
- }
288
- deletePreset(id) {
289
- const presets = this.getCommandPresets();
290
- // Don't delete if it's the last preset
291
- if (presets.presets.length <= 1) {
292
- return;
293
- }
294
- // Remove the preset
295
- presets.presets = presets.presets.filter(p => p.id !== id);
296
- // Update default if needed
297
- if (presets.defaultPresetId === id && presets.presets.length > 0) {
298
- presets.defaultPresetId = presets.presets[0].id;
299
- }
300
- this.setCommandPresets(presets);
301
- }
302
- setDefaultPreset(id) {
303
- const presets = this.getCommandPresets();
304
- // Only update if preset exists
305
- if (presets.presets.some(p => p.id === id)) {
306
- presets.defaultPresetId = id;
307
- this.setCommandPresets(presets);
308
- }
309
- }
310
- getSelectPresetOnStart() {
311
- const presets = this.getCommandPresets();
312
- return presets.selectPresetOnStart ?? false;
313
- }
314
- setSelectPresetOnStart(enabled) {
315
- const presets = this.getCommandPresets();
316
- presets.selectPresetOnStart = enabled;
317
- this.setCommandPresets(presets);
318
- }
319
- getWorktreeLastOpened() {
320
- return Object.fromEntries(this.worktreeLastOpened);
321
- }
322
- setWorktreeLastOpened(worktreePath, timestamp) {
323
- this.worktreeLastOpened.set(worktreePath, timestamp);
324
- }
325
- getWorktreeLastOpenedTime(worktreePath) {
326
- return this.worktreeLastOpened.get(worktreePath);
327
- }
328
- // Effect-based methods for type-safe error handling
329
- /**
330
- * Load configuration from file with Effect-based error handling
331
- *
332
- * @returns {Effect.Effect<ConfigurationData, FileSystemError | ConfigError, never>} Configuration data on success, errors on failure
333
- *
334
- * @example
335
- * ```typescript
336
- * const result = await Effect.runPromise(
337
- * configManager.loadConfigEffect()
338
- * );
339
- * ```
340
- */
341
- loadConfigEffect() {
342
- return Effect.try({
343
- try: () => {
344
- // Try to load the new config file
345
- if (existsSync(this.configPath)) {
346
- const configData = readFileSync(this.configPath, 'utf-8');
347
- const parsedConfig = JSON.parse(configData);
348
- return this.applyDefaults(parsedConfig);
349
- }
350
- else {
351
- // If new config doesn't exist, check for legacy shortcuts.json
352
- const migratedConfig = this.migrateLegacyShortcutsSync();
353
- return this.applyDefaults(migratedConfig || {});
354
- }
355
- },
356
- catch: (error) => {
357
- // Determine error type
358
- if (error instanceof SyntaxError) {
359
- return new ConfigError({
360
- configPath: this.configPath,
361
- reason: 'parse',
362
- details: String(error),
363
- });
364
- }
365
- return new FileSystemError({
366
- operation: 'read',
367
- path: this.configPath,
368
- cause: String(error),
369
- });
370
- },
371
- });
372
- }
373
- /**
374
- * Save configuration to file with Effect-based error handling
375
- *
376
- * @returns {Effect.Effect<void, FileSystemError, never>} Void on success, FileSystemError on write failure
377
- *
378
- * @example
379
- * ```typescript
380
- * await Effect.runPromise(
381
- * configManager.saveConfigEffect(config)
382
- * );
383
- * ```
384
- */
385
- saveConfigEffect(config) {
386
- return Effect.try({
387
- try: () => {
388
- this.config = config;
389
- writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
390
- },
391
- catch: (error) => {
392
- return new FileSystemError({
393
- operation: 'write',
394
- path: this.configPath,
395
- cause: String(error),
396
- });
397
- },
398
- });
399
- }
400
- /**
401
- * Validate configuration structure
402
- * Synchronous validation using Either
403
- */
404
- validateConfig(config) {
405
- if (!config || typeof config !== 'object') {
406
- return Either.left(new ValidationError({
407
- field: 'config',
408
- constraint: 'must be a valid configuration object',
409
- receivedValue: config,
410
- }));
411
- }
412
- // Validate shortcuts field if present
413
- const configObj = config;
414
- if (configObj['shortcuts'] !== undefined &&
415
- (typeof configObj['shortcuts'] !== 'object' ||
416
- configObj['shortcuts'] === null)) {
417
- return Either.left(new ValidationError({
418
- field: 'config',
419
- constraint: 'shortcuts must be a valid object',
420
- receivedValue: config,
421
- }));
422
- }
423
- // Additional validation could go here
424
- return Either.right(config);
425
- }
426
- /**
427
- * Get preset by ID with Either-based error handling
428
- * Synchronous lookup using Either
429
- */
430
- getPresetByIdEffect(id) {
431
- const presets = this.getCommandPresets();
432
- const preset = presets.presets.find(p => p.id === id);
433
- if (!preset) {
434
- return Either.left(new ValidationError({
435
- field: 'presetId',
436
- constraint: 'Preset not found',
437
- receivedValue: id,
438
- }));
439
- }
440
- return Either.right(preset);
441
- }
442
- /**
443
- * Set shortcuts with Effect-based error handling
444
- *
445
- * @returns {Effect.Effect<void, FileSystemError, never>} Void on success, FileSystemError on save failure
446
- *
447
- * @example
448
- * ```typescript
449
- * await Effect.runPromise(
450
- * configManager.setShortcutsEffect(shortcuts)
451
- * );
452
- * ```
453
- */
454
- setShortcutsEffect(shortcuts) {
455
- this.config.shortcuts = shortcuts;
456
- return this.saveConfigEffect(this.config);
457
- }
458
- /**
459
- * Set command presets with Effect-based error handling
460
- */
461
- setCommandPresetsEffect(presets) {
462
- this.config.commandPresets = presets;
463
- return this.saveConfigEffect(this.config);
464
- }
465
- /**
466
- * Add or update preset with Effect-based error handling
467
- */
468
- addPresetEffect(preset) {
469
- const presets = this.getCommandPresets();
470
- // Replace if exists, otherwise add
471
- const existingIndex = presets.presets.findIndex(p => p.id === preset.id);
472
- if (existingIndex >= 0) {
473
- presets.presets[existingIndex] = preset;
474
- }
475
- else {
476
- presets.presets.push(preset);
477
- }
478
- return this.setCommandPresetsEffect(presets);
479
- }
480
- /**
481
- * Delete preset with Effect-based error handling
482
- */
483
- deletePresetEffect(id) {
484
- const presets = this.getCommandPresets();
485
- // Don't delete if it's the last preset
486
- if (presets.presets.length <= 1) {
487
- return Effect.fail(new ValidationError({
488
- field: 'presetId',
489
- constraint: 'Cannot delete last preset',
490
- receivedValue: id,
491
- }));
492
- }
493
- // Remove the preset
494
- presets.presets = presets.presets.filter(p => p.id !== id);
495
- // Update default if needed
496
- if (presets.defaultPresetId === id && presets.presets.length > 0) {
497
- presets.defaultPresetId = presets.presets[0].id;
498
- }
499
- return this.setCommandPresetsEffect(presets);
500
- }
501
- /**
502
- * Set default preset with Effect-based error handling
503
- */
504
- setDefaultPresetEffect(id) {
505
- const presets = this.getCommandPresets();
506
- // Only update if preset exists
507
- if (!presets.presets.some(p => p.id === id)) {
508
- return Effect.fail(new ValidationError({
509
- field: 'presetId',
510
- constraint: 'Preset not found',
511
- receivedValue: id,
512
- }));
513
- }
514
- presets.defaultPresetId = id;
515
- return this.setCommandPresetsEffect(presets);
516
- }
517
- // Helper methods
518
- /**
519
- * Apply default values to configuration
520
- */
521
- applyDefaults(config) {
522
- // Ensure default values
523
- if (!config.shortcuts) {
524
- config.shortcuts = DEFAULT_SHORTCUTS;
525
- }
526
- if (!config.statusHooks) {
527
- config.statusHooks = {};
528
- }
529
- if (!config.worktreeHooks) {
530
- config.worktreeHooks = {};
531
- }
532
- if (!config.worktree) {
533
- config.worktree = {
534
- autoDirectory: false,
535
- copySessionData: true,
536
- sortByLastSession: false,
537
- };
538
- }
539
- if (!Object.prototype.hasOwnProperty.call(config.worktree, 'copySessionData')) {
540
- config.worktree.copySessionData = true;
541
- }
542
- if (!Object.prototype.hasOwnProperty.call(config.worktree, 'sortByLastSession')) {
543
- config.worktree.sortByLastSession = false;
544
- }
545
- if (!config.command) {
546
- config.command = {
547
- command: 'claude',
548
- };
549
- }
550
- if (!config.autoApproval) {
551
- config.autoApproval = {
552
- enabled: false,
553
- timeout: 30,
554
- };
555
- }
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
- }
563
- }
564
- return config;
565
- }
566
- /**
567
- * Synchronous legacy shortcuts migration helper
568
- */
569
- migrateLegacyShortcutsSync() {
570
- if (existsSync(this.legacyShortcutsPath)) {
571
- try {
572
- const shortcutsData = readFileSync(this.legacyShortcutsPath, 'utf-8');
573
- const shortcuts = JSON.parse(shortcutsData);
574
- // Validate that it's a valid shortcuts config
575
- if (shortcuts && typeof shortcuts === 'object') {
576
- const config = { shortcuts };
577
- // Save to new config format
578
- this.config = config;
579
- writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
580
- console.log('Migrated shortcuts from legacy shortcuts.json to config.json');
581
- return config;
582
- }
583
- }
584
- catch (error) {
585
- console.error('Failed to migrate legacy shortcuts:', error);
586
- }
587
- }
588
- return null;
589
- }
590
- /**
591
- * Get whether auto-approval is enabled
592
- */
593
- isAutoApprovalEnabled() {
594
- return this.config.autoApproval?.enabled ?? false;
595
- }
596
- }
597
- export const configurationManager = new ConfigurationManager();