better-cli について
ターミナル利用者とAIエージェント向けに、コマンドベースのCLIを設計・レビューするスキルです。全画面型アプリではなく、予測可能な構造化出力、実行可能なエラー情報、コマンドの組み合わせやすさを重視します。
主な機能
- 01データ出力と診断情報の分離
- 02一貫した構造化JSON出力
- 03次の操作が分かる成功・エラー表示
- 04非対話型オートメーションへの対応
- 05TTYとパイプに応じた出力調整
活用シーン
- 01新しいコマンドベースCLIの設計
- 02既存CLIのエージェント対応リファクタリング
- 03コマンドの使いやすさと組み合わせやすさのレビュー
- 04自動化向け出力とエラー処理の追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yogin16/ better-cli --skill better-cliインストール後、Claude Code で/better-cliを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdAGENTS.mdLICENSEPUBLISHING.mdpackage.jsonclaw.json.claude-plugin/plugin.jsonmarketplace.json.clinerules/better-cli.md.cursor/rules/better-cli.mdc.github/copilot-instructions.md.windsurf/rules/better-cli.mdreferences/composability.mddiscoverability.mdagent-patterns.mderror-handling.mdinteractivity.mdsecurity.mdoutput-design.mdtesting.md詳細
Better CLI:人間とAI エージェントのためのCLI構築
ターミナルを使う人間にとって快適で、なおかつ自動化パイプライン内のAI エージェントにとって信頼できるコマンドラインツールを構築します。この2つの目標は直交しており、互いに矛盾するものではありません。
このスキルの対象は、git、docker、gh、kubectlのようなコマンドベースのCLIです。サブコマンド、フラグ、構造化出力を備えたツールが該当します。全画面のTUIアプリケーションやインタラクティブなダッシュボードは対象外です。
使用する場面
- 任意の言語で新しいCLIツールを構築するとき
- 既存のCLIを、人間とエージェントの両方にとって使いやすくなるよう改善またはリファクタリングするとき
- 既存のCLIにコマンド、フラグ、出力形式を追加するとき
- CLIコードのユーザビリティ、組み合わせやすさ、エージェント対応状況をレビューするとき
対象外:TUI/全画面ターミナルアプリ、GUIアプリケーション、REST/GraphQL APIの設計、シェルスクリプト
基本理念
人間向けのDXでは、見つけやすさと寛容さを最適化します。エージェント向けのDXでは、予測可能性と多層防御を最適化します。優れたCLIは、その両方を実現します。
黄金律:次に何をすべきかがわかる出力にする。 成功時も失敗時も、すべてのコマンド出力には、ユーザー(人間またはAI エージェント)が次のアクションを判断するのに十分な情報を含めるべきです。CLIの出力を読むエージェントは、推測することなくワークフローを続行できる必要があります。
flowchart LR
H[Human at Terminal] --> TTY{stdout is TTY?}
A[AI Agent] --> TTY
TTY -->|Yes| RICH[Rich: colors, tables,\nprogress bars, prompts]
TTY -->|No| CLEAN[Clean: JSON, no ANSI,\nno prompts, parseable]
RICH --> CORE[Same data, same exit codes,\nsame semantics]
CLEAN --> CORE
ルール(優先順位順)
P0:必須 — すべてのCLIで実施すること
1. データはstdout、それ以外はすべてstderrへ出力する
stdout -> primary output (results, JSON, file contents) — for piping
stderr -> errors, warnings, progress, diagnostics, prompts — for humans
これは最も重要なルールです。パイプ、リダイレクト、エージェントによる解析を可能にします。絶対に混在させないでください。
2. 成功時は終了コード0、失敗時は0以外にする
スクリプトやエージェントが失敗の種類に応じて分岐できるよう、意味のある終了コードを使います。
| コード | 意味 | 使用する場面 |
|---|---|---|
| 0 | 成功 | 操作が完了した |
| 1 | 一般的な失敗 | その他のエラー |
| 2 | 使用方法のエラー | 引数の誤り、不明なフラグ |
| 3 | リソースが見つからない | ファイル、ユーザー、サービスが存在しない |
| 4 | アクセス拒否 | 認証の失敗、権限不足 |
| 5 | 競合 | リソースがすでに存在する |
| 75 | 一時的な失敗 | ネットワークのタイムアウト — 再試行で解決する可能性がある |
| 78 | 設定エラー | 設定の欠落または無効 |
終了コードをドキュメントに記載してください。transient(一時的)かどうかの区別(75とそれ以外)は重要です。エージェントはこれを使って再試行するかどうかを判断します。
3. 構造化出力のために--jsonをサポートする
出力を生成するすべてのコマンドで、--json(または--output json)をサポートする必要があります。統一された形式を使います。
{
"status": "ok",
"data": { "id": "abc-123", "name": "my-resource" },
"warnings": []
}
すべてのコマンドで同じ構造にします。エージェントは一度解析すれば、どこでも同じように使えます。これをバージョン管理されたAPI契約として扱ってください。オプションのフィールド追加は安全ですが、フィールドの削除や改名は互換性を破る変更です。
4. 次のアクションを導く出力にする
CLIをAI エージェントに活用してもらうための重要なポイントです。すべてのコマンド出力は解釈しやすく、次へ進めるものである必要があります。読む人が推測せずに次のアクションを判断できるようにします。
成功時 — 結果を確認し、次の手順を提案します。
Created deployment 'web-app' (ID: deploy-xyz)
Next steps:
View logs: mycli logs deploy-xyz
Check status: mycli status deploy-xyz
失敗時 — 何が問題だったか、どう直せばよいかを説明します。
Error: Cannot deploy — 2 failing health checks.
Fix: mycli health check web-app --verbose
mycli deploy --env staging --force (skip health checks)
部分的な結果の場合 — 何が不足しているか、どうすれば追加情報を得られるかを示します。
Showing 20 of 142 results. Next page: mycli list --cursor eyJpZCI6IDIwfQ==
このパターンによって、エージェントは自律的に動けるようになります。出力を読み、提案されたコマンドを確認して実行するからです。これがなければ、エージェントは先に進めなくなったり、次の手順を幻覚したりします。
5. --helpと--versionをサポートする
--helpには、説明、使用方法、型とデフォルト値を含むフラグ一覧、現実的な例を2~3件記載する必要があります。最もよく読まれるのは使用例のセクションです。例を先頭に配置してください。
EXAMPLES
$ mycli deploy --env staging Deploy to staging
$ mycli deploy --env prod --dry-run Preview production deployment
$ mycli deploy --env prod --yes Deploy without confirmation
6. インタラクティブな入力を決して必須にしない
すべてのインタラクティブなプロンプトには、対応するフラグを用意する必要があります。stdinがTTYでない場合は、決してプロンプトを表示せず、明確なエラーで終了するかデフォルト値を使用します。
| プロンプトの種類 | 対応するフラグ |
|---|---|
| 確認 | --yes / --force |
| 選択 | --type=value |
| テキスト入力 | --name=value |
| パスワード | --password-file=pathまたはstdinからのパイプ入力 |
エージェントは、プロンプトに「y」と入力できません。CLIが入力待ちで停止すると、エージェントのワークフローも止まります。
P1:重要 — 優れたCLIと非常に優れたCLIを分けるもの
7. TTY を検出して適応する
stdout が TTY の場合:色、テーブル、プログレスバー、対話型プロンプトを使用します。 stdout が TTY ではない場合:プレーンテキストを使用し、ANSI コード、スピナー、プロンプトは使用しません。
次の環境シグナルを尊重します。
| シグナル | 意味 |
|---|---|
NO_COLOR(空でない値) | ANSI カラーを抑制します |
TERM=dumb | 最小限のターミナルです — 書式設定を行いません |
CI=true | CI で実行中です — 対話型機能を使用しません |
MYCLI_NO_INPUT=1 | アプリ固有の非対話型オーバーライドです |
8. エラーは対処可能な内容にする
すべてのエラーに、次の4つの要素を含めます。
- 何が起きたか — エラーそのもの
- コンテキスト — 対象のリソース、操作、入力
- 修正方法 — 解決するための正確なコマンドまたは操作
- 参照先 — ドキュメントの URL または
mycli help <topic>
悪い例:Error: EACCES
良い例:Error: Cannot write to /etc/config — permission denied. Try: sudo mycli config set key=value
--json が有効な場合、エラーは必ず構造化します。
{
"status": "error",
"error": {
"code": "AUTH_EXPIRED",
"message": "API token has expired",
"fix": "Run: mycli auth login --refresh",
"transient": false
}
}
code フィールドは機械可読であり、エージェントはこの値に基づいて処理を分岐します。transient フィールドは、エージェントが再試行すべきかどうかを示します。
9. 設定の優先順位
常に次の順序にします:フラグ > 環境変数 > プロジェクト設定 > ユーザー設定 > デフォルト
| レイヤー | 例 | 用途 |
|---|---|---|
| フラグ | --port 8080 | 一時的な上書き |
| 環境変数 | MYCLI_PORT=8080 | CI/CD、コンテナー |
| プロジェクト設定 | リポジトリのルートにある .mycli.yaml | チームで共有する設定 |
| ユーザー設定 | ~/.config/mycli/config.yaml | 個人用のデフォルト |
| デフォルト | ハードコードされた値 | そのまま使える適切な設定 |
シークレットをフラグで受け付けてはいけません。ps の出力やシェルの履歴に漏れるためです。環境変数、設定ファイル、または --password-file を使用します。
10. パイプと組み合わせて使えるようにする
- 出力列を制限するために、
--fields name,status,idをサポートします(エージェントのトークン効率が向上します) - 標準入力からの読み込みをサポートします:
cat ids.txt | mycli get --stdin - ストリーミング/ページ分割されたデータには NDJSON(1行につき1つの JSON オブジェクト)を使用します
- リソースを作成するときは、後続のコマンドで連結して使用できるよう、識別子を出力します
# This pipeline must work
mycli create --json | jq -r '.data.id' | xargs mycli deploy --id
11. 既存の契約を決して破らない
既存の CLI を改善するときは、現在の動作を契約として扱います。ユーザーやスクリプトはその動作に依存しています。
破壊的変更(メジャーバージョンを上げずに行ってはいけません):
- フラグ、サブコマンド、環境変数を削除または名前変更する
- 既存の失敗ケースに対する終了コードを変更する
--json出力のフィールドを削除または名前変更する- 既存のコマンドのデフォルト動作を変更する
- 位置引数の順序または意味を変更する
安全な追加変更(常に問題ありません):
- 古い動作を維持するデフォルト値を持つ新しいフラグを追加する
- JSON 出力に新しい任意フィールドを追加する
- 新しいサブコマンドを追加する
- これまで対応していなかった箇所に
--jsonのサポートを追加する - 以前は 1 を返していたケースに新しい終了コードを追加する
- 新しいフラグとして
--fields、--quiet、--dry-runを追加する
迷ったときは、変更せずに追加します。古い動作を削除する前に、標準エラー出力に警告を表示して非推奨にします。
12. 位置引数よりフラグを優先する
フラグは内容が分かりやすく、順序に依存せず、将来の変更にも対応しやすいものです。
悪い例:mycli deploy production v2.1.0
良い例:mycli deploy --env production --version v2.1.0
位置引数は1つなら問題ありません(ファイルパスやリソース名など、「主な対象」を指定する場合)。2つは要注意です。3つは不適切です。
P2:中程度 — 信頼につながる仕上げ
13. 時間のかかる操作では標準エラー出力に進捗を表示する
TTY の場合:スピナー、プログレスバー、推定残り時間を表示します。 パイプの場合:完全に抑制するか、構造化された進捗を標準エラー出力に出力します。 進捗インジケーターを標準出力に出力してはいけません。データストリームが破損するためです。
14. 変更を伴うコマンドでは --dry-run をサポートする
実際に変更を加えず、何が変わるかを正確に表示します。--json の場合は、予定されている変更を構造化形式で出力します。これは AI エージェントにとって欠かせない安全策です。実行前に検証できます。
15. 操作を冪等にする
mycli config set key=value は2回実行しても安全でなければなりません。作成コマンドは --if-not-exists をサポートする必要があります。エージェントは一時的な失敗時に再試行するため、それを前提に設計します。
16. シェル補完を提供する
mycli completions <shell> で bash、zsh、fish をサポートします。ほとんどのフレームワーク(Cobra、Click、oclif、clap)では、これらは自動生成されます。
17. 一貫したコマンド文法を使う
パターンを1つ選び、すべてのコマンドで統一します。
- 名詞・動詞型:
mycli pod list、mycli pod delete(kubectl 形式) - 動詞・名詞型:
mycli list pods、mycli delete pod(docker 形式)
一般的な動詞に統一します:list、get/show、create、update、delete。
エージェント対応の判断ツリー
CLI をレビューまたは構築するときは、次の項目を確認してください。
flowchart TD
START[New CLI command] --> OUT{Produces output?}
OUT -->|Yes| JSON[Add --json with consistent envelope]
OUT -->|No| EXIT[Ensure meaningful exit code]
JSON --> MUT{Mutates state?}
EXIT --> MUT
MUT -->|Yes| DRY[Add --dry-run]
MUT -->|No| PIPE{Usable in pipes?}
DRY --> CONFIRM{Needs confirmation?}
CONFIRM -->|Yes| FORCE[Add --yes/--force bypass]
CONFIRM -->|No| IDEM[Make idempotent if possible]
FORCE --> IDEM
IDEM --> PIPE
PIPE --> FIELDS[Add --fields for column selection]
FIELDS --> ERR[Structured errors with code + fix + transient]
ERR --> DONE[Agent-ready]
アンチパターン
stdout にデータと診断情報を混在させる
初心者:「すべて stdout に出力すれば、ユーザーが見てくれます」
エキスパート:警告が stdout に出ると、mycli list | jq . は動作しなくなります。データは stdout に、それ以外はすべて stderr に出力します。例外はありません。
経緯:1977年以来の Unix の慣例です。今でも新しい CLI で最もよくある間違いです。
パイプ出力で色や ANSI を使う
初心者:「UX を向上させるため、常に色を表示します」
エキスパート:ANSI エスケープシーケンス(\x1b[38;2;...m)は LLM にテキストとしてトークン化され、コンテキストウィンドウを浪費し、解析を妨げます。ANSI コードを出力する前に isatty(stdout) と NO_COLOR を確認してください。
検出方法:出力を cat -v にパイプします。^[[ シーケンスが表示されたら、バグがあります。
回避方法のない対話型プロンプト
初心者:「破壊的な操作は安全のため、必ず確認します」
エキスパート:エージェントは「y」と入力できません。すべてのプロンプトには --yes/--force で回避できる方法を必ず用意してください。stdin が TTY ではなく、回避用のフラグも指定されていない場合は、明確なエラーを出して終了します。ハングしてはいけません。
検出方法:CI で実行したとき、または stdin を /dev/null からパイプしたときに、コマンドがハングします。
成功時に何も出力しない
初心者:「Unix の伝統では、何も表示されないのは成功の意味です」
エキスパート:人間にとって、何も表示されないと壊れているように感じます。簡潔な確認メッセージ(「リソース abc123 を作成しました」など)を表示してください。エージェントにとっては、何も表示されないと状況があいまいになります。終了コード 0 を返し、結果を返すために --json をサポートしてください。明示的に何も表示しないことを求めるスクリプト向けに、-q/--quiet を使います。
人間向けか機械向けのどちらか一方だけに設計する
初心者:「ユーザーは開発者なので、テキストを解析できます」または「常に JSON を出力すればよい」
エキスパート:コンテキスト(TTY かパイプか)を検出し、自動的に適応します。ターミナルではデフォルトで人間が読みやすい形式にし、パイプに出力する場合や --json が渡された場合は機械可読形式にします。同じコマンドで両方の利用者に対応できます。
次に何をすればよいか分からない出力
初心者:「結果を表示して終了すればよい」 エキスパート:すべての出力は次への道しるべです。成功時は次に実行するのが自然なコマンドを提案します。失敗時は修正用のコマンドを提案します。一部の結果だけを表示する場合は、追加の結果を取得する方法を示します。エージェントが自律的に処理を続けるには、こうした手がかりが必要です。手がかりがなければ、エージェントは行き詰まったり、やみくもに再試行したり、コマンドを幻覚したりします。 検出方法:コマンドを正常に実行し、「次に何をすればよいか、出力から分かるか?」と確認します。分からなければ、「次の手順」セクションを追加します。
「改善」の名目で既存の CLI 契約を破る
初心者:「このフラグ名はよくないので、名前を変更します」または「JSON 出力をもっとすっきりするように再構成します」 エキスパート:既存のユーザーやスクリプトは、フラグ名、終了コード、出力形式、デフォルトの動作など、現在の契約に依存しています。新機能は既存機能と併存させます。削除前に、stderr に警告を出して非推奨化します。フラグ名の変更や JSON フィールドの削除は、何千もの下流スクリプトやエージェントのワークフローを壊す可能性があります。 検出方法:メジャーバージョンを上げずに既存のフラグ、終了コード、出力フィールドを削除、改名、または型変更する PR はすべて該当します。
エージェントのコンテキストを浪費するデフォルトの冗長な出力
初心者:「情報は多いほどよい」
エキスパート:vitest を1回実行すると、419KB の出力が生成されます。エージェントに必要なのは5つの数値です。列を選択するには --fields、出力を最小限にするには --quiet、構造化データを出力するには --json をサポートしてください。stdout に出力する不要なバイトはすべて、エージェントのトークンを消費します。
エージェント対応チェックリスト
あらゆる CLI を手早く監査できます。
[ ] Every command supports --json with consistent envelope
[ ] stdout has ONLY data; stderr has everything else
[ ] Exit codes are semantic (not just 0/1)
[ ] Every prompt has a --yes / --force / --flag bypass
[ ] Errors include: code, message, fix suggestion, transient flag
[ ] --dry-run available for all mutating commands
[ ] Progress/spinners go to stderr, never stdout
[ ] NO_COLOR and TTY detection implemented
[ ] --fields flag limits output to requested columns
[ ] Create commands output the created resource identifier
[ ] Mutating commands are idempotent or have --if-not-exists
[ ] --help includes examples, not just flag descriptions
[ ] Secrets never accepted via flags (use env vars or files)
[ ] Piped output contains zero ANSI escape codes
[ ] Success output includes "next steps" with exact follow-up commands
[ ] Error output includes the exact command to fix or diagnose the issue
[ ] No debug traces or stack traces in default output (use --debug)
[ ] Existing flags, exit codes, and output fields are never removed or renamed (additive only)
参考資料
詳しく調べる場合は、以下を参照してください。デフォルトでは読み込まれません。
| ファイル | 参照するタイミング |
|---|---|
references/output-design.md | JSON エンベロープ、NDJSON ストリーミング、フィールド選択、出力のバージョン管理、トークン効率の高い形式を設計するとき |
references/agent-patterns.md | AI エージェントによる CLI の利用方法、MCP によるラッピング、スキーマのイントロスペクション、動的なツール検出について理解するとき |
references/error-handling.md | 終了コードの全一覧(sysexits.h)、構造化エラー形式の仕様、エラー分類の設計を確認するとき |
references/interactivity.md | TTY 検出のコードサンプル(Node/Python/Go/Rust)、NO_COLOR の実装、進捗バーのパターン、プロンプトの設計を確認するとき |
references/composability.md | パイプに適したパターン、標準入力の規約、コマンド間の連結、NDJSON ストリーミングを確認するとき |
references/discoverability.md | ヘルプテキストの構成、シェル補完の生成、man ページ、エージェントによるイントロスペクション用の --help-json を確認するとき |
references/security.md | シークレットの取り扱い、エージェントのコンテキストにおける入力検証(パストラバーサル、インジェクション)、権限のスコープ設定を確認するとき |
references/testing.md | 出力契約のテスト、CLI 統合テストのパターン、スナップショットテスト、CI でのスキーマ検証を確認するとき |