Skip to content

Commit 7083a1f

Browse files
zmwangxclaude
andcommitted
feat: add -s/--silent to suppress the runner's own output
## Summary - Add `-s, --silent` to `run` (mutually exclusive with `--verbose`), matching the `--silent` flag in npm/pnpm/yarn/bun. - Silent runs emit only the tasks' own stdout/stderr — the per-task command line, the inline cache-status indicator, inter-task spacing, the grouped-mode block header, and the run summary are all suppressed. Error banners and the process exit code are unaffected. - The summary is still computed and persisted, so `--last-details` keeps working after a silent run. Useful in agentic verification gates, where the command line and cache/summary diagnostics are pure noise that waste tokens when replayed on every run. Addresses voidzero-dev/vite-plus#1768. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b472a3f commit 7083a1f

12 files changed

Lines changed: 226 additions & 36 deletions

File tree

crates/vite_task/src/cli/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ pub struct RunFlags {
4040
#[clap(default_value = "false", short = 'v', long)]
4141
pub verbose: bool,
4242

43+
/// Suppress the runner's own output: the per-task command line, cache-status
44+
/// indicators, and the run summary. The tasks' own output is unaffected.
45+
#[clap(default_value = "false", short = 's', long, conflicts_with = "verbose")]
46+
pub silent: bool,
47+
4348
/// Force caching on for all tasks and scripts.
4449
#[clap(long, conflicts_with = "no_cache")]
4550
pub cache: bool,

crates/vite_task/src/session/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ impl<'a> Session<'a> {
263263
/// # Panics
264264
///
265265
/// Panics if parsing a hardcoded bare `RunCommand` fails (should never happen).
266+
#[expect(
267+
clippy::too_many_lines,
268+
reason = "single dispatch point that plans the command and wires up the reporter pipeline"
269+
)]
266270
async fn main_inner(&mut self, command: Command) -> Result<(), SessionError> {
267271
match command.into_resolved() {
268272
ResolvedCommand::Cache { ref subcmd } => self.handle_cache_command(subcmd),
@@ -335,6 +339,9 @@ impl<'a> Session<'a> {
335339
stderr: stderr_supports_color(),
336340
};
337341

342+
// `--silent` suppresses the per-task command line and summary.
343+
let silent = run_command.flags.silent;
344+
338345
let inner: Box<dyn reporter::GraphExecutionReporterBuilder> = match run_command
339346
.flags
340347
.log
@@ -343,16 +350,19 @@ impl<'a> Session<'a> {
343350
Arc::clone(&workspace_path),
344351
writer,
345352
color_support,
353+
silent,
346354
)),
347355
crate::cli::LogMode::Labeled => Box::new(LabeledReporterBuilder::new(
348356
Arc::clone(&workspace_path),
349357
writer,
350358
color_support,
359+
silent,
351360
)),
352361
crate::cli::LogMode::Grouped => Box::new(GroupedReporterBuilder::new(
353362
Arc::clone(&workspace_path),
354363
writer,
355364
color_support,
365+
silent,
356366
)),
357367
};
358368

@@ -364,6 +374,7 @@ impl<'a> Session<'a> {
364374
Some(self.make_summary_writer()),
365375
self.program_name.clone(),
366376
color_support,
377+
silent,
367378
));
368379
// Don't let SIGINT/CTRL_C kill the runner. Child tasks receive
369380
// the signal directly from the terminal driver and handle it

crates/vite_task/src/session/reporter/grouped/mod.rs

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub struct GroupedReporterBuilder {
2222
workspace_path: Arc<AbsolutePath>,
2323
writer: Box<dyn Write>,
2424
color_support: ColorSupport,
25+
silent: bool,
2526
}
2627

