~/hayashigoto/logs/agent-tools-cli-contract

>cat/logs/agent-tools-cli-contract.md

title: AIエージェント向けCLIは、次の判断を返す
published:
topics: Agent ToolsCLIAI AgentTypeScript

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は successShimonは 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が返すのは、選んだ検査条件の範囲で観測できた事実である。 人またはエージェントは、その事実を作業の目的と照らして次の操作を決める。

sitesnapShimonpdfmint別々のリポジトリで管理している。 共通ライブラリへまとめる前に、成果物の残し方、失敗の分類、SkillとCLIの分担を共通の基準として揃えている。