run method

Future<void> run(
  1. List<String> arguments
)

Implementation

Future<void> run(List<String> arguments) async {
  // Handle help and version flags
  if (arguments.isNotEmpty) {
    final firstArg = arguments[0].toLowerCase();
    if (firstArg == '--help' || firstArg == '-h' || firstArg == 'help') {
      printUsage();
      return;
    }
    if (firstArg == '--version' || firstArg == '-v' || firstArg == 'version') {
      printVersion();
      return;
    }
  }

  if (arguments.isEmpty) {
    printUsage();
    return;
  }

  final firstArg = arguments[0];
  final firstArgLower = firstArg.toLowerCase();

  // Reserved commands that cannot be feature names
  final reservedCommands = [
    'list',
    'tree',
    'init',
    'remove',
    'delete',
    'del',
    'rm',
    'rename',
    'mv',
    'ren',
    'clean',
    'config',
    'setup',
    'health',
  ];

  // Check if it's a reserved command first (case-insensitive)
  if (reservedCommands.contains(firstArgLower)) {
    if (firstArgLower == 'list') {
      await _listFeatures();
      return;
    }

    if (firstArgLower == 'tree') {
      await TreeManager.showTree();
      return;
    }

    if (firstArgLower == 'health') {
      await HealthManager.checkHealth();
      return;
    }

    if (firstArgLower == 'init') {
      // Check if app name is provided as second argument
      String? appName;
      if (arguments.length > 1) {
        appName = arguments[1];
        // Validate app name (lowercase, underscores only, no spaces or special characters)
        if (!RegExp(r'^[a-z][a-z0-9_]*$').hasMatch(appName)) {
          Logger.error('Invalid app name: $appName');
          Logger.info('App name must be lowercase, with underscores only, no spaces or special characters.');
          return;
        }
      }
      await InitManager.initialize(appName: appName);
      return;
    }

    // For other reserved commands, continue to their specific handlers below
    // They will be handled with case-insensitive checks
  }

  // Continue with original case for feature name
  final featureName = firstArg;

  // Handle remove command (case-insensitive)
  if (firstArgLower == 'remove' || firstArgLower == 'delete' || firstArgLower == 'del' || firstArgLower == 'rm') {
    if (arguments.length < 2) {
      Logger.error('Please specify a feature name to delete.');
      Logger.info('Usage: flarch delete <FeatureName>');
      return;
    }
    final featureToRemove = arguments[1];
    await _removeFeature(featureToRemove);
    return;
  }

  // Handle rename command (case-insensitive)
  if (firstArgLower == 'rename' || firstArgLower == 'mv' || firstArgLower == 'ren') {
    if (arguments.length < 2) {
      Logger.error('Please specify a feature name to rename.');
      Logger.info('Usage: flarch rename <OldFeatureName> [NewFeatureName]');
      Logger.info('  Examples:');
      Logger.info('    flarch rename old_feature new_feature');
      Logger.info('    flarch rename old_feature  (interactive mode)');
      return;
    }
    final oldFeatureName = arguments[1];
    final newFeatureName = arguments.length > 2 ? arguments[2] : null;
    await FeatureRenamer.renameFeature(oldFeatureName, newFeatureName: newFeatureName);
    return;
  }

  // Handle clean command (case-insensitive)
  if (firstArgLower == 'clean') {
    if (arguments.length < 2) {
      Logger.error('Please specify what to clean.');
      Logger.info('Usage: flarch clean pubspec');
      Logger.info('Available options: pubspec (cleans pubspec.yaml comments)');
      return;
    }
    final cleanTarget = arguments[1].toLowerCase();
    if (cleanTarget == 'pubspec' || cleanTarget == 'pubspec.yaml') {
      await PubspecCleaner.cleanPubspec();
    } else {
      Logger.error('Unknown clean target: $cleanTarget');
      Logger.info('Available options: pubspec');
    }
    return;
  }

  // Handle config command (case-insensitive)
  if (firstArgLower == 'config') {
    if (arguments.length < 2) {
      Logger.error('Please specify what to configure.');
      Logger.info(
          'Usage: flarch config assets | flarch config main | flarch config theme | flarch config router | flarch config storage | flarch config package [id] | flarch config name [name]');
      Logger.info('Available options:');
      Logger.info('  assets - Setup assets folder structure');
      Logger.info('  main - Clean main.dart and create app.dart');
      Logger.info('  theme - Setup theme configuration with light/dark themes');
      Logger.info('  router - Setup GoRouter configuration');
      Logger.info('  storage - Setup local storage (Hive, SharedPreferences, ObjectBox, Isar, Drift)');
      Logger.info('  package [id] - Replace application package ID');
      Logger.info('    Examples:');
      Logger.info('      flarch config package com.newpackage.app');
      Logger.info('      flarch config package --interactive');
      Logger.info('      flarch config package com.newpackage.app --backup');
      Logger.info('  name [name] - Change app display name');
      Logger.info('    Examples:');
      Logger.info('      flarch config name "My New App"');
      Logger.info('      flarch config name --interactive');
      Logger.info('      flarch config name "My New App" --backup');
      return;
    }
    final configTarget = arguments[1].toLowerCase();

    if (configTarget == 'assets') {
      await AssetConfigManager.setupAssets();
    } else if (configTarget == 'main') {
      await MainConfigManager.configureMain();
    } else if (configTarget == 'theme') {
      await ThemeConfigManager.configureTheme();
    } else if (configTarget == 'router') {
      await RouterConfigManager.configureRouter();
    } else if (configTarget == 'storage' || configTarget == 'local storage') {
      await StorageConfigManager.configureStorage();
    } else if (configTarget == 'package') {
      // Parse package command options
      bool interactive = arguments.contains('--interactive') || arguments.contains('-i');
      bool createBackup = arguments.contains('--backup') || arguments.contains('-b');
      bool dryRun = arguments.contains('--dry-run') || arguments.contains('--preview');

      // Get package ID from arguments (skip flags)
      String? newPackageId;
      for (int i = 2; i < arguments.length; i++) {
        final arg = arguments[i];
        if (!arg.startsWith('--') && arg != '-i' && arg != '-b') {
          newPackageId = arg;
          break;
        }
      }

      await PackageIdManager.replacePackageId(
        newPackageId: newPackageId,
        interactive: interactive || newPackageId == null,
        createBackup: createBackup,
        dryRun: dryRun,
      );
    } else if (configTarget == 'name') {
      // Parse app name command options
      bool interactive = arguments.contains('--interactive') || arguments.contains('-i');
      bool createBackup = arguments.contains('--backup') || arguments.contains('-b');
      bool dryRun = arguments.contains('--dry-run') || arguments.contains('--preview');

      // Get app name from arguments (skip flags)
      // App name might have spaces, so we need to handle quoted strings
      String? newAppName;
      for (int i = 2; i < arguments.length; i++) {
        final arg = arguments[i];
        if (!arg.startsWith('--') && arg != '-i' && arg != '-b') {
          // If it starts with a quote, collect until we find the closing quote
          if (arg.startsWith('"') || arg.startsWith("'")) {
            final quote = arg[0];
            String collected = arg.substring(1); // Remove opening quote

            // If it ends with the same quote, we're done
            if (collected.endsWith(quote)) {
              newAppName = collected.substring(0, collected.length - 1);
              break;
            }

            // Otherwise, collect more arguments until we find the closing quote
            for (int j = i + 1; j < arguments.length; j++) {
              collected += ' ${arguments[j]}';
              if (arguments[j].endsWith(quote)) {
                newAppName = collected.substring(0, collected.length - 1);
                break;
              }
            }
            if (newAppName != null) break;
          } else {
            newAppName = arg;
            break;
          }
        }
      }

      await AppNameManager.replaceAppName(
        newAppName: newAppName,
        interactive: interactive || newAppName == null,
        createBackup: createBackup,
        dryRun: dryRun,
      );
    } else {
      Logger.error('Unknown config target: $configTarget');
      Logger.info('Available options: assets, main, theme, router, storage, package, name');
    }
    return;
  }

  // Handle setup command (alias for config, case-insensitive)
  if (firstArgLower == 'setup') {
    if (arguments.length < 2) {
      Logger.error('Please specify what to setup.');
      Logger.info('Usage: flarch setup assets | flarch setup package [new.package.id]');
      Logger.info('Available options:');
      Logger.info('  assets - Setup assets folder structure');
      Logger.info('  package [id] - Replace application package ID');
      return;
    }
    final setupTarget = arguments[1].toLowerCase();

    if (setupTarget == 'assets') {
      await AssetConfigManager.setupAssets();
    } else if (setupTarget == 'package') {
      // Parse package command options
      bool interactive = arguments.contains('--interactive') || arguments.contains('-i');
      bool createBackup = arguments.contains('--backup') || arguments.contains('-b');
      bool dryRun = arguments.contains('--dry-run') || arguments.contains('--preview');

      // Get package ID from arguments (skip flags)
      String? newPackageId;
      for (int i = 2; i < arguments.length; i++) {
        final arg = arguments[i];
        if (!arg.startsWith('--') && arg != '-i' && arg != '-b') {
          newPackageId = arg;
          break;
        }
      }

      await PackageIdManager.replacePackageId(
        newPackageId: newPackageId,
        interactive: interactive || newPackageId == null,
        createBackup: createBackup,
        dryRun: dryRun,
      );
    } else if (setupTarget == 'name') {
      // Parse app name command options
      bool interactive = arguments.contains('--interactive') || arguments.contains('-i');
      bool createBackup = arguments.contains('--backup') || arguments.contains('-b');
      bool dryRun = arguments.contains('--dry-run') || arguments.contains('--preview');

      // Get app name from arguments (skip flags)
      String? newAppName;
      for (int i = 2; i < arguments.length; i++) {
        final arg = arguments[i];
        if (!arg.startsWith('--') && arg != '-i' && arg != '-b') {
          // Handle quoted strings for app names with spaces
          if (arg.startsWith('"') || arg.startsWith("'")) {
            final quote = arg[0];
            String collected = arg.substring(1);
            if (collected.endsWith(quote)) {
              newAppName = collected.substring(0, collected.length - 1);
              break;
            }
            for (int j = i + 1; j < arguments.length; j++) {
              collected += ' ${arguments[j]}';
              if (arguments[j].endsWith(quote)) {
                newAppName = collected.substring(0, collected.length - 1);
                break;
              }
            }
            if (newAppName != null) break;
          } else {
            newAppName = arg;
            break;
          }
        }
      }

      await AppNameManager.replaceAppName(
        newAppName: newAppName,
        interactive: interactive || newAppName == null,
        createBackup: createBackup,
        dryRun: dryRun,
      );
    } else {
      Logger.error('Unknown setup target: $setupTarget');
      Logger.info('Available options: assets, package, name');
    }
    return;
  }

  if (arguments.length > 1) {
    if (arguments.contains('-sm')) {
      await _handleOption(arguments, featureName);
      return;
    }

    if (_featureExists(featureName)) {
      await _handleOption(arguments, featureName);
      return;
    }
  }

  if (_featureExists(featureName) && arguments.length == 1) {
    Logger.error('Feature "$featureName" already exists.');
    return;
  }

  if (arguments.length > 1 && ['-mvc', '-mvvm', '-clean'].contains(arguments[1])) {
    final architecture = arguments[1].substring(1);
    final stateManagement = await _promptForStateManagement();
    final customClassName = stateManagement != null ? _promptForCustomClassName() : null;

    await GetItManager.manageGetIt(featureName, stateManagement, architecture);
    _createFeatureStructure(featureName, stateManagement, customClassName, architecture);
    Logger.success('Feature "$featureName" created successfully with $architecture architecture');
    return;
  }

  await _createFeatureWithPrompt(featureName);
}