web-reverse-engineer 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Uy Cung
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # Web Reverse-Engineer Skill
2
+
3
+ This skill defines the workflow and helper scripts used to reverse-engineer public websites for the purpose of clean-room reimplementation.
4
+
5
+ ## Overview
6
+
7
+ The `web-reverse-engineer` skill provides instructions and automation to audit reference sites, extract design and animation patterns, and reimplement them cleanly without copying proprietary assets or source code.
8
+
9
+ ### Core Philosophy
10
+
11
+ The primary focus is **clean-room reimplementation**.
12
+ This is **NOT** for copying a website, brand, or exact page expression. The goal is to:
13
+ 1. **Analyze**: Understand how a page does canvas animations, WebGL rendering, scroll triggers, etc.
14
+ 2. **Translate**: Extract high-level mathematical/design principles (e.g. "three blurred canvas blobs moving procedurally").
15
+ 3. **Rebuild**: Implement a distinct, original version (e.g. "SignalForge with teal and purple palettes, new copy, and different speeds").
16
+
17
+ ---
18
+
19
+ ## Folder Structure
20
+
21
+ ```txt
22
+ skills/web-reverse-engineer/
23
+ SKILL.md # The main instruction frontmatter/workflow file
24
+ README.md # This documentation file
25
+ templates/
26
+ clean-room-notes.md # Template for documenting reimplementations
27
+ DESIGN.md # Portable design-system template for demos/projects
28
+ scripts/
29
+ scrape_site_assets.js # Playwright deconstruction crawler
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Clarification Contract
35
+
36
+ Before an agent proposes or implements a reference-inspired demo, it must make the transfer contract explicit. Ask only for missing context, but cover these dimensions:
37
+
38
+ 1. **Target mode**
39
+ - `Motion-only transfer`: preserve layout, copywriting, assets, and brand; apply only animation/background/interaction principles.
40
+ - `Visual restyle`: preserve core content and brand; allow palette, surfaces, spacing, section rhythm, typography treatment, and composition changes.
41
+ - `Editorial redesign`: allow larger layout and copywriting changes while preserving the underlying product/theme.
42
+ - `Full concept demo`: create a new fictional brand/page inspired by the reference, not tied to the existing baseline.
43
+
44
+ Default: when the user says "dựa trên /sample", "giữ brand/copy", or "không sửa /sample", choose `Motion-only transfer` or `Visual restyle`. Do not substantially rewrite copy or layout unless explicitly approved.
45
+
46
+ 2. **Baseline preservation**
47
+ - Confirm whether the source page is protected.
48
+ - Confirm the output route.
49
+ - In this repo, `/sample` is immutable and experiments go under `/demos/...`.
50
+
51
+ 3. **Light vs dark direction**
52
+ - Ask or propose light/dark direction when either could work.
53
+ - Default to light when the user asks to keep the current brand feel.
54
+ - If the reference is dark or the user wants maximum visual impact, propose dark but ask first.
55
+ - Never silently convert a light baseline into dark mode.
56
+
57
+ 4. **Layout and copywriting permission**
58
+ - State what will be preserved.
59
+ - State what may change if approved.
60
+ - Do not rewrite headlines, replace product copy, change core layout, or change brand identity unless the transfer mode allows it.
61
+
62
+ 5. **Asset generation permission**
63
+ - Default to no new image assets.
64
+ - Programmatic canvas/CSS/SVG shapes are allowed when they are part of the implementation and do not copy reference assets.
65
+ - Ask before generating new supporting assets, and never download or reuse proprietary assets from the reference site.
66
+
67
+ 6. **Motion intensity**
68
+ - `Calm production`: subtle, premium, low distraction.
69
+ - `Showcase demo`: clearly visible within 3 seconds.
70
+ - `Debug/analysis`: exaggerated motion or overlays for testing only.
71
+
72
+ Default for public demo routes is `Showcase demo`. If a normal user cannot perceive the animation within 3 seconds without debug mode, the demo fails.
73
+
74
+ 7. **Technical preference**
75
+ - Ask only when relevant: CSS gradients/keyframes, Canvas 2D, WebGL, Framer Motion, simplest robust approach, or matching the reference mechanism where feasible.
76
+ - Default to the simplest robust clean-room approach that achieves the visible effect.
77
+
78
+ 8. **Design documentation**
79
+ - Every demo or real project application must include a `DESIGN.md`.
80
+ - Demo routes use `docs/demos/<demo-slug>/DESIGN.md` and `docs/demos/<demo-slug>/clean-room-notes.md`.
81
+ - If an existing demo uses flat docs such as `docs/demos/<demo-slug>.md`, keep that file working and link it to the folder-based `DESIGN.md` if adding one.
82
+ - Real projects use root-level `DESIGN.md`, updated carefully if it already exists.
83
+
84
+ Use the full Step 2 response format and post-clarification implementation plan in `SKILL.md`.
85
+
86
+ ---
87
+
88
+ ## How to Run the Audit Script
89
+
90
+ The local helper script crawls a target URL and records metadata about active canvas elements, style overrides, and network asset payloads.
91
+
92
+ ### Prerequisites
93
+
94
+ Ensure you have playwright dependencies installed:
95
+ ```bash
96
+ pnpm install
97
+ pnpm exec playwright install chromium
98
+ ```
99
+
100
+ ### Execution Command
101
+
102
+ ```bash
103
+ node skills/web-reverse-engineer/scripts/scrape_site_assets.js <target-url>
104
+ ```
105
+
106
+ For example, to audit JoinClyde:
107
+ ```bash
108
+ node skills/web-reverse-engineer/scripts/scrape_site_assets.js https://www.joinclyde.com
109
+ ```
110
+
111
+ ### Expected Output & Caveats
112
+
113
+ 1. **Manifest File**: Saved to `.tmp/reverse-engineer/[clean-domain]_manifest.json`. To prevent massive file sizes, large lists like loaded assets and animated elements are saved as a summary count with a capped sample of up to 50 items.
114
+ 2. **Screenshot**: Full-page view saved to `.tmp/reverse-engineer/[clean-domain]_screenshot.png`.
115
+ 3. **Terminal Output**: A concise text printout showcasing viewport metadata, canvas styles/counts, and request breakdowns.
116
+ 4. **Canvas/WebGL Caveat**: Detection of WebGL or 2D contexts is **best-effort**. If a site already initialized a WebGL context, the canvas may reject subsequent `.getContext` calls in our evaluator script. Therefore, context type is checked defensively and might show as `already_bound_or_error` or `unknown`.
117
+
118
+ ---
119
+
120
+ ## Clean-Room Rules
121
+
122
+ Every developer/agent building a demo inspired by a reference site must:
123
+ - State the transfer contract before coding.
124
+ - Keep `/sample` unchanged unless the user explicitly requests otherwise.
125
+ - Create a copy of `skills/web-reverse-engineer/templates/clean-room-notes.md` inside `docs/demos/<demo-slug>/clean-room-notes.md` for new demos.
126
+ - Create a `DESIGN.md` from `skills/web-reverse-engineer/templates/DESIGN.md` inside `docs/demos/<demo-slug>/DESIGN.md` for new demos.
127
+ - Document all observed principles, implementation strategies, and differences.
128
+ - Keep `.tmp/` out of Git commits. Keep reference screenshots and manifests local.
129
+ - Never use branding elements, copywriting, or logos from the reference site.
130
+ - Avoid public wording that implies exact copying. Use "inspired by", "reconstruction", or "style transfer".
package/SKILL.md ADDED
@@ -0,0 +1,694 @@
1
+ ---
2
+ name: web-reverse-engineer
3
+ description: "Web reverse-engineering workflow for inspecting public web frontends, extracting design/runtime principles, and rebuilding clean-room implementations inside a target project."
4
+ ---
5
+
6
+ # Web Reverse-Engineering Skill
7
+
8
+ ## Purpose
9
+
10
+ This skill helps an agent inspect a public reference website, understand its visual and runtime mechanisms, and create a **clean-room implementation** inside a target project.
11
+
12
+ It is intended for:
13
+
14
+ - design/style transfer,
15
+ - animation reverse-engineering,
16
+ - canvas/WebGL/background deconstruction,
17
+ - scroll and interaction analysis,
18
+ - runtime UI behavior analysis,
19
+ - creating project-specific implementations inspired by observed principles.
20
+
21
+ It is **not** a website copier, clone generator, or pixel-perfect reproduction tool.
22
+
23
+ The objective is to:
24
+
25
+ 1. Inspect a real website runtime.
26
+ 2. Understand technical and visual mechanisms.
27
+ 3. Extract high-level principles.
28
+ 4. Rebuild an original implementation in the user's project.
29
+ 5. Avoid copying proprietary code, assets, logos, copywriting, brand identity, or exact expressive output.
30
+
31
+ ---
32
+
33
+ ## Core Rules
34
+
35
+ ### Clean-room only
36
+
37
+ Do not copy or reuse:
38
+
39
+ - proprietary source code,
40
+ - minified bundles,
41
+ - shader strings,
42
+ - CSS files,
43
+ - image/video/audio assets,
44
+ - custom fonts,
45
+ - logos,
46
+ - trademarked brand elements,
47
+ - copywriting,
48
+ - exact layouts,
49
+ - exact animation timing or composition.
50
+
51
+ Allowed:
52
+
53
+ - inspect public runtime behavior,
54
+ - record metadata,
55
+ - take screenshots for analysis,
56
+ - extract high-level design and motion principles,
57
+ - rebuild original code using different implementation details,
58
+ - preserve the user's own existing project content when requested.
59
+
60
+ ### Public and accessible references only
61
+
62
+ Do not bypass:
63
+
64
+ - authentication,
65
+ - paywalls,
66
+ - private routes,
67
+ - access controls,
68
+ - anti-scraping protections.
69
+
70
+ If the reference depends on protected assets, private APIs, or proprietary media, state the limitation and propose an original alternative.
71
+
72
+ ### Ask before generating assets
73
+
74
+ Some agents can create images, SVGs, textures, icons, or other assets.
75
+
76
+ Default:
77
+
78
+ - Do not generate new image assets unless the user approves.
79
+ - Programmatic CSS/canvas/SVG shapes are allowed when they are part of the implementation and do not copy reference assets.
80
+ - Never download and reuse proprietary assets from the reference website.
81
+
82
+ ---
83
+
84
+ ## When to Use This Skill
85
+
86
+ Use this skill when the user asks for something like:
87
+
88
+ - “Make this page feel like [reference site].”
89
+ - “Copy the animation style from [reference site], but apply it to my project.”
90
+ - “Inspect this landing page and rebuild the background effect.”
91
+ - “Create a clean-room demo inspired by this website.”
92
+ - “Analyze the canvas/WebGL/scroll effect on this page.”
93
+ - “Extract more than static design tokens from this site.”
94
+
95
+ Do not use this skill for ordinary CSS tweaks, simple component styling, or static page implementation where no reference deconstruction is needed.
96
+
97
+ ---
98
+
99
+ ## Transfer Modes
100
+
101
+ Before planning or coding, identify the requested transfer mode.
102
+
103
+ ### 1. Motion-only transfer
104
+
105
+ Preserve:
106
+
107
+ - existing brand,
108
+ - layout,
109
+ - copywriting,
110
+ - section order,
111
+ - data,
112
+ - local assets.
113
+
114
+ Change only:
115
+
116
+ - animation layer,
117
+ - background behavior,
118
+ - transitions,
119
+ - interaction patterns,
120
+ - supporting motion wrappers.
121
+
122
+ Use this when the user wants to keep their current page but apply a reference-inspired animation or interaction.
123
+
124
+ ### 2. Visual restyle
125
+
126
+ Preserve:
127
+
128
+ - core content,
129
+ - brand,
130
+ - product/theme,
131
+ - general structure.
132
+
133
+ May change:
134
+
135
+ - color palette,
136
+ - surfaces,
137
+ - spacing rhythm,
138
+ - typography treatment,
139
+ - card styling,
140
+ - section atmosphere,
141
+ - motion treatment.
142
+
143
+ Use this when the user wants a recognizable restyle while keeping the same content.
144
+
145
+ ### 3. Editorial redesign
146
+
147
+ Preserve:
148
+
149
+ - underlying product/theme,
150
+ - core message.
151
+
152
+ May change:
153
+
154
+ - layout,
155
+ - copywriting,
156
+ - section rhythm,
157
+ - hierarchy,
158
+ - visual storytelling.
159
+
160
+ Use this only when the user approves broader creative changes.
161
+
162
+ ### 4. Full concept demo
163
+
164
+ Create a new fictional or independent page inspired by the reference.
165
+
166
+ Use this only when the user wants a standalone demo or concept, not an application to an existing page.
167
+
168
+ ---
169
+
170
+ ## Clarification Contract
171
+
172
+ Ask only for missing information. If the user already answered a dimension, record it and move on.
173
+
174
+ Before implementation, make the following explicit:
175
+
176
+ ### Baseline / target project
177
+
178
+ Clarify:
179
+
180
+ - What page, route, component, or project should receive the implementation?
181
+ - Is the source page a protected baseline?
182
+ - Should the implementation modify the existing page or create a separate variant/demo?
183
+ - What is the desired output path?
184
+
185
+ ### Transfer mode
186
+
187
+ State the recommended transfer mode:
188
+
189
+ - Motion-only transfer
190
+ - Visual restyle
191
+ - Editorial redesign
192
+ - Full concept demo
193
+
194
+ Do not substantially rewrite copy or layout unless the user approved a mode that allows it.
195
+
196
+ ### Light vs dark direction
197
+
198
+ If the reference style could work either way, ask or propose:
199
+
200
+ ```md
201
+ Which visual direction do you want?
202
+
203
+ A. Light adaptation
204
+ - Preserve the current light brand feel.
205
+ - Motion must remain visible on a light background.
206
+ - Lower contrast, more editorial.
207
+
208
+ B. Dark adaptation
209
+ - Convert the target/demo route to a darker, more immersive style.
210
+ - Higher contrast and stronger glow/motion.
211
+ - More dramatic departure from the baseline.
212
+
213
+ C. Agent proposes both options first.
214
+ ````
215
+
216
+ Do not silently convert a light baseline into dark mode.
217
+
218
+ ### Layout and copywriting permission
219
+
220
+ State clearly:
221
+
222
+ ```md
223
+ Preserve:
224
+ - Brand
225
+ - Product data
226
+ - Existing copywriting
227
+ - Section order
228
+ - Local assets
229
+
230
+ Allowed to change:
231
+ - Background layer
232
+ - Surface opacity
233
+ - Section spacing
234
+ - Card treatment
235
+ - Motion layer
236
+
237
+ Not allowed unless approved:
238
+ - Rewriting headlines
239
+ - Replacing product copy
240
+ - Changing core layout
241
+ - Changing brand identity
242
+ ```
243
+
244
+ If layout or copywriting changes would improve the result, propose them as optional.
245
+
246
+ ### Asset generation permission
247
+
248
+ Ask when relevant:
249
+
250
+ ```md
251
+ Do you want new supporting assets for this implementation?
252
+
253
+ A. No new assets. Reuse existing local assets only.
254
+ B. Generate simple local SVG/CSS/canvas assets only.
255
+ C. Generate image assets if useful, but ask before each major asset.
256
+ ```
257
+
258
+ ### Motion intensity
259
+
260
+ Ask or infer:
261
+
262
+ ```md
263
+ Motion intensity:
264
+
265
+ A. Calm production
266
+ - Subtle, premium, low distraction.
267
+
268
+ B. Showcase demo
269
+ - Clearly visible within a few seconds.
270
+ - Useful for portfolio/demo proof.
271
+
272
+ C. Debug/analysis
273
+ - Exaggerated motion or overlays for testing only.
274
+ ```
275
+
276
+ For public demos, prefer `Showcase demo` unless the user asks for production subtlety.
277
+
278
+ Acceptance rule:
279
+
280
+ > If a normal viewer cannot perceive the animation in normal mode without debug overlays, mouse movement, or explanation, the animation demo fails.
281
+
282
+ ### Technical preference
283
+
284
+ Ask only if needed:
285
+
286
+ * CSS gradients/keyframes
287
+ * Canvas 2D
288
+ * WebGL
289
+ * Framer Motion
290
+ * simplest robust approach
291
+ * match reference mechanism where feasible
292
+
293
+ Default:
294
+
295
+ > Use the simplest robust clean-room approach that achieves the approved visual effect.
296
+
297
+ ---
298
+
299
+ ## Standard Clarification Output
300
+
301
+ When clarification is needed, respond in this format:
302
+
303
+ ```md
304
+ ## Clarification Needed
305
+
306
+ ### Target
307
+ - Reference:
308
+ - Baseline / target page:
309
+ - Proposed output:
310
+
311
+ ### Baseline preservation
312
+ - I will / will not modify the source page.
313
+
314
+ ### Transfer mode
315
+ Recommended:
316
+ Reason:
317
+
318
+ ### Light or dark direction
319
+ Recommended:
320
+ Reason:
321
+
322
+ ### Layout & copy
323
+ Preserve:
324
+ - ...
325
+
326
+ May change if approved:
327
+ - ...
328
+
329
+ Will not change without approval:
330
+ - ...
331
+
332
+ ### Assets
333
+ Recommended:
334
+ - No new assets / SVG only / generated image assets with permission
335
+
336
+ ### Motion intensity
337
+ Recommended:
338
+ - Calm production / Showcase demo / Debug only
339
+
340
+ ### DESIGN.md
341
+ I will create or update:
342
+ - ...
343
+
344
+ ### Questions
345
+ 1. ...
346
+ 2. ...
347
+ ```
348
+
349
+ If the user already provided enough information, skip clarification and proceed to the implementation plan.
350
+
351
+ ---
352
+
353
+ ## Workflow
354
+
355
+ ### Step 1 — Intake and Contract
356
+
357
+ Collect:
358
+
359
+ * reference URL,
360
+ * target page/project,
361
+ * output path,
362
+ * transfer mode,
363
+ * light/dark direction,
364
+ * layout/copy permissions,
365
+ * asset generation permission,
366
+ * motion intensity,
367
+ * technical constraints,
368
+ * documentation paths.
369
+
370
+ Do not start implementation until the contract is clear enough.
371
+
372
+ ---
373
+
374
+ ### Step 2 — Reference Audit
375
+
376
+ Inspect the reference website using browser automation or devtools.
377
+
378
+ Capture:
379
+
380
+ * page title and URL,
381
+ * viewport size,
382
+ * screenshots,
383
+ * DOM structure around relevant sections,
384
+ * loaded scripts,
385
+ * loaded stylesheets,
386
+ * loaded images/media/fonts,
387
+ * canvas elements,
388
+ * canvas context type if detectable,
389
+ * CSS animations/transitions,
390
+ * transform/filter/mask/blend-mode usage,
391
+ * scroll-linked behavior,
392
+ * runtime animation libraries,
393
+ * WebGL indicators,
394
+ * requestAnimationFrame usage where detectable.
395
+
396
+ Save audit evidence in a temporary project-appropriate location, such as:
397
+
398
+ ```txt
399
+ .tmp/reverse-engineer/<slug>/
400
+ ```
401
+
402
+ unless the target project has a different convention.
403
+
404
+ ---
405
+
406
+ ### Step 3 — Static Design Analysis
407
+
408
+ Extract high-level static design principles:
409
+
410
+ * color families,
411
+ * foreground/background contrast,
412
+ * typography feel,
413
+ * spacing rhythm,
414
+ * layout density,
415
+ * surface treatment,
416
+ * borders/radius/shadows,
417
+ * card treatments,
418
+ * imagery style,
419
+ * section composition.
420
+
421
+ This is similar to static `design.md` extraction, but it is not enough for runtime animation. Continue into motion/runtime analysis when relevant.
422
+
423
+ ---
424
+
425
+ ### Step 4 — Runtime Animation Analysis
426
+
427
+ Analyze:
428
+
429
+ * CSS keyframes,
430
+ * transitions,
431
+ * transform states,
432
+ * masks,
433
+ * blend modes,
434
+ * filters,
435
+ * canvas drawing loops,
436
+ * WebGL contexts,
437
+ * shader indicators,
438
+ * scroll-linked timelines,
439
+ * pointer/mouse interactions,
440
+ * procedural noise or gradient systems.
441
+
442
+ If the effect depends on WebGL and normal inspection is insufficient, recommend deeper inspection with tools such as Spector.js or browser WebGL debuggers.
443
+
444
+ Document only high-level principles. Do not copy shader code, uniforms, textures, or proprietary implementation details.
445
+
446
+ ---
447
+
448
+ ### Step 5 — Clean-Room Transformation
449
+
450
+ Before coding, write a short transformation note:
451
+
452
+ ```md
453
+ Observed principle:
454
+ - ...
455
+
456
+ Clean-room implementation decision:
457
+ - ...
458
+
459
+ What is intentionally changed:
460
+ - ...
461
+ ```
462
+
463
+ Examples:
464
+
465
+ ```md
466
+ Observed principle:
467
+ - The reference uses a soft animated gradient field behind the hero.
468
+
469
+ Clean-room implementation decision:
470
+ - Rebuild with original Canvas 2D radial fields using a different palette, timing model, and composition.
471
+
472
+ What is intentionally changed:
473
+ - Brand, copy, layout rhythm, color values, motion speed, and shape positions.
474
+ ```
475
+
476
+ ---
477
+
478
+ ### Step 6 — Implementation
479
+
480
+ Implement only what the user approved.
481
+
482
+ Requirements:
483
+
484
+ * original code only,
485
+ * no copied reference assets,
486
+ * no copied copywriting,
487
+ * no copied logos,
488
+ * no exact reference layout,
489
+ * preserve baseline content when requested,
490
+ * support responsive layouts,
491
+ * respect `prefers-reduced-motion`,
492
+ * avoid hydration errors,
493
+ * avoid console errors,
494
+ * provide graceful fallback for Canvas/WebGL failures.
495
+
496
+ ---
497
+
498
+ ### Step 7 — DESIGN.md
499
+
500
+ Every real project application or demo should produce a `DESIGN.md`.
501
+
502
+ The file should be portable and useful for future agents/design tools.
503
+
504
+ Recommended sections:
505
+
506
+ ```md
507
+ # DESIGN.md — {Project or Demo Name}
508
+
509
+ ## Purpose
510
+
511
+ ## Design Intent
512
+
513
+ ## Source Context
514
+ - Baseline:
515
+ - Reference:
516
+ - Transfer mode:
517
+
518
+ ## Brand Principles
519
+
520
+ ## Visual System
521
+
522
+ ### Color Tokens
523
+ | Token | Value | Usage |
524
+ |---|---|---|
525
+
526
+ ### Typography
527
+ | Role | Font / Style | Usage |
528
+ |---|---|---|
529
+
530
+ ### Spacing & Layout
531
+
532
+ ### Surfaces
533
+
534
+ ### Borders, Radius, Shadows
535
+
536
+ ### Imagery & Assets
537
+
538
+ ## Component Guidelines
539
+
540
+ ### Header / Navigation
541
+
542
+ ### Hero
543
+
544
+ ### Cards
545
+
546
+ ### CTA
547
+
548
+ ### Footer
549
+
550
+ ## Motion System
551
+
552
+ ### Motion Intent
553
+
554
+ ### Animation Tokens
555
+ | Token | Value | Usage |
556
+ |---|---|---|
557
+
558
+ ### Reduced Motion
559
+
560
+ ## Accessibility & Readability
561
+
562
+ ## Clean-Room Differentiation
563
+
564
+ ## Implementation Notes
565
+
566
+ ## Do / Don’t
567
+ ```
568
+
569
+ Location rules:
570
+
571
+ * For a real user project, create or update root-level `DESIGN.md` unless the user or project convention says otherwise.
572
+ * If a root `DESIGN.md` already exists, update it carefully. Do not overwrite blindly.
573
+ * For local demos or experiments, save `DESIGN.md` near the demo documentation according to that project’s convention.
574
+
575
+ ---
576
+
577
+ ### Step 8 — Evidence and Validation
578
+
579
+ Save evidence that proves the reference was inspected and the implementation works.
580
+
581
+ Prefer:
582
+
583
+ * reference screenshot,
584
+ * audit manifest,
585
+ * transformation notes,
586
+ * implementation screenshot,
587
+ * normal-mode animation screenshots or video,
588
+ * reduced-motion screenshot if relevant.
589
+
590
+ Debug screenshots are allowed, but cannot be the only proof.
591
+
592
+ Run the project’s validation commands, usually:
593
+
594
+ ```bash
595
+ pnpm lint
596
+ pnpm typecheck
597
+ pnpm build
598
+ ```
599
+
600
+ or the equivalent package-manager scripts.
601
+
602
+ For animation demos, build success is not enough. Verify normal viewer perception:
603
+
604
+ * visible in normal mode,
605
+ * no debug mode required,
606
+ * no mouse movement required unless the effect is explicitly pointer-driven,
607
+ * no explanation required to notice the effect.
608
+
609
+ ---
610
+
611
+ ## Standard Implementation Plan Output
612
+
613
+ After clarification, respond with:
614
+
615
+ ```md
616
+ ## Implementation Plan
617
+
618
+ ### Output
619
+ - Route/component/path:
620
+
621
+ ### Transfer contract
622
+ - Mode:
623
+ - Light/dark:
624
+ - Preserve:
625
+ - Allowed changes:
626
+ - Not allowed:
627
+ - Assets:
628
+ - Motion intensity:
629
+
630
+ ### Reference findings
631
+ - ...
632
+
633
+ ### Clean-room transformation
634
+ Observed principle:
635
+ - ...
636
+
637
+ Implementation decision:
638
+ - ...
639
+
640
+ Intentionally changed:
641
+ - ...
642
+
643
+ ### DESIGN.md output
644
+ - ...
645
+
646
+ ### Files to create/change
647
+ - ...
648
+
649
+ ### Validation
650
+ - ...
651
+ ```
652
+
653
+ ---
654
+
655
+ ## Standard Final Report
656
+
657
+ After implementation, report:
658
+
659
+ ```md
660
+ ## Completed
661
+
662
+ ### Output
663
+ - ...
664
+
665
+ ### Reference inspected
666
+ - ...
667
+
668
+ ### Transfer contract followed
669
+ - ...
670
+
671
+ ### What changed
672
+ - ...
673
+
674
+ ### What was preserved
675
+ - ...
676
+
677
+ ### What was not copied
678
+ - ...
679
+
680
+ ### DESIGN.md
681
+ - ...
682
+
683
+ ### Evidence
684
+ - ...
685
+
686
+ ### Validation
687
+ - Lint:
688
+ - Typecheck:
689
+ - Build:
690
+ - Browser/runtime QA:
691
+
692
+ ### Notes
693
+ - ...
694
+ ```
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "web-reverse-engineer",
3
+ "version": "0.1.0",
4
+ "description": "Agent skill for clean-room web reverse engineering: inspect a public reference site, extract high-level animation/design principles, and rebuild an original, brand-distinct implementation.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/uycung/web-reverse-engineer.git",
9
+ "directory": "skills/web-reverse-engineer"
10
+ },
11
+ "keywords": [
12
+ "agent-skill",
13
+ "claude-code",
14
+ "skill",
15
+ "web-design",
16
+ "clean-room",
17
+ "reverse-engineering",
18
+ "animation",
19
+ "playwright",
20
+ "design-transfer"
21
+ ],
22
+ "files": [
23
+ "SKILL.md",
24
+ "scripts/"
25
+ ]
26
+ }
@@ -0,0 +1,285 @@
1
+ const { chromium } = require('playwright');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ // Extract target URL from CLI arguments
6
+ const targetUrl = process.argv[2];
7
+ if (!targetUrl) {
8
+ console.error('Error: Please provide a target URL.');
9
+ console.log('Usage: node skills/web-reverse-engineer/scripts/scrape_site_assets.js <url>');
10
+ process.exit(1);
11
+ }
12
+
13
+ // Validate URL format
14
+ try {
15
+ new URL(targetUrl);
16
+ } catch (e) {
17
+ console.error(`Error: Invalid URL "${targetUrl}". Please include protocol (e.g., https://).`);
18
+ process.exit(1);
19
+ }
20
+
21
+ (async () => {
22
+ console.log(`Starting clean-room deconstruction audit for: ${targetUrl}`);
23
+ console.log('Launching headless Chromium via Playwright...');
24
+
25
+ const browser = await chromium.launch({ headless: true });
26
+ const context = await browser.newContext({
27
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
28
+ });
29
+ const page = await context.newPage();
30
+
31
+ // Storage for categorized network requests
32
+ const assets = {
33
+ scripts: [],
34
+ stylesheets: [],
35
+ images: [],
36
+ fonts: [],
37
+ media: [],
38
+ fetchXhr: [],
39
+ other: []
40
+ };
41
+
42
+ // Track network requests and group them by type
43
+ page.on('request', request => {
44
+ const url = request.url();
45
+ const type = request.resourceType();
46
+ const method = request.method();
47
+
48
+ // We only record metadata of the requests, never downloading asset contents
49
+ const assetMeta = { url, method };
50
+
51
+ if (type === 'script') {
52
+ assets.scripts.push(assetMeta);
53
+ } else if (type === 'stylesheet') {
54
+ assets.stylesheets.push(assetMeta);
55
+ } else if (type === 'image') {
56
+ assets.images.push(assetMeta);
57
+ } else if (type === 'font') {
58
+ assets.fonts.push(assetMeta);
59
+ } else if (type === 'media') {
60
+ assets.media.push(assetMeta);
61
+ } else if (type === 'fetch' || type === 'xhr') {
62
+ assets.fetchXhr.push(assetMeta);
63
+ } else {
64
+ assets.other.push(assetMeta);
65
+ }
66
+ });
67
+
68
+ try {
69
+ console.log(`Navigating to ${targetUrl} (waiting for network idle)...`);
70
+ await page.goto(targetUrl, { waitUntil: 'networkidle', timeout: 35000 });
71
+
72
+ // Additional wait for client-side animations and canvas setups to run
73
+ await page.waitForTimeout(3000);
74
+ console.log('Page loaded. Analyzing runtime elements...');
75
+
76
+ // Run DOM audit in page context
77
+ const auditResults = await page.evaluate(() => {
78
+ // 1. Inspect Canvases (Best-Effort detection)
79
+ const canvases = Array.from(document.querySelectorAll('canvas'));
80
+ const canvasDetails = canvases.map((canvas, index) => {
81
+ let contextType = 'unknown';
82
+ try {
83
+ // Attempt best-effort context detection without breaking active renderings
84
+ // Some sites might throw if getContext is called after it's already bound to a different context, so we try-catch.
85
+ const gl = canvas.getContext('webgl') || canvas.getContext('webgl2') || canvas.getContext('experimental-webgl');
86
+ if (gl) {
87
+ contextType = gl instanceof WebGL2RenderingContext ? 'webgl2' : 'webgl';
88
+ } else {
89
+ const ctx = canvas.getContext('2d');
90
+ if (ctx) {
91
+ contextType = '2d';
92
+ }
93
+ }
94
+ } catch (e) {
95
+ contextType = 'already_bound_or_error';
96
+ }
97
+
98
+ const rect = canvas.getBoundingClientRect();
99
+ const style = window.getComputedStyle(canvas);
100
+
101
+ return {
102
+ index,
103
+ id: canvas.id || null,
104
+ className: canvas.className || null,
105
+ attributes: {
106
+ width: canvas.getAttribute('width'),
107
+ height: canvas.getAttribute('height')
108
+ },
109
+ renderedSize: {
110
+ width: Math.round(rect.width),
111
+ height: Math.round(rect.height)
112
+ },
113
+ styles: {
114
+ display: style.display,
115
+ position: style.position,
116
+ transform: style.transform !== 'none' ? style.transform : null,
117
+ filter: style.filter !== 'none' ? style.filter : null,
118
+ opacity: style.opacity,
119
+ zIndex: style.zIndex !== 'auto' ? style.zIndex : null
120
+ },
121
+ contextType
122
+ };
123
+ });
124
+
125
+ // 2. Inspect likely animated elements (animations, transitions, active transforms/filters/masks)
126
+ const allElements = Array.from(document.querySelectorAll('*'));
127
+ const animatedElements = [];
128
+
129
+ allElements.forEach(el => {
130
+ const style = window.getComputedStyle(el);
131
+
132
+ const hasAnim = style.animationName !== 'none' && style.animationDuration !== '0s';
133
+ const hasTrans = style.transitionProperty !== 'none' && style.transitionDuration !== '0s';
134
+ const hasTransform = style.transform !== 'none';
135
+ const hasFilter = style.filter !== 'none' || style.backdropFilter !== 'none';
136
+ const hasBlend = style.mixBlendMode !== 'normal';
137
+ const hasMask = style.maskImage !== 'none' || style.webkitMaskImage !== 'none';
138
+
139
+ if (hasAnim || hasTrans || hasTransform || hasFilter || hasBlend || hasMask) {
140
+ // Construct a selector or identifier for the element
141
+ let identifier = el.tagName.toLowerCase();
142
+ if (el.id) {
143
+ identifier += `#${el.id}`;
144
+ } else if (el.className) {
145
+ identifier += `.${Array.from(el.classList).join('.')}`;
146
+ }
147
+
148
+ animatedElements.push({
149
+ tag: el.tagName.toLowerCase(),
150
+ identifier,
151
+ indicators: {
152
+ animation: hasAnim ? { name: style.animationName, duration: style.animationDuration } : null,
153
+ transition: hasTrans ? { property: style.transitionProperty, duration: style.transitionDuration } : null,
154
+ transform: hasTransform ? style.transform : null,
155
+ filter: hasFilter ? { filter: style.filter, backdropFilter: style.backdropFilter } : null,
156
+ mixBlendMode: hasBlend ? style.mixBlendMode : null,
157
+ mask: hasMask ? 'detected' : null
158
+ }
159
+ });
160
+ }
161
+ });
162
+
163
+ // Page basic metadata
164
+ const rootClasses = document.documentElement.className || '';
165
+ const bodyClasses = document.body.className || '';
166
+ const title = document.title;
167
+ const viewport = document.querySelector('meta[name="viewport"]')?.getAttribute('content') || null;
168
+
169
+ return {
170
+ metadata: {
171
+ title,
172
+ viewport,
173
+ rootClasses,
174
+ bodyClasses
175
+ },
176
+ canvases: canvasDetails,
177
+ animatedElements
178
+ };
179
+ });
180
+
181
+ // Helper to slice sample data and keep JSON output bounded
182
+ const limitSample = (arr, limit = 50) => {
183
+ if (arr.length <= limit) return arr;
184
+ return arr.slice(0, limit);
185
+ };
186
+
187
+ // Construct bounded manifest
188
+ const manifest = {
189
+ targetUrl,
190
+ timestamp: new Date().toISOString(),
191
+ metadata: auditResults.metadata,
192
+ canvases: auditResults.canvases,
193
+ canvasCaveats: "Canvas/WebGL context detection is best-effort. Active WebGL drawing contexts may already be locked by runtime frameworks, rendering them undetectable via getContext in this script.",
194
+ assetsSummary: {
195
+ scriptsCount: assets.scripts.length,
196
+ stylesheetsCount: assets.stylesheets.length,
197
+ imagesCount: assets.images.length,
198
+ fontsCount: assets.fonts.length,
199
+ mediaCount: assets.media.length,
200
+ fetchXhrCount: assets.fetchXhr.length,
201
+ otherCount: assets.other.length,
202
+ totalAssets: Object.values(assets).reduce((acc, curr) => acc + curr.length, 0)
203
+ },
204
+ // Store total counts and capped samples so the file does not grow massive
205
+ assetsSamples: {
206
+ scripts: limitSample(assets.scripts),
207
+ stylesheets: limitSample(assets.stylesheets),
208
+ images: limitSample(assets.images),
209
+ fonts: limitSample(assets.fonts),
210
+ media: limitSample(assets.media),
211
+ fetchXhr: limitSample(assets.fetchXhr),
212
+ other: limitSample(assets.other)
213
+ },
214
+ animatedElementsSummary: {
215
+ totalDetected: auditResults.animatedElements.length,
216
+ samples: limitSample(auditResults.animatedElements)
217
+ }
218
+ };
219
+
220
+ // Ensure output directories exist
221
+ const outputDir = path.join(process.cwd(), '.tmp', 'reverse-engineer');
222
+ if (!fs.existsSync(outputDir)) {
223
+ fs.mkdirSync(outputDir, { recursive: true });
224
+ }
225
+
226
+ const domainName = new URL(targetUrl).hostname.replace(/\./g, '_');
227
+ const manifestPath = path.join(outputDir, `${domainName}_manifest.json`);
228
+ const screenshotPath = path.join(outputDir, `${domainName}_screenshot.png`);
229
+
230
+ // Save JSON manifest
231
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
232
+
233
+ // Save screenshot
234
+ await page.screenshot({ path: screenshotPath, fullPage: true });
235
+
236
+ // Print summary to terminal
237
+ console.log('\n==================================================');
238
+ console.log(' DECONSTRUCTION AUDIT SUMMARY (PLAYWRIGHT)');
239
+ console.log('==================================================');
240
+ console.log(`Target URL: ${targetUrl}`);
241
+ console.log(`Page Title: ${auditResults.metadata.title}`);
242
+ console.log(`Viewport Meta: ${auditResults.metadata.viewport}`);
243
+ console.log(`Root HTML Class: "${auditResults.metadata.rootClasses}"`);
244
+ console.log(`Body Class: "${auditResults.metadata.bodyClasses}"`);
245
+ console.log(`Canvas Elements: ${auditResults.canvases.length} detected`);
246
+
247
+ if (auditResults.canvases.length > 0) {
248
+ console.log('\n--- Canvas Details (Best-effort detection) ---');
249
+ auditResults.canvases.forEach(c => {
250
+ console.log(` Canvas #${c.index}: id="${c.id}" class="${c.className}" type=${c.contextType}`);
251
+ console.log(` Attributes: ${c.attributes.width || 'none'}x${c.attributes.height || 'none'}`);
252
+ console.log(` Rendered size: ${c.renderedSize.width}x${c.renderedSize.height}`);
253
+ console.log(` Positioning: display=${c.styles.display}, position=${c.styles.position}, zIndex=${c.styles.zIndex || 'none'}`);
254
+ if (c.styles.transform) console.log(` Transform: ${c.styles.transform}`);
255
+ if (c.styles.filter) console.log(` Filter: ${c.styles.filter}`);
256
+ });
257
+ }
258
+
259
+ console.log('\n--- Network Request Summary ---');
260
+ console.log(` Scripts: ${assets.scripts.length}`);
261
+ console.log(` Stylesheets: ${assets.stylesheets.length}`);
262
+ console.log(` Images: ${assets.images.length}`);
263
+ console.log(` Fonts: ${assets.fonts.length}`);
264
+ console.log(` Media: ${assets.media.length}`);
265
+ console.log(` Fetch/XHR: ${assets.fetchXhr.length}`);
266
+ console.log(` Others: ${assets.other.length}`);
267
+ console.log(` Total Requests: ${manifest.assetsSummary.totalAssets}`);
268
+
269
+ console.log('\n--- Animation Indicators ---');
270
+ console.log(` Likely animated elements: ${auditResults.animatedElements.length} detected`);
271
+ console.log(` (Saved first ${manifest.animatedElementsSummary.samples.length} elements to manifest samples)`);
272
+
273
+ console.log('\n==================================================');
274
+ console.log(`Manifest saved to: ${manifestPath}`);
275
+ console.log(`Screenshot saved to: ${screenshotPath}`);
276
+ console.log('NOTE: Proprietary asset contents have NOT been downloaded.');
277
+ console.log('Canvas/WebGL detection is best-effort; drawing contexts may be locked.');
278
+ console.log('==================================================\n');
279
+
280
+ } catch (error) {
281
+ console.error('An error occurred during deconstruction:', error);
282
+ } finally {
283
+ await browser.close();
284
+ }
285
+ })();