writing-user-outputs について
Worktrunk の開発者向けコントリビュータースキルで、CLI 出力の構成と書式ルールを説明します。シェル連携、標準出力と標準エラーの振り分け、JSON、ページャー、メッセージ規約を扱います。
主な機能
- 01シェルディレクティブ連携の説明
- 02回答と説明文を出力先別に振り分け
- 03JSON と CLI メッセージの書式化
- 04ANSI カラーとパイプ切断への対応
- 05長い人間向け出力のページ送り
- 06出力の安全性に関する規約
活用シーン
- 01Worktrunk コマンドの出力実装
- 02応答の標準出力・標準エラーの選択
- 03カラー、ページ送り、JSON 動作の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ max-sixty/ worktrunk --skill writing-user-outputsインストール後、Claude Code で/writing-user-outputsを入力して読み込まれたか確認します。
詳細
出力システムのアーキテクチャ
シェル統合
Worktrunk は、シェル統合にファイルベースのディレクティブを1つ使用します。
- シェルラッパーが
mktempで一時ファイルを作成します - シェルラッパーが
WORKTRUNK_DIRECTIVE_CD_FILEを設定します - wt がファイルにパスをそのまま書き込みます
- wt の終了後、シェルラッパーがそのパスにディレクトリを変更します
--execute は常に、選択したワークツリーを作業ディレクトリとして、外部プログラムを wt から直接起動します。ディレクティブは使用しません。
出力関数
出力システムがシェル統合を自動的に処理します。出力関数を呼び出すだけで、シェル統合が有効かどうかにかかわらず、適切に動作します。
// 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
出力の表示:
worktrunk::styling の eprintln! と println! を使用します(自動カラー対応と TTY 検出のため、anstream から再エクスポートされています)。
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/ には2つのクレートがあり、どちらのクレートかによってパスが異なります。このスキル全体の例で使われている、バイナリのモジュール(src/main.rs が宣言する commands、cli、display など)では worktrunk::styling を、ライブラリのモジュール(src/lib.rs が宣言する git、config、shell_exec など)では crate::styling を使います。ライブラリでは worktrunk:: は解決されません。ガードテストはどちらも受け入れるため、ファイル内でパスが間違っていることを教えてくれるのはコンパイラーだけです。
どの println! がスコープ内にあるかによって、閉じられたパイプでパニックするかどうかが決まります。標準ライブラリの println! は BrokenPipe 書き込みエラーでパニックしますが、anstream の println! はエラーを無視します。wt … | head ではパイプが閉じられるため、コマンドコードでは worktrunk::styling のものをインポートし、src/ に std::println! が残らないようにします。
標準エラー出力用のマクロにも、別の影響に関する同じルールがあります。標準エラー出力が端末でない場合、anstream の eprint!/eprintln! は ANSI を除去しますが、標準ライブラリのものは除去しません。そのため、一方だけをインポートしたファイルでは、wt … 2>log の実行時に、メッセージブロック内の行によってエスケープシーケンスが出力されたり、されなかったりします。見落としやすいのは改行を出力しない eprint! です。eprintln! だけをインポートしたファイルで、ブロックの途中に到達する場合があります。src/ 内のすべての裸の eprint!/eprintln! は、anstream のものに解決される必要があります。インポートするか、styling::eprintln!(…) のように呼び出しを修飾してください。tests/integration_tests/output_system_guard.rs の check_stderr_macros_come_from_styling は、このルールを静的にチェックします。スナップショットではチェックできないためです。テストスイートでは CLICOLOR_FORCE=1 が強制されるため、どちらのマクロがスコープ内にあっても両方の出力に色が付き、スナップショットは同じ結果になります。その STD_STDERR_ALLOWED_PATHS は呼び出し単位ではなくファイル全体を例外にします。そのため、標準ライブラリのマクロがファイル内のどこでも正しい場合に限り、登録してください。
マクロ名を指定しない書き込みは、名前を検索するこのスキャンでは検出されません。たとえば、writeln!(std::io::stderr(), …)、std::io::stderr().write_all(…)、ロックしたハンドル、変数に束縛したハンドルなどです。check_raw_stderr_writes_go_through_anstream は、独自の RAW_STDERR_ALLOWED_PATHS を使って、こうした形式も拒否します。現在、例外は progress.rs だけです。スピナーがフレーム全体にわたって1つのロックを保持し(anstream の stderr() にはそのようなロックがありません)、TTY の場合にのみ動作するため、その出力がリダイレクトされた標準エラー出力に届くことはありません。注意すべきなのは、ある場所でメッセージを描画し、何層も下で出力する形式です。その場合、出力側はナレーションを扱っていることを認識できません。Cmd::delayed_stream の進捗行がその例です。そこに生のハンドルを使った結果、リダイレクトされた wt switch のログで唯一の色付き行になってしまいました。
ANSI がすでに決定されている出力では、コマンドの冒頭で worktrunk::styling::ColorChoice::Always.write_global() を使って一度だけ宣言し、以降は同じ anstream マクロで出力します。シェルプロンプトまたは Claude Code がレンダリングするステータスラインや、エスケープシーケンスがドキュメントのパイプラインで HTML に変換される --help-page ドキュメントがこれに該当します(--plain と --help-md も同様に Never を宣言します)。どちらの出力先も TTY ではないため、この宣言がないと anstream は毎回カラーを削除します。テストスイートでは CLICOLOR_FORCE=1 で色を強制しているため、この動作は検出されません。強制指定なしの動作は test_color_follows_the_consumer で固定しています。パイプが出力先ではなく運搬手段である場合に限り Always を宣言してください。人が直接読むものはすべて通常の anstream に出力します。通常の anstream はパイプ上で色を削除し、NO_COLOR に従います。
--format=json の応答では、コマンドハンドラーで crate::output::print_json を使用します。内部処理は型付きの事実を返します。後続で失敗する前に処理が完了する場合は、それらの事実を CommandFailure<T> に保持します。外側のハンドラーでは finish_output を使って利用可能な結果を一度だけ出力し、元のエラーを返すことができます。これにより、main が読みやすい診断を stderr に出力し、ネイティブの終了処理を行えます。結果がないパスでは何も出力しません。有効な JSON の null は結果として扱います。
シェル統合関数(src/output/global.rs):
| 関数 | 目的 |
|---|---|
change_directory(path) | wt の終了後にシェルで cd します(設定されている場合はディレクティブファイルに書き込みます) |
execute(argv) | 選択したワークツリーで外部プログラムを実行します |
terminate_output() | stderr の ANSI 状態をリセットします |
is_shell_integration_active() | ディレクティブファイルが設定されているか確認します(通常は不要です) |
pre_hook_display_path(path) | pre-hook 用の表示パスを計算します |
post_hook_display_path(path) | post-hook 用の表示パスを計算します |
メッセージ整形関数(worktrunk::styling):
| 関数 | 記号 | 色 |
|---|---|---|
success_message() | ✓ | 緑 |
progress_message() | ◎ | シアン |
info_message() | ○ | 記号は暗く、テキストは通常 |
warning_message() | ▲ | 黄 |
hint_message() | ↳ | 暗色 |
error_message() | ✗ | 赤 |
prompt_message() | ❯ | シアン |
セクション見出し(worktrunk::styling):
use worktrunk::styling::format_heading;
// Plain heading
format_heading("BINARIES", None) // => "BINARIES" (cyan)
// Heading with suffix
format_heading("USER CONFIG", Some("@ ~/.config/wt.toml"))
// => "USER CONFIG @ ~/.config/wt.toml" (title cyan, suffix plain)
stdout と stderr
判断の基準: stdout はコマンドの回答を出力し、stderr は回答を生成する過程の説明を出力します。区別するポイントは、対象読者ではなく回答か説明かです。wt list は「ユーザー向け」ですが、それ自体が回答なので stdout に出力されます。「これはユーザーへのメッセージか?」では区別できません。ほとんどすべての出力が該当するためです。
- stdout → ユーザーが選択した形式での回答です。データ(テーブル、JSON、シェルコード、展開済みテンプレート)と
--dry-runのプレビューはいずれも該当します。何も変更されない場合、プレビューが回答のすべてだからです。人間が読みやすい形式の出力もここに含まれます。パイプに接続すると色は自動的に除去されるため(anstream)、wt list | grepも安全に使えます。 - stderr → 実行に関する説明です。進行状況、成功/警告/エラーメッセージ、ヒント、対話型プロンプト、
-v/-vvの診断情報が該当します。 - directive file → wt の終了後に読み込まれる、raw 形式の cd パスです。
同じ行でも、モードによって出力先が変わることがあります。wt config shell uninstall はファイルを削除するため、✓ Removed … @ ~/.zshrc はすでに発生した副作用を説明するだけなので stderr に出力されます(編集されたファイルが回答であり、stdout は空です)。wt config shell uninstall --dry-run は何も変更しないため、○ Will remove … @ ~/.zshrc が唯一の回答となり、stdout に出力されます。変わるのは文言ではなく、副作用が回答となるかどうかです。
プレビューの出力先が分かれる場合は、--format=json のペイロードを基準にします。json に含まれる行は stdout に、json から省略される説明は stderr に出力します。wt step prune --dry-run は削除計画を stdout に出力します(json が出力するものと同じ計画です)が、「Skipped young-branch (younger than 1d)」や「nothing to remove」は stderr に出力します。これらすべてに当てはまらないケースが 1 つあります。wt config shell install 中の ? による再プレビューのように、対話型プロンプトの内部に表示されるプレビューは、プロンプト中の説明なので stderr に出力します。
例:
wt list、wt config show→ 人間が読みやすいテーブル/ダンプ形式、または--format=jsonのいずれも stdout に出力wt step prune --dry-run→ 削除計画を stdout に出力(人間が読みやすい形式または json)。「nothing to remove」と、若すぎるブランチに関する注意は stderr に出力wt config shell init→ シェルコードを stdout に出力(eval用)wt switch→ ステータスメッセージのみ(パイプに渡すものはありません)
出力をページャーに表示するタイミング
人間向けの長い stdout 出力は crate::help_pager::show_help_in_pager に渡します。このヘルパーは内部で TTY を検出するため、パイプ(wt … | grep)も引き続き機能します。
見出し、余白、構造を含む人間向けの出力で、1 画面を超える可能性がある場合はページャーに表示します。パイプ処理を前提とするデータ(テーブル、JSON、シェルコード)、短い出力、委譲先のツールによってすでにページャーに表示されている出力(git diff)には使いません。
ページャーに表示する例:--help、wt config show、wt hook show、wt step {commit,squash} --dry-run。表示しない例:wt list、wt step diff、wt step eval、パイプ処理を前提に設計された --show-prompt。
まず出力全体を String に組み立て(ストリーム出力はしないでください)、その後、次のようにします。
crate::help_pager::show_help_in_pager(&out, true)?;
ページャーが無効または利用できない場合、stdout が TTY でない場合、あるいはページャーを起動できない場合、ヘルパーは通常の stdout 出力にフォールバックします。結果を伝播させ、ネイティブのシグナルエラーやキャンセルがコマンドのクリーンアップまで届くようにしてください。
セキュリティ
WORKTRUNK_DIRECTIVE_CD_FILEには raw 形式のパスが格納されます(シェルによる解析は行われません)。そのため、エイリアス/フックの子プロセスに安全に渡せます。このパスに書き込む本体ができるのは、最大でもcdのリダイレクトだけです。
すべての directive 環境変数は、デフォルトで shell_exec::scrub_directive_env_vars() によって起動されたサブプロセスから削除されます。ネストしたコマンドが親シェルをリダイレクトする可能性がある場合は、DirectivePassthrough::inherit_from_env() によって CD ファイルを再度追加します。
Windows との互換性(Git Bash / MSYS2)
Git Bash を使う Windows では、mktemp は /tmp/tmp.xxx のような POSIX 形式のパスを返します。ネイティブの Windows バイナリ(wt.exe)が directive file に書き込むには、Windows 形式のパスが必要です。
明示的なパス変換は不要です。 MSYS2 はネイティブの Windows バイナリを起動する際、環境変数内の POSIX パスを自動的に変換します。そのため、シェルラッパーでは $directive_file をそのまま使えます。詳しくは次を参照してください。
https://www.msys2.org/docs/filesystem-paths/
CLI 出力の書式設定基準
ユーザーメッセージの原則
出力メッセージでは、ユーザーが指定した引数(フラグ、オプション、値)をメッセージ内で反映し、その選択を確認できるようにします。
// User runs: wt switch --create feature --base=main
// GOOD - acknowledges the base branch
"Created new worktree for feature from main @ /path/to/worktree"
// BAD - ignores the base argument
"Created new worktree for feature @ /path/to/worktree"
「you/your」の代名詞は避けます: メッセージではユーザーに呼びかけず、対象を直接示します。「Run」「Use」「Add」のような命令形は、簡潔な CLI の慣用表現なので問題ありません。
// BAD - "To rebase your changes onto main, run wt merge"
// GOOD - "To rebase onto main, run wt merge"
冗長な括弧内の内容は避けます: 括弧内のテキストには、すでに述べた内容の言い換えではなく、新しい情報を含めます。
// BAD - parentheses restate "no changes"
"No changes after squashing 3 commits (commits resulted in no net changes)"
// GOOD - clear and concise
"No changes after squashing 3 commits"
// GOOD - parentheses add supplementary info
"Committing with default message... (3 files, +45, -12)"
括弧内の内容には、スタイルの異なる 2 種類があります:
-
統計情報の括弧 → グレー(
[90m明るい黒):省略しても意味が損なわれない補足的な数値情報です。✓ Merged to main (1 commit, 1 file, +1) ◎ Squashing 2 commits into a single commit (2 files, +2)... -
理由を示す括弧 → メッセージの色:アクションが実行される理由を説明するもので、理解に不可欠です。
◎ Removing feature worktree & branch in background (same commit as main, _)
統計情報は、あくまで任意の補足情報です。理由は「なぜ安全なのか/なぜ実行されているのか」に答えるもので、メッセージの主な内容に含めます。理由を示す括弧内の記号も、通常のスタイルで表示されます(下記の「記号のスタイル」を参照)。
フックが別のディレクトリで実行される場合はパスを表示します: フックがユーザーの現在地(または最終的な移動先)とは別のワークツリーで実行される場合は、パスを表示します。適切なヘルパー関数を使用します。
-
pre-hook と手動の
wt hook— ユーザーは cwd におり、cd は発生しません。output::pre_hook_display_path(hooks_run_at)を使用します。 例:pre-commit、pre-merge、pre-remove、手動のwt hook post-merge。 -
post-hook — シェル統合が有効な場合、ユーザーは移動先に cd します。
output::post_hook_display_path(destination)を使用します。 例:pre-start、post-switch、post-start、post-merge(削除後)。
// Pre-hooks: user is at cwd, no cd happens
run_hook_with_filter(..., crate::output::pre_hook_display_path(ctx.worktree_path))?;
// Post-hooks: user will cd to destination if shell integration active
ctx.spawn_post_create_commands(crate::output::post_hook_display_path(&destination))?;
メッセージをまたいで参照する代名詞は避けます: ヒントはエラーとは別のメッセージとして表示されます。エラーメッセージ内の内容を指す「it」のような代名詞は使わないでください。
// BAD - "it" refers to branch name in error message
// Error: "Branch 'feature' not found"
// Hint: "To create it, use --create"
// GOOD - self-contained hint
// Error: "Branch 'feature' not found"
// Hint: "To create a new branch, use --create"
見出しの大文字・小文字
ヘルプテキストの見出しにはセンテンスケースを使用します:「Configuration files」「JSON output」「LLM commit messages」。
メッセージの一貫性パターン
関連するメッセージでは、句読点と構成を統一してください。
複合アクションにはアンパサンドを使用する: 1 つの操作で複数の処理を行う場合は、& を使用します。
"Removing feature worktree & branch in background"
"Commands approved & saved to config"
節の接続にはセミコロンを使用する: 関連する情報をつなぐには、セミコロンを使用します。
"Removing feature worktree in background; retaining branch (--no-delete-branch)"
"Branch unmerged; to delete, run <underline>wt remove -D</>" // hint uses underline
"{tool} not authenticated; run <bold>{tool} auth login</>" // warning uses bold
フラグを明示的に示す: フラグによって動作が変わる場合は、括弧内にフラグを示します。
// GOOD - shows the flag explicitly
"Removing feature worktree in background; retaining branch (--no-delete-branch)"
// BAD - doesn't acknowledge user's explicit choice
"Removing feature worktree in background; retaining branch"
フラグを関連する箇所に置く: フラグの表示は、変更対象となる概念のすぐ近くに置いてください。メッセージの末尾ではなく、影響を与える名詞やアクションの直後に表示します。
// GOOD - (--force) is adjacent to "worktree" which it modifies
"Removing feature worktree (--force) & branch in background (same commit as main, _)"
// BAD - (--force) at end, disconnected from the worktree removal it enables
"Removing feature worktree & branch in background (same commit as main, _) (--force)"
この原則により、各注釈が何を変更するのかを読者がすぐに理解できます。
並列構造: 関連するメッセージは同じパターンに従ってください。
// GOOD - parallel structure with integration reason explaining branch deletion
// Target branch is bold; symbol uses its standard styling (dim for _ and ⊂)
"Removing feature worktree & branch in background (same commit as <bold>main</>, <dim>_</>)" // Integrated
"Removing feature worktree in background; retaining unmerged branch" // Unmerged
"Removing feature worktree in background; retaining branch (--no-delete-branch)" // User flag
記号のスタイル: 記号は色と一体であり、そのスタイルは表示上の選択ではなく、記号のアイデンティティの一部です。各記号には定められた外観があり、あらゆる状況で維持する必要があります。
_と⊂— 暗色(統合/安全に削除できることを示す記号)+Nと-N— 緑/赤(差分を示す記号)
色付きメッセージ(シアンの進行状況、緑の成功など)に記号を含める場合は、記号の前でメッセージの色を閉じ、記号が本来のスタイルで表示されるようにします。その後、記号の後でメッセージの色を再び有効にします。例については、src/output/handlers.rs の FlagNote を参照してください。これはフラグ確認の注記(統合理由など)を扱い、after(color) メソッドによって記号の後にメッセージの色を再び有効にし、適切に色を切り替えます。
制限事項にはコンマ+「but」+エムダッシュを使用する: 制限事項とその理由を含めて結果を述べる場合:
// Outcome, but limitation — reason
"Worktree for feature @ ~/repo.feature, but cannot change directory — shell integration not installed"
このパターンでは、次のように伝えます。
- 成功したこと(パスにワークツリーが存在する)
- 「but」を使って、できなかったこと(
cdできない)を示す - エムダッシュを使って、その理由(シェル統合の状態)を説明する
シェル統合に関する警告メッセージとヒントの完全な仕様については、src/output/shell_integration.rs の compute_shell_warning_reason() を参照してください。
判断は一度だけ行う: バックグラウンド操作では、条件を事前に確認してメッセージを表示し、その判断を明示的に渡します。バックグラウンドスクリプトで再確認しないでください。
// GOOD - check once, pass decision
let should_delete = check_if_merged();
show_message_based_on(should_delete);
spawn_background(build_command(should_delete));
// BAD - check twice (once for message, again in background script)
let is_merged = check_if_merged();
show_message_based_on(is_merged);
spawn_background(build_command_that_checks_merge_again()); // Duplicate check!
警告の順序
基本原則: 評価中に判明した状態に関するメッセージ(警告、情報通知)は、その評価から続くアクションメッセージより前に表示します。
コマンドが状態を評価して予期しないことを検出し、それでも処理を続ける場合は、そのメッセージを先に表示します。
▲ Auto-staging 1 untracked path:
notes.md
◎ Generating commit message...
次のようにはしません。
◎ Generating commit message...
▲ Auto-staging 1 untracked path:
notes.md
アクション自体によって発生した警告(実行中に何かが失敗した場合)は、自然な順序としてアクションの後に表示します。
メッセージの種類
成功と情報: 成功(✓)は、何かが作成または変更されたことを示します。情報(○)は、何も変更せずに状態を通知します。
| 成功 ✓ | 情報 ○ |
|---|---|
| "Created worktree for feature" | "Switched to worktree for feature" |
| "Created new worktree for feature" | "Already on worktree for feature" |
| "Commands approved & saved" | "All commands already approved" |
リスト内で行ごとのマーカーとして使われる単独の記号にも、同じルールが適用されます。
✓ は完了したアクションを示し、状態を示すことはありません。項目ごとのステータスを報告するリストでは、○(状態を通知)または ❯(承認/ユーザー入力待ち)で示します。これは wt hook show と wt config approvals list で共通して使う表記です。行ごとの ✓ は、コマンドが直前に実行した処理の結果(シェルのインストールアクション行、-v サブプロセスのトレース記号)にのみ使用します。
ヒントと情報: ヒントはユーザーにアクションを促すか、追加の必須ではないコンテキスト(ユーザーに必須ではないものの、役立つ可能性のある補足情報)を提供します。情報は、何も変更せずに状態を通知します。
| ヒント ↳ | 情報 ○ |
|---|---|
"To continue, run wt merge" | "Already up to date with main" |
| "Commit or stash changes first" | "Skipping hooks (--no-hooks)" |
| "Branch can be deleted" | "Worktree preserved (main worktree)" |
| "Failed command, exit code 128:" |
警告の配置: 想定外のことが起きた場合は、どこかで警告してください。配置場所は問題の性質によって異なります。
Is it unexpected?
├── No → Silent (e.g., gh not installed when no GitHub remote)
└── Yes → Warn somewhere:
├── Immediate impact OR temporary → Inline (warning_message or in-band indicator)
├── Persists until user action → wt config show (can be checked later)
└── Not user-fixable → log::warn! (developer diagnostics)
インライン警告は、現在のコマンドに影響する問題に使用します。
| 問題 | インラインにする理由 |
|---|---|
| CI の取得中に発生したレート制限 | 一時的なもので、次回には発生しない可能性があります |
| ネットワークタイムアウト | 一時的なもので、再試行すれば解決する可能性があります |
| 操作中にフックが失敗 | このコマンドにただちに影響します |
wt config show は、ユーザーが修正するまで継続する問題に使用します。これらの問題で毎回のコマンドを中断する必要はありません。ユーザーは調査時に診断情報を確認できます。
| 問題 | config show にする理由 |
|---|---|
gh が認証されていない | ユーザーが gh auth login を実行します |
| シェル統合の設定ミス | ユーザーがシェル設定を更新します |
| 設定構文エラー | ユーザーが設定ファイルを修正します |
log::warn!() は、ユーザーが修正できない問題に使用します。開発者のデバッグに役立ちますが、ユーザー向けの出力を煩雑にしないようにします。
| 問題 | log::warn! にする理由 |
|---|---|
| JSON の解析エラー(API の変更) | コードの修正が必要です |
| 内部不変条件に違反 | 開発者側のバグです |
ヒント内のコマンドの提案: ヒントに実行可能なコマンドを含める場合は、「To X, run Y」の形式を使用します。コピーしやすいよう、コマンドで終わらせてください。
// GOOD - command at end for easy copying
"To delete the unmerged branch, run wt remove feature -D"
"To rebase onto main, run wt step rebase or wt merge"
// GOOD - recovery command after shadowing a remote branch
"To switch to the remote branch, delete this branch and run without --create: wt remove --foreground feature && wt switch feature"
// BAD - command without context
"wt remove feature -D deletes unmerged branches"
// BAD - command not at end (hard to copy)
"Run wt switch feature (without --create) to switch to the remote branch"
特定のコマンドを伴わない一般的なアクションの案内では、直接的な命令形のほうが明確です。
// GOOD - direct imperative for general guidance
"Commit or stash changes first"
"Run from inside a worktree, or specify a branch name"
// VERBOSE - "To proceed" adds nothing
"To proceed, commit or stash changes first"
説明とコマンドを1つのメッセージにまとめる場合: 復旧コマンドを含む警告/エラーでは、セミコロンでつなぎ、ヒントと同様に復旧方法を「To X, run Y」の順にしてください。これにより、行末がコマンドになります。警告/エラー内のコマンドには <bold> を使用してください(<underline> を使用するのはヒントのみです)。
// Warning with inline recovery command (bold for commands)
warning_message("Failed to restore stash; to restore manually, run <bold>git stash pop {ref}</>")
warning_message("{tool} not authenticated; run <bold>{tool} auth login</>")
// For longer suggestions, use separate hint message (underline for commands)
warning_message("Failed to restore stash")
hint_message("To restore manually, run <underline>git stash pop {ref}</>")
1つのヒント内で複数の提案をする場合: セミコロンで提案をつなぐ場合は、ターミナルにコピーしやすいよう、最もよく使われるコマンドを最後に置いてください。
// GOOD - common action (create) last, easy to select and copy
"To list branches, run wt list --branches; to create a new branch, run wt switch feature --create"
// BAD - common action buried, harder to copy
"To create a new branch, run wt switch feature --create; to list branches, run wt list --branches"
適切なシェルエスケープには、worktrunk::styling の suggest_command() を使用してください。
ユーザー向けのメッセージにはすべて、記号またはガターが必要です。
セクションタイトル: セクション分けされた出力(wt hook show、wt config show)には、worktrunk::styling の format_heading() を使用してください(上記の説明を参照)。
インタラクティブなプロンプトと非インタラクティブなヒント
プロンプトとヒントは目的が異なり、--yes の動作も異なります。
プロンプトは、ワークフロー内で想定される手順です。ユーザーは確認を求められると分かったうえでコマンドを実行しています。wt merge 中のフック承認、設定更新の確認、シェルのインストール確認が該当します。ユーザーは質問されることを予期し、あらかじめ回答しておきたい(たとえば CI で実行する)ため、--yes はこれらを省略します。
セットアッププロンプトは予期しないものです。ユーザーが wt merge を実行したところ、知らなかった LLM の設定やシェル統合について尋ねられるケースです。--yes でこれらを省略してはいけません。フック承認を省略するために --yes を指定したユーザーは、シェルの自動設定に同意したわけではありません。非インタラクティブモード(TTY なし)では、エラーにせず、ヒントに切り替えるか、何も表示せずにスキップします。
| 種類 | 例 | --yes | 非 TTY での動作 |
|---|---|---|---|
| ワークフロープロンプト | フック承認、設定更新 | 省略する | エラー(NotInteractive) |
| セットアッププロンプト | LLM の設定、シェル統合 | 影響なし | ヒントまたは何も表示せずにスキップ |
非 TTY 時の切り替えパターン:
- ヒント — 実行のたびにオプションを知らせるメリットがある場合に使います。
シェル統合のヒント:
↳ To enable automatic cd, run wt config shell install - 何も表示せずにスキップ — ヒントを表示すると煩わしくなる場合に使います。コミット生成のセットアッププロンプトは何も表示せずにスキップします。未設定のケースは、別のフォールバックヒント(
emit_hint_if_needed)によって、コミットのたびにすでに案内されるためです。 - エラー — 同意なしに続行すると安全でないワークフロープロンプト(フック承認)に限ります。エラーには解決方法のヒントを含めます。
↳ To skip prompts in CI/CD, add --yes
重要な不変条件:
- 非 TTY モードで表示するヒントによって、スキップフラグを設定してはいけません。ヒントはプロンプトではないため、非 TTY で実行するたびに繰り返し表示する必要があります。
--yesは「このプロンプトが出ることを予期していた」という意味です。ユーザーが実行したワークフロープロンプトに適用され、セットアップや設定を検出するためのプロンプトには適用されません。
空行の原則
基本原則: ユーザーが読んで検討するテキストを提示する場合は、読みやすいように余白を設けます。出力をパイプに渡す場合(stdout)は、解析しやすいように出力を詰めます。
具体的なルール:
-
先頭と末尾に空行を置かない — すぐに始め、すっきりと終えます。
-
プロンプトの前に空行を置き、後には置かない — プロンプトの前に「一時停止して、インタラクティブな操作が始まります」と知らせます。ユーザーの応答後は、出力を途切れさせずに続けます。空行は区切られる側の説明に属するため、呼び出し元が出力し、
prompt_yes_no_previewは出力しません。コマンド出力の先頭に表示されるプロンプト(wt config shell install、wt config plugins claude install、wt mergeの冒頭に表示されるコミット生成の提案など)は、先頭の空行になってしまうため、直前に空行を置かずに始めます。 -
フェーズの間には空行を 1 つ置く — サブ操作が完了し、別の操作が始まるときは、視覚的に区切るため空行を追加します。
-
空行を 2 つ続けない — 要素間に置ける空行は最大 1 つです。
-
ヒントは関連する内容に続けて配置する — ヒントと、それが補足するメッセージの間に空行を置いてはいけません。ヒント(↳)は補足情報であるため、間を空けずに親メッセージの直下に置きます。
// GOOD - hint directly follows its subject ↳ fish: Not configured shell extension ↳ To configure, run wt config shell install // BAD - blank line detaches hint from subject ↳ fish: Not configured shell extension ↳ To configure, run wt config shell install
プロンプトの間隔: 説明の後に続くプロンプトの前に空行を置くと、「これから別のことが起きます」と伝わり、ユーザーが読んで応答する前に視線を止めやすくなります。プロンプトの後には空行を置きません。ユーザーの入力でインタラクティブなやり取りが終わり、その判断に続いて自然に出力が流れるためです。wt step commit の冒頭に表示される以下のセットアップの提案のように、上に何もないプロンプトは区切る対象がないため、直前に空行を置かずに始めます。
❯ Configure claude for commit messages? [y/N/?] y
✓ Added to user config:
[commit.generation]
command = "..."
↳ View config: wt config show
▲ Auto-staging 1 untracked path:
a
◎ Generating commit message...
フェーズの区切り: 「フェーズ」とは、論理的に区別される操作です。上記のヒントと警告の間にある空行は、「設定のセットアップが終わり、これからメインのコマンドワークフローを実行する」ことを示しています。
インタラクティブなプロンプトは、stdin の入力待ちに入る前に stderr をフラッシュする必要があります。
eprint!("❯ Allow and remember? [y/N] ");
stderr().flush()?;
io::stdin().read_line(&mut response)?;
時間的局所性:出力は操作のすぐそばに表示する
出力は、それが説明する操作の直後に表示してください。 進捗メッセージを表示するのは、時間のかかる操作(400ms 超)の場合に限ります。git 操作、 ネットワークリクエスト、ビルドなどが該当します。
逐次操作では、すぐにフィードバックを表示してください。
for item in items {
eprintln!("{}", progress_message(format!("Removing {item}...")));
perform_operation(item)?;
eprintln!("{}", success_message(format!("Removed {item}"))); // Immediate feedback
}
悪い例(出力と操作が切り離されています):
◎ Removing worktree for feature...
◎ Removing worktree for bugfix...
← Long delay, no feedback
Removed worktree for feature ← All output at the end
Removed worktree for bugfix
時間的局所性が低い兆候:メッセージをバッファーに溜め込む、一括操作に対して成功メッセージを 1 つだけ表示する、時間のかかる操作の前に進捗を表示しない。
時間のかかる単一操作にはスピナーを使用する
項目ごとの progress_messages が適さない場合(数千ファイルの再帰コピー、有用なストリーミング出力のない長時間のサブプロセスなど)は、stderr のスピナーを使うとユーザーは状況を把握できます。パターンについては src/progress.rs(Progress)を参照してください。動詞("Copying"、"Removing")は Progress::start(verb) で固定し、TTY の場合に限定し、verbosity() >= 1 または --dry-run の場合はスキップし、高速な操作では表示されないよう起動を 300 ms 以上遅らせ、IEC バイト単位(KiB/MiB)を使用し、サマリーを表示する前に行を消去し、サマリーにもスピナーに表示されたカウンターを繰り返し含めてください(グレーの (N files · X MiB) サフィックスには format_stats_paren を使用します)。
主要な出力の後まで必須でない処理を遅らせる
後片付けやキャッシュの一括処理など、ユーザーに見えない処理は、コマンドの最後のユーザー向けメッセージの後に実行します。先頭に配置すると、ユーザーが求めていない処理によって最初の出力までの時間が遅れます。
Worktrunk では WORKTRUNK_FIRST_OUTPUT を境界として使用しています。ハンドラーは出力開始位置で早期に return するため、その return より前の処理はクリティカルパス上にあり、後の処理はそうではありません。handle_remove_command を参照してください。run_internal_sweep は handle_remove_output の後に実行されます。
情報の表示:重複せず、一度だけ表示する
進捗メッセージには、関連する情報(実行内容、件数、統計情報、コンテキスト)をすべて含めてください。成功メッセージは最小限にし、参照情報(ハッシュ、パス)を添えて完了を伝えてください。
// GOOD - detailed progress, minimal success
eprintln!("{}", progress_message("Squashing 3 commits & working tree changes into a single commit (5 files, +60)..."));
perform_squash()?;
eprintln!("{}", success_message("Squashed @ a1b2c3d"));
セクションとグリフの間隔
隣接する要素がそれぞれ異なる意味を持つ、間隔の詰まった出力(ステータスラインの各セグメント、@+1、↓1、?^| などのプレフィックス文字インジケーター)を組み立てる場合は、セクションを隙間なく並べてください。マーカーと値は一体として読めるため、視覚的な密度を保つことで行を短くできます。
例外は 絵文字 です。絵文字グリフの後にはスペースを入れてください。多くのターミナルでは絵文字が全角幅で表示され、右隣のセルにはみ出すため、スペースのない 🌔65% では月と数字が重なります。スペースを入れた 🌔 65% なら読みやすくなります。ASCII や幅の狭い Unicode マーカー(@、↓、●、⊂)ではこの問題が起きないため、隙間なく並べてください。
// GOOD - ASCII marker flush with its value
format!("@{added}"); // @+1
// GOOD - emoji gets a trailing space to clear the next cell
format!("{moon} {pct:.0}%"); // 🌔 65%
// BAD - emoji runs into the digits; terminals overlap the glyph
format!("{moon}{pct:.0}%"); // 🌔65%
スタイル定数
テーブルの描画に使用する anstyle 定数は 3 つだけです(src/styling/constants.rs)。
ADDITION:緑(差分)DELETION:赤(差分)GUTTER:明るい白の背景
それ以外には cformat! タグを使用してください。
コマンドコードでのスタイル設定
書式設定関数とともに eprintln! を使用してください。内部のスタイル設定には cformat! を使用します。
eprintln!("{}", success_message(cformat!("Created <bold>{branch}</> from <bold>{base}</>")));
eprintln!("{}", hint_message(cformat!("To continue, run <underline>wt merge</>")));
color-print タグ: <bold>、<dim>、<underline>、<bright-black>、<red>、
<green>、<yellow>、<cyan>、<magenta>
ブランチ名とステータス値 は、メッセージ内で太字にしてください。
cformat! 内のシンボル定数: output:: 関数を経由しないメッセージ
(例:GitError の Display 実装)では、シンボル定数を直接使用してください。
cformat!("{ERROR_SYMBOL} <red>Branch <bold>{branch}</> not found</>")
メッセージ内のコマンドとブランチ
コマンドやブランチ名を引用符で囲まないでください。スタイル設定で目立たせてください。
- 通常のフォントの文脈: コマンドとブランチには
<bold>を使用します - ヒント内: コマンドとデータ値(パス、
ブランチ)には
<underline>を使用します。<dim>内でも下線は安全です。閉じタグの[24mは下線だけをリセットし、薄暗い表示は維持されます。ヒント内では<bold>を避けてください。閉じタグの[22mは太字と薄暗い表示の両方をリセットするため、</bold>より後のテキストは薄暗いスタイルを失います。
// GOOD - bold in normal context
eprintln!("{}", info_message(cformat!("To continue, use <bold>wt merge</>")));
// GOOD - underline for commands in hints
eprintln!("{}", hint_message(cformat!("To see worktrees, run <underline>wt list</>")));
// BAD - quoted commands
eprintln!("{}", hint_message("To see worktrees, run 'wt list'"));
ハイパーリンク
すべての OSC 8 リンクには下線を付けます。リンクテキストは列に収まるようにサイズが調整され(#3604、:11486)、通常のコンテンツとして読めます。また、色ですでに状態(CI の判定、応答のないポートを示す暗い表示)を伝えているため、テキストがクリック可能であることを示すのが下線です。
worktrunk::styling::hyperlink(url, text) はリンクと下線をまとめて出力するため、すべての呼び出し箇所でこのルールが保たれます。末尾の [24m は、周囲の色や暗い表示をそのまま維持します。
// GOOD - the helper carries the underline
let cell = hyperlink(url, &format!(":{port}"));
// GOOD - a caller's own style wraps the link
format!("{style}{}{style:#}", hyperlink(url, text))
// BAD - hand-assembled sequences, unmarked link
format!("{}{text}{}", osc8::Hyperlink::new(url), osc8::Hyperlink::END)
リンクでないテキストは通常のままで、テキストがリンクかどうかは、それを出力するセルではなくレンダリング側で決まります。LayoutConfig::link_style は行全体について一度だけ判定するため、CI の参照と開発サーバーのポートで不一致が起こりません。リンクを含まず、下線も付かない出力先が 2 つあります。OSC 8 に対応していないターミナルでは、wt list が開発サーバーの URL をすべて表示します。ピッカーの行は Destination::picker が LinkStyle::Unlinked としてレンダリングします(このバリアントのドキュメントコメントに理由があります)。ピッカー内は、2 つの仕組みによってどこもクリックできません。行のリンクスタイルはレンダリング時に決まるため、そもそも OSC 8 が付きません。また、プレビューペインは skim によって ansi_to_tui 経由で解析され、スタイル情報は一切保持されません。そこでの下線は別の意味に使えます。プレビューペインでは 2 つの用途があります。URL に付けてリンクではなく参照であることを示す用途(pr_pane::url_line)と、タブバーのアクティブなタブに付ける用途(items::render_preview_tabs)です。
設計原則
- スタイル設定には
cformat!を使用 — 手動のエスケープコード(\x1b[...])は使用しません cformat!の変数は安全です —<bold>などのタグはコンパイル時にのみ処理されます。実行時の変数値はマークアップとして解釈されないため、ユーザーコンテンツ(ブランチ名、コミットメッセージ、パス、</>リダイレクトを含むシェルコマンド)は、エスケープせずに直接埋め込めます。変数内の</>はエスケープしないでください。余分な文字が追加されます。- YAGNI — ほとんどの出力にスタイル設定は不要です
- 適切なフォールバック — スタイル設定は出力先に応じます(NO_COLOR、TTY 検出)。プレーン出力でも、文言、間隔、既存の意味的な記号によって意味を保ちます。装飾がないことだけを理由に新しいマーカーを追加しないでください。情報が失われる場合はテキストを追加してください。
- Unicode 対応 — 幅の計算では記号と CJK を考慮します(
StyledLineを使用)
適切な幅計算を行うテーブルのレンダリングには StyledLine を使用します。
use worktrunk::styling::StyledLine;
use anstyle::{AnsiColor, Color, Style};
let mut line = StyledLine::new();
line.push_styled("Branch", Style::new().dimmed());
line.push_raw(" ");
line.push_styled("main", Style::new().fg_color(Some(Color::Ansi(AnsiColor::Cyan))));
println!("{}", line.render());
高度な使い方については、src/commands/list/render.rs を参照してください。
ドキュメントの例
すべてのドキュメント、ヘルプテキスト、設定テンプレートで、一貫した例を使用してください。
標準的な例の設定
| 要素 | 値 | 備考 |
|---|---|---|
| リポジトリディレクトリ | myproject | 一般的なプレースホルダー |
| リポジトリのパス | ~/code/myproject | 現実的な開発用パス |
| ブランチ | feature/auth | sanitize フィルターを示します |
| ワークツリーパス | ~/code/myproject.feature-auth | デフォルトテンプレートの結果 |
テンプレート変数の例
すべての例で、上の表にある標準値を使用してください。
{{ repo }} — Repository directory name (e.g., `myproject`)
{{ branch }} — Branch name (e.g., `feature/auth`)
{{ worktree_path }} — Absolute path to worktree (e.g., `/path/to/myproject.feature-auth`)
TOML のコメントでは:
# {{ repo }} - Repository directory name (e.g., "myproject")
# {{ branch }} - Raw branch name (e.g., "feature/auth")
# {{ worktree_name }} - Worktree directory name (e.g., "myproject.feature-auth")
ワークツリーパスの例
ワークツリーパスのテンプレート例を示す場合:
# Default — siblings in parent directory
# Creates: ~/code/myproject.feature-auth
worktree-path = "../{{ repo }}.{{ branch | sanitize }}"
# Inside the repository
# Creates: ~/code/myproject/.worktrees/feature-auth
worktree-path = ".worktrees/{{ branch | sanitize }}"
ガターの書式設定
引用するコンテンツ(git の出力、コミットメッセージ、コピーする設定、表示するフックコマンド)にはガターを使用します。
ガターは背景色の付いた空白の列の後にスペースを置いたものです。プレーン出力では、2 つのスペースでインデントします。このスキル内のガターの例は、そのプレーン形式を示します。
format_bash_with_gutter()— シェルコマンド(暗い表示+構文のハイライト)format_with_gutter()— その他のコンテンツ
ガターとテーブルの使い分け: 構造化されたアプリデータにはテーブルを、外部コンテンツの引用にはガターを使用します。
ガターとヒントの使い分け: ヒント内のコマンド候補には、ガターではなくインラインの <underline> を使用します。ガターはコンテンツ(実行される内容、コピーする設定)の表示に使い、ヒントはユーザーが実行するコマンドを提案します。
改行の規約
基本原則: すべてのフォーマット関数は、末尾に改行を付けずにコンテンツを返します。要素の区切りは呼び出し側で処理します。
これは以下に適用されます。
- メッセージ関数:
error_message()、success_message()、hint_message()など - ガター関数:
format_with_gutter()、format_bash_with_gutter()
eprintln! の場合: 末尾に改行が自動的に追加されます。
eprintln!("{}", progress_message("Merging..."));
eprintln!("{}", format_with_gutter(&log, None));
Display の実装の場合: 要素を区切るには明示的に改行を使用します。
// Pattern: leading \n separates from previous element
write!(f, "{}", error_message(...))?; // first element, no leading \n
write!(f, "\n{}", format_with_gutter(...))?; // gutter, separated by \n
write!(f, "\n{}", hint_message(...)) // hint, separated by \n
// For blank line between elements, add extra \n
write!(f, "\n{}\n", format_with_gutter(...))?; // trailing \n creates blank line
write!(f, "\n{}", hint_message(...)) // hint after blank line
コンテンツの末尾に \n を追加しないでください。
// GOOD - eprintln! adds newline
eprintln!("{}", progress_message("Merging..."));
// BAD - double newline
eprintln!("{}", progress_message("Merging...\n"));
箇条書きは避け、代わりにガターを使用してください。 "\n - {}: {}" のような箇条書き形式ではなく、format_with_gutter() を使用してリストを表示します。
エラーのフォーマット
エラーメッセージの構成
エラーメッセージと警告メッセージでは、次の4点を伝える必要があります。
- 何が起きたか — 実際の状態または結果
- 何が期待されていたか — 正しい状態または望ましい状態
- 影響 — それが重要な理由(明らかな場合は省略可能)
- 解決方法 — ユーザーが行うべきこと(別のヒントメッセージに分けてもかまいません)
// GOOD - states actual, expected, and impact in main message
"Shell probe: wt is binary at /path, not function — won't auto-cd"
// ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^
// actual expected impact
// Resolution in separate hint: "Restart shell to activate"
// GOOD - actual vs expected with resolution inline
"Config file has 3 errors, expected valid TOML; run wt config validate for details"
// ^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// actual expected resolution
// BAD - only states actual, no expected or impact
"Shell probe: wt resolves to binary at /path"
// Missing: what should it be instead? why does it matter?
// BAD - vague, no actionable information
"Shell integration problem detected"
// Missing: what's wrong? what should it be? what to do?
期待される状態が文脈から明らかな場合は、暗示するだけでもかまいません。
// OK - expected state (file should exist) is obvious
"Config file not found at ~/.config/wt/config.toml"
// OK - expected state (should succeed) is obvious
"Failed to read config: permission denied"
診断メッセージ(wt config show など)では、ユーザーが問題を把握するために読むものなので、特にこのパターンに従ってください。
単一行と複数行
変数を含む単一行のエラーは問題ありません。
// GOOD - single-line with path variable
.map_err(|e| format!("Failed to read {}: {}", format_path_for_display(path), e))?
// GOOD - using .context() for simple errors
std::fs::read_to_string(&path).context("Failed to read config")?
外部コマンドの複数行出力(git、フック、LLM)にはガターが必要です。
- 実行したコマンド(引数を含む)を表示します
- 複数行の出力をガター内に配置します
✗ Commit generation command 'llm --model claude' failed
Error: [Errno 8] nodename nor servname provided
// NOT: ✗ ... failed: LLM command failed: Error: [Errno 8]...
このパターンの例は、src/git/error.rs の GitError の Display 実装を参照してください。
詳細出力(-v および -vv)
-v(詳細): ユーザー向けの診断出力です。以下のガイドラインに従う必要があります。設定のデバッグに必要となる可能性があるテンプレートの展開結果などを表示します。
テンプレートの展開では、元のテンプレートとレンダリング結果を、それぞれラベル付きのブロックとして表示します。各ブロックは、独自の情報ヘッダー(○ 記号、太字の名前、その後に続く source または result)の下に配置された bash ガター(format_bash_with_gutter による暗色表示+構文ハイライト)です。
○ name source
template
○ name result
result
入力と出力の区別は2つのヘッダーで示されるため、両方のブロックで同じガターを使用し、コンテンツは常にガターの2列目から始まります(ガター内に記号や追加のインデントはありません)。複数行のテンプレートと結果も同じ形式に従い、元の各行に対してガターの行を1行ずつ表示します。
expand_template は末尾に空行を出力しないため、単独で呼び出す場合は、後続の内容と区切るために空行を1行追加します(wt step eval はビューの後にこれを行います)。コマンドパイプラインでは、実行側が次の出力との間をすでに区切るため、展開処理では何も追加しません。
-vv(デバッグ): 開発者向けのログ出力です。これらのガイドラインに従わなくてもかまいません。詳細なデバッグには構造化フォーマットを使用する log::debug!() を使います。通常のユーザー向けではありません。
パスのフォーマット
ユーザー向けのパスにはすべて worktrunk::path の format_path_for_display() を使用してください。 この関数は、読みやすさのためにホームディレクトリのプレフィックスを ~ に置き換えます(例:/Users/alex/projects/repo → ~/projects/repo)。
ユーザー向けの出力では、パスの前に「at」ではなく @ を使用してください。 これは、ステータスメッセージ、セクション見出し、ヒントなど、あらゆる場所でエンティティと場所を関連付けるためのコードベースの規約です。
// GOOD - @ before path
"Created worktree for feature @ ~/code/repo.feature"
"Squashed @ a1b2c3d"
"Worktree for feature @ ~/repo.feature, but cannot change directory..."
format_heading("USER HOOKS", Some(&format!("@ {}", format_path_for_display(p))))
// BAD - "at" before path
"Created worktree for feature at ~/code/repo.feature"
// BAD - heading without @
format_heading("USER HOOKS", Some(&format_path_for_display(p)))
例外: 説明文の文脈(ドキュメントコメント、ヘルプテキスト)では「at」を使用します。@ は簡潔な出力にのみ使用します。
use worktrunk::path::format_path_for_display;
// GOOD - uses format_path_for_display
eprintln!("{}", success_message(cformat!(
"Created worktree @ {}",
format_path_for_display(&worktree_path)
)));
// GOOD - error messages too
.map_err(|e| format!("Failed to read {}: {}", format_path_for_display(path), e))?
// BAD - raw path.display()
eprintln!("{}", success_message(format!(
"Created worktree @ {}",
worktree_path.display() // Shows /Users/alex/... instead of ~/...
)));
適用対象:
- 成功/情報/警告/エラーメッセージ
- セクション見出し(パスの接尾辞を付けた
format_heading) - パスを提案するヒント
- 進捗メッセージ
- ドライランのプレビュー
例外:
- デバッグログ(
log::debug!)— デバッグにはフルパスが役立ちます - git コマンドに渡すパス — 実際のパスを使用する必要があります
テーブル列の配置
- テキスト列(Branch、Path、Message):左揃え
- 単一値の数値列(Age):
nowと4mが単位の位置で揃うよう、右揃え - 差分列(HEAD±、main↕):区切り文字(
+999 -999)の両側に、右揃えの領域を配置します。読み込み中や同期済みを示すマーカーなど、フィールド全体の状態を示す場合は中央揃えです
ヘッダーは内容に合わせます。ただし差分列では中央に配置します。どちらかの端に寄せると片側の値のラベルとなり、HEAD± の左側に +1 だけが取り残されます。
行の末尾にパディングはありません。パディングはセルを配置するためのもので、最後のセルの後には何も配置しません。行を選択した読者には、いずれにしても行全体が表示されます。
スナップショットテスト
すべてのコマンド出力にスナップショットテストが必要です(tests/integration_tests/)。setup_snapshot_settings()、make_snapshot_cmd()、assert_cmd_snapshot!() を使った標準的なパターンは、tests/integration_tests/remove.rs を参照してください。
成功時とエラー時、データの有無、フラグのバリエーションを網羅してください。