mcpField
L
launch

launch

github.com/microsoft開発194.4k298 回インストール今日更新
リポジトリ

launch について

VS Code 開発者向けの起動スキルで、ビルド済み Code OSS を UI 自動化やデバッグに使用できます。ユーザープロファイルを隔離コピーし、空きデバッグポートを割り当て、起動情報を JSON で出力します。

主な機能

  1. 01ビルド済み Code OSS の起動
  2. 02認証済みプロファイルの一時コピー
  3. 03デバッグ用ポートの割り当て
  4. 04起動情報の JSON 出力
  5. 05Playwright による UI 自動化と dap-cli デバッグ
  6. 06bash と PowerShell の起動スクリプト

活用シーン

  1. 01Code OSS のチャットやエージェントフローのテスト
  2. 02CDP 経由の Playwright UI 自動化
  3. 03レンダラーや拡張ホストのデバッグ
  4. 04複数インスタンスのポート競合回避

インストール

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

$npx skills add https://github.com/microsoft/vscode --skill launch

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

同梱ファイル

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

playwrightScripts/
focus-chat-input.ts
scripts/
launch.ps1
launch.sh
monaco-paste.sh
monaco-paste.ts
updateSettings.ts
waitForCdp.ts
scripts/bootstrap/
bootstrap-profile.ps1
bootstrap-profile.sh
windows-process-tree-monitor.ps1
scripts/cleanup/
cleanup.ps1
windows-process-arguments.ps1

詳細

Code OSS Dev - 起動とデバッグ

VS Code 自体を開発していて、次のことをしたい場合:

  1. Copilot、GitHub などにすでにサインインしているソースからの Code OSS ビルドを起動し、チャットやエージェントのフローを最初から最後まで動作させる。
  2. CDP 経由で @playwright/cli を使って操作する(UI 自動化)。
  3. 必要に応じて dap-cli でデバッガーを接続し、レンダラー、拡張機能ホスト、またはメインプロセスにブレークポイントを設定する。
  4. ポートの競合なしで複数のインスタンスを同時に実行する。

このスキルは、認証済みの user-data-dir を使い捨ての一時フォルダーに複製し、すべてのデバッグ対象に空きポートを割り当て、それらをプログラムから取得できるよう JSON 形式で出力するランチャーを提供します。

複製されるデータは最小限です。既定では、ワークスペースのストレージ、ブラウザーのキャッシュ、ファイル履歴、キャッシュされた VSIX バックアップ、古いログは除外されます。macOS では、認証トークンは OS のキーチェーンと User/globalStorage 内の小さなファイルに保存されており、どちらも保持されます。Windows では GitHub のセッションは代わりにshared-data-dirに保存されるため、ランチャーはそちらも別途コピーします(Windows での認証を参照)。

前提条件

  • macOS、Linux、または Windows。
    • macOS / Linux:ランチャーは bash スクリプト(scripts/launch.sh)で、PATH 上の rsync、nohup、Node に依存します。
    • Windows:代わりに scripts\launch.ps1 を使用してください。PATH 上の Node 以外に追加のツールは不要で、Windows PowerShell 5.1 と PowerShell 7 以降の両方で動作します。ランチャーの出力は ConvertFrom-Json で解析してください。Node を fnm で管理している場合は、先に PATH に追加してください。
      fnm env --use-on-cd --shell powershell | Out-String | Invoke-Expression
      fnm use   # リポジトリの .nvmrc を読み込みます
      
  • node_modules/ がインストールされた VS Code のチェックアウト(存在しない場合は npm install を実行してください。隣接するワークツリーからシンボリックリンクを作成しないでください。ビルドで微妙な問題が発生します)。
  • ソースがビルドされた VS Code のチェックアウト。npm run compile を一度実行するか、インクリメンタルビルドには npm run watch を実行してください。どちらもクライアント全体と、extensions/ 配下のすべての組み込み拡張機能をビルドします。正常に実行するには製品全体をビルドする必要があり、クライアントだけをビルドしても不十分です。
    • ランチャーがコンパイルするのは出力が存在しない場合のみです。古くなった依存関係や out/ ファイルは検出しません。プル、ブランチの切り替え、依存関係マニフェストの変更後は、必要に応じて npm install を実行し、起動前に再ビルドしてください。
  • 認証済みの Code OSS プロファイルを推奨しますが、必須ではありません。デフォルトでは、ランチャーは macOS/Linux では ~/.vscode-oss-dev、Windows では $env:USERPROFILE\.vscode-oss-dev からシードを試みます。これはリポジトリの launch.json 設定が使用するユーザーデータディレクトリです。別のプロファイル(たとえば、通常使用している ~/Library/Application Support/Code のインストール)からシードしたい場合に限り、--source-user-data-dir <path> を渡すか、$CODE_OSS_DEV_AUTHED_USER_DATA_DIR を設定してください。ソースプロファイルがない場合、ランチャーは空の分離プロファイルで続行します。
  • @playwright/cli が利用可能であること(vscode リポジトリの devDependency です。npm install の後、npx @playwright/cli を使用してください)。
  • デバッガーを使う場合:PATH 上に dap-cli が必要です。デバッガーのサポートが役立ちそうなのに dap-cli スキルがない場合は、https://github.com/roblourens/dap-cli からインストールするようユーザーに案内してください。
  • CSS セレクターは内部実装の詳細です。セレクターベースの eval が動作しなくなった場合は、新しい snapshot を取得して現在の DOM を調べ、古いセレクターが引き続き使えると決めつけずに更新してください。
  • Code OSS は、サンドボックス化されたエージェントターミナルから起動したり操作したりできません。Windows では、サンドボックスがローカルネットワークアクセスなしでは起動できないか、ランチャーが [launch.ps1] Access is denied で失敗します。ローカルネットワークアクセスを有効にするだけでは不十分です。シェルツールからサンドボックス外での実行を要求できる場合は、ランチャー、@playwright/cli、クリーンアップにそれを使用してください。それができない場合は、起動前にユーザーへ一度、このセッションのターミナルのサンドボックス化を無効にするよう依頼し(Permissions picker → "Sandboxing for terminal")、確認後に続行してください。

