Communitygithub.com

braxtonROSE4/clone-any-website

Ingénierie inverse et clonage pixel-parfait de n'importe quel site web en direct, une simple page d'atterrissage ou un site entier.

Qu'est-ce que clone-any-website ?

Cette compétence permet de cloner un site web en direct de manière automatisée et précise. Elle capture le DOM réel, les tokens de design CSS, les polices, les actifs et les données de configuration JavaScript compilées à partir de la page active. Ensuite, elle reconstruit le site sous forme de miroir HTML brut (pour les sites à export statique comme Webflow/Framer) ou de composants Next.js (pour les sites basés sur des frameworks JS), en reproduisant les animations de défilement et les interactions avec état. La fidélité du clone est prouvée par un benchmark noté qui compare les géométries, la couverture des polices et des animations, et les captures d'écran. La compétence s'appuie sur le runtime `ego-browser` et utilise des espaces de tâches isolés pour permettre à l'utilisateur de comparer visuellement l'original et le clone sans conflit. Elle est déclenchée par des expressions comme "clone ce site", "inverse cette page d'atterrissage", "reconstruis cette page pixel-parfait", "copie le design de ce site", ou même "复刻网站".

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add braxtonROSE4/clone-any-website

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

clone-website

Clonez n'importe quel site web en direct 1:1 (mise en page, typographie, actifs, animations de défilement, interactions avec état) à partir de sa seule URL, et prouvez la fidélité avec un benchmark noté au lieu de simplement le prétendre.

Dépendance d'exécution et amorçage

Chaque opération sur une page en direct s'exécute sous la forme ego-browser nodejs <<'EOF' ... EOF dans Bash, en utilisant les facades préchargées page, browser et taskSpaces du runtime ego lite.

Le runtime n'est pas interchangeable, car la méthode en dépend de trois manières. La boucle au niveau pixel a besoin de la même sonde tournant dans le même runtime sur l'original et le clone, sinon les chiffres de différence ne veulent rien dire. La boucle de correction a besoin d'espaces de tâches : l'agent sonde dans ses propres espaces isolés pendant que l'utilisateur navigue simultanément sur l'original et le clone dans les leurs, peut prendre en charge n'importe quel espace pour une vérification visuelle, et le rendre, sans que les deux parties ne se disputent le navigateur. Et chaque script de sonde et mise en garde de cette compétence est calibré sur le comportement mesuré de ce runtime. C'est pourquoi un runtime manquant redirige vers l'installation d'ego lite plutôt que vers un substitut.

  1. Si la compétence ego-browser est installée dans cet environnement, chargez-la avant la Phase 0 et respectez toutes ses règles de correction. Cette compétence est une surcouche ; en cas de conflit, les règles de la compétence ego-browser prévalent pour la mécanique du navigateur, les règles de cette compétence prévalent pour le flux de travail de clonage.
  2. Si seule cette compétence est présente, elle fonctionne de manière autonome. Le contrat est l'interface en ligne de commande ego-browser (fournie par l'application ego lite). Dans un environnement nouveau ou inconnu, vérifiez une fois avec command -v ego-browser avant la Phase 0.
  3. Quand l'interface en ligne de commande est manquante, n'improvisez pas. Arrêtez-vous, lisez references/install.md, et guidez l'utilisateur dans l'installation d'ego lite (téléchargement via scripts/install-ego-browser.sh, puis l'utilisateur termine l'intégration dans l'interface graphique de l'application, qui enregistre l'interface en ligne de commande ; attendez sa confirmation, vérifiez avec command -v ego-browser, puis reprenez). Ne recourez jamais à Playwright, un autre navigateur, WebFetch ou toute autre voie d'automatisation ; les résultats des sondes provenant d'un runtime différent ne sont pas comparables et les mises en garde environnementales de la compétence ne tiennent plus.
  4. Après la première commande réussie, supposez que le runtime est prêt pour le reste de la session ; n'enquêtez que lorsqu'une commande réelle génère une erreur.

La méthode en une phrase : traitez le site en direct comme une vérité terrain que vous pouvez interroger, pas une image que vous évaluez à l'œil. Extrayez le vrai DOM, les vrais tokens CSS, les vraies keyframes, les vraies polices et les vraies données de configuration de la page en cours, reconstruisez à partir de ces faits, puis exécutez la même sonde de mesure sur l'original et le clone et différenciez les nombres jusqu'à ce qu'ils convergent.

