design-clone 1.0.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 (47) hide show
  1. package/.env.example +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +166 -0
  4. package/SKILL.md +239 -0
  5. package/bin/cli.js +45 -0
  6. package/bin/commands/help.js +29 -0
  7. package/bin/commands/init.js +126 -0
  8. package/bin/commands/verify.js +99 -0
  9. package/bin/utils/copy.js +65 -0
  10. package/bin/utils/validate.js +122 -0
  11. package/docs/basic-clone.md +63 -0
  12. package/docs/cli-reference.md +94 -0
  13. package/docs/design-clone-architecture.md +247 -0
  14. package/docs/pixel-perfect.md +86 -0
  15. package/docs/troubleshooting.md +97 -0
  16. package/package.json +57 -0
  17. package/requirements.txt +5 -0
  18. package/src/ai/analyze-structure.py +305 -0
  19. package/src/ai/extract-design-tokens.py +439 -0
  20. package/src/ai/prompts/__init__.py +2 -0
  21. package/src/ai/prompts/design_tokens.py +183 -0
  22. package/src/ai/prompts/structure_analysis.py +273 -0
  23. package/src/core/cookie-handler.js +76 -0
  24. package/src/core/css-extractor.js +107 -0
  25. package/src/core/dimension-extractor.js +366 -0
  26. package/src/core/dimension-output.js +208 -0
  27. package/src/core/extract-assets.js +468 -0
  28. package/src/core/filter-css.js +499 -0
  29. package/src/core/html-extractor.js +102 -0
  30. package/src/core/lazy-loader.js +188 -0
  31. package/src/core/page-readiness.js +161 -0
  32. package/src/core/screenshot.js +380 -0
  33. package/src/post-process/enhance-assets.js +157 -0
  34. package/src/post-process/fetch-images.js +398 -0
  35. package/src/post-process/inject-icons.js +311 -0
  36. package/src/utils/__init__.py +16 -0
  37. package/src/utils/__pycache__/__init__.cpython-313.pyc +0 -0
  38. package/src/utils/__pycache__/env.cpython-313.pyc +0 -0
  39. package/src/utils/browser.js +103 -0
  40. package/src/utils/env.js +153 -0
  41. package/src/utils/env.py +134 -0
  42. package/src/utils/helpers.js +71 -0
  43. package/src/utils/puppeteer.js +281 -0
  44. package/src/verification/verify-layout.js +424 -0
  45. package/src/verification/verify-menu.js +422 -0
  46. package/templates/base.css +705 -0
  47. package/templates/base.html +293 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Verify command - check installation status
