本文へ移動
cccskills
無料GitHub で公開

wsl-windows-chrome-screenshot

WSL のワーカーから Windows 版 Chrome を headless で叩き、WSL 側に立てたプレビュー サーバの画面を PNG で撮るレシピ。

サーバ起動と撮影を 1 つの Bash 呼び出しにまとめ、 その呼び出しだけ sandbox を外す・1 枚目は捨て撮り・出力先は UNC でワークツリー、 という固定手順と、撮れていないときの症状の弁別(`ss -ltnp` / WSL 側 curl)を含む。

「WSL から Windows Chrome で画面を撮る」「rondo の画面のスクリーンショットを撮る」 「before/after を撮り比べる」「ライト / ダークを撮り分ける」等で発動。撮影を伴わない UI 実装・テストには発動しない。

インストール方法を見る

含まれるファイル(1)

  • SKILL.md12.1 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

WSL から Windows Chrome で画面を撮る

Linux 側の Chrome は共有ライブラリ不足で起動しない環境なので、Windows 版 Chrome を headless で使う(rondo#318 の申し送り、knowledge/raw/archive/2026-09-20-wsl-windows-chrome-file-url-screenshots.md)。 以下は rondo#314 / #318 / #350 / #351 / #352 で繰り返し踏んだ手順を固定したもの。 どの失敗もエラーが原因を指さず、PNG が「撮れてしまう」ことが多い。

0. sandbox を外す撮影の実行は窓口が行う

§1 の dangerouslyDisableSandbox: true の呼び出しは、auto mode のワーカーでは分類器に [Safety Bypass Flag] で拒否される(2026-09-22、2 回。sandbox 内で Chrome を 1 回起動する probe も拒否)。同じスクリプトは窓口が流して撮れている。よって役割を分ける。

  • ワーカー: サーバ起動・撮影・停止を 1 本にまとめた撮影スクリプトを .worker-scratch/ に用意し、sandbox 内でプレビューが起動する(curl が 200)ことまで確かめ、 そのまま流せる 1 行のコマンドを窓口への報告に書く。sandbox 解除は自分で要求しない。 1 回でも拒否されたら再要求しない(繰り返すとロックアウトされる)。
  • 窓口: スクリプトを読んでから sandbox 外で 1 回だけ流し、撮れた PNG が Chrome の 接続拒否画面(§2)でないことを確かめて、merge gate のページに載せる。

1. サーバ起動と撮影は 1 つの Bash 呼び出しに入れ、その呼び出しだけ sandbox を外す

sandbox は Bash 呼び出しごとに PID / network 名前空間を分けるため、

  • 別呼び出しで起こしたサーバはその呼び出しの終了と同時に死ぬ(setsid しても畳まれる。 run_in_background: true なら生きるが、次の呼び出しの curl からも Windows Chrome からも届かない)
  • Chrome 側も sandbox 内から叩くと <3>WSL (N - ) ERROR: UtilConnectUnix:526: socket failed 1 だけを出して PNG を 1 バイトも書かない(WSL interop の unix socket が bwrap に塞がれる。 エラー終了はしない)

よってサーバ起動・ヘルスチェック・撮影・停止をまとめた 1 呼び出しを dangerouslyDisableSandbox: true で流す。build / verify / git は sandbox 内のままでよい。

CHROME="/mnt/c/Program Files/Google/Chrome/Application/chrome.exe"
UNC='\\wsl.localhost\Ubuntu-24.04\home\happy_ryo\work\org\workers\<proj>\.worktrees\<task>\.worker-scratch\shots'
PORT=7361

shoot() {  # shoot <name> <scheme 0=dark|1=light> <url>
  "$CHROME" --headless=new --disable-gpu --hide-scrollbars \
    --blink-settings=preferredColorScheme=$2 --window-size=2560,1440 \
    --virtual-time-budget=4000 --screenshot="$UNC\\$1.png" "$3"
}

mkdir -p .worker-scratch/shots
node .worker-scratch/preview-$PORT.mjs > .worker-scratch/preview.log 2>&1 &
SRV=$!
for i in $(seq 1 20); do curl -fsS -o /dev/null "http://127.0.0.1:$PORT/?lang=en" && break; sleep 1; done
shoot warmup   1 "http://127.0.0.1:$PORT/?lang=en"   # 捨て撮り(§2)
shoot after-en 1 "http://127.0.0.1:$PORT/?lang=en"
shoot after-ja 1 "http://127.0.0.1:$PORT/?lang=ja"
kill $SRV
tail -5 .worker-scratch/preview.log

& で起こしたプロセスはその呼び出しの終了で死ぬので後始末も自然に済み、 pgrep -f / pkill -f を使わずに済む(Bash ツールは zsh -c '<コマンド全文>' で 起動するため、同じコマンド内に殺したいパターン文字列があると pgrep -f が その zsh 自身を拾って自殺する。Exit code 144 だけが返る。[3] のブラケット trick も 同一コマンド内に生の文字列がある限り効かない)。

  • while read ループの中で撮るときは Chrome の stdin を </dev/null にする。 塞がないと chrome.exe がループの入力を読み尽くし、1 画面目しか撮れない。
  • プレビューを sh -c や npm 経由で起こすなら exec node ... で起動し、$! を node の pid にする(間に親シェルが挟まると kill $SRV が node に届かず残る)。残ったサーバがあると、 次の実行が古いビルドを撮るか EADDRINUSE になる。起動前にポートの使用状況を確かめ、 止めてよいのはこのタスクで自分が起動し pid を控えたプレビューだけ。自分のものでない プロセスが使っていたら止めずに、§6 のとおり別のポートを選ぶ。

2. サーバ起動直後の 1 枚目は必ず ERR_CONNECTION_REFUSED になる

同一呼び出し内で curl が 200 を返した後でも、Chrome の 1 枚目だけ 「このサイトにアクセスできません / ERR_CONNECTION_REFUSED」の PNG になる (2 枚目以降は同じ URL で正常。独立した 2 回の実行で再現)。WSL2 の localhost forwarding が Windows 側からの初回接続で張られるまでのラグ。curl のヘルスチェックを強化しても検出できない ので、撮影ループの先頭に捨て撮りを 1 枚置く。判別はバイト数でよい (2560x1440 のエラー画面はこの環境で 33,866 bytes 固定)。

3. 出力先は UNC でワークツリーへ書く

/mnt/c/Users/<user>/AppData/Local/Temp/... への mkdir は sandbox の write allowlist 外で Read-only file system になる。Chrome には UNC でワークツリーを渡すのが通る(上の $UNC)。 既存の Temp 直下へ直接置くならディレクトリを作らず r<issue>-<name>.png のように タスク識別子を名前に入れる(並走ワーカーと衝突しない)。

成功時は NNNNN bytes written to file ... を stdout に吐く。 この行が出ないときは撮れていない(ファイルの有無より先にこの行を見る)。

4. ライト / ダークは preferredColorScheme、値は 0=dark / 1=light

headless Chrome は既定で Windows 側の OS テーマ(この環境では dark)に従う。 --blink-settings=preferredColorScheme=1 でライト固定。2 を渡すとエラーにならず ライトのまま撮れるので、「ダークのつもりでライトを撮った」が無言で起きる。 ライトとダークのバイト数はほぼ必ず違うので、md5sum が一致したら固定できていない。

5. 症状の弁別

見えるもの切り分け実体
Chrome が ERR_CONNECTION_REFUSED の PNG、WSL 側 curl は通る2 枚目が正常に撮れるか§2 の初回ラグ。捨て撮りで解決
Chrome も WSL 側 curl(別呼び出し)も届かない別の Bash 呼び出しで ss -ltnp | grep <port> が空サーバが呼び出し終了で死んでいる。§1 の 1 呼び出し化
PNG が生成されない / UtilConnectUnix: socket failed—撮影側が sandbox 内。§1 で sandbox を外す
curl は 200 だがログ末尾が EADDRINUSEログ末尾を読む別ワーカーの残骸が同じポートで応答している。§6 のとおりポートをずらす
真っ白 / 無スタイルの PNG対象が file:// かWindows Chrome は WSL パスを file:// で開けない。HTML と CSS を Windows 側 Temp へコピーしてから撮る

ポート判定は「curl が通るか」ではなく別の Bash 呼び出しで ss -ltnp に残っているかで行う (curl は同一呼び出し内なら死ぬ前に通ってしまう)。

6. before/after は git archive した別ツリーを別ポートで立てる

ワーカーは git stash 変更系も git checkout -- <path> も使えない。ワークツリーを触らずに 「前」を出す:

W=<worktree>
mkdir -p "$W/.worker-scratch/base"
git -C "$W" archive origin/main | tar -x -C "$W/.worker-scratch/base"
ln -sfn "$W/node_modules" "$W/.worker-scratch/base/node_modules"        # install は 1 回で済む
sed -i 's/7334/7434/g' "$W/.worker-scratch/base/scripts/page-preview.mjs"  # ポートを退避
npm --prefix "$W/.worker-scratch/base" run build

scripts/page-preview.mjs の new URL("../dist/...", import.meta.url) はスクリプトの位置から 解決されるので、コピー先の dist/ を見る。よって「前」と「後」が同時に立ち、同じ Chrome ループで撮り分けられる。cd は使わず npm --prefix で回す。

「後」側もコピーしてポートを変えること。tracked ファイルを sed -i で書き換えると git diff に混ざる。やむを得ず直接書き換えるなら、書き換える前に cp <path> .worker-scratch/<name>.bak を取り、戻すときは command cp -f (git show HEAD:<path> > <path> は commit 前のタスク変更ごと巻き戻す。 ワーカーは git checkout -- <path> / git restore --source= を hook で deny されている)。 戻したあと git diff -- <path> が撮影前の状態と一致することを確認する。

ポートは scripts/page-preview.mjs が 7334 決め打ち。削除済み worktree のプレビューが 生き残って掴んでいることがあるので、他ワーカーのプロセスは撃たず、複製してポートを ずらす(ls -l /proc/<pid>/cwd が (deleted) なら残骸)。

7. 撮った PNG は必ず 1 回見る

  • --window-size の高さが足りないと下が切れた PNG がそのまま出る(エラーにならない)。
  • 判定対象が画面下端の要素(sticky bottom-* 等)のときは、高さ違いを 2 枚撮る (例 2560,1440 と 2560,1700)。低い方では下端に貼り付いて必ず見え、高い方では 静的位置に落ちるので、2 枚あると「貼り付いているのか、たまたま収まっているのか」が区別できる。
  • 案と現物を比較する用途では、同じ --window-size(= 同じ fold)で撮る。
  • 言語は cookie に残る。英語で並べたいなら ?lang=en を明示する。
  • SPA のハッシュ遷移(#narrow/...)は headless の 1 発撮りでは効かず、トップが撮れる (バイト数がトップと完全一致するのが手がかり)。撮れないものは「撮れなかった」と報告に書く。

出典

  • knowledge/raw/archive/2026-09-21-wsl-chrome-screenshot-and-pgrep.md(rondo#314)
  • knowledge/raw/archive/2026-09-21-windows-chrome-first-shot-refused.md(rondo#318)
  • knowledge/raw/archive/2026-09-21-before-after-shots-need-two-builds-and-no-sandbox.md(rondo#350)
  • knowledge/raw/archive/2026-09-21-screenshot-server-and-chrome-in-one-bash-call.md(rondo#351)
  • knowledge/raw/archive/2026-09-21-wsl-chrome-shots-need-sandbox-off-on-both-legs.md(rondo#352)
  • knowledge/raw/archive/2026-09-20-wsl-windows-chrome-file-url-screenshots.md(rondo#319、file:// とウィンドウ高さ)
  • knowledge/raw/archive/2026-09-22-screenshot-skill-blocked-by-auto-mode-classifier.md(rondo#411-#413、§0)

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

dispatcher-handover

無料日本語概要

ディスパッチャーのコンテキストを圧迫したまま session を続けるのを避けるため、 monitoring 状態(active workers / 直近 polling cursor / pending escalations)を handover ファイルに書き出し、secretary の指示で /clear → /dispatcher-resume の 流れで新しいディスパッチャー session を開始する準備をする。 Secretary から DISPATCHER_HANDOVER peer message を受領したとき、または ディスパッチャー自身が context が長くなったと判断したときに使う。

suisya-systems/claude-org-ja52026年10月12日 更新

dispatcher-resume

無料日本語概要

/dispatcher-handover で書き出した handover ファイルを読み込み、 ディスパッチャーを新しい session で復帰させる。/clear 直後の最初のターンで使う。 state.db の dispatcher_pane_id / dispatcher_peer_id を atomic に更新し、 worker monitoring の /loop 3m を再開する。 「ディスパッチャー復帰」「resume」「引き継ぎから再開」と secretary から指示された ときに使う。/org-start ではない(ワーカー・窓口・キュレーターは生きている前提)。

suisya-systems/claude-org-ja52026年10月12日 更新

goal-setup

無料日本語概要

プロジェクトのゴール台帳(registry/goals/<owner>/<repo>.md)を人間と対話して新規作成・改訂する。 README と open Issue からゴール候補を推定して提示し、「何を目指すか」「何が起きていたら未達か」を 短く質問して条項を固め、書式検証(tools/work_discovery_goals.py validate)を通してから保存する。 「ゴール台帳を書きたい」「ゴールを設定して」「ゴール未設定と出た」「台帳エラーを直して」 「このプロジェクトの目標を決めたい」等や、/work-discovery の「ゴール未設定」「ゴール台帳エラー」案内を 受けて窓口が起動する。候補の提示そのものは /work-discovery の役目で本スキルではない。

suisya-systems/claude-org-ja52026年10月12日 更新

org-attach

無料日本語概要

組織の生きているペイン(secretary / dispatcher / worker)へ tmux で**直接入る** ための **コマンドを出力するだけ** の read-only スキル。`mcp__org-broker__list_panes`(論理ペイン) と `/usr/bin/tmux -L claude-org-broker list-panes -a`(pane_id ↔ session マッピング)を pane_id(%N)で突き合わせ、各ペインに role + name(task_id) ラベル付きの attach コマンドを 生成する(読取専用 `-r` / 書込は `-r` 無し / デタッチは `Ctrl-b d`)。 「ワーカーのペインを見たい」「ワーカーのペインに入りたい」「dispatcher の様子を直接見たい」 「ペインに入りたい」「あのペインを直接覗きたい」「attach コマンド教えて」 「tmux でつなぎたい」「worker の画面に入る」等で発動。 自分で attach はせず・ペインも一切変更しない(コマンド文字列とペイン一覧表を表示するのみ)。 作業委譲(org-delegate)・ダッシュボードで状況一覧(org-dashboard)・watcher 停止(org-attention-stop) には発動しない。

suisya-systems/claude-org-ja52026年10月12日 更新

org-attention-start

無料日本語概要

attention notification watcher(承認待ち / 判断待ち / CI 失敗等を OS 通知 + 音で能動通知)を dispatcher ペインの右側に split で常駐起動する。`.state/attention.json` が未配置なら ja 既定テンプレートを `tools/templates/attention.example.json` から自動コピーする。 起動後のペイン id は `.state/attention_pane.json` に記録し、`/org-attention-stop` から参照する。 「attention 起動」「通知監視を始めて」「watcher を立てて」等で発動。 `/org-start` からの auto-start はしない(明示起動推奨ポリシー)。

suisya-systems/claude-org-ja52026年10月12日 更新

org-attention-stop

無料日本語概要

`/org-attention-start` で起動した attention watcher ペインを停止する。 `.state/attention_pane.json` に記録された pane_id を `list_panes` の name/role で identity 確認し、いまも attention watcher を指している場合だけ `close_pane`(pane 破棄)で 破棄する(pane_id が別ペインへ再割当て済みなら close せず stale sidecar として削除)。 「attention 止めて」「通知監視を停止」「watcher 落として」等で発動。

suisya-systems/claude-org-ja52026年10月12日 更新

suisya-systems のスキルをすべて見る

このスキルの問題を報告する