Add manual mode
diff --git a/pkgs/firehose/bin/health.dart b/pkgs/firehose/bin/health.dart
index 122e044..162ab3c 100644
--- a/pkgs/firehose/bin/health.dart
+++ b/pkgs/firehose/bin/health.dart
@@ -7,6 +7,8 @@
 import 'package:args/args.dart';
 import 'package:firehose/src/github.dart';
 import 'package:firehose/src/health/health.dart';
+import 'package:github/src/common/model/repos.dart';
+import 'package:glob/glob.dart';
 
 void main(List<String> arguments) async {
   var checkTypes = Check.values.map((c) => c.name);
@@ -53,7 +55,13 @@
       'flutter_packages',
       defaultsTo: [],
       help: 'The Flutter packages in this repo',
-    );
+    )
+    ..addFlag(
+      'cli_mode',
+      help: 'Whether to use the Github API or manually provide the input.',
+    )
+    ..addOption('pr_body')
+    ..addOption('file_list', defaultsTo: '');
   final parsedArgs = argParser.parse(arguments);
   final checkStr = parsedArgs.option('check');
   final check = Check.values.firstWhere((c) => c.name == checkStr);
@@ -69,8 +77,23 @@
     throw ArgumentError('The checks for which warnings are displayed and the '
         'checks which lead to failure must be disjoint.');
   }
+  var current = Directory.current;
+  GithubApi githubApi;
+  if (parsedArgs.flag('cli_mode')) {
+    final prBody = parsedArgs.option('pr_body');
+    final gitFiles = _listNonEmpty(parsedArgs, 'file_list')
+        .map((e) => GitFile(e, FileStatus.modified, current))
+        .toList();
+    githubApi = ManualFileApi(
+      prBody: prBody ?? '',
+      files: gitFiles,
+      prLabels: [],
+    );
+  } else {
+    githubApi = GithubApi();
+  }
   await Health(
-    Directory.current,
+    current,
     check,
     warnOn,
     failOn,
@@ -79,10 +102,70 @@
     ignoreLicense,
     ignoreCoverage,
     experiments,
-    GithubApi(),
+    githubApi,
     flutterPackages,
   ).healthCheck();
 }
 
 List<String> _listNonEmpty(ArgResults parsedArgs, String key) =>
