design-clone 1.2.0 → 2.3.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/README.md +32 -39
- package/SKILL.md +69 -45
- package/bin/cli.js +22 -4
- package/bin/commands/clone-site.js +31 -106
- package/bin/commands/help.js +19 -6
- package/bin/commands/init.js +11 -56
- package/bin/commands/uninstall.js +105 -0
- package/bin/commands/update.js +70 -0
- package/bin/commands/verify.js +11 -16
- package/bin/utils/paths.js +28 -0
- package/bin/utils/validate.js +24 -28
- package/bin/utils/version.js +23 -0
- package/docs/code-standards.md +789 -0
- package/docs/codebase-summary.md +556 -0
- package/docs/index.md +74 -0
- package/docs/project-overview-pdr.md +797 -0
- package/docs/system-architecture.md +718 -0
- package/package.json +20 -21
- package/src/ai/prompts/design-tokens/basic.md +80 -0
- package/src/ai/prompts/design-tokens/section-with-css.md +41 -0
- package/src/ai/prompts/design-tokens/section.md +48 -0
- package/src/ai/prompts/design-tokens/with-css.md +87 -0
- package/src/ai/prompts/structure-analysis/basic.md +55 -0
- package/src/ai/prompts/structure-analysis/with-context.md +59 -0
- package/src/ai/prompts/structure-analysis/with-dimensions.md +63 -0
- package/src/ai/prompts/structure-analysis/with-hierarchy.md +73 -0
- package/src/ai/prompts/ux-audit/aggregation.md +42 -0
- package/src/ai/prompts/ux-audit/desktop.md +92 -0
- package/src/ai/prompts/ux-audit/mobile.md +93 -0
- package/src/ai/prompts/ux-audit/tablet.md +92 -0
- package/src/core/animation/animation-extractor-ast.js +183 -0
- package/src/core/animation/animation-extractor-output.js +152 -0
- package/src/core/animation/animation-extractor.js +178 -0
- package/src/core/animation/state-capture-detection.js +200 -0
- package/src/core/animation/state-capture.js +193 -0
- package/src/core/capture/browser-context-pool.js +96 -0
- package/src/core/capture/multi-page-screenshot-page.js +110 -0
- package/src/core/capture/multi-page-screenshot.js +208 -0
- package/src/core/capture/screenshot-extraction.js +186 -0
- package/src/core/capture/screenshot-helpers.js +175 -0
- package/src/core/capture/screenshot-orchestrator.js +174 -0
- package/src/core/capture/screenshot-viewport.js +93 -0
- package/src/core/capture/screenshot.js +192 -0
- package/src/core/content/content-counter-dom.js +191 -0
- package/src/core/content/content-counter.js +76 -0
- package/src/core/css/breakpoint-detector.js +66 -0
- package/src/core/css/chromium-defaults.json +23 -0
- package/src/core/css/computed-style-extractor.js +102 -0
- package/src/core/css/css-chunker.js +103 -0
- package/src/core/{css-extractor.js → css/css-extractor.js} +4 -4
- package/src/core/css/filter-css-dead-code.js +120 -0
- package/src/core/css/filter-css-html-analyzer.js +110 -0
- package/src/core/css/filter-css-selector-matcher.js +172 -0
- package/src/core/css/filter-css.js +206 -0
- package/src/core/css/merge-css-atrule-processor.js +158 -0
- package/src/core/css/merge-css-file-io.js +68 -0
- package/src/core/css/merge-css.js +148 -0
- package/src/core/detection/framework-detector-routing.js +68 -0
- package/src/core/detection/framework-detector-signals.js +65 -0
- package/src/core/detection/framework-detector.js +198 -0
- package/src/core/dimension/dimension-extractor-card-detector.js +82 -0
- package/src/core/dimension/dimension-extractor.js +317 -0
- package/src/core/dimension/dimension-output-ai-summary.js +111 -0
- package/src/core/dimension/dimension-output.js +173 -0
- package/src/core/dimension/dom-tree-analyzer-tree-builders.js +95 -0
- package/src/core/dimension/dom-tree-analyzer.js +191 -0
- package/src/core/discovery/app-state-snapshot-capture.js +195 -0
- package/src/core/discovery/app-state-snapshot-utils.js +178 -0
- package/src/core/discovery/app-state-snapshot.js +131 -0
- package/src/core/discovery/discover-pages-routes.js +84 -0
- package/src/core/discovery/discover-pages-utils.js +177 -0
- package/src/core/discovery/discover-pages.js +191 -0
- package/src/core/html/html-extractor-inline-styler.js +70 -0
- package/src/core/html/html-extractor.js +147 -0
- package/src/core/html/semantic-enhancer-mappings.js +200 -0
- package/src/core/html/semantic-enhancer-page.js +148 -0
- package/src/core/html/semantic-enhancer.js +135 -0
- package/src/core/links/rewrite-links-css-rewriter.js +53 -0
- package/src/core/links/rewrite-links.js +173 -0
- package/src/core/media/asset-validator.js +118 -0
- package/src/core/media/extract-assets-downloader.js +187 -0
- package/src/core/media/extract-assets-page-scraper.js +115 -0
- package/src/core/media/extract-assets.js +159 -0
- package/src/core/media/video-capture-convert.js +200 -0
- package/src/core/media/video-capture.js +201 -0
- package/src/core/{cookie-handler.js → page-prep/cookie-handler.js} +1 -1
- package/src/core/{lazy-loader.js → page-prep/lazy-loader.js} +44 -46
- package/src/core/{page-readiness.js → page-prep/page-readiness.js} +8 -8
- package/src/core/section/section-cropper-helpers.js +43 -0
- package/src/core/section/section-cropper.js +132 -0
- package/src/core/section/section-detector-strategies.js +139 -0
- package/src/core/section/section-detector-utils.js +100 -0
- package/src/core/section/section-detector.js +88 -0
- package/src/core/tests/test-section-cropper.js +177 -0
- package/src/core/tests/test-section-detector.js +55 -0
- package/src/post-process/enhance-assets.js +29 -4
- package/src/post-process/fetch-images-unsplash-client.js +123 -0
- package/src/post-process/fetch-images.js +60 -263
- package/src/post-process/inject-gosnap.js +88 -0
- package/src/post-process/inject-icons-svg-replacer.js +76 -0
- package/src/post-process/inject-icons.js +47 -200
- package/src/route-discoverers/angular-discoverer.js +157 -0
- package/src/route-discoverers/astro-discoverer.js +123 -0
- package/src/route-discoverers/base-discoverer-utils.js +137 -0
- package/src/route-discoverers/base-discoverer.js +153 -0
- package/src/route-discoverers/index.js +106 -0
- package/src/route-discoverers/next-discoverer.js +130 -0
- package/src/route-discoverers/nuxt-discoverer.js +138 -0
- package/src/route-discoverers/react-discoverer.js +139 -0
- package/src/route-discoverers/svelte-discoverer.js +109 -0
- package/src/route-discoverers/universal-discoverer.js +227 -0
- package/src/route-discoverers/vue-discoverer.js +118 -0
- package/src/shared/config.js +38 -0
- package/src/shared/error-codes.js +31 -0
- package/src/shared/viewports.js +46 -0
- package/src/utils/browser.js +11 -44
- package/src/utils/helpers.js +4 -0
- package/src/utils/log.js +12 -0
- package/src/utils/playwright-loader.js +76 -0
- package/src/utils/playwright.js +147 -0
- package/src/utils/progress.js +32 -0
- package/src/verification/generate-audit-report-css-fixes.js +52 -0
- package/src/verification/generate-audit-report-sections.js +158 -0
- package/src/verification/generate-audit-report.js +122 -0
- package/src/verification/quality-scorer.js +92 -0
- package/src/verification/verify-footer-checks.js +103 -0
- package/src/verification/verify-footer-helpers.js +178 -0
- package/src/verification/verify-footer.js +135 -0
- package/src/verification/verify-header-checks.js +104 -0
- package/src/verification/verify-header-helpers.js +156 -0
- package/src/verification/verify-header.js +144 -0
- package/src/verification/verify-layout-report.js +101 -0
- package/src/verification/verify-layout.js +14 -260
- package/src/verification/verify-menu-checks.js +104 -0
- package/src/verification/verify-menu-helpers.js +112 -0
- package/src/verification/verify-menu.js +18 -302
- package/src/verification/verify-slider-checks.js +115 -0
- package/src/verification/verify-slider-constants.js +65 -0
- package/src/verification/verify-slider-helpers.js +164 -0
- package/src/verification/verify-slider.js +142 -0
- package/.env.example +0 -14
- package/docs/basic-clone.md +0 -63
- package/docs/cli-reference.md +0 -118
- package/docs/design-clone-architecture.md +0 -275
- package/docs/pixel-perfect.md +0 -86
- package/docs/troubleshooting.md +0 -169
- package/requirements.txt +0 -5
- package/src/ai/analyze-structure.py +0 -305
- package/src/ai/extract-design-tokens.py +0 -439
- package/src/ai/prompts/__init__.py +0 -2
- package/src/ai/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/ai/prompts/__pycache__/design_tokens.cpython-313.pyc +0 -0
- package/src/ai/prompts/__pycache__/structure_analysis.cpython-313.pyc +0 -0
- package/src/ai/prompts/design_tokens.py +0 -183
- package/src/ai/prompts/structure_analysis.py +0 -273
- package/src/core/animation-extractor.js +0 -526
- package/src/core/design-tokens.js +0 -103
- package/src/core/dimension-extractor.js +0 -366
- package/src/core/dimension-output.js +0 -208
- package/src/core/discover-pages.js +0 -314
- package/src/core/extract-assets.js +0 -468
- package/src/core/filter-css.js +0 -499
- package/src/core/html-extractor.js +0 -171
- package/src/core/merge-css.js +0 -407
- package/src/core/multi-page-screenshot.js +0 -377
- package/src/core/rewrite-links.js +0 -226
- package/src/core/screenshot.js +0 -572
- package/src/core/state-capture.js +0 -602
- package/src/core/video-capture.js +0 -540
- package/src/utils/__init__.py +0 -16
- package/src/utils/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/utils/__pycache__/env.cpython-313.pyc +0 -0
- package/src/utils/env.py +0 -134
- package/src/utils/puppeteer.js +0 -281
package/bin/commands/help.js
CHANGED
|
@@ -7,33 +7,46 @@ export function help() {
|
|
|
7
7
|
design-clone - Claude Code skill for website design cloning
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
10
|
-
design-clone init [options]
|
|
11
|
-
design-clone verify
|
|
12
|
-
design-clone
|
|
13
|
-
design-clone
|
|
10
|
+
design-clone init [options] Install skill to ~/.claude/skills/
|
|
11
|
+
design-clone verify Check installation status
|
|
12
|
+
design-clone update [options] Update to latest version
|
|
13
|
+
design-clone uninstall [options] Remove skill installation
|
|
14
|
+
design-clone clone-site <url> Clone multiple pages from a website
|
|
15
|
+
design-clone help Show this help
|
|
16
|
+
design-clone --version Show version
|
|
14
17
|
|
|
15
18
|
Init Options:
|
|
16
19
|
--force, -f Overwrite existing installation
|
|
17
20
|
--skip-deps Skip dependency installation
|
|
18
21
|
|
|
22
|
+
Update Options:
|
|
23
|
+
--force, -f Reinstall even if already up to date
|
|
24
|
+
--skip-deps Skip dependency installation
|
|
25
|
+
|
|
26
|
+
Uninstall Options:
|
|
27
|
+
--yes, -y Skip confirmation prompt
|
|
28
|
+
|
|
19
29
|
Clone-site Options:
|
|
20
30
|
--pages <paths> Comma-separated paths (e.g., /,/about,/contact)
|
|
21
31
|
--max-pages <n> Maximum pages to auto-discover (default: 10)
|
|
22
32
|
--viewports <list> Viewport list (default: desktop,tablet,mobile)
|
|
23
33
|
--yes Skip confirmation prompt
|
|
24
34
|
--output <dir> Custom output directory
|
|
25
|
-
--ai
|
|
35
|
+
--ai (Removed: AI analysis is now built-in via Claude Code)
|
|
26
36
|
|
|
27
37
|
Examples:
|
|
28
38
|
design-clone init # Install skill
|
|
29
39
|
design-clone init --force # Reinstall, overwrite existing
|
|
30
40
|
design-clone verify # Check if installed correctly
|
|
41
|
+
design-clone update # Update to latest version
|
|
42
|
+
design-clone uninstall # Remove skill installation
|
|
43
|
+
design-clone --version # Show version
|
|
31
44
|
design-clone clone-site https://example.com
|
|
32
45
|
design-clone clone-site https://example.com --max-pages 5
|
|
33
46
|
design-clone clone-site https://example.com --pages /,/about,/contact
|
|
34
47
|
|
|
35
48
|
After installation:
|
|
36
|
-
1.
|
|
49
|
+
1. AI analysis is built-in via Claude Code vision (no API key needed)
|
|
37
50
|
2. Use /design:clone or /design:clone-px in Claude Code
|
|
38
51
|
|
|
39
52
|
For more info: https://github.com/bienhoang/design-clone
|
package/bin/commands/init.js
CHANGED
|
@@ -9,16 +9,13 @@ import { exec as execCallback } from 'child_process';
|
|
|
9
9
|
import { promisify } from 'util';
|
|
10
10
|
import { copyRecursive, exists } from '../utils/copy.js';
|
|
11
11
|
import { runAllChecks } from '../utils/validate.js';
|
|
12
|
+
import { getSkillDest, getCommandsDest } from '../utils/paths.js';
|
|
12
13
|
|
|
13
14
|
const exec = promisify(execCallback);
|
|
14
15
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
16
|
|
|
16
17
|
// Source: package root (where SKILL.md is)
|
|
17
18
|
const SKILL_SOURCE = path.resolve(__dirname, '../..');
|
|
18
|
-
// Destination: ~/.claude/skills/design-clone
|
|
19
|
-
const getSkillDest = () => path.join(process.env.HOME || process.env.USERPROFILE || '', '.claude/skills/design-clone');
|
|
20
|
-
// Commands destination: ~/.claude/commands/design/
|
|
21
|
-
const getCommandsDest = () => path.join(process.env.HOME || process.env.USERPROFILE || '', '.claude/commands/design');
|
|
22
19
|
|
|
23
20
|
/**
|
|
24
21
|
* Install skill to Claude Code skills directory
|
|
@@ -36,7 +33,6 @@ export async function init(args) {
|
|
|
36
33
|
const checks = await runAllChecks();
|
|
37
34
|
|
|
38
35
|
console.log(` Node.js: ${checks.node.ok ? '✓' : '✗'} ${checks.node.message}`);
|
|
39
|
-
console.log(` Python: ${checks.python.ok ? '✓' : '✗'} ${checks.python.message}`);
|
|
40
36
|
console.log(` Chrome: ${checks.chrome.ok ? '✓' : '✗'} ${checks.chrome.message}`);
|
|
41
37
|
console.log('');
|
|
42
38
|
|
|
@@ -45,12 +41,8 @@ export async function init(args) {
|
|
|
45
41
|
process.exit(1);
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
if (!checks.python.ok) {
|
|
49
|
-
console.warn('Warning: Python 3.9+ not found. AI analysis features will be unavailable.');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
44
|
if (!checks.chrome.ok) {
|
|
53
|
-
console.warn('Warning: Chrome not found.
|
|
45
|
+
console.warn('Warning: Chrome not found. Playwright will download Chromium during installation.');
|
|
54
46
|
}
|
|
55
47
|
|
|
56
48
|
// Check existing installation
|
|
@@ -129,58 +121,21 @@ export async function init(args) {
|
|
|
129
121
|
console.warn(` Warning: npm install failed: ${error.message}`);
|
|
130
122
|
}
|
|
131
123
|
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
alreadyInstalled = true;
|
|
141
|
-
console.log(' Python packages already available');
|
|
142
|
-
} catch {
|
|
143
|
-
// Need to install
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (!alreadyInstalled) {
|
|
147
|
-
const HOME = process.env.HOME || process.env.USERPROFILE || '';
|
|
148
|
-
const sharedVenvPip = path.join(HOME, '.claude/skills/.venv/bin/pip');
|
|
149
|
-
|
|
150
|
-
// Try installation methods in order of preference
|
|
151
|
-
const pipCommands = [
|
|
152
|
-
{ cmd: `"${sharedVenvPip}" install google-genai`, name: 'shared venv' },
|
|
153
|
-
{ cmd: 'pip3 install --user google-genai', name: 'pip3 --user' },
|
|
154
|
-
{ cmd: 'pip install --user google-genai', name: 'pip --user' }
|
|
155
|
-
];
|
|
156
|
-
|
|
157
|
-
let installed = false;
|
|
158
|
-
for (const { cmd, name } of pipCommands) {
|
|
159
|
-
try {
|
|
160
|
-
await exec(cmd);
|
|
161
|
-
console.log(` Python packages installed via ${name}`);
|
|
162
|
-
installed = true;
|
|
163
|
-
break;
|
|
164
|
-
} catch {
|
|
165
|
-
// Try next method
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (!installed) {
|
|
170
|
-
console.warn(' Warning: Could not install Python packages automatically');
|
|
171
|
-
console.warn(' Try one of these manually:');
|
|
172
|
-
console.warn(` ${sharedVenvPip} install google-genai`);
|
|
173
|
-
console.warn(' pip3 install --user google-genai');
|
|
174
|
-
console.warn(' pip3 install --break-system-packages google-genai');
|
|
175
|
-
}
|
|
176
|
-
}
|
|
124
|
+
// Install Playwright browsers (chromium only for smaller footprint)
|
|
125
|
+
console.log('Installing Playwright browsers...');
|
|
126
|
+
try {
|
|
127
|
+
await exec('npx playwright install chromium', { cwd: SKILL_DEST, timeout: 300000 });
|
|
128
|
+
console.log(' Chromium browser installed');
|
|
129
|
+
} catch (browserError) {
|
|
130
|
+
console.warn(` Warning: Browser install failed: ${browserError.message}`);
|
|
131
|
+
console.warn(' Run manually: npx playwright install chromium');
|
|
177
132
|
}
|
|
178
133
|
}
|
|
179
134
|
|
|
180
135
|
// Success
|
|
181
136
|
console.log('\n✓ design-clone skill installed successfully!\n');
|
|
182
137
|
console.log('Next steps:');
|
|
183
|
-
console.log(' 1.
|
|
138
|
+
console.log(' 1. AI analysis is built-in (no API key needed)');
|
|
184
139
|
console.log(' 2. Use slash commands in Claude Code:');
|
|
185
140
|
console.log(' /design:clone - Clone single page');
|
|
186
141
|
console.log(' /design:clone-site - Clone multiple pages');
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uninstall command - remove skill from ~/.claude/skills/
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import readline from 'readline';
|
|
7
|
+
import { exists } from '../utils/copy.js';
|
|
8
|
+
import { getSkillDest, getCommandsDest } from '../utils/paths.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Prompt user for confirmation
|
|
12
|
+
* @param {string} message - Prompt message
|
|
13
|
+
* @returns {Promise<boolean>}
|
|
14
|
+
*/
|
|
15
|
+
function confirm(message) {
|
|
16
|
+
const rl = readline.createInterface({
|
|
17
|
+
input: process.stdin,
|
|
18
|
+
output: process.stdout,
|
|
19
|
+
});
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
22
|
+
rl.close();
|
|
23
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Uninstall skill from Claude Code skills directory
|
|
30
|
+
* @param {string[]} args - CLI arguments
|
|
31
|
+
*/
|
|
32
|
+
export async function uninstall(args) {
|
|
33
|
+
const skipConfirm = args.includes('--yes') || args.includes('-y');
|
|
34
|
+
const SKILL_DEST = getSkillDest();
|
|
35
|
+
const COMMANDS_DEST = getCommandsDest();
|
|
36
|
+
|
|
37
|
+
console.log('design-clone uninstaller\n');
|
|
38
|
+
|
|
39
|
+
// Check what exists
|
|
40
|
+
const skillExists = await exists(SKILL_DEST);
|
|
41
|
+
const commandsExist = await exists(COMMANDS_DEST);
|
|
42
|
+
|
|
43
|
+
if (!skillExists && !commandsExist) {
|
|
44
|
+
console.log('Nothing to uninstall. design-clone is not installed.');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Show what will be removed
|
|
49
|
+
console.log('The following will be removed:');
|
|
50
|
+
if (skillExists) console.log(` - ${SKILL_DEST}`);
|
|
51
|
+
if (commandsExist) console.log(` - ${COMMANDS_DEST}`);
|
|
52
|
+
console.log('');
|
|
53
|
+
|
|
54
|
+
// Confirm
|
|
55
|
+
if (!skipConfirm) {
|
|
56
|
+
const confirmed = await confirm('Are you sure you want to uninstall?');
|
|
57
|
+
if (!confirmed) {
|
|
58
|
+
console.log('Uninstall cancelled.');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.log('');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Remove directories - attempt both before exiting on errors
|
|
65
|
+
const errors = [];
|
|
66
|
+
|
|
67
|
+
if (skillExists) {
|
|
68
|
+
try {
|
|
69
|
+
await fs.rm(SKILL_DEST, { recursive: true, force: true });
|
|
70
|
+
console.log(` Removed: ${SKILL_DEST}`);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
errors.push(`skill directory: ${error.message}`);
|
|
73
|
+
console.error(` Error removing skill directory: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (commandsExist) {
|
|
78
|
+
try {
|
|
79
|
+
await fs.rm(COMMANDS_DEST, { recursive: true, force: true });
|
|
80
|
+
console.log(` Removed: ${COMMANDS_DEST}`);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
errors.push(`commands directory: ${error.message}`);
|
|
83
|
+
console.error(` Error removing commands directory: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (errors.length > 0) {
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Verify cleanup
|
|
92
|
+
const skillStillExists = await exists(SKILL_DEST);
|
|
93
|
+
const commandsStillExist = await exists(COMMANDS_DEST);
|
|
94
|
+
|
|
95
|
+
console.log('');
|
|
96
|
+
if (!skillStillExists && !commandsStillExist) {
|
|
97
|
+
console.log('design-clone uninstalled successfully.');
|
|
98
|
+
console.log('\nTo reinstall: design-clone init');
|
|
99
|
+
} else {
|
|
100
|
+
console.error('Warning: Some files may not have been removed completely.');
|
|
101
|
+
if (skillStillExists) console.error(` Still exists: ${SKILL_DEST}`);
|
|
102
|
+
if (commandsStillExist) console.error(` Still exists: ${COMMANDS_DEST}`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update command - update installed skill to latest version
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { exists } from '../utils/copy.js';
|
|
8
|
+
import { getVersion } from '../utils/version.js';
|
|
9
|
+
import { getSkillDest } from '../utils/paths.js';
|
|
10
|
+
import { init } from './init.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Read version from installed skill's package.json
|
|
14
|
+
* @returns {string|null} Installed version or null if not found
|
|
15
|
+
*/
|
|
16
|
+
function getInstalledVersion() {
|
|
17
|
+
const pkgPath = path.join(getSkillDest(), 'package.json');
|
|
18
|
+
try {
|
|
19
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
20
|
+
return pkg.version;
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Update installed skill to current version
|
|
28
|
+
* @param {string[]} args - CLI arguments
|
|
29
|
+
*/
|
|
30
|
+
export async function update(args) {
|
|
31
|
+
const force = args.includes('--force') || args.includes('-f');
|
|
32
|
+
const skipDeps = args.includes('--skip-deps');
|
|
33
|
+
const SKILL_DEST = getSkillDest();
|
|
34
|
+
|
|
35
|
+
console.log('design-clone updater\n');
|
|
36
|
+
|
|
37
|
+
// Check if installed
|
|
38
|
+
const installed = await exists(SKILL_DEST);
|
|
39
|
+
if (!installed) {
|
|
40
|
+
console.log('design-clone is not installed.');
|
|
41
|
+
console.log('Run "design-clone init" to install.');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Compare versions
|
|
46
|
+
const currentVersion = getVersion();
|
|
47
|
+
const installedVersion = getInstalledVersion();
|
|
48
|
+
|
|
49
|
+
console.log(` Installed: v${installedVersion || 'unknown'}`);
|
|
50
|
+
console.log(` Available: v${currentVersion}`);
|
|
51
|
+
console.log('');
|
|
52
|
+
|
|
53
|
+
if (installedVersion === currentVersion && !force) {
|
|
54
|
+
console.log('Already up to date.');
|
|
55
|
+
console.log('Use --force to reinstall anyway.');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Build args for init
|
|
60
|
+
const initArgs = ['--force'];
|
|
61
|
+
if (skipDeps) initArgs.push('--skip-deps');
|
|
62
|
+
|
|
63
|
+
// Run init with --force
|
|
64
|
+
console.log('Updating...\n');
|
|
65
|
+
await init(initArgs);
|
|
66
|
+
|
|
67
|
+
// Verify new version
|
|
68
|
+
const newVersion = getInstalledVersion();
|
|
69
|
+
console.log(`\nUpdate complete: v${installedVersion || 'unknown'} -> v${newVersion}`);
|
|
70
|
+
}
|
package/bin/commands/verify.js
CHANGED
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
* Verify command - check installation status
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import fs from 'fs/promises';
|
|
6
5
|
import path from 'path';
|
|
7
6
|
import { exists } from '../utils/copy.js';
|
|
8
7
|
import { runAllChecks } from '../utils/validate.js';
|
|
9
|
-
|
|
10
|
-
const getSkillDir = () => path.join(process.env.HOME || process.env.USERPROFILE || '', '.claude/skills/design-clone');
|
|
8
|
+
import { getSkillDest } from '../utils/paths.js';
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* Verify skill installation
|
|
14
12
|
*/
|
|
15
13
|
export async function verify() {
|
|
16
|
-
const SKILL_DIR =
|
|
14
|
+
const SKILL_DIR = getSkillDest();
|
|
17
15
|
let allOk = true;
|
|
18
16
|
|
|
19
17
|
console.log('design-clone skill verification\n');
|
|
@@ -30,13 +28,10 @@ export async function verify() {
|
|
|
30
28
|
// Check required files
|
|
31
29
|
const requiredFiles = [
|
|
32
30
|
'SKILL.md',
|
|
33
|
-
'src/core/screenshot.js',
|
|
34
|
-
'src/core/filter-css.js',
|
|
35
|
-
'src/ai/analyze-structure.py',
|
|
31
|
+
'src/core/capture/screenshot.js',
|
|
32
|
+
'src/core/css/filter-css.js',
|
|
36
33
|
'src/utils/browser.js',
|
|
37
|
-
'src/utils/env.js'
|
|
38
|
-
'src/utils/env.py',
|
|
39
|
-
'requirements.txt'
|
|
34
|
+
'src/utils/env.js'
|
|
40
35
|
];
|
|
41
36
|
|
|
42
37
|
let filesOk = true;
|
|
@@ -63,16 +58,16 @@ export async function verify() {
|
|
|
63
58
|
console.log('\nEnvironment:');
|
|
64
59
|
const checks = await runAllChecks();
|
|
65
60
|
|
|
66
|
-
console.log(` Node.js:
|
|
67
|
-
console.log(`
|
|
68
|
-
console.log(` Chrome:
|
|
61
|
+
console.log(` Node.js: ${checks.node.ok ? '✓' : '✗'} ${checks.node.message}`);
|
|
62
|
+
console.log(` Playwright: ${checks.playwright.ok ? '✓' : '✗'} ${checks.playwright.message}`);
|
|
63
|
+
console.log(` Chrome: ${checks.chrome.ok ? '✓' : '○'} ${checks.chrome.message}${checks.playwright.ok ? ' (optional with Playwright)' : ''}`);
|
|
69
64
|
|
|
70
65
|
if (!checks.node.ok) allOk = false;
|
|
66
|
+
if (!checks.playwright.ok && !checks.chrome.ok) allOk = false;
|
|
71
67
|
|
|
72
|
-
// Check
|
|
68
|
+
// Check optional features
|
|
73
69
|
console.log('\nOptional:');
|
|
74
|
-
|
|
75
|
-
console.log(` GEMINI_API_KEY: ${geminiKey ? '✓ set' : '○ not set (AI analysis disabled)'}`);
|
|
70
|
+
console.log(` AI analysis: ✓ built-in (Claude Code vision)`);
|
|
76
71
|
|
|
77
72
|
// Check .env files
|
|
78
73
|
const envLocations = [
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared path helpers for CLI commands
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get user home directory or exit if not available
|
|
9
|
+
* @returns {string} Home directory path
|
|
10
|
+
*/
|
|
11
|
+
export function getHomeDir() {
|
|
12
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
13
|
+
if (!home) {
|
|
14
|
+
console.error('Error: HOME environment variable is not set.');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
return home;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Skill installation directory: ~/.claude/skills/design-clone */
|
|
21
|
+
export function getSkillDest() {
|
|
22
|
+
return path.join(getHomeDir(), '.claude/skills/design-clone');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Slash commands directory: ~/.claude/commands/design */
|
|
26
|
+
export function getCommandsDest() {
|
|
27
|
+
return path.join(getHomeDir(), '.claude/commands/design');
|
|
28
|
+
}
|
package/bin/utils/validate.js
CHANGED
|
@@ -26,25 +26,6 @@ export async function checkNode() {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
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
29
|
/**
|
|
49
30
|
* Check Chrome/Chromium
|
|
50
31
|
* @returns {Promise<{ok: boolean, path: string, message: string}>}
|
|
@@ -94,15 +75,31 @@ export async function checkChrome() {
|
|
|
94
75
|
}
|
|
95
76
|
|
|
96
77
|
/**
|
|
97
|
-
* Check
|
|
78
|
+
* Check Playwright
|
|
98
79
|
* @returns {Promise<{ok: boolean, message: string}>}
|
|
99
80
|
*/
|
|
100
|
-
export async function
|
|
81
|
+
export async function checkPlaywright() {
|
|
101
82
|
try {
|
|
102
|
-
await import('
|
|
103
|
-
|
|
83
|
+
const playwright = await import('playwright');
|
|
84
|
+
// Check if browsers are installed by checking chromium executable
|
|
85
|
+
if (playwright.chromium?.executablePath) {
|
|
86
|
+
try {
|
|
87
|
+
const fs = await import('fs/promises');
|
|
88
|
+
await fs.access(playwright.chromium.executablePath());
|
|
89
|
+
return { ok: true, message: 'Playwright installed with browsers' };
|
|
90
|
+
} catch {
|
|
91
|
+
return { ok: true, message: 'Playwright installed (browsers may need install)' };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { ok: true, message: 'Playwright installed' };
|
|
104
95
|
} catch {
|
|
105
|
-
|
|
96
|
+
// Try playwright-core
|
|
97
|
+
try {
|
|
98
|
+
await import('playwright-core');
|
|
99
|
+
return { ok: true, message: 'playwright-core installed (needs Chrome)' };
|
|
100
|
+
} catch {
|
|
101
|
+
return { ok: false, message: 'Playwright not installed' };
|
|
102
|
+
}
|
|
106
103
|
}
|
|
107
104
|
}
|
|
108
105
|
|
|
@@ -111,12 +108,11 @@ export async function checkPuppeteer() {
|
|
|
111
108
|
* @returns {Promise<Object>}
|
|
112
109
|
*/
|
|
113
110
|
export async function runAllChecks() {
|
|
114
|
-
const [node,
|
|
111
|
+
const [node, chrome, playwright] = await Promise.all([
|
|
115
112
|
checkNode(),
|
|
116
|
-
checkPython(),
|
|
117
113
|
checkChrome(),
|
|
118
|
-
|
|
114
|
+
checkPlaywright()
|
|
119
115
|
]);
|
|
120
116
|
|
|
121
|
-
return { node,
|
|
117
|
+
return { node, chrome, playwright };
|
|
122
118
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version utility - reads version from package.json
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
import { resolve, dirname } from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get package version from package.json
|
|
13
|
+
* @returns {string} Version string (e.g., "2.1.0")
|
|
14
|
+
*/
|
|
15
|
+
export function getVersion() {
|
|
16
|
+
try {
|
|
17
|
+
const pkgPath = resolve(__dirname, '../../package.json');
|
|
18
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
19
|
+
return pkg.version;
|
|
20
|
+
} catch {
|
|
21
|
+
return 'unknown';
|
|
22
|
+
}
|
|
23
|
+
}
|