async function cli({ cwd = process.cwd(), providenceConf, argv = process.argv }) {
  /** @type {(value: any) => void} */
  let resolveCli;
  /** @type {(reason?: any) => void} */
  let rejectCli;

  const cliPromise = new Promise((resolve, reject) => {
    resolveCli = resolve;
    rejectCli = reject;
  });

  /** @type {object} */
  let analyzerOptions;

  // TODO: change back to "InputDataService.getExternalConfig();" once full package ESM
  const externalConfig = providenceConf;

  /**
   * @param {{analyzerOptions:{name:AnalyzerName; config:object;promptOptionalConfig:object}}} opts
   */
  async function getQueryConfigAndMeta(opts) {
    let queryConfig = null;
    let queryMethod = null;

    // eslint-disable-next-line prefer-const
    let { name, config } = opts.analyzerOptions;
    if (!name) {
      throw new Error('Please provide an analyzer name');
    }
    // Will get metaConfig from ./providence.conf.js
    const metaConfig = externalConfig ? externalConfig.metaConfig : {};
    config = { ...config, metaConfig };
    queryConfig = await QueryService.getQueryConfigFromAnalyzer(name, config);
    queryMethod = 'ast';
    return { queryConfig, queryMethod };
  }

  async function launchProvidence() {
    const { queryConfig, queryMethod } = await getQueryConfigAndMeta({ analyzerOptions });

    const searchTargetPaths = commander.searchTargetCollection || commander.searchTargetPaths;
    let referencePaths;
    if (queryConfig.analyzer.requiresReference) {
      referencePaths = commander.referenceCollection || commander.referencePaths;
    }

    /**
     * May or may not include dependencies of search target
     * @type {string[]}
     */
    let totalSearchTargets;
    if (commander.targetDependencies !== undefined) {
      totalSearchTargets = await _cliHelpersModule.appendProjectDependencyPaths(
        searchTargetPaths,
        commander.targetDependencies,
      );
    } else {
      totalSearchTargets = searchTargetPaths;
    }

    // TODO: filter out:
    // - dependencies listed in reference (?) Or at least, inside match-imports, make sure that
    //   we do not test against ourselves...
    // -

    _providenceModule.providence(queryConfig, {
      gatherFilesConfig: {
        extensions: commander.extensions,
        allowlistMode: commander.allowlistMode,
        allowlist: commander.allowlist,
      },
      gatherFilesConfigReference: {
        extensions: commander.extensions,
        allowlistMode: commander.allowlistModeReference,
        allowlist: commander.allowlistReference,
      },
      debugEnabled: commander.debug,
      queryMethod,
      targetProjectPaths: totalSearchTargets,
      referenceProjectPaths: referencePaths,
      targetProjectRootPaths: searchTargetPaths,
      writeLogFile: commander.writeLogFile,
      skipCheckMatchCompatibility: commander.skipCheckMatchCompatibility,
      measurePerformance: commander.measurePerf,
      addSystemPathsInResult: commander.addSystemPaths,
      fallbackToBabel: commander.fallbackToBabel,
    });
  }

  commander
    .version(version, '-v, --version')
    .option('-e, --extensions [extensions]', 'extensions like "js,html"', extensionsFromCs, [
      '.js',
      '.html',
    ])
    .option('-D, --debug', 'shows extensive logging')
    .option(
      '-t, --search-target-paths [targets]',
      `path(s) to project(s) on which analysis/querying should take place. Requires
    a list of comma seperated values relative to project root`,
      v => _cliHelpersModule.pathsArrayFromCs(v, cwd),
      targetDefault(cwd),
    )
    .option(
      '-r, --reference-paths [references]',
      `path(s) to project(s) which serve as a reference (applicable for certain analyzers like
    'match-imports'). Requires a list of comma seperated values relative to
    project root (like 'node_modules/lion-based-ui, node_modules/lion-based-ui-labs').`,
      v => _cliHelpersModule.pathsArrayFromCs(v, cwd),
      InputDataService.referenceProjectPaths,
    )
    .option('-a, --allowlist [allowlist]', `allowlisted paths, like 'src/**/*, packages/**/*'`, v =>
      _cliHelpersModule.csToArray(v),
    )
    .option(
      '--allowlist-reference [allowlist-reference]',
      `allowed paths for reference, like 'src/**/*, packages/**/*'`,
      v => _cliHelpersModule.csToArray(v),
    )
    .option(
      '--search-target-collection [collection-name]',
      `path(s) to project(s) which serve as a reference (applicable for certain analyzers like
    'match-imports'). Should be a collection defined in providence.conf.js as paths relative to
    project root.`,
      v => _cliHelpersModule.pathsArrayFromCollectionName(v, 'search-target', externalConfig),
    )
    .option(
      '--reference-collection [collection-name]',
      `path(s) to project(s) on which analysis/querying should take place. Should be a collection
    defined in providence.conf.js as paths relative to project root.`,
      v => _cliHelpersModule.pathsArrayFromCollectionName(v, 'reference', externalConfig),
    )
    .option('--write-log-file', `Writes all logs to 'providence.log' file`)
    .option(
      '--target-dependencies [target-dependencies]',
      `For all search targets, will include all its dependencies
    (node_modules and bower_components). When --target-dependencies is applied
    without argument, it will act as boolean and include all dependencies.
    When a regex is supplied like --target-dependencies /^my-brand-/, it will filter
    all packages that comply with the regex`,
    )
    .option(
      '--allowlist-mode [allowlist-mode]',
      `Depending on whether we are dealing with a published artifact
      (a dependency installed via npm) or a git repository, different paths will be
      automatically put in the appropiate mode.
      A mode of 'npm' will look at the package.json "files" entry and a mode of
      'git' will look at '.gitignore' entry. A mode of 'export-map' will look for all paths
      exposed via an export map.
      The mode will be auto detected, but can be overridden
      via this option.`,
    )
    .option(
      '--allowlist-mode-reference [allowlist-mode-reference]',
      `allowlist mode applied to refernce project`,
    )
    .option(
      '--skip-check-match-compatibility',
      `skips semver checks, handy for forward compatible libs or libs below v1`,
    )
    .option('--measure-perf', 'Logs the completion time in seconds')
    .option('--add-system-paths', 'Adds system paths to results')
    .option(
      '--fallback-to-babel',
      'Uses babel instead of swc. This will be slower, but guaranteed to be 100% compatible with @babel/generate and @babel/traverse',
    );

  commander
    .command('analyze [analyzer-name]')
    .alias('a')
    .description(
      `predefined "query" for ast analysis. Can be a script found in program/analyzers,
    like "find-imports"`,
    )
    .option(
      '-o, --prompt-optional-config',
      `by default, only required configuration options are
    asked for. When this flag is provided, optional configuration options are shown as well`,
    )
    .option('-c, --config [config]', 'configuration object for analyzer', c => JSON.parse(c))
    .action((analyzerName, options) => {
      analyzerOptions = options;
      analyzerOptions.name = analyzerName;
      launchProvidence().then(resolveCli).catch(rejectCli);
    });

  commander.parse(argv);

  await cliPromise;
}
@param {{cwd?:string; argv?: string[]; providenceConf?: Partial<ProvidenceCliConf>}} cfg