@kaitranntt/ccs 4.3.3 → 4.3.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/README.md +4 -5
- package/VERSION +1 -1
- package/bin/ccs.js +212 -15
- package/bin/utils/claude-dir-installer.js +102 -0
- package/bin/utils/claude-symlink-manager.js +1 -2
- package/lib/ccs +1 -1
- package/lib/ccs.ps1 +1 -1
- package/package.json +2 -2
- package/scripts/postinstall.js +3 -0
package/README.md
CHANGED
|
@@ -323,8 +323,7 @@ graph LR
|
|
|
323
323
|
~/.ccs/
|
|
324
324
|
├── .claude/ # CCS items (ships with package, v4.1)
|
|
325
325
|
│ ├── commands/ccs/ # Delegation commands (/ccs, /ccs:continue)
|
|
326
|
-
│
|
|
327
|
-
│ └── agents/ccs-delegator.md # Proactive delegation agent
|
|
326
|
+
│ └── skills/ccs-delegation/ # AI decision framework (replaces deprecated agents)
|
|
328
327
|
├── shared/ # Symlinks to ~/.claude/ (for profiles)
|
|
329
328
|
│ ├── agents@ → ~/.claude/agents/
|
|
330
329
|
│ ├── commands@ → ~/.claude/commands/
|
|
@@ -341,7 +340,7 @@ graph LR
|
|
|
341
340
|
~/.claude/ # User's Claude directory
|
|
342
341
|
├── commands/ccs@ → ~/.ccs/.claude/commands/ccs/ # Selective symlink
|
|
343
342
|
├── skills/ccs-delegation@ → ~/.ccs/.claude/skills/ccs-delegation/
|
|
344
|
-
|
|
343
|
+
# agents/ccs-delegator.md@ → ~/.ccs/.claude/agents/ccs-delegator.md # Deprecated in v4.3.2
|
|
345
344
|
```
|
|
346
345
|
|
|
347
346
|
**Symlink Chain**: `work profile → ~/.ccs/shared/ → ~/.claude/ → ~/.ccs/.claude/` (CCS items)
|
|
@@ -507,8 +506,8 @@ Savings: $0.0275 (86% reduction)
|
|
|
507
506
|
### Documentation
|
|
508
507
|
|
|
509
508
|
- **Workflow Diagrams**: See [docs/ccs-delegation-diagrams.md](docs/ccs-delegation-diagrams.md) for visual architecture
|
|
510
|
-
- **Skill Reference**: `.claude/skills/ccs-delegation/` for AI decision framework
|
|
511
|
-
- **Agent Docs**: `.claude/agents/ccs-delegator.md`
|
|
509
|
+
- **Skill Reference**: `.claude/skills/ccs-delegation/` for AI decision framework (replaces deprecated agents)
|
|
510
|
+
- **Agent Docs**: `.claude/agents/ccs-delegator.md` was deprecated in v4.3.2, functionality moved to ccs-delegation skill
|
|
512
511
|
|
|
513
512
|
<br>
|
|
514
513
|
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.3.
|
|
1
|
+
4.3.5
|
package/bin/ccs.js
CHANGED
|
@@ -286,6 +286,11 @@ async function handleSyncCommand() {
|
|
|
286
286
|
|
|
287
287
|
console.log('');
|
|
288
288
|
|
|
289
|
+
const cleanupResult = installer.cleanupDeprecated();
|
|
290
|
+
if (cleanupResult.success && cleanupResult.cleanedFiles.length > 0) {
|
|
291
|
+
console.log('');
|
|
292
|
+
}
|
|
293
|
+
|
|
289
294
|
// Then, create symlinks from ~/.ccs/.claude/ to ~/.claude/
|
|
290
295
|
const ClaudeSymlinkManager = require('./utils/claude-symlink-manager');
|
|
291
296
|
const manager = new ClaudeSymlinkManager();
|
|
@@ -298,6 +303,150 @@ async function handleSyncCommand() {
|
|
|
298
303
|
process.exit(0);
|
|
299
304
|
}
|
|
300
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Detect installation method
|
|
308
|
+
* @returns {'npm'|'direct'} - Installation method
|
|
309
|
+
*/
|
|
310
|
+
function detectInstallationMethod() {
|
|
311
|
+
const scriptPath = process.argv[1];
|
|
312
|
+
|
|
313
|
+
// Method 1: Check if script is inside node_modules
|
|
314
|
+
if (scriptPath.includes('node_modules')) {
|
|
315
|
+
return 'npm';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Method 2: Check if script is in npm global bin directory
|
|
319
|
+
// Common patterns for npm global installations
|
|
320
|
+
const npmGlobalBinPatterns = [
|
|
321
|
+
/\.npm\/global\/bin\//, // ~/.npm/global/bin/ccs
|
|
322
|
+
/\/\.nvm\/versions\/node\/[^\/]+\/bin\//, // ~/.nvm/versions/node/v22.19.0/bin/ccs
|
|
323
|
+
/\/usr\/local\/bin\//, // /usr/local/bin/ccs (if npm global prefix is /usr/local)
|
|
324
|
+
/\/usr\/bin\// // /usr/bin/ccs (if npm global prefix is /usr)
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
for (const pattern of npmGlobalBinPatterns) {
|
|
328
|
+
if (pattern.test(scriptPath)) {
|
|
329
|
+
// Verify this is actually CCS by checking the linked target
|
|
330
|
+
try {
|
|
331
|
+
const binDir = path.dirname(scriptPath);
|
|
332
|
+
const nodeModulesDir = path.join(binDir, '..', 'lib', 'node_modules', '@kaitranntt', 'ccs');
|
|
333
|
+
const globalModulesDir = path.join(binDir, '..', 'node_modules', '@kaitranntt', 'ccs');
|
|
334
|
+
|
|
335
|
+
if (fs.existsSync(nodeModulesDir) || fs.existsSync(globalModulesDir)) {
|
|
336
|
+
return 'npm';
|
|
337
|
+
}
|
|
338
|
+
} catch (err) {
|
|
339
|
+
// Continue checking other patterns
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Method 3: Check if package.json exists in parent directory (development mode)
|
|
345
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
346
|
+
|
|
347
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
348
|
+
try {
|
|
349
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
350
|
+
// If package.json has name "@kaitranntt/ccs", it's npm install
|
|
351
|
+
if (pkg.name === '@kaitranntt/ccs') {
|
|
352
|
+
return 'npm';
|
|
353
|
+
}
|
|
354
|
+
} catch (err) {
|
|
355
|
+
// Ignore parse errors
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Method 4: Check if script is a symlink pointing to node_modules
|
|
360
|
+
try {
|
|
361
|
+
const stats = fs.lstatSync(scriptPath);
|
|
362
|
+
if (stats.isSymbolicLink()) {
|
|
363
|
+
const targetPath = fs.readlinkSync(scriptPath);
|
|
364
|
+
if (targetPath.includes('node_modules') || targetPath.includes('@kaitranntt/ccs')) {
|
|
365
|
+
return 'npm';
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
} catch (err) {
|
|
369
|
+
// Continue to default
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Default to direct installation
|
|
373
|
+
return 'direct';
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Detect which package manager was used for installation
|
|
378
|
+
* @returns {'npm'|'yarn'|'pnpm'|'bun'|'unknown'}
|
|
379
|
+
*/
|
|
380
|
+
function detectPackageManager() {
|
|
381
|
+
const scriptPath = process.argv[1];
|
|
382
|
+
|
|
383
|
+
// Check if script path contains package manager indicators
|
|
384
|
+
if (scriptPath.includes('.pnpm')) return 'pnpm';
|
|
385
|
+
if (scriptPath.includes('yarn')) return 'yarn';
|
|
386
|
+
if (scriptPath.includes('bun')) return 'bun';
|
|
387
|
+
|
|
388
|
+
// Check parent directories for lock files
|
|
389
|
+
const binDir = path.dirname(scriptPath);
|
|
390
|
+
const fs = require('fs');
|
|
391
|
+
|
|
392
|
+
// Check global node_modules parent for lock files
|
|
393
|
+
let checkDir = binDir;
|
|
394
|
+
for (let i = 0; i < 5; i++) {
|
|
395
|
+
if (fs.existsSync(path.join(checkDir, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
396
|
+
if (fs.existsSync(path.join(checkDir, 'yarn.lock'))) return 'yarn';
|
|
397
|
+
if (fs.existsSync(path.join(checkDir, 'bun.lockb'))) return 'bun';
|
|
398
|
+
checkDir = path.dirname(checkDir);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Check if package managers are available on the system
|
|
402
|
+
const { spawnSync } = require('child_process');
|
|
403
|
+
|
|
404
|
+
// Try yarn global list to see if CCS is installed via yarn
|
|
405
|
+
try {
|
|
406
|
+
const yarnResult = spawnSync('yarn', ['global', 'list', '--pattern', '@kaitranntt/ccs'], {
|
|
407
|
+
encoding: 'utf8',
|
|
408
|
+
shell: true,
|
|
409
|
+
timeout: 5000
|
|
410
|
+
});
|
|
411
|
+
if (yarnResult.status === 0 && yarnResult.stdout.includes('@kaitranntt/ccs')) {
|
|
412
|
+
return 'yarn';
|
|
413
|
+
}
|
|
414
|
+
} catch (err) {
|
|
415
|
+
// Continue to next check
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Try pnpm list -g to see if CCS is installed via pnpm
|
|
419
|
+
try {
|
|
420
|
+
const pnpmResult = spawnSync('pnpm', ['list', '-g', '--pattern', '@kaitranntt/ccs'], {
|
|
421
|
+
encoding: 'utf8',
|
|
422
|
+
shell: true,
|
|
423
|
+
timeout: 5000
|
|
424
|
+
});
|
|
425
|
+
if (pnpmResult.status === 0 && pnpmResult.stdout.includes('@kaitranntt/ccs')) {
|
|
426
|
+
return 'pnpm';
|
|
427
|
+
}
|
|
428
|
+
} catch (err) {
|
|
429
|
+
// Continue to next check
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Try bun pm ls -g to see if CCS is installed via bun
|
|
433
|
+
try {
|
|
434
|
+
const bunResult = spawnSync('bun', ['pm', 'ls', '-g', '--pattern', '@kaitranntt/ccs'], {
|
|
435
|
+
encoding: 'utf8',
|
|
436
|
+
shell: true,
|
|
437
|
+
timeout: 5000
|
|
438
|
+
});
|
|
439
|
+
if (bunResult.status === 0 && bunResult.stdout.includes('@kaitranntt/ccs')) {
|
|
440
|
+
return 'bun';
|
|
441
|
+
}
|
|
442
|
+
} catch (err) {
|
|
443
|
+
// Continue to default
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Default to npm
|
|
447
|
+
return 'npm';
|
|
448
|
+
}
|
|
449
|
+
|
|
301
450
|
async function handleUpdateCommand() {
|
|
302
451
|
const { checkForUpdates } = require('./utils/update-checker');
|
|
303
452
|
const { spawn } = require('child_process');
|
|
@@ -307,8 +456,8 @@ async function handleUpdateCommand() {
|
|
|
307
456
|
console.log('');
|
|
308
457
|
|
|
309
458
|
// Detect installation method for proper update source
|
|
310
|
-
const
|
|
311
|
-
const
|
|
459
|
+
const installMethod = detectInstallationMethod();
|
|
460
|
+
const isNpmInstall = installMethod === 'npm';
|
|
312
461
|
|
|
313
462
|
// Check for updates (force check)
|
|
314
463
|
const updateResult = await checkForUpdates(CCS_VERSION, true, installMethod);
|
|
@@ -323,7 +472,27 @@ async function handleUpdateCommand() {
|
|
|
323
472
|
console.log('');
|
|
324
473
|
console.log('Try again later or update manually:');
|
|
325
474
|
if (isNpmInstall) {
|
|
326
|
-
|
|
475
|
+
const packageManager = detectPackageManager();
|
|
476
|
+
let manualCommand;
|
|
477
|
+
|
|
478
|
+
switch (packageManager) {
|
|
479
|
+
case 'npm':
|
|
480
|
+
manualCommand = 'npm install -g @kaitranntt/ccs@latest';
|
|
481
|
+
break;
|
|
482
|
+
case 'yarn':
|
|
483
|
+
manualCommand = 'yarn global add @kaitranntt/ccs@latest';
|
|
484
|
+
break;
|
|
485
|
+
case 'pnpm':
|
|
486
|
+
manualCommand = 'pnpm add -g @kaitranntt/ccs@latest';
|
|
487
|
+
break;
|
|
488
|
+
case 'bun':
|
|
489
|
+
manualCommand = 'bun add -g @kaitranntt/ccs@latest';
|
|
490
|
+
break;
|
|
491
|
+
default:
|
|
492
|
+
manualCommand = 'npm install -g @kaitranntt/ccs@latest';
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
console.log(colored(` ${manualCommand}`, 'yellow'));
|
|
327
496
|
} else {
|
|
328
497
|
const isWindows = process.platform === 'win32';
|
|
329
498
|
if (isWindows) {
|
|
@@ -361,13 +530,38 @@ async function handleUpdateCommand() {
|
|
|
361
530
|
console.log('');
|
|
362
531
|
|
|
363
532
|
if (isNpmInstall) {
|
|
364
|
-
// npm installation -
|
|
365
|
-
|
|
533
|
+
// npm installation - detect package manager and update
|
|
534
|
+
const packageManager = detectPackageManager();
|
|
535
|
+
let updateCommand, updateArgs;
|
|
536
|
+
|
|
537
|
+
switch (packageManager) {
|
|
538
|
+
case 'npm':
|
|
539
|
+
updateCommand = 'npm';
|
|
540
|
+
updateArgs = ['install', '-g', '@kaitranntt/ccs@latest'];
|
|
541
|
+
break;
|
|
542
|
+
case 'yarn':
|
|
543
|
+
updateCommand = 'yarn';
|
|
544
|
+
updateArgs = ['global', 'add', '@kaitranntt/ccs@latest'];
|
|
545
|
+
break;
|
|
546
|
+
case 'pnpm':
|
|
547
|
+
updateCommand = 'pnpm';
|
|
548
|
+
updateArgs = ['add', '-g', '@kaitranntt/ccs@latest'];
|
|
549
|
+
break;
|
|
550
|
+
case 'bun':
|
|
551
|
+
updateCommand = 'bun';
|
|
552
|
+
updateArgs = ['add', '-g', '@kaitranntt/ccs@latest'];
|
|
553
|
+
break;
|
|
554
|
+
default:
|
|
555
|
+
updateCommand = 'npm';
|
|
556
|
+
updateArgs = ['install', '-g', '@kaitranntt/ccs@latest'];
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
console.log(colored(`Updating via ${packageManager}...`, 'cyan'));
|
|
366
560
|
console.log('');
|
|
367
561
|
|
|
368
|
-
const child = spawn(
|
|
369
|
-
stdio: 'inherit'
|
|
370
|
-
shell
|
|
562
|
+
const child = spawn(updateCommand, updateArgs, {
|
|
563
|
+
stdio: 'inherit'
|
|
564
|
+
// No shell needed for direct commands
|
|
371
565
|
});
|
|
372
566
|
|
|
373
567
|
child.on('exit', (code) => {
|
|
@@ -382,7 +576,7 @@ async function handleUpdateCommand() {
|
|
|
382
576
|
console.log(colored('[X] Update failed', 'red'));
|
|
383
577
|
console.log('');
|
|
384
578
|
console.log('Try manually:');
|
|
385
|
-
console.log(colored(
|
|
579
|
+
console.log(colored(` ${updateCommand} ${updateArgs.join(' ')}`, 'yellow'));
|
|
386
580
|
console.log('');
|
|
387
581
|
}
|
|
388
582
|
process.exit(code || 0);
|
|
@@ -390,10 +584,10 @@ async function handleUpdateCommand() {
|
|
|
390
584
|
|
|
391
585
|
child.on('error', (err) => {
|
|
392
586
|
console.log('');
|
|
393
|
-
console.log(colored(
|
|
587
|
+
console.log(colored(`[X] Failed to run ${packageManager} update`, 'red'));
|
|
394
588
|
console.log('');
|
|
395
589
|
console.log('Try manually:');
|
|
396
|
-
console.log(colored(
|
|
590
|
+
console.log(colored(` ${updateCommand} ${updateArgs.join(' ')}`, 'yellow'));
|
|
397
591
|
console.log('');
|
|
398
592
|
process.exit(1);
|
|
399
593
|
});
|
|
@@ -406,16 +600,19 @@ async function handleUpdateCommand() {
|
|
|
406
600
|
let command, args;
|
|
407
601
|
|
|
408
602
|
if (isWindows) {
|
|
603
|
+
// PowerShell
|
|
409
604
|
command = 'powershell.exe';
|
|
410
|
-
args = ['-
|
|
605
|
+
args = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command',
|
|
606
|
+
'irm ccs.kaitran.ca/install | iex'];
|
|
411
607
|
} else {
|
|
412
|
-
|
|
608
|
+
// Unix (bash with proper shell invocation)
|
|
609
|
+
command = '/bin/bash';
|
|
413
610
|
args = ['-c', 'curl -fsSL ccs.kaitran.ca/install | bash'];
|
|
414
611
|
}
|
|
415
612
|
|
|
416
613
|
const child = spawn(command, args, {
|
|
417
|
-
stdio: 'inherit'
|
|
418
|
-
shell: true
|
|
614
|
+
stdio: 'inherit'
|
|
615
|
+
// Do NOT use shell: true
|
|
419
616
|
});
|
|
420
617
|
|
|
421
618
|
child.on('exit', (code) => {
|
|
@@ -148,6 +148,108 @@ class ClaudeDirInstaller {
|
|
|
148
148
|
return { files, dirs };
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Clean up deprecated files from previous installations
|
|
153
|
+
* Removes ccs-delegator.md that was deprecated in v4.3.2
|
|
154
|
+
* @param {boolean} silent - Suppress console output
|
|
155
|
+
*/
|
|
156
|
+
cleanupDeprecated(silent = false) {
|
|
157
|
+
const deprecatedFile = path.join(this.ccsClaudeDir, 'agents', 'ccs-delegator.md');
|
|
158
|
+
const userSymlinkFile = path.join(this.homeDir, '.claude', 'agents', 'ccs-delegator.md');
|
|
159
|
+
const migrationMarker = path.join(this.homeDir, '.ccs', '.migrations', 'v435-delegator-cleanup');
|
|
160
|
+
|
|
161
|
+
let cleanedFiles = [];
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
// Check if cleanup already done
|
|
165
|
+
if (fs.existsSync(migrationMarker)) {
|
|
166
|
+
return { success: true, cleanedFiles: [] }; // Already cleaned
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Clean up user symlink in ~/.claude/agents/ccs-delegator.md FIRST
|
|
170
|
+
// This ensures we can detect broken symlinks before deleting the target
|
|
171
|
+
try {
|
|
172
|
+
const userStats = fs.lstatSync(userSymlinkFile);
|
|
173
|
+
if (userStats.isSymbolicLink()) {
|
|
174
|
+
fs.unlinkSync(userSymlinkFile);
|
|
175
|
+
cleanedFiles.push('user symlink');
|
|
176
|
+
} else {
|
|
177
|
+
// It's not a symlink (user created their own file), backup it
|
|
178
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
|
|
179
|
+
const backupPath = `${userSymlinkFile}.backup-${timestamp}`;
|
|
180
|
+
fs.renameSync(userSymlinkFile, backupPath);
|
|
181
|
+
if (!silent) console.log(`[i] Backed up user file to ${path.basename(backupPath)}`);
|
|
182
|
+
cleanedFiles.push('user file (backed up)');
|
|
183
|
+
}
|
|
184
|
+
} catch (err) {
|
|
185
|
+
// File doesn't exist or other error - that's okay
|
|
186
|
+
if (err.code !== 'ENOENT' && !silent) {
|
|
187
|
+
console.log(`[!] Failed to remove user symlink: ${err.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Clean up package copy in ~/.ccs/.claude/agents/ccs-delegator.md
|
|
192
|
+
if (fs.existsSync(deprecatedFile)) {
|
|
193
|
+
try {
|
|
194
|
+
// Check if file was modified by user (compare with expected content)
|
|
195
|
+
const shouldBackup = this._shouldBackupDeprecatedFile(deprecatedFile);
|
|
196
|
+
|
|
197
|
+
if (shouldBackup) {
|
|
198
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
|
|
199
|
+
const backupPath = `${deprecatedFile}.backup-${timestamp}`;
|
|
200
|
+
fs.renameSync(deprecatedFile, backupPath);
|
|
201
|
+
if (!silent) console.log(`[i] Backed up modified deprecated file to ${path.basename(backupPath)}`);
|
|
202
|
+
} else {
|
|
203
|
+
fs.rmSync(deprecatedFile, { force: true });
|
|
204
|
+
}
|
|
205
|
+
cleanedFiles.push('package copy');
|
|
206
|
+
} catch (err) {
|
|
207
|
+
if (!silent) console.log(`[!] Failed to remove package copy: ${err.message}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Create migration marker
|
|
212
|
+
if (cleanedFiles.length > 0) {
|
|
213
|
+
const migrationsDir = path.dirname(migrationMarker);
|
|
214
|
+
if (!fs.existsSync(migrationsDir)) {
|
|
215
|
+
fs.mkdirSync(migrationsDir, { recursive: true, mode: 0o700 });
|
|
216
|
+
}
|
|
217
|
+
fs.writeFileSync(migrationMarker, new Date().toISOString());
|
|
218
|
+
|
|
219
|
+
if (!silent) {
|
|
220
|
+
console.log(`[OK] Cleaned up deprecated agent files: ${cleanedFiles.join(', ')}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return { success: true, cleanedFiles };
|
|
225
|
+
} catch (err) {
|
|
226
|
+
if (!silent) console.log(`[!] Cleanup failed: ${err.message}`);
|
|
227
|
+
return { success: false, error: err.message, cleanedFiles };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Check if deprecated file should be backed up (user modified)
|
|
233
|
+
* @param {string} filePath - Path to check
|
|
234
|
+
* @returns {boolean} True if file should be backed up
|
|
235
|
+
* @private
|
|
236
|
+
*/
|
|
237
|
+
_shouldBackupDeprecatedFile(filePath) {
|
|
238
|
+
try {
|
|
239
|
+
// Simple heuristic: if file size differs significantly from expected, assume user modified
|
|
240
|
+
// Expected size for ccs-delegator.md was around 2-3KB
|
|
241
|
+
const stats = fs.statSync(filePath);
|
|
242
|
+
const expectedMinSize = 1000; // 1KB minimum
|
|
243
|
+
const expectedMaxSize = 10000; // 10KB maximum
|
|
244
|
+
|
|
245
|
+
// If size is outside expected range, likely user modified
|
|
246
|
+
return stats.size < expectedMinSize || stats.size > expectedMaxSize;
|
|
247
|
+
} catch (err) {
|
|
248
|
+
// If we can't determine, err on side of caution and backup
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
151
253
|
/**
|
|
152
254
|
* Check if ~/.ccs/.claude/ exists and is valid
|
|
153
255
|
* @returns {boolean} True if directory exists
|
|
@@ -28,8 +28,7 @@ class ClaudeSymlinkManager {
|
|
|
28
28
|
// CCS items to symlink (selective, item-level)
|
|
29
29
|
this.ccsItems = [
|
|
30
30
|
{ source: 'commands/ccs', target: 'commands/ccs', type: 'directory' },
|
|
31
|
-
{ source: 'skills/ccs-delegation', target: 'skills/ccs-delegation', type: 'directory' }
|
|
32
|
-
{ source: 'agents/ccs-delegator.md', target: 'agents/ccs-delegator.md', type: 'file' }
|
|
31
|
+
{ source: 'skills/ccs-delegation', target: 'skills/ccs-delegation', type: 'directory' }
|
|
33
32
|
];
|
|
34
33
|
}
|
|
35
34
|
|
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.3.
|
|
5
|
+
CCS_VERSION="4.3.5"
|
|
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"
|
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.3.
|
|
15
|
+
$CcsVersion = "4.3.5"
|
|
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"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaitranntt/ccs",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.5",
|
|
4
4
|
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"cli-table3": "^0.6.5",
|
|
62
|
-
"ora": "^
|
|
62
|
+
"ora": "^9.0.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"mocha": "^11.7.5"
|
package/scripts/postinstall.js
CHANGED
|
@@ -109,6 +109,9 @@ function createConfigFiles() {
|
|
|
109
109
|
const installer = new ClaudeDirInstaller();
|
|
110
110
|
const packageDir = path.join(__dirname, '..');
|
|
111
111
|
installer.install(packageDir);
|
|
112
|
+
|
|
113
|
+
// Clean up deprecated files (v4.3.2)
|
|
114
|
+
installer.cleanupDeprecated();
|
|
112
115
|
} catch (err) {
|
|
113
116
|
console.warn('[!] Failed to install .claude/ directory:', err.message);
|
|
114
117
|
console.warn(' CCS items may not be available');
|