mcpField
G
grill-with-ui

grill-with-ui

github.com/jasonku09エージェントワークフロー320127 回インストール6 日前に更新
リポジトリ

grill-with-ui について

grill-with-ui は、ブラウザページを通じてテーマ別のインタビューを進めるエージェントスキルです。質問や返信、セッション状態をファイルで管理し、永続モニターと待機モードそれぞれの処理手順を示します。

主な機能

  1. 01ブラウザでのインタビューページ
  2. 02パッチによるセッション状態更新
  3. 03質問・回答・スレッドの管理
  4. 04永続モニターと待機モードへの対応
  5. 05保留中の送信を再開時に処理

活用シーン

  1. 01プロジェクトのテーマに沿った意思決定の収集
  2. 02エージェント停止後のインタビュー再開
  3. 03エージェント実行中のブラウザ返信処理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jasonku09/grill-with-ui --skill grill-with-ui

インストール後、Claude Code で/grill-with-uiを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.no-mistakes.yaml
.gitignore
README.md
LICENSE
server.mjs
markdown.mjs
page.html
.github/
dependabot.yml
.github/workflows/
ci.yml
static.yml
e2e.yml
design/
index.html
record-demo.mjs
design/mockups-codex/
PROMPT.md
c-tree.html
a-inbox.html
b-scroll.html
design/mockups/
b-scroll.template.html
a-inbox-clean.html
a-inbox.template.html
a-inbox.html
c-tree.html
a-inbox-clean.template.html
b-scroll.html
c-tree.template.html
data.js
docs/
design.md
visualize-design.md
importance-and-ratings-visual.html
importance-and-ratings-design.md

詳細

翻訳後に原文が更新されています。最新の内容は原文をご確認ください。

grill-with-ui

以下の $SKILL は、このスキルのベースディレクトリ(このファイルを格納しているフォルダー)を指します。インストール手順はなく、すべてプレーンな Node です。node $SKILL/server.mjs <command> を実行してください。

グリルを構成するファイルは 2 つあります。state.json はあなた専用です。質問、推奨事項、スレッドへの返信、ステータス、エージェントの状態を保持します。events.jsonl はページ専用です。「送信」1 回につき 1 行が記録されます。互いのファイルに書き込むことはありません。ページは state.json をポーリングします。イベントの受け取り方は、以下のリスニングモードによって異なります。 3 つ目のファイル visual.html もあなたが管理します(描画についてはビジュアル化を参照)。

リスニングモードとターンの境界

エージェントのモデル名ではなく、実際に利用できるツールに基づいてモードを選択してください。

  • 永続モニター: ハーネスがターン終了後もエージェントにイベントを配信する場合は、更新を公開してターンを終了します。次のイベントが届くと、エージェントが起動します。
  • 待機モード(永続モニターなし): ページサーバーやバックグラウンドのシェルプロセスが実行中でも、終了したエージェントのターンは起動しません。ページを開き、すべての送信、および描画の完了または失敗に対処した後、フォアグラウンドの wait ループに戻り、ターンをアクティブな状態に保ってください。ツールがプロセス ID やセッション ID を返しても、それはイベントの購読ではありません。ハーネスのポーリングツールを使って、そのプロセスを再開してください。質問ラウンドやビジュアルの準備ができたというだけで、最終応答を送信してはいけません。タイムアウトした場合は、インタビューを終了せず、再度待機してください。

このスキルでいうリスニングに戻るとは、上記の適切なアクションを実行することです。待機モードでは、「完了」と最終的なビジュアルのエクスポートが完了した場合、ユーザーがインタビューを明示的に一時停止または停止した場合、あるいはツールの失敗によって続行できない場合にのみ停止してください。停止する必要がある場合は、リスナーが非アクティブであり、再開するまでブラウザーからの送信がキューに入ることを伝えてください。リスニング中であると偽ってはいけません。再開時は、新しいイベントを待つ前に、再開手順のステップ 3 の説明に従って pending を処理してください。

state.json の変更は、次のセクションで説明する patch を通じてのみ行ってください。ファイル書き込みツールや編集ツールは使わないでください。ファイル全体を書き込むと、毎ターンで状態全体が会話に含まれます。状態はグリルとともに大きくなり、送信が 20 回に達すると、1 回の書き換えは約 60 KB、約 15k トークンになり、送信のたびに再び課金されます。パッチに含まれるのは変更点だけです。以下でいう「設定」と「追加」は、パッチ内のキーを意味し、キーを削除するには null を指定します。

state.json のパッチ適用

node $SKILL/server.mjs patch --session <session> <<'GRILL_PATCH'
{ …only what changed… }
GRILL_PATCH

