screenmap(aleqsio/screenmap)は、Expo / React Native アプリのすべての画面を地図として持ち、プルリクエストの差分が届く画面だけをCIで撮影してレビューに貼るMITライセンスのOSSだ。Webの PR にはプレビューURLがあるが、モバイルの PR は QR コードとビルドを渡されて「自分で画面を探してね」になりがちで、視覚的な変化がレビューで素通りする。screenmap はその隙間を埋める。本記事はリポジトリを clone し、静的ルート解析・Action CLI・Map Viewer のビルドを Linux で実測し、同梱のデモ地図(Bluesky クライアント・70画面)を Playwright で開いて実画面を撮った。GitHubスターは執筆時点で 189、公開は 2026年8月8日で、まだ若いプロジェクトである。

screenmapのMap Viewerでデモ地図(Bluesky・70画面・126フロー)を開いた画面。画面ごとのスクリーンショットがカードとして並び、ルート同士が線で結ばれている。左にAIが記録した54本のフロー一覧
本記事でビルドした Map Viewer に同梱のデモ地図(demo.scrmap)を読み込んだ実画面。ヘッダに「70 screens・50 captured・126 flows」、左に AGENT FLOWS 54 本。線は実線がコードから宣言されたリンク、破線が AI が観測した遷移。
30秒でわかる screenmap(2026-09-16時点)
  • 正体:Expo / React Native 向けの GitHub Action+Claude Code プラグイン+Map Viewer。ルータを静的解析して画面グラフを作り、PR の差分が到達しうる画面(suspects)だけをシミュレータで撮って before/after と注記を PR コメントに貼る。
  • 設計の芯:コミット済みの flow(argent 形式 YAML)が真実で、main の地図はキャッシュ、AI は flow が無い画面だけを予算内で探索する。合否ゲートは持たず「報告だけ」。
  • 実測:静的解析は fixture 2 件が 0.19 秒で期待グラフと一致。Action CLI は 26 パッケージ・3 秒、Viewer は 454 パッケージ・ビルド 1.2 秒。シミュレータが要る撮影と CI 実行は Linux コンテナでは未検証
  • 注意:★189・貢献者 1 人・公開 5 週間。AI レーンは Claude Code を --dangerously-skip-permissions で起動する。iOS は macOS ランナー(Linux の 10 倍課金)。

screenmap は「AIでレビューを自動化する」ツール群の中では、判定を人に残して証拠だけを機械が集める側に位置する。周辺ツールの全体像はAI自動化ツール|ノーコードからコードまで2026年版の比較と選び方にまとめている。

screenmapとは——PRが届く画面だけを撮る3つの入口

README 冒頭の定義は次の一文だ(原文)。

screenmap shows you every screen in an Expo / React Native app, and shows your reviewers exactly which screens a pull request changed, without anyone writing a test.

入口は3つある。

使い方 何を使うか 何が得られるか
PR の画面変化をレビューする GitHub Action aleqsio/screenmap@v1 before/after・変更領域のボックス・1画面1行の注記・Viewer へのリンクが付いた sticky コメント
手元のアプリを地図にする Claude Code プラグイン /screenmap .screenmap/out/graph.json・画面PNG・flow YAML・map.html
地図を眺める・共有する Map Viewer(app.screenmap.dev またはローカル) ブラウザ内でパース(アップロード無し)。?map=<url>&changes=<url> で PR の差分を重ねて表示

読者の3問に答える。

