本文へ移動
cccskills
無料GitHub で公開

clone-website

Rebuilds an existing website as a clean new codebase that looks and behaves like the original: captures pages, breakpoints, design tokens, assets and interaction states with a browser tool, plans the build, rebuilds section by section and proves each one against the capture with a pixel diff. Use when the user says "clone this website", "rebuild my site in Next.js or Astro", "recreate this page pixel-perfect", "move my Webflow or WordPress site to code" or "copy this layout". Full copies are only for sites the user owns or is authorized to copy; for anyone else's site it borrows the layout ideas and never the brand, content or sign-in pages.

インストール方法を見る

含まれるファイル(2)

  • SKILL.md21.8 KB
  • _scores.json1.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Clone Website

Overview

Cloning a website here means rebuilding it: fresh code in a modern stack that renders the same pages at the same widths and reacts the same way to a hover, a click or a scroll. Nothing is lifted out of the old site's bundles. The agent watches the live site through a browser tool, writes what it measures into a capture/ folder, builds from that record, and shows the match with screenshots compared pixel by pixel.

Five stages: settle the right to copy, capture, plan, rebuild one section at a time with a diff after each, hand over a parity report. A full copy is for a site the user owns or has the owner's permission to copy. Someone else's site can only serve as a layout reference for the user's own brand. The skill does not produce look-alikes of another brand.

Instructions

1. Settle rights and scope before the browser opens

Ask whatever the conversation has not answered yet:

  1. Whose site is it: yours, a client's (who agreed, and how), or another company's?
  2. What is the rebuild for: a move to a new stack, a base for a redesign, or a look wanted for a different business?
  3. Which pages are in scope, and where will the content live afterwards (in the code, Markdown, a CMS)?
  4. Is there a project to build into, or is the stack open?

Choose a lane and record it, with the user's answer, at the top of capture/plan.md:

LaneWhenWhat is reproduced
Faithful rebuildThe user owns the site, or its owner authorized the copyLayout, styling, behaviour, and the owner's own text, images and logo
Reference buildThe site is somebody else'sSection order, grid, spacing rhythm and interaction ideas. Name, logo, copy, images, palette and typefaces are the user's own
DeclineAnother brand's sign-in, checkout, payment or account-recovery page; a wish to keep that brand's name or logo; a copy meant to be mistaken for the originalNothing. Explain, and offer a reference build

Ownership cannot be verified from a chat, but a story that does not fit can be noticed. An owner can reach the CMS, the hosting or the DNS, or can name who does. Warning signs: the target is the login of a bank, shop, mail or social service the user does not run; the new domain is spelled almost like the target's; "visitors should not notice the difference". Any of these moves the request to Decline, whatever purpose is given (a demo, a test, staff training).

Manners while capturing: a handful of page loads, not a crawl; respect robots.txt; never work around a login, paywall or bot check; signed-in pages only through a session the user opens on their own account.

2. Capture the source into capture/

Every step below is a page load, a resize, a script run in the page, or a screenshot, so any browser tool will do: browser_navigate, browser_resize, browser_evaluate and browser_take_screenshot in Playwright MCP; navigate_page, resize_page, evaluate_script and take_screenshot in Chrome DevTools MCP; or a Playwright script (npm install --save-dev playwright pixelmatch pngjs, then npx playwright install chromium). The scripts below are the repeatable route; with an MCP tool alone, perform the same sequence by hand and keep the file names.

Pages. Read sitemap.xml, the navigation and the footer. Group the URLs by template (home, listing, detail, article, legal) and capture one URL per template. For a faithful rebuild also note each page's path, title and meta description: the new site must keep them or redirect them.

Tokens, breakpoints, assets. Run this function in the page at the widest viewport and save what it returns as capture/survey.json:

() => {
  const tally = (map, key) => key && map.set(key, (map.get(key) || 0) + 1);
  const seen = { text: new Map(), fill: new Map(), family: new Map(), type: new Map(), space: new Map(), radius: new Map(), shadow: new Map(), maxWidth: new Map() };
  const assets = [];
  for (const el of document.body.querySelectorAll('*')) {
    const box = el.getBoundingClientRect();
    if (!box.width || !box.height) continue;
    const cs = getComputedStyle(el);
    if ([...el.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim())) {
      tally(seen.text, cs.color);
      tally(seen.family, cs.fontFamily);
      tally(seen.type, `${cs.fontSize} / ${cs.lineHeight} / ${cs.fontWeight}`);
    }
    for (const gap of [cs.paddingTop, cs.paddingLeft, cs.rowGap, cs.columnGap]) if (/^[1-9]/.test(gap)) tally(seen.space, gap);
    if (cs.backgroundColor !== 'rgba(0, 0, 0, 0)') tally(seen.fill, cs.backgroundColor);
    if (cs.borderTopLeftRadius !== '0px') tally(seen.radius, cs.borderTopLeftRadius);
    if (cs.boxShadow !== 'none') tally(seen.shadow, cs.boxShadow);
    if (cs.maxWidth !== 'none') tally(seen.maxWidth, cs.maxWidth);
    for (const m of cs.backgroundImage.matchAll(/url\("([^"]+)"\)/g)) assets.push({ kind: 'background', url: m[1] });
  }
  for (const img of document.images) assets.push({ kind: 'img', url: img.currentSrc, alt: img.alt, natural: `${img.naturalWidth}x${img.naturalHeight}`, shown: `${img.clientWidth}x${img.clientHeight}` });
  for (const media of document.querySelectorAll('video, audio')) assets.push({ kind: media.tagName.toLowerCase(), url: media.currentSrc, poster: media.poster });
  for (const link of document.querySelectorAll('link[rel~="icon"]')) assets.push({ kind: 'icon', url: link.href });
  const variables = {}, breakpoints = new Set(), unreadable = [];
  const walk = (rules) => {
    for (const rule of rules) {
      if (rule instanceof CSSMediaRule) for (const m of rule.conditionText.matchAll(/width[^)]*?([\d.]+(?:px|em|rem))/g)) breakpoints.add(m[1]);
      if (rule instanceof CSSStyleRule && /(^|,)\s*(:root|html|body)\s*(,|$)/.test(rule.selectorText)) {
        for (const name of rule.style) if (name.startsWith('--')) variables[name] = rule.style.getPropertyValue(name).trim();
      }
      if (rule.cssRules) walk(rule.cssRules);
    }
  };
  for (const sheet of document.styleSheets) {
    try { walk(sheet.cssRules); } catch { unreadable.push(sheet.href); }
  }
  const ranked = Object.fromEntries(Object.entries(seen).map(([k, map]) => [k, [...map].sort((a, b) => b[1] - a[1]).slice(0, 12)]));
  const fonts = [...document.fonts].filter((f) => f.status === 'loaded').map((f) => `${f.family} ${f.weight} ${f.style}`);
  return { variables, breakpoints: [...breakpoints], unreadable, fonts, inlineSvg: document.querySelectorAll('svg').length, ...ranked, assets };
}

How to read it: variables are the tokens the authors named, so keep their roles. Each tally is [value, uses]; a value used once or twice is a one-off, not a token. breakpoints decides the capture widths: one inside every range (720px and 1040px give 390, 768 and 1440). unreadable lists stylesheets served from another origin, whose rules the browser will not expose; for those, resize in 80px steps and note the widths where a section's box jumps.

Screenshots and section boxes. Name every top-level block in page order in capture/sections.source.json, for instance {"header": ".bar", "hero": ".hero", "faq": "#faq"}. Save this as capture/shots.mjs and run it for the source now, and for the rebuild after every change:

// node capture/shots.mjs https://fernhillcycleworks.co.uk/ capture/source capture/sections.source.json 390 768 1440
import fs from 'node:fs';
import { chromium } from 'playwright';