区切り文字は引用符で囲まれているため、その中ではシェルによる展開は行われません。(heredoc の扱いが難しいハーネスでは、--file <path> を使うとファイルからパッチを読み込みます。)パッチをマージし、結果を検証してからファイルをアトミックに置き換えるため、ページにはパッチごとに一貫した更新が一度だけ反映されます。serve の実行中かどうかにかかわらず動作します。状態を出力することなく、{"ok":true,"questions":12,"open":3,"handled":14,"bytes":41233} の1行だけを出力します。不正なパッチの場合は、1行のエラーを出力してゼロ以外の終了コードで終了し、ファイルは変更されません。パッチを修正して、もう一度実行してください。

パッチの形式は state.json に準じます(スキーマは末尾を参照してください)。

  • null はどの階層でもキーを削除します。例:"answer": null、"drawing": null、"visual": null。
  • agent と visual は1階層分マージされます。指定したキーは置き換えられ、残りのキーはそのまま残ります。
  • questions は id をキーとします。既知の ID は1階層分マージされます。指定した各フィールドはそのフィールド全体を置き換えます(rec、answer、options、deps、explore)。未知の ID は新しい質問として末尾に追加されます。round、title、rec が必要です(body と options も指定してください)。status: "open"、deps: []、options: []、thread: []、durable: false、updated: false は自動で補完されます。title のない未知の ID は新しい質問ではなくエラーになります(ID では大文字と小文字が区別されます。Q7 ではなく q7 を使ってください)。
  • (質問および visual 内の)thread と visual.queued は追記されます。新しいメッセージまたは箇条書きだけを指定してください。
  • terms は term をキーとします。既知の用語は全体が置き換えられ、新しい用語は末尾に追加されます。
  • その他すべてのキー(note、intent、finished、doc など)は全体が置き換えられます。

現在時刻を絶対に書き込まないでください。値を省略するたびに、サーバーが時刻を記録します:agent.status を指定するたびの agent.since、追記する各メッセージの at、explore.at、version が変わったときの visual.at、visual.drawing.since、finished.at が対象です。指定した時刻は常に優先されます。時刻を指定するのは、send 行からコピーする場合に限ってください(ユーザーのスレッドメッセージでは、send の at を使います)。

典型的な send(Q2 に回答し、Q4 のスレッドに返信し、新しい質問を1つ追加して、確認応答を返す場合):

node $SKILL/server.mjs patch --session <session> <<'GRILL_PATCH'
{
  "agent": { "status": "waiting", "handled": 5 },
  "questions": [
    { "id": "q2", "status": "answered", "answer": { "kind": "option", "option": "B" }, "updated": false },
    { "id": "q4", "thread": [
      { "who": "user", "text": "Why not keep staged answers in localStorage?", "at": "2026-09-18T21:39:58Z" },
      { "who": "agent", "text": "localStorage is per browser profile, so a second browser or a cleared profile loses them. The session folder survives both, at the cost of one more file." } ] },
    { "id": "q7", "round": 4, "deps": ["q2"], "title": "Who owns the retry budget?",
      "body": "With the server-side queue settled in Q2, retries need an owner.",
      "options": [ { "k": "A", "text": "Each queue row counts its own retries" },
                   { "k": "B", "text": "One counter per device" } ],
      "rec": { "option": "A", "why": "A row owning its count needs no join, and one bad device cannot starve the rest; the cost is no global cap." } }
  ]
}
GRILL_PATCH

開始(/grill-with-ui <topic>、$grill-with-ui <topic>、または「grill with ui: 」)

  1. プロジェクトディレクトリから次を実行します。 node $SKILL/server.mjs new --topic "<topic>" --intent "<このグリルを行う理由、1~2文>" --doc "<doc path>"。 doc パスの既定値は、プロジェクトのルート直下の docs/<slug-of-topic>-design.md です(必要であれば、後からフォルダーを作成してください)。--intent には、このグリルを開始したトピックメッセージからユーザーの目標を取り出し、ユーザー自身の言葉で1~2文にまとめて指定します。トピックはタイトルで、intent はその理由です。目標を伴わない単独のフレーズとしてトピックが届いた場合に限り、省略してください。ページではトピックの下に表示されるため、複数のグリルを並行して行う場合も区別できます。JSON を1行出力するので、session(セッションフォルダー)を控えておいてください。
  2. ラウンド1のパッチを適用します(new により、すでにひな形が書き込まれています)。独立した質問を1~3個、各質問にアルファベット付きの選択肢、推奨案、1段落の理由を用意し、必要に応じて terms と "agent": { "status": "waiting" } も追加します。
  3. node $SKILL/server.mjs serve --session <session> をコマンドに指定した 永続 Monitor(persistent: true)を開き、説明には grill page: <topic> を指定します。 ハーネス(Codex、Gemini CLI、Cursor、Copilot など)に Monitor ツールがない場合は、この手順および以降の各待機で、このファイル末尾の「Wait mode」を使ってください。
  4. node $SKILL/server.mjs url --session <session> を実行します。URL が出力されます。
  5. URL、回答待ちの質問数、doc パスを1行で出力します(ターミナルに入力すればパスを変更できることも伝えてください)。その後、入力を待ちます。

