web-clone について
Web Cloneは、ライブURLから単一ページを再現し、ブラウザーで情報抽出と視覚確認を行う非推奨スキルです。呼び出しは禁止されており、ライブURLを使った視覚的な実装には `$visual-ralph` が案内されています。
主な機能
- 01非推奨・呼び出し禁止
- 02単一ページのURL複製
- 03ページ構造とスタイルの抽出
- 04主要なUI操作の実装
- 05ブラウザー操作にPlaywright MCPを使用
活用シーン
- 01新しい作業には使用しない
- 02ライブURLの実装には `$visual-ralph`
MCP 対応
ブラウザー自動化にはPlaywright MCPサーバーの設定が必要です。ページ移動、スナップショット、スクリーンショット、DOM調査に使用します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yeachan-heo/ oh-my-codex --skill web-cloneインストール後、Claude Code で/web-cloneを入力して読み込まれたか確認します。
詳細
Web Clone 非推奨
ハードデプリケーション(完全非推奨)です。このスキルを呼び出したり、ルーティングしたりしないでください。ライブURLを使ったビジュアル実装およびビジュアルQAのワークフローには、$visual-ralph を直接使用してください。
<Do_Not_Use_When>
- ユーザーがライブURLなしでスクリーンショットの参照資料しか持っていない場合 —
$visual-verdictを直接使用してください - ユーザーがサイトを変更、再デザイン、または「改善」したい場合 — 標準の実装フローを使用してください
- 対象が認証、決済フロー、またはバックエンドAPIの再現を必要とする場合 — v1の範囲外です
- 複数ページ / 複数ルートの深いクローンが必要な場合 — v1は単一ページの範囲のみを扱います </Do_Not_Use_When>
<Scope_Limits> v1の範囲: 指定されたURLの単一ページのクローンです。
含まれるもの:
- レイアウト構造(ヘッダー、ナビゲーション、コンテンツ領域、サイドバー、フッター)
- タイポグラフィ(フォントファミリー、サイズ、ウェイト、行の高さ)
- 色、余白、罫線、border-radius
- 主要なインタラクション: ナビゲーションリンク、ボタン、フォーム要素、ドロップダウン、モーダル、トグル
- 抽出されたレイアウトからのレスポンシブのヒント(flexbox / gridのパターン)
除外されるもの:
- バックエンドAPIとの連携またはデータ取得
- 認証フローまたは保護されたコンテンツ
- 動的/パーソナライズされたコンテンツ(ユーザー固有のデータ)
- 複数ページのクロールまたはルートグラフのクローン
- サードパーティ製ウィジェットの機能(地図、埋め込み、チャットウィジェット)
- 画像/アセットの複製(外部画像にはプレースホルダーを使用)
法的注意事項: 所有している、または複製について明示的な許可を得ているサイトのみをクローンしてください。著作権および商標を尊重してください。 </Scope_Limits>
ブラウザ自動化のために Playwright MCP サーバーが利用可能である必要があります。- ブラウザツールが見つからない場合、ユーザーに次の内容を案内してください:
Playwright MCP is required. Configure it: codex mcp add playwright npx "@playwright/mcp@latest" - 必要なツール:
browser_navigate、browser_snapshot、browser_take_screenshot、browser_evaluate、browser_wait_for。任意:browser_click、browser_network_requests。
<Tool_Usage>
- ブラウザツールが見つからない場合は、直ちに停止し、Playwright MCP を設定するようユーザーに案内してください。
- 構造の理解には
browser_snapshot(アクセシビリティツリー)を使用してください。スクリーンショットよりもトークン効率がはるかに高いです。 browser_take_screenshotはビジュアルな検証が必要な場合(パス1のベースライン、パス4の比較)にのみ使用してください。- DOM / スタイルの抽出には
browser_evaluateを使用してください。このスキルのスクリプトは、一切変更せずに記載どおりそのまま渡してください。 - ralph の内部で実行している場合は、イテレーション間のWebクローンの状態永続化に
state_write/state_readを使用してください。 - 単純な抽出にはCodexへの相談を省略してください。同じ問題で検証が繰り返し失敗する場合にのみ使用してください。 </Tool_Usage>
<State_Management> パイプラインが中断された場合に再開できるよう、抽出データと進捗データを永続化してください。
- パス1の完了後: 抽出サマリーを
.omx/state/{scope}/web-clone-extraction.jsonに書き込んでください。含める内容:target_url、extracted_atタイムスタンプscreenshot_path(target-full.pngへのパス)landmark_count(nav、main、footer、form要素の数)interactive_count(検出されたインタラクティブ要素の数)extraction_size_kb(DOM抽出データのおおよそのサイズ)
- パス4の検証のたび: 複合判定結果を
.omx/state/{scope}/web-clone-verdicts.jsonに追記してください。 - ralph の内部で実行している場合: ralph との互換性のため、複合判定結果の
visual部分も.omx/state/{scope}/ralph-progress.jsonに永続化してください。visual.scoreをトップレベルのscoreに、visual.verdictをトップレベルのverdictに対応させてください。 - 完了または失敗時:
completed_atまたはfailed_atタイムスタンプを付けて、最終ステータスを書き込んでください。 </State_Management>
<Context_Budget> パス1の抽出は非常に大きなデータを生成する可能性があります。次の上限を先回りして適用してください:
- DOM tree: シリアル化された JSON が約30KBを超える場合は、
depthパラメーターを 8 から 4 に減らして再抽出してください。トップレベルの構造に焦点を当てます。 - Accessibility snapshot: 約20KBを超える場合でも、複雑なページでは一般的なことです。完全なツリーを保持するのではなく、主要なランドマークを要約してください。
- Interactive elements: 上限は50要素です。それ以上ある場合は、表示されている要素(
isVisible: true)のみを残してください。 - Total extraction context: 合計で60KB未満を目指してください。超えた場合は、優先順位を スクリーンショット > アクセシビリティスナップショット > インタラクティブ要素 > DOMスタイル の順にしてください。
- Image tokens: フルページのスクリーンショットはコストが高くなります。Pass 1 でベースラインを1枚、Pass 4 で比較用を1枚撮影してください。特定の領域をデバッグする場合を除き、反復の間にスクリーンショットを撮らないでください。 </Context_Budget>
Pass 1 — Extract
対象ページの構造、スタイル、インタラクション、ビジュアルのベースラインを取得します。
- Navigate:
target_urlに対してbrowser_navigateを実行します。 - Wait for render: 遅延読み込みされるコンテンツを含めて完全に描画されるよう、適切な条件(ネットワークのアイドル状態、または5秒のタイムアウト)で
browser_wait_forを実行します。 - Accessibility snapshot:
browser_snapshotを実行します。セマンティックツリー(ロール、名前、値、インタラクティブな状態)を取得します。これが主要な構造上の参照になります。 - Full-page screenshot:
fullPage: trueを指定してbrowser_take_screenshotを実行し、参照用のベースラインとしてtarget-full.pngに保存します。 - DOM + computed styles: 以下のスクリプトで
browser_evaluateを実行します。このスクリプトは一字一句そのままコピーし、変更しないでください:(() => { const walk = (el, depth = 0) => { if (depth > 8 || !el.tagName) return null; const cs = window.getComputedStyle(el); return { tag: el.tagName.toLowerCase(), id: el.id || undefined, classes: [...el.classList].slice(0, 5), styles: { display: cs.display, position: cs.position, width: cs.width, height: cs.height, padding: cs.padding, margin: cs.margin, fontSize: cs.fontSize, fontFamily: cs.fontFamily, fontWeight: cs.fontWeight, lineHeight: cs.lineHeight, color: cs.color, backgroundColor: cs.backgroundColor, border: cs.border, borderRadius: cs.borderRadius, flexDirection: cs.flexDirection, justifyContent: cs.justifyContent, alignItems: cs.alignItems, gap: cs.gap, gridTemplateColumns: cs.gridTemplateColumns, }, text: el.childNodes.length === 1 && el.childNodes[0].nodeType === 3 ? el.textContent?.trim().slice(0, 100) : undefined, children: [...el.children].map(c => walk(c, depth + 1)).filter(Boolean), }; }; return walk(document.body); })() - Interactive elements:
browser_evaluateを実行して、操作可能なすべての要素を一覧化します。このスクリプトは一字一句そのままコピーし、変更しないでください:(() => { const results = []; document.querySelectorAll( 'button, a[href], input, select, textarea, [role="button"], ' + '[onclick], [aria-haspopup], [aria-expanded], details, dialog' ).forEach(el => { results.push({ tag: el.tagName.toLowerCase(), type: el.type || el.getAttribute('role') || 'interactive', text: (el.textContent || '').trim().slice(0, 80), href: el.href || undefined, ariaLabel: el.getAttribute('aria-label') || undefined, isVisible: el.offsetParent !== null, }); }); return results; })() - Network patterns(任意):
browser_network_requestsを実行し、参照用に XHR/fetch の呼び出しを記録します。バックエンドを再現しようとしないでください。
抽出結果はすべて作業メモリに保持し、Pass 2 で使用します。
Pass 2 — Build Plan
抽出結果を分析し、コンポーネント計画に分解します。
-
Identify page regions: DOM ツリーとアクセシビリティスナップショットから、主要なセクションを特定します:
- ナビゲーションバー / ヘッダー
- ヒーロー / バナーセクション
- メインコンテンツ領域
- サイドバー(存在する場合)
- フッター
- オーバーレイ要素(モーダル、ドロワー)
-
Map components: 各領域について以下を定義します:
- コンポーネント名と役割
- 主要なスタイルプロパティ(computed styles から)
- コンテンツの概要(見出し、テキスト、画像)
- ネストされている場合の子コンポーネント
-
Create interaction map: インタラクティブ要素の一覧から以下を作成します:
- ナビゲーションリンク →
hrefを持つアンカータグ - フォーム要素 → 入力、ラベル、バリデーションを備えた適切な
<form> - ボタン → クリックハンドラー(トグル、送信、ナビゲーション)
- ドロップダウン / モーダル → トランジションを伴う表示・非表示の切り替え
- アコーディオン / タブ → 状態に基づく表示制御
- ナビゲーションリンク →
-
Extract design tokens: 繰り返し使われる値を特定します:
- カラーパレット(プライマリ、セカンダリ、背景、テキストの色)
- フォントスタック(ファミリー、サイズのスケール、ウェイトのスケール)
- スペーシングのスケール(padding / margin のパターン)
- border-radius の値
-
Define file structure:
{output_dir}/ ├── index.html (or App.tsx / App.vue) ├── styles/ │ ├── globals.css (reset + tokens) │ └── components.css (or scoped styles) ├── scripts/ │ └── interactions.js (toggle, modal, dropdown logic) └── assets/ (placeholder images)tech_stackが指定されている場合はそれに合わせて調整します(React コンポーネント、Vue SFC など)。
パス 3 — クローンを生成
計画に基づいてクローンを実装します。コンポーネントごとに作業します。
- スキャフォールド: ディレクトリ構造とベースファイルを作成します。
- デザイントークンを最初に: 抽出したトークンから CSS カスタムプロパティまたは Tailwind 設定を実装します。
- レイアウトシェル: 元のページの flexbox/grid 構造に合わせて、ページレベルのレイアウトを構築します。
- コンポーネント: 各リージョンを上から順に実装します:
- 抽出した DOM 構造に合わせます(セマンティックタグ、ランドマークロール)
- 計算済みスタイルを適用します。レイアウトプロパティを優先し、次にタイポグラフィ、最後に装飾に適用します
- 抽出した実際のテキストコンテンツを使用します。外部画像にはプレースホルダーの
<img>を使用します
- インタラクション: 検出した動作を結線します:
- ナビゲーション: 動作する
<a>タグ(#アンカーまたは v1 用のスタブへのリンク) - フォーム:
<label>、入力タイプ、プレースホルダーテキストを備えた適切な構造 - トグル: ドロップダウン、モーダル、アコーディオンのための JavaScript
- ホバー/フォーカス状態: 元の動作に合わせた CSS トランジション
- ナビゲーション: 動作する
- レスポンシブ対応: 元のページがレスポンシブブレークポイントを使用している場合(計算済みスタイルのメディアクエリ、またはビューポートの挙動から検出可能)、基本的なレスポンシブルールを追加します。
パス 4 — 検証
クローンを元のページと 3 つの観点で比較します。
-
クローンを配信する: 生成したプロジェクトのローカルサーバーを起動します:
npx serve {output_dir} -l 3456 --no-clipboardnpx serveが利用できない場合は、次のコマンドにフォールバックします:python3 -m http.server 3456 -d {output_dir}。 クローンにはhttp://localhost:3456でアクセスできます。 -
ビジュアル検証:
- Playwright で
browser_navigateを使ってクローンの URL に移動します。 - クローンのフルページスクリーンショットを撮影します。
reference_images=["target-full.png"]、generated_screenshot="clone-full.png"、category_hint="web-clone"を指定して$visual-verdictを実行します。- ビジュアル部分の判定結果は、下記の総合判定に直接反映されます。
- ビジュアルの合格基準: スコア >= 85。
- Playwright で
-
構造検証: ランドマーク数を比較します:
- 元のページとクローンの両方で
<nav>、<main>、<footer>、<form>、<button>、<a>の数を数えます。 - 主要なランドマークがすべて存在する場合に構造検証は合格となります(ランドマークが欠けている場合は不合格)。
- 元のページとクローンの両方で
-
機能の抜き取り確認: Playwright で検出した インタラクションを 2〜3 件テストします:
- ナビゲーションリンクをクリックし、URL の変化またはスクロールの動作を確認します
- ドロップダウンやモーダルを切り替え、表示状態の変化を確認します
- フォームフィールドを操作し、入力を受け付けることを確認します
browser_clickとbrowser_snapshotを使って状態の変化を確認します。
-
総合判定を出力する:
{
"visual": {
"score": 82,
"verdict": "revise",
"category_match": true,
"differences": ["Header spacing tighter than original"],
"suggestions": ["Increase nav gap to 24px"]
},
"functional": {
"tested": 3,
"passed": 2,
"failures": ["Dropdown does not open on click"]
},
"structure": {
"landmark_match": true,
"missing": [],
"extra": []
},
"overall_verdict": "revise",
"priority_fixes": [
"Fix dropdown toggle interaction",
"Increase header nav spacing"
]
}
Pass 5 — 反復改善
影響の大きい問題を修正し、再検証します。
- 修正の優先順位付け: 影響度の順に、レイアウト > インタラクション > 余白 > タイポグラフィ > 色の順で対応します。
- 対象を絞った編集:
priority_fixesに挙げられた問題のみを修正します。動作しているコードはリファクタリングしないでください。 - 再検証: Pass 4 を繰り返します。
- ループ:
overall_verdictがpassになるか、最大 5 回 の反復に達するまで続けます。 - 最終レポート: 正常にクローンできた内容、残っている差異、再現できなかった要素をまとめます。
<Output_Contract> 各検証パスの後に、複合的な Web クローン判定の JSON を出力します:
{
"visual": {
"score": 0,
"verdict": "revise",
"category_match": false,
"differences": ["..."],
"suggestions": ["..."],
"reasoning": "short explanation"
},
"functional": {
"tested": 0,
"passed": 0,
"failures": ["..."]
},
"structure": {
"landmark_match": false,
"missing": ["..."],
"extra": ["..."]
},
"overall_verdict": "revise",
"priority_fixes": ["..."]
}
ルール:
visualは$visual-verdictのVisualVerdict形式に従います。functional.tested/passedは件数です。failuresには具体的なインタラクションの失敗を列挙します。structure.landmark_matchは、主要な HTML ランドマーク(nav、main、footer、form)がすべて存在する場合にtrueとします。overall_verdict: visual.score >= 85 かつ functional.failures が空かつ structure.landmark_match が true の場合にpassとします。priority_fixes: 影響度順に並べ、次の反復の作業を決定します。 </Output_Contract>
<Iteration_Thresholds>
- ビジュアル合格: score >= 85
- 機能合格: テストしたインタラクションで失敗がゼロ
- 構造合格: 主要なランドマークがすべて存在
- 総合合格: 3 つの観点がすべて合格
- 最大反復回数: 5 回(閾値に達しない場合は、達成できた最良の結果を報告) </Iteration_Thresholds>
<Error_Handling>
- Playwright MCP が利用できない場合: 処理を停止し、ユーザーに設定を案内します。ブラウザツールなしでクローンを試みないでください。
- ページの読み込みに失敗する場合: URL と HTTP ステータスを報告します。ユーザーに URL へアクセスできるか確認するよう提案します。
- browser_evaluate が空の結果を返す場合: ページがクライアントサイドで重くレンダリングされている可能性があります。
browser_wait_forでタイムアウトを延長して待機し、1 回だけ再試行します。 - 3 回の反復後もビジュアルスコアが閾値を下回る場合: 現在の状態をベストエフォートの結果として報告します。未解決の差異を一覧にしてユーザーに提示します。
- 抽出データがコンテキストに対して大きすぎる場合: 深さ 6 を超える DOM の深い階層を切り詰めます。トップレベルの構造に焦点を当て、入れ子の詳細は反復での修正に回します。 </Error_Handling>
Pass 1: HN に移動します。抽出結果: テーブルベースのレイアウト、オレンジ色(#ff6600)のナビゲーションバー、リンク・ポイント・コメント付きのストーリー一覧、フッター。スクリーンショットを保存します。
Pass 2: 領域: ナビゲーションバー(ロゴ + リンク)、ストーリーテーブル(title + meta の 30 行)、フッター。トークン: オレンジ #ff6600、グレー #828282、Verdana フォント、ベース 10pt。インタラクションマップ: ストーリーリンク(外部)、コメントリンク、「more」のページネーション。
Pass 3: HN 風のテーブルレイアウトを持つ index.html を生成し、抽出した色とフォントに合わせた CSS を適用し、ストーリーには動作する <a> タグを配置します。
Pass 4: ビジュアルスコア=78(フォントサイズがずれている、ストーリー間の余白が狭すぎる)。機能 2/2(リンクは動作)。構造一致=true。
Pass 5 反復 1: フォントを Verdana 10pt に修正し、行の余白を広げる → スコア=88。機能 2/2。構造一致。→ overall_verdict: pass。完了。
<Final_Checklist>
- Pass 1 の抽出を完了し、要約した(スクリーンショット + アクセシビリティツリー + DOM スタイル + インタラクション)
- Pass 2 のコンポーネント計画をファイル構成とともに作成した
- Pass 3 のクローンを生成し、ファイルを
output_dirに書き込んだ - クローンがエラーなくローカルで配信される
- Pass 4 の複合判定を 3 つの観点すべてについて出力した
-
overall_verdictがpass、または最大 5 回の反復に達し、ベストエフォートのレポートを作成した - ralph 実行中の場合: ビジュアル判定を
ralph-progress.jsonに保存した - 抽出の要約を
web-clone-extraction.jsonに保存した </Final_Checklist>