ランチャーはソースプロファイルを一時ディレクトリにコピーし、元のプロファイルを変更することはありません。起動ごとに、独立した --user-data-dir と --extensions-dir が割り当てられます。

ランチャーは起動したプロファイルの User/settings.json で、常に files.simpleDialog.enable: true を設定します。これは自動化に必要です。VS Code のネイティブ OS ファイルダイアログは、CDP 経由で @playwright/cli を使って操作できず、ヘッドレス macOS では SSH 経由で完全にアクセスできないためです。シンプルな(クイック入力の)ダイアログは、press とクリップボードからの貼り付けで操作できます。この上書きは起動ごとに適用され、使い捨てプロファイルにのみ影響します。

エージェントセッションから起動する前に get_current_session を呼び出し、その title を --session-title として渡してください。通常のエディターウィンドウでは、ランチャーはそのタイトルを使い捨てプロファイルの window.title 設定に書き込みます。Agents ウィンドウでは、そのタイトルを Command Center に渡します。元のプロファイルが変更されることはありません。 無人の自動化では、信頼ダイアログによって処理フローや拡張機能ホストの起動がブロックされないよう、--disable-workspace-trust を指定してください。この上書きはプロセス単位で適用され、ソースプロファイルは変更しません。信頼できるコンテンツに対してのみ使用してください。

起動

ランチャースクリプトは、この SKILL.md と同じ場所にある scripts/launch.sh(macOS/Linux)または scripts\launch.ps1(Windows)です。このスキルファイルがインストールされている場所を基準にパスを解決してください。絶対パスをハードコードしないでください。

# LAUNCH=<dir-of-this-SKILL.md>/scripts/launch.sh
SESSION_TITLE=<title-from-get_current_session>
"$LAUNCH" --session-title "$SESSION_TITLE"   # default: workbench
"$LAUNCH" --agents --session-title "$SESSION_TITLE"
"$LAUNCH" -- <workspace-path>                # forward extra args to code.sh
"$LAUNCH" --source-user-data-dir <path>      # pick a specific authed profile
"$LAUNCH" --repo <vscode-repo-root>          # if not run from the repo
"$LAUNCH" --clone-extensions                 # start with a copy of the source extensions/ (~few seconds)
"$LAUNCH" --full                             # skip slim excludes; copy everything
"$LAUNCH" --skip-prelaunch                   # reuse already-current build outputs
"$LAUNCH" --disable-workspace-trust          # avoid trust prompts for trusted automation inputs

Windows では、同じフラグを指定して PowerShell ランチャーを呼び出してください。

$skillDir = '<dir-of-this-SKILL.md>'
$launch = Join-Path $skillDir 'scripts\launch.ps1'
$sessionTitle = '<title-from-get_current_session>'
& $launch --session-title $sessionTitle         # default: workbench
& $launch --agents --session-title $sessionTitle
& $launch -- --use-mock-keychain               # forward extra args to code.bat
& $launch --source-user-data-dir C:\path\to\profile
& $launch --repo C:\path\to\vscode
& $launch --clone-extensions
& $launch --full
& $launch --skip-prelaunch
& $launch --disable-workspace-trust

ローカルの実行ポリシーによってスクリプトの実行がブロックされる場合は、powershell -ExecutionPolicy Bypass -File <path-to-launch.ps1> で呼び出してください。Windows 版も、プロファイルの分離、スリムコピーの除外項目、設定のマージ、ポートの割り当て、フォアグラウンドでの事前起動、および CDP 準備完了の契約は bash ランチャーと同じです。異なるのはシェルコマンドとパスの構文だけです。

サインインが必要な場合

ソースプロファイルがない場合や、保存済みの GitHub セッションがない場合でも、停止したり、起動前に確認を求めたりしないでください。分離された Code OSS ウィンドウに通常の GitHub/Copilot サインイン UI が表示されるまで続行してください。Code OSS でサインインが必要になっても、あきらめないでください。

その時点で、質問ツールを使って次の選択肢を提示してください。

  • サインインしました
  • 今後の起動に備えてプロファイルを初期設定する

ユーザーがサインインした場合は、現在の分離された起動を続行してください。初期設定を選んだ場合は、現在の分離された起動をクリーンアップし、プラットフォームの初期設定スクリプトを実行してください。

BOOTSTRAP=<dir-of-this-SKILL.md>/scripts/bootstrap/bootstrap-profile.sh
"$BOOTSTRAP" --repo <vscode-repo-root>
$bootstrap = Join-Path $skillDir 'scripts\bootstrap\bootstrap-profile.ps1'
& $bootstrap -Repo <vscode-repo-root>

失敗した起動でカスタムの --source-user-data-dir を使用した場合は、macOS/Linux では同じパスを --user-data-dir <path> として、Windows では -UserDataDir <path> として渡してください。

初期設定スクリプトは、永続的な Code OSS ウィンドウの CDP エンドポイントが準備完了になってから戻ります。ログのパスを報告し、ウィンドウが使用可能にならない場合はログ末尾を表示して失敗します。そのウィンドウで GitHub/Copilot にサインインしてからウィンドウを閉じるようユーザーに依頼し、確認の選択肢には サインインしました を使用してください。macOS では、ユーザーは Cmd+Q で Code OSS を完全に終了する必要があります。最後のウィンドウを閉じても、アプリケーションは終了しません。

