From 26070593536acf955f2770f3068ff902497bd99b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 08:50:35 +0000 Subject: [PATCH 1/2] feat(session): print chained errors with red "error:" prefix `Session::main` now formats any error it encounters as error: * * with a bold red `error:` prefix (when stderr supports ANSI), matching the format vp uses today, and returns only the exit status instead of `anyhow::Result`. The same formatting is exposed as `vite_task::print_error` so other entry points (e.g. `Session::init` errors in the `vt` binary) can use it. vite-plus is pinned to a vite-task revision and will be updated separately to drop its now-redundant error chain formatter at the napi boundary. https://claude.ai/code/session_012BLuM9bhiDFBfc8U1CMst8 --- crates/vite_task/src/lib.rs | 2 +- crates/vite_task/src/session/mod.rs | 42 +++++++++++++++---- crates/vite_task_bin/src/main.rs | 25 +++++------ .../snapshots/cycle_dependency_error.md | 2 +- .../non_interactive_recursive_typo_errors.md | 2 +- .../recursive_without_task_errors.md | 2 +- .../snapshots/transitive_typo_errors.md | 2 +- .../typo_in_task_script_fails_without_list.md | 6 +-- .../snapshots/verbose_without_task_errors.md | 2 +- 9 files changed, 54 insertions(+), 31 deletions(-) diff --git a/crates/vite_task/src/lib.rs b/crates/vite_task/src/lib.rs index c1659756f..48b4a9d54 100644 --- a/crates/vite_task/src/lib.rs +++ b/crates/vite_task/src/lib.rs @@ -4,7 +4,7 @@ pub mod session; // Public exports for vite_task_bin pub use cli::{CacheSubcommand, Command, RunCommand, RunFlags}; -pub use session::{CommandHandler, ExitStatus, HandledCommand, Session, SessionConfig}; +pub use session::{CommandHandler, ExitStatus, HandledCommand, Session, SessionConfig, print_error}; pub use vite_task_graph::{ config::{ self, diff --git a/crates/vite_task/src/session/mod.rs b/crates/vite_task/src/session/mod.rs index ed1fd5cb8..c8a7a30ea 100644 --- a/crates/vite_task/src/session/mod.rs +++ b/crates/vite_task/src/session/mod.rs @@ -244,15 +244,19 @@ impl<'a> Session<'a> { /// Primary entry point for CLI usage. Plans and executes the given command. /// - /// # Errors - /// - /// Returns an error if planning or execution fails. + /// Any error encountered during planning or execution is printed to stderr + /// with a bold red `error:` prefix, with each level of the error chain on + /// its own `* `-prefixed line. Returns the exit status — callers exit the + /// process with it. #[tracing::instrument(level = "debug", skip_all)] - pub async fn main(mut self, command: Command) -> anyhow::Result { + pub async fn main(mut self, command: Command) -> ExitStatus { match self.main_inner(command).await { - Ok(()) => Ok(ExitStatus::SUCCESS), - Err(SessionError::EarlyExit(status)) => Ok(status), - Err(SessionError::Anyhow(err)) => Err(err), + Ok(()) => ExitStatus::SUCCESS, + Err(SessionError::EarlyExit(status)) => status, + Err(SessionError::Anyhow(err)) => { + print_error(&err); + ExitStatus::FAILURE + } } } @@ -795,6 +799,30 @@ impl<'a> Session<'a> { } } +/// Print `error` to stderr formatted as the `vp` CLI does: +/// +/// ```text +/// error: +/// * +/// * +/// ``` +/// +/// The `error:` prefix is bold red when stderr supports ANSI colors. +pub fn print_error(error: &anyhow::Error) { + use std::io::Write as _; + + use owo_colors::{OwoColorize as _, Stream, Style}; + + let prefix = + "error:".if_supports_color(Stream::Stderr, |s| s.style(Style::new().red().bold())); + let mut stderr = std::io::stderr().lock(); + let _ = write!(stderr, "{prefix} {error}"); + for source in error.chain().skip(1) { + let _ = write!(stderr, "\n* {source}"); + } + let _ = writeln!(stderr); +} + /// Whether stdout supports ANSI color output for the current process. Honors /// `NO_COLOR`/`FORCE_COLOR` and detects TTY capability via the `supports-color` /// crate. Result is cached for the process lifetime. diff --git a/crates/vite_task_bin/src/main.rs b/crates/vite_task_bin/src/main.rs index d627e2c32..e1599059d 100644 --- a/crates/vite_task_bin/src/main.rs +++ b/crates/vite_task_bin/src/main.rs @@ -3,24 +3,21 @@ use vite_task::{Command, ExitStatus, Session}; use vite_task_bin::OwnedSessionConfig; fn main() -> ! { - let exit_code: i32 = - tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(async { - match run().await { - Ok(status) => i32::from(status.0), - #[expect(clippy::print_stderr, reason = "top-level error reporting")] - Err(err) => { - eprintln!("Error: {err:?}"); - 1 - } - } - }); + let status: ExitStatus = + tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(run()); - std::process::exit(exit_code); + std::process::exit(i32::from(status.0)); } -async fn run() -> anyhow::Result { +async fn run() -> ExitStatus { let args = Command::parse(); let mut owned_config = OwnedSessionConfig::default(); - let session = Session::init(owned_config.as_config())?; + let session = match Session::init(owned_config.as_config()) { + Ok(session) => session, + Err(err) => { + vite_task::print_error(&err); + return ExitStatus::FAILURE; + } + }; session.main(args).await } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/error_cycle_dependency/snapshots/cycle_dependency_error.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/error_cycle_dependency/snapshots/cycle_dependency_error.md index 80dec14ab..b2ec4748d 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/error_cycle_dependency/snapshots/cycle_dependency_error.md +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/error_cycle_dependency/snapshots/cycle_dependency_error.md @@ -9,5 +9,5 @@ task-a -> task-b -> task-a cycle **Exit code:** 1 ``` -Error: Cycle dependency detected: error-cycle-dependency-test#task-a -> error-cycle-dependency-test#task-b -> error-cycle-dependency-test#task-a +error: Cycle dependency detected: error-cycle-dependency-test#task-a -> error-cycle-dependency-test#task-b -> error-cycle-dependency-test#task-a ``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/non_interactive_recursive_typo_errors.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/non_interactive_recursive_typo_errors.md index c3b50a9c9..69e2df4de 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/non_interactive_recursive_typo_errors.md +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/non_interactive_recursive_typo_errors.md @@ -7,5 +7,5 @@ A typoed task name combined with `-r` (not cwd-only) should error without listin **Exit code:** 1 ``` -Error: Task "buid" not found +error: Task "buid" not found ``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/recursive_without_task_errors.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/recursive_without_task_errors.md index fd2c3bd04..818103276 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/recursive_without_task_errors.md +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/recursive_without_task_errors.md @@ -7,5 +7,5 @@ **Exit code:** 1 ``` -Error: No task specifier provided for 'run' command +error: No task specifier provided for 'run' command ``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/transitive_typo_errors.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/transitive_typo_errors.md index ab055b3aa..a79295b1f 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/transitive_typo_errors.md +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/transitive_typo_errors.md @@ -7,5 +7,5 @@ **Exit code:** 1 ``` -Error: Task "buid" not found +error: Task "buid" not found ``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/typo_in_task_script_fails_without_list.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/typo_in_task_script_fails_without_list.md index 6922dc291..386668976 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/typo_in_task_script_fails_without_list.md +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/typo_in_task_script_fails_without_list.md @@ -7,8 +7,6 @@ A typo inside a task's own script (i.e. a nested `vp run` command) should surfac **Exit code:** 1 ``` -Error: Failed to plan tasks from `vt run nonexistent-xyz` in task task-select-test#run-typo-task - -Caused by: - Task "nonexistent-xyz" not found +error: Failed to plan tasks from `vt run nonexistent-xyz` in task task-select-test#run-typo-task +* Task "nonexistent-xyz" not found ``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/verbose_without_task_errors.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/verbose_without_task_errors.md index fb34abe66..083134214 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/verbose_without_task_errors.md +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task_select/snapshots/verbose_without_task_errors.md @@ -7,5 +7,5 @@ **Exit code:** 1 ``` -Error: No task specifier provided for 'run' command +error: No task specifier provided for 'run' command ``` From e69a03dc0e80b12ebe77d406bee58e9e751be887 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 09:02:59 +0000 Subject: [PATCH 2/2] style: cargo fmt https://claude.ai/code/session_012BLuM9bhiDFBfc8U1CMst8 --- crates/vite_task/src/lib.rs | 4 +++- crates/vite_task/src/session/mod.rs | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/vite_task/src/lib.rs b/crates/vite_task/src/lib.rs index 48b4a9d54..8a8f03867 100644 --- a/crates/vite_task/src/lib.rs +++ b/crates/vite_task/src/lib.rs @@ -4,7 +4,9 @@ pub mod session; // Public exports for vite_task_bin pub use cli::{CacheSubcommand, Command, RunCommand, RunFlags}; -pub use session::{CommandHandler, ExitStatus, HandledCommand, Session, SessionConfig, print_error}; +pub use session::{ + CommandHandler, ExitStatus, HandledCommand, Session, SessionConfig, print_error, +}; pub use vite_task_graph::{ config::{ self, diff --git a/crates/vite_task/src/session/mod.rs b/crates/vite_task/src/session/mod.rs index c8a7a30ea..d1d68b1f0 100644 --- a/crates/vite_task/src/session/mod.rs +++ b/crates/vite_task/src/session/mod.rs @@ -813,8 +813,7 @@ pub fn print_error(error: &anyhow::Error) { use owo_colors::{OwoColorize as _, Stream, Style}; - let prefix = - "error:".if_supports_color(Stream::Stderr, |s| s.style(Style::new().red().bold())); + let prefix = "error:".if_supports_color(Stream::Stderr, |s| s.style(Style::new().red().bold())); let mut stderr = std::io::stderr().lock(); let _ = write!(stderr, "{prefix} {error}"); for source in error.chain().skip(1) {