//! `impeccable` binary: verb router. //! //! Every skill script and CLI subcommand is a verb here. Verb crates expose //! `run(args: &[String], io: &mut Io) -> i32` (exit code) and never call //! `std::process::exit` themselves, so this file is the single place exit codes //! and stream flushing are decided (contract: docs/CLI-CONTRACT.md in the //! public repo). Verb names are the JS script basenames; a few carry aliases //! (`signals` for context-signals, `hooks` for hook-admin). use std::io::Write; use impeccable_common::Io; mod font_render; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); fn main() { let args: Vec = std::env::args().skip(1).collect(); let mut io = Io::stdio(); let code = run(&args, &mut io); let _ = io.stdout.flush(); let _ = io.stderr.flush(); std::process::exit(code); } fn run(args: &[String], io: &mut Io) -> i32 { // cli/bin/cli.js dispatch: help / version / detect / ignores / skills verbs let Some(verb) = args.first().map(String::as_str) else { io.out(impeccable_detect::ROOT_USAGE); return 0; }; let rest = &args[1..]; match verb { "--help" | "-h" => { io.out(impeccable_detect::ROOT_USAGE); 0 } "--version" | "-v" => { io.out(&format!("{CLI_VERSION}\n")); 0 } // Launcher handshake: a cheap discriminator so the launchers can tell // this engine apart from the retired 3.x npm CLI (which answers any // unknown verb with `Unknown command`, exit 1) before exec'ing a // candidate found on PATH or in the unversioned user cache. Kept out // of --help on purpose; not part of the user-facing contract. "engine-probe" => { io.out(&format!("impeccable-engine {VERSION}\n")); 0 } "detect" => impeccable_detect::run_detect(rest, io, &engines()), "ignores" | "ignore" => impeccable_detect::run_ignores(rest, io), "skills" => impeccable_skills::run(rest, io), "help" | "install" | "link" | "update" | "check" => impeccable_skills::run(args, io), // skill scripts "context" => impeccable_context::run_context(rest, io), "pin" => impeccable_context::run_pin(rest, io), "detect-csp" => impeccable_context::run_detect_csp(rest, io), "palette" => impeccable_context::run_palette(rest, io), "surface-brief" => impeccable_context::run_surface_brief(rest, io), "critique-storage" => impeccable_context::run_critique_storage(rest, io), "embed-prompt" => impeccable_context::run_embed_prompt(rest, io), "signals" | "context-signals" => impeccable_context::run_signals(rest, io), "doctor" => impeccable_context::run_doctor(rest, io), "concept-seed" => impeccable_context::run_concept_seed(rest, io), "generate-image" => impeccable_context::run_generate_image(rest, io), "serve-question" => impeccable_context::run_serve_question(rest, io), // comp-fidelity verbs (crates/comp-verbs over crates/comp) "comp-spec" => impeccable_comp_verbs::run_comp_spec(rest, io), "comp-diff" => impeccable_comp_verbs::run_comp_diff(rest, io), "font-match" => { let mut renderer = font_render::CdpFontRenderer::from_process_env(); impeccable_comp_verbs::run_font_match(rest, io, &mut renderer) } "build-phase" => { // Inject the organic-clip-path CSS scanner (a rule that lives in the // closed `core` crate) so comp-verbs stays core-free. let organic = |html: &str| -> Vec<(Option, String)> { impeccable_core::checks::css_scan::scan_css_text_for_organic_clip_path(html) .into_iter() .map(|f| (f.selector, f.snippet)) .collect() }; impeccable_comp_verbs::run_build_phase(rest, io, &organic) } "hook" => impeccable_hook::run_hook(rest, io, engines().html), "hook-before-edit" => impeccable_hook::run_hook_before_edit(rest, io, engines().html), "hooks" | "hook-admin" => impeccable_hook::run_hook_admin(rest, io), v if v.starts_with("live") => impeccable_live::run(v, rest, io), // `npx impeccable src/` shorthand: a path-shaped, flag, URL, or existing // first arg is a detect target (cli.js looksLikeDetectTarget). v if impeccable_detect::looks_like_detect_target(v, &io.cwd.to_string_lossy()) => { impeccable_detect::run_detect(args, io, &engines()) } "init" => { io.err(impeccable_detect::INIT_MESSAGE); 1 } other => { io.err(&format!( "Unknown command: \"{other}\"\n\nTo see a list of supported commands, run:\n impeccable --help\n" )); 1 } } } /// The npm `impeccable` package version `cli.js --version` prints (its /// `package.json`), tracked separately from the crate version. pub const CLI_VERSION: &str = "3.6.0"; /// The engines wired into `impeccable detect`: the static HTML engine /// (crates/html). The browser engine (crates/browser) plugs in here once it /// lands; until then URL scans report the puppeteer message. fn engines() -> impeccable_detect::Engines<'static> { static HTML: impeccable_html::StaticHtmlEngine = impeccable_html::StaticHtmlEngine { // The shipped binary carries the built-in rules only. static_rule_pack: None, }; impeccable_detect::Engines { html: &HTML, url: Some(url_engine()), } } // --- browser engine (crates/browser) ------------------------------------- /// The URL engine, built once from the process environment (browser /// discovery reads `IMPECCABLE_BROWSER` / `PUPPETEER_EXECUTABLE_PATH` / /// `CHROME_PATH`, sandbox flags read `CI`). fn url_engine() -> &'static impeccable_browser::BrowserEngine { static ENGINE: std::sync::OnceLock = std::sync::OnceLock::new(); ENGINE.get_or_init(impeccable_browser::BrowserEngine::from_process_env) } // -------------------------------------------------------------------------