ユーザーが確認した後、再度起動する前に、初期設定プロセスツリーが永続プロファイルを解放するまで待機してください。

"$BOOTSTRAP" --wait-for-exit <exitMarker-from-bootstrap-json>
& $bootstrap -WaitForExit -ExitMarker <exitMarker-from-bootstrap-json>

初期設定で報告されたものと同じプロファイルパスを使用してください。デフォルトのプロファイルを使う場合に限り、パスの明示を省略できます。待機に成功したら、通常のランチャーを再度実行してください。分離されたコピーは保存済みの認証情報を引き継ぎます。初期設定ウィンドウがまだ保持しているファイルとの競合も発生しません。

ユーザーが明示的に選択しない限り、初期設定を実行しないでください。初期設定ウィンドウは、通常の分離された起動とは異なり、永続プロファイルと共有認証ストレージを使用します。

コピーされるもの(デフォルトのスリムモード)

除外リストは、VS Code 独自のパフォーマンステスト用スキル(.github/skills/auto-perf-optimize)で使用されているものと同じです。このリストは、Copilot の認証と言語モデルの利用可能性を維持することが確認されています。具体的には、WebStorage/、Service Worker/、Local Storage/、Cookies、Network Persistent State、TransportSecurity、Trust Tokens、Preferences、machineid、および state.vscdb が格納されている User/globalStorage/ 全体が保持されます。

Windows の認証

Windows には、これらのシークレット用のアプリ単位で共有されるキーチェーンがないため、シークレットはディスク上のファイルに保存されます。ただし、すべてが user-data-dir 内にあるわけではありません。GitHub セッションが StorageScope.APPLICATION_SHARED に保存されるのは Windows のみ です(src/vs/platform/secrets/common/secrets.ts の useSharedStorage と CROSS_APP_SHARED_SECRET_KEYS を参照)。そのため、認証情報の 2 つの部分は異なるディレクトリに保存されます。

要素場所
暗号化された GitHub セッションの BLOB<shared-data-dir>/sharedStorage/state.vscdb
DPAPI でラップされた復号キー(os_crypt.encrypted_key)<user-data-dir>/Local State
そのため、ランチャーは両方をシードします。ソースのプロファイルをコピーし、さらにソースの shared-data-dir を実行用の使い捨て shared-data ディレクトリにコピーします。ソースの解決方法は IEnvironmentService.appSharedDataHome と同じです。$env:CODE_OSS_DEV_AUTHED_SHARED_DATA_DIR が設定されていればその値、そうでなければポータブル実行時は $env:VSCODE_PORTABLE\shared-data、それ以外は ~/<product.sharedDataFolderName>(つまり %USERPROFILE%\.vscode-oss-shared)です。また、プロファイルのコピー後に Local State、machineid、Network が保持されていることを確認し、どちらのデータベースにも GitHub セッションがない場合は stderr に警告を出力します。

この非対称性は、同じトークンがプロファイル内に格納される macOS/Linux では表面化しません。Windows だけで発生する「常にサインアウトされる」症状は、プロファイルの問題ではなく、shared-data-dir の問題です。ソースプロファイルでサインインすると正常なセッションが書き込まれますが、このシード処理が導入される前は、起動のたびに Code OSS に空の shared dir が渡され、そのセッションは破棄されていました。

ブートストラップスクリプトはセッション blob を %USERPROFILE%\.vscode-oss-shared に、キーをプロファイルの Local State に書き込みます。その後の起動では両方がコピーされ、セッションが引き継がれます。

APPLICATION_SHARED 移行前のプロファイルには、秘密情報が User/globalStorage/state.vscdb に残っている場合があります。ApplicationSharedStorageMain はアプリケーションストレージを読み取り用のフォールバックとして登録するため、shared-data-dir がなくても、それらのプロファイルで認証できます。このため、shared dir がない場合は致命的だと決めつけず、事実として報告します。

除外対象(一時的、再生成可能、または既知の不要なもの):

  • User/workspaceStorage/ - ワークスペースごとの状態。保存済みのチャットセッションを含みます(数 GB になることもよくあります)
  • User/History/ - ローカルファイルの編集履歴
  • CachedExtensionVSIXs - VSIX のバックアップ(数百 MB)
  • logs
  • プロファイルルートの Chromium キャッシュ:Cache、Code Cache、CachedData、GPUCache、ShaderCache、Dawn*Cache、component_crx_cache。また、永続的な統合ブラウザーのパーティション内の Cache、Code Cache、GPUCache、Dawn*Cache
  • Backups、blob_storage、BrowserMetrics、Crashpad、Session Storage
  • Singleton*、*.lock、*.sock(ソースインスタンスとの競合を招くため)

永続的な統合ブラウザーのパーティションには、Cookie、ローカルストレージとセッションストレージ、IndexedDB、WebStorage、サービスワーカー、設定などのウェブサイト状態が保持されます。除外されるのは再生成可能なキャッシュのみです。

extensions/ はデフォルトで空の新規ディレクトリになります。最速で競合もありませんが、起動したインスタンスにはサードパーティ製拡張機能がインストールされていません。--clone-extensions を渡すと、ソースの extensions ディレクトリが一時プロファイルにコピーされ、新しいインスタンスはソースから独立します。スリムコピーに必要なものが不足していると思われる場合は、--full を渡してすべての除外を無効にします。

