multiclone 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/README.md +89 -2
  2. package/agents/section-builder.md +37 -0
  3. package/commands/multiclone.md +20 -0
  4. package/dist/adapters/cli.js +120 -0
  5. package/dist/adapters/cli.js.map +1 -0
  6. package/dist/adapters/cliArgs.js +63 -0
  7. package/dist/adapters/cliArgs.js.map +1 -0
  8. package/dist/adapters/mcp.js +83 -0
  9. package/dist/adapters/mcp.js.map +1 -0
  10. package/dist/analyze/analyze.js +85 -0
  11. package/dist/analyze/analyze.js.map +1 -0
  12. package/dist/analyze/animations.js +340 -0
  13. package/dist/analyze/animations.js.map +1 -0
  14. package/dist/analyze/ast.js +182 -0
  15. package/dist/analyze/ast.js.map +1 -0
  16. package/dist/analyze/chunks.js +160 -0
  17. package/dist/analyze/chunks.js.map +1 -0
  18. package/dist/analyze/jobs/code.js +117 -0
  19. package/dist/analyze/jobs/code.js.map +1 -0
  20. package/dist/analyze/jobs/sections.js +356 -0
  21. package/dist/analyze/jobs/sections.js.map +1 -0
  22. package/dist/analyze/pretty.js +20 -0
  23. package/dist/analyze/pretty.js.map +1 -0
  24. package/dist/analyze/report.js +101 -0
  25. package/dist/analyze/report.js.map +1 -0
  26. package/dist/analyze/worker.js +71 -0
  27. package/dist/analyze/worker.js.map +1 -0
  28. package/dist/capture/capture.js +231 -0
  29. package/dist/capture/capture.js.map +1 -0
  30. package/dist/capture/collectors/a11y.js +92 -0
  31. package/dist/capture/collectors/a11y.js.map +1 -0
  32. package/dist/capture/collectors/animations.js +113 -0
  33. package/dist/capture/collectors/animations.js.map +1 -0
  34. package/dist/capture/collectors/breakpoints.js +31 -0
  35. package/dist/capture/collectors/breakpoints.js.map +1 -0
  36. package/dist/capture/collectors/consent.js +39 -0
  37. package/dist/capture/collectors/consent.js.map +1 -0
  38. package/dist/capture/collectors/coverage.js +119 -0
  39. package/dist/capture/collectors/coverage.js.map +1 -0
  40. package/dist/capture/collectors/dom.js +16 -0
  41. package/dist/capture/collectors/dom.js.map +1 -0
  42. package/dist/capture/collectors/fonts.js +154 -0
  43. package/dist/capture/collectors/fonts.js.map +1 -0
  44. package/dist/capture/collectors/frames.js +93 -0
  45. package/dist/capture/collectors/frames.js.map +1 -0
  46. package/dist/capture/collectors/index.js +50 -0
  47. package/dist/capture/collectors/index.js.map +1 -0
  48. package/dist/capture/collectors/interactions.js +208 -0
  49. package/dist/capture/collectors/interactions.js.map +1 -0
  50. package/dist/capture/collectors/intro.js +84 -0
  51. package/dist/capture/collectors/intro.js.map +1 -0
  52. package/dist/capture/collectors/listeners.js +84 -0
  53. package/dist/capture/collectors/listeners.js.map +1 -0
  54. package/dist/capture/collectors/motion.js +86 -0
  55. package/dist/capture/collectors/motion.js.map +1 -0
  56. package/dist/capture/collectors/network.js +288 -0
  57. package/dist/capture/collectors/network.js.map +1 -0
  58. package/dist/capture/collectors/probes.js +33 -0
  59. package/dist/capture/collectors/probes.js.map +1 -0
  60. package/dist/capture/collectors/runtime.js +38 -0
  61. package/dist/capture/collectors/runtime.js.map +1 -0
  62. package/dist/capture/collectors/scripts.js +98 -0
  63. package/dist/capture/collectors/scripts.js.map +1 -0
  64. package/dist/capture/collectors/sections.js +17 -0
  65. package/dist/capture/collectors/sections.js.map +1 -0
  66. package/dist/capture/collectors/shots.js +38 -0
  67. package/dist/capture/collectors/shots.js.map +1 -0
  68. package/dist/capture/collectors/snapshot.js +47 -0
  69. package/dist/capture/collectors/snapshot.js.map +1 -0
  70. package/dist/capture/collectors/sourcemaps.js +153 -0
  71. package/dist/capture/collectors/sourcemaps.js.map +1 -0
  72. package/dist/capture/collectors/storage.js +72 -0
  73. package/dist/capture/collectors/storage.js.map +1 -0
  74. package/dist/capture/context.js +35 -0
  75. package/dist/capture/context.js.map +1 -0
  76. package/dist/capture/hooks/candidates.js +104 -0
  77. package/dist/capture/hooks/candidates.js.map +1 -0
  78. package/dist/capture/hooks/consent.js +82 -0
  79. package/dist/capture/hooks/consent.js.map +1 -0
  80. package/dist/capture/hooks/recorders.js +507 -0
  81. package/dist/capture/hooks/recorders.js.map +1 -0
  82. package/dist/capture/hooks/sections.js +62 -0
  83. package/dist/capture/hooks/sections.js.map +1 -0
  84. package/dist/capture/hooks/styles.js +86 -0
  85. package/dist/capture/hooks/styles.js.map +1 -0
  86. package/dist/capture/probes.js +491 -0
  87. package/dist/capture/probes.js.map +1 -0
  88. package/dist/capture/report.js +115 -0
  89. package/dist/capture/report.js.map +1 -0
  90. package/dist/compare/compare.js +391 -0
  91. package/dist/compare/compare.js.map +1 -0
  92. package/dist/compare/image.js +105 -0
  93. package/dist/compare/image.js.map +1 -0
  94. package/dist/compare/probes.js +129 -0
  95. package/dist/compare/probes.js.map +1 -0
  96. package/dist/compare/report.js +99 -0
  97. package/dist/compare/report.js.map +1 -0
  98. package/dist/compare/session.js +133 -0
  99. package/dist/compare/session.js.map +1 -0
  100. package/dist/compare/shoot.js +44 -0
  101. package/dist/compare/shoot.js.map +1 -0
  102. package/dist/contracts/catalog.js +24 -0
  103. package/dist/contracts/catalog.js.map +1 -0
  104. package/dist/contracts/context.js +19 -0
  105. package/dist/contracts/context.js.map +1 -0
  106. package/dist/contracts/fields.js +16 -0
  107. package/dist/contracts/fields.js.map +1 -0
  108. package/dist/contracts/tools/analyze.js +38 -0
  109. package/dist/contracts/tools/analyze.js.map +1 -0
  110. package/dist/contracts/tools/capture.js +71 -0
  111. package/dist/contracts/tools/capture.js.map +1 -0
  112. package/dist/contracts/tools/compare.js +142 -0
  113. package/dist/contracts/tools/compare.js.map +1 -0
  114. package/dist/contracts/tools/doctor.js +23 -0
  115. package/dist/contracts/tools/doctor.js.map +1 -0
  116. package/dist/contracts/tools/inspect.js +41 -0
  117. package/dist/contracts/tools/inspect.js.map +1 -0
  118. package/dist/contracts/tools/method.js +56 -0
  119. package/dist/contracts/tools/method.js.map +1 -0
  120. package/dist/contracts/tools/mirror.js +53 -0
  121. package/dist/contracts/tools/mirror.js.map +1 -0
  122. package/dist/contracts/tools/project.js +61 -0
  123. package/dist/contracts/tools/project.js.map +1 -0
  124. package/dist/contracts/types.js +20 -0
  125. package/dist/contracts/types.js.map +1 -0
  126. package/dist/core/cdp.js +146 -0
  127. package/dist/core/cdp.js.map +1 -0
  128. package/dist/core/chrome.js +185 -0
  129. package/dist/core/chrome.js.map +1 -0
  130. package/dist/core/files.js +31 -0
  131. package/dist/core/files.js.map +1 -0
  132. package/dist/core/inpage.js +24 -0
  133. package/dist/core/inpage.js.map +1 -0
  134. package/dist/core/log.js +10 -0
  135. package/dist/core/log.js.map +1 -0
  136. package/dist/core/page.js +248 -0
  137. package/dist/core/page.js.map +1 -0
  138. package/dist/doctor.js +37 -0
  139. package/dist/doctor.js.map +1 -0
  140. package/dist/domain/breakpoints.js +47 -0
  141. package/dist/domain/breakpoints.js.map +1 -0
  142. package/dist/domain/budget.js +38 -0
  143. package/dist/domain/budget.js.map +1 -0
  144. package/dist/domain/compare.js +138 -0
  145. package/dist/domain/compare.js.map +1 -0
  146. package/dist/domain/coverage.js +96 -0
  147. package/dist/domain/coverage.js.map +1 -0
  148. package/dist/domain/motion.js +142 -0
  149. package/dist/domain/motion.js.map +1 -0
  150. package/dist/domain/paths.js +55 -0
  151. package/dist/domain/paths.js.map +1 -0
  152. package/dist/domain/result.js +38 -0
  153. package/dist/domain/result.js.map +1 -0
  154. package/dist/domain/routes.js +160 -0
  155. package/dist/domain/routes.js.map +1 -0
  156. package/dist/domain/sections.js +94 -0
  157. package/dist/domain/sections.js.map +1 -0
  158. package/dist/domain/snapshot.js +268 -0
  159. package/dist/domain/snapshot.js.map +1 -0
  160. package/dist/domain/sourcemap.js +95 -0
  161. package/dist/domain/sourcemap.js.map +1 -0
  162. package/dist/domain/stack.js +342 -0
  163. package/dist/domain/stack.js.map +1 -0
  164. package/dist/domain/tokens.js +183 -0
  165. package/dist/domain/tokens.js.map +1 -0
  166. package/dist/domain/types.js +2 -0
  167. package/dist/domain/types.js.map +1 -0
  168. package/dist/domain/viewports.js +45 -0
  169. package/dist/domain/viewports.js.map +1 -0
  170. package/dist/mirror/rewrite.js +56 -0
  171. package/dist/mirror/rewrite.js.map +1 -0
  172. package/dist/mirror/server.js +191 -0
  173. package/dist/mirror/server.js.map +1 -0
  174. package/dist/mirror/store.js +86 -0
  175. package/dist/mirror/store.js.map +1 -0
  176. package/dist/project/assets.js +48 -0
  177. package/dist/project/assets.js.map +1 -0
  178. package/dist/project/method.js +152 -0
  179. package/dist/project/method.js.map +1 -0
  180. package/dist/project/scaffold.js +140 -0
  181. package/dist/project/scaffold.js.map +1 -0
  182. package/dist/store/inspect.js +212 -0
  183. package/dist/store/inspect.js.map +1 -0
  184. package/dist/store/run.js +112 -0
  185. package/dist/store/run.js.map +1 -0
  186. package/dist/store/summary.js +69 -0
  187. package/dist/store/summary.js.map +1 -0
  188. package/dist/version.js +2 -0
  189. package/dist/version.js.map +1 -0
  190. package/package.json +66 -4
  191. package/skills/multiclone/SKILL.md +102 -0
  192. package/skills/multiclone/references/capture-format.md +73 -0
  193. package/skills/multiclone/references/fidelity.md +54 -0
  194. package/skills/multiclone/references/quality.md +47 -0
  195. package/skills/multiclone/references/stacks.md +156 -0
  196. package/templates/react-vite/.prettierignore +5 -0
  197. package/templates/react-vite/.prettierrc.json +6 -0
  198. package/templates/react-vite/eslint.config.js +38 -0
  199. package/templates/react-vite/gitignore +4 -0
  200. package/templates/react-vite/index.html +12 -0
  201. package/templates/react-vite/package.json +36 -0
  202. package/templates/react-vite/src/app/App.tsx +10 -0
  203. package/templates/react-vite/src/app/Page.tsx +4 -0
  204. package/templates/react-vite/src/components/README.md +7 -0
  205. package/templates/react-vite/src/content/README.md +2 -0
  206. package/templates/react-vite/src/hooks/useActiveSection.ts +37 -0
  207. package/templates/react-vite/src/hooks/useInView.ts +45 -0
  208. package/templates/react-vite/src/hooks/useMediaQuery.ts +15 -0
  209. package/templates/react-vite/src/lib/cn.ts +4 -0
  210. package/templates/react-vite/src/lib/math.ts +21 -0
  211. package/templates/react-vite/src/lib/mc.ts +13 -0
  212. package/templates/react-vite/src/lib/media.ts +4 -0
  213. package/templates/react-vite/src/main.tsx +13 -0
  214. package/templates/react-vite/src/providers/SmoothScroll.tsx +24 -0
  215. package/templates/react-vite/src/styles/base.css +42 -0
  216. package/templates/react-vite/src/styles/fonts.css +1 -0
  217. package/templates/react-vite/src/styles/index.css +3 -0
  218. package/templates/react-vite/src/styles/tokens.css +3 -0
  219. package/templates/react-vite/src/vite-env.d.ts +1 -0
  220. package/templates/react-vite/tsconfig.app.json +29 -0
  221. package/templates/react-vite/tsconfig.json +4 -0
  222. package/templates/react-vite/tsconfig.node.json +19 -0
  223. package/templates/react-vite/vite.config.ts +37 -0