const [url, outDir, mapFile, ...widths] = process.argv.slice(2);
const sections = JSON.parse(fs.readFileSync(mapFile, 'utf8'));
fs.mkdirSync(outDir, { recursive: true });
const browser = await chromium.launch();
for (const width of widths.map(Number)) {
  const context = await browser.newContext({ viewport: { width, height: 900 }, deviceScaleFactor: 1, reducedMotion: 'reduce' });
  const page = await context.newPage();
  await page.goto(url, { waitUntil: 'load' });
  await page.evaluate(async () => {
    await document.fonts.ready;
    for (let y = 0; y < document.documentElement.scrollHeight; y += 600) { // wakes lazy images and reveal-on-scroll blocks
      scrollTo(0, y);
      await new Promise((done) => setTimeout(done, 150));
    }
    scrollTo(0, 0);
  });
  await page.waitForTimeout(300);
  await page.screenshot({ path: `${outDir}/page-${width}.png`, fullPage: true, animations: 'disabled' });
  const boxes = {};
  for (const [name, selector] of Object.entries(sections)) {
    const block = page.locator(selector).first();
    boxes[name] = await block.evaluate((el) => ({ top: Math.round(el.getBoundingClientRect().top + scrollY), height: Math.round(el.offsetHeight) }));
    await block.screenshot({ path: `${outDir}/${name}-${width}.png`, animations: 'disabled' });
  }
  fs.writeFileSync(`${outDir}/boxes-${width}.json`, JSON.stringify(boxes, null, 2));
  await context.close();
}
await browser.close();

States and motion. For each link style, button, menu, tab set, accordion, carousel, sticky bar and form field, run the probe, trigger the state (hover, focus, click, or scrolling in 20px steps) and run it again. It waits for running transitions to end, so it reports settled values:

async () => {
  const frame = () => new Promise((next) => requestAnimationFrame(next));
  const running = () => document.getAnimations().filter((a) => a instanceof CSSTransition && a.playState === 'running');
  await frame(); await frame(); // let the trigger take effect
  while (running().length) await Promise.allSettled(running().map((a) => a.finished)).then(frame); // wait for transitions to end
  const cs = getComputedStyle(document.querySelector('.hero .btn')); // the element under test
  const watch = ['color', 'backgroundColor', 'borderColor', 'boxShadow', 'transform', 'opacity', 'padding', 'height', 'textDecorationLine', 'transitionProperty', 'transitionDuration', 'transitionTimingFunction'];
  return Object.fromEntries(watch.map((name) => [name, cs[name]]));
}

Write one line per state in capture/states.md: element, trigger, what changed, timing. Sort motion into four kinds: CSS transitions, keyframe animations (document.getAnimations() lists what is running), scroll-linked effects, and canvas, WebGL or video. The last kind cannot be read from styles: ask the owner for the original files or plan a simpler stand-in and say so.

Rights per asset. Add a rights field to every entry in assets: owner, licensed (name the licence) or third-party. Ask the owner for original images and a content export before falling back on the served, compressed copies and the page's innerText. Font files are licensed software, usually for named domains: self-host them only if the owner's licence allows it, otherwise load them from the owner's font account or pick an open-licensed face and log the swap. Stock photos, icon sets, customer logos and testimonials need the same question.

3. Plan the build in capture/plan.md

Lane: faithful rebuild. Maya Okafor owns fernhillcycleworks.co.uk (she administers the hosted builder and the DNS).
Stack: static HTML and CSS built with Vite; content stays in the markup.
Widths: 390, 768, 1440 (source breakpoints at 720px and 1040px).
Tokens: 6 colours, 2 font stacks, 7 font sizes, radius 8px and 16px, 1 shadow, container 1120px.
Sections, in build order: header (sticky, shrinks after 40px of scroll), hero, services, faq, footer.
Pass mark, at every width: equal section height, under 0.5% differing pixels, no solid red patch in the diff image.
Left out, for the owner to re-add: analytics tag, cookie banner, booking widget embed.
Open questions: is the photo in the hero hers or stock?