ソースの extensions/ ディレクトリを直接共有してはいけないのはなぜですか? 拡張機能管理サービスは共有の .obsolete ファイルに書き込みます。2 つのプロセスが同時に書き込むと、互いの共有バックグラウンドプロセスがクラッシュします。そのため、--shared-data-dir を使うのと同じ理由で、ランチャーは常に分離された extensions ディレクトリを使用します(下記参照)。

スクリプトは起動前処理(electron のダウンロード、未コンパイルの場合のコンパイル、組み込み拡張機能)をフォアグラウンドで実行し、その後 Code OSS をデタッチして起動します。また、レンダラーの CDP エンドポイントが応答するまで(最大約 90 秒)ブロックしてから、stdout に JSON 行を出力します。起動前処理のエラー、code.sh の早期終了、CDP が開かないなど、何らかの問題が発生した場合、スクリプトはゼロ以外の終了コードで終了し、関連するログの末尾を stderr に出力します。

準備済みビルドを繰り返し起動する場合は、通常の起動を一度成功させた後に --skip-prelaunch を渡します。すべての出力を最新に保つ watch タスクが動作している場合、またはソースにもビルド出力にも変更がない場合にのみ使用してください。そうでなければ、新しいインスタンスで古いコードや不完全なコードが実行される可能性があります。

{"pid":12345,"cdpPort":53111,"extHostPort":53112,"mainPort":53113,"agentHostPort":53114,"userDataDir":".../user-data","extensionsDir":".../extensions","sharedDataDir":".../shared-data","runDir":"...","logFile":".../code.log","repo":"...","agents":false,"timings":{"profileMs":231,"preLaunchMs":251,"cdpReadyMs":459,"totalMs":941}}

追加される timings オブジェクトは、単調増加する経過時間を使って、分離されたプロファイルの準備、起動前処理、Code OSS の起動から CDP の準備完了までにかかった時間を特定します。totalMs は、準備完了までのランチャーの全操作を対象とします。 ランチャーの前提条件としてすでに用意されている Node で取得します。JSON が出力される時点で CDP はすでに起動しているため、再試行ループは不要です:

INFO=$("$LAUNCH" | tail -n1)
CDP=$(node -p 'JSON.parse(process.argv[1]).cdpPort' "$INFO")
EXT=$(node -p 'JSON.parse(process.argv[1]).extHostPort' "$INFO")
MAIN=$(node -p 'JSON.parse(process.argv[1]).mainPort' "$INFO")
AGENT=$(node -p 'JSON.parse(process.argv[1]).agentHostPort' "$INFO")
LOG=$(node -p 'JSON.parse(process.argv[1]).logFile' "$INFO")
PID=$(node -p 'JSON.parse(process.argv[1]).pid' "$INFO")

Windows では、PowerShell で JSON を取得して解析します:

$info = & $launch | Select-Object -Last 1 | ConvertFrom-Json
$cdp = $info.cdpPort
$ext = $info.extHostPort
$main = $info.mainPort
$agent = $info.agentHostPort
$log = $info.logFile
$pid = $info.pid
各ポートの用途
ポートプロセス用途
cdpPort (--remote-debugging-port)レンダラー(ワークベンチウィンドウ)CDP 経由の @playwright/cli、および Chrome DevTools
extHostPort (--inspect-extensions)拡張機能ホスト(Node)dap-cli(Node インスペクタープロトコル)
mainPort (--inspect)Electron メインプロセス(Node)dap-cli(Node インスペクタープロトコル)
agentHostPort (--inspect-agenthost)エージェントホストプロセス(Node)dap-cli(Node インスペクタープロトコル)

@playwright/cli で UI を操作する

起動 JSON の動的な cdpPort を使用します。通常の手順は、接続し、対象を確認し、スクリーンショットを調べ、スナップショットを取得し、操作してから、UI に意味のある変更を加えた後に再度スナップショットを取得することです。

Playwright CLI の構文がわからない場合は、オプション名を推測せず、npx @playwright/cli --help または npx @playwright/cli <command> --help を実行してください。

必ず一意の PW_SESSION 名を選び、すべての npx @playwright/cli ... 呼び出しで -s=$PW_SESSION を指定してください。 CLI はセッション名をキーとする永続デーモン(cliDaemon.js)を使用します。2 つのシェルでどちらも -s= を省略すると、暗黙の "default" セッションを共有し、最も最近接続された CDP が、以降の両方のシェルからのすべてのコマンドに使われます。起動スキルは分離(インスタンスごとの UDD、ポート、shared-data-dir)を前提としており、この方法により Playwright の操作レイヤーでもその分離を維持できます。代替の PLAYWRIGHT_CLI_SESSION 環境変数について: パッケージの README に記載されており、open 形式のワークフローでは正しく機能しますが、attach --cdp=... とは相性がよくありません(デーモンに --cdp=... と --endpoint=<env-value> の両方が渡され、後者が優先されるため、connect ENOENT エラーが発生します)。@playwright/[email protected] で確認済みです。明示的な -s=NAME はすべてのモードで機能します。

# At the top of your script / subagent prompt:
PW_SESSION="my-uniq-$$"        # any unique string; $$ is fine for one shell per agent

# launch.sh blocks until CDP is ready, so a single attach is enough.
npx @playwright/cli -s=$PW_SESSION attach --cdp=http://127.0.0.1:$CDP
npx @playwright/cli -s=$PW_SESSION tab-list
npx @playwright/cli -s=$PW_SESSION snapshot

attach の後は、同じ -s=$PW_SESSION を指定し続ける限り、閉じるか再接続するまで、後続の @playwright/cli コマンドは接続中のアプリを使い続けます。