Plan des fichiers : scripts de sonde copier-coller dans references/probes.md ; protocole de benchmark dans references/eval.md ; guide d'installation d'ego lite dans references/install.md (installateur vendu dans scripts/install-ego-browser.sh) ; modèle de conception d'outil/fonction dans references/tool-design-template.md ; implémentations canoniques des outils dans tools/ (enregistrées dans tools/manifest.json) ; évaluateur dans scripts/eval_score.py ; découpeur de captures d'écran dans scripts/slice.py.

Modèle d'exécution

  • Un espace de tâche pour tout le travail. taskSpaces.useOrCreate('reverse <site>') en haut de chaque heredoc ; le même nom pour toutes les invocations. Ne le completez pas entre les sondes ; ne le complétez avec { keep: false } qu'après acceptation finale. Si l'utilisateur prend le contrôle de l'espace, c'est un arrêt brutal : signalez l'état et attendez, ne faites jamais takeOver de votre propre chef.
  • Regroupez par phase, pas par action. Un heredoc doit terminer une sonde entière (ouvrir, geler les vidéos, mesurer à chaque profondeur, imprimer). Ne divisez les invocations qu'aux véritables limites : travail shell entre les travaux du navigateur (téléchargements curl, greps, Python), confirmation de l'utilisateur, ou inspection visuelle d'une image sauvegardée.
  • Artefacts dans des fichiers, résumés sur stdout. Chaque sonde écrit son JSON/HTML complet dans reference/, et console.log uniquement les comptages et les chemins. Ne déversez jamais un DOM complet, un fichier CSS ou une table de géométrie dans la conversation.
  • Chaque script de sonde est autonome. Sélection de l'espace de tâche et browser.openOrReuseTab(url, { wait: true, timeout: 30000 }) en haut, à chaque fois ; l'état du runtime ne survit pas d'un heredoc à l'autre.
  • Chemins absolus uniquement dans les heredocs. Le processus nodejs d'ego n'hérite pas du répertoire de travail courant du shell ; les chemins relatifs fs.writeFileSync génèrent une erreur ENOENT. Définissez une constante ROOT en haut de chaque script.

Démarrage rapide : amorçage de la reconnaissance

Une seule invocation capture la vérité terrain à partir de laquelle commence une reconstruction. Adaptez l'URL et les chemins ; la forme composite est le point important.

mkdir -p reference/{dom,css,js,screenshots} && ego-browser nodejs <<'EOF'
const fs = await import('node:fs')
const ROOT = '/abs/path/to/project'  // le nodejs d'ego n'hérite pas du cwd du shell
await taskSpaces.useOrCreate('reverse example.com')
await browser.openOrReuseTab('https://example.com', { wait: true, timeout: 30000 })

// GELER les vidéos en lecture automatique EN PREMIER : elles saturent le thread de rendu et les évaluations ultérieures expirent.
await page.evaluate(() => {
  const freeze = () => document.querySelectorAll('video').forEach(v => v.pause())
  freeze(); setInterval(freeze, 400)
})

// PROTÉGER la zone d'affichage : w/h de 0 produit silencieusement des captures vides et des rects inutilisables.
const info = await page.info()
if (!info.w || !info.h) throw new Error('La zone d\'affichage est 0, restaurez le vrai onglet avant la reconnaissance : ' + JSON.stringify(info))

const facts = await page.evaluate(() => ({
  html: document.documentElement.outerHTML,
  outline: Array.from(document.querySelectorAll('body section, body header, body footer, body main > div'))
    .map(el => { const r = el.getBoundingClientRect(); return {
      tag: el.tagName, cls: (el.className?.toString() || '').slice(0, 120),
      y: Math.round(r.top + scrollY), h: Math.round(r.height), w: Math.round(r.width) } })
    .filter(s => s.h > 40).sort((a, b) => a.y - b.y),
  cssUrls: performance.getEntriesByType('resource').map(e => e.name).filter(u => /\.css(\?|$)/.test(u)),
  fontFiles: performance.getEntriesByType('resource').map(e => e.name).filter(u => /\.(woff2?|ttf|otf)(\?|$)/.test(u)),
  jsChunks: performance.getEntriesByType('resource').map(e => e.name).filter(u => /\.js(\?|$)/.test(u)),
}))
fs.writeFileSync(ROOT + '/reference/dom/full-page.html', facts.html)
fs.writeFileSync(ROOT + '/reference/dom/outline.json', JSON.stringify(facts.outline, null, 2))
fs.writeFileSync(ROOT + '/reference/urls.json', JSON.stringify({ css: facts.cssUrls, fonts: facts.fontFiles, js: facts.jsChunks }, null, 2))

