@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.
- package/README.ja.md +325 -0
- package/README.md +149 -102
- package/README.vi.md +147 -94
- package/VERSION +1 -1
- package/bin/auth-commands.js +405 -0
- package/bin/ccs.js +113 -35
- package/bin/config-manager.js +36 -7
- package/bin/doctor.js +365 -0
- package/bin/error-manager.js +159 -0
- package/bin/instance-manager.js +218 -0
- package/bin/profile-detector.js +199 -0
- package/bin/profile-registry.js +226 -0
- package/bin/recovery-manager.js +135 -0
- package/lib/ccs +856 -301
- package/lib/ccs.ps1 +792 -122
- package/package.json +1 -1
- package/scripts/postinstall.js +111 -12
|
@@ -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
|
|
95
|
-
console.log('
|
|
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
|
-
//
|
|
99
|
-
console.log(colored('
|
|
100
|
-
console.log(` ${colored('ccs', 'yellow')}
|
|
101
|
-
console.log(` ${colored('ccs
|
|
102
|
-
console.log(` ${colored('ccs
|
|
103
|
-
console.log(` ${colored('ccs
|
|
104
|
-
console.log(` ${colored('ccs
|
|
105
|
-
console.log(` ${colored('ccs
|
|
106
|
-
console.log(` ${colored('ccs
|
|
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
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
241
|
-
const
|
|
272
|
+
// Auto-recovery for missing configuration
|
|
273
|
+
const recovery = new RecoveryManager();
|
|
274
|
+
const recovered = recovery.recoverAll();
|
|
242
275
|
|
|
243
|
-
|
|
244
|
-
|
|
276
|
+
if (recovered) {
|
|
277
|
+
recovery.showRecoveryHints();
|
|
278
|
+
}
|
|
245
279
|
|
|
246
|
-
//
|
|
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
|
-
|
|
286
|
+
ErrorManager.showClaudeNotFound();
|
|
249
287
|
process.exit(1);
|
|
250
288
|
}
|
|
251
289
|
|
|
252
|
-
//
|
|
253
|
-
|
|
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
|
+
});
|