適切な Electron ターゲットを選択する

Electron アプリには、複数のウィンドウやウェブビューが表示されることがあります。tab-list に about:blank、ウェブビュー、またはそれ以外の誤ったターゲットが表示される場合は、操作する前にターゲットを切り替えてください:

npx @playwright/cli -s=$PW_SESSION tab-list
npx @playwright/cli -s=$PW_SESSION tab-select 2
npx @playwright/cli -s=$PW_SESSION snapshot

再起動後にターゲットが古いように見える場合は、npx @playwright/cli -s=$PW_SESSION close を実行し、$CDP で再度接続してから、tab-list をもう一度確認してください。

続行する前に起動したウィンドウを確認する

起動または再起動のたびに、対象のウィンドウが描画されたら、スクリーンショットを撮影して画像表示ツールで開いてください。アプリを操作したり、ユーザーにサインインを求めたりする前に、想定した画面、テキスト、アイコン、レイアウトが正常であることを目視で確認してください。スクリーンショットを保存しただけで確認していない場合や、DOM/アクセシビリティのスナップショットを読んだ場合、あるいは CDP エンドポイントに到達した場合は、この確認を満たしません。

スクリーンショットにアイコンの欠落、空白のコンテンツ、または明らかに崩れたレイアウトが見られる場合は、シナリオを中止し、続行する前に確認できた問題を調査してください。修復後に再起動して、もう一度確認してください。ユーザーから UI の不具合を指摘されるまで待ってはいけません。

チャット入力欄にフォーカスする

通常のワークベンチと Agents ウィンドウの両方で、playwrightScripts/focus-chat-input.ts スクリプトを使用してください。Playwright を 1 回呼び出すだけで、フォーカスに必要な処理をすべて実行します。

  1. 表示中のチャット入力欄にすでにフォーカスがある場合、何もしません。
  2. 表示中のチャット入力欄があり、フォーカスされていない場合、その入力欄にフォーカスします。
  3. それ以外の場合は、プラットフォームのチャットフォーカス用ショートカットを実行し、入力欄が表示されるのを待ってから、ショートカットですでにフォーカスされていない場合に限りフォーカスします。

スクリプトはブラウザーのページからプラットフォームを判別し、アクティブな Agents セッションを優先して、インラインチャット入力欄を除外します。クローンしたプロファイルでデフォルトのショートカットが変更されている場合は、コマンドパレットを使って、対象の画面に応じたコマンドを実行します。

LAUNCH_DIR=<dir-of-this-SKILL.md>
FOCUS_CHAT="$LAUNCH_DIR/playwrightScripts/focus-chat-input.ts"
npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT"
$skillDir = '<dir-of-this-SKILL.md>'
$focusChat = Join-Path $skillDir 'playwrightScripts\focus-chat-input.ts'
npx @playwright/cli "-s=$pwSession" run-code "--filename=$focusChat"

スクリプトは { focused, focusChanged, focusInvoked, shortcutInvoked, commandPaletteFallbackInvoked, selector } を返します。focusChanged は、このスクリプトの実行によってフォーカスが Chat に移動したかどうかを示し、focusInvoked は、スクリプトが直接 focus() を呼び出す必要があったかどうかを示します。チャット入力欄にすでにフォーカスがある場合、どちらも false になります。スクリプトが失敗した場合は、新しいスナップショットを取得し、再試行する前に、処理を妨げているダイアログやチャットを利用できない状態を解消してください。

Monaco への入力(チャット入力欄、エディター)

