@youtyan/browser-pilot 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,367 @@
1
+ ---
2
+ name: bp-generate-manual
3
+ description: Use when creating site operation manuals with screenshots and videos using browser-pilot MCP tools. Triggers on "マニュアル作成", "操作マニュアル", "手順書", "使い方ガイド", "site manual", "how-to guide".
4
+ ---
5
+
6
+ # Site Manual Generator — ブラウザ操作マニュアル自動生成
7
+
8
+ 2つのワークフローがある。手動で設計してから撮影する **Design-First** と、操作を記録して自動生成する **Record-First**。
9
+
10
+ ## Workflow Selection
11
+
12
+ ```dot
13
+ digraph {
14
+ rankdir=TB;
15
+ "目的は?" [shape=diamond];
16
+ "Design-First (手動設計)" [shape=box];
17
+ "Record-First (自動生成)" [shape=box];
18
+
19
+ "目的は?" -> "Design-First (手動設計)" [label="丁寧なマニュアルが必要\nレイアウト・文言を制御したい"];
20
+ "目的は?" -> "Record-First (自動生成)" [label="操作手順をすばやく記録したい\nテストスクリプトも欲しい"];
21
+ }
22
+ ```
23
+
24
+ ---
25
+
26
+ # Workflow A: Design-First(手動設計 → 撮影)
27
+
28
+ サイトを回遊して構造を把握し、撮影仕様を詰めてから操作を録画・撮影し、Markdownマニュアルを生成する。「先に設計、後で撮影」で録画中の無駄な尺を排除する。
29
+
30
+ ## Decision Tree
31
+
32
+ ```dot
33
+ digraph {
34
+ rankdir=TB;
35
+ "Phase 1: ヒアリング" [shape=box];
36
+ "Phase 2: 回遊・調査" [shape=box];
37
+ "Phase 3: spec.md 作成" [shape=box];
38
+ "ユーザー承認?" [shape=diamond];
39
+ "Phase 4: 撮影実行" [shape=box];
40
+ "最初3ステップで失敗?" [shape=diamond];
41
+ "Phase 5: manual.md 生成" [shape=box];
42
+
43
+ "Phase 1: ヒアリング" -> "Phase 2: 回遊・調査";
44
+ "Phase 2: 回遊・調査" -> "Phase 3: spec.md 作成";
45
+ "Phase 3: spec.md 作成" -> "ユーザー承認?";
46
+ "ユーザー承認?" -> "Phase 3: spec.md 作成" [label="修正"];
47
+ "ユーザー承認?" -> "Phase 4: 撮影実行" [label="OK"];
48
+ "Phase 4: 撮影実行" -> "最初3ステップで失敗?";
49
+ "最初3ステップで失敗?" -> "Phase 3: spec.md 作成" [label="yes: spec見直し"];
50
+ "最初3ステップで失敗?" -> "Phase 5: manual.md 生成" [label="no: 続行"];
51
+ }
52
+ ```
53
+
54
+ ## Phase 1: ヒアリング
55
+
56
+ ユーザーに以下を確認する。未指定のものはデフォルト値を使う。
57
+
58
+ | 項目 | 選択肢 | デフォルト |
59
+ |------|--------|-----------|
60
+ | 対象サイトURL | 自由入力 or 既に開いてるタブ | — |
61
+ | マニュアルの目的 | 自由入力(例: 経費申請の手順) | — |
62
+ | 読者レベル | A: 社内メンバー(簡潔) / B: エンドユーザー(丁寧) | B |
63
+ | 出力形式 | A: Markdownのみ / B: 動画のみ / C: 両方 | C |
64
+
65
+ ヒアリング後、出力ディレクトリを決定:
66
+
67
+ ```
68
+ docs/YYYY-MM-DD-{site}-{topic}/
69
+ spec.md 撮影仕様(Phase 3)
70
+ manual.md 最終成果物(Phase 5)
71
+ images/ stepNN-{name}.png
72
+ videos/ stepNN-{name}.webm
73
+ ```
74
+
75
+ ## Phase 2: 回遊・調査
76
+
77
+ 対象サイトをブラウザツールで実際に操作し、構造を把握する。
78
+
79
+ ```
80
+ browser_tab(action: "list") or browser_tab(action: "create", url: "...")
81
+ browser_tab(action: "connect", tabId: ...)
82
+ browser_get_page(mode: "markdown")
83
+ browser_screenshot()
84
+ browser_find_elements(tag: "button", limit: 50)
85
+ ```
86
+
87
+ **目的:**
88
+ - 各画面のDOM構造を把握する
89
+ - 操作対象のセレクタ(CSS selector / text / role)を特定する
90
+ - 画面遷移のフローを確認する
91
+ - 動的要素(ローディング、モーダル等)の有無を把握する
92
+
93
+ **セレクタ選定の優先順位:**
94
+ 1. `data-testid` / `data-*` 属性(最も安定)
95
+ 2. `id` 属性
96
+ 3. `role` + `name`(アクセシビリティ属性)
97
+ 4. CSS セレクタ(クラス名)
98
+ 5. テキストマッチ(最終手段、ロケール依存リスク)
99
+
100
+ ## Phase 3: spec.md 作成
101
+
102
+ 回遊結果を元に撮影仕様を作成する。
103
+
104
+ **確認タイミングの判断:**
105
+ - ステップ数 8以下: spec.md を一括でユーザー確認
106
+ - ステップ数 9以上: セクション単位で段階的に確認
107
+
108
+ ### spec.md フォーマット
109
+
110
+ ```markdown
111
+ # {トピック} — 撮影仕様
112
+
113
+ ## メタ情報
114
+ - サイト: {URL}
115
+ - 読者: {レベル}
116
+ - 出力: {形式}
117
+ - 作成日: {YYYY-MM-DD}
118
+
119
+ ## ステップ一覧
120
+
121
+ ### Step 1: {画面名}
122
+ - 撮影: screenshot -> images/step01-{name}.png
123
+ - 操作: なし(画面説明用)
124
+ - 説明: {このステップでマニュアルに書く内容の概要}
125
+
126
+ ### Step 2: {操作名}
127
+ - 撮影: record start
128
+ - 操作:
129
+ - browser_set_values(fields: [{selector: "#email", value: "demo@example.com"}])
130
+ - browser_click(selector: "button[type=submit]")
131
+ - browser_wait_for(selector: ".dashboard", condition: "appear")
132
+ - 撮影: record stop -> videos/step02-{name}.webm
133
+ - 撮影: screenshot -> images/step02-{name}.png
134
+ - 説明: {このステップでマニュアルに書く内容の概要}
135
+ ```
136
+
137
+ **spec.md の各ステップに必須の項目:**
138
+ - 撮影指示(screenshot / record start / record stop + ファイルパス)
139
+ - 操作内容(使うツールとパラメータ。セレクタは具体値)
140
+ - wait_for 条件(操作後に何を待つか)
141
+ - 説明(manual.md に書く内容の概要)
142
+
143
+ ## Phase 4: 撮影実行
144
+
145
+ spec.md に従い、順番に操作と撮影を実行する。
146
+
147
+ **撮影手順:**
148
+ 1. screenshot の場合: `browser_screenshot()` → 画像を確認 → `images/` に保存指示
149
+ 2. record の場合: `browser_record(action: "start", savePath: "/path/to/videos/stepNN-name.webm")` → 操作実行 → `browser_record(action: "stop")`
150
+ 3. 各操作後に `browser_wait_for` で画面遷移を確認してから次へ
151
+
152
+ **browser_record パラメータ:**
153
+
154
+ | パラメータ | 型 | デフォルト | 説明 |
155
+ |-----------|-----|-----------|------|
156
+ | action | "start" / "stop" / "status" / "abort" | — | 必須。start で録画開始、stop で保存、abort で破棄 |
157
+ | savePath | string | — | 出力ファイルのフルパス(例: ~/docs/videos/login.webm)。filename と排他 |
158
+ | filename | string | recording-{timestamp}.webm | ファイル名のみ(パス区切り不可)。savePath と排他。一時ディレクトリに保存 |
159
+ | fps | number (1-30) | 15 | フレームレート |
160
+ | quality | number (1-100) | 80 | JPEG品質(フレームキャプチャ用) |
161
+ | includeAudio | boolean | false | タブ音声を含める(CDP screencast では無視) |
162
+
163
+ **エラーハンドリング:**
164
+ - 最初の3ステップ以内で失敗: spec.md の見直しが必要。Phase 3 に戻りセレクタ・手順を修正
165
+ - 途中の1ステップが失敗: スキップして続行。失敗ステップを記録
166
+ - 最後にまとめて失敗一覧を報告
167
+
168
+ **撮影中の注意:**
169
+ - `browser_record` の前にタブがアクティブであることを確認(CDP screencast はコンポジタ活動が必要)
170
+ - 動画録画中は操作を淀みなく実行する(spec で事前に詰めた通りに)
171
+ - 静的ページではフレームが生成されないため、スクショのみにする
172
+ - `savePath` を使えば直接出力ディレクトリに保存できる。`filename` はテンポラリ保存用
173
+
174
+ ## Phase 5: manual.md 生成
175
+
176
+ spec.md と撮影済み素材から最終マニュアルを組み立てる。
177
+
178
+ ### manual.md フォーマット
179
+
180
+ ```markdown
181
+ # {トピック} マニュアル
182
+
183
+ ## Step 1: ログイン画面
184
+
185
+ ブラウザで {URL} にアクセスすると、ログイン画面が表示されます。
186
+
187
+ ![ログイン画面](./images/step01-login.png)
188
+
189
+ ## Step 2: ログイン
190
+
191
+ メールアドレスとパスワードを入力し、「ログイン」ボタンをクリックします。
192
+
193
+ 操作動画: [ログイン操作](./videos/step02-login.webm)
194
+
195
+ ![ログイン後のダッシュボード](./images/step02-dashboard.png)
196
+
197
+ ログインに成功するとダッシュボード画面が表示されます。
198
+ ```
199
+
200
+ **読者レベルによる文体:**
201
+ - 社内メンバー: 「○○画面で△△を入力して送信」程度の簡潔な記述
202
+ - エンドユーザー: スクショの吹き出し的な説明。「画面左上の青い『申請』ボタンをクリックしてください」レベルの丁寧さ
203
+
204
+ **失敗ステップの扱い:**
205
+ 撮影に失敗したステップは manual.md に「※ このステップは手動で操作してください」と注記する。
206
+
207
+ ---
208
+
209
+ # Workflow B: Record-First(操作記録 → 自動生成)
210
+
211
+ 操作をそのまま実行し、アクションログから自動でマニュアルとテストスクリプトを生成する。Work IR(中間表現)を経由してマルチ出力を得る。
212
+
213
+ ## パイプライン概要
214
+
215
+ ```
216
+ 操作実行(browser_click, browser_set_values, ...)
217
+ ↓ 自動でアクションログに記録
218
+ ↓ begin_step / end_step でステップをグルーピング
219
+
220
+ browser_tab(action: "generate_manual") → Markdown マニュアル
221
+ browser_tab(action: "generate_test") → bun:test E2E テストスクリプト
222
+ browser_tab(action: "export_bundle") → JSON バンドル(IR + manual + test + source map)
223
+ browser_tab(action: "save_bundle", savePath: "...") → バンドルをファイルに保存
224
+ ```
225
+
226
+ ## Step 1: アクションログの管理
227
+
228
+ 全ての `browser_*` ツール呼び出しは自動的にアクションログに記録される。明示的な設定は不要。
229
+
230
+ **ログの確認と管理:**
231
+
232
+ ```
233
+ browser_tab(action: "log") → テキスト形式でログ表示
234
+ browser_tab(action: "log", format: "json") → JSON形式でログ取得(machine-readable)
235
+ browser_tab(action: "clear_log") → ログをリセット(新しいマニュアル生成の前に)
236
+ ```
237
+
238
+ ログエントリの各項目: timestamp, tool, tabId, ok (成否), target (パラメータ), summary, artifacts (スクショ・動画パス)。
239
+
240
+ ## Step 2: ステップのグルーピング
241
+
242
+ `begin_step` / `end_step` で操作を意味のあるステップにグルーピングする。グルーピングすると、generate_manual の出力で複数の操作が1つの番号付きステップにまとまる。
243
+
244
+ ```
245
+ browser_tab(action: "begin_step", label: "ログイン")
246
+ browser_set_values(fields: [{selector: "#email", value: "user@example.com"}])
247
+ browser_click(selector: "button[type=submit]")
248
+ browser_screenshot()
249
+ browser_tab(action: "end_step")
250
+
251
+ browser_tab(action: "begin_step", label: "ダッシュボード確認")
252
+ browser_get_page(mode: "markdown")
253
+ browser_screenshot()
254
+ browser_tab(action: "end_step")
255
+ ```
256
+
257
+ **ルール:**
258
+ - `begin_step` は `label` 必須。ステップ名になる
259
+ - ネスト不可。`begin_step` → `end_step` を閉じてから次の `begin_step`
260
+ - `end_step` せずに `begin_step` するとエラー
261
+ - グルーピングなしでも generate_manual は動く(各ツール呼び出しが個別ステップになる)
262
+
263
+ ## Step 3: マニュアル自動生成
264
+
265
+ **generate_manual** — アクションログを番号付きMarkdownマニュアルに変換:
266
+
267
+ ```
268
+ browser_tab(action: "generate_manual")
269
+ ```
270
+
271
+ - 成功した操作のみ出力(失敗はスキップ)
272
+ - `begin_step` / `end_step` で囲んだ操作は1つのステップにマージ
273
+ - スクリーンショットは `![Step N](path)` として埋め込み
274
+ - 動画は `[Video](path)` としてリンク
275
+ - アーティファクトが見つからない場合は `<!-- WARNING: file not found -->` コメント付き
276
+ - コンパイル診断(warnings/errors数)が末尾に付与
277
+
278
+ ## Step 4: テストスクリプト自動生成
279
+
280
+ **generate_test** — アクションログを bun:test E2E テストスクリプトに変換:
281
+
282
+ ```
283
+ browser_tab(action: "generate_test")
284
+ ```
285
+
286
+ - Browser Pilot HTTP API (`/api/tool`) を叩く `callTool` ヘルパーを自動生成
287
+ - `browser_set_values` の値は機密としてリダクト(TODO プレースホルダー + fieldHints からの環境変数名)
288
+ - `browser_batch` は TODO コメントとして展開(サブアクション個別に)
289
+ - 失敗した操作はコメントアウト
290
+ - 安定ロケーターがないステップ(ephemeral ref/backendNodeId のみ)は TODO として出力
291
+ - 末尾に replayability サマリー: runnable / needs_todo / needs_manual_fix
292
+
293
+ ## Step 5: バンドルのエクスポート
294
+
295
+ **export_bundle** — Work IR 全体を JSON で取得:
296
+
297
+ ```
298
+ browser_tab(action: "export_bundle")
299
+ ```
300
+
301
+ バンドル内容:
302
+ - `version`: "1.0"
303
+ - `compiledAt`: ISO timestamp
304
+ - `steps`: WorkStep[] — コンパイル済みステップ(intent, locator, assertions, artifacts 等)
305
+ - `diagnostics`: CompileDiagnostic[] — エラー/警告/info
306
+ - `summary`: { total, runnable, needsTodo, needsManualFix, warnings, errors }
307
+ - `manual`: 生成されたMarkdown(または null)
308
+ - `test`: 生成されたテストスクリプト(または null)
309
+ - `sourceMap`: SourceMapEntry[] — ステップとmanual/testの行番号のマッピング
310
+
311
+ **save_bundle** — バンドルをファイルに保存:
312
+
313
+ ```
314
+ browser_tab(action: "save_bundle", savePath: "/path/to/bundle.json")
315
+ ```
316
+
317
+ ## Record-First の典型的なフロー
318
+
319
+ ```
320
+ # 1. ログをクリア(前回の記録が残っている場合)
321
+ browser_tab(action: "clear_log")
322
+
323
+ # 2. 対象ページに接続
324
+ browser_tab(action: "connect", tabId: ...)
325
+
326
+ # 3. ステップを記録しながら操作
327
+ browser_tab(action: "begin_step", label: "ログインページを開く")
328
+ browser_navigation(url: "https://example.com/login")
329
+ browser_screenshot()
330
+ browser_tab(action: "end_step")
331
+
332
+ browser_tab(action: "begin_step", label: "認証情報を入力してログイン")
333
+ browser_set_values(fields: [{selector: "#email", value: "..."}])
334
+ browser_click(selector: "button[type=submit]")
335
+ browser_wait_for(selector: ".dashboard", condition: "appear")
336
+ browser_screenshot()
337
+ browser_tab(action: "end_step")
338
+
339
+ # 4. ログを確認
340
+ browser_tab(action: "log")
341
+
342
+ # 5. マニュアルを生成
343
+ browser_tab(action: "generate_manual")
344
+
345
+ # 6. テストスクリプトを生成(オプション)
346
+ browser_tab(action: "generate_test")
347
+
348
+ # 7. バンドルを保存(オプション)
349
+ browser_tab(action: "save_bundle", savePath: "/path/to/output/bundle.json")
350
+ ```
351
+
352
+ ---
353
+
354
+ # Common Mistakes
355
+
356
+ | 間違い | 正しい対応 |
357
+ |--------|-----------|
358
+ | 回遊せずにいきなり撮影(Design-First) | Phase 2 で必ずセレクタを特定してから spec を書く |
359
+ | spec.md にセレクタを書かない | 具体的な CSS selector / text / role を必ず記載 |
360
+ | 録画中に要素を探し始める | spec で詰めた通りに淀みなく操作する |
361
+ | wait_for を省略 | SPA は画面遷移に時間がかかる。必ず待機条件を入れる |
362
+ | 静的ページを動画で撮る | 動きがないページは screenshot のみにする |
363
+ | 全ステップを動画にする | 動きのある操作だけ動画。画面説明はスクショで十分 |
364
+ | begin_step を閉じずに次の begin_step | 必ず end_step してからネストなしで次の begin_step |
365
+ | clear_log せずに新しいマニュアルを生成 | 前回の操作が混入する。clear_log でリセットしてから記録開始 |
366
+ | generate_manual の出力をそのまま最終成果物にする | 自動生成は骨格。文言・レイアウトはユーザーが調整する |
367
+ | savePath と filename を同時指定 | 排他パラメータ。どちらか一方のみ |
@@ -0,0 +1,296 @@
1
+ ---
2
+ name: bp-test-scripts
3
+ description: Use when writing executable test scripts that control a real Chrome browser via browser-pilot HTTP API. Triggers on "テストスクリプト", "E2Eスクリプト", "自動テスト", "test script", "automated test", "browser test code", "コード駆動テスト".
4
+ ---
5
+
6
+ # Browser Testing Scripts — コード駆動ブラウザテスト
7
+
8
+ browser-pilot MCP ServerのHTTP APIを使い、Bun/TypeScriptでテストスクリプトを書く。
9
+ リアルChromeが目の前で動く。MCP Server(Claude Code接続)と同時利用可能。
10
+
11
+ ## Two API Layers
12
+
13
+ | エンドポイント | 用途 | レスポンス |
14
+ |---------------|------|-----------|
15
+ | `POST /api/tool` | **推奨。** MCP toolと同じ処理を経由 | markdown変換済み、fuzzy検索済み |
16
+ | `POST /api/request` | Extension直通(raw) | 生HTML、生DOM情報 |
17
+
18
+ ### `/api/tool` (推奨)
19
+
20
+ MCP toolハンドラを直接呼ぶ。`browser_get_page` のmarkdown変換、`browser_find_elements` のfuzzy検索等、全ての内部処理が適用される。
21
+
22
+ ```
23
+ テストスクリプト (Bun)
24
+ ↓ HTTP POST /api/tool
25
+ MCP Tool Handler (markdown変換, fuzzy検索, etc.)
26
+ ↓ bridge.request()
27
+ Chrome Extension (WebSocket client)
28
+
29
+ Real Chrome Browser (visible)
30
+ ```
31
+
32
+ ### `/api/request` (raw)
33
+
34
+ Extension直通。MCP toolの変換処理をスキップ。生HTMLが必要な場合に使う。
35
+
36
+ ## Authentication
37
+
38
+ 全 `/api/*` エンドポイントは Bearer トークン認証が必須。トークンは `~/.browser-pilot-token` に保存されている。
39
+
40
+ ```typescript
41
+ import { readFile } from "node:fs/promises";
42
+ import { join } from "node:path";
43
+ import { homedir } from "node:os";
44
+
45
+ const API_TOKEN = (await readFile(join(homedir(), ".browser-pilot-token"), "utf-8")).trim();
46
+ ```
47
+
48
+ 全リクエストに `Authorization: Bearer ${API_TOKEN}` ヘッダーを付与すること。
49
+
50
+ ## Quick Start (推奨: /api/tool)
51
+
52
+ ```typescript
53
+ const TOOL_API = "http://127.0.0.1:18888/api/tool";
54
+
55
+ async function tool(name: string, args: Record<string, unknown> = {}) {
56
+ const res = await fetch(TOOL_API, {
57
+ method: "POST",
58
+ headers: {
59
+ "Content-Type": "application/json",
60
+ Authorization: `Bearer ${API_TOKEN}`,
61
+ },
62
+ body: JSON.stringify({ tool: name, args }),
63
+ });
64
+ const body = await res.json();
65
+ if (body.isError) throw new Error(body.content?.[0]?.text ?? "Unknown error");
66
+ return body;
67
+ }
68
+
69
+ // タブ一覧取得
70
+ const tabs = await tool("browser_tab", { action: "list" });
71
+ console.log(tabs.content[0].text);
72
+
73
+ // タブ作成(自動connect)
74
+ await tool("browser_tab", { action: "create", url: "https://example.com" });
75
+
76
+ // ページ取得 → markdownで返る(htmlToText不要)
77
+ const page = await tool("browser_get_page", { mode: "markdown" });
78
+ console.log(page.content[0].text);
79
+
80
+ // 要素検索 → fuzzy検索対応
81
+ const els = await tool("browser_find_elements", { text: "送信", tag: "button" });
82
+ console.log(els.content[0].text);
83
+ ```
84
+
85
+ ## Quick Start (Raw: /api/request)
86
+
87
+ ```typescript
88
+ const RAW_API = "http://127.0.0.1:18888/api/request";
89
+
90
+ async function bp(action: string, params: Record<string, unknown> = {}) {
91
+ const res = await fetch(RAW_API, {
92
+ method: "POST",
93
+ headers: {
94
+ "Content-Type": "application/json",
95
+ Authorization: `Bearer ${API_TOKEN}`,
96
+ },
97
+ body: JSON.stringify({ action, params }),
98
+ });
99
+ const body = await res.json();
100
+ if (body.status === "error") throw new Error(body.error ?? body.message);
101
+ return body;
102
+ }
103
+
104
+ // get_page は生HTMLを返す → テキスト抽出ヘルパーが必要
105
+ function htmlToText(html: string): string {
106
+ return html
107
+ .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "")
108
+ .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "")
109
+ .replace(/<[^>]+>/g, " ")
110
+ .replace(/\s+/g, " ")
111
+ .trim();
112
+ }
113
+
114
+ const tabs = await bp("list_tabs");
115
+ const tab = tabs.data[0];
116
+ const page = await bp("get_page", { tabId: tab.id });
117
+ console.log(htmlToText(page.data.html));
118
+ ```
119
+
120
+ ## Response Format Comparison
121
+
122
+ | 操作 | `/api/tool` response | `/api/request` response |
123
+ |------|---------------------|------------------------|
124
+ | get_page | `{ content: [{ type: "text", text: "# markdown..." }] }` | `{ data: { html: "<div>..." } }` |
125
+ | find_elements | `{ content: [{ type: "text", text: "Found 3..." }] }` | `{ data: { count: 3, elements: [...] } }` |
126
+ | click | `{ content: [{ type: "text", text: "Clicked..." }] }` | `{ data: { clicked: true } }` |
127
+ | screenshot | `{ content: [{ type: "image", data: "base64...", mimeType: "image/webp" }] }` | `{ screenshot: "base64..." }` |
128
+
129
+ ## Test Pattern with bun:test (推奨: /api/tool)
130
+
131
+ ```typescript
132
+ import { describe, it, expect, beforeAll } from "bun:test";
133
+ import { readFile } from "node:fs/promises";
134
+ import { join } from "node:path";
135
+ import { homedir } from "node:os";
136
+
137
+ const API = "http://127.0.0.1:18888/api/tool";
138
+ const API_TOKEN = (await readFile(join(homedir(), ".browser-pilot-token"), "utf-8")).trim();
139
+
140
+ async function tool(name: string, args: Record<string, unknown> = {}) {
141
+ const res = await fetch(API, {
142
+ method: "POST",
143
+ headers: {
144
+ "Content-Type": "application/json",
145
+ Authorization: `Bearer ${API_TOKEN}`,
146
+ },
147
+ body: JSON.stringify({ tool: name, args }),
148
+ });
149
+ const body = await res.json();
150
+ if (body.isError) throw new Error(body.content?.[0]?.text ?? "Unknown error");
151
+ return body;
152
+ }
153
+
154
+ function getText(result: { content: Array<{ type: string; text?: string }> }): string {
155
+ return result.content
156
+ .filter((c) => c.type === "text")
157
+ .map((c) => c.text)
158
+ .join("\n");
159
+ }
160
+
161
+ describe("My App", () => {
162
+ beforeAll(async () => {
163
+ await tool("browser_tab", { action: "create", url: "http://localhost:3000" });
164
+ await tool("browser_wait_for", { selector: "main", condition: "appear", timeout: 10000 });
165
+ });
166
+
167
+ it("shows page title", async () => {
168
+ const page = await tool("browser_get_page", { mode: "markdown" });
169
+ expect(getText(page)).toContain("My App");
170
+ });
171
+
172
+ it("has submit button", async () => {
173
+ const els = await tool("browser_find_elements", { tag: "button", text: "Submit" });
174
+ expect(getText(els)).not.toContain("No elements found");
175
+ });
176
+
177
+ it("click navigates", async () => {
178
+ await tool("browser_click", { text: "Next" });
179
+ await tool("browser_wait_for", { selector: ".step-2", condition: "appear" });
180
+ const page = await tool("browser_get_page", { mode: "markdown" });
181
+ expect(getText(page)).toContain("Step 2");
182
+ });
183
+ });
184
+ ```
185
+
186
+ Run: `mise x -- bun test tests/e2e/my-test.test.ts`
187
+
188
+ ## Tool Name Mapping (/api/tool vs /api/request)
189
+
190
+ ### Tab Management (unified `browser_tab` tool)
191
+
192
+ `browser_tab` は action パラメータで操作を切り替える統合ツール。`/api/request` では個別アクション名を使う。
193
+
194
+ | `/api/tool` の呼び方 | `/api/request` の action名 |
195
+ |---------------------|---------------------------|
196
+ | `browser_tab` (action: "list") | `list_tabs` |
197
+ | `browser_tab` (action: "connect", tabId) | N/A (MCP tool専用) |
198
+ | `browser_tab` (action: "create", url) | `create_tab` |
199
+ | `browser_tab` (action: "close", tabId) | `close_tab` |
200
+ | `browser_tab` (action: "lock") | N/A (MCP tool専用) |
201
+ | `browser_tab` (action: "unlock") | N/A (MCP tool専用) |
202
+ | `browser_tab` (action: "log") | N/A (MCP tool専用) |
203
+ | `browser_tab` (action: "clear_log") | N/A (MCP tool専用) |
204
+ | `browser_tab` (action: "begin_step", label) | N/A (MCP tool専用) |
205
+ | `browser_tab` (action: "end_step") | N/A (MCP tool専用) |
206
+ | `browser_tab` (action: "generate_manual") | N/A (MCP tool専用) |
207
+ | `browser_tab` (action: "generate_test") | N/A (MCP tool専用) |
208
+ | `browser_tab` (action: "export_bundle") | N/A (MCP tool専用) |
209
+ | `browser_tab` (action: "save_bundle", savePath) | N/A (MCP tool専用) |
210
+
211
+ ### Page & Navigation
212
+
213
+ | `/api/tool` の tool名 | `/api/request` の action名 |
214
+ |----------------------|---------------------------|
215
+ | `browser_get_page` | `get_page` |
216
+ | `browser_navigation` (action: "goto", url) | `get_page` (with url param) |
217
+ | `browser_navigation` (action: "back") | `go_back` |
218
+ | `browser_navigation` (action: "forward") | `go_forward` |
219
+ | `browser_navigation` (action: "reload") | `reload` |
220
+ | `browser_scroll` | `scroll` |
221
+ | `browser_wait_for` | `wait_for` |
222
+
223
+ ### Element Interaction
224
+
225
+ | `/api/tool` の tool名 | `/api/request` の action名 |
226
+ |----------------------|---------------------------|
227
+ | `browser_click` | `click_element` |
228
+ | `browser_set_values` | `set_value` |
229
+ | `browser_select_option` | `click_element` (sequence) |
230
+ | `browser_action` | `action_element` |
231
+ | `browser_keyboard` | `press_key` |
232
+ | `browser_find_elements` | `find_elements` |
233
+ | `browser_get_attributes` | `get_attributes` |
234
+ | `browser_get_bounds` | `get_bounds` |
235
+ | `browser_diagnose_interaction` | `diagnose_interaction` |
236
+ | `browser_paste_file` | `paste_file` |
237
+ | `browser_upload_file` | `upload_file` |
238
+ | `browser_handle_dialog` | N/A (background直通) |
239
+
240
+ ### Data Extraction & Inspection
241
+
242
+ | `/api/tool` の tool名 | `/api/request` の action名 |
243
+ |----------------------|---------------------------|
244
+ | `browser_extract` | `extract_data` |
245
+ | `browser_css_inspect` | `css_inspect` |
246
+ | `browser_execute_javascript` | `evaluate` |
247
+ | `browser_assert` | N/A (MCP tool専用) |
248
+
249
+ ### Capture & Media
250
+
251
+ | `/api/tool` の tool名 | `/api/request` の action名 |
252
+ |----------------------|---------------------------|
253
+ | `browser_screenshot` | `screenshot` |
254
+ | `browser_pdf` | `print_to_pdf` |
255
+ | `browser_annotate` | `annotate` / `clear_annotations` |
256
+ | `browser_record` | `start_recording` / `stop_recording` / `recording_status` |
257
+
258
+ ### Diagnostics & DevTools
259
+
260
+ | `/api/tool` の tool名 | `/api/request` の action名 |
261
+ |----------------------|---------------------------|
262
+ | `browser_console` | `get_console_messages` |
263
+ | `browser_network` | `get_network_requests` |
264
+ | `browser_performance` | `get_performance_metrics` |
265
+ | `browser_storage` | `get_storage` |
266
+ | `browser_cookie` | `set_cookie` / `delete_cookie` / (get via `get_storage` type:cookies) |
267
+ | `browser_health` | N/A (MCP tool専用) |
268
+
269
+ ### Batch Execution
270
+
271
+ | `/api/tool` の tool名 | `/api/request` の action名 |
272
+ |----------------------|---------------------------|
273
+ | `browser_batch` | N/A (MCP tool専用 — 複数ツール呼び出しを1リクエストで実行) |
274
+
275
+ ## Prerequisites
276
+
277
+ - MCP Server が起動中(Claude Code接続 or `mise x -- bun run apps/mcp/src/index.ts`)
278
+ - Chrome Extension がインストール済み & 接続済み
279
+ - テスト対象のWebアプリが起動済み
280
+
281
+ ## Common Mistakes
282
+
283
+ | 間違い | 正しい対応 |
284
+ |--------|-----------|
285
+ | `browser_create_tab` を使う | `browser_tab` (action: "create", url: "...") を使う |
286
+ | `browser_list_tabs` を使う | `browser_tab` (action: "list") を使う |
287
+ | `browser_connect_tab` を使う | `browser_tab` (action: "connect", tabId: N) を使う |
288
+ | `browser_set_value` (単数形) を使う | `browser_set_values` (複数形) を使う |
289
+ | `/api/tool` で action名を使う | MCP tool名を使う(`browser_get_page` 等) |
290
+ | `/api/request` でtool名を使う | content.tsのメソッド名を使う(`get_page` 等) |
291
+ | `/api/request` の `page.data` を文字列扱い | `page.data.html` が生HTML。`/api/tool` なら変換済み |
292
+ | `/api/request` で tabId を渡さない | `create_tab` の戻り値から取得して毎回渡す |
293
+ | `/api/tool` で tabId を渡す | 不要。TabSessionが自動注入(`browser_tab` action:"connect" で設定済みなら) |
294
+ | MCP Server 未起動で実行 | Claude Code接続中 or 手動起動が必要 |
295
+ | wait なしで次の操作 | `browser_wait_for` で遷移完了を待つ |
296
+ | `Authorization` ヘッダー未付与で `401 Unauthorized` | `~/.browser-pilot-token` を読んで `Bearer` トークンを全リクエストに付与 |