Skip to content

Commit ea2f983

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 ea2f983

13 files changed

Lines changed: 227 additions & 36 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Changelog
22

3+
- **Added** `-s`/`--silent` flag to suppress `vp run`'s own output (command line, cache-status indicators, and summary), leaving only the tasks' output ([#429](https://github.com/voidzero-dev/vite-task/pull/429))
34
- **Added** A task's `env` and `untrackedEnv` glob patterns now support `!` negation: a `!`-prefixed pattern excludes matching variables (e.g. `["VITE_*", "!VITE_SECRET"]` tracks every `VITE_*` except `VITE_SECRET`) ([#425](https://github.com/voidzero-dev/vite-task/pull/425))
45
- **Fixed** `package.json` and `pnpm-workspace.yaml` files with a UTF-8 BOM no longer fail to parse ([#424](https://github.com/voidzero-dev/vite-task/pull/424))
56
- **Changed** `vp run --filter <expr>` now exits 0 with a warning when the filter matches no packages, matching pnpm. Use `--fail-if-no-match` to restore the previous strict behavior ([#393](https://github.com/voidzero-dev/vite-task/pull/393))

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);

0 commit comments

Comments
 (0)