Code OSS では、fill と type は何のエラーも出さずに失敗します。Monaco の native-edit-context 要素は、Playwright のデフォルトの入力処理に反応しません。次のいずれかを使用してください。

  • scripts/monaco-paste.ts ヘルパー(推奨 — クロスプラットフォーム、高速、システムクリップボード不要、並列実行に安全)。位置引数または stdin からテキストを読み取り、フォーカス中のチャット入力用 Monaco エディターに DataTransfer ペイロードを設定した ClipboardEvent('paste') をディスパッチします。--session NAME または PW_SESSION に対応しているため、ほかの処理と同じ -s= セッション内で実行できます。互換性のため、Bash ラッパーの scripts/monaco-paste.sh も引き続き利用できます。

    LAUNCH_DIR=<dir-of-this-SKILL.md>           # the same dir that holds scripts/launch.sh
    FOCUS_CHAT="$LAUNCH_DIR/playwrightScripts/focus-chat-input.ts"
    PASTE="$LAUNCH_DIR/scripts/monaco-paste.ts"
    export PW_SESSION                            # helper reads this env var
    
    # Send a prompt:
    npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT"
    node "$PASTE" 'Please run `pwd && ls` using your terminal tool.'
    npx @playwright/cli -s=$PW_SESSION press Enter
    
    # Long / arbitrary text via stdin (avoids any shell-quoting headaches):
    printf 'multi-line prompt\nwith backticks `x`\nand emoji 🎉' | node "$PASTE"
    
    # Append without clearing:
    node "$PASTE" --append " continued text"
    
    # Skip the read-back check (useful when intentionally pasting more than the
    # chat input's ~600-character soft cap):
    node "$PASTE" --no-verify "...long text..."
    
    # Or pass the session explicitly per call (if you don't want to export PW_SESSION):
    node "$PASTE" --session "$PW_SESSION" "..."
    
    $paste = Join-Path $skillDir 'scripts\monaco-paste.ts'
    node $paste --session $pwSession 'Please respond with OK.'
    npx @playwright/cli "-s=$pwSession" press Enter
    

    ヘルパーは stdout に単一の JSON 行 {ok, actualLength, expectedLength, viewLineCount, firstViewLine, error?} を出力します。検証に成功した場合は終了コード 0、検証に失敗した場合は 1、引数エラーの場合は 2 です。Unicode(中文)、絵文字(🎉)、バッククォート、アンパサンド、引用符を含むテキスト、改行を含む 20 回以上の連続貼り付けで、信頼性を確認しています。

    説明だけでなくヘルパースクリプトを用意している理由: インラインの手順では、JavaScript のテンプレートリテラルを含む複数行の node -e ヒアドキュメントを使いますが、これはまさに誤ってコピーされやすいコードです。また、正確に処理するうえで見落としやすい問題が 3 つあり、ヘルパーが内部で対処しています。

    1. Monaco の native-edit-context は fill や type には反応せず、実際の貼り付けイベント(またはキーごとの press)にのみ反応します。
    2. Monaco は ASCII のスペースをビュー行の DOM 内で U+00A0(NBSP)として描画するため、比較前に正規化して検証する必要があります。
    3. Monaco は貼り付けイベントの後、DOM を非同期で更新します。同じ eval 内で同期的に読み戻すと、古い状態が返されます。ヘルパーは、貼り付けた先頭部分が現れるか検証がタイムアウトするまで、描画されたビュー行を複数回の描画サイクルにわたってポーリングします。
  • キーごとの press(どの環境でも使えますが、キー入力ごとに Node の起動コストを伴う CLI 呼び出しが必要なため低速です):

    npx @playwright/cli -s=$PW_SESSION press H
    npx @playwright/cli -s=$PW_SESSION press i
    npx @playwright/cli -s=$PW_SESSION press Enter
    
  • pbcopy によるクリップボードへの貼り付け(macOS では高速ですが、NSPasteboard はシステム全体で共有されるため、クリップボードを操作するシェルが並行していると競合します)。貼り付けの間、マシン上のほかの処理がクリップボードを使用しない場合に限って使用してください。

    LAUNCH_DIR=<dir-of-this-SKILL.md>
    FOCUS_CHAT="$LAUNCH_DIR/playwrightScripts/focus-chat-input.ts"
    printf '%s' "Your prompt here" | pbcopy
    npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT"
    npx @playwright/cli -s=$PW_SESSION press Meta+v
    npx @playwright/cli -s=$PW_SESSION press Enter
    
並列マルチインスタンスパターン

起動スキルは分離を前提としているため、自然なワークロードは1 台のマシンで多数のエージェントを動かし、それぞれが独自の Code OSS を操作することです。このパターンの要点は、各エージェントに固有の PW_SESSION を割り当て、あらゆる場所でそれを渡すことです。

LAUNCH_DIR=<dir-of-this-SKILL.md>
FOCUS_CHAT="$LAUNCH_DIR/playwrightScripts/focus-chat-input.ts"
PASTE="$LAUNCH_DIR/scripts/monaco-paste.ts"
export PW_SESSION

# In agent A's shell:
PW_SESSION="agent-A-$$"
INFO=$("$LAUNCH" --agents -- --use-mock-keychain | tail -n1)
CDP=$(node -p 'JSON.parse(process.argv[1]).cdpPort' "$INFO")
npx @playwright/cli -s=$PW_SESSION attach --cdp=http://127.0.0.1:$CDP
npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT"
node "$PASTE" "prompt for A"   # helper picks up $PW_SESSION

# In agent B's shell (running concurrently):
PW_SESSION="agent-B-$$"
INFO=$("$LAUNCH" --agents -- --use-mock-keychain | tail -n1)
CDP=$(node -p 'JSON.parse(process.argv[1]).cdpPort' "$INFO")
npx @playwright/cli -s=$PW_SESSION attach --cdp=http://127.0.0.1:$CDP
npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT"
node "$PASTE" "prompt for B"

各エージェントには、それぞれ独自の CDP にバインドされた cliDaemon が割り当てられるため、貼り付け、クリック、スナップショットが互いに干渉しません。2 つのインスタンスを同時に実行し、動作を確認済みです。macOS の Mach ポートに関する注意点: macOS では、Code OSS インスタンスを同時に約 2~3 個以上起動すると、Crashpad の例外ハンドラーが mach_port_request_notification: invalid capability で停止する傾向があります。これは OS レベルの別個の制限であり、セッション名の影響は受けません。

cliDaemon プロセスのクリーンアップ: npx @playwright/cli -s=$PW_SESSION close を実行してセッションのデーモンを停止するか、(Code OSS のウィンドウをすべて終了してから)npx @playwright/cli kill-all を実行して、残存するデーモンをすべて終了してください。セッションのデーモンは ~/Library/Caches/ms-playwright/daemon/<hash>/ にあります。

Agents ウィンドウのセレクターの違い

Agents ウィンドウでは、通常のワークベンチの .interactive-input-editor ラッパーは使用されません。このラッパーにスコープを限定したセレクターチェックでは、Agents のチャット入力にフォーカスがある場合でも、何も返されないことがあります。

// Regular-workbench-specific selector; do not assume this exists in Agents.
document.querySelectorAll('.interactive-input-editor .view-line')

// More useful checks in Agents.
document.querySelectorAll('.view-line')
document.activeElement?.matches('.native-edit-context, textarea.inputarea')

フォーカススクリプトはこうした DOM の違いに対応し、アクティブな Agents セッションを優先します。

チャットテキストの確認とクリア

通常のワークベンチのサイドバーでは、次の方法でテキストが Monaco の入力欄に入力されたことを確認できます。

