# How Worktrunk Command Execution Differs from `Command::output()`

> Discover how Worktrunk's custom Cmd wrapper enhances command execution beyond std::process::Command::output with structured tracing, Git sanitization, streaming output, and signal forwarding.

- Repository: [Maximilian Roos/worktrunk](https://github.com/max-sixty/worktrunk)
- Tags: internals
- Published: 2026-09-14

---

**Worktrunk replaces the standard `std::process::Command::output()` method with a custom `shell_exec::Cmd` wrapper that adds structured tracing, automatic Git environment sanitization, streaming output support, and signal forwarding to every subprocess invocation.**

The `max-sixty/worktrunk` repository implements a Rust CLI for managing git worktrees. Rather than invoking `Command::output()` directly, every external command flows through the **`shell_exec::Cmd`** abstraction defined in [`src/shell_exec.rs`](https://github.com/max-sixty/worktrunk/blob/main/src/shell_exec.rs). This design centralizes cross-cutting concerns like logging, environment hygiene, and process lifecycle management that raw `std::process::Command` leaves to individual call sites.

## The `shell_exec::Cmd` Abstraction

Worktrunk's command execution centers on the `Cmd` struct, which wraps `std::process::Command` with domain-specific behavior for git operations. While `Command::output()` returns raw bytes that the caller must parse and log manually, `Cmd` provides methods like **`run()`**, **`stream()`**, and **`pipe_into()`** that handle output modes, error conversion, and observability automatically.

### Structured Tracing with `CommandTrace`

Every command spawned through `Cmd` emits a `[wt-trace]` record via the `CommandTrace` enum. In [`src/trace/emit.rs`](https://github.com/max-sixty/worktrunk/blob/main/src/trace/emit.rs), the implementation calls **`CommandTrace::complete`** or **`CommandTrace::fail`** to log the execution result, worktree context, and exit status. This eliminates the need for manual `println!` statements at each call site and ensures uniform observability across the codebase.

### Git Environment Sanitization

When executing git commands inside worktrees, inherited environment variables like `GIT_DIR` or `GIT_WORK_TREE` can accidentally hijack repository discovery. The **`Cmd::scrub_git_discovery_env()`** method removes these variables before spawning the child process, ensuring the command operates on the intended repository. This safety check is applied consistently in files like [`src/commands/worktree/add.rs`](https://github.com/max-sixty/worktrunk/blob/main/src/commands/worktree/add.rs).

### Streaming vs. Buffered Output

Unlike `Command::output()`, which buffers the entire stdout and stderr before returning, `Cmd` offers **`stream()`** for line-by-line output inheritance. This keeps the CLI responsive during long-running operations like `git fetch`, whereas `output()` would block until completion and consume memory proportional to the output size.

### Signal Propagation and Error Handling

`Cmd` handles process signals explicitly. When a user sends `SIGINT` or `SIGTERM`, Worktrunk forwards these signals to the child process group rather than orphaning the process. The wrapper also converts exit codes into **`anyhow::Result`** types and exposes precise signal information through helpers like **`interrupt_signal()`**, whereas `Command::output()` requires manual inspection of `ExitStatus`.

## Code Examples in Practice

Capturing output with automatic tracing and environment scrubbing:

```rust
// From src/commands/worktree/add.rs
Cmd::new("git")
    .args(["worktree", "add", "-b", &branch, &path])
    .current_dir(&repo_root)
    .context(&branch)               // adds worktree context to traces
    .scrub_git_discovery_env()      // prevents GIT_* env leakage
    .run()?;                        // returns anyhow::Result<String>

```

Streaming output for real-time user feedback:

```rust
// Pattern from src/commands/worktree/remove.rs
Cmd::new("git")
    .args(["worktree", "remove", &worktree_path])
    .current_dir(&repo_root)
    .context(&branch)
    .scrub_git_discovery_env()
    .stream()?;        // inherits stdio line-by-line

```

Handling signals in long-running operations:

```rust
// SIGINT is forwarded to the git process automatically
Cmd::new("git")
    .args(["fetch", "--all"])
    .current_dir(&repo_root)
    .context(&branch)
    .scrub_git_discovery_env()
    .run()?;

```

## Summary

- **Worktrunk** uses **`shell_exec::Cmd`** instead of raw `Command::output()` to centralize command execution logic in [`src/shell_exec.rs`](https://github.com/max-sixty/worktrunk/blob/main/src/shell_exec.rs).
- The **`CommandTrace`** infrastructure in [`src/trace/emit.rs`](https://github.com/max-sixty/worktrunk/blob/main/src/trace/emit.rs) provides structured logging for every spawn via `complete` and `fail` events.
- **`scrub_git_discovery_env()`** prevents accidental git repository hijacking by sanitizing `GIT_DIR` and `GIT_WORK_TREE` variables.
- **`stream()`** enables responsive CLI output during long operations, unlike the buffering behavior of `output()`.
- Signal forwarding and **`anyhow::Result`** conversion provide robust error handling that `Command::output()` lacks.

## Frequently Asked Questions

### Why doesn't Worktrunk use `Command::output()` directly?

Worktrunk requires consistent tracing, environment sanitization, and signal handling across dozens of git invocations. Reimplementing these checks at every `Command::output()` call site would risk inconsistencies and bugs. The `Cmd` wrapper enforces these policies uniformly according to the source code in [`src/shell_exec.rs`](https://github.com/max-sixty/worktrunk/blob/main/src/shell_exec.rs).

### How does `Cmd::stream()` differ from `Command::stdout(Stdio::inherit())`?

While `Command` allows inheriting stdout, `Cmd::stream()` combines inheritance with structured tracing, error handling, and the **`context()`** worktree identifier. It also integrates with Worktrunk's signal forwarding system, which raw `Command` configurations do not provide automatically.

### What happens to git environment variables when running commands?

**`Cmd::scrub_git_discovery_env()`** removes `GIT_DIR` and `GIT_WORK_TREE` from the child's environment before execution. This ensures that commands running inside a Worktrunk-managed worktree do not accidentally reference the parent repository or other worktrees, as implemented in [`src/commands/worktree/add.rs`](https://github.com/max-sixty/worktrunk/blob/main/src/commands/worktree/add.rs).

### Does `Cmd` support piping output between commands?

Yes. The **`pipe_into()`** method allows chaining commands where the output of one process feeds into another, while maintaining the same tracing and error handling guarantees provided by `run()` and `stream()`. This is part of the consistent API that `shell_exec::Cmd` provides over raw `Command` usage.