await page.evaluate(() => scrollTo(0, 0))
await page.waitForTimeout(800)
await page.screenshot({ path: ROOT + '/reference/screenshots/original-full.png', fullPage: true })

console.log(JSON.stringify({ sections: facts.outline.length, cssFiles: facts.cssUrls.length,
  fontFiles: facts.fontFiles.length, jsChunks: facts.jsChunks.length, htmlKB: Math.round(facts.html.length / 1024) }))
EOF

Ensuite, travail shell : téléchargez chaque URL CSS/police/JS dans reference/, et découpez la capture d'écran par section avec python3 scripts/slice.py reference/screenshots/original-full.png reference/dom/outline.json reference/screenshots/original/.

Flux de travail

Phase 0, configuration

Répertoire du projet avec reference/{dom,css,js,screenshots}/ pour les artefacts de reconnaissance (jamais livrés, exclus de tout push de dépôt) ; serveur de développement sur un port fixe en arrière-plan pour que l'utilisateur puisse examiner en continu. Construisez Next.js manuellement lorsque public/ contient déjà les actifs téléchargés ; create-next-app écrase le répertoire.

Avant de servir, lsof -iTCP:<port> -sTCP:LISTEN (un serveur restant d'un précédent travail de clonage répond de manière convaincante sur votre port), et avant toute sonde du clone, vérifiez que document.title correspond ; une réponse curl plausible ne prouve pas que l'onglet du navigateur affiche votre page.

Phase 1, capture statique

Exécutez l'amorçage de la reconnaissance, puis comblez les lacunes (scripts complets dans references/probes.md) :

  • Tokens de design : grep sur le CSS téléchargé pour --brand-*, --font-size-*, --color-*, échelles d'espacement. Lisez les tailles et les couleurs à partir des tokens, ne les estimez jamais à partir de captures d'écran. Un token que vous attendez mais ne trouvez pas se trouve généralement dans un fichier CSS différent ; grepez-les tous avant de conclure qu'il n'existe pas.
  • Polices : @font-face est souvent injecté par JS au moment de l'exécution, donc greper le HTML sauvegardé ne prouve rien. Sur la page en direct, combinez trois sources : performance.getEntriesByType('resource') filtré sur les extensions de police, document.fonts pour la famille/poids/statut, et une parcours de document.styleSheets pour CSSFontFaceRule afin de mapper la famille au poids à la source. Téléchargez avec curl --globoff (les noms de fichiers entre crochets cassent le globbing de curl). Auto-hébergez dans public/fonts/.
  • Actifs : énumérez img[src], srcset, video src, les URL des images d'arrière-plan, les SVG inline. Décodez les chemins /_next/image?url=... vers les chemins d'origine. Miroir des chemins d'origine sous public/. Extrayez les SVG inline textuellement, ne les redessinez pas.
  • Contenu fictif/UI produit : les tableaux de bord, éditeurs et panneaux de cartes reçoivent leur contenu d'objets de configuration dans des chunks JS compilés. Grepez les chunks téléchargés pour une chaîne visible ; l'objet de configuration à côté de la correspondance contient l'ensemble complet des données (toutes les variantes de carrousel, toutes les lignes), bien plus fiable que des captures d'écran qui manquent l'état chargé paresseusement.

Les captures d'écran de référence suivent les règles de Correction ci-dessous : une capture fullPage au défilement 0 découpée par section avec scripts/slice.py, vidéos gelées en premier, barres avec état réinitialisées en premier.

Phase 2, inversion du mouvement et de l'interaction

Travaillez du plus simple au plus coûteux ; arrêtez-vous dès que l'effet est expliqué :

  1. Analyse statique CSS (gratuite, déjà téléchargée) : grep pour animation-timeline|view-timeline|scroll-timeline|@keyframes. Une correspondance sur animation-timeline: view() signifie des animations CSS natives pilotées par le défilement : copiez les keyframes et les plages textuellement, pas besoin de JS.
  2. Inventaire d'exécution : document.getAnimations({ subtree: true }), la vérité terrain. Par animation : effect.getKeyframes(), timeline.constructor.name (ViewTimeline est piloté par le défilement, DocumentTimeline est piloté par le temps), rangeStart/rangeEnd, élément cible. Les paramètres par élément comme --parallax-from: 6% vivent sur les styles inline.
  3. MutationObserver plus défilement réel de la molette pour les effets déclenchés par JS : attributeFilter: ['class','style','src'], faites défiler la page entière avec page.mouse.wheel, enregistrez les mutations. Zéro mutation de classe sur un défilement complet prouve que les effets sont purement CSS, ce qui simplifie massivement la reconstruction.
  4. Échantillonnage d'état multi-profondeur pour les machines à états fixes/collantes (barres de saisie flottantes, en-têtes rétrécissables) : prenez un instantané de l'ensemble du sous-arbre de l'élément (rect, opacité, rayon, arrière-plan, ombre de chaque enfant) à plusieurs profondeurs de défilement dans les deux directions, puis différenciez les instantanés entre les états. Les vérifications basées uniquement sur la position manquent les changements de forme ; la transition d'état est l'interaction.

Phase 3, reconstruction

Choisissez la voie de reconstruction d'abord par type de site. Les sites à export statique (Webflow, Framer : attribut data-wf-site, CDN website-files.com/framerusercontent.com) se clonent avec la plus haute fidélité sous forme de miroir HTML brut, pas une reconstruction par composants : téléchargez le HTML serveur pré-hydratation comme squelette (le DOM capturé en direct est déjà hydraté — les clones de carrousel insérés, les caractères SplitText divisés — et se double-initialise lorsque les scripts sont réexécutés), réécrivez chaque URL CDN vers des copies auto-hébergées, supprimez les scripts de suivi/consentement, conservez le runtime fonctionnel (JS du site, GSAP, bibliothèques de carrousel) auto-hébergé. Quatre règles pour cette voie :

  • Les actifs peuvent s'étendre sur plusieurs dossiers CDN (bibliothèques de médias partagées) ; réécrivez toute cdn-host/<dossier>/<fichier> par nom de base, pas par le préfixe du dossier principal du site.
  • Supprimez integrity/crossorigin de chaque balise de ressource réécrite : le hachage SRI a été calculé sur les octets d'origine, et le navigateur rejette le fichier modifié silencieusement — le symptôme est une page sans style (la hauteur du document explose, document.fonts vide) sans erreur visible par la sonde.
  • La suppression des scripts de suivi par liste de blocage de mots-clés tue les scripts fonctionnels qui mentionnent simplement une API d'analyse avec repli gracieux (un script en-tête propriétaire du changement de texte de détection du système d'exploitation des boutons de téléchargement référençait PostHog) ; auditez la liste des scripts supprimés et mettez sur liste blanche ceux qui sont fonctionnels.
  • Les scripts d'expérimentation non déterministes (Math.random() pour les répartitions de trafic, canaris de redirection) restent supprimés même s'ils sont fonctionnels ; ils rendent les exécutions de benchmark non reproductibles.