-    (parsedArgs[key] as List<String>).where((e) => e.isNotEmpty).toList();
+    parsedArgs.multiOption(key).where((option) => option.isNotEmpty).toList();
+
+class ManualFileApi implements GithubApi {
+  final String prBody;
+  final List<GitFile> files;
+
+  @override
+  final List<String> prLabels;
+
+  ManualFileApi({
+    required this.prBody,
+    required this.files,
+    required this.prLabels,
+  });
+
+  @override
+  String? get actor => throw UnimplementedError();
+
+  @override
+  void appendStepSummary(String markdownSummary) {}
+
+  @override
+  String? get baseRef => throw UnimplementedError();
+
+  @override
+  void close() {}
+
+  @override
+  Future<int?> findCommentId({required String user, String? searchTerm}) {
+    throw UnimplementedError();
+  }
+
+  @override
+  String? get githubAuthToken => null;
+
+  @override
+  bool get inGithubContext => false;
+
+  @override
+  Future<List<GitFile>> listFilesForPR(Directory directory,
+          [List<Glob> ignoredFiles = const []]) async =>
+      files;
+
+  @override
+  void notice({required String message}) {}
+
+  @override
+  Future<String> pullrequestBody() async => prBody;
+
+  @override
+  String? get refName => throw UnimplementedError();
+
+  @override
+  RepositorySlug? get repoSlug => RepositorySlug('owner', 'name');
+
+  @override
+  int? get issueNumber => -1;
+
+  @override
+  String? get sha => '';
+}
diff --git a/pkgs/firehose/lib/src/health/health.dart b/pkgs/firehose/lib/src/health/health.dart
index 3b8b106..fc3cba8 100644
--- a/pkgs/firehose/lib/src/health/health.dart
+++ b/pkgs/firehose/lib/src/health/health.dart
@@ -100,19 +100,19 @@
     log(' experiments: $experiments');
     log('Checking for $checkName');
     if (!github.prLabels.contains('skip-$checkName-check')) {
-      final firstResult = await checkFor(check)();
-      final HealthCheckResult finalResult;
+      final firstResult = await checkFor(check).call();
+      final HealthCheckResult result;
       if (warnOn.contains(check.name) &&
           firstResult.severity == Severity.error) {
-        finalResult = firstResult.withSeverity(Severity.warning);
+        result = firstResult.withSeverity(Severity.warning);
       } else if (failOn.contains(check.name) &&
           firstResult.severity == Severity.warning) {
-        finalResult = firstResult.withSeverity(Severity.error);
+        result = firstResult.withSeverity(Severity.error);
       } else {
-        finalResult = firstResult;
+        result = firstResult;
       }
-      await writeInComment(github, finalResult);
-      var severity = finalResult.severity.name.toUpperCase();
+      await writeInComment(github, result);
+      var severity = result.severity.name.toUpperCase();
       log('\n\n$severity: $checkName done.\n\n');
     } else {
       log('Skipping $checkName, as the skip tag is present.');
diff --git a/pkgs/firehose/test/health_test.dart b/pkgs/firehose/test/health_test.dart
index 0c830cd..d83411c 100644
--- a/pkgs/firehose/test/health_test.dart
+++ b/pkgs/firehose/test/health_test.dart
@@ -4,49 +4,51 @@
 
 import 'dart:io';
 
-import 'package:collection/collection.dart';
 import 'package:firehose/src/github.dart';
 import 'package:firehose/src/health/health.dart';
-import 'package:github/src/common/model/repos.dart';
-import 'package:glob/glob.dart';
 import 'package:path/path.dart' as p;
 import 'package:test/test.dart';
 
+import '../bin/health.dart';
+
 Future<void> main() async {
   late final Directory directory;
-  late final FakeGithubApi Function(List<GitFile> additional) fakeGithubApi;
+  late final ManualFileApi Function(List<GitFile> additional) fakeGithubApi;
 
   setUpAll(() async {
     directory = Directory(p.join('test_data', 'test_repo'));
-    fakeGithubApi =
-        (List<GitFile> additional) => FakeGithubApi(prLabels: [], files: [
-              GitFile(
-                'pkgs/package1/bin/package1.dart',
-                FileStatus.modified,
-                directory,
-              ),
-              GitFile(
-                'pkgs/package2/lib/anotherLib.dart',
-                FileStatus.added,
-                directory,
-              ),
-              GitFile(
-                'pkgs/package2/someImage.png',
-                FileStatus.added,
-                directory,
-              ),
-              GitFile(
-                'pkgs/package5/lib/src/package5_base.dart',
-                FileStatus.modified,
-                directory,
-              ),
-              GitFile(
-                'pkgs/package5/pubspec.yaml',
-                FileStatus.modified,
-                directory,
-              ),
-              ...additional
-            ]);
+    fakeGithubApi = (List<GitFile> additional) => ManualFileApi(
+          prBody: '',
+          prLabels: [],
+          files: [
+            GitFile(
+              'pkgs/package1/bin/package1.dart',
+              FileStatus.modified,
+              directory,
+            ),
+            GitFile(
+              'pkgs/package2/lib/anotherLib.dart',
+              FileStatus.added,
+              directory,
+            ),
+            GitFile(
+              'pkgs/package2/someImage.png',
+              FileStatus.added,
+              directory,
+            ),
+            GitFile(
+              'pkgs/package5/lib/src/package5_base.dart',
+              FileStatus.modified,
+              directory,
+            ),
+            GitFile(
+              'pkgs/package5/pubspec.yaml',
+              FileStatus.modified,
+              directory,
+            ),
+            ...additional
+          ],
+        );
 
     await Process.run('dart', ['pub', 'global', 'activate', 'dart_apitool']);
     await Process.run('dart', ['pub', 'global', 'activate', 'coverage']);
@@ -110,7 +112,7 @@
 
 Future<void> checkGolden(
   Check check,
-  FakeGithubApi fakeGithubApi,
+  GithubApi githubApi,
   Directory directory, {
   String suffix = '',
   List<String> ignoredLicense = const [],
@@ -129,7 +131,7 @@
     ignoredLicense,
     [],
     [],
-    fakeGithubApi,
+    githubApi,
     flutterPackages,
     base: Directory(p.join('test_data', 'base_test_repo')),
     comment: commentPath,
@@ -144,65 +146,3 @@
     expect(comment, goldenFile.readAsStringSync());
   }
 }
-
-class FakeGithubApi implements GithubApi {
-  final List<GitFile> files;
-
-  FakeGithubApi({
-    required this.prLabels,
-    required this.files,
-  });
-
-  @override
-  String? get actor => throw UnimplementedError();
-
-  @override
-  void appendStepSummary(String markdownSummary) {}
-
-  @override
-  String? get baseRef => throw UnimplementedError();
-
-  @override
-  void close() {}
-
-  @override
-  Future<int?> findCommentId({required String user, String? searchTerm}) {
-    throw UnimplementedError();
-  }
-
-  @override
-  String? get githubAuthToken => throw UnimplementedError();
-
-  @override
-  bool get inGithubContext => throw UnimplementedError();
-
-  @override
-  int? get issueNumber => 1;
-
-  @override
-  Future<List<GitFile>> listFilesForPR(Directory directory,
-      [List<Glob> ignoredFiles = const []]) async {
-    return files
-        .where((element) =>
-            ignoredFiles.none((p0) => p0.matches(element.filename)))
-        .toList();
-  }
-
-  @override
-  void notice({required String message}) {}
-
-  @override
-  final List<String> prLabels;
-
-  @override
-  Future<String> pullrequestBody() async => 'Test body';
-
-  @override
-  String? get refName => throw UnimplementedError();
-
-  @override
-  RepositorySlug? get repoSlug => RepositorySlug('test_owner', 'test_repo');
-
-  @override
-  String? get sha => 'test_sha';
-}