// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® module Crafter.Review:Driver_impl; import :Driver; import :Core; import :Platform; import :Process; import :Inventory; import :Config; import std; namespace fs = std::filesystem; namespace Crafter::Review { namespace { constexpr std::string_view Usage = R"(usage: crafter-review [--config FILE] {run,freeze,slots,config} ... crafter-review {seats,inventory,sql,totp,init,doc,browser,stop} ... crafter-review --version The pre-push review pipeline: adversarial lenses, one claude process per inventory unit, against a frozen commit range.)"; std::int32_t PrintVersion() { std::println("{}", DriverLine()); try { std::println("home {}", Home().string()); for (const auto& [name, sha] : CharterHashes()) std::println("charter {} {}", name, sha); } catch (const std::exception& e) { std::println("home not found: {}", e.what()); } return 0; } std::int32_t UsageError(std::string_view message) { std::println(std::cerr, "{}\ncrafter-review: error: {}", Usage, message); return 3; } } std::int32_t Main(std::span args) { const std::span rest = args.empty() ? args : args.subspan(1); // The supervisor first and alone: it is single-threaded and has to // stay so, and it runs between the driver and claude, not as a // command anyone types. if (!rest.empty() && rest.front() == Process::SuperviseVerb) return Process::SuperviseMain(rest.subspan(1)); Platform::Init(); // The options before the command: --config FILE or --config=FILE, and // any unique prefix of a long option. std::optional configPath; std::size_t at = 0; while (at < rest.size() && rest[at].starts_with("--") && rest[at] != "--") { const std::string_view arg = rest[at]; const std::size_t equals = arg.find('='); const std::string_view name = arg.substr(0, equals); if (name.size() > 2 && std::string_view("--config").starts_with(name)) { if (equals != std::string_view::npos) { configPath = std::string(arg.substr(equals + 1)); } else if (at + 1 < rest.size()) { configPath = std::string(rest[++at]); } else { return UsageError("argument --config: expected one argument"); } } else if (name.size() > 2 && std::string_view("--version").starts_with(name)) { return PrintVersion(); } else if (name.size() > 2 && std::string_view("--help").starts_with(name)) { std::println("{}", Usage); return 0; } else { return UsageError(std::format("unrecognized arguments: {}", arg)); } ++at; } if (at < rest.size() && rest[at] == "-h") { std::println("{}", Usage); return 0; } if (at >= rest.size()) return UsageError("a command is required"); const std::string_view command = rest[at]; const std::span after = rest.subspan(at + 1); if (command == "inventory" && !configPath) return Inventory::Main(after); if (command == "config") { if (!after.empty()) return UsageError(std::format("unrecognized arguments: {}", after.front())); return Config::Main(fs::current_path(), configPath ? fs::path(*configPath) : fs::path()); } return UsageError(std::format("argument cmd: invalid choice: '{}'", command)); } }