package/README.md CHANGED
@@ -1,3 +1,90 @@
1
- # Temporary Holding Version
1
+ # multiclone
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Clone any website from a link. multiclone captures the page, plays it back offline, rebuilds it as a clean React + Vite + TypeScript project with the same design and animations, and scores how close the rebuild is.
4
+
5
+ It runs on your own machine: your Chrome, your CPU and your Claude Code. Nothing is uploaded anywhere.
6
+
7
+ ## Quick start
8
+
9
+ You need Node 22.12 or newer and Google Chrome (Chromium, Edge or Brave also work). Nothing to clone or build: npm downloads it ready to run.
10
+
11
+ In the folder where you want the clone:
12
+
13
+ 1. `npx multiclone setup` installs the cloning method and the MCP server for Claude Code, Cursor and any agent that reads `AGENTS.md`. Limit it with `--agents claude`, `--agents cursor` or `--agents agents`.
14
+ 2. `npx multiclone doctor` checks Chrome and WebGL.
15
+ 3. Open your agent in that folder and ask it: "clone https://example.com".
16
+
17
+ ### Claude Code
18
+
19
+ - After `setup`, start `claude` in the folder and approve the multiclone server when asked. Then type `/multiclone https://example.com` or just ask for a clone.
20
+ - To have it in every project instead: `claude mcp add --scope user multiclone -- npx -y multiclone mcp`. The server tells the agent how to use it, and the `guide` tool holds the full method.
21
+
22
+ ### Cursor
23
+
24
+ - After `setup`, open the folder and turn on multiclone under Settings → MCP. The method lives in `.cursor/rules/multiclone.mdc`.
25
+ - Or add it for every project in `~/.cursor/mcp.json`: `{ "mcpServers": { "multiclone": { "command": "npx", "args": ["-y", "multiclone", "mcp"] } } }`.
26
+
27
+ ### Any other agent (GG Coder, Codex, Windsurf, Cline and others)
28
+
29
+ - Add an MCP server with the command `npx -y multiclone mcp` in the agent's settings.
30
+ - `setup` also writes `.multiclone/GUIDE.md` and a short block in `AGENTS.md` so the agent finds the method.
31
+ - No MCP support? The agent can run every tool from the terminal: `npx multiclone --help` lists them and `npx multiclone guide` prints the method.
32
+
33
+ ## Use it
34
+
35
+ Ask your agent to clone a URL. It then:
36
+
37
+ 1. **Captures** the page on desktop and mobile and analyses it into one bundle per section.
38
+ 2. **Reads** `REPORT.md` and the section briefs: stack, tokens, fonts, motion, animation code, hover states.
39
+ 3. **Scaffolds** a React project with the captured fonts, design tokens, media and a mock API.
40
+ 4. **Rebuilds** it section by section (the `section-builder` agent can take independent sections).
41
+ 5. **Compares** the clone with the capture per section, motion track and hover, fixes the worst first, and finishes with a localhost link and a score.
42
+
43
+ ## What's inside
44
+
45
+ - **Capture:** your Chrome (headless or headed, with an optional persistent profile) scrolls the page with real wheel events, so Lenis and GSAP sites move. It dismisses consent banners and records:
46
+ - every response, redirect and WebSocket, plus every script and its source maps;
47
+ - a computed-style DOM snapshot, the hydrated DOM, screenshots and an intro filmstrip per viewport;
48
+ - fonts, storage, CSS breakpoints and hydration data (Next, Remix, Nuxt and others);
49
+ - the scroll motion map (transform, opacity, clip, filter, colours, size, text) with exact curves;
50
+ - live animation state: GSAP tweens and ScrollTriggers, Lenis, WAAPI, IntersectionObservers, WebGL shaders, Lottie and Rive;
51
+ - event listeners, the code that runs while things move, and hover, focus and click states.
52
+ - **Analyze:** in an isolated worker it splits the page into section bundles, extracts design tokens, prettifies and splits the code into app and vendor, finds every animation call with file and line, and writes `REPORT.md`.
53
+ - **Inspect:** pages through any part of a capture so big captures never flood the chat.
54
+ - **Mirror:** an offline copy on localhost. `live` mode runs the original scripts with storage seeded; captured API calls become a mock API.
55
+ - **Scaffold:** a strict React 19 + Vite + TypeScript starter (ESLint strictTypeChecked, Prettier, `@` alias, Lenis provider, hooks, `check` script). From a capture it also brings the fonts, `tokens.css`, the media and a dev proxy to the mock API.
56
+ - **Shoot:** screenshots at exact scroll offsets, plus a JavaScript probe for measuring.
57
+ - **Compare:** scores frames, sections (aligned by `data-mc-section`), structure, scroll motion (`data-mc-track`), hover states (`data-mc-hover`) and the intro, with an HTML report and the worst sections to fix next. The score stays below 100 while anything could not be compared.
58
+ - **The skill and agent:** the method Claude follows, recipes for each stack, a fidelity playbook, the capture format, and a `section-builder` agent that rebuilds one section from its bundle.
59
+
60
+ ## CLI
61
+
62
+ Everything also works from the terminal (`npx multiclone <command>`, or `multiclone <command>` after `npm i -g multiclone`). Flags are the tool fields in kebab case; `multiclone <command> --help` lists them.
63
+
64
+ | Command | What it does |
65
+ | ---------------------------------------------------------------------------------------------------- | -------------------------------- |
66
+ | `multiclone setup [dir] [--agents claude,cursor,agents]` | Install the method and MCP entry |
67
+ | `multiclone guide [topic]` | Print the method |
68
+ | `multiclone capture <url> [--out dir] [--viewports desktop,mobile\|auto] [--headed] [--profile dir]` | Record and analyse the page |
69
+ | `multiclone analyze <capture-dir>` | Rebuild the derived files |
70
+ | `multiclone inspect <capture-dir> section [03-pricing]` | Page through a capture |
71
+ | `multiclone serve <capture-dir> [--port 4400] [--mode live\|snapshot]` | Offline mirror and mock API |
72
+ | `multiclone scaffold <dir> [--capture capture-dir] [--port 5180]` | New React project |
73
+ | `multiclone assets <capture-dir> <project-dir>` | Copy media in |
74
+ | `multiclone shoot <url> [--positions 0,900] [--probe js]` | Screenshots and measurements |
75
+ | `multiclone compare <capture-dir\|url> <clone-url> [--intro] [--positions]` | Score the clone |
76
+ | `multiclone doctor` | Check the setup |
77
+
78
+ Add `--json` for machine-readable output. Set `MULTICLONE_CHROME` to use a specific browser.
79
+
80
+ ## Known limits
81
+
82
+ - Server features (logins, search, carts, live data) are rebuilt as static visuals; the mock API only replays what was recorded.
83
+ - WebGL and heavy canvas work need the most iteration. Shaders are captured, but matching them takes rounds of comparing.
84
+ - Some sites block automated browsers. Use `--headed` with `--profile` and pass the check once.
85
+ - Video, random and time-based effects never match pixel for pixel, so judge those frames by eye.
86
+
87
+ ## Development
88
+
89
+ - `npm run check`: typecheck, lint, format check, unit tests and build
90
+ - `npm run e2e`: fixture-site capture → mirror → compare
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: section-builder
3
+ description: Rebuilds one section of a multiclone capture as a React component in an existing clone project. Give it the capture folder, the section id and the project folder. Use for pages with many independent sections after the shell (fonts, tokens, header, footer, smooth scroll) is in place.
4
+ tools: Read, Write, Edit, Glob, Grep, Bash
5
+ ---
6
+
7
+ You rebuild exactly one section of a captured website inside an existing React + Vite + TypeScript clone project. You get three things: the capture folder, the section id (for example `03-pricing`) and the project folder.
8
+
9
+ ## Read first
10
+
11
+ 1. `<capture>/sections/<id>/section.md`: the brief. Follow it.
12
+ 2. The crops `<capture>/sections/<id>/<viewport>.png` for every viewport. Look at them.
13
+ 3. `dom-<viewport>.txt` and `styles-<viewport>.json` for structure, boxes and exact CSS values; `tokens-<viewport>.json` for local values.
14
+ 4. When present: `motion.json` (scroll curves with track ids), `animations.json` and `animations-code.json` (keyframes, library calls with file and line), `interactions.json` (hover, focus and click states with ids), `assets.json` (media used), `hot-code.json` (code that ran while it moved).
15
+ 5. `<project>/src/styles/tokens.css`, `src/lib/mc.ts` and existing components, so you reuse what the shell already has.
16
+
17
+ Use the multiclone `inspect` tool (`facet: "section"`, `selector: "<id>"`) for anything too big to read whole.
18
+
19
+ ## Build
20
+
21
+ - One component in `<project>/src/components/sections/<Name>.tsx`, plus typed content in `src/content/` when the section has copy or lists. Copy text from the capture; never retype or invent it.
22
+ - Root element: `<section {...mcSection('<id>')}>`.
23
+ - Every element in `motion.json` gets `{...mcTrack(id)}`; every element in `interactions.json` gets `{...mcHover(id)}`.
24
+ - Match the captured values: sizes, spacing, colours, fonts, radii, shadows and the motion curves (start, end, easing). Prefer the existing tokens when a value matches one.
25
+ - Media paths come from `src/content/asset-map.json`.
26
+ - Semantic, accessible markup; respect `prefers-reduced-motion`. Heavy parts load lazily and clean up on unmount.
27
+ - Strict TypeScript: no `any`, no non-null `!`, named exports.
28
+
29
+ ## Do not
30
+
31
+ - Touch other sections, the shell, global styles or config, except to add your component to the page when asked.
32
+ - Install packages. If the section needs one that is missing, say so in your report.
33
+ - Run compare or the dev server unless asked.
34
+
35
+ ## Finish
36
+
37
+ Run `npm run typecheck` and `npm run lint` in the project and fix what you introduced. Report in a few plain lines: the files you wrote, the tracks and hovers you tagged, and anything approximated.
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: Clone a website from a link into a React + Vite + TypeScript project with the same design and animations
3
+ argument-hint: <url> [project folder]
4
+ ---
5
+
6
+ Clone this website with the multiclone method: $ARGUMENTS
7
+
8
+ Follow the `multiclone` skill end to end:
9
+
10
+ 1. Capture (it analyses into section bundles too)
11
+ 2. Understand: `REPORT.md`, then each `sections/<id>/section.md`
12
+ 3. Scaffold from the capture (fonts, tokens, media, mock API)
13
+ 4. Rebuild section by section with the `data-mc-*` markers; use the `section-builder` agent for independent sections
14
+ 5. Compare against the capture folder and fix
15
+
16
+ If no project folder is given, use `./<site-name>-clone` in the current directory, with the capture in `./<site-name>-capture`.
17
+
18
+ If the multiclone MCP tools are not available, run the same tools from the terminal with `npx multiclone <command>` and tell the user to add the server with `claude mcp add multiclone -- npx -y multiclone mcp`, then restart.
19
+
20
+ Keep progress updates short and in plain words. Finish with the localhost link, the final compare score, and an honest list of what is approximated.
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { CATALOG } from '../contracts/catalog.js';
4
+ import { MirrorRegistry } from '../contracts/context.js';
5
+ import { setQuiet } from '../core/log.js';
6
+ import { toMcError } from '../domain/result.js';
7
+ import { VERSION } from '../version.js';
8
+ import { coerce, toolFields } from './cliArgs.js';
9
+ const GLOBAL_HELP = ` --json Print the result as JSON
10
+ --quiet No progress output
11
+ -h, --help Help for a command`;
12
+ function fieldHelp(field) {
13
+ const value = field.kind === 'boolean'
14
+ ? ''
15
+ : field.kind === 'enum'
16
+ ? ` <${field.options.join('|')}>`
17
+ : field.kind.endsWith('list')
18
+ ? ' <a,b,c>'
19
+ : ` <${field.kind}>`;
20
+ const fallback = field.defaultValue === undefined || field.kind === 'boolean'
21
+ ? ''
22
+ : ` (default ${JSON.stringify(field.defaultValue)})`;
23
+ const negate = field.kind === 'boolean' && field.defaultValue === true ? ` [--no-${field.flag}]` : '';
24
+ return ` --${field.flag}${value}${negate} ${field.description}${fallback}`;
25
+ }
26
+ function usage(tool) {
27
+ const binding = tool.cli;
28
+ if (!binding)
29
+ return '';
30
+ const positionals = binding.positionals.map((key) => `<${key}>`).join(' ');
31
+ const options = toolFields(tool)
32
+ .filter((field) => !binding.positionals.includes(field.key))
33
+ .map(fieldHelp)
34
+ .join('\n');
35
+ return ` multiclone ${binding.command} ${positionals}\n ${tool.title}\n${options}`;
36
+ }
37
+ function help() {
38
+ const tools = CATALOG.filter((tool) => tool.cli !== null).map(usage);
39
+ return `multiclone ${VERSION} — clone any website from a link\n\nStart: multiclone setup, then ask your agent to clone a URL. Read the method: multiclone guide.\n\nUsage\n${tools.join('\n')}\n multiclone mcp\n Run the MCP server (for Claude Code, Cursor or any MCP agent)\n\nGlobal\n${GLOBAL_HELP}\n`;
40
+ }
41
+ function fail(message) {
42
+ process.stderr.write(`multiclone: ${message}\n`);
43
+ process.exit(2);
44
+ }
45
+ async function runTool(tool, argv) {
46
+ const binding = tool.cli;
47
+ if (!binding)
48
+ fail(`${tool.name} has no CLI command`);
49
+ const fields = toolFields(tool);
50
+ const options = {
51
+ json: { type: 'boolean' },
52
+ quiet: { type: 'boolean' },
53
+ help: { type: 'boolean', short: 'h' },
54
+ };
55
+ for (const field of fields) {
56
+ options[field.flag] = { type: field.kind === 'boolean' ? 'boolean' : 'string' };
57
+ if (field.kind === 'boolean')
58
+ options[`no-${field.flag}`] = { type: 'boolean' };
59
+ }
60
+ const { values, positionals } = parseArgs({ args: argv, options, allowPositionals: true });
61
+ if (values.help === true)
62
+ return void process.stdout.write(`${usage(tool)}\n`);
63
+ if (values.quiet === true)
64
+ setQuiet(true);
65
+ const input = {};
66
+ binding.positionals.forEach((key, index) => {
67
+ const value = positionals[index];
68
+ if (value !== undefined)
69
+ input[key] = value;
70
+ });
71
+ for (const field of fields) {
72
+ const raw = values[field.flag];
73
+ if (typeof raw === 'string' || typeof raw === 'boolean')
74
+ input[field.key] = coerce(field, raw);
75
+ if (field.kind === 'boolean' && values[`no-${field.flag}`] === true)
76
+ input[field.key] = false;
77
+ }
78
+ const mirrors = new MirrorRegistry();
79
+ const controller = new AbortController();
80
+ const stop = () => {
81
+ controller.abort();
82
+ void mirrors.stop().then(() => process.exit(130));
83
+ };
84
+ process.on('SIGINT', stop);
85
+ process.on('SIGTERM', stop);
86
+ const context = { mirrors, signal: controller.signal, surface: 'cli' };
87
+ const result = await tool.run(input, context);
88
+ if (!result.ok) {
89
+ const hint = result.error.hint === undefined ? '' : `\nHint: ${result.error.hint}`;
90
+ fail(`[${result.error.tag}] ${result.error.message}${hint}`);
91
+ }
92
+ const output = result.value;
93
+ process.stdout.write(values.json === true ? `${JSON.stringify(output.data, null, 2)}\n` : `${output.text}\n`);
94
+ if (binding.longRunning)
95
+ process.stderr.write('Running. Ctrl+C to stop.\n');
96
+ }
97
+ async function main() {
98
+ const [command, ...rest] = process.argv.slice(2);
99
+ if (command === '--version' || command === '-v')
100
+ return void process.stdout.write(`${VERSION}\n`);
101
+ if (command === undefined || command === 'help' || command === '--help' || command === '-h') {
102
+ return void process.stdout.write(help());
103
+ }
104
+ if (command === 'mcp') {
105
+ setQuiet(true);
106
+ const { runMcpServer } = await import('./mcp.js');
107
+ await runMcpServer();
108
+ return;
109
+ }
110
+ const tool = CATALOG.find((entry) => entry.cli?.command === command);
111
+ if (!tool)
112
+ fail(`unknown command "${command}"\n\n${help()}`);
113
+ await runTool(tool, rest);
114
+ }
115
+ main().catch((error) => {
116
+ const mapped = toMcError(error);
117
+ process.stderr.write(`multiclone: [${mapped.tag}] ${mapped.message}\n`);
118
+ process.exit(1);
119
+ });
120
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/adapters/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import { readFileSync } from 'node:fs';
2
+ export const kebab = (key) => key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
3
+ /** Reads one zod field down to its base type, keeping the outer description and default. */
4
+ export function describeField(key, schema) {
5
+ let current = schema;
6
+ let description = schema.description ?? '';
7
+ let defaultValue = undefined;
8
+ for (;;) {
9
+ const def = current.def;
10
+ if (def.type === 'optional' || def.type === 'nullable') {
11
+ current = current.unwrap();
12
+ }
13
+ else if (def.type === 'default') {
14
+ const wrapped = current;
15
+ defaultValue = wrapped.def.defaultValue;
16
+ current = wrapped.unwrap();
17
+ }
18
+ else
19
+ break;
20
+ description ||= current.description ?? '';
21
+ }
22
+ const base = current.def.type;
23
+ let kind = 'string';
24
+ let options = [];
25
+ if (base === 'number')
26
+ kind = 'number';
27
+ else if (base === 'boolean')
28
+ kind = 'boolean';
29
+ else if (base === 'enum') {
30
+ kind = 'enum';
31
+ options = current.options.map(String);
32
+ }
33
+ else if (base === 'array') {
34
+ const element = current.element.def.type;
35
+ kind = element === 'number' ? 'number-list' : 'string-list';
36
+ }
37
+ return { key, flag: kebab(key), kind, description, options, defaultValue };
38
+ }
39
+ export function toolFields(tool) {
40
+ return Object.entries(tool.input.shape).map(([key, schema]) => describeField(key, schema));
41
+ }
42
+ /** Converts parsed CLI strings to the shapes the tool schema expects; zod validates the rest. */
43
+ export function coerce(field, raw) {
44
+ if (field.kind === 'boolean')
45
+ return raw;
46
+ const text = String(raw);
47
+ if (field.kind === 'number')
48
+ return Number(text);
49
+ if (field.kind === 'number-list')
50
+ return text.split(',').filter(Boolean).map(Number);
51
+ if (field.kind === 'string-list')
52
+ return text.split(',').filter(Boolean);
53
+ if (field.key === 'probe' && text.length < 400 && !text.includes('(')) {
54
+ try {
55
+ return readFileSync(text, 'utf8');
56
+ }
57
+ catch {
58
+ return text;
59
+ }
60
+ }
61
+ return text;
62
+ }
63
+ //# sourceMappingURL=cliArgs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliArgs.js","sourceRoot":"","sources":["../../src/adapters/cliArgs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { CATALOG } from '../contracts/catalog.js';
4
+ import { MirrorRegistry } from '../contracts/context.js';
5
+ import { toMcError } from '../domain/result.js';
6
+ import { readGuide, TOPICS } from '../project/method.js';
7
+ import { z } from 'zod';
8
+ import { VERSION } from '../version.js';
9
+ const render = (output) => ({
10
+ content: [
11
+ { type: 'text', text: output.text },
12
+ ...(output.images ?? []).map((image) => ({
13
+ type: 'image',
14
+ data: image.data.toString('base64'),
15
+ mimeType: image.mimeType,
16
+ })),
17
+ ],
18
+ });
19
+ const failure = (message) => ({
20
+ content: [{ type: 'text', text: message }],
21
+ isError: true,
22
+ });
23
+ const INSTRUCTIONS = [
24
+ 'multiclone clones a website from a link into a React + Vite + TypeScript project with the same design, animations and interactions.',
25
+ 'Before a clone, call the `guide` tool (topic "method") and follow it step by step.',
26
+ 'Flow: doctor, capture (also analyses into section bundles), read REPORT.md and sections/<id>/section.md, inspect for anything big, scaffold with capture, rebuild section by section with data-mc-section / data-mc-track / data-mc-hover markers, then serve_mirror and compare against the capture folder until the worst sections are fixed.',
27
+ ].join(' ');
28
+ /** MCP server generated from the tool catalog. */
29
+ export async function runMcpServer() {
30
+ const server = new McpServer({ name: 'multiclone', version: VERSION }, { instructions: INSTRUCTIONS });
31
+ const mirrors = new MirrorRegistry();
32
+ for (const tool of CATALOG) {
33
+ server.registerTool(tool.name, {
34
+ title: tool.title,
35
+ description: tool.description,
36
+ inputSchema: tool.input,
37
+ annotations: { readOnlyHint: tool.readOnly, openWorldHint: tool.openWorld },
38
+ }, async (args, extra) => {
39
+ const context = { mirrors, signal: extra.signal, surface: 'mcp' };
40
+ try {
41
+ const result = await tool.run(args, context);
42
+ if (result.ok)
43
+ return render(result.value);
44
+ const hint = result.error.hint === undefined ? '' : `\nHint: ${result.error.hint}`;
45
+ return failure(`[${result.error.tag}] ${result.error.message}${hint}`);
46
+ }
47
+ catch (error) {
48
+ const mapped = toMcError(error);
49
+ return failure(`[${mapped.tag}] ${mapped.message}`);
50
+ }
51
+ });
52
+ }
53
+ for (const topic of TOPICS) {
54
+ server.registerResource(`guide-${topic}`, `multiclone://guide/${topic}`, { title: `multiclone guide: ${topic}`, mimeType: 'text/markdown' }, async (uri) => ({
55
+ contents: [{ uri: uri.href, mimeType: 'text/markdown', text: await readGuide(topic) }],
56
+ }));
57
+ }
58
+ server.registerPrompt('clone', {
59
+ title: 'Clone a website',
60
+ description: 'Clone a website from a link with the multiclone method.',
61
+ argsSchema: {
62
+ url: z.string().describe('Page to clone.'),
63
+ dir: z.string().optional().describe('Project folder for the rebuild.'),
64
+ },
65
+ }, async ({ url, dir }) => ({
66
+ messages: [
67
+ {
68
+ role: 'user',
69
+ content: {
70
+ type: 'text',
71
+ text: `Clone ${url} with multiclone${dir === undefined ? '' : ` into ${dir}`}. Follow this method step by step; read the other guides with the \`guide\` tool when it points to them.\n\n${await readGuide('method')}`,
72
+ },
73
+ },
74
+ ],
75
+ }));
76
+ const close = async () => {
77
+ await mirrors.stop();
78
+ };
79
+ process.on('SIGINT', () => void close().then(() => process.exit(0)));
80
+ process.on('SIGTERM', () => void close().then(() => process.exit(0)));
81
+ await server.connect(new StdioServerTransport());
82
+ }
83
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/adapters/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, join } from 'node:path';
4
+ import { log } from '../core/log.js';
5
+ import { err, mcError, ok } from '../domain/result.js';
6
+ /**
7
+ * Runs the analysis in an isolated Node child process with heap and time limits, so a huge or
8
+ * hostile bundle can't take the capture/MCP process down. Messages flow back as NDJSON.
9
+ */
10
+ export function analyze(options) {
11
+ const timeoutMs = options.timeoutMs ?? 300_000;
12
+ const heap = options.maxHeapMb ?? 2048;
13
+ const here = dirname(fileURLToPath(import.meta.url));
14
+ const worker = join(here, `worker${import.meta.url.endsWith('.ts') ? '.ts' : '.js'}`);
15
+ const execArgv = import.meta.url.endsWith('.ts') ? ['--import', 'tsx'] : [];
16
+ return new Promise((resolve) => {
17
+ const child = spawn(process.execPath, [
18
+ ...execArgv,
19
+ `--max-old-space-size=${String(heap)}`,
20
+ worker,
21
+ options.dir,
22
+ String(Date.now() + timeoutMs - 10_000),
23
+ ], {
24
+ stdio: ['ignore', 'pipe', 'pipe'],
25
+ });
26
+ let buffer = '';
27
+ let stderr = '';
28
+ let summary = null;
29
+ let failure = null;
30
+ const timer = setTimeout(() => {
31
+ failure = `analysis exceeded ${String(timeoutMs)}ms`;
32
+ child.kill('SIGKILL');
33
+ }, timeoutMs);
34
+ const onAbort = () => {
35
+ failure = 'analysis cancelled';
36
+ child.kill('SIGKILL');
37
+ };
38
+ options.signal?.addEventListener('abort', onAbort, { once: true });
39
+ child.stdout.setEncoding('utf8');
40
+ child.stdout.on('data', (chunk) => {
41
+ buffer += chunk;
42
+ let newline = buffer.indexOf('\n');
43
+ while (newline >= 0) {
44
+ const line = buffer.slice(0, newline).trim();
45
+ buffer = buffer.slice(newline + 1);
46
+ newline = buffer.indexOf('\n');
47
+ if (!line)
48
+ continue;
49
+ let message;
50
+ try {
51
+ message = JSON.parse(line);
52
+ }
53
+ catch {
54
+ continue;
55
+ }
56
+ if (message.type === 'progress')
57
+ log(` analysis: ${message.step}`);
58
+ else if (message.type === 'warning')
59
+ log(` ! ${message.message}`);
60
+ else if (message.type === 'done')
61
+ summary = message.summary;
62
+ else
63
+ failure = message.message;
64
+ }
65
+ });
66
+ child.stderr.setEncoding('utf8');
67
+ child.stderr.on('data', (chunk) => {
68
+ stderr = (stderr + chunk).slice(-4000);
69
+ });
70
+ child.on('close', (code) => {
71
+ clearTimeout(timer);
72
+ options.signal?.removeEventListener('abort', onAbort);
73
+ if (summary) {
74
+ resolve(ok(summary));
75
+ return;
76
+ }
77
+ const reason = failure ??
78
+ (/heap out of memory/i.test(stderr)
79
+ ? `analysis ran out of memory (${String(heap)} MiB)`
80
+ : `analysis worker exited with code ${String(code)}: ${stderr.trim().slice(-400)}`);
81
+ resolve(err(mcError(failure?.includes('exceeded') ? 'timeout' : 'worker', reason)));
82
+ });
83
+ });
84
+ }
85
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/analyze/analyze.ts"],"names":[],"mappings":"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"}