2728
impl GroupedReporterBuilder {
@@ -30,12 +31,18 @@ impl GroupedReporterBuilder {
3031
/// `LeafExecutionReporter::start`) strip ANSI on the way into the buffer,
3132
/// so by the time the buffer reaches `writer` it already matches the
3233
/// terminal's colour capability. `writer` is therefore stored unwrapped.
34+
///
35+
/// `silent` suppresses the runner's chrome — the labeled command line /
36+
/// cache indicator emitted in `LeafExecutionReporter::start` and the
37+
/// `── [pkg#task] ──` block header — leaving just the buffered task output;
38+
/// error banners are unaffected.
3339
pub fn new(
3440
workspace_path: Arc<AbsolutePath>,
3541
writer: Box<dyn Write>,
3642
color_support: ColorSupport,
43+
silent: bool,
3744
) -> Self {
38-
Self { workspace_path, writer, color_support }
45+
Self { workspace_path, writer, color_support, silent }
3946
}
4047
}
4148

@@ -45,6 +52,7 @@ impl GraphExecutionReporterBuilder for GroupedReporterBuilder {
4552
writer: Rc::new(RefCell::new(self.writer)),
4653
workspace_path: self.workspace_path,
4754
color_support: self.color_support,
55+
silent: self.silent,
4856
})
4957
}
5058
}
@@ -53,6 +61,7 @@ struct GroupedGraphReporter {
5361
writer: Rc<RefCell<Box<dyn Write>>>,
5462
workspace_path: Arc<AbsolutePath>,
5563
color_support: ColorSupport,
64+
silent: bool,
5665
}
5766

5867
impl GraphExecutionReporter for GroupedGraphReporter {
@@ -70,6 +79,7 @@ impl GraphExecutionReporter for GroupedGraphReporter {
7079
started: false,
7180
grouped_buffer: None,
7281
color_support: self.color_support,
82+
silent: self.silent,
7383
})
7484
}
7585

@@ -88,20 +98,27 @@ struct GroupedLeafReporter {
8898
started: bool,
8999
grouped_buffer: Option<Rc<RefCell<Vec<u8>>>>,
90100
color_support: ColorSupport,
101+
silent: bool,
91102
}
92103

93104
impl LeafExecutionReporter for GroupedLeafReporter {
94105
fn start(&mut self, cache_status: CacheStatus) -> StdioConfig {
95-
let line =
96-
format_command_with_cache_status(&self.display, &self.workspace_path, &cache_status);
97-
98106
self.started = true;
99107

100108
// Print labeled command line immediately (before output is buffered).
101-
let labeled_line = vite_str::format!("{} {line}", self.label);
102-
let mut writer = self.writer.borrow_mut();
103-
let _ = writer.write_all(labeled_line.as_bytes());
104-
let _ = writer.flush();
109+
// `--silent` suppresses it; the buffered task output is still flushed
110+
// in `finish`.
111+
if !self.silent {
112+
let line = format_command_with_cache_status(
113+
&self.display,
114+
&self.workspace_path,
115+
&cache_status,
116+
);
117+
let labeled_line = vite_str::format!("{} {line}", self.label);
118+
let mut writer = self.writer.borrow_mut();
119+
let _ = writer.write_all(labeled_line.as_bytes());
120+
let _ = writer.flush();
121+
}
105122

106123
// Create shared buffer for both stdout and stderr.
107124
let buffer = Rc::new(RefCell::new(Vec::new()));
@@ -128,23 +145,26 @@ impl LeafExecutionReporter for GroupedLeafReporter {
128145
_cache_update_status: CacheUpdateStatus,
129146
error: Option<ExecutionError>,
130147
) {
131-
// Build grouped block: header + buffered output.
148+
// Build grouped block: header + buffered output. `--silent` drops the
149+
// `── [pkg#task] ──` block header, leaving just the task's own output.
132150
let mut extra = Vec::new();
133151
if let Some(ref grouped_buffer) = self.grouped_buffer {
134152
let content = grouped_buffer.borrow();
135153
if !content.is_empty() {
136-
let header = vite_str::format!(
137-
"{} {} {}\n",
138-
"──".style(Style::new().bright_black()),
139-
self.label,
140-
"──".style(Style::new().bright_black())
141-
);
142-
extra.extend_from_slice(header.as_bytes());
154+
if !self.silent {
155+
let header = vite_str::format!(
156+
"{} {} {}\n",
157+
"──".style(Style::new().bright_black()),
158+
self.label,
159+
"──".style(Style::new().bright_black())
160+
);
161+
extra.extend_from_slice(header.as_bytes());
162+
}
143163
extra.extend_from_slice(&content);
144164
}
145165
}
146166

147-
write_leaf_trailing_output(&self.writer, error, self.started, &extra);
167+
write_leaf_trailing_output(&self.writer, error, self.started, self.silent, &extra);
148168
}
149169
}
150170

@@ -177,6 +197,7 @@ mod tests {
177197
test_path(),
178198
Box::new(std::io::sink()),
179199
ColorSupport::uniform(false),
200+
false,
180201
));
181202
let mut reporter = builder.build();
182203
let mut leaf = reporter.new_leaf_execution(&item.execution_item_display, leaf_kind(item));

