--- name: web-clone description: "[OMX] URL-driven website cloning with visual + functional verification" --- Clone a target website from its URL, replicating both visual appearance and core interactive functionality. Uses Playwright MCP for live page extraction, LLM-driven code generation, and iterative verification with `$visual-verdict` for visual scoring. - User provides a target URL and wants the site replicated as working code - User says "clone site", "clone website", "copy webpage", or "web-clone" - Task requires both visual fidelity AND functional parity with the original - Reference is a live URL (not a static screenshot — use `$visual-verdict` for screenshot-only tasks) - User only has screenshot references without a live URL — use `$visual-verdict` directly - User wants to modify, redesign, or "improve" the site — use standard implementation flow - Target requires authentication, payment flows, or backend API parity — out of scope for v1 - Multi-page / multi-route deep cloning — v1 handles single-page scope only **v1 scope**: Single page clone of the provided URL. Included: - Layout structure (header, nav, content areas, sidebar, footer) - Typography (font families, sizes, weights, line heights) - Colors, spacing, borders, border-radius - Core interactions: navigation links, buttons, form elements, dropdowns, modals, toggles - Responsive hints from the extracted layout (flexbox/grid patterns) Excluded: - Backend API integration or data fetching - Authentication flows or protected content - Dynamic/personalized content (user-specific data) - Multi-page crawling or route graph cloning - Third-party widget functionality (maps, embeds, chat widgets) - Image/asset replication (use placeholders for external images) **Legal notice**: Only clone sites you own or have explicit permission to replicate. Respect copyright and trademarks. Playwright MCP server must be available for browser automation. 1. Before first tool use, call `ToolSearch("browser")` or `ToolSearch("playwright")` to discover available browser tools. 2. If no browser tools are found, instruct the user: ``` Playwright MCP is required. Configure it: codex mcp add playwright npx "@playwright/mcp@latest" ``` 3. Required tools: `browser_navigate`, `browser_snapshot`, `browser_take_screenshot`, `browser_evaluate`, `browser_wait_for`. Optional: `browser_click`, `browser_network_requests`. - `target_url` (required): The URL to clone - `output_dir` (optional, default: current working directory): Where to generate the clone project - `tech_stack` (optional, inferred from project context): HTML/CSS/JS, React, Vue, Svelte, etc. - Before first MCP tool use, call `ToolSearch("browser")` or `ToolSearch("playwright")` to discover deferred Playwright MCP tools. - If no browser tools are found, stop immediately and instruct the user to configure Playwright MCP. - Use `browser_snapshot` (accessibility tree) for structural understanding — it is far more token-efficient than screenshots. - Use `browser_take_screenshot` only when visual verification is needed (Pass 1 baseline, Pass 4 comparison). - Use `browser_evaluate` for DOM/style extraction — pass the scripts from this skill EXACTLY as written (do not modify them). - If running within ralph, use `state_write` / `state_read` for web-clone state persistence between iterations. - Skip Codex consultation for straightforward extraction; use it only if verification repeatedly fails on the same issue. Persist extraction and progress data so the pipeline can resume if interrupted. - **After Pass 1 completes**: Write extraction summary to `.omx/state/{scope}/web-clone-extraction.json` containing: - `target_url`, `extracted_at` timestamp - `screenshot_path` (path to `target-full.png`) - `landmark_count` (number of nav, main, footer, form elements) - `interactive_count` (number of detected interactive elements) - `extraction_size_kb` (approximate size of DOM extraction data) - **After each Pass 4 verification**: Append the composite verdict to `.omx/state/{scope}/web-clone-verdicts.json`. - **When running within ralph**: Also persist the `visual` portion of the composite verdict to `.omx/state/{scope}/ralph-progress.json` for ralph compatibility, mapping `visual.score` → top-level `score` and `visual.verdict` → top-level `verdict`. - **On completion or failure**: Write final status with `completed_at` or `failed_at` timestamp. Pass 1 extraction can produce very large data. Apply these limits proactively: - **DOM tree**: If the serialized JSON exceeds ~30KB, reduce `depth` parameter from 8 to 4 and re-extract. Focus on top-level structure. - **Accessibility snapshot**: If it exceeds ~20KB, this is normal for complex pages. Summarize key landmarks rather than keeping the full tree. - **Interactive elements**: Cap at 50 elements. If more exist, keep only visible ones (`isVisible: true`). - **Total extraction context**: Aim for under 60KB combined. If exceeded, prioritize: screenshot > accessibility snapshot > interactive elements > DOM styles. - **Image tokens**: Full-page screenshots are expensive. Take one baseline in Pass 1 and one comparison in Pass 4. Do not take screenshots between iterations unless debugging a specific region. ## Pass 1 — Extract Capture the target page's structure, styles, interactions, and visual baseline. 1. **Navigate**: `browser_navigate` to `target_url`. 2. **Wait for render**: `browser_wait_for` with appropriate condition (network idle or timeout of 5s) to ensure full render including lazy-loaded content. 3. **Accessibility snapshot**: `browser_snapshot` — captures the semantic tree (roles, names, values, interactive states). This is your primary structural reference. 4. **Full-page screenshot**: `browser_take_screenshot` with `fullPage: true` — save as reference baseline `target-full.png`. 5. **DOM + computed styles**: `browser_evaluate` with the following script. **COPY THIS SCRIPT EXACTLY — do not modify it**: ```javascript (() => { 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); })() ``` 6. **Interactive elements**: `browser_evaluate` to catalog all interactable elements. **COPY THIS SCRIPT EXACTLY — do not modify it**: ```javascript (() => { 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; })() ``` 7. **Network patterns** (optional): `browser_network_requests` — note XHR/fetch calls for reference. Do not attempt to replicate backends. Keep all extraction results in working memory for Pass 2. ## Pass 2 — Build Plan Analyze extraction results and decompose into a component plan. 1. **Identify page regions**: From DOM tree + accessibility snapshot, identify major sections: - Navigation bar / header - Hero / banner section - Main content area(s) - Sidebar (if present) - Footer - Overlay elements (modals, drawers) 2. **Map components**: For each region, define: - Component name and responsibility - Key style properties (from computed styles) - Content summary (headings, text, images) - Child components if nested 3. **Create interaction map**: From interactive elements list: - Navigation links → anchor tags with `href` - Form elements → proper `
` with inputs, labels, validation - Buttons → click handlers (toggle, submit, navigate) - Dropdowns/modals → show/hide toggle with transitions - Accordions/tabs → state-based visibility 4. **Extract design tokens**: Identify recurring values: - Color palette (primary, secondary, background, text colors) - Font stack (families, size scale, weight scale) - Spacing scale (padding/margin patterns) - Border radius values 5. **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) ``` Adapt to `tech_stack` if specified (React components, Vue SFCs, etc.). ## Pass 3 — Generate Clone Implement the clone from the plan. Work component-by-component. 1. **Scaffold**: Create the directory structure and base files. 2. **Design tokens first**: Implement CSS custom properties or Tailwind config from extracted tokens. 3. **Layout shell**: Build the page-level layout matching the original's flexbox/grid structure. 4. **Components**: Implement each region top-down: - Match DOM structure from extraction (semantic tags, landmark roles) - Apply computed styles — prioritize layout properties, then typography, then decorative - Use actual extracted text content; use placeholder `` for external images 5. **Interactions**: Wire up detected behaviors: - Navigation: working `` tags (to `#` anchors or stubs for v1) - Forms: proper structure with `