npx @playwright/cli -s=$PW_SESSION eval '
(() => {
  const sidebar = document.querySelector(".part.auxiliarybar");
  const viewLines = sidebar?.querySelectorAll(".interactive-input-editor .view-line") ?? [];
  return Array.from(viewLines).map(viewLine => viewLine.textContent).join("|");
})()'

Agents ウィンドウでは、通常のサイドバーラッパーが存在すると決めつけず、代わりに新しいスナップショットを取得し、上記のより広範なセレクターとフォーカスのチェックを使用してください。

フォーカス中の Monaco 入力欄をクリアするには、次のようにします。

# macOS
npx @playwright/cli -s=$PW_SESSION press Meta+a
# Linux / Windows
npx @playwright/cli -s=$PW_SESSION press Control+a
npx @playwright/cli -s=$PW_SESSION press Backspace

フォーカススクリプトから Chat にアクセスできない場合は、まだ利用可能な画面が表示されていない可能性があります。スナップショットを取得し、チャットが表示される状態まで UI を操作してから再試行してください。CLI コマンドの実行が完了したことを、テキストが入力された証拠と見なさないでください。

スクリーンショット(記録用)
SHOTS="$PWD/screenshots/$(date +%Y-%m-%dT%H-%M-%S)"
mkdir -p "$SHOTS"
npx @playwright/cli -s=$PW_SESSION screenshot --filename="$SHOTS/after-launch.png"

スクリーンショットは /tmp ではなくワークスペース内に保存してください。そうすればレビュー用に残ります。

幅の広いウィンドウでは、--full-page を指定するとレイアウトを確認しやすくなります。また、スナップショットで確認したいパネルの安定した ref が得られる場合は、要素のスクリーンショットが役立ちます。

npx @playwright/cli -s=$PW_SESSION screenshot --full-page --filename="$SHOTS/full-window.png"
npx @playwright/cli -s=$PW_SESSION screenshot e42 --filename="$SHOTS/panel.png"

macOS でスクリーンショットの「Permission denied」エラーが発生する場合、通常はターミナルに画面収録の権限がありません。その権限の問題を解決する間は、テキストや状態の確認を使用してください。

動画の録画

デモ動画を録画するには、アタッチ済みセッションを Playwright の page として使用し、一般的な page.screencast の録画ワークフローを実行します。

  • page.screencast.start() と stop() を含むシナリオ全体を、ファイル内に単独の async page => { ... } 関数として記述し、npx @playwright/cli -s=$PW_SESSION run-code --filename=<file> を 1 回実行します。page はアタッチ済みのワークベンチウィンドウです。
  • 入力するテキストは短くしてください。Monaco にキーを 1 文字ずつ入力すると様子が映りますが、時間がかかります。約 400 文字の入力で、動画はほぼ 4 分になりました。長いセットアップ用のテキストは録画開始前に貼り付けてください。
  • コマンドを実行するには、F1 を押し、Type the name of a command to run. テキストボックスに >Command Name を入力して、完全に一致する項目をクリックします。リストの更新は非同期で行われるため、入力直後に Enter を押すと、古い検索結果が実行されることがあります。

dap-cli でデバッグする

ウィンドウの実行中に VS Code のソースコードにブレークポイントを設定するには、いずれかのポートに dap-cli をアタッチします。dap-cli が役立ちそうなのに対応するスキルが利用できない場合は、デバッガー固有の手順に進む前に、https://github.com/roblourens/dap-cli からインストールするようユーザーに案内してください。

利用可能な場合は、アタッチ、ブレークポイント設定、状態確認のワークフロー全体について dap-cli スキルを読んでください。このスキルでは、接続先のポートのみを案内します。

  • 拡張機能ホスト(最も一般的です。Copilot Chat 拡張機能、組み込み拡張機能、開発中の独自拡張機能) -> extHostPort
  • メインプロセス(Electron のライフサイクル、ウィンドウやメニューの配線、IPC) -> mainPort
  • ローカルエージェントホスト(src/vs/platform/agentHost/node/...、エージェントのセッションライフサイクル、AHP の配線、Claude/Copilot のエージェントプロバイダー) -> agentHostPort
  • レンダラー(ワークベンチ本体、src/vs/workbench/...) -> cdpPort

@playwright/cli と dap-cli を同じウィンドウに対して同時に実行できます。一方のターミナルで UI を操作し、もう一方でブレークポイントに到達させて状態を確認できます。

複数のインスタンス

起動のたびに新しいポートと新しい一時 runDir が割り当てられるため、マシンの処理能力が許す限り、Code OSS ウィンドウを同時にいくつでも実行できます。それぞれのポートは個別の JSON ブロブに含まれて返されるため、混同しないようにしてください。

ランチャーは --shared-data-dir=<runDir>/shared-data も渡します。これは複数インスタンスを分離するために必須です。Code OSS は ~/.<dataFolderName>-shared/sharedStorage/state.vscdb に固定パスの SQLite DB を保持しますが、これは --user-data-dir の対象外です。上書きしない場合、同時に実行する 2 つのインスタンスが同じファイルを奪い合い、一方が「shared background process terminated unexpectedly」というエラーで終了します。各起動では、Windows の GitHub セッションを維持できるよう、ソースの shared-data-dir を元にした独自の shared-data ディレクトリが作成されます。このコピーが重要な理由については、Windows authentication を参照してください。

ソースの変更後に再起動する

ワークベンチのコードは Code OSS ウィンドウの起動時に読み込まれるため、ソースを変更しても実行中のインスタンスにはホットリロードされません。ビルド出力が最新の状態になったら、起動したプロセスを終了し、再度起動して、新しい JSON ブロブに含まれる新しい cdpPort に再アタッチしてください。

