diff --git a/hive-forge/src/main.rs b/hive-forge/src/main.rs index 3aad7deb..3f9960f3 100644 --- a/hive-forge/src/main.rs +++ b/hive-forge/src/main.rs @@ -28,6 +28,7 @@ mod verbs; use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; +use std::process::ExitCode; #[derive(Parser)] #[command( @@ -173,7 +174,24 @@ enum Verb { CiRerun(verbs::ci_rerun::Args), } -fn main() -> Result<()> { +/// Wrapper over [`run`] that owns how a failure reaches the operator. +/// +/// `fn main() -> Result<()>` would format the error with anyhow's `Debug` +/// impl, which prints a bare `Error:` header. `hive-forge` is almost always +/// invoked from an agent's bash task, where stdout is what gets surfaced +/// first and stderr is easy to miss — so failures are prefixed with the +/// binary name to be unmistakably ours, and rendered with `{:#}` +/// (alternate `Display`), which keeps the full `context` chain inline +/// rather than dropping it the way plain `Display` would. +fn main() -> ExitCode { + if let Err(e) = run() { + eprintln!("hive-forge: FAILED: {e:#}"); + return ExitCode::FAILURE; + } + ExitCode::SUCCESS +} + +fn run() -> Result<()> { let cli = Cli::parse(); let client = client::Client::from_env(cli.repo, cli.json, cli.forge) .context("initialize forge client")?;