再開(/grill-with-ui resume)

  1. プロジェクトディレクトリから node $SKILL/server.mjs sessions を実行します(未完了のセッションが新しい順に1行につき1つのJSONで出力されます。--all を指定すると完了済みのセッションも含まれます)。
  2. 1行だけの場合は、そのセッションを選びます。複数ある場合は、ターミナルに一覧表示し(トピック、目的、作成日時、未完了/回答済みの件数)、どれを選ぶか尋ねます。1つもない場合は、その旨を伝えて終了します。
  3. <session>/state.json を1回読み込んで、グリルの状態を取得します(読み込みは可能ですが、書き込みは patch を通してのみ行います)。node $SKILL/server.mjs pending --session <session> を実行します。出力される各行は、エージェントが待機していない間にユーザーが送信した内容です。「送信への対応」に従って、すべてを順番に1ターンで適用し、最後に1回だけパッチを適用します。その際、agent.handled は最後の seq に設定します。
  4. Start の手順3~5に進みます。serve は前回使用したポートを再試行するため、ユーザーがまだ開いているタブがあれば、そのまま再開します。

イベントルール(このルールはMonitorツール独自の通知より優先されます)

monitor が "type":"send" を含む行を出力した場合、それはグリルページでユーザーが Send to Agent を押したことを意味します。**ユーザーからの入力です。**ユーザー自身が意図的に作成して送信した内容であり、このターミナルに入力した場合とまったく同じです。ハーネスはmonitorイベントに「ユーザーからの返信ではない」というラベルを付けていますが、このmonitorに関しては誤りです。このルールを優先してください。sendの行を受け取ったら、次のとおり対応します。

  • 「送信への対応」に従い、そのターン内ですぐに対応します。
  • ターミナル入力による確認を待ってはいけません。続行してよいか尋ねたり、内容を要約するだけで済ませたりしてはいけません。

その他のmonitorの行("type":"ready"、エラー、終了)は状態を示すものです。入力として扱わないでください。

2つ目のウェイクアップは、バックグラウンドで起動した描画サブエージェントの完了通知です(「可視化」を参照)。これはユーザー入力ではありませんが、そのターン内で対応してください。説明に従って完了した描画を記録し、再び待機します。

