ccconfig 1.4.3 → 1.5.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.
Files changed (2) hide show
  1. package/ccconfig.js +113 -53
  2. package/package.json +1 -1
package/ccconfig.js CHANGED
@@ -90,7 +90,7 @@ function ensureProfileAvailable(
90
90
  // All supported commands
91
91
  const COMMANDS = [
92
92
  'list', 'ls', 'add', 'update', 'use', 'start', 'safe-start', 'remove', 'rm',
93
- 'current', 'mode', 'env', 'edit', 'completion'
93
+ 'current', 'mode', 'env', 'completion'
94
94
  ];
95
95
 
96
96
  // ccconfig markers for shell config files
@@ -688,9 +688,8 @@ async function add(name) {
688
688
  if (profilesMap[name]) {
689
689
  console.error(`Error: Configuration '${name}' already exists`);
690
690
  console.error('');
691
- console.error('To modify this configuration, use one of:');
692
- console.error(` ccconfig update ${name} # Interactive update`);
693
- console.error(` ccconfig edit # Manual edit`);
691
+ console.error('To modify this configuration, use:');
692
+ console.error(` ccconfig update ${name}`);
694
693
  process.exit(1);
695
694
  }
696
695
 
@@ -704,18 +703,60 @@ async function add(name) {
704
703
 
705
704
  console.log(`✓ Configuration '${name}' added`);
706
705
  console.log('');
707
- console.log('Run the following command to activate:');
708
- console.log(` ccconfig use ${name}`);
709
- console.log('');
710
- console.log('Saved environment variables:');
711
- displayEnvVars(envVars);
706
+
707
+ // Check if claude binary exists before offering to start
708
+ let claudeAvailable = false;
709
+ try {
710
+ const command =
711
+ process.platform === 'win32' ? 'where claude' : 'which claude';
712
+ execSync(command, {stdio: 'pipe'});
713
+ claudeAvailable = true;
714
+ } catch (err) {
715
+ // Claude not found, don't offer to start
716
+ }
717
+
718
+ // Ask if user wants to start Claude Code with this profile immediately
719
+ const shouldStart = claudeAvailable ?
720
+ await helper.ask(`Start Claude Code with ${name} now? (yes/no)`, 'no') :
721
+ 'no';
722
+ const normalized = shouldStart.trim().toLowerCase();
723
+
712
724
  console.log('');
713
- console.log('This information has been saved to:');
714
- console.log(` ${PROFILES_FILE}`);
715
- console.log(
716
- 'You can edit this file directly to further customize the profile:');
717
- console.log(` vim ${PROFILES_FILE}`);
718
- console.log('Or run ccconfig edit to open it with your preferred editor');
725
+
726
+ // Create auto-execute output manager (RAII-style)
727
+ const output = (() => {
728
+ let executed = false;
729
+ return {
730
+ execute: () => {
731
+ if (!executed) {
732
+ console.log('');
733
+ console.log(`Configuration '${name}' summary:`);
734
+ console.log('');
735
+ console.log('Saved environment variables:');
736
+ displayEnvVars(envVars);
737
+ console.log('');
738
+ console.log(`To start Claude Code with this configuration:`);
739
+ console.log(` ccconfig start ${name}`);
740
+ console.log('');
741
+ console.log('To update this configuration:');
742
+ console.log(` ccconfig update ${name}`);
743
+ console.log('');
744
+ console.log('Configuration saved to:');
745
+ console.log(` ${PROFILES_FILE}`);
746
+ executed = true;
747
+ }
748
+ }
749
+ };
750
+ })();
751
+
752
+ // Attempt to start Claude (if user chose yes), deferring output to exit
753
+ const shouldStartClaude = normalized === 'yes' || normalized === 'y';
754
+ if (shouldStartClaude) {
755
+ start(name, [], {onExit: output.execute});
756
+ } else {
757
+ // If not starting Claude, show output immediately
758
+ output.execute();
759
+ }
719
760
  } finally {
720
761
  helper.close();
721
762
  }
@@ -781,17 +822,20 @@ async function update(name) {
781
822
  /**
782
823
  * Remove configuration
783
824
  */
784
- function remove(name) {
825
+ async function remove(name) {
785
826
  if (!name) {
786
827
  console.error('Error: Missing configuration name');
787
828
  console.error('Usage: ccconfig remove <name>');
788
829
  process.exit(1);
789
830
  }
790
831
 
832
+ // Check if terminal is interactive before proceeding
833
+ requireInteractive('removing configurations');
834
+
791
835
  // Validate configuration name
792
836
  validateConfigName(name);
793
837
 
794
- const {profiles} = ensureProfileAvailable(name, {
838
+ const {profile, profiles} = ensureProfileAvailable(name, {
795
839
  allowEmptyEnv: true,
796
840
  onEmptyProfiles: () => {
797
841
  console.error('Error: Configuration file does not exist');
@@ -801,9 +845,45 @@ function remove(name) {
801
845
  }
802
846
  });
803
847
 
804
- delete getProfilesMap(profiles)[name];
805
- saveProfiles(profiles);
806
- console.log(`✓ Configuration '${name}' removed`);
848
+ // Display profile information before removal
849
+ console.log('');
850
+ console.log('WARNING: PERMANENT DELETION');
851
+ console.log('═══════════════════════════════════════════════════════════');
852
+ console.log('');
853
+ console.log(`Configuration to be removed: ${name}`);
854
+ console.log('');
855
+ console.log('Profile details:');
856
+ if (profile.env && Object.keys(profile.env).length > 0) {
857
+ displayEnvVars(profile.env, true, ' ');
858
+ } else {
859
+ console.log(' (no environment variables configured)');
860
+ }
861
+ console.log('');
862
+ console.log('This action CANNOT be undone!');
863
+ console.log('All configuration data for this profile will be \x1b[1mpermanently deleted\x1b[0m.');
864
+ console.log('');
865
+
866
+ // Ask for confirmation
867
+ const helper = new ReadlineHelper();
868
+ try {
869
+ const confirmation = await helper.ask(
870
+ `Are you sure you want to remove '${name}'? (yes/no)`, 'no');
871
+ const normalized = confirmation.trim().toLowerCase();
872
+
873
+ if (normalized !== 'yes' && normalized !== 'y') {
874
+ console.log('');
875
+ console.log('Operation cancelled.');
876
+ return;
877
+ }
878
+
879
+ // Proceed with removal
880
+ delete getProfilesMap(profiles)[name];
881
+ saveProfiles(profiles);
882
+ console.log('');
883
+ console.log(`✓ Configuration '${name}' removed`);
884
+ } finally {
885
+ helper.close();
886
+ }
807
887
  }
808
888
 
809
889
  /**
@@ -1262,25 +1342,6 @@ function current(showSecret = false) {
1262
1342
  console.log('═══════════════════════════════════════════');
1263
1343
  }
1264
1344
 
1265
- /**
1266
- * Show configuration file path
1267
- */
1268
- function edit() {
1269
- if (!fs.existsSync(PROFILES_FILE)) {
1270
- console.error('Error: Configuration file does not exist');
1271
- console.error('Please add a configuration first: ccconfig add <name>');
1272
- process.exit(1);
1273
- }
1274
-
1275
- const editor = process.env.EDITOR || process.env.VISUAL || 'vim';
1276
-
1277
- console.log('Configuration file path:');
1278
- console.log(` ${PROFILES_FILE}`);
1279
- console.log('');
1280
- console.log('Open it with your preferred editor, for example:');
1281
- console.log(` ${editor} ${PROFILES_FILE}`);
1282
- }
1283
-
1284
1345
  /**
1285
1346
  * Switch/view mode
1286
1347
  */
@@ -1376,9 +1437,10 @@ function env(format = 'bash') {
1376
1437
  * @param {Array} extraArgs - Additional arguments to pass to Claude
1377
1438
  * @param {Object} options - Options object
1378
1439
  * @param {boolean} options.safe - Whether to run in safe mode (default: false)
1440
+ * @param {Function} options.onExit - Callback to execute before process exits
1379
1441
  */
1380
1442
  function startClaude(name, extraArgs = [], options = {}) {
1381
- const {safe = false} = options;
1443
+ const {safe = false, onExit = null} = options;
1382
1444
  const commandName = safe ? 'safe-start' : 'start';
1383
1445
 
1384
1446
  if (!name) {
@@ -1531,6 +1593,11 @@ function startClaude(name, extraArgs = [], options = {}) {
1531
1593
  process.removeListener('SIGINT', signalHandler);
1532
1594
  process.removeListener('SIGTERM', signalHandler);
1533
1595
 
1596
+ // Execute onExit callback before showing promotion message
1597
+ if (typeof onExit === 'function') {
1598
+ onExit();
1599
+ }
1600
+
1534
1601
  // Show project promotion message on exit
1535
1602
  console.log('');
1536
1603
  console.log('──────────────────────────────────────────');
@@ -1566,16 +1633,16 @@ function startClaude(name, extraArgs = [], options = {}) {
1566
1633
  /**
1567
1634
  * Start Claude Code with specified profile (auto-approve mode)
1568
1635
  */
1569
- function start(name, extraArgs = []) {
1570
- return startClaude(name, extraArgs, {safe: false});
1636
+ function start(name, extraArgs = [], options = {}) {
1637
+ return startClaude(name, extraArgs, {safe: false, ...options});
1571
1638
  }
1572
1639
 
1573
1640
  /**
1574
1641
  * Start Claude Code with specified profile (safe mode - requires permission
1575
1642
  * confirmation)
1576
1643
  */
1577
- function safeStart(name, extraArgs = []) {
1578
- return startClaude(name, extraArgs, {safe: true});
1644
+ function safeStart(name, extraArgs = [], options = {}) {
1645
+ return startClaude(name, extraArgs, {safe: true, ...options});
1579
1646
  }
1580
1647
 
1581
1648
  /**
@@ -1663,7 +1730,6 @@ _ccconfig() {
1663
1730
  'current:Display current configuration'
1664
1731
  'mode:View or switch mode'
1665
1732
  'env:Output environment variables'
1666
- 'edit:Show configuration file location'
1667
1733
  )
1668
1734
 
1669
1735
  modes=('settings' 'env')
@@ -1724,7 +1790,6 @@ complete -c ccconfig -f -n "__fish_use_subcommand" -a "rm" -d "Remove configurat
1724
1790
  complete -c ccconfig -f -n "__fish_use_subcommand" -a "current" -d "Display current configuration"
1725
1791
  complete -c ccconfig -f -n "__fish_use_subcommand" -a "mode" -d "View or switch mode"
1726
1792
  complete -c ccconfig -f -n "__fish_use_subcommand" -a "env" -d "Output environment variables"
1727
- complete -c ccconfig -f -n "__fish_use_subcommand" -a "edit" -d "Show configuration file location"
1728
1793
 
1729
1794
  # Get profile names dynamically
1730
1795
  function __ccconfig_profiles
@@ -1776,7 +1841,7 @@ function Get-CconfigProfiles {
1776
1841
  Register-ArgumentCompleter -Native -CommandName ccconfig -ScriptBlock {
1777
1842
  param($wordToComplete, $commandAst, $cursorPosition)
1778
1843
 
1779
- $commands = @('list', 'ls', 'add', 'update', 'use', 'start', 'safe-start', 'remove', 'rm', 'current', 'mode', 'env', 'edit', 'completion')
1844
+ $commands = @('list', 'ls', 'add', 'update', 'use', 'start', 'safe-start', 'remove', 'rm', 'current', 'mode', 'env', 'completion')
1780
1845
  $modes = @('settings', 'env')
1781
1846
  $formats = @('bash', 'zsh', 'fish', 'sh', 'powershell', 'pwsh', 'dotenv')
1782
1847
 
@@ -1889,8 +1954,6 @@ function help() {
1889
1954
  ' mode [settings|env] View or switch mode');
1890
1955
  console.log(
1891
1956
  ' env [format] Output environment variables (env mode)');
1892
- console.log(
1893
- ' edit Show configuration file location');
1894
1957
  console.log(
1895
1958
  ' completion <bash|zsh|fish|pwsh> Generate shell completion script');
1896
1959
  console.log('');
@@ -2001,7 +2064,7 @@ async function main() {
2001
2064
  break;
2002
2065
  case 'remove':
2003
2066
  case 'rm':
2004
- remove(filteredArgs[1]);
2067
+ await remove(filteredArgs[1]);
2005
2068
  break;
2006
2069
  case 'current':
2007
2070
  current(showSecret);
@@ -2012,9 +2075,6 @@ async function main() {
2012
2075
  case 'env':
2013
2076
  env(filteredArgs[1] || 'bash');
2014
2077
  break;
2015
- case 'edit':
2016
- edit();
2017
- break;
2018
2078
  case 'start':
2019
2079
  if (!filteredArgs[1]) {
2020
2080
  console.error('Error: Missing configuration name');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccconfig",
3
- "version": "1.4.3",
3
+ "version": "1.5.0",
4
4
  "description": "Cross-platform Claude Code configuration switching tool",
5
5
  "main": "ccconfig.js",
6
6
  "bin": {