run method

  1. @override
Future<void> run()
override

Runs this command.

The return value is wrapped in a Future if necessary and returned by CommandRunner.runCommand.

Implementation

@override
Future<void> run() async {
  final planFile = File('.spectra/PLAN.md');
  if (!planFile.existsSync()) {
    logger.err('PLAN.md not found. Run `spectra plan` first.');
    return;
  }

  final content = planFile.readAsStringSync();
  final allTasks = _parseTasks(content);

  if (allTasks.isEmpty) {
    logger.warn('No tasks found in PLAN.md.');
    return;
  }

  // Tasks already marked completed (by a previous run) are not re-executed,
  // so `execute` is idempotent and `resume` continues where it left off.
  final taskDocs = allTasks
      .where((d) => d.rootElement.getAttribute('status') != 'completed')
      .toList();
  final alreadyDone = allTasks.length - taskDocs.length;
  if (alreadyDone > 0) {
    logger.detail('Skipping $alreadyDone already-completed task(s).');
  }
  if (taskDocs.isEmpty) {
    logger.success(
      'All ${allTasks.length} tasks in PLAN.md are already '
      'completed. Nothing to execute.',
    );
    return;
  }

  // Determine execution mode
  final config = await _configService.loadConfig();
  final manualFlag = argResults?['manual'] as bool? ?? false;
  final autoFlag = argResults?['auto'] as bool? ?? false;

  final ExecutionMode mode;
  if (manualFlag) {
    mode = ExecutionMode.manual;
  } else if (autoFlag) {
    mode = ExecutionMode.automatic;
  } else {
    // Use config or default to automatic
    final modeStr = config.executionMode ?? 'automatic';
    mode = ExecutionMode.values.firstWhere(
      (m) => m.name == modeStr,
      orElse: () => ExecutionMode.automatic,
    );
  }

  if (mode == ExecutionMode.manual) {
    await _displayTasksForManualExecution(taskDocs);
    return;
  }

  // Use coding provider for actual code generation
  final provider = await _llmService.getProviderForUsage(LLMUsageType.coding);
  if (provider == null) {
    logger.err('No coding provider configured.');
    return;
  }

  final interactive = mode == ExecutionMode.interactive;
  logger.info(
    'Executing ${taskDocs.length} tasks using ${provider.name} '
    '(${interactive ? 'Interactive Review' : 'Code Generation'})...',
  );

  for (final taskDoc in taskDocs) {
    final keepGoing = await _executeTask(
      taskDoc,
      provider,
      interactive: interactive,
    );
    if (!keepGoing) {
      logger.info('Execution stopped by user.');
      return;
    }
  }

  // Prune state if it gets too large
  _stateManager.pruneState();

  logger.success('All tasks executed successfully!');
}