release
Worktrunk release workflow. Use when user asks to "do a release", "release a new version", "cut a release", or wants to publish a…
CLI output formatting standards for worktrunk. Load before editing any code that calls warning_message, hint_message, error_message, info_message, eprintln, or println, or that produces strings the user will see (CLI help, progress UI, snapshot text). Documents ANSI color
Claude Code
$ npx -y skills add max-sixty/worktrunk --skill writing-user-outputs --agent claude-code
Codex
$ npx skills add max-sixty/worktrunk --skill writing-user-outputs -a codex -y
Cursor
$ npx skills add max-sixty/worktrunk --skill writing-user-outputs -a cursor -y
Gemini CLI
$ npx skills add max-sixty/worktrunk --skill writing-user-outputs -a gemini-cli -y
GitHub Copilot
$ npx skills add max-sixty/worktrunk --skill writing-user-outputs -a github-copilot -y
OpenCode
$ npx skills add max-sixty/worktrunk --skill writing-user-outputs -a opencode -y
Windsurf
$ npx skills add max-sixty/worktrunk --skill writing-user-outputs -a windsurf -y
name: writing-user-outputs description: CLI output formatting standards for worktrunk. Load before editing any code that calls warning_message, hint_message, error_message, info_message, eprintln, or println, or that produces strings the user will see (CLI help, progress UI, snapshot text). Documents ANSI color nesting rules, message patterns, and output system architecture. metadata: internal: true
Worktrunk uses one file-based directive for shell integration:
1. Shell wrapper creates a temp file via `mktemp` 2. Shell wrapper sets `WORKTRUNK_DIRECTIVE_CD_FILE` 3. wt writes a raw path to the file 4. Shell wrapper changes directory to that path after wt exits
`--execute` always launches its external program directly from wt, with the selected worktree as its working directory. It does not use a directive.
The output system handles shell integration automatically. Just call output functions — they do the right thing regardless of whether shell integration is active.
// NEVER DO THIS - don't check mode in command code
if is_shell_integration_active() {
// different behavior
}
// ALWAYS DO THIS - just call output functions
eprintln!("{}", success_message("Created worktree"));
output::change_directory(&path)?; // Writes to directive file if set, else no-op**Printing output:**
Use `eprintln!` and `println!` from `worktrunk::styling` (re-exported from `anstream` for automatic color support and TTY detection):
use worktrunk::styling::{eprintln, println, stderr};
// Status messages to stderr
eprintln!("{}", success_message("Created worktree"));
// Primary output to stdout (tables, shell code, pipeable)
println!("{}", table_output);
// Flush before interactive prompts
stderr().flush()?;`src/` holds two crates, and the path differs between them: `worktrunk::styling` from the binary's modules — the ones `src/main.rs` declares (`commands`, `cli`, `display`, …), which the examples throughout this skill are written for — and `crate::styling` from the library's, the ones `src/lib.rs` declares (`git`, `config`, `shell_exec`, …), where `worktrunk::` does not resolve at all. The guard tests accept either, so the compiler is the only thing that tells you the path is wrong for the file.
Which `println!` is in scope decides whether a closed pipe panics: std's panics on the `BrokenPipe` write error, anstream's drops it. `wt … | head` closes the pipe, so command code imports the `worktrunk::styling` one and no `std::println!` is left in `src/`.
The stderr macros carry the same rule for a different consequence: anstream's `eprint!` / `eprintln!` strip ANSI when stderr isn't a terminal, std's keep it, so a file importing one but not the other writes escapes on one line of a message block and not the next under `wt … 2>log`. `eprint!` is the half that slips — it has no newline, so it gets reached for mid-block in a file that imported only `eprintln`. Every bare `eprint!` / `eprintln!` under `src/` must resolve to anstream's: import it, or qualify the call as `styling::eprintln!(…)`. `check_stderr_macros_come_from_styling` in `tests/integration_tests/output_system_guard.rs` holds that statically, since no snapshot can — the suite forces `CLICOLOR_FORCE=1`, so both printers emit color and a snapshot agrees whichever macro is in scope. Its `STD_STDERR_ALLOWED_PATHS` exempts whole files, not calls, so an entry is only right where std's macro is right throughout.
A write that names no macro misses that scan entirely, since the scan looks for the name: `writeln!(std::io::stderr(), …)`, `std::io::stderr().write_all(…)`, a locked handle, a bound one. `check_raw_stderr_writes_go_through_anstream` refuses those shapes too, with its own `RAW_STDERR_ALLOWED_PATHS` — currently just `progress.rs`, whose spinner holds one lock across a frame (anstream's `stderr()` has none) and is tty-gated, so nothing of its output ever reaches a redirected stderr. The shape to watch for is a message rendered in one place and printed several layers below, where the printer has no idea it is handling narration: `Cmd::delayed_stream`'s progress line is the example, and a raw handle there made it the only colored line in a redirected `wt switch` log.
**Output whose ANSI is already decided** declares that once at the top of the command with `worktrunk::styling::ColorChoice::Always.write_global()` and then prints through the same anstream macros — the statusline a shell prompt or Claude Code renders, and the `--help-page` document whose escapes the docs pipeline turns into HTML (`--plain` and `--help-md` declare `Never` the same way). Neither consumer is ever a tty, so without the declaration anstream would strip their color every time — and the test suite would not catch it, because it forces color with `CLICOLOR_FORCE=1`; `test_color_follows_the_consumer` pins the unforced behavior. Declare `Always` only when the pipe is a courier rather than the destination; anything a person reads directly stays on plain anstream, which is what strips color on a pipe and honors `NO_COLOR`.
**`--format=json` answers** use `crate::output::print_json` at the command handler. Inner operations return typed facts; when work can complete before a later failure, retain those facts in `CommandFailure<T>`. The outer handler can use `finish_output` to emit the available result once and return the original error for main's readable stderr diagnostics and native exit handling. A path with no result emits nothing; a legitimate JSON null remains a result.
**Shell integration functions** (`src/output/global.rs`):
| Function | Purpose | |----------|---------| | `change_directory(path)` | Shell cd after wt exits (writes to directive file if set) | | `execute(argv)` | Run an external program in the selected worktree | | `terminate_output()` | Reset ANSI state on stderr | | `is_shell_integration_active()` | Check if directive file set (rarely needed)
Worktrunk is a CLI for Git worktree management, designed for parallel AI agent workflows
Worktrunk release workflow. Use when user asks to "do a release", "release a new version", "cut a release", or wants to publish a…
Worktrunk-specific guidance for tend CI workflows. Adds codecov polling, Rust test commands, labels, and review criteria on top…
Guidance for Worktrunk (the `wt` CLI) — git worktree management, hooks, and config. Load when working out which worktree a `wt`…
Create a new worktrunk worktree (optionally in another repo) and switch this session's working directory into it. Use when…
Exercise git tools, resources, and prompts against a live HTTP server via MCP JSON-RPC over curl. Starts the server, surfaces the…
Non-interactive hunk-level git staging, unstaging, discarding, undoing, fold, amend, squash, commit splitting, and commit…