AIエージェント向けCLIは、自然言語で呼び出せるCLIを指すのではない。
自然言語をコマンドへ変換する仕事は、現在のモデルだけでも行える。
CLI側に必要なのは、実行後の状態をエージェントが読み、次の操作を選べる形で返すことである。
この考え方を、sitesnap、Shimon、pdfmintという三つのCLIへ適用している。
三つは用途も出力するファイルも異なるが、エージェントとの境界には同じ設計を置いた。
SkillからCLIへ切り出す
最初に分けたのは、Agent Skillが担う処理とCLIが担う処理である。
Skillは、使う場面、引数の選び方、失敗時の対応をエージェントへ伝える。
PlaywrightやPuppeteerを起動してファイルを生成する処理は、TypeScriptで実装したCLIが担う。
処理本体をSkillへ書くと、エージェントが手順を解釈するたびに実行方法が揺れる。
Chromiumのような外部依存もあるため、Markdownの指示だけでは実行環境を揃えられない。
一方、CLIへ切り出せば、人がシェルから実行するときも、エージェントやCIが実行するときも同じ入口を使える。
SkillはCLIの代わりではない。
CLIの利用条件をモデルへ渡す薄いインターフェースとして置いている。
観測、検証、成果物化
三つのCLIは、開発作業の異なる位置を受け持つ。
| CLI | 役割 | 主な成果物 |
|---|
| sitesnap | 画面を観測する | PNG、画面状態、検査結果 |
| Shimon | 変更を検証する | 観測値のJSON、変更前後の差分 |
| pdfmint | 文書を成果物にする | PDF、PNG、ページ数 |
sitesnapは、Web画面を撮影し、横方向のはみ出し、コンソールエラー、失敗したリクエスト、アクセシビリティを検査する。
必要な場合は、要素の位置や計算後のCSSを数値として取得する。
Shimonは、プロジェクトが選んだUIの観測値を保存し、変更前後で比較する。
比較を始める前には、同じ条件を二回測るselftestで観測の安定性を確かめる。
pdfmintは、HTMLやMarkdownをPDFとPNGへ変換する。
出力したファイルに加えてページ数や適用設定を返し、提出できる状態まで処理を進める。
次の判断に必要なJSON
人間向けCLIなら、「完了しました」という一文でも作業を続けられる。
人は出力先を探し、ファイルを開き、必要ならコマンドを変えて実行し直せるからである。
エージェントに同じ探索をさせると、余分なコマンドが増え、古い成果物を読む可能性も生まれる。
そのため、成果物を作るコマンドは絶対パスを返し、各コマンドは結果の意味を判断する値をJSONへ含めている。
sitesnapの単発撮影は、次のような結果を返す。
{
"success": true,
"file": "/abs/.cache/sitesnap/localhost_4322/shots/index.png",
"viewport": { "width": 1440, "height": 900 },
"http_status": 200
}
pdfmintは、生成した文書を検査する値も返す。
{
"success": true,
"output": "/abs/report.pdf",
"size_bytes": 920701,
"page_count": 1,
"brand": { "source": "/abs/pdfmint.brand.md", "applied": true }
}
Shimonのselftestはファイル生成ではなく、比較を続けられる状態かを返す。
{
"ok": true,
"command": "selftest",
"changes": []
}
三つのJSONスキーマは完全には統一していない。
sitesnapとpdfmintは success、Shimonは ok を使っている。
共通にしているのはフィールド名ではなく、成否、成果物、実行条件、差分を次の分岐に使える形で返すことである。
stdoutとstderrの役割
--jsonを付けた実行では、成功結果をstdoutへ一つのJSONとして出す。
進捗をstderrへ分けることで、エージェントはstdoutをそのままパースできる。
実行失敗も、エラーコード、説明、修正のヒントを持つ構造へ変換する。
{
"success": false,
"error": {
"code": "INPUT_NOT_FOUND",
"message": "入力ファイルが見つかりません",
"hint": "ファイルパスを確認してください",
"input": "./missing.md"
}
}
messageは起きたことを説明し、codeは分岐に使い、hintは次の候補を示す。
エージェントは文字列から原因を推測せず、入力を直すのか、環境を診断するのかを選べる。
ただし、ヒントに従えば必ず復旧するわけではない。
CLIは候補を構造化し、実際に再実行するかはエージェントが周囲の状態を確認して決める。
終了コードで状態を分ける
JSONを読まない呼び出し元には、終了コードが実行結果の境界になる。
三つのCLIでは「差分を見つけた」と「実行できなかった」を同じ失敗にしない。
Shimonは、一致または取得成功を 0、差分または不安定な観測を 1、設定やブラウザの実行失敗を 2として返す。
エージェントは 1なら差分を調べ、2なら比較結果を評価せず実行環境を直す。
sitesnapは通常のレポートでは検査結果をJSONへ返し、--strictを付けた場合だけ不合格を非ゼロ終了にする。
対話的な調査とCIのゲートでは、同じ検査でも終了コードに求める意味が違うためである。
pdfmintの --expect-pages は、PDFを生成できても期待したページ数でなければ失敗にする。
ファイルが存在することと、用途に合う形式であることを分けている。
再読できる成果物
エージェントの応答だけに結果を残すと、別のエージェントや後続の作業は同じ状態を参照できない。
三つのCLIは、PNG、JSON、PDFをファイルとして保存し、そのパスを実行結果へ含める。
sitesnapのPNGは人が画面全体を確認でき、検査JSONはエージェントが問題箇所を絞るために使える。
Shimonの観測値は変更前後の名前を付けて保存でき、あとから同じ項目を比較できる。
pdfmintのPDFとPNGは、そのまま共有または提出できる。
ファイルを残す目的は履歴を増やすことではない。
作業の途中でモデルやセッションが変わっても、同じ結果を開き直せるようにするためである。
CLIが保証する範囲
Agent Skillには「いつ、どのコマンドを使うか」を書く。
CLI本体は、受け取った引数を検証し、ブラウザを操作し、成果物と実行結果を返す。
モデルの判断に残す部分と、プログラムで固定する部分をこの境界で分けている。
sitesnapは画面が美しいかを判断しない。
Shimonは検出した差分を採用すべきか判断しない。
pdfmintは文章が読みやすいか判断しない。
各CLIが返すのは、選んだ検査条件の範囲で観測できた事実である。
人またはエージェントは、その事実を作業の目的と照らして次の操作を決める。
sitesnap、Shimon、pdfmintは別々のリポジトリで管理している。
共通ライブラリへまとめる前に、成果物の残し方、失敗の分類、SkillとCLIの分担を共通の基準として揃えている。