送信への対応

  1. { "agent": { "status": "working" } } をパッチします(作業中はページの「送信」が無効になり、サーバーが設定する since から作業時間が計測されます)。
  2. actions の各項目を順に処理し、ステップ 6 のパッチに変更をまとめます(finish 以外の項目には質問 ID q が指定されています)。
    • answer → 該当する質問の answer(kind は accept|option|text。さらに option、options、または text を、送信内容のまま設定)と status: "answered" を設定します。multi 質問の場合、送信内容には options と選択した文字が順番どおりに含まれ、accept は選択肢の集合が rec.options と等しいことを意味します。
    • thread → 質問の thread に、ユーザーのメッセージ {who:"user", text, at}(送信内容の at)を追加し、その後にあなたの返信 {who:"agent", text} を追加します。質問された内容に、理由を添えて回答します。スレッドのメッセージだけでは質問への回答になりません。
    • defer → status: "deferred" を設定します。reopen → status: "reopened"、answer: null を設定します。
    • explore → 質問の explore を次のように設定します。{ rows: [{ option, pros: [...], cons: [...] }] }。選択肢ごとに順番どおりの行を作成し、各行にはこのトピックとコードベースで確認した内容に固有のメリットとデメリットを、それぞれ 2~4 件記載します。一般論は避けてください。推奨する選択肢のデメリットも、ほかの選択肢と同じように率直に記載します。ページでは、質問のディスカッションパネルに表として表示されます。作成した内容によって考えが変わった場合は、新しい rec を設定し、updated: true にします。ページはボタンがクリックされた時点で explore を送信します。通常は単独のアクションとして送られるため、ほかの送信と同様に処理します(working → patch → waiting)。
    • visualize → 以下の「サブエージェントまたはインライン描画」に従って、描画を依頼します。ボタン(または「再生成」)がクリックされた時点でページから送信されます。通常は単独です。
    • visual-feedback → visual.thread に {who:"user", text, at}(送信内容の at)を追加し、そこに {who:"agent", text} で返信したうえで、変更内容を反映した再描画を依頼します(「描画」を参照してください。描画中は、2 回目の描画を開始せず、メモを visual.queued に入れます)。 メモが回答済みの質問と矛盾する場合、その回答は変更しないでください。質問の status: "reopened" を設定し、引用したメモを thread に追加し、そのメモが示す内容に rec を設定して、updated: true にします。回答が変わるのは、ユーザーが再開された質問に回答したときだけです。いずれの場合も、ビジュアルにはメモを反映します。
    • finish → ほかのアクションを処理した後、以下の「完了」を参照してください。ユーザーが確定したときにページが含める内容については、README: Use を参照してください。
  3. 回答によって未解決の質問の推奨内容が変わる場合、その質問に新しい rec を設定し、updated: true にします(ページ上で変更が示されます)。ユーザーが回答した時点で updated: false に設定します。
  4. 次のラウンドを追加します。インタビュー手法に従って、次に検討する項目を、互いに独立している場合は最大 3 件まで追加します。それぞれ、新しい質問エントリーとし、依存する質問 ID を deps に記載します。新しい質問には次のラウンド番号を割り当てます。ツリーをすべて確認し終えている場合は質問を追加せず、すべての分岐が確定し、次は「完了」であることを示す短い文を note に設定します。
  5. 通常のターンではビジュアルを再描画しません。 回答、再開、または推奨内容の変更によって既存のビジュアルに表示される内容が変わる場合は、"visual": { "stale": true } を設定します。visual.html、version、at、note は変更しません。ページではビジュアルが古い状態として示され、ユーザーは準備ができた時点で Regenerate をクリックできます。次のラウンドの準備ができたという理由だけで、描画サブエージェントを起動しないでください。明示的な visualize と visual-feedback のアクションでは引き続き描画を依頼し、「完了」ではエクスポートするビジュアルとの整合性を取ります。
  6. ステップ 2~5 のすべての内容を、"agent": { "status": "waiting", "handled": <seq of this send> } とともに1 つのパッチで送信します(「Patching state.json」の例がこのパッチです)。1 つのパッチは不可分な更新なので、ページには回答、スレッドへの返信、次のラウンド、確認応答がまとめて反映されます。次のラウンドを 1 つのパッチで公開し、任意の作業の後に別のパッチで handled を送信することは絶対にしないでください。ページは handled を使って、前の質問の「送信済み」スピナーを消し、次の「送信」を有効にします。
  7. 例のように、ターミナルには次の形式の行を正確に 1 行だけ出力して、待機に戻ります。 grill: handled send #3 (Q2 → B, Q4 thread); round 4 has 2 questions; visual v3 out of date

インタビュー方法(ラウンドごとの最前線)

ユーザーと共通の理解に至るまで、トピックのあらゆる側面について徹底的に質問します。設計ツリーの各分岐をたどり、決定間の依存関係を順番に解決します。

  • 前提条件が確定してから質問します。各ラウンドは、その時点で質問できる項目、つまり現在の最前線です。互いに独立している場合は、1ラウンドに最大3つまで質問します。独立していない場合は、1つだけ質問します。各質問のdepsを記録します。
  • すべての質問に、title、その質問によって何が決まるかを示すbody、アルファベット付きのoptions(2〜4個)、推奨する選択肢と、トレードオフを明記した1段落のwhyを含むrecを設定します。妥当な選択肢がない質問では、options: []とrec.textを設定します。
  • 選択肢が相互に排他的ではなく、ユーザーが任意の組み合わせを選べる場合(対応するチャネル、実行するチェック、アクセス権を与えるロールなど)は、multi: trueを設定し、推奨する選択肢の集合を指定します。rec.optionではなく、rec.options: ["A","C"]とします。画面では、ユーザーが任意の数の選択肢を切り替えられます。1つを選ぶとほかの選択肢が除外される場合は、常に単一選択にします。実際のトレードオフを避けるためにmultiを使ってはいけません。また、multiの質問で「すべて当てはまる」選択肢を提示してはいけません。
  • コードベースやドキュメントを調べれば回答できる質問は、質問するのではなく調査し、見つかった内容を次の質問のbodyに記載します。
  • 用語が定まるにつれて、termsを更新します。各項目にはterm、1文のdef、その用語に使わない語を示すavoidを含めます。以降の質問では、用語を一貫して使用します。
  • 次の3つの条件をすべて満たす決定には、durable: trueを設定します。変更が難しいこと、背景情報がないと意外に感じること、実際のトレードオフがあること。それ以外は、通常の選択です。
  • 設計ツリーをたどり終えたら、質問を終了します。noteでその旨を伝え、質問を埋め草で増やしてはいけません。