crates/vite_task/src/session/reporter/interleaved/mod.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct InterleavedReporterBuilder {
1616
workspace_path: Arc<AbsolutePath>,
1717
writer: Box<dyn Write>,
1818
color_support: ColorSupport,
19+
silent: bool,
1920
}
2021

2122
impl InterleavedReporterBuilder {
@@ -24,12 +25,17 @@ impl InterleavedReporterBuilder {
2425
/// stored unwrapped. `color_support` is forwarded to the pipe writers
2526
/// in `LeafExecutionReporter::start`, where ANSI emitted by child tasks is stripped
2627
/// for non-terminal sinks.
28+
///
29+
/// `silent` suppresses the per-task command line / cache indicator emitted
30+
/// in `LeafExecutionReporter::start`; task output and error banners are
31+
/// unaffected.
2732
pub fn new(
2833
workspace_path: Arc<AbsolutePath>,
2934
writer: Box<dyn Write>,
3035
color_support: ColorSupport,
36+
silent: bool,
3137
) -> Self {
32-
Self { workspace_path, writer, color_support }
38+
Self { workspace_path, writer, color_support, silent }
3339
}
3440
}
3541

@@ -39,6 +45,7 @@ impl GraphExecutionReporterBuilder for InterleavedReporterBuilder {
3945
writer: Rc::new(RefCell::new(self.writer)),
4046
workspace_path: self.workspace_path,
4147
color_support: self.color_support,
48+
silent: self.silent,
4249
})
4350
}
4451
}
@@ -47,6 +54,7 @@ struct InterleavedGraphReporter {
4754
writer: Rc<RefCell<Box<dyn Write>>>,
4855
workspace_path: Arc<AbsolutePath>,
4956
color_support: ColorSupport,
57+
silent: bool,
5058
}
5159

5260
impl GraphExecutionReporter for InterleavedGraphReporter {
@@ -67,6 +75,7 @@ impl GraphExecutionReporter for InterleavedGraphReporter {
6775
stdio_suggestion,
6876
started: false,
6977
color_support: self.color_support,
78+
silent: self.silent,
7079
})
7180
}
7281

@@ -84,18 +93,25 @@ struct InterleavedLeafReporter {
8493
stdio_suggestion: StdioSuggestion,
8594
started: bool,
8695
color_support: ColorSupport,
96+
silent: bool,
8797
}
8898

