@kaitranntt/ccs 4.1.6 → 4.2.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.
- package/VERSION +1 -1
- package/bin/ccs.js +165 -0
- package/bin/utils/update-checker.js +243 -0
- package/lib/ccs +126 -1
- package/lib/ccs.ps1 +134 -1
- package/package.json +1 -1
- package/scripts/completion/ccs.bash +1 -1
- package/scripts/completion/ccs.fish +10 -9
- package/scripts/completion/ccs.ps1 +1 -1
- package/scripts/completion/ccs.zsh +2 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.2.0
|
package/bin/ccs.js
CHANGED
|
@@ -181,6 +181,7 @@ function handleHelpCommand() {
|
|
|
181
181
|
console.log(colored('Diagnostics:', 'cyan'));
|
|
182
182
|
console.log(` ${colored('ccs doctor', 'yellow')} Run health check and diagnostics`);
|
|
183
183
|
console.log(` ${colored('ccs sync', 'yellow')} Sync delegation commands and skills`);
|
|
184
|
+
console.log(` ${colored('ccs update', 'yellow')} Update CCS to latest version`);
|
|
184
185
|
console.log('');
|
|
185
186
|
|
|
186
187
|
// Flags
|
|
@@ -285,6 +286,164 @@ async function handleSyncCommand() {
|
|
|
285
286
|
process.exit(0);
|
|
286
287
|
}
|
|
287
288
|
|
|
289
|
+
async function handleUpdateCommand() {
|
|
290
|
+
const { checkForUpdates } = require('./utils/update-checker');
|
|
291
|
+
const { spawn } = require('child_process');
|
|
292
|
+
|
|
293
|
+
console.log('');
|
|
294
|
+
console.log(colored('Checking for updates...', 'cyan'));
|
|
295
|
+
console.log('');
|
|
296
|
+
|
|
297
|
+
// Detect installation method for proper update source
|
|
298
|
+
const isNpmInstall = process.argv[1].includes('node_modules');
|
|
299
|
+
const installMethod = isNpmInstall ? 'npm' : 'direct';
|
|
300
|
+
|
|
301
|
+
// Check for updates (force check)
|
|
302
|
+
const updateResult = await checkForUpdates(CCS_VERSION, true, installMethod);
|
|
303
|
+
|
|
304
|
+
if (updateResult.status === 'check_failed') {
|
|
305
|
+
console.log(colored(`[X] ${updateResult.message}`, 'red'));
|
|
306
|
+
console.log('');
|
|
307
|
+
console.log(colored('[i] Possible causes:', 'yellow'));
|
|
308
|
+
console.log(' • Network connection issues');
|
|
309
|
+
console.log(' • Firewall blocking requests');
|
|
310
|
+
console.log(' • GitHub/npm API temporarily unavailable');
|
|
311
|
+
console.log('');
|
|
312
|
+
console.log('Try again later or update manually:');
|
|
313
|
+
if (isNpmInstall) {
|
|
314
|
+
console.log(colored(' npm install -g @kaitranntt/ccs@latest', 'yellow'));
|
|
315
|
+
} else {
|
|
316
|
+
const isWindows = process.platform === 'win32';
|
|
317
|
+
if (isWindows) {
|
|
318
|
+
console.log(colored(' irm ccs.kaitran.ca/install | iex', 'yellow'));
|
|
319
|
+
} else {
|
|
320
|
+
console.log(colored(' curl -fsSL ccs.kaitran.ca/install | bash', 'yellow'));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
console.log('');
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (updateResult.status === 'no_update') {
|
|
328
|
+
let message = `You are already on the latest version (${CCS_VERSION})`;
|
|
329
|
+
|
|
330
|
+
// Add context for why no update is shown
|
|
331
|
+
switch (updateResult.reason) {
|
|
332
|
+
case 'dismissed':
|
|
333
|
+
message = `Update dismissed. You are on version ${CCS_VERSION}`;
|
|
334
|
+
console.log(colored(`[i] ${message}`, 'yellow'));
|
|
335
|
+
break;
|
|
336
|
+
case 'cached':
|
|
337
|
+
message = `No updates available (cached result). You are on version ${CCS_VERSION}`;
|
|
338
|
+
console.log(colored(`[i] ${message}`, 'cyan'));
|
|
339
|
+
break;
|
|
340
|
+
default:
|
|
341
|
+
console.log(colored(`[OK] ${message}`, 'green'));
|
|
342
|
+
}
|
|
343
|
+
console.log('');
|
|
344
|
+
process.exit(0);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Update available
|
|
348
|
+
console.log(colored(`[i] Update available: ${updateResult.current} → ${updateResult.latest}`, 'yellow'));
|
|
349
|
+
console.log('');
|
|
350
|
+
|
|
351
|
+
if (isNpmInstall) {
|
|
352
|
+
// npm installation - use npm update
|
|
353
|
+
console.log(colored('Updating via npm...', 'cyan'));
|
|
354
|
+
console.log('');
|
|
355
|
+
|
|
356
|
+
const child = spawn('npm', ['install', '-g', '@kaitranntt/ccs@latest'], {
|
|
357
|
+
stdio: 'inherit',
|
|
358
|
+
shell: true
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
child.on('exit', (code) => {
|
|
362
|
+
if (code === 0) {
|
|
363
|
+
console.log('');
|
|
364
|
+
console.log(colored('[OK] Update successful!', 'green'));
|
|
365
|
+
console.log('');
|
|
366
|
+
console.log(`Run ${colored('ccs --version', 'yellow')} to verify`);
|
|
367
|
+
console.log('');
|
|
368
|
+
} else {
|
|
369
|
+
console.log('');
|
|
370
|
+
console.log(colored('[X] Update failed', 'red'));
|
|
371
|
+
console.log('');
|
|
372
|
+
console.log('Try manually:');
|
|
373
|
+
console.log(colored(' npm install -g @kaitranntt/ccs@latest', 'yellow'));
|
|
374
|
+
console.log('');
|
|
375
|
+
}
|
|
376
|
+
process.exit(code || 0);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
child.on('error', (err) => {
|
|
380
|
+
console.log('');
|
|
381
|
+
console.log(colored('[X] Failed to run npm update', 'red'));
|
|
382
|
+
console.log('');
|
|
383
|
+
console.log('Try manually:');
|
|
384
|
+
console.log(colored(' npm install -g @kaitranntt/ccs@latest', 'yellow'));
|
|
385
|
+
console.log('');
|
|
386
|
+
process.exit(1);
|
|
387
|
+
});
|
|
388
|
+
} else {
|
|
389
|
+
// Direct installation - re-run installer
|
|
390
|
+
console.log(colored('Updating via installer...', 'cyan'));
|
|
391
|
+
console.log('');
|
|
392
|
+
|
|
393
|
+
const isWindows = process.platform === 'win32';
|
|
394
|
+
let command, args;
|
|
395
|
+
|
|
396
|
+
if (isWindows) {
|
|
397
|
+
command = 'powershell.exe';
|
|
398
|
+
args = ['-Command', 'irm ccs.kaitran.ca/install | iex'];
|
|
399
|
+
} else {
|
|
400
|
+
command = 'bash';
|
|
401
|
+
args = ['-c', 'curl -fsSL ccs.kaitran.ca/install | bash'];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const child = spawn(command, args, {
|
|
405
|
+
stdio: 'inherit',
|
|
406
|
+
shell: true
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
child.on('exit', (code) => {
|
|
410
|
+
if (code === 0) {
|
|
411
|
+
console.log('');
|
|
412
|
+
console.log(colored('[OK] Update successful!', 'green'));
|
|
413
|
+
console.log('');
|
|
414
|
+
console.log(`Run ${colored('ccs --version', 'yellow')} to verify`);
|
|
415
|
+
console.log('');
|
|
416
|
+
} else {
|
|
417
|
+
console.log('');
|
|
418
|
+
console.log(colored('[X] Update failed', 'red'));
|
|
419
|
+
console.log('');
|
|
420
|
+
console.log('Try manually:');
|
|
421
|
+
if (isWindows) {
|
|
422
|
+
console.log(colored(' irm ccs.kaitran.ca/install | iex', 'yellow'));
|
|
423
|
+
} else {
|
|
424
|
+
console.log(colored(' curl -fsSL ccs.kaitran.ca/install | bash', 'yellow'));
|
|
425
|
+
}
|
|
426
|
+
console.log('');
|
|
427
|
+
}
|
|
428
|
+
process.exit(code || 0);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
child.on('error', (err) => {
|
|
432
|
+
console.log('');
|
|
433
|
+
console.log(colored('[X] Failed to run installer', 'red'));
|
|
434
|
+
console.log('');
|
|
435
|
+
console.log('Try manually:');
|
|
436
|
+
if (isWindows) {
|
|
437
|
+
console.log(colored(' irm ccs.kaitran.ca/install | iex', 'yellow'));
|
|
438
|
+
} else {
|
|
439
|
+
console.log(colored(' curl -fsSL ccs.kaitran.ca/install | bash', 'yellow'));
|
|
440
|
+
}
|
|
441
|
+
console.log('');
|
|
442
|
+
process.exit(1);
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
288
447
|
// Smart profile detection
|
|
289
448
|
function detectProfile(args) {
|
|
290
449
|
if (args.length === 0 || args[0].startsWith('-')) {
|
|
@@ -528,6 +687,12 @@ async function main() {
|
|
|
528
687
|
return;
|
|
529
688
|
}
|
|
530
689
|
|
|
690
|
+
// Special case: update command (update CCS to latest version)
|
|
691
|
+
if (firstArg === 'update' || firstArg === '--update') {
|
|
692
|
+
await handleUpdateCommand();
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
|
|
531
696
|
// Special case: auth command (multi-account management)
|
|
532
697
|
if (firstArg === 'auth') {
|
|
533
698
|
const AuthCommands = require('./auth/auth-commands');
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const { colored } = require('./helpers');
|
|
8
|
+
|
|
9
|
+
const UPDATE_CHECK_FILE = path.join(os.homedir(), '.ccs', 'update-check.json');
|
|
10
|
+
const CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
|
|
11
|
+
const GITHUB_API_URL = 'https://api.github.com/repos/kaitranntt/ccs/releases/latest';
|
|
12
|
+
const NPM_REGISTRY_URL = 'https://registry.npmjs.org/@kaitranntt/ccs/latest';
|
|
13
|
+
const REQUEST_TIMEOUT = 5000; // 5 seconds
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Compare semantic versions
|
|
17
|
+
* @param {string} v1 - First version (e.g., "4.1.6")
|
|
18
|
+
* @param {string} v2 - Second version
|
|
19
|
+
* @returns {number} - 1 if v1 > v2, -1 if v1 < v2, 0 if equal
|
|
20
|
+
*/
|
|
21
|
+
function compareVersions(v1, v2) {
|
|
22
|
+
const parts1 = v1.replace(/^v/, '').split('.').map(Number);
|
|
23
|
+
const parts2 = v2.replace(/^v/, '').split('.').map(Number);
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < 3; i++) {
|
|
26
|
+
const p1 = parts1[i] || 0;
|
|
27
|
+
const p2 = parts2[i] || 0;
|
|
28
|
+
if (p1 > p2) return 1;
|
|
29
|
+
if (p1 < p2) return -1;
|
|
30
|
+
}
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fetch latest version from GitHub releases
|
|
36
|
+
* @returns {Promise<string|null>} - Latest version or null on error
|
|
37
|
+
*/
|
|
38
|
+
function fetchLatestVersionFromGitHub() {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const req = https.get(GITHUB_API_URL, {
|
|
41
|
+
headers: { 'User-Agent': 'CCS-Update-Checker' },
|
|
42
|
+
timeout: REQUEST_TIMEOUT
|
|
43
|
+
}, (res) => {
|
|
44
|
+
let data = '';
|
|
45
|
+
|
|
46
|
+
res.on('data', (chunk) => {
|
|
47
|
+
data += chunk;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
res.on('end', () => {
|
|
51
|
+
try {
|
|
52
|
+
if (res.statusCode !== 200) {
|
|
53
|
+
resolve(null);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const release = JSON.parse(data);
|
|
58
|
+
const version = release.tag_name?.replace(/^v/, '') || null;
|
|
59
|
+
resolve(version);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
resolve(null);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
req.on('error', () => resolve(null));
|
|
67
|
+
req.on('timeout', () => {
|
|
68
|
+
req.destroy();
|
|
69
|
+
resolve(null);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Fetch latest version from npm registry
|
|
76
|
+
* @returns {Promise<string|null>} - Latest version or null on error
|
|
77
|
+
*/
|
|
78
|
+
function fetchLatestVersionFromNpm() {
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
const req = https.get(NPM_REGISTRY_URL, {
|
|
81
|
+
headers: { 'User-Agent': 'CCS-Update-Checker' },
|
|
82
|
+
timeout: REQUEST_TIMEOUT
|
|
83
|
+
}, (res) => {
|
|
84
|
+
let data = '';
|
|
85
|
+
|
|
86
|
+
res.on('data', (chunk) => {
|
|
87
|
+
data += chunk;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
res.on('end', () => {
|
|
91
|
+
try {
|
|
92
|
+
if (res.statusCode !== 200) {
|
|
93
|
+
resolve(null);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const packageData = JSON.parse(data);
|
|
98
|
+
const version = packageData.version || null;
|
|
99
|
+
resolve(version);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
resolve(null);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
req.on('error', () => resolve(null));
|
|
107
|
+
req.on('timeout', () => {
|
|
108
|
+
req.destroy();
|
|
109
|
+
resolve(null);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Read update check cache
|
|
116
|
+
* @returns {Object} - Cache object
|
|
117
|
+
*/
|
|
118
|
+
function readCache() {
|
|
119
|
+
try {
|
|
120
|
+
if (!fs.existsSync(UPDATE_CHECK_FILE)) {
|
|
121
|
+
return { last_check: 0, latest_version: null, dismissed_version: null };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const data = fs.readFileSync(UPDATE_CHECK_FILE, 'utf8');
|
|
125
|
+
return JSON.parse(data);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
return { last_check: 0, latest_version: null, dismissed_version: null };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Write update check cache
|
|
133
|
+
* @param {Object} cache - Cache object to write
|
|
134
|
+
*/
|
|
135
|
+
function writeCache(cache) {
|
|
136
|
+
try {
|
|
137
|
+
const ccsDir = path.join(os.homedir(), '.ccs');
|
|
138
|
+
if (!fs.existsSync(ccsDir)) {
|
|
139
|
+
fs.mkdirSync(ccsDir, { recursive: true, mode: 0o700 });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fs.writeFileSync(UPDATE_CHECK_FILE, JSON.stringify(cache, null, 2), 'utf8');
|
|
143
|
+
} catch (err) {
|
|
144
|
+
// Silently fail - not critical
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Check for updates (async, non-blocking)
|
|
150
|
+
* @param {string} currentVersion - Current CCS version
|
|
151
|
+
* @param {boolean} force - Force check even if within interval
|
|
152
|
+
* @param {string} installMethod - Installation method ('npm' or 'direct')
|
|
153
|
+
* @returns {Promise<Object>} - Update result object with status and data
|
|
154
|
+
*/
|
|
155
|
+
async function checkForUpdates(currentVersion, force = false, installMethod = 'direct') {
|
|
156
|
+
const cache = readCache();
|
|
157
|
+
const now = Date.now();
|
|
158
|
+
|
|
159
|
+
// Check if we should check for updates
|
|
160
|
+
if (!force && (now - cache.last_check < CHECK_INTERVAL)) {
|
|
161
|
+
// Use cached result if available
|
|
162
|
+
if (cache.latest_version && compareVersions(cache.latest_version, currentVersion) > 0) {
|
|
163
|
+
// Don't show if user dismissed this version
|
|
164
|
+
if (cache.dismissed_version === cache.latest_version) {
|
|
165
|
+
return { status: 'no_update', reason: 'dismissed' };
|
|
166
|
+
}
|
|
167
|
+
return { status: 'update_available', latest: cache.latest_version, current: currentVersion };
|
|
168
|
+
}
|
|
169
|
+
return { status: 'no_update', reason: 'cached' };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Fetch latest version from appropriate source
|
|
173
|
+
let latestVersion;
|
|
174
|
+
let fetchError = null;
|
|
175
|
+
|
|
176
|
+
if (installMethod === 'npm') {
|
|
177
|
+
latestVersion = await fetchLatestVersionFromNpm();
|
|
178
|
+
if (!latestVersion) fetchError = 'npm_registry_error';
|
|
179
|
+
} else {
|
|
180
|
+
latestVersion = await fetchLatestVersionFromGitHub();
|
|
181
|
+
if (!latestVersion) fetchError = 'github_api_error';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Update cache
|
|
185
|
+
cache.last_check = now;
|
|
186
|
+
if (latestVersion) {
|
|
187
|
+
cache.latest_version = latestVersion;
|
|
188
|
+
}
|
|
189
|
+
writeCache(cache);
|
|
190
|
+
|
|
191
|
+
// Handle fetch errors
|
|
192
|
+
if (fetchError) {
|
|
193
|
+
return {
|
|
194
|
+
status: 'check_failed',
|
|
195
|
+
reason: fetchError,
|
|
196
|
+
message: `Failed to check for updates: ${fetchError.replace(/_/g, ' ')}`
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Check if update available
|
|
201
|
+
if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
|
|
202
|
+
// Don't show if user dismissed this version
|
|
203
|
+
if (cache.dismissed_version === latestVersion) {
|
|
204
|
+
return { status: 'no_update', reason: 'dismissed' };
|
|
205
|
+
}
|
|
206
|
+
return { status: 'update_available', latest: latestVersion, current: currentVersion };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { status: 'no_update', reason: 'latest' };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Show update notification
|
|
214
|
+
* @param {Object} updateInfo - Update information
|
|
215
|
+
*/
|
|
216
|
+
function showUpdateNotification(updateInfo) {
|
|
217
|
+
console.log('');
|
|
218
|
+
console.log(colored('═══════════════════════════════════════════════════════', 'cyan'));
|
|
219
|
+
console.log(colored(` Update available: ${updateInfo.current} → ${updateInfo.latest}`, 'yellow'));
|
|
220
|
+
console.log(colored('═══════════════════════════════════════════════════════', 'cyan'));
|
|
221
|
+
console.log('');
|
|
222
|
+
console.log(` Run ${colored('ccs update', 'yellow')} to update`);
|
|
223
|
+
console.log('');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Dismiss update notification for a specific version
|
|
228
|
+
* @param {string} version - Version to dismiss
|
|
229
|
+
*/
|
|
230
|
+
function dismissUpdate(version) {
|
|
231
|
+
const cache = readCache();
|
|
232
|
+
cache.dismissed_version = version;
|
|
233
|
+
writeCache(cache);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = {
|
|
237
|
+
compareVersions,
|
|
238
|
+
checkForUpdates,
|
|
239
|
+
showUpdateNotification,
|
|
240
|
+
dismissUpdate,
|
|
241
|
+
readCache,
|
|
242
|
+
writeCache
|
|
243
|
+
};
|
package/lib/ccs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
# Version (updated by scripts/bump-version.sh)
|
|
5
|
-
CCS_VERSION="4.
|
|
5
|
+
CCS_VERSION="4.2.0"
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
7
|
readonly CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs/config.json}"
|
|
8
8
|
readonly PROFILES_JSON="$HOME/.ccs/profiles.json"
|
|
@@ -199,6 +199,7 @@ show_help() {
|
|
|
199
199
|
echo -e "${CYAN}Diagnostics:${RESET}"
|
|
200
200
|
echo -e " ${YELLOW}ccs doctor${RESET} Run health check and diagnostics"
|
|
201
201
|
echo -e " ${YELLOW}ccs sync${RESET} Sync delegation commands and skills"
|
|
202
|
+
echo -e " ${YELLOW}ccs update${RESET} Update CCS to latest version"
|
|
202
203
|
echo ""
|
|
203
204
|
|
|
204
205
|
echo -e "${CYAN}Flags:${RESET}"
|
|
@@ -591,6 +592,124 @@ sync_run() {
|
|
|
591
592
|
echo ""
|
|
592
593
|
}
|
|
593
594
|
|
|
595
|
+
# --- Update Command ---
|
|
596
|
+
|
|
597
|
+
update_run() {
|
|
598
|
+
echo ""
|
|
599
|
+
echo -e "${CYAN}Checking for updates...${RESET}"
|
|
600
|
+
echo ""
|
|
601
|
+
|
|
602
|
+
# Detect installation method
|
|
603
|
+
local install_method="direct"
|
|
604
|
+
if command -v npm &>/dev/null && npm list -g @kaitranntt/ccs &>/dev/null 2>&1; then
|
|
605
|
+
install_method="npm"
|
|
606
|
+
fi
|
|
607
|
+
|
|
608
|
+
# Fetch latest version from appropriate source
|
|
609
|
+
local latest_version=""
|
|
610
|
+
if command -v curl &>/dev/null; then
|
|
611
|
+
if [[ "$install_method" == "npm" ]]; then
|
|
612
|
+
# Check npm registry for npm installations
|
|
613
|
+
latest_version=$(curl -fsSL https://registry.npmjs.org/@kaitranntt/ccs/latest 2>/dev/null | \
|
|
614
|
+
grep '"version"' | head -1 | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([0-9.]+)".*/\1/')
|
|
615
|
+
else
|
|
616
|
+
# Check GitHub releases for direct installations
|
|
617
|
+
latest_version=$(curl -fsSL https://api.github.com/repos/kaitranntt/ccs/releases/latest 2>/dev/null | \
|
|
618
|
+
grep '"tag_name"' | sed -E 's/.*"v?([0-9.]+)".*/\1/')
|
|
619
|
+
fi
|
|
620
|
+
fi
|
|
621
|
+
|
|
622
|
+
if [[ -z "$latest_version" ]]; then
|
|
623
|
+
echo -e "${YELLOW}[!] Unable to check for updates${RESET}"
|
|
624
|
+
echo ""
|
|
625
|
+
echo "Try manually:"
|
|
626
|
+
if [[ "$install_method" == "npm" ]]; then
|
|
627
|
+
echo -e " ${YELLOW}npm install -g @kaitranntt/ccs@latest${RESET}"
|
|
628
|
+
else
|
|
629
|
+
echo -e " ${YELLOW}curl -fsSL ccs.kaitran.ca/install | bash${RESET}"
|
|
630
|
+
fi
|
|
631
|
+
echo ""
|
|
632
|
+
exit 1
|
|
633
|
+
fi
|
|
634
|
+
|
|
635
|
+
# Compare versions
|
|
636
|
+
if [[ "$latest_version" == "$CCS_VERSION" ]]; then
|
|
637
|
+
echo -e "${GREEN}[OK] You are already on the latest version (${CCS_VERSION})${RESET}"
|
|
638
|
+
echo ""
|
|
639
|
+
exit 0
|
|
640
|
+
fi
|
|
641
|
+
|
|
642
|
+
# Check if update available
|
|
643
|
+
local current_major=$(echo "$CCS_VERSION" | cut -d. -f1)
|
|
644
|
+
local current_minor=$(echo "$CCS_VERSION" | cut -d. -f2)
|
|
645
|
+
local current_patch=$(echo "$CCS_VERSION" | cut -d. -f3)
|
|
646
|
+
|
|
647
|
+
local latest_major=$(echo "$latest_version" | cut -d. -f1)
|
|
648
|
+
local latest_minor=$(echo "$latest_version" | cut -d. -f2)
|
|
649
|
+
local latest_patch=$(echo "$latest_version" | cut -d. -f3)
|
|
650
|
+
|
|
651
|
+
local is_newer=0
|
|
652
|
+
if [[ $latest_major -gt $current_major ]]; then
|
|
653
|
+
is_newer=1
|
|
654
|
+
elif [[ $latest_major -eq $current_major ]] && [[ $latest_minor -gt $current_minor ]]; then
|
|
655
|
+
is_newer=1
|
|
656
|
+
elif [[ $latest_major -eq $current_major ]] && [[ $latest_minor -eq $current_minor ]] && [[ $latest_patch -gt $current_patch ]]; then
|
|
657
|
+
is_newer=1
|
|
658
|
+
fi
|
|
659
|
+
|
|
660
|
+
if [[ $is_newer -eq 0 ]]; then
|
|
661
|
+
echo -e "${GREEN}[OK] You are on version ${CCS_VERSION} (latest is ${latest_version})${RESET}"
|
|
662
|
+
echo ""
|
|
663
|
+
exit 0
|
|
664
|
+
fi
|
|
665
|
+
|
|
666
|
+
echo -e "${YELLOW}[i] Update available: ${CCS_VERSION} → ${latest_version}${RESET}"
|
|
667
|
+
echo ""
|
|
668
|
+
|
|
669
|
+
# Perform update based on installation method
|
|
670
|
+
if [[ "$install_method" == "npm" ]]; then
|
|
671
|
+
echo -e "${CYAN}Updating via npm...${RESET}"
|
|
672
|
+
echo ""
|
|
673
|
+
|
|
674
|
+
if npm install -g @kaitranntt/ccs@latest; then
|
|
675
|
+
echo ""
|
|
676
|
+
echo -e "${GREEN}[OK] Update successful!${RESET}"
|
|
677
|
+
echo ""
|
|
678
|
+
echo -e "Run ${YELLOW}ccs --version${RESET} to verify"
|
|
679
|
+
echo ""
|
|
680
|
+
exit 0
|
|
681
|
+
else
|
|
682
|
+
echo ""
|
|
683
|
+
echo -e "${RED}[X] Update failed${RESET}"
|
|
684
|
+
echo ""
|
|
685
|
+
echo "Try manually:"
|
|
686
|
+
echo -e " ${YELLOW}npm install -g @kaitranntt/ccs@latest${RESET}"
|
|
687
|
+
echo ""
|
|
688
|
+
exit 1
|
|
689
|
+
fi
|
|
690
|
+
else
|
|
691
|
+
echo -e "${CYAN}Updating via installer...${RESET}"
|
|
692
|
+
echo ""
|
|
693
|
+
|
|
694
|
+
if curl -fsSL ccs.kaitran.ca/install | bash; then
|
|
695
|
+
echo ""
|
|
696
|
+
echo -e "${GREEN}[OK] Update successful!${RESET}"
|
|
697
|
+
echo ""
|
|
698
|
+
echo -e "Run ${YELLOW}ccs --version${RESET} to verify"
|
|
699
|
+
echo ""
|
|
700
|
+
exit 0
|
|
701
|
+
else
|
|
702
|
+
echo ""
|
|
703
|
+
echo -e "${RED}[X] Update failed${RESET}"
|
|
704
|
+
echo ""
|
|
705
|
+
echo "Try manually:"
|
|
706
|
+
echo -e " ${YELLOW}curl -fsSL ccs.kaitran.ca/install | bash${RESET}"
|
|
707
|
+
echo ""
|
|
708
|
+
exit 1
|
|
709
|
+
fi
|
|
710
|
+
fi
|
|
711
|
+
}
|
|
712
|
+
|
|
594
713
|
# --- Claude CLI Detection Logic ---
|
|
595
714
|
|
|
596
715
|
detect_claude_cli() {
|
|
@@ -1669,6 +1788,12 @@ if [[ $# -gt 0 ]] && [[ "${1}" == "sync" || "${1}" == "--sync" ]]; then
|
|
|
1669
1788
|
exit $?
|
|
1670
1789
|
fi
|
|
1671
1790
|
|
|
1791
|
+
# Special case: update command
|
|
1792
|
+
if [[ $# -gt 0 ]] && [[ "${1}" == "update" || "${1}" == "--update" ]]; then
|
|
1793
|
+
update_run
|
|
1794
|
+
exit $?
|
|
1795
|
+
fi
|
|
1796
|
+
|
|
1672
1797
|
# Run auto-recovery before main logic
|
|
1673
1798
|
auto_recover || {
|
|
1674
1799
|
msg_error "Auto-recovery failed. Check permissions."
|
package/lib/ccs.ps1
CHANGED
|
@@ -12,7 +12,7 @@ param(
|
|
|
12
12
|
$ErrorActionPreference = "Stop"
|
|
13
13
|
|
|
14
14
|
# Version (updated by scripts/bump-version.sh)
|
|
15
|
-
$CcsVersion = "4.
|
|
15
|
+
$CcsVersion = "4.2.0"
|
|
16
16
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
17
17
|
$ConfigFile = if ($env:CCS_CONFIG) { $env:CCS_CONFIG } else { "$env:USERPROFILE\.ccs\config.json" }
|
|
18
18
|
$ProfilesJson = "$env:USERPROFILE\.ccs\profiles.json"
|
|
@@ -246,6 +246,7 @@ function Show-Help {
|
|
|
246
246
|
Write-ColorLine "Diagnostics:" "Cyan"
|
|
247
247
|
Write-ColorLine " ccs doctor Run health check and diagnostics" "Yellow"
|
|
248
248
|
Write-ColorLine " ccs sync Sync delegation commands and skills" "Yellow"
|
|
249
|
+
Write-ColorLine " ccs update Update CCS to latest version" "Yellow"
|
|
249
250
|
Write-Host ""
|
|
250
251
|
|
|
251
252
|
Write-ColorLine "Flags:" "Cyan"
|
|
@@ -1031,6 +1032,132 @@ function Sync-Run {
|
|
|
1031
1032
|
Write-Host ""
|
|
1032
1033
|
}
|
|
1033
1034
|
|
|
1035
|
+
# --- Update Command ---
|
|
1036
|
+
|
|
1037
|
+
function Update-Run {
|
|
1038
|
+
Write-Host ""
|
|
1039
|
+
Write-Host "Checking for updates..." -ForegroundColor Cyan
|
|
1040
|
+
Write-Host ""
|
|
1041
|
+
|
|
1042
|
+
# Detect installation method
|
|
1043
|
+
$InstallMethod = "direct"
|
|
1044
|
+
try {
|
|
1045
|
+
$NpmList = npm list -g @kaitranntt/ccs 2>&1
|
|
1046
|
+
if ($LASTEXITCODE -eq 0) {
|
|
1047
|
+
$InstallMethod = "npm"
|
|
1048
|
+
}
|
|
1049
|
+
} catch {
|
|
1050
|
+
# npm not available or not installed via npm
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
# Fetch latest version from appropriate source
|
|
1054
|
+
$LatestVersion = ""
|
|
1055
|
+
try {
|
|
1056
|
+
if ($InstallMethod -eq "npm") {
|
|
1057
|
+
# Check npm registry for npm installations
|
|
1058
|
+
$Response = Invoke-RestMethod -Uri "https://registry.npmjs.org/@kaitranntt/ccs/latest" -TimeoutSec 5
|
|
1059
|
+
$LatestVersion = $Response.version
|
|
1060
|
+
} else {
|
|
1061
|
+
# Check GitHub releases for direct installations
|
|
1062
|
+
$Response = Invoke-RestMethod -Uri "https://api.github.com/repos/kaitranntt/ccs/releases/latest" -TimeoutSec 5
|
|
1063
|
+
$LatestVersion = $Response.tag_name -replace '^v', ''
|
|
1064
|
+
}
|
|
1065
|
+
} catch {
|
|
1066
|
+
Write-Host "[!] Unable to check for updates" -ForegroundColor Yellow
|
|
1067
|
+
Write-Host ""
|
|
1068
|
+
Write-Host "Try manually:"
|
|
1069
|
+
if ($InstallMethod -eq "npm") {
|
|
1070
|
+
Write-Host " npm install -g @kaitranntt/ccs@latest" -ForegroundColor Yellow
|
|
1071
|
+
} else {
|
|
1072
|
+
Write-Host " irm ccs.kaitran.ca/install | iex" -ForegroundColor Yellow
|
|
1073
|
+
}
|
|
1074
|
+
Write-Host ""
|
|
1075
|
+
exit 1
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
# Compare versions
|
|
1079
|
+
if ($LatestVersion -eq $CcsVersion) {
|
|
1080
|
+
Write-Host "[OK] You are already on the latest version ($CcsVersion)" -ForegroundColor Green
|
|
1081
|
+
Write-Host ""
|
|
1082
|
+
exit 0
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
# Check if update available
|
|
1086
|
+
$CurrentParts = $CcsVersion.Split('.')
|
|
1087
|
+
$LatestParts = $LatestVersion.Split('.')
|
|
1088
|
+
|
|
1089
|
+
$IsNewer = $false
|
|
1090
|
+
for ($i = 0; $i -lt 3; $i++) {
|
|
1091
|
+
$Current = [int]$CurrentParts[$i]
|
|
1092
|
+
$Latest = [int]$LatestParts[$i]
|
|
1093
|
+
|
|
1094
|
+
if ($Latest -gt $Current) {
|
|
1095
|
+
$IsNewer = $true
|
|
1096
|
+
break
|
|
1097
|
+
} elseif ($Latest -lt $Current) {
|
|
1098
|
+
break
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
if (-not $IsNewer) {
|
|
1103
|
+
Write-Host "[OK] You are on version $CcsVersion (latest is $LatestVersion)" -ForegroundColor Green
|
|
1104
|
+
Write-Host ""
|
|
1105
|
+
exit 0
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
Write-Host "[i] Update available: $CcsVersion → $LatestVersion" -ForegroundColor Yellow
|
|
1109
|
+
Write-Host ""
|
|
1110
|
+
|
|
1111
|
+
# Perform update based on installation method
|
|
1112
|
+
if ($InstallMethod -eq "npm") {
|
|
1113
|
+
Write-Host "Updating via npm..." -ForegroundColor Cyan
|
|
1114
|
+
Write-Host ""
|
|
1115
|
+
|
|
1116
|
+
try {
|
|
1117
|
+
npm install -g @kaitranntt/ccs@latest
|
|
1118
|
+
if ($LASTEXITCODE -eq 0) {
|
|
1119
|
+
Write-Host ""
|
|
1120
|
+
Write-Host "[OK] Update successful!" -ForegroundColor Green
|
|
1121
|
+
Write-Host ""
|
|
1122
|
+
Write-Host "Run ccs --version to verify" -ForegroundColor Yellow
|
|
1123
|
+
Write-Host ""
|
|
1124
|
+
exit 0
|
|
1125
|
+
} else {
|
|
1126
|
+
throw "npm install failed"
|
|
1127
|
+
}
|
|
1128
|
+
} catch {
|
|
1129
|
+
Write-Host ""
|
|
1130
|
+
Write-Host "[X] Update failed" -ForegroundColor Red
|
|
1131
|
+
Write-Host ""
|
|
1132
|
+
Write-Host "Try manually:"
|
|
1133
|
+
Write-Host " npm install -g @kaitranntt/ccs@latest" -ForegroundColor Yellow
|
|
1134
|
+
Write-Host ""
|
|
1135
|
+
exit 1
|
|
1136
|
+
}
|
|
1137
|
+
} else {
|
|
1138
|
+
Write-Host "Updating via installer..." -ForegroundColor Cyan
|
|
1139
|
+
Write-Host ""
|
|
1140
|
+
|
|
1141
|
+
try {
|
|
1142
|
+
irm ccs.kaitran.ca/install | iex
|
|
1143
|
+
Write-Host ""
|
|
1144
|
+
Write-Host "[OK] Update successful!" -ForegroundColor Green
|
|
1145
|
+
Write-Host ""
|
|
1146
|
+
Write-Host "Run ccs --version to verify" -ForegroundColor Yellow
|
|
1147
|
+
Write-Host ""
|
|
1148
|
+
exit 0
|
|
1149
|
+
} catch {
|
|
1150
|
+
Write-Host ""
|
|
1151
|
+
Write-Host "[X] Update failed" -ForegroundColor Red
|
|
1152
|
+
Write-Host ""
|
|
1153
|
+
Write-Host "Try manually:"
|
|
1154
|
+
Write-Host " irm ccs.kaitran.ca/install | iex" -ForegroundColor Yellow
|
|
1155
|
+
Write-Host ""
|
|
1156
|
+
exit 1
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1034
1161
|
# --- Auth Commands (Phase 3) ---
|
|
1035
1162
|
|
|
1036
1163
|
function Show-AuthHelp {
|
|
@@ -1519,6 +1646,12 @@ if ($RemainingArgs.Count -gt 0 -and ($RemainingArgs[0] -eq "sync" -or $Remaining
|
|
|
1519
1646
|
exit 0
|
|
1520
1647
|
}
|
|
1521
1648
|
|
|
1649
|
+
# Special case: update command
|
|
1650
|
+
if ($RemainingArgs.Count -gt 0 -and ($RemainingArgs[0] -eq "update" -or $RemainingArgs[0] -eq "--update")) {
|
|
1651
|
+
Update-Run
|
|
1652
|
+
exit 0
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1522
1655
|
# Run auto-recovery before main logic
|
|
1523
1656
|
if (-not (Invoke-AutoRecovery)) {
|
|
1524
1657
|
Write-ErrorMsg "Auto-recovery failed. Check permissions."
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ _ccs_completion() {
|
|
|
18
18
|
|
|
19
19
|
# Top-level completion (first argument)
|
|
20
20
|
if [[ ${COMP_CWORD} -eq 1 ]]; then
|
|
21
|
-
local commands="auth doctor sync"
|
|
21
|
+
local commands="auth doctor sync update"
|
|
22
22
|
local flags="--help --version --shell-completion -h -v -sc"
|
|
23
23
|
local profiles=""
|
|
24
24
|
|
|
@@ -73,21 +73,22 @@ complete -c ccs -s v -l version -d 'Show version information'
|
|
|
73
73
|
complete -c ccs -s sc -l shell-completion -d 'Install shell completion'
|
|
74
74
|
|
|
75
75
|
# Top-level commands (blue color for commands)
|
|
76
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'auth' -d (set_color blue)'Manage multiple Claude accounts'(set_color normal)
|
|
77
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'doctor' -d (set_color blue)'Run health check and diagnostics'(set_color normal)
|
|
78
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'sync' -d (set_color blue)'Sync delegation commands and skills'(set_color normal)
|
|
76
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'auth' -d (set_color blue)'Manage multiple Claude accounts'(set_color normal)
|
|
77
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'doctor' -d (set_color blue)'Run health check and diagnostics'(set_color normal)
|
|
78
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'sync' -d (set_color blue)'Sync delegation commands and skills'(set_color normal)
|
|
79
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'update' -d (set_color blue)'Update CCS to latest version'(set_color normal)
|
|
79
80
|
|
|
80
81
|
# Top-level known settings profiles (green color for model profiles)
|
|
81
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'default' -d (set_color green)'Default Claude Sonnet 4.5'(set_color normal)
|
|
82
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'glm' -d (set_color green)'GLM-4.6 (cost-optimized)'(set_color normal)
|
|
83
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'glmt' -d (set_color green)'GLM-4.6 with thinking mode'(set_color normal)
|
|
84
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'kimi' -d (set_color green)'Kimi for Coding (long-context)'(set_color normal)
|
|
82
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'default' -d (set_color green)'Default Claude Sonnet 4.5'(set_color normal)
|
|
83
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'glm' -d (set_color green)'GLM-4.6 (cost-optimized)'(set_color normal)
|
|
84
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'glmt' -d (set_color green)'GLM-4.6 with thinking mode'(set_color normal)
|
|
85
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a 'kimi' -d (set_color green)'Kimi for Coding (long-context)'(set_color normal)
|
|
85
86
|
|
|
86
87
|
# Top-level custom settings profiles (dynamic, with generic description in green)
|
|
87
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a '(__fish_ccs_get_custom_settings_profiles)' -d (set_color green)'Settings-based profile'(set_color normal)
|
|
88
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a '(__fish_ccs_get_custom_settings_profiles)' -d (set_color green)'Settings-based profile'(set_color normal)
|
|
88
89
|
|
|
89
90
|
# Top-level account profiles (dynamic, yellow color for account profiles)
|
|
90
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a '(__fish_ccs_get_account_profiles)' -d (set_color yellow)'Account profile'(set_color normal)
|
|
91
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync update' -a '(__fish_ccs_get_account_profiles)' -d (set_color yellow)'Account profile'(set_color normal)
|
|
91
92
|
|
|
92
93
|
# shell-completion subflags
|
|
93
94
|
complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l bash -d 'Install for bash'
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
Register-ArgumentCompleter -CommandName ccs -ScriptBlock {
|
|
13
13
|
param($commandName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
|
14
14
|
|
|
15
|
-
$commands = @('auth', 'doctor', 'sync', '--help', '--version', '--shell-completion', '-h', '-v', '-sc')
|
|
15
|
+
$commands = @('auth', 'doctor', 'sync', 'update', '--help', '--version', '--shell-completion', '-h', '-v', '-sc')
|
|
16
16
|
$authCommands = @('create', 'list', 'show', 'remove', 'default', '--help', '-h')
|
|
17
17
|
$shellCompletionFlags = @('--bash', '--zsh', '--fish', '--powershell')
|
|
18
18
|
$listFlags = @('--verbose', '--json')
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
# Color codes: 0;34=blue, 0;32=green, 0;33=yellow, 2;37=dim white
|
|
17
17
|
# Pattern format: =(#b)(group1)(group2)==color_for_group1=color_for_group2
|
|
18
18
|
# The leading '=' means no color for whole match, then each '=' assigns to each group
|
|
19
|
-
zstyle ':completion:*:*:ccs:*:commands' list-colors '=(#b)(auth|doctor|sync)([[:space:]]#--[[:space:]]#*)==0\;34=2\;37'
|
|
19
|
+
zstyle ':completion:*:*:ccs:*:commands' list-colors '=(#b)(auth|doctor|sync|update)([[:space:]]#--[[:space:]]#*)==0\;34=2\;37'
|
|
20
20
|
zstyle ':completion:*:*:ccs:*:model-profiles' list-colors '=(#b)(default|glm|glmt|kimi|[^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;32=2\;37'
|
|
21
21
|
zstyle ':completion:*:*:ccs:*:account-profiles' list-colors '=(#b)([^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;33=2\;37'
|
|
22
22
|
zstyle ':completion:*:*:ccs:*' group-name ''
|
|
@@ -35,6 +35,7 @@ _ccs() {
|
|
|
35
35
|
'auth:Manage multiple Claude accounts'
|
|
36
36
|
'doctor:Run health check and diagnostics'
|
|
37
37
|
'sync:Sync delegation commands and skills'
|
|
38
|
+
'update:Update CCS to latest version'
|
|
38
39
|
)
|
|
39
40
|
|
|
40
41
|
# Define known settings profiles with descriptions (consistent padding)
|