@kaitranntt/ccs 2.5.1 → 3.0.1

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.
@@ -0,0 +1,405 @@
1
+ 'use strict';
2
+
3
+ const { spawn } = require('child_process');
4
+ const ProfileRegistry = require('./profile-registry');
5
+ const InstanceManager = require('./instance-manager');
6
+ const { colored } = require('./helpers');
7
+ const { detectClaudeCli } = require('./claude-detector');
8
+
9
+ /**
10
+ * Auth Commands (Simplified)
11
+ *
12
+ * CLI interface for CCS multi-account management.
13
+ * Commands: create, list, show, remove, default
14
+ *
15
+ * Login-per-profile model: Each profile is an isolated Claude instance.
16
+ * Users login directly in each instance (no credential copying).
17
+ */
18
+ class AuthCommands {
19
+ constructor() {
20
+ this.registry = new ProfileRegistry();
21
+ this.instanceMgr = new InstanceManager();
22
+ }
23
+
24
+ /**
25
+ * Show help for auth commands
26
+ */
27
+ showHelp() {
28
+ console.log(colored('CCS Account Management', 'bold'));
29
+ console.log('');
30
+ console.log(colored('Usage:', 'cyan'));
31
+ console.log(` ${colored('ccs auth', 'yellow')} <command> [options]`);
32
+ console.log('');
33
+ console.log(colored('Commands:', 'cyan'));
34
+ console.log(` ${colored('create <profile>', 'yellow')} Create new profile and login`);
35
+ console.log(` ${colored('list', 'yellow')} List all saved profiles`);
36
+ console.log(` ${colored('show <profile>', 'yellow')} Show profile details`);
37
+ console.log(` ${colored('remove <profile>', 'yellow')} Remove saved profile`);
38
+ console.log(` ${colored('default <profile>', 'yellow')} Set default profile`);
39
+ console.log('');
40
+ console.log(colored('Examples:', 'cyan'));
41
+ console.log(` ${colored('ccs auth create work', 'yellow')} # Create & login to work profile`);
42
+ console.log(` ${colored('ccs auth list', 'yellow')} # List all profiles`);
43
+ console.log(` ${colored('ccs work "review code"', 'yellow')} # Use work profile`);
44
+ console.log('');
45
+ console.log(colored('Options:', 'cyan'));
46
+ console.log(` ${colored('--force', 'yellow')} Allow overwriting existing profile`);
47
+ console.log('');
48
+ }
49
+
50
+ /**
51
+ * Create new profile and prompt for login
52
+ * @param {Array} args - Command arguments
53
+ */
54
+ async handleCreate(args) {
55
+ const profileName = args.find(arg => !arg.startsWith('--'));
56
+ const force = args.includes('--force');
57
+
58
+ if (!profileName) {
59
+ console.error('[X] Profile name is required');
60
+ console.log('');
61
+ console.log(`Usage: ${colored('ccs auth create <profile> [--force]', 'yellow')}`);
62
+ console.log('');
63
+ console.log('Example:');
64
+ console.log(` ${colored('ccs auth create work', 'yellow')}`);
65
+ process.exit(1);
66
+ }
67
+
68
+ // Check if profile already exists
69
+ if (!force && this.registry.hasProfile(profileName)) {
70
+ console.error(`[X] Profile already exists: ${profileName}`);
71
+ console.log(` Use ${colored('--force', 'yellow')} to overwrite`);
72
+ process.exit(1);
73
+ }
74
+
75
+ try {
76
+ // Create instance directory
77
+ console.log(`[i] Creating profile: ${profileName}`);
78
+ const instancePath = this.instanceMgr.ensureInstance(profileName);
79
+
80
+ // Create/update profile entry
81
+ if (this.registry.hasProfile(profileName)) {
82
+ this.registry.updateProfile(profileName, {
83
+ type: 'account'
84
+ });
85
+ } else {
86
+ this.registry.createProfile(profileName, {
87
+ type: 'account'
88
+ });
89
+ }
90
+
91
+ console.log(`[i] Instance directory: ${instancePath}`);
92
+ console.log('');
93
+ console.log(colored('[i] Starting Claude in isolated instance...', 'yellow'));
94
+ console.log(colored('[i] You will be prompted to login with your account.', 'yellow'));
95
+ console.log('');
96
+
97
+ // Detect Claude CLI
98
+ const claudeCli = detectClaudeCli();
99
+ if (!claudeCli) {
100
+ console.error('[X] Claude CLI not found');
101
+ console.log('');
102
+ console.log('Please install Claude CLI first:');
103
+ console.log(' https://claude.ai/download');
104
+ process.exit(1);
105
+ }
106
+
107
+ // Execute Claude in isolated instance (will auto-prompt for login if no credentials)
108
+ const child = spawn(claudeCli, [], {
109
+ stdio: 'inherit',
110
+ env: { ...process.env, CLAUDE_CONFIG_DIR: instancePath }
111
+ });
112
+
113
+ child.on('exit', (code) => {
114
+ if (code === 0) {
115
+ console.log('');
116
+ console.log(colored('[OK] Profile created successfully', 'green'));
117
+ console.log('');
118
+ console.log(` Profile: ${profileName}`);
119
+ console.log(` Instance: ${instancePath}`);
120
+ console.log('');
121
+ console.log('Usage:');
122
+ console.log(` ${colored(`ccs ${profileName} "your prompt here"`, 'yellow')}`);
123
+ console.log('');
124
+ process.exit(0);
125
+ } else {
126
+ console.log('');
127
+ console.error('[X] Login failed or cancelled');
128
+ console.log('');
129
+ console.log('To retry:');
130
+ console.log(` ${colored(`ccs auth create ${profileName} --force`, 'yellow')}`);
131
+ console.log('');
132
+ process.exit(1);
133
+ }
134
+ });
135
+
136
+ child.on('error', (err) => {
137
+ console.error(`[X] Failed to execute Claude CLI: ${err.message}`);
138
+ process.exit(1);
139
+ });
140
+
141
+ } catch (error) {
142
+ console.error(`[X] Failed to create profile: ${error.message}`);
143
+ process.exit(1);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * List all saved profiles
149
+ * @param {Array} args - Command arguments
150
+ */
151
+ async handleList(args) {
152
+ const verbose = args.includes('--verbose');
153
+
154
+ try {
155
+ const profiles = this.registry.getAllProfiles();
156
+ const defaultProfile = this.registry.getDefaultProfile();
157
+ const profileNames = Object.keys(profiles);
158
+
159
+ if (profileNames.length === 0) {
160
+ console.log(colored('No account profiles found', 'yellow'));
161
+ console.log('');
162
+ console.log('To create your first profile:');
163
+ console.log(` ${colored('ccs auth create <profile>', 'yellow')} # Create and login to profile`);
164
+ console.log('');
165
+ console.log('Example:');
166
+ console.log(` ${colored('ccs auth create work', 'yellow')}`);
167
+ console.log('');
168
+ return;
169
+ }
170
+
171
+ console.log(colored('Saved Account Profiles:', 'bold'));
172
+ console.log('');
173
+
174
+ // Sort by last_used (descending), then alphabetically
175
+ const sorted = profileNames.sort((a, b) => {
176
+ const aProfile = profiles[a];
177
+ const bProfile = profiles[b];
178
+
179
+ // Default first
180
+ if (a === defaultProfile) return -1;
181
+ if (b === defaultProfile) return 1;
182
+
183
+ // Then by last_used
184
+ if (aProfile.last_used && bProfile.last_used) {
185
+ return new Date(bProfile.last_used) - new Date(aProfile.last_used);
186
+ }
187
+ if (aProfile.last_used) return -1;
188
+ if (bProfile.last_used) return 1;
189
+
190
+ // Then alphabetically
191
+ return a.localeCompare(b);
192
+ });
193
+
194
+ sorted.forEach(name => {
195
+ const profile = profiles[name];
196
+ const isDefault = name === defaultProfile;
197
+ const indicator = isDefault ? colored('[*]', 'green') : '[ ]';
198
+
199
+ console.log(`${indicator} ${colored(name, 'cyan')}${isDefault ? colored(' (default)', 'green') : ''}`);
200
+
201
+ console.log(` Type: ${profile.type || 'account'}`);
202
+
203
+ if (verbose) {
204
+ console.log(` Created: ${new Date(profile.created).toLocaleString()}`);
205
+ if (profile.last_used) {
206
+ console.log(` Last used: ${new Date(profile.last_used).toLocaleString()}`);
207
+ }
208
+ }
209
+
210
+ console.log('');
211
+ });
212
+
213
+ console.log(`Total profiles: ${profileNames.length}`);
214
+ console.log('');
215
+
216
+ } catch (error) {
217
+ console.error(`[X] Failed to list profiles: ${error.message}`);
218
+ process.exit(1);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Show details for a specific profile
224
+ * @param {Array} args - Command arguments
225
+ */
226
+ async handleShow(args) {
227
+ const profileName = args.find(arg => !arg.startsWith('--'));
228
+
229
+ if (!profileName) {
230
+ console.error('[X] Profile name is required');
231
+ console.log('');
232
+ console.log(`Usage: ${colored('ccs auth show <profile>', 'yellow')}`);
233
+ process.exit(1);
234
+ }
235
+
236
+ try {
237
+ const profile = this.registry.getProfile(profileName);
238
+ const defaultProfile = this.registry.getDefaultProfile();
239
+ const isDefault = profileName === defaultProfile;
240
+
241
+ console.log(colored(`Profile: ${profileName}`, 'bold'));
242
+ console.log('');
243
+ console.log(` Type: ${profile.type || 'account'}`);
244
+ console.log(` Default: ${isDefault ? 'Yes' : 'No'}`);
245
+ console.log(` Instance: ${this.instanceMgr.getInstancePath(profileName)}`);
246
+ console.log(` Created: ${new Date(profile.created).toLocaleString()}`);
247
+
248
+ if (profile.last_used) {
249
+ console.log(` Last used: ${new Date(profile.last_used).toLocaleString()}`);
250
+ } else {
251
+ console.log(` Last used: Never`);
252
+ }
253
+
254
+ console.log('');
255
+
256
+ } catch (error) {
257
+ console.error(`[X] ${error.message}`);
258
+ process.exit(1);
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Remove a saved profile
264
+ * @param {Array} args - Command arguments
265
+ */
266
+ async handleRemove(args) {
267
+ const profileName = args.find(arg => !arg.startsWith('--'));
268
+ const force = args.includes('--force');
269
+
270
+ if (!profileName) {
271
+ console.error('[X] Profile name is required');
272
+ console.log('');
273
+ console.log(`Usage: ${colored('ccs auth remove <profile> [--force]', 'yellow')}`);
274
+ process.exit(1);
275
+ }
276
+
277
+ if (!this.registry.hasProfile(profileName)) {
278
+ console.error(`[X] Profile not found: ${profileName}`);
279
+ process.exit(1);
280
+ }
281
+
282
+ // Require --force for safety
283
+ if (!force) {
284
+ console.error('[X] Removal requires --force flag for safety');
285
+ console.log('');
286
+ console.log(`Run: ${colored(`ccs auth remove ${profileName} --force`, 'yellow')}`);
287
+ process.exit(1);
288
+ }
289
+
290
+ try {
291
+ // Delete instance
292
+ this.instanceMgr.deleteInstance(profileName);
293
+
294
+ // Delete profile
295
+ this.registry.deleteProfile(profileName);
296
+
297
+ console.log(colored('[OK] Profile removed successfully', 'green'));
298
+ console.log(` Profile: ${profileName}`);
299
+ console.log('');
300
+
301
+ } catch (error) {
302
+ console.error(`[X] Failed to remove profile: ${error.message}`);
303
+ process.exit(1);
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Set default profile
309
+ * @param {Array} args - Command arguments
310
+ */
311
+ async handleDefault(args) {
312
+ const profileName = args.find(arg => !arg.startsWith('--'));
313
+
314
+ if (!profileName) {
315
+ console.error('[X] Profile name is required');
316
+ console.log('');
317
+ console.log(`Usage: ${colored('ccs auth default <profile>', 'yellow')}`);
318
+ process.exit(1);
319
+ }
320
+
321
+ try {
322
+ this.registry.setDefaultProfile(profileName);
323
+
324
+ console.log(colored('[OK] Default profile set', 'green'));
325
+ console.log(` Profile: ${profileName}`);
326
+ console.log('');
327
+ console.log('Now you can use:');
328
+ console.log(` ${colored('ccs "your prompt"', 'yellow')} # Uses ${profileName} profile`);
329
+ console.log('');
330
+
331
+ } catch (error) {
332
+ console.error(`[X] ${error.message}`);
333
+ process.exit(1);
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Route auth command to appropriate handler
339
+ * @param {Array} args - Command arguments
340
+ */
341
+ async route(args) {
342
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
343
+ this.showHelp();
344
+ return;
345
+ }
346
+
347
+ const command = args[0];
348
+ const commandArgs = args.slice(1);
349
+
350
+ switch (command) {
351
+ case 'create':
352
+ await this.handleCreate(commandArgs);
353
+ break;
354
+
355
+ case 'save':
356
+ // Deprecated - redirect to create
357
+ console.log(colored('[!] Command "save" is deprecated', 'yellow'));
358
+ console.log(` Use: ${colored('ccs auth create <profile>', 'yellow')} instead`);
359
+ console.log('');
360
+ await this.handleCreate(commandArgs);
361
+ break;
362
+
363
+ case 'list':
364
+ await this.handleList(commandArgs);
365
+ break;
366
+
367
+ case 'show':
368
+ await this.handleShow(commandArgs);
369
+ break;
370
+
371
+ case 'remove':
372
+ await this.handleRemove(commandArgs);
373
+ break;
374
+
375
+ case 'default':
376
+ await this.handleDefault(commandArgs);
377
+ break;
378
+
379
+ case 'current':
380
+ console.log(colored('[!] Command "current" has been removed', 'yellow'));
381
+ console.log('');
382
+ console.log('Each profile has its own login in an isolated instance.');
383
+ console.log('Use "ccs auth list" to see all profiles.');
384
+ console.log('');
385
+ break;
386
+
387
+ case 'cleanup':
388
+ console.log(colored('[!] Command "cleanup" has been removed', 'yellow'));
389
+ console.log('');
390
+ console.log('No cleanup needed - no separate vault files.');
391
+ console.log('Use "ccs auth list" to see all profiles.');
392
+ console.log('');
393
+ break;
394
+
395
+ default:
396
+ console.error(`[X] Unknown command: ${command}`);
397
+ console.log('');
398
+ console.log('Run for help:');
399
+ console.log(` ${colored('ccs auth --help', 'yellow')}`);
400
+ process.exit(1);
401
+ }
402
+ }
403
+ }
404
+
405
+ module.exports = AuthCommands;
package/bin/ccs.js CHANGED
@@ -4,9 +4,12 @@
4
4
  const { spawn } = require('child_process');
5
5
  const path = require('path');
6
6
  const fs = require('fs');
7
+ const os = require('os');
7
8
  const { error, colored } = require('./helpers');
8
9
  const { detectClaudeCli, showClaudeNotFoundError } = require('./claude-detector');
9
10
  const { getSettingsPath, getConfigPath } = require('./config-manager');
11
+ const { ErrorManager } = require('./error-manager');
12
+ const RecoveryManager = require('./recovery-manager');
10
13
 
11
14
  // Version (sync with package.json)
12
15
  const CCS_VERSION = require('../package.json').version;
@@ -18,10 +21,13 @@ function escapeShellArg(arg) {
18
21
  }
19
22
 
20
23
  // Execute Claude CLI with unified spawn logic
21
- function execClaude(claudeCli, args) {
24
+ function execClaude(claudeCli, args, envVars = null) {
22
25
  const isWindows = process.platform === 'win32';
23
26
  const needsShell = isWindows && /\.(cmd|bat|ps1)$/i.test(claudeCli);
24
27
 
28
+ // Prepare environment (merge with process.env if envVars provided)
29
+ const env = envVars ? { ...process.env, ...envVars } : process.env;
30
+
25
31
  let child;
26
32
  if (needsShell) {
27
33
  // When shell needed: concatenate into string to avoid DEP0190 warning
@@ -29,13 +35,15 @@ function execClaude(claudeCli, args) {
29
35
  child = spawn(cmdString, {
30
36
  stdio: 'inherit',
31
37
  windowsHide: true,
32
- shell: true
38
+ shell: true,
39
+ env
33
40
  });
34
41
  } else {
35
42
  // When no shell needed: use array form (faster, no shell overhead)
36
43
  child = spawn(claudeCli, args, {
37
44
  stdio: 'inherit',
38
- windowsHide: true
45
+ windowsHide: true,
46
+ env
39
47
  });
40
48
  }
41
49
 
@@ -91,19 +99,34 @@ function handleHelpCommand() {
91
99
 
92
100
  // Description
93
101
  console.log(colored('Description:', 'cyan'));
94
- console.log(' Switch between Claude models instantly. Stop hitting rate limits.');
95
- console.log(' Maps profile names to Claude settings files via ~/.ccs/config.json');
102
+ console.log(' Switch between multiple Claude accounts (work, personal, team) and');
103
+ console.log(' alternative models (GLM, Kimi) instantly. Concurrent sessions with');
104
+ console.log(' auto-recovery. Zero downtime.');
105
+ console.log('');
106
+
107
+ // Model Switching
108
+ console.log(colored('Model Switching:', 'cyan'));
109
+ console.log(` ${colored('ccs', 'yellow')} Use default Claude account`);
110
+ console.log(` ${colored('ccs glm', 'yellow')} Switch to GLM 4.6 model`);
111
+ console.log(` ${colored('ccs kimi', 'yellow')} Switch to Kimi for Coding`);
112
+ console.log(` ${colored('ccs glm', 'yellow')} "debug this code" Use GLM and run command`);
96
113
  console.log('');
97
114
 
98
- // Profile Switching
99
- console.log(colored('Profile Switching:', 'cyan'));
100
- console.log(` ${colored('ccs', 'yellow')} Use default profile`);
101
- console.log(` ${colored('ccs glm', 'yellow')} Switch to GLM profile`);
102
- console.log(` ${colored('ccs kimi', 'yellow')} Switch to Kimi profile`);
103
- console.log(` ${colored('ccs glm', 'yellow')} "debug this code" Switch to GLM and run command`);
104
- console.log(` ${colored('ccs kimi', 'yellow')} "write tests" Switch to Kimi and run command`);
105
- console.log(` ${colored('ccs glm', 'yellow')} --verbose Switch to GLM with Claude flags`);
106
- console.log(` ${colored('ccs kimi', 'yellow')} --verbose Switch to Kimi with Claude flags`);
115
+ // Account Management
116
+ console.log(colored('Account Management:', 'cyan'));
117
+ console.log(` ${colored('ccs auth create <profile>', 'yellow')} Create new profile and login`);
118
+ console.log(` ${colored('ccs auth list', 'yellow')} List all saved profiles`);
119
+ console.log(` ${colored('ccs auth show <profile>', 'yellow')} Show profile details`);
120
+ console.log(` ${colored('ccs auth remove <profile>', 'yellow')} Remove profile (requires --force)`);
121
+ console.log(` ${colored('ccs auth default <profile>', 'yellow')} Set default profile`);
122
+ console.log(` ${colored('ccs work', 'yellow')} Switch to work account`);
123
+ console.log(` ${colored('ccs personal', 'yellow')} Switch to personal account`);
124
+ console.log(` ${colored('ccs work', 'yellow')} "review code" Run command with work account`);
125
+ console.log('');
126
+
127
+ // Diagnostics
128
+ console.log(colored('Diagnostics:', 'cyan'));
129
+ console.log(` ${colored('ccs doctor', 'yellow')} Run health check and diagnostics`);
107
130
  console.log('');
108
131
 
109
132
  // Flags
@@ -183,6 +206,16 @@ function handleUninstallCommand() {
183
206
  process.exit(0);
184
207
  }
185
208
 
209
+ async function handleDoctorCommand() {
210
+ const Doctor = require('./doctor');
211
+ const doctor = new Doctor();
212
+
213
+ await doctor.runAllChecks();
214
+
215
+ // Exit with error code if unhealthy
216
+ process.exit(doctor.results.isHealthy() ? 0 : 1);
217
+ }
218
+
186
219
  // Smart profile detection
187
220
  function detectProfile(args) {
188
221
  if (args.length === 0 || args[0].startsWith('-')) {
@@ -195,7 +228,7 @@ function detectProfile(args) {
195
228
  }
196
229
 
197
230
  // Main execution
198
- function main() {
231
+ async function main() {
199
232
  const args = process.argv.slice(2);
200
233
 
201
234
  // Special case: version command (check BEFORE profile detection)
@@ -222,36 +255,81 @@ function main() {
222
255
  return;
223
256
  }
224
257
 
225
- // Detect profile
226
- const { profile, remainingArgs } = detectProfile(args);
227
-
228
- // Special case: "default" profile just runs claude directly
229
- if (profile === 'default') {
230
- const claudeCli = detectClaudeCli();
231
- if (!claudeCli) {
232
- showClaudeNotFoundError();
233
- process.exit(1);
234
- }
258
+ // Special case: doctor command
259
+ if (firstArg === 'doctor' || firstArg === '--doctor') {
260
+ await handleDoctorCommand();
261
+ return;
262
+ }
235
263
 
236
- execClaude(claudeCli, remainingArgs);
264
+ // Special case: auth command (multi-account management)
265
+ if (firstArg === 'auth') {
266
+ const AuthCommands = require('./auth-commands');
267
+ const authCommands = new AuthCommands();
268
+ await authCommands.route(args.slice(1));
237
269
  return;
238
270
  }
239
271
 
240
- // Get settings path for profile
241
- const settingsPath = getSettingsPath(profile);
272
+ // Auto-recovery for missing configuration
273
+ const recovery = new RecoveryManager();
274
+ const recovered = recovery.recoverAll();
242
275
 
243
- // Detect Claude CLI
244
- const claudeCli = detectClaudeCli();
276
+ if (recovered) {
277
+ recovery.showRecoveryHints();
278
+ }
245
279
 
246
- // Check if claude was found
280
+ // Detect profile
281
+ const { profile, remainingArgs } = detectProfile(args);
282
+
283
+ // Detect Claude CLI first (needed for all paths)
284
+ const claudeCli = detectClaudeCli();
247
285
  if (!claudeCli) {
248
- showClaudeNotFoundError();
286
+ ErrorManager.showClaudeNotFound();
249
287
  process.exit(1);
250
288
  }
251
289
 
252
- // Execute claude with --settings
253
- execClaude(claudeCli, ['--settings', settingsPath, ...remainingArgs]);
290
+ // Use ProfileDetector to determine profile type
291
+ const ProfileDetector = require('./profile-detector');
292
+ const InstanceManager = require('./instance-manager');
293
+ const ProfileRegistry = require('./profile-registry');
294
+ const { getSettingsPath } = require('./config-manager');
295
+
296
+ const detector = new ProfileDetector();
297
+
298
+ try {
299
+ const profileInfo = detector.detectProfileType(profile);
300
+
301
+ if (profileInfo.type === 'settings') {
302
+ // EXISTING FLOW: Settings-based profile (glm, kimi)
303
+ // Use --settings flag (backward compatible)
304
+ const expandedSettingsPath = getSettingsPath(profileInfo.name);
305
+ execClaude(claudeCli, ['--settings', expandedSettingsPath, ...remainingArgs]);
306
+ } else if (profileInfo.type === 'account') {
307
+ // NEW FLOW: Account-based profile (work, personal)
308
+ // All platforms: Use instance isolation with CLAUDE_CONFIG_DIR
309
+ const registry = new ProfileRegistry();
310
+ const instanceMgr = new InstanceManager();
311
+
312
+ // Ensure instance exists (lazy init if needed)
313
+ const instancePath = instanceMgr.ensureInstance(profileInfo.name);
314
+
315
+ // Update last_used timestamp
316
+ registry.touchProfile(profileInfo.name);
317
+
318
+ // Execute Claude with instance isolation
319
+ const envVars = { CLAUDE_CONFIG_DIR: instancePath };
320
+ execClaude(claudeCli, remainingArgs, envVars);
321
+ } else {
322
+ // DEFAULT: No profile configured, use Claude's own defaults
323
+ execClaude(claudeCli, remainingArgs);
324
+ }
325
+ } catch (error) {
326
+ console.error(`[X] ${error.message}`);
327
+ process.exit(1);
328
+ }
254
329
  }
255
330
 
256
331
  // Run main
257
- main();
332
+ main().catch(error => {
333
+ console.error('Fatal error:', error.message);
334
+ process.exit(1);
335
+ });