cli-design について
ストリーム、終了コード、出力形式、合成可能性の契約を定める、言語に依存しないCLI設計スキルです。CLIの開発やレビューを対象とし、必要に応じて参照できるTypeScript実装資料もあります。
主な機能
- 01stdoutとstderrの分離
- 02人間向け・plain・JSON・NDJSONの出力契約
- 03終了コードの指針
- 04TTYに応じた出力ルール
- 05純粋なハンドラーとエントリーポイントのI/O
- 06TypeScript実装リファレンス
活用シーン
- 01CLIのコマンドツリーとフラグの設計
- 02パイプ出力への診断情報混入の防止
- 03スクリプト向け安定JSON出力の定義
- 04ストリーム、パイプ、終了コードのテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ citypaul/ .dotfiles --skill cli-designインストール後、Claude Code で/cli-designを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEresources/composability.mdoutput-architecture.mdsource-notes.mdstream-contracts.mdtesting-cli.md詳細
CLI 設計:Unix と組み合わせて使えるコマンドラインインターフェース
このスキルでは、言語に依存しない CLI 設計の原則を扱います。ストリームの分離、終了コード、フォーマットフラグ、組み合わせやすさに関するルールは、実装言語にかかわらず適用されます。
このバンドルは、CC BY-SA 4.0 の コマンドラインインターフェースガイドライン を基にしています。
固定された出典情報、変更に関する注記、ライセンスの適用範囲は、
resources/source-notes.md と LICENSE に記録されています。
API コントラクトの安定性とハイラムの法則については、api-design スキルを参照してください。設定、環境変数、グレースフルシャットダウンについては、twelve-factor スキルを参照してください。
TypeScript の実装パターンは resources/ ディレクトリにあります。TypeScript で CLI を構築するときに、必要に応じて読み込んでください。
| リソース | 次の場合に読み込みます…… |
|---|---|
output-architecture.md | Result 型、エントリーポイントの配線、フォーマッター、ロガー、JSON エンベロープスキーマを実装するとき |
testing-cli.md | CLI の動作(ストリーム、終了コード、パイプ、コントラクトテスト)に関する Vitest テストを書くとき |
stream-contracts.md | Node.js のバッファリング、NDJSON、シグナル処理、クラッシュオンリー設計を理解するとき |
composability.md | パイプの動作を設計またはテストするとき — 実践的なシェルの例(jq によるフィルタリング、NDJSON ストリーミング、stdin、連結、--fields、並列の xargs) |
使用する場面
- あらゆる言語でコマンドラインツールを構築するとき
- コマンドツリー、フラグ、I/O コントラクトを設計するとき
- 出力レイヤー(フォーマット検出、ストリームの振り分け)を実装するとき
- CLI の動作(stdout/stderr の分離、終了コード)をテストするとき
- Unix と組み合わせて使えるか、CLI をレビューするとき
基本原則
stdout はデータ専用です — ユーザーが求めた成果物を出力します。 stderr はそれ以外すべてに使います — 診断情報、進捗、スピナー、警告、エラーです。
この分離があるからこそ、mycli --json | jq ... が機能します。stdout にスピナーの文字がひとつでも出力されると、その後続のパイプはすべて壊れます。
「どのようなソフトウェアを作る場合でも、人々があなたの予想しなかった使い方をすることは間違いありません。ソフトウェアはより大きなシステムの一部になります。あなたにできるのは、行儀のよい一部にするかどうかを選ぶことだけです。」— clig.dev
Unix ストリームコントラクト
| 内容 | ストリーム | 理由 |
|---|---|---|
| 主な出力(データ、結果、JSON) | stdout | パイプで利用でき、スループット向けにバッファリングされます |
| 進捗バー、スピナー、ステータス | stderr。ただし stderr が TTY(process.stderr.isTTY)の場合に限ります — パイプに接続された stderr には警告とエラー以外を出力しません | データではないため、パイプを壊してはいけません。また、パイプの利用側がスピナーを必要とすることもありません |
| 警告、エラー、診断情報 | stderr | stdout がパイプに接続されていても、ユーザーに表示されます |
| デバッグ/詳細出力 | stderr | 診断情報であり、データではありません |
ストリームの動作:
- stdout と stderr は個別に確認してください。stdout がパイプに接続されていても、stderr もパイプに接続されているとは限りません
- C 標準 I/O の行バッファリング/ブロックバッファリング/無バッファリングのルールが、あらゆる言語ランタイムにも当てはまると考えないでください
- Node.js では、同期/非同期のプロセスストリームの動作は OS や出力先によって異なります。大量の出力では、常に
.write()のバックプレッシャーを尊重してください
stdout がパイプに接続されている場合、ユーザーはステータスメッセージがデータに混ざることを望みません。データ以外の出力はすべて stderr に送る必要があります。
バッファリングの動作とパフォーマンスへの影響について詳しくは、resources/stream-contracts.md を参照してください。
ハンドラーは純粋に保つ
実践的なルールは、処理を行う関数はデータを返し、標準出力には書き込まないことです。 I/O は CLI のエントリーポイントがすべて処理します。
Entry point (CLI main) Your logic (handlers)
───────────────────── ─────────────────────
parse args (input) → structured result
detect format (json/plain/human) no printing to stdout
call handler no writing to stderr
format the result no calling exit
write to correct stream just returns data
set exit code
これはアーキテクチャ上の義務ではありません。関数を適切に設計するためのルールで、厳守すべき点はひとつだけです。ストリームへの書き込みやプロセスの終了を行うのは、エントリーポイントのファイルだけです。 すでに出力や終了処理を行っているモジュールを拡張・編集する場合は、その変更の一環として出力処理をエントリーポイントに移してください。元の場所に残したままにせず、「その関数はもともとそうしていたから」といって、そこに出力処理を追加してはいけません。得られるメリットは明確です。
- サブプロセスを起動せずにテスト可能 — ハンドラーを呼び出し、返された値を検証できます
- 形式を簡単に切り替え可能 — ひとつの関数を差し替えるだけで、同じデータを JSON、プレーンテキスト、色付きの表として表示できます
- 再利用可能 — 同じハンドラーを CLI、MCP サーバー、HTTP API、プログラムからのインポートで使用できます
ハンドラーがライブラリを呼び出すだけのシンプルな CLI では、この分離は自然に実現します。ライブラリがデータを返し、CLI がその形式を整えます。追加のレイヤーは必要ありません。
プロジェクトでヘキサゴナルアーキテクチャを採用している場合、対応関係は明確です。CLI のエントリーポイントはドライビングアダプターで、ハンドラーはポートを介して結果を返すユースケースです。hexagonal-architecture スキルを参照してください。これらのパターンは相互に補強し合いますが、ハンドラーを純粋に保つメリットを得るためにヘキサゴナルアーキテクチャが必須というわけではありません。
TypeScript の実装パターン(Result 型、エントリーポイントの接続、フォーマッター、ロガーインターフェース)については、resources/output-architecture.md を参照してください。
TypeScript スタックの選定
このスキルの要件を満たせる場合は、リポジトリですでに採用されているコマンドライブラリとプロンプトライブラリを優先してください。新たに重要な依存関係を追加する場合は、現在のバージョンとプロジェクトの制約を調べ、evaluate-existing-solutions を使用してください。記憶に頼った人気や、古くなったパッケージの特性をもとに選定してはいけません。
Stricli は型付けされ、依存関係を注入できるコマンドハンドラーに適している場合があり、@clack/prompts は任意の TTY 対応インタラクティブ機能に適している場合があります。これらはデフォルトではなく、候補として扱ってください。既存のスタックや代替案と比較する際は、ハンドラーのテスト容易性、型安全性、対象ランタイムで測定した起動コスト、保守性、依存関係の重さ、非インタラクティブ時の動作、移行コストを検討してください。
フォーマットフラグの仕様
出力は次の3段階の階層にします。
デフォルト:人間が読みやすい形式
- 色、表、整形済みテキスト
- 進捗バーとスピナーは標準エラー出力に表示
- ターミナルの幅に合わせた出力
- バージョン間で変わることがあります。これは仕様ではありません
--plain:Grep/Awk で扱いやすい形式
- 1行につき1レコード。整形や色付けはしません
- マイナーバージョン間で安定しています。これは仕様です
- 表の行はフラットにし、枠線やグループ化されたセクションは使いません
- 次のように使用できます:
mycli list --plain | grep error | wc -l
"スクリプトで出力を安定させるために、ユーザーには
--plainまたは--jsonを使うよう勧めてください。" — clig.dev
--json:構造化データ
- 成功時、標準出力には有効なJSONのみが含まれます。スピナー、色、進捗表示はありません。
- ゼロ以外の終了コードの場合、標準出力は空のままで、標準エラー出力の最後の空でない行が構造化JSONエラーエンベロープになります。
- これにはドメインエラー(終了コード1)も含まれます。処理が完了して数値が得られていても、しきい値、ゲート、予算を満たさない場合は失敗です。エンベロープ内に
falseフラグを含む成功として扱ってはいけません。呼び出し元が数値を必要とする場合は、標準出力ではなくerror内(detailsフィールド)に含めてください。 - その他の診断情報は、最後の行より前に出力してかまいませんが、標準出力を汚染してはいけません。利用側は診断ストリーム全体ではなく、最後の空でない行を解析します。
- スキーマにはバージョンが付与されます。JSON出力の互換性を損なう変更は、CLIの互換性を損なう変更です。
--jsonを指定すると、TTYに関係なく非対話モードになります。
一貫性のあるエンベロープ:
成功時の標準出力:
{ "ok": true, "data": { ... } }
エンベロープは必須であり、フィールド名は変更されません。利用側が求めたフィールド名はdata内にそのまま保持し、レコードのリストはエンベロープを裸の配列に置き換えるのではなく、dataに格納します。
失敗時の標準エラー出力:
{ "ok": false, "error": { "code": "CONFIG_MISSING", "message": "...", "fix": "..." } }
ストリーミングにはNDJSON
大規模なデータセットには、NDJSON(レコードごとに1つのJSONテキストを出力し、\nで終端)を使用します。
- 各行は個別に解析できます。
- 任意のJSON値が有効なNDJSON構文です。スキーマはアプリケーションのデコーダーが管理します。
- このスキルの例では、
type識別子と任意の最終サマリーレコードを使用するローカルイベントプロファイルを指定していますが、どちらもNDJSONの必須要件ではありません。 - 空行を読み取り側が無視するのか拒否するのかを明記してください。
- 次のように使用できます:
mycli run --format ndjson | while read -r line; do ...; done
NDJSONの仕様の詳細については、resources/stream-contracts.mdを参照してください。
終了コード
| コード | 意味 | 場合 |
|---|---|---|
| 0 | 成功 | 操作が想定どおりに完了した場合 |
| 1 | ドメインエラー | ツール固有の失敗(例:品質しきい値を満たさない) |
| 2 | 使用方法が無効 | 不正なフラグ、必須引数の欠落、検証エラー |
| 78 | 設定エラー | 設定ファイルが無効、必須設定がない |
| 75 | 一時的で再試行しても安全な失敗 | ディスパッチ前に失敗した場合、または操作が再試行しても安全であると文書化され、実証されている場合 |
| 130 | SIGINT | ユーザーがCtrl-Cを押した(128 + 2) |
| 143 | SIGTERM形式のステータス | シグナルによる終了を維持する場合の任意の文書化されたステータス(128 + 15)。処理済みの正常なシャットダウンでは、代わりに0を返す場合があります |
ルール:
- ゼロ以外の終了コードには、必ず標準エラー出力で説明を付けてください。
--helpに終了コードを記載してください。- アプリケーションエラーに125を超えるコードを使用してはいけません(128 + シグナル番号で表されるシグナル用に予約されています)。
- 終了コード75は、変更処理がディスパッチされていない場合、または冪等性/照合によって再試行の安全性が確立されている場合にのみ使用してください。単に一時的な原因であるだけでは不十分です。
- 変更を伴うリクエストが宛先に到達した可能性がある場合は、結果が不確定であることを報告し、むやみに再試行させるのではなく、
status/照合を案内してください。 - 処理済みのSIGTERMでは、終了コード0、または143などの文書化されたアプリケーションステータスを返せます。正常なシャットダウンで143を返すことは、DockerやKubernetesの必須要件ではありません。
- ゼロ以外のコードを、ツールにとって最も重要な失敗モードに割り当ててください。
- 呼び出し元が単に渡さなかったフラグは、使用方法の無効を意味しません。終了コード2は、ツールが処理できない入力に対して使用します。追加のゲート、しきい値、フィルターフラグはオプトインです。指定がない場合はチェックをオフにし、コマンドは終了コード0で成功します。コマンドに意味を持たせるために必須である場合に限り、新しいフラグを必須にしてください。そうでなければ、通常の呼び出しがすべて使用方法のエラーとなり、終了コードが区別するために存在する失敗が隠れてしまいます。
- 各失敗がその失敗を示すコードに到達するよう、チェックの順序を設定してください。後段の検証が設定エラーやデータエラーを横取りし、使用方法の誤りとして報告してはいけません。
TTY の検出
優先順位を確認します(最初に一致したものが適用されます)。
| 優先順位 | 条件 | 効果 |
|---|---|---|
| 1 | --format json または --json フラグ | 非対話型、色なし、アニメーションなし |
| 2 | --no-color フラグ | 色を無効化します(出力は引き続き対話型の場合があります) |
| 3 | FORCE_COLOR 環境変数 | 空、1、2、3、または true の場合は色を有効化します。それ以外の値(0 を含む)の場合は無効化します。サポートされている値は NO_COLOR と NODE_DISABLE_COLORS より優先されます |
| 4 | FORCE_COLOR が未設定で、NO_COLOR が空でない、または NODE_DISABLE_COLORS が定義済み | 色を無効化します |
| 5 | TERM=dumb | 色とアニメーションを無効化します |
| 6 | CI=true | 対話型プロンプトを表示しません |
| 7 | stdout が TTY ではない(!isatty(stdout)) | プレーンな出力にし、stdout ではアニメーションを表示しません |
| 8 | デフォルト | 色を使った完全な対話型 |
エントリーポイントで、何かを書き込む前に出力モードを一度だけ決定します。 上の表に沿って、フォーマットフラグ、--no-color、FORCE_COLOR、NO_COLOR、TERM、CI、および各ストリームの TTY 状態(isatty/isTTY)を確認し、単一のモード値にまとめてフォーマッターに渡します。現時点では出力に色もアニメーションもなくても、このチェックを実装してください。いずれかが追加されたときに、判定を一か所にまとめておけます。
stdout と stderr は個別に確認します。 stdout がパイプにつながっていても、stderr が TTY であれば、stdout をパイプの受信側向けにクリーンに保ちながら、stderr にスピナーを表示できます。ステータス行、スピナー、進行状況は、それらを書き込むストリームが TTY の場合にのみ表示します。stderr がパイプの場合は、警告とエラー以外は出力しません。
stdin は個別に確認し、プロンプトを制御します。 プロンプトを表示するのは、stdin とプロンプトの出力ストリームの両方が TTY の場合に限ります。stdout の TTY 状態はデータのフォーマットを制御するものであり、入力を安全に要求できるかどうかを示すものではありません。
アプリ固有の色の上書き設定として、MYCLI_NO_COLOR をサポートしてもかまいません。
入力の設計
引数よりフラグを優先
- 位置引数 1 個:許容範囲です(「主な対象」)
- 位置引数 2 個:要注意です。代わりにフラグを検討してください
- 位置引数 3 個以上:位置関係が明確になる一般的なコマンド構文がある場合を除き、名前付きフラグを優先してください
- 例外:同じ型の可変個引数(
rm a.txt b.txt c.txt)や、一般的な慣用形(cp source dest)は問題ありません
フラグを使うと、自己説明的で、順序に依存せず、将来の変更にも対応しやすくなります。
# Bad — which is source, which is destination?
mycli copy myapp backup
# Good — explicit
mycli copy --from myapp --to backup
標準フラグ
常にロング形式を用意してください。ショートフラグは、最も一般的な操作に限って用意します。
| フラグ | 意味 |
|---|---|
-h、--help | ヘルプを表示します(ヘルプの表示だけを意味するようにしてください) |
--version | バージョンを stdout に出力します |
-q、--quiet | 不要な出力を抑制します |
-v、--verbose | 人間向けの出力に詳細を追加します |
-d、--debug | 診断情報を stderr に出力します |
-f、--force | 確認プロンプトを省略します |
-n、--dry-run | 実行せずに、実行した場合の内容を表示します |
--json | 構造化された JSON 出力 |
--plain | 安定した、grep しやすいプレーンテキスト |
--no-color | 色付き出力を無効化します |
--no-input | すべてのプロンプトと対話機能を無効化します |
-o、--output | 出力ファイル |
--fields | 出力する列を選択します |
プロンプトと対話性
- すべてのプロンプトは、スクリプトから利用できるようフラグで必ず回避できるようにします
- 確認 →
--yesまたは--force - 選択 →
--type=value - テキスト入力 →
--name=value - パスワード →
--password-file=pathまたは stdin パイプ - stdin が TTY でない場合、プロンプトは表示せず、明確なエラーを出すかデフォルト値を使用します
- シークレットをフラグの値として渡すことは禁止です(
psの出力やシェルの履歴に漏れるため)。次の順に優先します。OS のキーチェーンまたは0600の認証情報ファイル(~/.config/mycli/credentials)、次に stdin(mycli login --with-token < token.txt)、その次に環境変数。ただし、環境変数を使用するのはプラットフォームが注入する場合(CI のシークレットストア)のみです。環境変数は子プロセスやクラッシュレポートに漏れるため、ドキュメントで主な方法として案内しないでください - 確認の強さは重大度に応じて変えます。軽度 →
--yesで回避できるy/Nプロンプト、中程度 → プロンプトを表示し、まず--dry-runを使うよう提案、重大/取り消し不能(データベースの削除、本番環境の上書き) → 確認のためリソース名の入力を必須にする
規約
- フラグの解析を終了する
--をサポートします:mycli run -- --flag-for-child-process - ファイル引数に stdin/stdout を指定する
-をサポートします:curl ... | mycli process - --flag=valueと--flag valueの両方を受け付けます- stdin が必要なのに対話型ターミナルの場合は、停止したままにならないよう(
catのように)直ちにヘルプを表示します
設定の優先順位
優先度の高い順:
- フラグ — 呼び出しごとの上書き
- 環境変数 —
MYCLI_*接頭辞、セッションごと - プロジェクト設定 —
.myclirc、mycli.config.ts、またはpackage.json内 - ユーザー設定 —
~/.config/mycli/(XDG 仕様に準拠) - デフォルト値 — 妥当な組み込み値
ルール:
- 設定ファイルの場所には XDG Base Directory Specification に従います
- 環境変数の命名:
MYCLI_*接頭辞を付け、大文字・数字・アンダースコアを使用します。値は単一行にし、POSIX の名前を横取りしないでください - 該当する場合は、汎用環境変数を尊重します:
NO_COLOR、FORCE_COLOR、DEBUG、EDITOR、PAGER、HTTP_PROXY/HTTPS_PROXY/NO_PROXY、TMPDIR、TERM、LINES/COLUMNS - シークレットをフラグで受け付けないでください。キーチェーン/認証情報ファイルまたは stdin を優先し、環境変数を使うのはプラットフォームから注入される場合(CI)のみとします。詳しくは「プロンプトと対話性」を参照してください
- 適切な場合は
.envを読み込みますが、適切な設定の代わりとして使用しないでください - 別のプログラムに属する設定を変更する場合は、事前に同意を求めてください
エラー設計
すべてのエラーに次の情報が必要です:
- 機械可読コード —
UPPER_SNAKE_CASE(例:CONFIG_MISSING、AUTH_EXPIRED) - 問題の内容 — 対象リソース、操作、入力などのコンテキスト
- 解決方法 — ユーザーが実行すべき具体的なコマンドまたは操作
- 参照先 — ドキュメントの URL または
mycli help <topic>(任意)
人間向けモード
Error: CONFIG_MISSING — Configuration file not found
No configuration file found at ./mycli.config.ts or ~/.config/mycli/config.ts
Fix: Run `mycli init` to create a default configuration file
Docs: https://mycli.dev/docs/configuration
- 最も重要な情報は最後に置きます(目は末尾に引き付けられます)
- 赤色は控えめに、意図的に使います
- 入力ミスには修正候補を提示します(「「deploy」のことですか?」)
- 似たエラーは共通の見出しの下にまとめます。よく似た行を 50 行繰り返さないでください
- デバッグログは、(
--debugが指定されていない限り)ターミナルではなくファイルに書き込みます
JSON モード
エラーも構造化します。成功時の応答だけではありません:
{
"ok": false,
"error": {
"code": "CONFIG_MISSING",
"message": "No configuration file found at ./mycli.config.ts",
"fix": "Run `mycli init` to create a default configuration file",
"transient": false
}
}
transient ブール値は、原因が解消される可能性があることを示します。ただし、この値だけでは
再試行の許可にはなりません。呼び出し側には、終了コード 75 または再試行の安全性を明示する契約も必要です。
状態変更と透明性
- 状態変更を確認する — 何が変わったかを伝え、変更後の状態を表示する(またはその場所を示す)。従来のように何も表示しないコマンドは、人間には壊れているように見えます
- 現在の状態を簡単に確認できるようにする — 状態が複雑なものには、
git statusのパターンにならい、statusのようなコマンドを用意します - 隠れた処理を明示する — 引数として渡されていないファイルを読み書きしたり、リモートサーバーと通信したりする場合は、そのことを伝えます(人間向けモードでは stderr に出力します)
- 曖昧な変更結果を解消する — ディスパッチ後に通信が失敗した場合は、状態を確認または整合させる方法をユーザーに伝えます。結果を再試行しても安全だと示してはいけません
- stdout が TTY の場合に限り、長い出力を
$PAGER経由でページ表示します。パイプに渡されている場合は使いません。値をシェル経由で実行せず、それが実行可能ファイルのトークンなのか、レビュー済みのシェル単語パーサーで解析するのかを文書化します
堅牢性
- 入力を早い段階で検証する — 副作用が発生する前に、わかりやすいメッセージを表示して失敗させます
- 継続中の人間の作業に応答する — 対話型の人間向けモードでは、
--quietが指定されていない限り、処理が約 100ms を超えて続く場合は stderr に進捗を表示します。プロトコルで明示的に進捗が定義されている場合を除き、機械向けモードでは出力を控えます。また、対話型の進捗表示が停滞する可能性がある場合は、所要時間の見積もりを含めます - すべてのネットワーク処理にタイムアウトを設定する — タイムアウトは設定可能にします。終了コード 75 を使うのは、ディスパッチ前、または再試行の安全性/冪等性が確立されている場合に限ります
- 復旧可能な操作にする — 再実行によって処理を再開するのは安全な場合に限ります。変更結果が曖昧な場合に備え、明示的な状態確認または整合方法を用意します
- クラッシュオンリーデザイン — 失敗時はすぐに終了し、クリーンアップは次回の実行に委ねます。可視性を保ったアトミックな置き換えと、クラッシュ耐性のある永続化を区別し、上限時間のあるクリーンアップタイマーは参照を保持して Ctrl-C で上限時間を回避できないようにします。
resources/stream-contracts.mdを参照してください - 誤用を想定する — スクリプトによるラッピング、不良な接続、同時実行される複数のインスタンス、テストしたことのない環境に備えます
合成可能性のパターン
実環境のパイプ処理を想定して設計します。jq によるフィルタリング、NDJSON の行ごとのストリーミング、stdin からの入力、出力された識別子を使ったコマンドの連結、--fields による列の選択、xargs -P による並列実行などです。
各パターンを網羅したシェルの例については、resources/composability.md を参照してください。
主なパターン:
- 作成コマンドは識別子を出力し、後続のコマンドで連結できるようにします
- 一覧表示コマンドでは
--fieldsによる列選択をサポートします(出力量を減らせるため、エージェントの効率向上に不可欠です) - 終了コードだけを確認する CI スクリプト向けに
--quietを用意します - すべてのデータをメモリにバッファーせずに大規模なデータセットをストリーミングできるよう、NDJSON を使います
--jsonと併用した--dry-runでは、予定されている変更を構造化データとして出力します
サブコマンドの設計
mycli config setやmycli report generateのような 名詞・動詞 のパターンが最も一般的です- すべてのサブコマンドで一貫性を保ちます。同じ対象には同じフラグ名を使います
- 曖昧な組み合わせは避けます(
updateとupgradeは混乱を招きます) - 万能サブコマンドは避けます(名前が競合するサブコマンドを後から追加できなくなります)
- 恣意的な略語は避けます。エイリアスは明示的で、安定していなければなりません
- 引数がない場合は、サブコマンドを列挙するか(複数コマンドを持つ CLI)、ヘルプを表示します(単一コマンドの CLI)
ヘルプ
mycli --help— トップレベルのヘルプmycli help <subcommand>— サブコマンドのヘルプmycli <subcommand> --help— 上記と同じ- 必須の引数が不足した状態で実行された場合は、簡潔なヘルプと 1~2 個の例を表示し、「詳細は --help を使用してください」と案内します
- 最もよく読まれるのは例のセクションです — 先頭に配置します
- フラグの型、デフォルト値、選択肢が有限の場合は指定可能な値を記載します
- トップレベルのヘルプにサポート/バグ報告へのリンクを含めます。可能であれば、診断情報をあらかじめ入力した課題 URL を用意します
- 明らかな入力ミスには、可能性の高いコマンドを提案します(「もしかして 'deploy' ですか?」)。確認を求め、自動実行はしません
出力の安定性に関する契約
標準出力はパブリック API です。 標準出力の形式に対する破壊的変更は、CLI に対する破壊的変更です。
| 変更 | 影響 |
|---|---|
| 新しい任意の JSON フィールドの追加 | 安全(追加のみ) |
| 新しいサブコマンドの追加 | 安全 |
| デフォルトを維持した新しいフラグの追加 | 安全 |
| フラグの削除または名前変更 | 破壊的 |
| JSON フィールドの削除または名前変更 | 破壊的 |
| 終了コードの変更 | 破壊的 |
| デフォルトの動作の変更 | 破壊的 |
| 人が読める出力の変更 | 通常は問題ありません(契約ではありません) |
迷ったら、既存のものを変更せずに追加してください。削除する前に標準エラー出力の警告で非推奨を知らせ、ユーザーの移行を検知できるようになったら、警告を停止してください。
命名、配布、テレメトリー
- 名前:短く、覚えやすく、小文字で、入力しやすい名前にしてください。既存のコマンドと衝突するほど汎用的な名前は避けてください。
- 配布:単一バイナリを推奨します。言語エコシステムのツール(npm、pip)は、そのインタープリターを前提としてもかまいません。アンインストールを簡単にし、その方法を文書化してください。
- テレメトリー:明示的な同意なしに、使用状況やクラッシュデータを収集しないでください。収集するデータ、目的、保持期間を明示したオプトイン方式にしてください。計測機能付きのウェブドキュメントやダウンロード数で通常は十分です。
アンチパターン
| # | アンチパターン | 問題点 |
|---|---|---|
| 1 | データと診断情報を標準出力に混在させる | すべてのパイプが壊れます。警告が標準出力に出ると、mycli list | jq . が失敗します。 |
| 2 | パイプ出力に色や ANSI を含める | ANSI シーケンスが後続の解析を壊します。isatty(stdout) と NO_COLOR を確認してください。 |
| 3 | フラグで回避できない対話型プロンプト | エージェントは「y」を入力できません。すべてのプロンプトに --yes/--force が必要です。回避手段がない非 TTY では処理が停止します。 |
| 4 | 成功時に何も出力しない | 無出力では状況が曖昧になります。簡潔な確認メッセージを表示してください。無出力を必要とするスクリプト向けに -q を用意してください。 |
| 5 | 人間か機械のどちらか一方だけを想定して設計する | コンテキスト(TTY かパイプか)を検出し、自動的に対応を切り替えてください。 |
| 6 | 次のアクションにつながらない出力 | すべての出力を道しるべにしてください。成功時は次のコマンド、失敗時は修正コマンドを示します。 |
| 7 | 既存の CLI 契約を破る | フラグ名、終了コード、出力形式は契約です。変更せず、既存のものに追加してください。 |
| 8 | CLI アダプター以外で console.log を使う | ハンドラーはデータを返し、ストリームへの書き込みはプレゼンテーション層だけが行う必要があります。 |
| 9 | プロセスを直接終了するハンドラー | 判断はエントリーポイントに任せてください。ハンドラーはエラーをデータとして返します。 |
| 10 | 標準エラー出力での説明なしにゼロ以外の終了コードを返す | スクリプトには終了コードと理由の両方が必要です。 |
| 11 | デフォルトで冗長な出力を行う | 1 回のテスト実行で 419KB が生成されることがあります。--fields、--quiet、--json に対応してください。 |
検証チェックリスト
CLI の設計またはレビュー後に、以下を確認してください。
- stdout にはデータのみを出力し、それ以外はすべて stderr に出力する
- すべてのコマンドが
--jsonに対応し、成功データは stdout に、構造化されたエラーは stderr に出力する - 終了コードは意味に基づいて設定し、
--helpに記載する - すべてのプロンプトに、
--yes/--force/--flagによる確認の省略手段を用意する - エラーには、コード、メッセージ、修正方法の提案を含める
- データを変更するコマンドで
--dry-runを利用できるようにする - 進捗表示やスピナーは stderr に出力し、stdout には出力しない
-
NO_COLOR、TERM=dumb、--no-colorを尊重する - パイプへの出力には ANSI エスケープコードを一切含めない
- 成功時の出力に、次のアクションの案内を含める
- 既存のフラグ、終了コード、出力フィールドを削除または名称変更しない
- JSON スキーマにバージョンを付ける(追加は安全、削除は互換性を損なう変更)
- 設定の優先順位は、フラグ > 環境変数 > プロジェクト > ユーザー > デフォルトとする
- シークレットをフラグ経由で渡さない。キーチェーン/認証情報ファイル/stdin を優先し、プラットフォームから注入される場合(CI)に限り環境変数を使用する
- 重大な破壊的操作では、単なる y/N ではなく、リソース名を入力する確認を必須にする
- ネットワーク操作に設定可能なタイムアウトを設ける。終了コード 75 は、ディスパッチ前の失敗、または安全に再試行できることが実証されている失敗のために確保する
- およそ 100ms を超えて実行される対話型の人間向け操作では、
--quietが指定されていない限り、stderr に進捗を表示する。機械モードのプロトコルでは出力を汚さない - Ctrl-C で速やかに終了し、クリーンアップには上限を設ける
-
--helpに現実的な使用例を 2~3 件含める - 人間向けの出力を grep で解析できる形式にする(フラットな行、表の罫線なし)
クイックリファレンス
ストリームの振り分け、終了コード、標準フラグは本文の表にまとめています。「Unix ストリームの契約」、「終了コード」、「標準フラグ」を参照してください。
フォーマットの優先順位
Default (TTY) → colors, tables, formatted text
--plain → one record per line, stable, grep-friendly
--json → structured JSON, versioned schema
--format ndjson → streaming, one JSON object per line
設定の優先順位
flags > env vars > project config > user config > defaults