Use the project's existing stack if there is one. Otherwise ask, and suggest a static-first framework for content sites and a component framework where the pages are an application. Map tokens to whatever the stack uses for theming: CSS custom properties, or @theme variables (--color-*, --font-*, --radius-*, --breakpoint-*) in Tailwind CSS v4. Build order: tokens and fonts, page shell, sections from the top down, states, remaining templates. Leave tracking IDs, pixels, chat widgets and any key found in the source out of the code, and list them for the owner.

4. Rebuild one section, measure, fix, repeat

Write the markup fresh and semantic (landmarks, ordered headings, real buttons, alt text) instead of mirroring the source's DOM or class names. Give each section root an attribute such as data-section="hero", so that capture/sections.rebuild.json maps the same names to [data-section=hero] and its siblings. After each section, at each width:

  1. Run capture/shots.mjs against the rebuild's production preview, never the dev server, which may draw its own overlay.
  2. Compare the boxes files first. A height that is off means wrong spacing, font size, line height or wrapping, and everything below it is shifted, so fix heights before pixels and work from the top down.
  3. Run the diff and open the diff image; changed pixels are red. Scattered specks along glyph edges are anti-aliasing. A solid patch is a defect however small the percentage.
  4. Fix what sits at the first differing row, rerun, and stop when the section meets the pass mark. After three rounds without progress, log the deviation with its cause and move on.
// node capture/diff.mjs capture/source/hero-1440.png capture/rebuild/hero-1440.png capture/diff/hero-1440.png
import fs from 'node:fs';
import path from 'node:path';
import { PNG } from 'pngjs';
import pixelmatch from 'pixelmatch';

const [sourcePath, rebuildPath, diffPath] = process.argv.slice(2);
const source = PNG.sync.read(fs.readFileSync(sourcePath));
const rebuild = PNG.sync.read(fs.readFileSync(rebuildPath));
if (source.width !== rebuild.width) throw new Error(`widths differ: ${source.width} vs ${rebuild.width}`);
const width = source.width;
const height = Math.max(source.height, rebuild.height);
const pad = (img) => { // the shorter image gets transparent rows at the bottom
  const canvas = new PNG({ width, height });
  PNG.bitblt(img, canvas, 0, 0, width, img.height, 0, 0);
  return canvas.data;
};
const diff = new PNG({ width, height });
const changed = pixelmatch(pad(source), pad(rebuild), diff.data, width, height, { threshold: 0.1 });
fs.mkdirSync(path.dirname(diffPath), { recursive: true });
fs.writeFileSync(diffPath, PNG.sync.write(diff));
let firstRow = -1;
for (let i = 0; i < diff.data.length && firstRow < 0; i += 4) {
  if (diff.data[i] === 255 && diff.data[i + 1] === 0 && diff.data[i + 2] === 0) firstRow = Math.floor(i / 4 / width);
}
console.log(`${(100 * changed / (width * height)).toFixed(2)}% differ | height ${source.height} -> ${rebuild.height} | first difference at y=${firstRow}`);

Then replay every line of capture/states.md on the rebuild with the same probe and compare values and timings.

A reference build uses the capture differently: it yields a pattern brief (section order, column counts, spacing scale, behaviours) and no pixel target. Its acceptance test is the user's review plus a side-by-side look confirming that name, logo, palette, typefaces, copy and imagery are all different from the reference.

5. Whole-page pass and handover