何ができる:ルータの静的解析で画面とリンクのグラフを作り、ディープリンクと flow 再生で各画面を撮り、.scrmap(zip・manifest+map.json+screens/*.png)に固める。PR では差分から suspects を出し、head 側だけ撮り直して .diff.scrmap にする
何を解決する:モバイル PR で「どの画面がどう変わったか」をレビュアーが見られない問題。テストを書かずに視覚的な証拠を PR に残す
何を代替する:QR コード+ビルド配布の手動確認、そして「画面の証拠のためだけに書く」E2E テストの一部。E2E テストランナー(Maestro・Detox)やビジュアルリグレッションのゲート(Chromatic 型)の代替ではない

従来のモバイルPRレビューとscreenmapの比較図
README が問題設定に挙げる「モバイル PR のレビューは視覚的な半分が飛ばされる」と、screenmap の答え。

なお同梱デモの manifest.json には "generator": "expo-map/2.0" とあり、プロジェクトは expo-map から改名されたと読める。同名の別 OSS(地図ライブラリなど)と混同しやすい名前なので、検索するときは aleqsio/screenmap で引くのがよい。

screenmapの仕組み:静的解析・suspects・決定論レーンとAIレーン

README「Three ideas the design rests on」は次の3点だ。

  1. コミット済み flow が真実.screenmap/flows/*.yaml.meta.json はアプリのリポジトリに入り、コードと同じくレビューされる。flow のある画面は決定論的に再生される
  2. main の地図はキャッシュ:完全な .scrmap は一度作り、push のたびに差分更新する。PR では suspects の head 側だけを撮り、base 側は地図から流用する
  3. AI は隙間を埋めて退場する:flow が無い画面だけをヘッドレスのコーディングエージェントが .screenmap/SKILL.md の指示で探索し、effort の予算で打ち切られる
flowchart LR PR["PR opened"] --> B["baseline 復元
screenmaps ブランチの .scrmap"] B --> P["parse-routes(head)
+ diff-map suspects"] P --> S{"suspect ごとに
flow がある?"} S -->|"yes"| F["argent flow run
OCR で着地確認(決定論)"] S -->|"no・deep link 可"| D["simctl openurl / adb am start
+ screenshot(決定論)"] S -->|"no・flow なし"| A["agent explores
予算 6 / 8 / 24 画面"] F --> K["diff-map pack
.diff.scrmap"] D --> K A --> K K --> O["publish: screenmaps ブランチ+artifact"] O --> C["sticky PR comment
?map=…&changes=…"]

パイプラインは README「How it works under the hood」で4段に分かれる。

Static parseparse-routes.mjs):expo-router のファイル規約か react-navigation のルートマップを読み、ルート一覧・エッジ・状態ヒントを graph.json に出す。プロバイダ方式で、docs/route-providers.md に従えば独自ルータも足せる
Exploration:iOS シミュレータまたは Android エミュレータでディープリンクを総なめし、撮影して分類、タップ操作は argent(Software Mansion 製・@swmansion/[email protected])の flow YAML として記録
Packpack-map.mjs):producer に依存しない .scrmap zip に固める
Visualise:React 19+Vite 8+Tailwind 4+shadcn/ui+React Flow+elkjs の SPA。差分は pixelmatch と opencv-js で領域を検出する

flow 再生には「着地確認」がある。argent はタップがどこに落ちても成功を返すので、再生後の画面を OCR(macOS は Apple Vision、Linux は tesseract)にかけ、flow のサイドカーに記録した 2〜5 語のランドマークと突き合わせる。ずれていればその画面は「再生失敗」として扱われ、無言で古い画面が貼られることを防ぐ。

PR実行の4段階:baseline復元、静的解析とsuspects算出、flow再生またはagent探索、PRコメント
README「What a PR run does, step by step」の8ステップを4段に要約。

実測:静的解析・Action CLI・Map Viewer を Linux で動かす

シミュレータが無い Linux コンテナで動かせる範囲を、README の記載どおりに実行した。

git clone https://github.com/aleqsio/screenmap.git && cd screenmap
node plugins/screenmap/skills/screenmap/scripts/parse-routes.mjs fixtures/demo-app   # 静的解析
node fixtures/run-tests.mjs                                                          # 同梱 fixture の期待グラフと照合
cd action/cli && npm install && node screenmap-ci.mjs --help                         # Action の CLI
工程 結果 備考
parse-routes.mjs fixtures/demo-app 成功(0.06 秒) mode: expo-router・routes 7・layouts 2・edges 10・unresolvedEdges 0。プロバイダ自動判定は「app/ に _layout がある」で expo-router(score 0.60)
fixtures/run-tests.mjs 2 件合格(0.19 秒) demo-app(expo-router・7 routes・10 edges)と rn-demo-app(react-navigation・5 routes・4 edges)が expected-graph.json と一致
action/clinpm install 26 パッケージ・3 秒 依存は puppeteer-core 25.9 と yaml 2.9 のみ
screenmap-ci --help 成功 サブコマンドは baseline / pr / comment / status / publish / flows-pr / flows-adopt / resolve-app / merge / shot の 10 個
apps/visualisernpm installnpm run build 454 パッケージ・11 秒 → ビルド 1.2 秒 出力 JS は 2.16 MB(gzip 674 KB)。500 KB 超のチャンク警告あり
Viewer に ?template=bluesky でデモを読込 成功 70 画面・50 captured・126 flows。Changes タブで PR #11123(Bluesky 本家の PR 番号)の差分「+1 added・±1 changed・−0 removed・1 edge」を表示

検証環境:Linux x86_64/Node.js v22.22.2/2026-09-16。確認したのは静的解析・fixture テスト・Action CLI の起動・Viewer のビルドとデモ地図の表示まで。iOS シミュレータ/Android エミュレータでの撮影、EAS ビルド、GitHub Actions 上での PR 実行、AI レーンの探索は未検証で、これらの挙動は README と action.yml の記述に基づく。

Map Viewer は README のとおり apps/visualiser で動かした。開発サーバー(npm run dev)ではなく本番ビルドを vite preview で配信し、Playwright で ?template=bluesky を開いて撮ったのが本記事のスクリーンショットだ。

cd apps/visualiser && npm install && npm run build       # 454 パッケージ・ビルド 1.2 秒
npx vite preview --port 4173 --host 127.0.0.1             # http://127.0.0.1:4173/?template=bluesky でデモ地図

デモ地図の中身も数えた。demo.scrmap は 5.17 MB の zip で 341 エントリ、screens/ に PNG 85 枚、flows/ に YAML 126 本、map.json は nodes 70・edges 56。Viewer で開くと、ディープリンクで開けなかった画面には「CRASHES ON DEEP LINK」「NEEDS NAVIGATION」「EMPTY-STATE」のバッジと、AI が書いた到達手順の注記(例:「集約された通知をタップして到達する」)が付いている。README が「AI は隙間を埋める」と言う中身はこれだ。

Map ViewerのChangesタブ。PR #11123で追加された/settings/beta-featuresと変更された/settingsの2画面がハイライトされ、7つの広くimportされる変更ファイルがsuspect判定から除外されたと表示
同じデモの Changes ビュー(本記事の実画面)。追加 1・変更 1 の画面が色付きで浮き、「広く import される変更ファイル 7 件は suspect 判定から除外」と明示される。

screenmapをGitHub Actionsへ導入する:テンプレートとActionの入力

README「Before you start」が求める前提は4つで、expo-router か react-navigation を使う Expo / RN アプリ(expo-dev-client 入り・ディープリンク可能)、シミュレータビルド(iOS)か APK(Android。.aab はエミュレータに入らない)を出す EAS プロファイルか自前ビルド、secrets を置ける GitHub リポジトリ、そしてランナー時間だ。導入は action/templates/ の2つのワークフローを .github/workflows/ にコピーするところから始まる。PR 側のテンプレートは次の内容で、本記事はこのファイルを clone から読んだ(CI での実行はしていない)。

# .github/workflows/screenmap-pr.yml(action/templates より・コメント一部省略)
name: screenmap · PR changes
on:
  pull_request:
    types: [opened, synchronize, reopened]
permissions:
  contents: write        # screenmaps ブランチへ push(publish: false なら不要)
  pull-requests: write   # sticky コメント
  actions: write         # 地図が無いとき baseline ワークフローを起動
jobs:
  changes:
    runs-on: macos-26
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - uses: aleqsio/screenmap@v1
        with:
          mode: pr
          agent_api_key: $   # 省略すると決定論レーンのみ
          expo_token: $             # EAS が dev client を用意(app_path でも可)

もう1本の screenmap-baseline.ymlmain への push と平日 cron で地図を更新し、AI が記録した flow を PR として main に対して開く(feature ブランチには触らない)。主な入力を action.yml から抜くと次のようになる。

入力 既定値 意味
mode 必須 pr または baseline
platform ios iosandroid。1 ジョブ 1 プラットフォーム
agent_provider claude claudecodexgeminiopencode
agent_api_key 空なら AI レーン無し
effort balanced fastbalancedthorough。AI が探索する画面数の上限 6/8/24 と suspect の探索深さ 1/2/3 ホップ
publish "true" screenmaps ブランチへ発行。private リポジトリでは raw URL が匿名で読めないため "false" にして artifact リンクを使う
simulator iPhone 17 Pro 起動する iOS デバイス
app_path ビルド済み .app.apk。指定すると EAS を飛ばす
expo_token app_path が無いとき必須
screenmaps_branch screenmaps 地図と PR ごとの差分バンドルを置く orphan ブランチ

ファイルの読み書きは明快で、読むのは .screenmap/SKILL.md(AI へのアプリ固有の指示)・.screenmap/config.json(scheme・デバイス・待ち時間・suspect の深さ・AI 予算)・.screenmap/flows/eas.json、書くのは screenmaps ブランチの main/<sha7>.scrmapmain/latest.scrmappr-<n>/<headsha>.diff.scrmap と、90 日保持の workflow artifact だ。Action 自身はビルドを一切持たない(EAS か app_path が dev client を渡す)。ステータスバーは simctl status_bar override で 9:41 に固定し、simctl privacy grant all でシステムダイアログを先に潰しておく、といった細部も README「Decisions baked in」に明記されている。

AIレーンの中身と権限:どのCLIをどう起動するか

AI レーンはプロバイダ非依存で、契約はファイルベースだ。エージェントは「どの画面を担当し、撮影・flow・notes.jsonsummary.json をどこに書くか」を指示され、CLI が後から検証する。README の表をそのまま引く。

agent_provider 起動される CLI キーの環境変数
claude(既定) claude -p … --dangerously-skip-permissions ANTHROPIC_API_KEY
codex codex exec --dangerously-bypass-approvals-and-sandbox OPENAI_API_KEY
gemini gemini --yolo -p … GEMINI_API_KEY
opencode opencode run … 設定したプロバイダ次第

つまり CI ランナー上で Claude Code を権限確認なしで走らせる。用途がシミュレータ操作とファイル書き出しに限られ、書いたファイルを CLI が検証するとはいえ、contents: writepull-requests: write を持つジョブの中で確認スキップのエージェントが動くことは把握しておきたい。Action は選んだ CLI をその場でインストールし、.screenmap/config.json"agent": { "command": "myagent --prompt-file {promptFile}", "keyEnv": "..." } で任意の CLI に差し替えられる。予算は effort で 6/8/24 画面に上限が付き、超えた分は「skipped」としてコメントに出る。README はコスト(トークン量や金額)を示していないので、fast から始めて様子を見るのが妥当だ。

手元での地図作りは Claude Code プラグインで行う。claude plugin marketplace add aleqsio/screenmapclaude plugin install screenmap@screenmap を実行し、Expo プロジェクト内で /screenmap(静的解析+シミュレータ探索+パック)、/screenmap --static(デバイス不要・解析と描画のみ)、/screenmap replay <flow名>/screenmap pr <番号> を使う(本記事の環境では Claude Code のプラグイン導入とデバイス操作を実行していない)。プラグインの SKILL.md は 333 行あり、iOS/Android の対応コマンド表(xcrun simctl openurladb shell am startsimctl io screenshotscreencap)や、Android で Metro に届かせるための adb reverse tcp:8081 tcp:8081 の注意まで書き込まれている。

制約とメンテナンス状況:採用前に見るべき点

README「Known limits」は隠さず書かれている。

1 ジョブ 1 プラットフォーム:iOS は macOS、Android は Linux。両方は別ジョブで撮って screenmap-ci merge で結合
Android の dev メニュー抑止はベストエフォートrun-as が要るので debuggable ビルド限定
Linux の OCR は tesseract:着地確認の誤検出が Apple Vision より増える
flow はプラットフォームごと:座標は正規化するが、レイアウトやシステム UI の差は吸収しない
ルータ必須:expo-router のファイル規約か react-navigation のルートマップ以外は認識しない。URL の無い画面は AI 探索頼み
エッジ抽出は正規表現:動的 href はルートパターンに丸める
報告のみ・ゲート無し:合否は出さない。設計上の意思

メンテナンス状況の実測値は次の通り。

項目 実測値(2026-09-16)
GitHub star / fork / open issues 189 / 14 / 2
コミット 72(初コミット 2026-08-08・最終 2026-09-09)
貢献者 1 人(aleqsio・非マージ 69 コミット)
タグ v1(2026-08-28)。main はそこから 15 コミット先で、直近は Android 対応(PR #8)
ランナー テンプレートは macos-26・Node 22。iOS 26 シミュレータ前提
LICENSE 実体 MIT License(Copyright (c) 2026 Aleksander Mikucki)

v1 タグは動くタグで、Action の参照 aleqsio/screenmap@v1main の進みに追随する。Android 対応は 9 月 8〜9 日に入ったばかりなので、Android で使うなら main の直近コミット(「The Android PR lane works」など)を読んでから判断したい。

類似ツールとの比較

観点 screenmap Maestro Playwright(Web E2E) Chromatic 型のビジュアルリグレッション
対象 Expo / React Native の画面 iOS/Android/Web の UI テスト Web Storybook コンポーネント/Web
何を書くか 何も書かない(flow は AI が記録し YAML でコミット) YAML の flow を自分で書く テストコード ストーリー
出力 PR コメントの before/after と注記・地図 テストの合否 テストの合否・トレース ピクセル差分の承認フロー
ゲート 無し(報告のみ) 有り 有り 有り(承認)
AI の役割 flow が無い画面の探索と注記(任意) 無し 記事参照 無し
実行場所 自分の GitHub Actions・画面は自分のリポジトリの orphan ブランチ 任意 任意 SaaS

Maestro は flow を人が書いて合否を出すテストランナーで、screenmap は flow を機械が記録して証拠を出す観察ツール、という違いになる。両者は排他ではなく、screenmap の flow 形式である argent も Maestro と同じ YAML 志向の操作記述だ。Web 側で「壊れたら直す」ループを組む発想はPlaywright E2Eで自己改善ループを作る実践ガイド|壊れたら自分で直るテストへに近く、コードレビュー全体を AI に委ねるツールとの役割分担はAIコードレビューツール比較2026を参照してほしい。

まとめ

screenmap は「モバイル PR の画面変化をレビュアーに見せる」一点に絞った OSS で、テストを書かず、AI を flow の無い画面にだけ使い、合否を出さずに証拠だけを貼る。静的解析・CLI・Viewer は Linux でも実測どおり動き、同梱デモの 70 画面地図は設計の到達点をよく示している。一方で ★189・貢献者 1 人・公開 5 週間で、iOS は macOS ランナーのコスト、AI レーンは確認スキップのエージェントを CI で走らせる構造を伴う。Expo で expo-router を使い、EAS のシミュレータビルドがすでにあるチームなら、まず agent_api_key 無しの決定論レーンだけで 1 本 PR を流し、flow を育ててから AI を足す順番が現実的だ。

参照ソース

aleqsio/screenmap(公式リポジトリ) — README・LICENSE・action/templates/plugins/screenmap/skills/screenmap/SKILL.md(2026-09-16 時点の main
action.yml — 入力 20 項目と出力 3 項目の定義
docs/scrmap-format.mddocs/diff-scrmap-format.md — 地図と差分バンドルの仕様
app.screenmap.dev — ホスト版 Map Viewer(?template=bluesky でデモ)