SilentRunner is a lightweight Windows command runner that executes console applications without creating a console window (no flashing) while capturing exit code, stdout, stderr, and execution diagnostics.
SilentRunner is designed for unattended and automated execution scenarios such as scripting, CI/CD pipelines, and scheduled tasks, while retaining detailed control over process execution, output, and diagnostics. Execution results can be independently used to control parent stdout/stderr emission, log retention, and post-execution actions.
Key capabilities include:
- Silent windowless execution via
cmd.exe /d /s /c. - Child process tree management, tracking, and controlled termination.
- Independent routing of child stdout, child stderr, and SilentRunner diagnostics to parent stdout/stderr and persistent logs.
- Immediate or delayed emission to parent stdout/stderr,
with delayed output optionally replayed from buffer or persistent logs
at the end of execution,on execution success, oron execution failure. - Persistent TXT and JSONL logging with configurable retention
policies (
always|success|failure) and the execution result recorded in log filenames. - Configurable execution IDs for log naming and workflow integration.
- Post-execution hooks (
on success|on failure) with execution metadata exposed through environment variables. - Configurable environment: working directory, standard input, UTF-8 mode, and execution timeout.
- Multiple diagnostic levels ranging from normal execution messages to detailed debugging and verbose execution summaries.
SilentRunner is organized as an event-driven processing pipeline rather than a monolithic command wrapper.
SilentRunner separates child process execution management from child stdout/stderr capture and routing to parent stdout/stderr and persistent logs. The child process tree is tracked and controlled through Windows Job Object integration, including process-tree termination and debug monitoring.
Child process stdout/stderr and SilentRunner diagnostics are collected into a common execution timeline. The timeline maintains job state and queue semantics, while independent worker components handle parent stdout/stderr emission and persistent logging.
SilentRunner supports two execution modes:
SilentRunner.exe [SilentRunner options...] <script-or-exe> [child args...]
In Script/Executable mode, SilentRunner parses its own options first. The first non-option argument marks the beginning of the child command. That argument is treated as the script or executable path, and all remaining arguments are treated as child arguments.
Paths and arguments containing spaces should be enclosed in quotes.
SilentRunner.exe [SilentRunner options...] -c "<raw-cmd>"
In Raw mode, -c consumes the following argument as the complete raw
command string. Enclose the command in quotes when needed to keep it as
a single argument.
SilentRunner options are organized into logical groups. Most execution scenarios require configuring only a small subset of these categories.
Controls the execution environment and runtime behavior of the child process and run hooks.
By default, the child process inherits SilentRunner's current working
directory, receives NUL as its standard input, uses the system's
default console code page, and runs without a time limit.
The configured working directory controls the working directory in which the child process and run hooks execute. It does not change the base directory used to resolve relative paths in SilentRunner path options; those paths are resolved relative to SilentRunner's inherited working directory.
These options allow the working directory, standard input handling, console code page, and execution timeout to be customized.
Options:
--cwd <dir>--inherit-stdin--utf8or--utf-8--timeout-ms <ms>
Controls the identifier assigned to each execution.
The execution ID is used to uniquely identify an execution and forms the base name of all log files created during that run.
The execution ID is composed as:
id-prefix + id-base + id-suffix
All three components are optional and may be used independently.
If none is specified, the execution ID defaults to timestamp+pid (UTC).
Options:
--id-prefix <value>--id-base <value>--id-suffix <timestamp|pid|timestamp+pid|pid+timestamp>
Controls which output is emitted to the parent process and when it is emitted.
Emission modes:
stream-- Emit child stdout/stderr to the parent stdout/stderr as it is produced (default).end-- Emit the buffered output after the execution finishes.success-- Emit the buffered output only if execution succeeds.failure-- Emit the buffered output only if execution fails.never-- Never emit the selected stream to the parent. When used with--stderr-emit, this disables the default parent diagnostic channel. Unless another SilentRunner diagnostic channel, such as--stderr-dir, is available, SilentRunner terminates before starting the child process with exit code 254.
Options:
--stdout-emit <mode>-- Controls emission of the child process stdout.--stderr-emit <mode>-- Controls emission of the mixed stderr view: child stderr and SilentRunner diagnostics.--stderr-emit-child <mode>-- Controls emission of child stderr only.--stderr-emit-sr <mode>-- Controls emission of SilentRunner diagnostics only.
The three stderr emit options are mutually exclusive.
In addition to Output Routing, SilentRunner can write execution output to persistent log files.
Persistent logging is independent of parent stdout/stderr emission. Any combination of parent emission and log files can be used simultaneously. For example, output may be streamed to the parent process while also being recorded as TXT and/or JSONL logs.
Unlike the parent stderr routing options, all stderr log destinations may be enabled at the same time. This allows child stderr and SilentRunner diagnostics (described below) to be logged both separately and as a mixed stderr stream in parallel.
Each execution creates a new set of log files. SilentRunner never appends output to an existing log file. Log file names are derived from the execution ID, which is described above.
While execution is in progress, log file names include the running state.
After execution completes, they are renamed to reflect the final execution
result: success or failure.
For example: my-execution_stdout_running.log →
my-execution_stdout_success.log or my-execution_stdout_failure.log.
Options:
--stdout-dir <dir>--stdout-dir-jsonl <dir>--stderr-dir <dir>--stderr-dir-jsonl <dir>--stderr-dir-child <dir>--stderr-dir-child-jsonl <dir>--stderr-dir-sr <dir>--stderr-dir-sr-jsonl <dir>
Controls whether persistent log files are retained after execution completes.
Each stdout and stderr log target has its own retention policy. JSONL log files use the same retention policy as the corresponding TXT log stream.
Logs removed by a retention policy are deleted permanently and are not moved to the Windows Recycle Bin.
Supported modes:
always-- Always keep the log file (default).success-- Keep the log file only if execution succeeds.failure-- Keep the log file only if execution fails.
Options:
--stdout-dir-keep-log <mode>--stderr-dir-keep-log <mode>--stderr-dir-child-keep-log <mode>--stderr-dir-sr-keep-log <mode>
Controls how much output may be buffered in memory for delayed parent
replay (end, success, or failure emission modes). By default,
buffer limits are 0 (unlimited).
Buffering is only used when parent stdout/stderr emission is delayed. When output is streamed to the parent process, these limits are not used.
If persistent logging is enabled for the corresponding stream,
SilentRunner can replay the output from the persistent log file instead
of the in-memory buffer. For example, --stderr-emit-sr end can replay
from logs written by --stderr-dir-sr or --stderr-dir-sr-jsonl. This
allows delayed replay of arbitrarily large outputs while keeping memory
usage bounded.
Options:
--stdout-max-buffer-bytes <bytes>--stderr-max-buffer-bytes <bytes>--std-total-max-buffer-bytes <bytes>
Runs an external program after execution without arguments.
Hooks execute in the same process environment as the child process,
including the configured working directory and environment variables.
The hook path itself is resolved before execution relative to SilentRunner's
inherited working directory, not relative to the directory specified by
--cwd.
SilentRunner also provides additional execution-specific environment variables, allowing hooks to access execution metadata such as the execution ID, execution result, and log file locations.
The complete list of currently available environment variables can be displayed
using SilentRunner --help. Requests for additional environment variables are
welcome.
Post-execution hooks are started as detached processes without inherited standard handles. The hook and programs started by it therefore cannot rely on the original SilentRunner parent stdout or stderr handles.
If a hook starts another SilentRunner instance, that instance must have an
available diagnostic channel. For example, persistent stderr logging can be
enabled with --stderr-dir; otherwise, if no diagnostic channel is available,
the nested SilentRunner terminates with exit code 254.
Options:
--run-on-success <path>--run-on-failure <path>
Controls the level of SilentRunner diagnostics.
Informational, error, and fatal diagnostics are enabled by default. Their parent emission and persistent logging follow the SilentRunner diagnostic routing configured through the stderr Output Routing and Persistent Logging options.
If no SilentRunner diagnostic channel is available (neither parent emission nor persistent logging) before the child process starts, SilentRunner terminates immediately with exit code 254, because diagnostic messages could not be reported.
Exit code 255 indicates an internal SilentRunner failure.
The --debug option enables additional diagnostic messages describing
internal execution flow, child (sub)process lifecycle, and output
routing decisions.
The --verbose option implies --debug and adds detailed execution
summaries, including event processing, worker activity, and final
processing results.
Options:
--debug--verbose
Displays the built-in CLI reference.
Options:
--help
Run a command using the default SilentRunner configuration:
SilentRunner.exe task.cmd
task.cmdruns without creating a console window.- The child process inherits SilentRunner's current working directory.
- Standard input is connected to
NUL. - UTF-8 mode is not enabled.
- No execution timeout is applied.
- Child stdout is streamed to parent stdout.
- Child stderr and SilentRunner diagnostics are both streamed to parent stderr.
- No persistent log files are created.
- Debug and verbose diagnostics are disabled.
- No post-execution hooks are executed.
Run the child process in a specific working directory with a five-second execution timeout:
SilentRunner.exe --cwd "D:\Work" --utf8 --timeout-ms 5000 task.cmd
task.cmd:
@echo off
echo Příliš žluťoučký kůň úpěl ďábelské ódy
echo こんにちは
task.cmdruns withD:\Workas its working directory.- UTF-8 code page (
65001) is enabled for the child process. - If execution exceeds five seconds, SilentRunner terminates the child process tree with exit code 124.
- All other settings retain their default behavior described in the previous example.
Keep child stdout/stderr out of parent output during execution and emit it only if the execution fails:
SilentRunner.exe --stdout-emit failure --stderr-emit failure task.cmd
- Child stdout and stderr and SilentRunner diagnostics are not streamed to parent
stdout/stderr while
task.cmdis running. - Child stdout/stderr and SilentRunner diagnostics are buffered in memory in the execution timeline during execution.
- If the execution fails, the buffered stdout/stderr is replayed to parent stdout/stderr after execution completes.
- If the execution succeeds, no child stdout/stderr is emitted to the parent.
- All other settings retain their default behavior described in the first example.
Stream child output to the parent while also recording it in persistent log files:
SilentRunner.exe --stdout-dir "D:\Logs" --stderr-dir "D:\Logs" task.cmd
- Child stdout is streamed to parent stdout.
- Child stderr and SilentRunner diagnostics are streamed to parent stderr.
- Child stdout is also written to a persistent TXT stdout log.
- Child stderr and SilentRunner diagnostics are also written to a persistent mixed stderr TXT log.
- While execution is running, the log file names contain the
runningstate. After execution completes, they are renamed to containsuccessorfailureaccording to the final execution result. - The log files are retained after execution using the default
alwaysretention policy. - All other settings retain their default behavior described in the first example.
Record child stdout, child stderr, and SilentRunner diagnostics to persistent TXT and JSONL log files:
SilentRunner.exe ^
--stdout-dir "D:\Logs" ^
--stdout-dir-jsonl "D:\Logs" ^
--stderr-dir "D:\Logs" ^
--stderr-dir-jsonl "D:\Logs" ^
--stderr-dir-child "D:\Logs" ^
--stderr-dir-child-jsonl "D:\Logs" ^
--stderr-dir-sr "D:\Logs" ^
--stderr-dir-sr-jsonl "D:\Logs" ^
task.cmd
- Child stdout is recorded independently in TXT and JSONL formats.
- The mixed stderr stream, combining child stderr and SR diagnostics, is recorded independently in TXT and JSONL formats.
- Child stderr is also recorded separately in TXT and JSONL formats.
- SilentRunner diagnostics are also recorded separately in TXT and JSONL formats.
- All persistent log destinations may be enabled at the same time.
- Parent stdout/stderr emission keeps its default streaming behavior.
- Log retention keeps its default
alwayspolicy.
Keep SilentRunner diagnostics out of parent stderr during execution, persist them as JSONL, and replay them to the parent only if execution fails:
SilentRunner.exe ^
--stderr-emit-sr failure ^
--stderr-dir-sr-jsonl "D:\Logs" ^
--stderr-dir-sr-keep-log failure ^
task.cmd
- SilentRunner diagnostics are not streamed to parent stderr while
task.cmdis running. - SilentRunner diagnostics are written to a persistent JSONL log.
- The persistent log is used as the replay source instead of the in-memory execution timeline buffer.
- If execution fails, the diagnostics are replayed from the JSONL log to parent stderr after execution completes.
- If execution succeeds, no SilentRunner diagnostics are emitted to parent stderr.
- The JSONL diagnostic log is retained only on failure. The
--stderr-dir-sr-keep-logpolicy applies to both TXT and JSONL logs for the SilentRunner diagnostic stream. - Child stdout keeps its default streaming behavior to parent stdout.
Run a data-processing task with a custom execution ID, persist structured SilentRunner diagnostics, and process them through chained post-execution hooks:
SilentRunner.exe ^
--id-prefix processing ^
--id-base data ^
--id-suffix timestamp+pid ^
--stderr-dir-sr-jsonl "D:\Logs" ^
--debug ^
--run-on-success "D:\Hooks\filter-execution-data.cmd" ^
process-data.cmd "D:\Input" --mode fast
process-data.cmd:
@echo off
set "SOURCE=%~1"
set "MODE=%~3"
echo Processing %SOURCE%...
echo Mode: %MODE%
filter-execution-data.cmd:
@echo off
pathTo\SilentRunner.exe ^
--stdout-dir "%TEMP%" ^
--stderr-dir "%TEMP%" ^
--run-on-success "%~dp0store-execution-data.cmd" ^
"%~dp0jq.exe" -c "select(.message | startswith(\"[JOB]\"))" ^
"%SILENTRUNNER_STDERR_SR_JSONL_LOG%"
store-execution-data.cmd:
@echo off
set "FILTERED_DATA=%SILENTRUNNER_STDOUT_LOG%"
rem Store the filtered data in SQLite or elsewhere.
...
- The execution ID is built from the configured prefix, base, and
timestamp+pidsuffix and identifies the original execution and its log files. process-data.cmdis the child script;"D:\Input"and--mode fastare passed to it as child arguments.- SilentRunner diagnostics are recorded in a persistent JSONL log.
- Debug diagnostics are enabled, including child process lifecycle information.
filter-execution-data.cmdruns after a successful execution.- Hooks receive execution metadata through SilentRunner environment variables, including the execution ID, execution result, and retained log file locations.
- Use
--helpfor the complete list of available environment variables. filter-execution-data.cmdusesSILENTRUNNER_STDERR_SR_JSONL_LOGto access the retained SilentRunner diagnostic JSONL log.jqis an external command-line tool for processing JSON data. Here it filters the structured diagnostic log to retain only records whosemessagestarts with[JOB].[JOB]identifies process lifecycle diagnostics generated from Windows Job Object events.--stderr-dir "%TEMP%"provides a persistent diagnostic channel for the nested SilentRunner. Since post-execution hooks do not inherit standard handles, without an available diagnostic channel the nested SilentRunner would terminate before startingjqwith exit code 254.- The nested SilentRunner starts a separate execution with its own execution ID
and captures the filtered
jqoutput using its TXT stdout log. Sincejq -calready produces one JSON object per line, this preserves the filtered JSONL data directly. - After
jqcompletes successfully, the nested SilentRunner runsstore-execution-data.cmd. ItsSILENTRUNNER_STDOUT_LOGenvironment variable provides the path to the filtered data produced byjq. store-execution-data.cmdcan then store the filtered data in SQLite or another destination.
Execute a complete command using raw cmd.exe shell syntax:
SilentRunner.exe -c "echo Starting... & task1.exe | task2.exe"
- The entire quoted string after
-cis treated as a single raw command. - Shell operators such as
&and|are interpreted bycmd.exe. - Unlike Script/Executable mode, the command is not separated into a script or executable path and individual child arguments.
- All other settings retain their default behavior described in the first example.
Prebuilt binary is included in bin/. Release builder is included in
scripts/. A GUI tester for running and inspecting SilentRunner commands
is included in tools/.
Developed in C++ with a focus on a minimal, self-contained Windows binary without external dependencies, using the Windows API directly. The project uses Snapshot Toolset to provide anchored project snapshots for ChatGPT-assisted patch generation and application. ChatGPT also assists with design and documentation. Contributions, bug reports, and security notes are welcome.
Released under the MIT License --- see LICENSE for details.