Diff page-*.png at every width, walk the page by keyboard, check focus visibility and text contrast against WCAG 2.2 AA (do not inherit the source's faults; record each deliberate departure), confirm a clean production build with an empty console, and make sure the network log shows no request to the old site's domain. Then write PARITY.md at the project root:

| Section  | 390   | 768   | 1440  | States checked        | Notes |
|----------|-------|-------|-------|-----------------------|-------|
| header   | 0.00% | 0.00% | 0.00% | sticky shrink at 40px |       |
| hero     | 0.00% | 0.00% | 0.00% | button hover 0.15s    | photo: owner to confirm rights |
Known deviations: booking widget replaced by a link (third-party embed).
Owner to do: re-add analytics tag, confirm the font licence covers the new host, keep every page URL or redirect it.
Run: npx vite build, npx vite preview (port 4173), then capture/shots.mjs and capture/diff.mjs for each section and width.

Examples

Example 1: a faithful rebuild of the user's own site

Request: "Clone my shop's site, fernhillcycleworks.co.uk, into plain HTML and CSS with Vite. I'm leaving the site builder next month." Maya confirms she administers the builder account and the DNS, so the lane is a faithful rebuild. The survey returns eight named variables, breakpoints at 720px and 1040px, and one content image. First pass of the rebuild at 1440:

header-1440:   0.28% differ | height 84 -> 84   | first difference at y=36
hero-1440:     8.14% differ | height 510 -> 497 | first difference at y=76
services-1440: 3.84% differ | height 335 -> 333 | first difference at y=134
faq-1440:      0.00% differ | height 312 -> 312 | first difference at y=-1
footer-1440:   0.00% differ | height 120 -> 120 | first difference at y=-1

Reading it top-down: the header's 0.28% is small but sits in one patch, the button label, drawn in ink instead of white because the nav link colour outranks the button rule. The hero is 13px short: its three-line headline is set at 48px instead of the surveyed 52px, and the grid gap is 40px instead of 48px. The service cards lack their 1px border, which is the missing 2px, and their shadow. After four fixes every section reports 0.00% at 390, 768 and 1440. The probe matches the source on the button hover (backgroundColor rgb(47, 107, 79) to rgb(36, 82, 61), 0.15s ease-out) and on the header, whose padding drops from 20px to 10px once the page is scrolled past 40px. PARITY.md goes to Maya with one open item: confirm the hero photo is hers.

Example 2: somebody else's page becomes a reference build

Request: "Clone the pricing page of Northpane for our invoicing app Tallybird, same as theirs." Northpane is a competitor, so the agent answers before capturing anything:

I can't reproduce Northpane's page: the wording, illustrations, logo and brand colours are theirs,
and a page that reads as Northpane's would mislead your visitors. I can study how it is put together
and build Tallybird's pricing page on the same pattern with your own brand. I need your three plans
with prices, your logo and palette, and the copy (or I draft it for your review).

The capture then feeds a pattern brief instead of a pixel target: three plan cards in a 3/2/1-column grid with the middle one raised, a monthly or yearly toggle that swaps prices without a reload, a comparison table whose header row stays pinned while scrolling, an accordion of questions. The page is built with Tallybird's tokens and copy and reviewed by the user. No diff is run against Northpane.

Example 3: a request to decline

Request: "Make an exact copy of the Corran Bank login page for corran-bank-secure.net, logo included." This is another brand's sign-in page on a look-alike domain. The agent declines, says that such a page is the working part of a phishing kit whatever it is meant for, and offers what it can do: a login screen for the user's own product, or a training page under an invented brand.

Guidelines

  • Not legal advice. Roughly: text, photos, illustrations, video and code are protected works; a general layout usually is not, yet a site that confuses visitors about who runs it is unlawful in most places regardless of what was copied. When the lane is unclear, take the reference lane and tell the user to ask a lawyer.
  • Take both sets of screenshots with one browser, one machine, one device scale factor. Fonts rasterize differently across operating systems, and that alone can fail a diff.
  • Text set in a substitute font never passes on pixels. Judge it by equal box heights and identical line breaks, and say so in the report.
  • Freeze what moves before capturing: dismiss cookie banners, pause carousels, and hide rotating testimonials, dates and counters with the screenshot mask option on both sides. A sticky bar or chat bubble can cover part of a section shot.
  • reducedMotion: 'reduce' only helps on sites that honour the preference; animations: 'disabled' is what fast-forwards finite animations and resets looping ones for the shot.
  • A fixed sleep is not a safe wait for a transition in a headless browser: in testing, a 0.2s transition still reported mid-way values 400ms later. Wait on the animations themselves, as the probe does.
  • Computed values are results, not intent. A 1120px block is usually max-width with auto margins: read the matching rule before hard-coding a width.
  • deviceScaleFactor: 1 keeps diffs fast but picks 1x images; check the sharpness of photos and icons once at scale 2 by eye.
  • On very tall pages rely on the section shots; a single full-page image can become too large to compare.
  • This covers the presentation layer only. Forms, search, accounts, carts and CMS content need real back ends, designed separately.
  • Skip it when the user has the site's source code (migrate the code) or a design file (build from the design): both are better inputs than a rendered page.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Scripts and configures production rendering in Autodesk 3ds Max with the V-Ray and Corona renderers: output size and files, render elements, denoising, light mix, batch and command-line rendering, and network rendering. Use when a user asks to set up a production render, render several cameras in one batch, render from the command line or on a render farm, add render passes for compositing, or cut render time for archviz and product shots.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

Covers scripting Autodesk 3ds Max, the 3D modeling and rendering application, with MAXScript and Python (pymxs): scene manipulation, object creation, material assignment, camera and light setup, batch operations, and file I/O. Use when tasks involve automating repetitive 3ds Max workflows, batch processing scenes, running scripts headless with 3dsmaxbatch, creating custom tools, or scripting scene setup for archviz, product visualization, or VFX.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

3proxy

無料

3proxy is a small open-source proxy server that runs HTTP/HTTPS, SOCKS4/5, SNI and TCP/UDP port-mapping proxies from one config file. Use when a user asks to set up an HTTP or SOCKS5 proxy, add proxy users and passwords, write 3proxy access rules, chain or rotate upstream (parent) proxies, limit bandwidth, connections or monthly traffic per user, run 3proxy in Docker, or fix a 3proxy.cfg that will not start.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

Builds Agent2Agent (A2A) servers and clients, the open protocol (originally from Google, now under the Linux Foundation) that lets AI agents from different frameworks call each other. Use when the user wants to create an A2A-compliant agent, build an Agent Card, implement task management, connect agents across frameworks, set up agent discovery, handle streaming responses, implement push notifications, or orchestrate multi-agent workflows. Trigger words: a2a, agent to agent, agent2agent, a2a protocol, a2a server, a2a client, agent card, agent interoperability, agent collaboration, multi-agent, agent discovery, a2a sdk, a2a task.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

Plans a controlled experiment (A/B test) so its result can be trusted: writes the hypothesis, picks one primary metric and the guardrails, computes sample size and run time, specifies how visitors are assigned and when exposure is logged, and reads out the result with a confidence interval. Use when someone says "set up an A/B test", "split test this page", "how many visitors do I need", "how long should the experiment run", "is this result significant", "can I stop the test early", or wants to test a headline, price, layout or onboarding change against the current version.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

ably

無料

Ably is a hosted realtime messaging service: clients publish and subscribe to named channels over WebSockets, see who is present, replay message history, and resume after a dropped connection. Use when a user asks to "add realtime updates", "push live notifications to the browser", "show who is online", "add a chat room with typing indicators", "publish from a serverless function", or "authenticate Ably clients without exposing the API key". Covers the ably 2.x JavaScript SDK (Realtime and REST), JWT token authentication, presence, history and rewind, batch publishing, and the @ably/chat 1.x SDK.

日本語の概要は準備中です。原文の説明を表示しています。

TerminalSkills/skills1632026年10月4日 更新

TerminalSkills のスキルをすべて見る

このスキルの問題を報告する