kill "$PID" 2>/dev/null || true
INFO=$("$LAUNCH" | tail -n1)
CDP=$(node -p 'JSON.parse(process.argv[1]).cdpPort' "$INFO")
PID=$(node -p 'JSON.parse(process.argv[1]).pid' "$INFO")
npx @playwright/cli -s=$PW_SESSION attach --cdp=http://127.0.0.1:$CDP
npx @playwright/cli -s=$PW_SESSION tab-list
npx @playwright/cli -s=$PW_SESSION snapshot

頻繁に変更を繰り返す場合は、再起動時に生成済みの出力を利用できるよう、リポジトリのビルド/ウォッチタスクを別途実行したままにしてください。通常の起動に一度成功した後は、出力が最新の状態である限り、--skip-prelaunch を使うと準備を繰り返さずに済みます。

クリーンアップ

ランチャーは一時 runDir(JSON に表示されます)の下にすべてを書き込みます。作業が終わったら、次の操作を行ってください。

# Disconnect this session's playwright daemon (leaves other sessions' daemons alone)
npx @playwright/cli -s=$PW_SESSION close

# Or nuke any stale daemons left behind by crashed callers across all sessions:
# npx @playwright/cli kill-all

# Kill the Code OSS instance
kill "$PID" 2>/dev/null || true
# Remove the throwaway profile
rm -rf "$(dirname "$LOG")"

Windows では、クリーンアップ用ヘルパーを使用してください。code.bat が返す PID は短時間しか存在しないラッパーのものの場合があるため、ヘルパーはこの起動固有の正確な runDir を --user-data-dir に含む Code - OSS.exe プロセスだけを検索して終了し、その後、一時プロファイルを削除します。

$cleanup = Join-Path $skillDir 'scripts\cleanup\cleanup.ps1'
& $cleanup -RunDir $info.runDir -PlaywrightSession $pwSession

Code OSS は完全な Electron アプリであり、1~4 GB を簡単に消費します。必ずクリーンアップしてください。

トラブルシューティング

  • @playwright/cli からの Daemon pid=...: listen EINVAL - デーモンのソケットパス(TMPDIR + 固定の約33文字のプレフィックス + -s= のセッション名)が、Unix ソケットの上限である約103バイトを超えています。macOS のデフォルトの TMPDIR では、セッション名に使えるのは約16文字です。まず -s= を短くしてください。さらに長い名前が必要な場合は、export するのではなく、単一のコマンドにだけ上書きを適用してください(TMPDIR=/tmp npx @playwright/cli ...)。そうすれば、ランチャーは引き続きユーザーごとのプライベートな一時ディレクトリを使用します。
  • 「Sent env to running instance. Terminating...」 - 動的な --user-data-dir によって、この問題は発生しないはずです。表示された場合は、別の Code OSS が同じプロファイルパスを使用しています。別のソースに対して --source-user-data-dir を指定するか、一時コピーが実際に行われたことを確認してください(ls "$(node -p 'JSON.parse(process.argv[1]).userDataDir' "$INFO")")。
  • Renderer の ESM エラー/import { Menu } from 'electron' - 環境変数に ELECTRON_RUN_AS_NODE が設定されています。ランチャーは子プロセス用にこれを解除しますが、自分で code.sh を起動する場合も同様に解除してください。
  • Git からの missing config value GIT_CONFIG_VALUE_* - エージェントのランタイムから継承されたインデックス付きの Git 設定で、アプリのプロセス起動中に空の値が失われました。ランチャーはホスト側のみの Git 上書き設定が Code OSS に影響しないよう、GIT_CONFIG_COUNT と GIT_CONFIG_PARAMETERS を解除します。そのようなランタイムから code.sh を直接実行する場合も同様にしてください。
  • 組み込み拡張機能の読み込みに失敗する(Cannot find module .../extensions/.../out/extension.js) - 拡張機能がコンパイルされていません。npm run compile(1回実行で、すべての組み込み拡張機能も再ビルドします)または npm run watch(差分ビルド)を実行してください。よくある原因は、ユニットテストを通すために npm run transpile-client を実行した結果、out/ は生成されたものの extensions/*/out/ は生成されず、preLaunch の「out/ がないか」というチェックがコンパイルをスキップしたことです。
  • launch.sh がログの末尾を表示してゼロ以外の終了コードで終了する - pre-launch が失敗した、CDP の起動前に code.sh が終了した、または CDP が90秒以内に開かなかった、のいずれかです。stderr に出力されるログの末尾は runDir/code.log からのものです。診断するには、そのログを確認してください。
  • スナップショットに誤ったページが表示される、または想定したコントロールが表示されない - tab-list を使い、必要に応じて tab-select <index> で切り替えてから、操作する前にもう一度スナップショットを取得してください。
  • CLI の入力コマンドは完了するが、入力欄が空のまま - playwrightScripts/focus-chat-input.ts を実行し、fill/type ではなく press またはクリップボードからの貼り付けを使ってから、送信前に入力欄の状態を確認してください。
  • 起動したウィンドウで認証情報が見つからない - サインイン情報が見つからない場合のフローを使い、ユーザーが現在の分離されたウィンドウでサインインを完了するか、永続化されたソースプロファイルをブートストラップするか選べるようにしてください。Windows では、まず shared-data-dir を確認してください。GitHub セッションの blob はプロファイル内ではなく、%USERPROFILE%\.vscode-oss-shared\sharedStorage\state.vscdb に保存されています。ランチャーは該当データが見つかった場合、stderr に copying shared data: <src> -> <dst> と記録し、見つからない場合は no shared-data-dir at <path> と警告します。

コメント