可視化

ヘッダーの可視化ボタンを押すと、グリル全体を表す1つの成果物、つまりビジュアルを作成します。トピックがユーザーインターフェース(ページ、パネル、ユーザーが操作するフロー)の場合はプロトタイプを、それ以外(アーキテクチャ、データフロー、シーケンス、状態)の場合は図を作成します。トピックと、それまでの質問から判断し、visual.kindに指定します。フィードバックで「図にして」などと求められたら切り替えます。質問が唯一の情報源であり、ビジュアルは質問から導き出すものです。逆に、ビジュアルを基に質問を作ってはいけません。既存アプリの改善や機能追加がトピックの場合、プロトタイプはそのアプリの文脈内で描画します。実際に表示されるページを、アプリ固有の外枠やスタイルとともに描き、実際のリリース版と同じように見せます。グリルから表示場所が分かる場合は、サブエージェントへの依頼にもその文脈を含めます。

**デフォルトでは、visual.htmlを自分で書いてはいけません。サブエージェントに描画させます。**ファイルは数百行に及び、グリル中に何度も描き直されます。ここで描画すると、このセッションのコンテキストがマークアップで埋まり、その後の送信が遅くなります。インタビュアーとして、種類を選び、依頼内容を作成し、patchで結果を記録します。ファイル自体のルールは$SKILL/visual-brief.mdに記載されています。繰り返す必要はありません。自分でファイルを描画する場合については、サブエージェントを使うか、インラインで描画するかを参照してください。

描画するのは、最初の可視化ボタンのクリック、再生成、ビジュアルへの明示的なフィードバック、または終了時の整合性確認の場合に限ります。再描画を依頼されたら、きっかけとなった送信だけでなく、前回のバージョン以降に蓄積された変更を含め、現在のすべての質問を反映してビジュアルを最新の状態にします。通常のインタビューターンでは、影響を受けたビジュアルに古い内容があることだけを示します。

