runDoctor function

void runDoctor()

river_cli doctor — a one-shot health check of the current Flutter project: SDK availability, project metadata, installed modules, key dependencies, scaffolded features, and whether the Claude Code skill is installed.

Implementation

void runDoctor() {
  print('river_cli doctor (v$kRiverCliVersion)\n');

  var problems = 0;
  void ok(String msg) => print('  ✓ $msg');
  void warn(String msg) {
    print('  ! $msg');
    problems++;
  }

  void fail(String msg) {
    print('  ✗ $msg');
    problems++;
  }

  // --- Environment ---
  print('Environment');
  if (Utils.isFlutterAvailable()) {
    ok('Flutter is on PATH');
  } else {
    warn('Flutter not found on PATH (package versions cannot auto-resolve)');
  }

  if (!ProjectContext.isFlutterRoot) {
    fail('pubspec.yaml not found — run inside a Flutter project root');
    print('\nFound $problems issue(s).');
    exit(1);
  }
  ok('pubspec.yaml found');

  final ctx = ProjectContext.detect();
  print('\nProject');
  if (ctx.packageName != null) {
    ok('package: ${ctx.packageName}');
  } else {
    warn('could not read package name from pubspec.yaml');
  }

  // --- Modules ---
  print('\nModules installed');
  final modules = <String, bool>{
    'config': ctx.hasConfig,
    'utils': ctx.hasUtils,
    'extensions': ctx.hasExtensions,
    'widgets': ctx.hasWidgets,
    'network': ctx.hasNetwork,
    'storage': ctx.hasStorage,
  };
  final anyModule = modules.values.any((v) => v);
  if (!anyModule) {
    warn('no river_cli modules detected — run "river_cli init" to scaffold');
  } else {
    modules.forEach((key, present) {
      print('  ${present ? '✓' : '·'} $key');
    });
  }

  // --- Core dependencies ---
  print('\nCore dependencies');
  final pubspec = File('pubspec.yaml').readAsStringSync();
  for (final dep in ['flutter_riverpod', 'go_router']) {
    if (RegExp('^\\s+$dep:', multiLine: true).hasMatch(pubspec)) {
      ok('$dep present');
    } else {
      warn('$dep missing from pubspec.yaml');
    }
  }

  // --- Routing ---
  print('\nRouting');
  if (File('lib/app/routes/route_page.dart').existsSync()) {
    ok('go_router config found (lib/app/routes/route_page.dart)');
  } else {
    warn('no routing file yet — created on first "river_cli create page:"');
  }

  // --- Features ---
  print('\nFeatures');
  final features = ProjectContext.findFeatures();
  if (features.isEmpty) {
    print('  · none scaffolded yet');
  } else {
    for (final f in features) {
      print('  ✓ $f');
    }
  }

  // --- Claude Code skill ---
  print('\nClaude Code skill');
  final localSkill =
      File('.claude/skills/river-cli-flutter/SKILL.md').existsSync();
  final home =
      Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
  final globalSkill = home != null &&
      File('$home/.claude/skills/river-cli-flutter/SKILL.md').existsSync();
  if (localSkill) {
    ok('installed in this project (.claude/skills/river-cli-flutter)');
  } else if (globalSkill) {
    ok('installed globally (~/.claude/skills/river-cli-flutter)');
  } else {
    warn(
        'not installed — run "river_cli skill" to enable AI-assisted workflow');
  }

  // --- Summary ---
  print('\n${'=' * 40}');
  if (problems == 0) {
    print('All checks passed. You are good to go!');
  } else {
    print('$problems item(s) need attention (see ! and ✗ above).');
  }
}