@kaitranntt/ccs 4.3.3 → 4.3.4

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.3.3
1
+ 4.3.4
package/bin/ccs.js CHANGED
@@ -298,6 +298,150 @@ async function handleSyncCommand() {
298
298
  process.exit(0);
299
299
  }
300
300
 
301
+ /**
302
+ * Detect installation method
303
+ * @returns {'npm'|'direct'} - Installation method
304
+ */
305
+ function detectInstallationMethod() {
306
+ const scriptPath = process.argv[1];
307
+
308
+ // Method 1: Check if script is inside node_modules
309
+ if (scriptPath.includes('node_modules')) {
310
+ return 'npm';
311
+ }
312
+
313
+ // Method 2: Check if script is in npm global bin directory
314
+ // Common patterns for npm global installations
315
+ const npmGlobalBinPatterns = [
316
+ /\.npm\/global\/bin\//, // ~/.npm/global/bin/ccs
317
+ /\/\.nvm\/versions\/node\/[^\/]+\/bin\//, // ~/.nvm/versions/node/v22.19.0/bin/ccs
318
+ /\/usr\/local\/bin\//, // /usr/local/bin/ccs (if npm global prefix is /usr/local)
319
+ /\/usr\/bin\// // /usr/bin/ccs (if npm global prefix is /usr)
320
+ ];
321
+
322
+ for (const pattern of npmGlobalBinPatterns) {
323
+ if (pattern.test(scriptPath)) {
324
+ // Verify this is actually CCS by checking the linked target
325
+ try {
326
+ const binDir = path.dirname(scriptPath);
327
+ const nodeModulesDir = path.join(binDir, '..', 'lib', 'node_modules', '@kaitranntt', 'ccs');
328
+ const globalModulesDir = path.join(binDir, '..', 'node_modules', '@kaitranntt', 'ccs');
329
+
330
+ if (fs.existsSync(nodeModulesDir) || fs.existsSync(globalModulesDir)) {
331
+ return 'npm';
332
+ }
333
+ } catch (err) {
334
+ // Continue checking other patterns
335
+ }
336
+ }
337
+ }
338
+
339
+ // Method 3: Check if package.json exists in parent directory (development mode)
340
+ const packageJsonPath = path.join(__dirname, '..', 'package.json');
341
+
342
+ if (fs.existsSync(packageJsonPath)) {
343
+ try {
344
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
345
+ // If package.json has name "@kaitranntt/ccs", it's npm install
346
+ if (pkg.name === '@kaitranntt/ccs') {
347
+ return 'npm';
348
+ }
349
+ } catch (err) {
350
+ // Ignore parse errors
351
+ }
352
+ }
353
+
354
+ // Method 4: Check if script is a symlink pointing to node_modules
355
+ try {
356
+ const stats = fs.lstatSync(scriptPath);
357
+ if (stats.isSymbolicLink()) {
358
+ const targetPath = fs.readlinkSync(scriptPath);
359
+ if (targetPath.includes('node_modules') || targetPath.includes('@kaitranntt/ccs')) {
360
+ return 'npm';
361
+ }
362
+ }
363
+ } catch (err) {
364
+ // Continue to default
365
+ }
366
+
367
+ // Default to direct installation
368
+ return 'direct';
369
+ }
370
+
371
+ /**
372
+ * Detect which package manager was used for installation
373
+ * @returns {'npm'|'yarn'|'pnpm'|'bun'|'unknown'}
374
+ */
375
+ function detectPackageManager() {
376
+ const scriptPath = process.argv[1];
377
+
378
+ // Check if script path contains package manager indicators
379
+ if (scriptPath.includes('.pnpm')) return 'pnpm';
380
+ if (scriptPath.includes('yarn')) return 'yarn';
381
+ if (scriptPath.includes('bun')) return 'bun';
382
+
383
+ // Check parent directories for lock files
384
+ const binDir = path.dirname(scriptPath);
385
+ const fs = require('fs');
386
+
387
+ // Check global node_modules parent for lock files
388
+ let checkDir = binDir;
389
+ for (let i = 0; i < 5; i++) {
390
+ if (fs.existsSync(path.join(checkDir, 'pnpm-lock.yaml'))) return 'pnpm';
391
+ if (fs.existsSync(path.join(checkDir, 'yarn.lock'))) return 'yarn';
392
+ if (fs.existsSync(path.join(checkDir, 'bun.lockb'))) return 'bun';
393
+ checkDir = path.dirname(checkDir);
394
+ }
395
+
396
+ // Check if package managers are available on the system
397
+ const { spawnSync } = require('child_process');
398
+
399
+ // Try yarn global list to see if CCS is installed via yarn
400
+ try {
401
+ const yarnResult = spawnSync('yarn', ['global', 'list', '--pattern', '@kaitranntt/ccs'], {
402
+ encoding: 'utf8',
403
+ shell: true,
404
+ timeout: 5000
405
+ });
406
+ if (yarnResult.status === 0 && yarnResult.stdout.includes('@kaitranntt/ccs')) {
407
+ return 'yarn';
408
+ }
409
+ } catch (err) {
410
+ // Continue to next check
411
+ }
412
+
413
+ // Try pnpm list -g to see if CCS is installed via pnpm
414
+ try {
415
+ const pnpmResult = spawnSync('pnpm', ['list', '-g', '--pattern', '@kaitranntt/ccs'], {
416
+ encoding: 'utf8',
417
+ shell: true,
418
+ timeout: 5000
419
+ });
420
+ if (pnpmResult.status === 0 && pnpmResult.stdout.includes('@kaitranntt/ccs')) {
421
+ return 'pnpm';
422
+ }
423
+ } catch (err) {
424
+ // Continue to next check
425
+ }
426
+
427
+ // Try bun pm ls -g to see if CCS is installed via bun
428
+ try {
429
+ const bunResult = spawnSync('bun', ['pm', 'ls', '-g', '--pattern', '@kaitranntt/ccs'], {
430
+ encoding: 'utf8',
431
+ shell: true,
432
+ timeout: 5000
433
+ });
434
+ if (bunResult.status === 0 && bunResult.stdout.includes('@kaitranntt/ccs')) {
435
+ return 'bun';
436
+ }
437
+ } catch (err) {
438
+ // Continue to default
439
+ }
440
+
441
+ // Default to npm
442
+ return 'npm';
443
+ }
444
+
301
445
  async function handleUpdateCommand() {
302
446
  const { checkForUpdates } = require('./utils/update-checker');
303
447
  const { spawn } = require('child_process');
@@ -307,8 +451,8 @@ async function handleUpdateCommand() {
307
451
  console.log('');
308
452
 
309
453
  // Detect installation method for proper update source
310
- const isNpmInstall = process.argv[1].includes('node_modules');
311
- const installMethod = isNpmInstall ? 'npm' : 'direct';
454
+ const installMethod = detectInstallationMethod();
455
+ const isNpmInstall = installMethod === 'npm';
312
456
 
313
457
  // Check for updates (force check)
314
458
  const updateResult = await checkForUpdates(CCS_VERSION, true, installMethod);
@@ -323,7 +467,27 @@ async function handleUpdateCommand() {
323
467
  console.log('');
324
468
  console.log('Try again later or update manually:');
325
469
  if (isNpmInstall) {
326
- console.log(colored(' npm install -g @kaitranntt/ccs@latest', 'yellow'));
470
+ const packageManager = detectPackageManager();
471
+ let manualCommand;
472
+
473
+ switch (packageManager) {
474
+ case 'npm':
475
+ manualCommand = 'npm install -g @kaitranntt/ccs@latest';
476
+ break;
477
+ case 'yarn':
478
+ manualCommand = 'yarn global add @kaitranntt/ccs@latest';
479
+ break;
480
+ case 'pnpm':
481
+ manualCommand = 'pnpm add -g @kaitranntt/ccs@latest';
482
+ break;
483
+ case 'bun':
484
+ manualCommand = 'bun add -g @kaitranntt/ccs@latest';
485
+ break;
486
+ default:
487
+ manualCommand = 'npm install -g @kaitranntt/ccs@latest';
488
+ }
489
+
490
+ console.log(colored(` ${manualCommand}`, 'yellow'));
327
491
  } else {
328
492
  const isWindows = process.platform === 'win32';
329
493
  if (isWindows) {
@@ -361,13 +525,38 @@ async function handleUpdateCommand() {
361
525
  console.log('');
362
526
 
363
527
  if (isNpmInstall) {
364
- // npm installation - use npm update
365
- console.log(colored('Updating via npm...', 'cyan'));
528
+ // npm installation - detect package manager and update
529
+ const packageManager = detectPackageManager();
530
+ let updateCommand, updateArgs;
531
+
532
+ switch (packageManager) {
533
+ case 'npm':
534
+ updateCommand = 'npm';
535
+ updateArgs = ['install', '-g', '@kaitranntt/ccs@latest'];
536
+ break;
537
+ case 'yarn':
538
+ updateCommand = 'yarn';
539
+ updateArgs = ['global', 'add', '@kaitranntt/ccs@latest'];
540
+ break;
541
+ case 'pnpm':
542
+ updateCommand = 'pnpm';
543
+ updateArgs = ['add', '-g', '@kaitranntt/ccs@latest'];
544
+ break;
545
+ case 'bun':
546
+ updateCommand = 'bun';
547
+ updateArgs = ['add', '-g', '@kaitranntt/ccs@latest'];
548
+ break;
549
+ default:
550
+ updateCommand = 'npm';
551
+ updateArgs = ['install', '-g', '@kaitranntt/ccs@latest'];
552
+ }
553
+
554
+ console.log(colored(`Updating via ${packageManager}...`, 'cyan'));
366
555
  console.log('');
367
556
 
368
- const child = spawn('npm', ['install', '-g', '@kaitranntt/ccs@latest'], {
369
- stdio: 'inherit',
370
- shell: true
557
+ const child = spawn(updateCommand, updateArgs, {
558
+ stdio: 'inherit'
559
+ // No shell needed for direct commands
371
560
  });
372
561
 
373
562
  child.on('exit', (code) => {
@@ -382,7 +571,7 @@ async function handleUpdateCommand() {
382
571
  console.log(colored('[X] Update failed', 'red'));
383
572
  console.log('');
384
573
  console.log('Try manually:');
385
- console.log(colored(' npm install -g @kaitranntt/ccs@latest', 'yellow'));
574
+ console.log(colored(` ${updateCommand} ${updateArgs.join(' ')}`, 'yellow'));
386
575
  console.log('');
387
576
  }
388
577
  process.exit(code || 0);
@@ -390,10 +579,10 @@ async function handleUpdateCommand() {
390
579
 
391
580
  child.on('error', (err) => {
392
581
  console.log('');
393
- console.log(colored('[X] Failed to run npm update', 'red'));
582
+ console.log(colored(`[X] Failed to run ${packageManager} update`, 'red'));
394
583
  console.log('');
395
584
  console.log('Try manually:');
396
- console.log(colored(' npm install -g @kaitranntt/ccs@latest', 'yellow'));
585
+ console.log(colored(` ${updateCommand} ${updateArgs.join(' ')}`, 'yellow'));
397
586
  console.log('');
398
587
  process.exit(1);
399
588
  });
@@ -406,16 +595,19 @@ async function handleUpdateCommand() {
406
595
  let command, args;
407
596
 
408
597
  if (isWindows) {
598
+ // PowerShell
409
599
  command = 'powershell.exe';
410
- args = ['-Command', 'irm ccs.kaitran.ca/install | iex'];
600
+ args = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command',
601
+ 'irm ccs.kaitran.ca/install | iex'];
411
602
  } else {
412
- command = 'bash';
603
+ // Unix (bash with proper shell invocation)
604
+ command = '/bin/bash';
413
605
  args = ['-c', 'curl -fsSL ccs.kaitran.ca/install | bash'];
414
606
  }
415
607
 
416
608
  const child = spawn(command, args, {
417
- stdio: 'inherit',
418
- shell: true
609
+ stdio: 'inherit'
610
+ // Do NOT use shell: true
419
611
  });
420
612
 
421
613
  child.on('exit', (code) => {
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.3"
5
+ CCS_VERSION="4.3.4"
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.3"
15
+ $CcsVersion = "4.3.4"
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",
3
+ "version": "4.3.4",
4
4
  "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
5
5
  "keywords": [
6
6
  "cli",