@kaitranntt/ccs 4.1.3 → 4.1.5
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/VERSION +1 -1
- package/bin/auth/auth-commands.js +1 -1
- package/bin/ccs.js +69 -54
- package/bin/utils/claude-symlink-manager.js +7 -7
- package/bin/utils/shell-completion.js +34 -12
- package/lib/ccs +203 -63
- package/lib/ccs.ps1 +237 -49
- package/package.json +1 -1
- package/scripts/completion/ccs.bash +8 -2
- package/scripts/completion/ccs.fish +49 -13
- package/scripts/completion/ccs.ps1 +17 -1
- package/scripts/completion/ccs.zsh +52 -10
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.1.
|
|
1
|
+
4.1.5
|
|
@@ -29,7 +29,7 @@ class AuthCommands {
|
|
|
29
29
|
* Show help for auth commands
|
|
30
30
|
*/
|
|
31
31
|
showHelp() {
|
|
32
|
-
console.log(colored('CCS Account Management', 'bold'));
|
|
32
|
+
console.log(colored('CCS Concurrent Account Management', 'bold'));
|
|
33
33
|
console.log('');
|
|
34
34
|
console.log(colored('Usage:', 'cyan'));
|
|
35
35
|
console.log(` ${colored('ccs auth', 'yellow')} <command> [options]`);
|
package/bin/ccs.js
CHANGED
|
@@ -63,45 +63,69 @@ function handleVersionCommand() {
|
|
|
63
63
|
console.log(colored(`CCS (Claude Code Switch) v${CCS_VERSION}`, 'bold'));
|
|
64
64
|
console.log('');
|
|
65
65
|
|
|
66
|
-
// Installation section
|
|
66
|
+
// Installation section with table-like formatting
|
|
67
67
|
console.log(colored('Installation:', 'cyan'));
|
|
68
68
|
|
|
69
69
|
// Location
|
|
70
70
|
const installLocation = process.argv[1] || '(not found)';
|
|
71
|
-
console.log(` ${colored('Location:', 'cyan')} ${installLocation}`);
|
|
71
|
+
console.log(` ${colored('Location:'.padEnd(17), 'cyan')} ${installLocation}`);
|
|
72
|
+
|
|
73
|
+
// .ccs/ directory location
|
|
74
|
+
const ccsDir = path.join(os.homedir(), '.ccs');
|
|
75
|
+
console.log(` ${colored('CCS Directory:'.padEnd(17), 'cyan')} ${ccsDir}`);
|
|
72
76
|
|
|
73
77
|
// Config path
|
|
74
78
|
const configPath = getConfigPath();
|
|
75
|
-
console.log(` ${colored('Config:', 'cyan')} ${configPath}`);
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
console.log(` ${colored('Config:'.padEnd(17), 'cyan')} ${configPath}`);
|
|
80
|
+
|
|
81
|
+
// Profiles.json location
|
|
82
|
+
const profilesJson = path.join(os.homedir(), '.ccs', 'profiles.json');
|
|
83
|
+
console.log(` ${colored('Profiles:'.padEnd(17), 'cyan')} ${profilesJson}`);
|
|
84
|
+
|
|
85
|
+
// Delegation status - check multiple indicators
|
|
86
|
+
const delegationSessionsPath = path.join(os.homedir(), '.ccs', 'delegation-sessions.json');
|
|
87
|
+
const delegationConfigured = fs.existsSync(delegationSessionsPath);
|
|
88
|
+
|
|
89
|
+
let readyProfiles = [];
|
|
90
|
+
|
|
91
|
+
// Check for profiles with valid API keys
|
|
92
|
+
for (const profile of ['glm', 'kimi']) {
|
|
93
|
+
const settingsPath = path.join(os.homedir(), '.ccs', `${profile}.settings.json`);
|
|
94
|
+
if (fs.existsSync(settingsPath)) {
|
|
95
|
+
try {
|
|
96
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
97
|
+
const apiKey = settings.env?.ANTHROPIC_AUTH_TOKEN;
|
|
98
|
+
if (apiKey && !apiKey.match(/YOUR_.*_API_KEY_HERE/) && !apiKey.match(/sk-test.*/)) {
|
|
99
|
+
readyProfiles.push(profile);
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
// Invalid JSON, skip
|
|
92
103
|
}
|
|
93
104
|
}
|
|
105
|
+
}
|
|
94
106
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
107
|
+
const hasValidApiKeys = readyProfiles.length > 0;
|
|
108
|
+
const delegationEnabled = delegationConfigured || hasValidApiKeys;
|
|
109
|
+
|
|
110
|
+
if (delegationEnabled) {
|
|
111
|
+
console.log(` ${colored('Delegation:'.padEnd(17), 'cyan')} Enabled`);
|
|
100
112
|
} else {
|
|
101
|
-
console.log(` ${colored('Delegation:', 'cyan')} Not configured`);
|
|
113
|
+
console.log(` ${colored('Delegation:'.padEnd(17), 'cyan')} Not configured`);
|
|
102
114
|
}
|
|
115
|
+
|
|
103
116
|
console.log('');
|
|
104
117
|
|
|
118
|
+
// Ready Profiles section - make it more prominent
|
|
119
|
+
if (readyProfiles.length > 0) {
|
|
120
|
+
console.log(colored('Delegation Ready:', 'cyan'));
|
|
121
|
+
console.log(` ${colored('✓', 'yellow')} ${readyProfiles.join(', ')} profiles are ready for delegation`);
|
|
122
|
+
console.log('');
|
|
123
|
+
} else if (delegationEnabled) {
|
|
124
|
+
console.log(colored('Delegation Ready:', 'cyan'));
|
|
125
|
+
console.log(` ${colored('!', 'yellow')} Delegation configured but no valid API keys found`);
|
|
126
|
+
console.log('');
|
|
127
|
+
}
|
|
128
|
+
|
|
105
129
|
// Documentation
|
|
106
130
|
console.log(`${colored('Documentation:', 'cyan')} https://github.com/kaitranntt/ccs`);
|
|
107
131
|
console.log(`${colored('License:', 'cyan')} MIT`);
|
|
@@ -126,9 +150,9 @@ function handleHelpCommand() {
|
|
|
126
150
|
|
|
127
151
|
// Description
|
|
128
152
|
console.log(colored('Description:', 'cyan'));
|
|
129
|
-
console.log(' Switch between multiple Claude accounts
|
|
130
|
-
console.log('
|
|
131
|
-
console.log(' auto-recovery. Zero downtime.');
|
|
153
|
+
console.log(' Switch between multiple Claude accounts and alternative models');
|
|
154
|
+
console.log(' (GLM, Kimi) instantly. Run different Claude CLI sessions concurrently');
|
|
155
|
+
console.log(' with auto-recovery. Zero downtime.');
|
|
132
156
|
console.log('');
|
|
133
157
|
|
|
134
158
|
// Model Switching
|
|
@@ -143,31 +167,27 @@ function handleHelpCommand() {
|
|
|
143
167
|
|
|
144
168
|
// Account Management
|
|
145
169
|
console.log(colored('Account Management:', 'cyan'));
|
|
146
|
-
console.log(` ${colored('ccs auth --help', 'yellow')}
|
|
147
|
-
console.log(` ${colored('ccs work', 'yellow')} Switch to work account`);
|
|
148
|
-
console.log(` ${colored('ccs personal', 'yellow')} Switch to personal account`);
|
|
170
|
+
console.log(` ${colored('ccs auth --help', 'yellow')} Run multiple Claude accounts concurrently`);
|
|
149
171
|
console.log('');
|
|
150
172
|
|
|
151
|
-
// Delegation (
|
|
152
|
-
console.log(colored('Delegation (
|
|
153
|
-
console.log(` ${colored('/ccs:glm "task"', 'yellow')} Delegate to GLM-4.6
|
|
173
|
+
// Delegation (inside Claude Code CLI)
|
|
174
|
+
console.log(colored('Delegation (inside Claude Code CLI):', 'cyan'));
|
|
175
|
+
console.log(` ${colored('/ccs:glm "task"', 'yellow')} Delegate to GLM-4.6 for simple tasks`);
|
|
154
176
|
console.log(` ${colored('/ccs:kimi "task"', 'yellow')} Delegate to Kimi for long context`);
|
|
155
|
-
console.log(
|
|
156
|
-
console.log(' Use delegation to save tokens on simple tasks');
|
|
157
|
-
console.log(' Commands work inside Claude Code sessions only');
|
|
177
|
+
console.log(' Save tokens by delegating simple tasks to cost-optimized models');
|
|
158
178
|
console.log('');
|
|
159
179
|
|
|
160
180
|
// Diagnostics
|
|
161
181
|
console.log(colored('Diagnostics:', 'cyan'));
|
|
162
182
|
console.log(` ${colored('ccs doctor', 'yellow')} Run health check and diagnostics`);
|
|
163
|
-
console.log(` ${colored('ccs
|
|
183
|
+
console.log(` ${colored('ccs sync', 'yellow')} Sync delegation commands and skills`);
|
|
164
184
|
console.log('');
|
|
165
185
|
|
|
166
186
|
// Flags
|
|
167
187
|
console.log(colored('Flags:', 'cyan'));
|
|
168
188
|
console.log(` ${colored('-h, --help', 'yellow')} Show this help message`);
|
|
169
189
|
console.log(` ${colored('-v, --version', 'yellow')} Show version and installation info`);
|
|
170
|
-
console.log(` ${colored('--shell-completion', 'yellow')}
|
|
190
|
+
console.log(` ${colored('-sc, --shell-completion', 'yellow')} Install shell auto-completion`);
|
|
171
191
|
console.log('');
|
|
172
192
|
|
|
173
193
|
// Configuration
|
|
@@ -189,15 +209,10 @@ function handleHelpCommand() {
|
|
|
189
209
|
|
|
190
210
|
// Examples
|
|
191
211
|
console.log(colored('Examples:', 'cyan'));
|
|
192
|
-
console.log('
|
|
193
|
-
console.log(`
|
|
194
|
-
console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # Cost-optimized model`);
|
|
195
|
-
console.log('');
|
|
196
|
-
console.log(' Multi-account workflow:');
|
|
197
|
-
console.log(` ${colored('$ ccs auth create work', 'yellow')} # Create work profile`);
|
|
198
|
-
console.log(` ${colored('$ ccs work "review PR"', 'yellow')} # Use work account`);
|
|
212
|
+
console.log(` ${colored('$ ccs', 'yellow')} # Use default account`);
|
|
213
|
+
console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # Cost-optimized model`);
|
|
199
214
|
console.log('');
|
|
200
|
-
console.log(` For more: ${colored('https://github.com/kaitranntt/ccs
|
|
215
|
+
console.log(` For more: ${colored('https://github.com/kaitranntt/ccs/blob/main/README.md', 'cyan')}`);
|
|
201
216
|
console.log('');
|
|
202
217
|
|
|
203
218
|
// Uninstall
|
|
@@ -254,7 +269,7 @@ async function handleDoctorCommand() {
|
|
|
254
269
|
process.exit(doctor.results.isHealthy() ? 0 : 1);
|
|
255
270
|
}
|
|
256
271
|
|
|
257
|
-
async function
|
|
272
|
+
async function handleSyncCommand() {
|
|
258
273
|
// First, copy .claude/ directory from package to ~/.ccs/.claude/
|
|
259
274
|
const ClaudeDirInstaller = require('./utils/claude-dir-installer');
|
|
260
275
|
const installer = new ClaudeDirInstaller();
|
|
@@ -264,8 +279,8 @@ async function handleUpdateCommand() {
|
|
|
264
279
|
const ClaudeSymlinkManager = require('./utils/claude-symlink-manager');
|
|
265
280
|
const manager = new ClaudeSymlinkManager();
|
|
266
281
|
|
|
267
|
-
console.log('[i]
|
|
268
|
-
manager.
|
|
282
|
+
console.log('[i] Syncing delegation commands and skills to ~/.claude/...');
|
|
283
|
+
manager.sync();
|
|
269
284
|
|
|
270
285
|
process.exit(0);
|
|
271
286
|
}
|
|
@@ -496,7 +511,7 @@ async function main() {
|
|
|
496
511
|
}
|
|
497
512
|
|
|
498
513
|
// Special case: shell completion installer
|
|
499
|
-
if (firstArg === '--shell-completion') {
|
|
514
|
+
if (firstArg === '--shell-completion' || firstArg === '-sc') {
|
|
500
515
|
await handleShellCompletionCommand(args.slice(1));
|
|
501
516
|
return;
|
|
502
517
|
}
|
|
@@ -507,9 +522,9 @@ async function main() {
|
|
|
507
522
|
return;
|
|
508
523
|
}
|
|
509
524
|
|
|
510
|
-
// Special case:
|
|
511
|
-
if (firstArg === '
|
|
512
|
-
await
|
|
525
|
+
// Special case: sync command (sync delegation commands and skills to ~/.claude/)
|
|
526
|
+
if (firstArg === 'sync' || firstArg === '--sync') {
|
|
527
|
+
await handleSyncCommand();
|
|
513
528
|
return;
|
|
514
529
|
}
|
|
515
530
|
|
|
@@ -53,7 +53,7 @@ class ClaudeSymlinkManager {
|
|
|
53
53
|
this._installItem(item);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
console.log('[OK]
|
|
56
|
+
console.log('[OK] Delegation commands and skills installed to ~/.claude/');
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
@@ -178,9 +178,9 @@ class ClaudeSymlinkManager {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
if (removed > 0) {
|
|
181
|
-
console.log(`[OK] Removed ${removed}
|
|
181
|
+
console.log(`[OK] Removed ${removed} delegation commands and skills from ~/.claude/`);
|
|
182
182
|
} else {
|
|
183
|
-
console.log('[i] No
|
|
183
|
+
console.log('[i] No delegation commands or skills to remove');
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -226,11 +226,11 @@ class ClaudeSymlinkManager {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
/**
|
|
229
|
-
*
|
|
230
|
-
* Same as install() but with explicit
|
|
229
|
+
* Sync delegation commands and skills to ~/.claude/ (used by 'ccs sync' command)
|
|
230
|
+
* Same as install() but with explicit sync message
|
|
231
231
|
*/
|
|
232
|
-
|
|
233
|
-
console.log('[i]
|
|
232
|
+
sync() {
|
|
233
|
+
console.log('[i] Syncing delegation commands and skills to ~/.claude/...');
|
|
234
234
|
this.install();
|
|
235
235
|
}
|
|
236
236
|
}
|
|
@@ -52,6 +52,34 @@ class ShellCompletionInstaller {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Safely create directory, checking for file conflicts
|
|
57
|
+
* @param {string} dirPath - Path to create
|
|
58
|
+
* @throws {Error} If path exists but is a file
|
|
59
|
+
*/
|
|
60
|
+
ensureDirectory(dirPath) {
|
|
61
|
+
if (fs.existsSync(dirPath)) {
|
|
62
|
+
const stat = fs.statSync(dirPath);
|
|
63
|
+
if (!stat.isDirectory()) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Cannot create directory: ${dirPath} exists but is a file.\n` +
|
|
66
|
+
`Please remove or rename this file and try again.`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
// Directory exists, nothing to do
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check parent directories recursively
|
|
74
|
+
const parentDir = path.dirname(dirPath);
|
|
75
|
+
if (parentDir !== dirPath) {
|
|
76
|
+
this.ensureDirectory(parentDir);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Create the directory
|
|
80
|
+
fs.mkdirSync(dirPath);
|
|
81
|
+
}
|
|
82
|
+
|
|
55
83
|
/**
|
|
56
84
|
* Install bash completion
|
|
57
85
|
*/
|
|
@@ -97,10 +125,8 @@ class ShellCompletionInstaller {
|
|
|
97
125
|
throw new Error('Completion file not found. Please reinstall CCS.');
|
|
98
126
|
}
|
|
99
127
|
|
|
100
|
-
// Create zsh completion directory
|
|
101
|
-
|
|
102
|
-
fs.mkdirSync(zshCompDir, { recursive: true });
|
|
103
|
-
}
|
|
128
|
+
// Create zsh completion directory (with file conflict checking)
|
|
129
|
+
this.ensureDirectory(zshCompDir);
|
|
104
130
|
|
|
105
131
|
// Copy to zsh completion directory
|
|
106
132
|
const destFile = path.join(zshCompDir, '_ccs');
|
|
@@ -142,10 +168,8 @@ class ShellCompletionInstaller {
|
|
|
142
168
|
throw new Error('Completion file not found. Please reinstall CCS.');
|
|
143
169
|
}
|
|
144
170
|
|
|
145
|
-
// Create fish completion directory
|
|
146
|
-
|
|
147
|
-
fs.mkdirSync(fishCompDir, { recursive: true });
|
|
148
|
-
}
|
|
171
|
+
// Create fish completion directory (with file conflict checking)
|
|
172
|
+
this.ensureDirectory(fishCompDir);
|
|
149
173
|
|
|
150
174
|
// Copy to fish completion directory (fish auto-loads from here)
|
|
151
175
|
const destFile = path.join(fishCompDir, 'ccs.fish');
|
|
@@ -178,11 +202,9 @@ class ShellCompletionInstaller {
|
|
|
178
202
|
const sourceCmd = `. "${completionPath.replace(/\\/g, '\\\\')}"`;
|
|
179
203
|
const block = `\n${marker}\n${sourceCmd}\n`;
|
|
180
204
|
|
|
181
|
-
// Create profile directory if needed
|
|
205
|
+
// Create profile directory if needed (with file conflict checking)
|
|
182
206
|
const profileDir = path.dirname(profilePath);
|
|
183
|
-
|
|
184
|
-
fs.mkdirSync(profileDir, { recursive: true });
|
|
185
|
-
}
|
|
207
|
+
this.ensureDirectory(profileDir);
|
|
186
208
|
|
|
187
209
|
// Check if already installed
|
|
188
210
|
if (fs.existsSync(profilePath)) {
|