@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 CHANGED
@@ -1 +1 @@
1
- 4.1.3
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
- // Delegation status
78
- const delegationRulesPath = path.join(os.homedir(), '.ccs', 'delegation-rules.json');
79
- const delegationEnabled = fs.existsSync(delegationRulesPath);
80
-
81
- if (delegationEnabled) {
82
- console.log(` ${colored('Delegation:', 'cyan')} Enabled`);
83
-
84
- // Check which profiles are delegation-ready
85
- const readyProfiles = [];
86
- const { DelegationValidator } = require('./utils/delegation-validator');
87
-
88
- for (const profile of ['glm', 'kimi']) {
89
- const validation = DelegationValidator.validate(profile);
90
- if (validation.valid) {
91
- readyProfiles.push(profile);
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
- if (readyProfiles.length > 0) {
96
- console.log(` ${colored('Ready:', 'cyan')} ${readyProfiles.join(', ')}`);
97
- } else {
98
- console.log(` ${colored('Ready:', 'cyan')} None (configure profiles first)`);
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 (work, personal, team) and');
130
- console.log(' alternative models (GLM, Kimi) instantly. Concurrent sessions with');
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')} Manage multiple Claude accounts`);
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 (NEW)
152
- console.log(colored('Delegation (Token Optimization):', 'cyan'));
153
- console.log(` ${colored('/ccs:glm "task"', 'yellow')} Delegate to GLM-4.6 within Claude session`);
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(` ${colored('/ccs:create m2', 'yellow')} Create custom delegation command`);
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 update', 'yellow')} Re-install CCS items to ~/.claude/`);
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')} Install shell auto-completion`);
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(' Quick start:');
193
- console.log(` ${colored('$ ccs', 'yellow')} # Use default account`);
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#usage', 'cyan')}`);
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 handleUpdateCommand() {
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] Updating CCS items in ~/.claude/...');
268
- manager.update();
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: update command (re-install CCS symlinks)
511
- if (firstArg === 'update' || firstArg === '--update') {
512
- await handleUpdateCommand();
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] CCS items installed to ~/.claude/');
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} CCS items from ~/.claude/`);
181
+ console.log(`[OK] Removed ${removed} delegation commands and skills from ~/.claude/`);
182
182
  } else {
183
- console.log('[i] No CCS items to remove');
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
- * Re-install symlinks (used by 'ccs update' command)
230
- * Same as install() but with explicit re-installation message
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
- update() {
233
- console.log('[i] Updating CCS items in ~/.claude/...');
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
- if (!fs.existsSync(zshCompDir)) {
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
- if (!fs.existsSync(fishCompDir)) {
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
- if (!fs.existsSync(profileDir)) {
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)) {