8999
impl LeafExecutionReporter for InterleavedLeafReporter {
90100
fn start(&mut self, cache_status: CacheStatus) -> StdioConfig {
91-
let line =
92-
format_command_with_cache_status(&self.display, &self.workspace_path, &cache_status);
93-
94101
self.started = true;
95102

96-
let mut writer = self.writer.borrow_mut();
97-
let _ = writer.write_all(line.as_bytes());
98-
let _ = writer.flush();
103+
// `--silent` suppresses the command line / cache indicator; the task's
104+
// own output still streams through the pipe writers below.
105+
if !self.silent {
106+
let line = format_command_with_cache_status(
107+
&self.display,
108+
&self.workspace_path,
109+
&cache_status,
110+
);
111+
let mut writer = self.writer.borrow_mut();
112+
let _ = writer.write_all(line.as_bytes());
113+
let _ = writer.flush();
114+
}
99115

100116
StdioConfig {
101117
suggestion: self.stdio_suggestion,
@@ -118,7 +134,7 @@ impl LeafExecutionReporter for InterleavedLeafReporter {
118134
_cache_update_status: CacheUpdateStatus,
119135
error: Option<ExecutionError>,
120136
) {
121-
write_leaf_trailing_output(&self.writer, error, self.started, &[]);
137+
write_leaf_trailing_output(&self.writer, error, self.started, self.silent, &[]);
122138
}
123139
}
124140

@@ -150,6 +166,7 @@ mod tests {
150166
test_path(),
151167
Box::new(std::io::sink()),
152168
ColorSupport::uniform(false),
169+
false,
153170
));
154171
let mut reporter = builder.build();
155172
let mut leaf = reporter.new_leaf_execution(display, leaf_kind);

crates/vite_task/src/session/reporter/labeled/mod.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,24 @@ pub struct LabeledReporterBuilder {
2121
workspace_path: Arc<AbsolutePath>,
2222
writer: Box<dyn Write>,
2323
color_support: ColorSupport,
24+
silent: bool,
2425
}
2526

2627
impl LabeledReporterBuilder {
2728
/// `writer` is stored unwrapped — the reporter's own writes pick
2829
/// colour-vs-plain at format time via `ColorizeExt`. Child-process
2930
/// pipes are stripped per-stream inside `LeafExecutionReporter::start`.
31+
///
32+
/// `silent` suppresses the labeled command line / cache indicator emitted
33+
/// in `LeafExecutionReporter::start`; task output and error banners are
34+
/// unaffected.
3035
pub fn new(
3136
workspace_path: Arc<AbsolutePath>,
3237
writer: Box<dyn Write>,
3338
color_support: ColorSupport,
39+
silent: bool,
3440
) -> Self {
35-
Self { workspace_path, writer, color_support }
41+
Self { workspace_path, writer, color_support, silent }
3642
}
3743
}
3844

@@ -42,6 +48,7 @@ impl GraphExecutionReporterBuilder for LabeledReporterBuilder {
4248
writer: Rc::new(RefCell::new(self.writer)),
4349
workspace_path: self.workspace_path,
4450
color_support: self.color_support,
51+
silent: self.silent,
4552
})
4653
}
4754
}
@@ -50,6 +57,7 @@ struct LabeledGraphReporter {
5057
writer: Rc<RefCell<Box<dyn Write>>>,
5158
workspace_path: Arc<AbsolutePath>,
5259
color_support: ColorSupport,
60+
silent: bool,
5361
}
5462

5563
impl GraphExecutionReporter for LabeledGraphReporter {
@@ -64,6 +72,7 @@ impl GraphExecutionReporter for LabeledGraphReporter {
6472
workspace_path: Arc::clone(&self.workspace_path),
6573
started: false,
6674
color_support: self.color_support,
75+
silent: self.silent,
6776
})
6877
}
6978

@@ -80,20 +89,28 @@ struct LabeledLeafReporter {
8089
workspace_path: Arc<AbsolutePath>,
8190
started: bool,
8291
color_support: ColorSupport,
92+
silent: bool,
8393
}
8494

8595
impl LeafExecutionReporter for LabeledLeafReporter {
8696
fn start(&mut self, cache_status: CacheStatus) -> StdioConfig {
8797
let label = format_task_label(&self.display);
88-
let line =
89-
format_command_with_cache_status(&self.display, &self.workspace_path, &cache_status);
9098

9199
self.started = true;
92100

93-
let labeled_line = vite_str::format!("{label} {line}");
94-
let mut writer = self.writer.borrow_mut();
95-
let _ = writer.write_all(labeled_line.as_bytes());
96-
let _ = writer.flush();
101+
// `--silent` suppresses the labeled command line / cache indicator; the
102+
// task's own output still streams through the labeled pipe writers below.
103+
if !self.silent {
104+
let line = format_command_with_cache_status(
105+
&self.display,
106+
&self.workspace_path,
107+
&cache_status,
108+
);
109+
let labeled_line = vite_str::format!("{label} {line}");
110+
let mut writer = self.writer.borrow_mut();
111+
let _ = writer.write_all(labeled_line.as_bytes());
112+
let _ = writer.flush();
113+
}
97114

98115
let prefix = vite_str::format!("{label} ");
99116

@@ -118,7 +135,7 @@ impl LeafExecutionReporter for LabeledLeafReporter {
118135
_cache_update_status: CacheUpdateStatus,
119136
error: Option<ExecutionError>,
120137
) {
121-
write_leaf_trailing_output(&self.writer, error, self.started, &[]);
138+
write_leaf_trailing_output(&self.writer, error, self.started, self.silent, &[]);
122139
}
123140
}
124141

@@ -151,6 +168,7 @@ mod tests {
151168
test_path(),
152169
Box::new(std::io::sink()),
153170
ColorSupport::uniform(false),
171+
false,
154172
));
155173
let mut reporter = builder.build();
156174
let mut leaf = reporter.new_leaf_execution(&item.execution_item_display, leaf_kind(item));

0 commit comments

Comments
 (0)