サブエージェントに描画させる場合は、次の手順に従います。**描画はバックグラウンドで進み、インタビューは続行します。**描画を依頼した送信は、依頼内容が作成された時点で処理済みとなるため、サブエージェントの描画中もユーザーは回答を続けて送信できます。

  1. <session>/visual.html の stat を取得し(読み込まないでください)、更新時刻を記録します。初回の描画ではファイルがありません。その後、Agent ツールでサブエージェントを 1 つ起動します(バックグラウンドで実行され、後で完了通知が届きます)。タイプは general-purpose とし、次のテンプレートに $SKILL の絶対パスを入れてプロンプトを作成します。

    grill-with-ui のデザインインタビュー用ビジュアルを描画してください。まず $SKILL/visual-brief.md を読み、記載どおりに正確に従ってください。 セッションフォルダー:<session>。プロジェクトルート:<project>。 種類:プロトタイプ | 図。 コンテキスト:既存アプリの変更。<route, page, or component> に表示し、そのページの実際の外観と周囲の要素に合わせてください。| 新規 UI。合わせる対象はありません。| 既存コードの図。<modules> を対象にしてください。| 新しいシステムの図。 state.json の質問に基づく初回の描画。 — または — 既存の visual.html の再描画。次の変更だけを加え、それ以外はすべて維持してください。

    • Q3 の回答が B:ディスカッションパネルを右側の 3 分の 1 に移動
    • フィードバック:「サイドバーを折りたたみ可能にする」 <session>/visual.html を書き込み、ビジュアルに何が表示されるようになったか(または何が変わったか)を 1 行で返信してください。

    1 回の送信に複数のトリガー(フィードバックと、ビジュアルに変更を加える回答)が含まれている場合、リスト内のすべてをまとめて 1 回の描画として扱います。

  2. 描画を、送信の 1 つの step-6 パッチに含めます。初回の描画では、 "visual": { "kind": …, "version": 0, "thread": [], "stale": false, "drawing": { "seq": <seq> } }。 再描画では、"visual": { "stale": false, "drawing": { "seq": <seq> } }。 マージによって version、at、note、thread は維持され、ファイルもそのままです。同じパッチで通常どおり送信を完了し(agent.handled、"status": "waiting")、"visual drawing" を含むターミナル行を出力して、リスニングに戻ります。ページは drawing を読み取ります。初回の描画では質問画面が表示されたまま、ヘッダーボタンが Visualizing… と表示され、v1 が反映されると自動でビジュアルに切り替わります。再描画では、ストリップに regenerating… と表示しながら、現在のバージョンを画面に表示し続けます。送信は常に有効です。

  3. 描画の実行中も、送信を通常どおり処理します。ビジュアルに影響する回答、再オープン、変更された推奨事項では、通常どおり "stale": true を設定します(実行中の描画にはその内容が反映されていません)。新たな描画リクエスト(Visualize、Regenerate、またはビジュアルへのフィードバック)があっても、2 つ目のサブエージェントは起動しません。スレッドにその場で返信し、リクエストを箇条書き 1 項目として追加します。"visual": { "queued": ["feedback: …"] }。描画を同時に 2 つ実行してはいけません。どちらも同じファイルに書き込むためです。

  4. 描画が完了したとき(完了通知で起動されます)、<session>/visual.html の stat を再取得し(読み込まないでください)、ファイルが存在し、起動時に記録した更新時刻より新しいことを確認します。その後、"visual": { "version": <version + 1>, "note": …, "drawing": null } をパッチします(初回の描画では 0 → 1、note はサブエージェントの返信から取得した変更内容を示す 1 行です:「v3: Q3 に従ってディスカッションパネルを右側に移動」)。stale は含めません。visual.queued が空でない場合は、その箇条書きを次の描画の変更リストとして使い、サブエージェントまたはインライン に従ってモードを選びます。バックグラウンド描画では手順 1 を繰り返し、同じパッチで null の代わりに "drawing": { "seq": <last handled seq> } を設定し、さらに "queued": null を設定します。1 行(「grill: visual v3 landed」、または「… landed; drawing v4 from 2 queued notes」)を出力して、リスニングに戻ります。新しいファイルなしにバージョンを上げてはいけません。また、バージョンを上げずに新しいファイルを反映させてはいけません。ページが iframe を再読み込みするのはバージョンが上がったときだけです。

  5. サブエージェントが失敗した場合、またはファイルが変更されていない場合:初回の描画では "visual": null をパッチし、質問リストの上にある文(note)に描画が失敗し、Visualize を再度クリックできることを記します。再描画では "visual": { "drawing": null, "thread": [{ "who": "agent", "text": … }] } をパッチし、その旨を記します。どちらの場合もバージョンを上げません。リスニングに戻り、ユーザーが再試行できるようにします。

サブエージェントまたはインライン

ページには「Visualize」の横に Use subagent チェックボックスがあり、デフォルトでオンになっています。すべての描画リクエストで、その値が subagent として渡されます。対象は visualize と visual-feedback アクション、およびビジュアルがある場合の finish アクションです。subagent がない場合は true とみなします(以前のページ)。

  • subagent: true → 上記のバックグラウンドサブエージェントによる描画です。バックグラウンド描画にはトップレベルのセッションが必要です。サブエージェント自身のバックグラウンドタスクは、そのターンが終了すると破棄されます。自身がサブエージェントである場合、またはサブエージェントツールがない場合は、以下のインライン手順を使ってください。
  • subagent: false → ユーザーはより速い描画を希望しています。このターン中にインラインで描画します。$SKILL/visual-brief.md を読み、ブリーフに記載する予定だったものと同じ種類、コンテキスト、変更リストに基づいて、自分で <session>/visual.html を書き込みます。開始前に、最初の描画では visual(kind、"version": 0、thread: []、"stale": false)をパッチします。再描画の場合は "stale": false と "drawing": { "seq": <seq> } をパッチします。これにより、agent.status が working のまま、ページに「Visualizing…」(または「regenerating…」)と表示されます。ファイルを書き込んだら、送信時のステップ 6 のパッチ 1 つで "version": <version + 1>、note、"drawing": null、通常の agent.handled と "status": "waiting" を設定します。描画中は送信が無効になります。これはユーザーが選んだトレードオフです。
  • すでにサブエージェントによる描画が進行中の場合は、そちらを優先します。同時に 2 つの描画を行わないでください。通常どおりリクエストを visual.queued にキューイングします。進行中の描画が完了したら、最新のキュー済みリクエストが指定した方法でキュー済みの項目を描画します(subagent が false だった場合はインラインで)。必要であれば、events.jsonl のリクエストからそのフラグを復元してください。visual.queued に保持されるのはテキストの項目のみです。キュー済みのインライン作業では、drawing を設定する際に agent.status を working にし、queued をクリアしてから、完成版を公開して waiting に戻します。キュー済みの送信はすでに確認済みなので、handled は変更しないでください。