3
+ */
4
+
5
+ import fs from 'fs/promises';
6
+ import path from 'path';
7
+ import { exists } from '../utils/copy.js';
8
+ import { runAllChecks } from '../utils/validate.js';
9
+
10
+ const getSkillDir = () => path.join(process.env.HOME || process.env.USERPROFILE || '', '.claude/skills/design-clone');
11
+
12
+ /**
13
+ * Verify skill installation
14
+ */
15
+ export async function verify() {
16
+ const SKILL_DIR = getSkillDir();
17
+ let allOk = true;
18
+
19
+ console.log('design-clone skill verification\n');
20
+
21
+ // Check skill directory
22
+ console.log('Installation:');
23
+ const skillExists = await exists(SKILL_DIR);
24
+ console.log(` Skill directory: ${skillExists ? '✓' : '✗'} ${SKILL_DIR}`);
25
+ if (!skillExists) {
26
+ console.log('\n Skill not installed. Run: design-clone init');
27
+ return;
28
+ }
29
+
30
+ // Check required files
31
+ const requiredFiles = [
32
+ 'SKILL.md',
33
+ 'multi-screenshot.js',
34
+ 'filter-css.js',
35
+ 'analyze-structure.py',
36
+ 'lib/browser.js',
37
+ 'lib/env.js',
38
+ 'lib/env.py',
39
+ 'requirements.txt'
40
+ ];
41
+
42
+ let filesOk = true;
43
+ for (const file of requiredFiles) {
44
+ const filePath = path.join(SKILL_DIR, file);
45
+ const fileExists = await exists(filePath);
46
+ if (!fileExists) {
47
+ console.log(` ${file}: ✗ missing`);
48
+ filesOk = false;
49
+ }
50
+ }
51
+ if (filesOk) {
52
+ console.log(` Required files: ✓ all present`);
53
+ } else {
54
+ allOk = false;
55
+ }
56
+
57
+ // Check node_modules
58
+ const nodeModulesExists = await exists(path.join(SKILL_DIR, 'node_modules'));
59
+ console.log(` Node modules: ${nodeModulesExists ? '✓' : '✗'} ${nodeModulesExists ? 'installed' : 'not installed'}`);
60
+ if (!nodeModulesExists) allOk = false;
61
+
62
+ // Check environment
63
+ console.log('\nEnvironment:');
64
+ const checks = await runAllChecks();
65
+
66
+ console.log(` Node.js: ${checks.node.ok ? '✓' : '✗'} ${checks.node.message}`);
67
+ console.log(` Python: ${checks.python.ok ? '✓' : '✗'} ${checks.python.message}`);
68
+ console.log(` Chrome: ${checks.chrome.ok ? '✓' : '✗'} ${checks.chrome.message}`);
69
+
70
+ if (!checks.node.ok) allOk = false;
71
+
72
+ // Check Gemini API key
73
+ console.log('\nOptional:');
74
+ const geminiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
75
+ console.log(` GEMINI_API_KEY: ${geminiKey ? '✓ set' : '○ not set (AI analysis disabled)'}`);
76
+
77
+ // Check .env files
78
+ const envLocations = [
79
+ path.join(SKILL_DIR, '.env'),
80
+ path.join(process.env.HOME || '', '.claude/skills/.env'),
81
+ path.join(process.env.HOME || '', '.claude/.env')
82
+ ];
83
+
84
+ for (const envPath of envLocations) {
85
+ const envExists = await exists(envPath);
86
+ if (envExists) {
87
+ console.log(` .env found: ${envPath}`);
88
+ break;
89
+ }
90
+ }
91
+
92
+ // Summary
93
+ console.log('\nStatus:');
94
+ if (allOk) {
95
+ console.log(' ✓ Ready to use! Try /design:clone in Claude Code');
96
+ } else {
97
+ console.log(' ✗ Some issues found. Run: design-clone init --force');
98
+ }
99
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * File copy utilities
3
+ */
4
+
5
+ import fs from 'fs/promises';
6
+ import path from 'path';
7
+
8
+ /**
9
+ * Copy directory recursively
10
+ * @param {string} src - Source directory
11
+ * @param {string} dest - Destination directory
12
+ * @param {Object} options - Options
13
+ * @param {string[]} options.exclude - Patterns to exclude
14
+ */
15
+ export async function copyRecursive(src, dest, options = {}) {
16
+ const exclude = options.exclude || [
17
+ 'node_modules',
18
+ '.git',
19
+ '__pycache__',
20
+ 'test-*.js',
21
+ 'run-all-tests.js',
22
+ '.DS_Store',
23
+ '*.log'
24
+ ];
25
+
26
+ await fs.mkdir(dest, { recursive: true });
27
+
28
+ const entries = await fs.readdir(src, { withFileTypes: true });
29
+
30
+ for (const entry of entries) {
31
+ const srcPath = path.join(src, entry.name);
32
+ const destPath = path.join(dest, entry.name);
33
+
34
+ // Check exclusions
35
+ const shouldExclude = exclude.some(pattern => {
36
+ if (pattern.includes('*')) {
37
+ const regex = new RegExp('^' + pattern.replace('*', '.*') + '$');
38
+ return regex.test(entry.name);
39
+ }
40
+ return entry.name === pattern;
41
+ });
42
+
43
+ if (shouldExclude) continue;
44
+
45
+ if (entry.isDirectory()) {
46
+ await copyRecursive(srcPath, destPath, options);
47
+ } else {
48
+ await fs.copyFile(srcPath, destPath);
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Check if path exists
55
+ * @param {string} filePath - Path to check
56
+ * @returns {Promise<boolean>}
57
+ */
58
+ export async function exists(filePath) {
59
+ try {
60
+ await fs.access(filePath);
61
+ return true;
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Environment validation utilities
3
+ */
4
+
5
+ import { exec as execCallback } from 'child_process';
6
+ import { promisify } from 'util';
7
+
8
+ const exec = promisify(execCallback);
9
+
10
+ /**
11
+ * Check Node.js version
12
+ * @returns {Promise<{ok: boolean, version: string, message: string}>}
13
+ */
14
+ export async function checkNode() {
15
+ try {
16
+ const { stdout } = await exec('node --version');
17
+ const version = stdout.trim();
18
+ const major = parseInt(version.slice(1).split('.')[0], 10);
19
+
20
+ if (major >= 18) {
21
+ return { ok: true, version, message: `Node.js ${version}` };
22
+ }
23
+ return { ok: false, version, message: `Node.js ${version} (requires >=18)` };
24
+ } catch {
25
+ return { ok: false, version: 'unknown', message: 'Node.js not found' };
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Check Python version
31
+ * @returns {Promise<{ok: boolean, version: string, message: string}>}
32
+ */
33
+ export async function checkPython() {
34
+ try {
35
+ const { stdout } = await exec('python3 --version');
36
+ const version = stdout.trim().replace('Python ', '');
37
+ const [major, minor] = version.split('.').map(Number);
38
+
39
+ if (major >= 3 && minor >= 9) {
40
+ return { ok: true, version, message: `Python ${version}` };
41
+ }
42
+ return { ok: false, version, message: `Python ${version} (requires >=3.9)` };
43
+ } catch {
44
+ return { ok: false, version: 'unknown', message: 'Python 3 not found' };
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Check Chrome/Chromium
50
+ * @returns {Promise<{ok: boolean, path: string, message: string}>}
51
+ */
52
+ export async function checkChrome() {
53
+ const paths = {
54
+ darwin: [
55
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
56
+ '/Applications/Chromium.app/Contents/MacOS/Chromium'
57
+ ],
58
+ linux: [
59
+ '/usr/bin/google-chrome',
60
+ '/usr/bin/chromium-browser',
61
+ '/usr/bin/chromium'
62
+ ],
63
+ win32: [
64
+ 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
65
+ 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
66
+ ]
67
+ };
68
+
69
+ const platformPaths = paths[process.platform] || [];
70
+
71
+ for (const chromePath of platformPaths) {
72
+ try {
73
+ const fs = await import('fs/promises');
74
+ await fs.access(chromePath);
75
+ return { ok: true, path: chromePath, message: 'Chrome found' };
76
+ } catch {
77
+ // Continue to next path
78
+ }
79
+ }
80
+
81
+ // Try which/where command
82
+ try {
83
+ const cmd = process.platform === 'win32' ? 'where chrome' : 'which google-chrome || which chromium';
84
+ const { stdout } = await exec(cmd);
85
+ const found = stdout.trim().split('\n')[0];
86
+ if (found) {
87
+ return { ok: true, path: found, message: 'Chrome found' };
88
+ }
89
+ } catch {
90
+ // Not found
91
+ }
92
+
93
+ return { ok: false, path: '', message: 'Chrome/Chromium not found' };
94
+ }
95
+
96
+ /**
97
+ * Check Puppeteer
98
+ * @returns {Promise<{ok: boolean, message: string}>}
99
+ */
100
+ export async function checkPuppeteer() {
101
+ try {
102
+ await import('puppeteer');
103
+ return { ok: true, message: 'Puppeteer installed' };
104
+ } catch {
105
+ return { ok: false, message: 'Puppeteer not installed (optional)' };
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Run all checks
111
+ * @returns {Promise<Object>}
112
+ */
113
+ export async function runAllChecks() {
114
+ const [node, python, chrome, puppeteer] = await Promise.all([
115
+ checkNode(),
116
+ checkPython(),
117
+ checkChrome(),
118
+ checkPuppeteer()
119
+ ]);
120
+
121
+ return { node, python, chrome, puppeteer };
122
+ }
@@ -0,0 +1,63 @@
1
+ # Basic Clone Workflow
2
+
3
+ Quick website design capture with screenshots and source extraction.
4
+
5
+ ## When to Use
6
+
7
+ - Quick design reference/inspiration
8
+ - Simple single-page sites
9
+ - Initial exploration before pixel-perfect clone
10
+
11
+ ## Steps
12
+
13
+ ### 1. Capture Screenshots + Extract Source
14
+
15
+ ```bash
16
+ node src/core/screenshot.js \
17
+ --url "https://example.com" \
18
+ --output ./cloned-design \
19
+ --extract-html \
20
+ --extract-css
21
+ ```
22
+
23
+ ### 2. Filter Unused CSS (Optional)
24
+
25
+ ```bash
26
+ node src/core/filter-css.js \
27
+ --html ./cloned-design/source.html \
28
+ --css ./cloned-design/source-raw.css \
29
+ --output ./cloned-design/source.css
30
+ ```
31
+
32
+ ## Output Files
33
+
34
+ | File | Description |
35
+ |------|-------------|
36
+ | desktop.png | 1920x1080 viewport screenshot |
37
+ | tablet.png | 768x1024 viewport screenshot |
38
+ | mobile.png | 375x812 viewport screenshot |
39
+ | source.html | Cleaned HTML (inline styles removed) |
40
+ | source-raw.css | All extracted CSS (unfiltered) |
41
+ | source.css | Filtered CSS (after filter-css.js) |
42
+
43
+ ## Common Options
44
+
45
+ ```bash
46
+ # Custom viewports
47
+ --viewports '[{"width":1440,"height":900,"name":"laptop"}]'
48
+
49
+ # Full page capture
50
+ --full-page
51
+
52
+ # Wait for animations
53
+ --wait 3000
54
+
55
+ # Skip HTML extraction
56
+ --extract-html false
57
+ ```
58
+
59
+ ## Tips
60
+
61
+ - Add `--wait 2000` for sites with loading animations
62
+ - Use `--full-page` for long scrolling pages
63
+ - Check `source.html` for missing sections before pixel-perfect
@@ -0,0 +1,94 @@
1
+ # CLI Reference
2
+
3
+ All script options and parameters.
4
+
5
+ ## screenshot.js
6
+
7
+ Core screenshot and extraction tool.
8
+
9
+ ```bash
10
+ node src/core/screenshot.js [options]
11
+ ```
12
+
13
+ | Option | Type | Default | Description |
14
+ |--------|------|---------|-------------|
15
+ | --url | string | required | Target URL |
16
+ | --output | string | required | Output directory |
17
+ | --viewports | string | all | Comma-separated: desktop,tablet,mobile |
18
+ | --full-page | bool | true | Capture full page height |
19
+ | --max-size | number | 5 | Max file size in MB before compression |
20
+ | --headless | bool | false | Run in headless mode (desktop always uses headless) |
21
+ | --scroll-delay | number | 1500 | Pause time in ms between scroll steps for lazy content |
22
+ | --close | bool | false | Close browser after capture (false keeps session) |
23
+ | --extract-html | bool | false | Extract cleaned HTML |
24
+ | --extract-css | bool | false | Extract all CSS from page |
25
+ | --filter-unused | bool | true | Filter CSS to remove unused selectors |
26
+ | --verbose | bool | false | Verbose logging |
27
+
28
+ **Output**: JSON with screenshot paths and metadata. Includes `browserRestarts` count tracking for stability monitoring.
29
+
30
+ ## filter-css.js
31
+
32
+ Remove unused CSS selectors.
33
+
34
+ ```bash
35
+ node src/core/filter-css.js --html FILE --css FILE --output FILE [--verbose]
36
+ ```
37
+
38
+ | Option | Required | Description |
39
+ |--------|----------|-------------|
40
+ | --html | yes | Source HTML file |
41
+ | --css | yes | Raw CSS file |
42
+ | --output | yes | Filtered CSS output |
43
+ | --verbose | no | Show stats |
44
+
45
+ ## analyze-structure.py
46
+
47
+ AI structure analysis with Gemini.
48
+
49
+ ```bash
50
+ python src/ai/analyze-structure.py -s SCREENSHOT -o OUTPUT [options]
51
+ ```
52
+
53
+ | Option | Required | Description |
54
+ |--------|----------|-------------|
55
+ | -s, --screenshot | yes | Desktop screenshot |
56
+ | -o, --output | yes | Output directory |
57
+ | --html | no | Source HTML (improves accuracy) |
58
+ | --css | no | Source CSS (improves accuracy) |
59
+ | --model | no | Gemini model (default: gemini-2.5-flash) |
60
+ | -v, --verbose | no | Verbose output |
61
+
62
+ ## extract-design-tokens.py
63
+
64
+ Extract colors, typography, spacing.
65
+
66
+ ```bash
67
+ python src/ai/extract-design-tokens.py -s SCREENSHOT -o OUTPUT [options]
68
+ ```
69
+
70
+ Same options as analyze-structure.py.
71
+
72
+ ## extract-assets.js
73
+
74
+ Download images, fonts, icons.
75
+
76
+ ```bash
77
+ node src/core/extract-assets.js --url URL --output DIR
78
+ ```
79
+
80
+ ## verify-menu.js
81
+
82
+ Validate navigation structure.
83
+
84
+ ```bash
85
+ node src/verification/verify-menu.js --html FILE
86
+ ```
87
+
88
+ ## verify-layout.js
89
+
90
+ Verify layout consistency.
91
+
92
+ ```bash
93
+ node src/verification/verify-layout.js --html FILE
94
+ ```
@@ -0,0 +1,247 @@
1
+ # Design Clone Skill Architecture
2
+
3
+ Technical architecture of the design-clone skill for Claude Code.
4
+
5
+ ## Overview
6
+
7
+ ```
8
+ design-clone/
9
+ ├── SKILL.md # Entry point
10
+ ├── bin/ # npm CLI tool
11
+ │ ├── cli.js
12
+ │ ├── commands/
13
+ │ └── utils/
14
+ ├── src/
15
+ │ ├── core/ # Core scripts
16
+ │ ├── ai/ # AI analysis
17
+ │ ├── verification/ # Verification scripts
18
+ │ ├── post-process/ # Post-processing
19
+ │ └── utils/ # Shared utilities
20
+ ├── docs/ # Documentation
21
+ ├── templates/ # Output templates
22
+ └── tests/ # Test files
23
+ ```
24
+
25
+ ## Architecture Diagram
26
+
27
+ ```
28
+ ┌─────────────────────────────────────────────────────────────────┐
29
+ │ Claude Code │
30
+ │ ┌──────────────────┐ ┌──────────────────┐ │
31
+ │ │ /design:clone │ │ /design:clone-px │ │
32
+ │ └────────┬─────────┘ └────────┬─────────┘ │
33
+ └───────────┼─────────────────────┼───────────────────────────────┘
34
+ │ │
35
+ ▼ ▼
36
+ ┌─────────────────────────────────────────────────────────────────┐
37
+ │ SKILL.md │
38
+ │ - Activation triggers: clone, copy, replicate website │
39
+ │ - Commands: design:clone, design:clone-px │
40
+ │ - References: progressive disclosure │
41
+ └─────────────────────────────────────────────────────────────────┘
42
+
43
+
44
+ ┌─────────────────────────────────────────────────────────────────┐
45
+ │ Core Scripts (src/) │
46
+ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
47
+ │ │ core/ │ │ core/ │ │ ai/ │ │
48
+ │ │ screenshot.js │ │ filter-css.js │ │ analyze-struct │ │
49
+ │ └────────┬────────┘ └────────┬────────┘ │ .py │ │
50
+ │ │ │ └────────┬────────┘ │
51
+ └───────────┼────────────────────┼───────────────────┼───────────┘
52
+ │ │ │
53
+ ▼ ▼ ▼
54
+ ┌─────────────────────────────────────────────────────────────────┐
55
+ │ src/utils/ (Shared) │
56
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
57
+ │ │ browser.js │ │ env.js │ │ env.py │ │
58
+ │ │ (facade) │ │ (Node.js) │ │ (Python) │ │
59
+ │ └──────┬──────┘ └─────────────┘ └─────────────┘ │
60
+ │ │ │
61
+ │ ▼ │
62
+ │ ┌─────────────────────────────────────────────────────────┐ │
63
+ │ │ Browser Provider Selection │ │
64
+ │ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │
65
+ │ │ │ chrome-devtools │ OR │ puppeteer.js (standalone)│ │ │
66
+ │ │ │ (if exists) │ │ (bundled fallback) │ │ │
67
+ │ │ └─────────────────┘ └─────────────────────────┘ │ │
68
+ │ └─────────────────────────────────────────────────────────┘ │
69
+ └─────────────────────────────────────────────────────────────────┘
70
+
71
+
72
+ ┌─────────────────────────────────────────────────────────────────┐
73
+ │ Chrome/Chromium │
74
+ │ Auto-detected paths: │
75
+ │ - macOS: /Applications/Google Chrome.app/... │
76
+ │ - Linux: /usr/bin/google-chrome, /usr/bin/chromium │
77
+ │ - Windows: C:\Program Files\Google\Chrome\... │
78
+ └─────────────────────────────────────────────────────────────────┘
79
+ ```
80
+
81
+ ## Component Details
82
+
83
+ ### 1. Browser Abstraction Layer
84
+
85
+ ```
86
+ src/utils/
87
+ ├── browser.js # Facade - auto-selects provider
88
+ ├── puppeteer.js # Standalone Puppeteer wrapper
89
+ ├── helpers.js # CLI utilities (parseArgs, outputJSON)
90
+ ├── env.js # Node.js env resolution
91
+ └── env.py # Python env resolution
92
+ ```
93
+
94
+ **browser.js** - Facade pattern for browser automation:
95
+ ```javascript
96
+ // Auto-detects chrome-devtools skill or falls back to standalone
97
+ async function initProvider() {
98
+ if (fs.existsSync(CHROME_DEVTOOLS_PATH)) {
99
+ browserModule = await import(CHROME_DEVTOOLS_PATH);
100
+ providerName = 'chrome-devtools';
101
+ } else {
102
+ browserModule = await import('./puppeteer.js');
103
+ providerName = 'standalone';
104
+ }
105
+ }
106
+ ```
107
+
108
+ **puppeteer.js** - Standalone browser wrapper:
109
+ - Cross-platform Chrome detection (macOS, Linux, Windows)
110
+ - Session persistence via WebSocket endpoint caching
111
+ - PID tracking for cleanup
112
+
113
+ ### 2. Environment Resolution
114
+
115
+ Both Node.js and Python share same resolution order:
116
+
117
+ ```
118
+ 1. process.env / os.environ (already set)
119
+ 2. .env in current working directory
120
+ 3. .env in skill directory
121
+ 4. .env in ~/.claude/skills/
122
+ 5. .env in ~/.claude/
123
+ ```
124
+
125
+ **Cross-platform support:**
126
+ - Windows: Uses `USERPROFILE` when `HOME` unavailable
127
+ - Python 3.9+: Uses `List[Path]` from typing module
128
+
129
+ ### 3. Core Scripts
130
+
131
+ | Script | Location | Language | Purpose |
132
+ |--------|----------|----------|---------|
133
+ | screenshot.js | src/core/ | Node.js | Screenshot capture, HTML/CSS extraction |
134
+ | filter-css.js | src/core/ | Node.js | Remove unused CSS selectors |
135
+ | extract-assets.js | src/core/ | Node.js | Download images, fonts, icons |
136
+ | analyze-structure.py | src/ai/ | Python | Gemini AI structure analysis |
137
+ | extract-design-tokens.py | src/ai/ | Python | Color, typography, spacing extraction |
138
+ | verify-menu.js | src/verification/ | Node.js | Test responsive navigation |
139
+ | verify-layout.js | src/verification/ | Node.js | Verify layout consistency |
140
+
141
+ ### 4. Post-Processing
142
+
143
+ ```
144
+ src/post-process/
145
+ ├── fetch-images.js # Fetch and optimize images
146
+ ├── inject-icons.js # Replace icons with Font Awesome
147
+ └── enhance-assets.js # Enhance extracted assets
148
+ ```
149
+
150
+ ### 5. Progressive Disclosure
151
+
152
+ SKILL.md kept concise. Detailed docs in docs/:
153
+
154
+ ```
155
+ docs/
156
+ ├── basic-clone.md # design:clone workflow
157
+ ├── pixel-perfect.md # design:clone-px workflow
158
+ ├── cli-reference.md # All script options
159
+ ├── design-clone-architecture.md # This file
160
+ └── troubleshooting.md # Common issues
161
+ ```
162
+
163
+ ### 6. CLI Tool
164
+
165
+ ```
166
+ bin/
167
+ ├── cli.js # Entry point (bin: design-clone)
168
+ ├── commands/
169
+ │ ├── init.js # Install skill to ~/.claude/skills/
170
+ │ ├── verify.js # Check installation status
171
+ │ └── help.js # Usage information
172
+ └── utils/
173
+ ├── copy.js # Recursive file copy
174
+ └── validate.js # Environment checks
175
+ ```
176
+
177
+ ## Data Flow
178
+
179
+ ### design:clone
180
+
181
+ ```
182
+ URL → src/core/screenshot.js → Screenshots (3 viewports)
183
+ → source.html (cleaned)
184
+ → source-raw.css
185
+ → src/core/filter-css.js → source.css (filtered)
186
+ ```
187
+
188
+ ### design:clone-px
189
+
190
+ ```
191
+ URL → src/core/screenshot.js → Screenshots + HTML/CSS
192
+ → src/core/filter-css.js → Filtered CSS
193
+ → src/core/extract-assets.js → assets/ (images, fonts, icons)
194
+ → src/ai/analyze-structure.py → structure.md (AI analysis)
195
+ → src/ai/extract-design-tokens.py → tokens.json, tokens.css
196
+ → src/verification/verify-menu.js → Menu validation report
197
+ ```
198
+
199
+ ## Output Structure
200
+
201
+ ```
202
+ cloned-design/
203
+ ├── desktop.png # 1920x1080
204
+ ├── tablet.png # 768x1024
205
+ ├── mobile.png # 375x812
206
+ ├── source.html # Cleaned HTML
207
+ ├── source.css # Filtered CSS
208
+ ├── source-raw.css # Original CSS
209
+ ├── structure.md # AI analysis (optional)
210
+ ├── tokens.json # Design tokens
211
+ ├── tokens.css # CSS variables
212
+ └── assets/
213
+ ├── images/
214
+ ├── fonts/
215
+ └── icons/
216
+ ```
217
+
218
+ ## Dependencies
219
+
220
+ ### Node.js (package.json)
221
+ - `css-tree`: CSS parsing and filtering
222
+ - `puppeteer`: Browser automation (peerDep, optional)
223
+
224
+ ### Python (requirements.txt)
225
+ - `google-genai`: Gemini AI for vision analysis
226
+
227
+ ## Installation Methods
228
+
229
+ ### npm (Recommended)
230
+ ```bash
231
+ npm install -g design-clone
232
+ design-clone init
233
+ ```
234
+
235
+ ### Manual
236
+ ```bash
237
+ cp -r design-clone ~/.claude/skills/design-clone
238
+ cd ~/.claude/skills/design-clone
239
+ npm install && pip install -r requirements.txt
240
+ ```
241
+
242
+ ## Security Considerations
243
+
244
+ 1. **Path validation**: Prevents directory traversal
245
+ 2. **CSS sanitization**: Removes XSS vectors (expression(), javascript:)
246
+ 3. **Size limits**: 10MB max CSS input
247
+ 4. **No secrets in output**: Scripts don't expose env vars