Pour les sites basés sur un framework JS (Next.js, Remix — une divergence d'hydratation rend un miroir irréalisable), reconstruisez par composants :

  • Un composant par section de premier niveau, nommé d'après le contenu métier de la section.
  • Les tokens de la Phase 1 vont dans globals.css tels quels ; les polices sont auto-hébergées avec la véritable correspondance des poids.
  • Miroir de la stratégie de montage du DOM de l'original, pas seulement ce qui est visible : les originaux gardent toutes les images de carrousel, les variantes de rotation et les murs d'avatars montés (masqués), tandis qu'un clone à rendu conditionnel affiche les mêmes pixels avec une fraction des éléments, puis échoue aux comptages de médias par section dans le benchmark. Faites correspondre les comptages d'éléments par section avec le DOM de l'original, pas avec sa capture d'écran.
  • Repliquez la hiérarchie de conteneurs de l'original, pas seulement ses visuels. sticky se limite à son parent : une navigation qui doit survivre jusqu'au pied de page a besoin d'un parent qui s'étend sur toute la page ; un panneau à dévoilement par rideau a besoin d'un parent qui commence là où l'effet commence, sinon il s'épingle dès le premier écran. Lorsqu'une section se casse après une restructuration, suspectez d'abord un changement de portée sticky ou de contexte d'empilement.
  • Les éléments fixes par rapport à la zone d'affichage (barres de chat flottantes) sont des conteneurs fixes au niveau du corps dans les originaux bien construits : utilisez un portail React, jamais absolute à l'intérieur d'une section.
  • Les en-têtes rétrécissables qui modifient la hauteur du document luttent contre l'ancrage de défilement de Chrome et saccadent. Architecture robuste : un espace réservé de hauteur constante dans le flux (la hauteur du document ne change jamais) avec l'en-tête interne passant à fixed plus une animation de glissement une fois complètement défilé. Si la hauteur doit s'animer, ajoutez overflow-anchor: none et une hystérésis entre les seuils de rétrécissement et d'expansion.
  • Bords décoratifs dentelés (bordures en coquille Saint-Jacques/timbre) : SVG à face pleine orienté vers l'espace avec un chevauchement de 1px dans le voisin

Skills associés