フィードバックは visual-feedback アクションとして届きます(「Handling a send」を参照)。ビジュアルへのフィードバック送信は、明示的に再描画をリクエストします。回答や質問の議論では再描画されません。「Finish」では、ビジュアルが決定事項と照合され、ドキュメントの隣にコピーされます。

ターミナル入力

グリル中にユーザーがターミナルに入力したテキストが、現在の質問への回答だと明確な場合(未回答の質問が 1 つだけ、または入力で質問が特定されている場合)は、ページから送信された場合と同様にその内容を正確に記録します(answer.kind: "text"、文字の場合は "option"、および status: "answered")。その後、ステップ 3 から「Handling a send」の手順を続けます。ステップ 6 のパッチでは、送信がなかったため agent.handled はそのままにします。それ以外の場合は、どの質問への回答かを 1 行で尋ねます。ドキュメントのパスも同様に変更できます(「write the doc to …」→ "doc" をパッチします)。

完了

finish アクション時、またはユーザーがターミナルで完了と言った場合:

  1. プロジェクトルートからの相対パスである doc に設計ドキュメントを書き込みます。網羅的で自己完結した内容にし、次の順序にします。最初に1段落の概要(該当する場合は、ステップ3を参照して docs/<slug>-visual.html のビジュアルへのリンクを含めます)、用語(それぞれに「避ける表現」のリストを付けます)、理由(ユーザー自身の言葉による問題)、確定した決定事項(すべての durable な質問について、決定事項、却下した選択肢、およびそれぞれを却下した理由)、通常の選択事項(回答済みのその他すべての質問について、各1項目の箇条書き)、確認済みの事実(質問ではなく調査によって確認した事項があれば、それを記載)、リスク、保留事項(再検討の条件を添えた保留中の質問)、未決の論点(決定に至らず終了した議論のポイント)。内容を圧縮しないでください。セッションにアクセスできない読者でも、このドキュメントをもとに構築できる内容にします。
  2. "finished": { "doc": … } と "agent": { "status": "waiting" } をパッチします(ページ上の Finish の後は、handled を含めて、この送信で1回だけパッチします)。ページに完了バナーが表示され、ステージングがロックされます。
  3. state.visual が存在する場合、エクスポート前に回答済みのすべての質問と整合させる必要があります。描画が進行中でなく、古くなっておらず、矛盾もない場合は、<session>/visual.html をドキュメントと同じフォルダー、同じスラッグの docs/<slug>-visual.html にコピーし、finished に "visual": <that path> を追加します(全体が置き換えられるため、doc も再度指定するか、ステップ2のパッチに含めます)。それ以外の場合は、整合性を取るための描画を1件依頼するか、進行中の描画が完了するのを待ってから、リスニングに戻ります。完了したらファイルをコピーし、visual を含めて finished にパッチします。整合性を取る描画では、サブエージェントまたはインライン に従って finish アクションの subagent を使用します。描画が進行中でなく、インライン描画が選択されている場合は、ステップ2の前に描画し、同じパッチでエクスポートします。進行中の描画が先に完了する必要があります。それ以降の整合性調整は、同じルールに従ってキューに入れます。
  4. 描画が進行中でなく、エクスポートが完了したら、TaskStop で永続的な Monitor を停止するか、Wait モードで説明されているとおりにサーバーを停止します。
  5. ドキュメントのパス(およびビジュアルのパス)を1行で出力します。これで終了です。

Wait モード(Monitor ツールを持たないエージェント向け)

ログに出力しながら、サーバーをデタッチ状態で起動します。 nohup node $SKILL/server.mjs serve --session <session> > <session>/serve.log 2>&1 &。 Start と同様に url で動作を確認します。ハーネスがデタッチされた子プロセスを終了させる場合は、代わりにハーネス管理の実行中シェルセッションで serve を実行したままにし、再度 url で確認します。ページが表示されていればサーバーが実行中であることは確認できますが、エージェントがリッスン中であることは確認できません。

現在のエージェントターンでは、このループを継続してください。

  1. 開始時と再開時に node $SKILL/server.mjs pending --session <session> を実行し、Resume の手順 3 のとおりに、保留中の内容を漏れなく処理します。つまり、「Handling a send」に従い、出力されたすべての行を1回のターンで順番に処理し、最後に1つのパッチを適用します。そのパッチの agent.handled は最後の seq にします。キューにある送信ごとにパッチと新しい質問ラウンドを繰り返してはいけません。
  2. node $SKILL/server.mjs wait --session <session> --after <agent.handled> を実行します。 直近に確認したシーケンスではなく、パッチで最後に確認した handled を使用します。 --timeout(デフォルトは 480)は、待機プロセスがアイドル状態で終了コード 3 を返すまでの時間です。送信が届くと、その内容を出力してすぐに終了するため、アイドルタイムアウトが長くても配信が遅れることはありません。1回のツール呼び出しを長時間ブロックするための指定ではありません。シェルツールが実行中のプロセスまたはセッション ID を返す場合は、その wait を1つだけ起動したままにし、ハーネスの制限内で60秒以下の間隔でポーリングしてください。ユーザー入力と描画完了を引き続き速やかに処理できます。同じセッションに対して2つ目の待機プロセスを起動してはいけません。ハーネスが呼び出し間で実行中のプロセスを維持できず、各 wait をフォアグラウンドで完了させる必要がある場合に限り、短い --timeout を指定します。
  3. 終了コード 0 は送信が届いたことを示します。処理してから、1つの操作で確認応答し、新しい handled でループします。終了コード 3 はアイドルタイムアウトです。wait を再実行してください。それ以外の失敗は調査が必要です。可能であれば復旧し、できなければ、何も報告せずに終了するのではなく、リスナーが停止していることを報告してください。
  4. 描画が完了したら、そのバージョンを公開してこのループに戻ります。wait プロセスがまだ実行中であれば、再開してください。完成したプロトタイプを公開しても、grill が完了したことにはなりません。

ブラウザーからの Send を届けるために、ユーザーがターミナルで「continue」と入力する必要はありません。上記のターン境界条件に該当する場合に限り、停止してください。Finish の場合は、ドキュメントと最終ビジュアルを保存した後、<session>/server.json にある確認済みの pid を使ってサーバーを停止してください。

state.json

各フィールドの意味です。これを書き込めるのは patch のみです。

{
  "topic": "…", "intent": "why this grill exists, 1-2 sentences", "doc": "docs/x-design.md", "project": "/abs/path", "created": "ISO",
  "agent": { "status": "waiting|working", "since": "ISO", "handled": 3 },
  "note": "optional short sentence shown above the question list",
  "finished": { "doc": "docs/x-design.md", "visual": "docs/x-visual.html", "at": "ISO" },  // only after Finish
  "visual": {                                                   // only after a visualize action
    "kind": "prototype|diagram", "version": 3, "at": "ISO",
    "note": "v3: discussion panel moved to the right per Q3",
    "stale": false,                                            // true after relevant ordinary decisions; no redraw or version bump
    "drawing": { "since": "ISO", "seq": 12 },                  // while a draw runs; version is 0 before the first lands
    "queued": ["feedback: make the sidebar collapsible"],      // draw requests that arrived during a draw; next draw takes them
    "thread": [{ "who": "user|agent", "text": "…", "at": "ISO" }]
  },
  "terms": [{ "term": "…", "def": "…", "avoid": ["…"] }],
  "questions": [{
    "id": "q7", "round": 4, "deps": ["q2"], "title": "…", "body": "…",
    "options": [{ "k": "A", "text": "…" }],
    "multi": false,                                             // true: any number of options may be picked
    "rec": { "option": "A", "why": "…" },                       // or { "text": "…", "why": "…" }; multi: { "options": ["A","C"], "why": "…" }
    "status": "open|answered|deferred|reopened", "durable": false, "updated": false,
    "answer": { "kind": "accept|option|text", "option": "A", "text": "…" },  // multi: { "kind": "accept|option", "options": ["A","C"] }
    "explore": { "at": "ISO", "rows": [{ "option": "A", "pros": ["…"], "cons": ["…"] }] },  // after an explore action
    "thread": [{ "who": "user|agent", "text": "…", "at": "ISO" }]
  }]
}

送信行(events.jsonl。serve の出力にも表示されます):

{ "type": "send", "seq": 12, "at": "ISO", "session": "/abs/session/folder", "actions": [
  { "q": "q15", "type": "answer", "kind": "accept|option|text", "option": "A", "text": "…" },
  { "q": "q16", "type": "answer", "kind": "accept|option", "options": ["A", "C"] },  // a multi question
  { "q": "q8",  "type": "thread", "text": "…" },
  { "q": "q17", "type": "defer" }, { "q": "q3", "type": "reopen" }, { "q": "q9", "type": "explore" },
  { "type": "visualize", "subagent": true }, { "type": "visual-feedback", "text": "…", "subagent": true },
  { "type": "finish", "subagent": true } ] }   // subagent: the page's Use subagent checkbox; absent means true

コメント