pagescrape-init 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 (3) hide show
  1. package/README.md +65 -0
  2. package/dist/index.js +321 -0
  3. package/package.json +42 -0
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # pagescrape-init
2
+
3
+ > Zero-friction CLI to scaffold a production-ready Next.js clone from a PageScrape blueprint.
4
+
5
+ Deconstruct any website with [PageScrape](https://github.com/pagescrape), then run a single command to generate a fully configured project — complete with design tokens, animation components, AI IDE rules, and a step-by-step build roadmap.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ # From a running PageScrape server
11
+ npx pagescrape-init <job-id>
12
+
13
+ # From a local blueprint file
14
+ npx pagescrape-init --file ./blueprint.json
15
+ ```
16
+
17
+ ## What It Does
18
+
19
+ Running `pagescrape-init` performs 5 automated phases:
20
+
21
+ | Phase | Action |
22
+ |-------|--------|
23
+ | **1. Scaffold** | Creates a new Next.js app with TypeScript, Tailwind, ESLint |
24
+ | **2. Dependencies** | Installs all libraries detected in the blueprint (GSAP, Three.js, Lenis, etc.) |
25
+ | **3. AI Rules** | Generates `.cursorrules` so your AI IDE understands the project immediately |
26
+ | **4. Components** | Writes starter components (3D scenes, scroll animations, etc.) |
27
+ | **5. Roadmap** | Creates `tasks.md` — a phased build checklist for your AI assistant |
28
+
29
+ ## Usage
30
+
31
+ ```
32
+ npx pagescrape-init <job-id> Fetch blueprint & scaffold
33
+ npx pagescrape-init --file <path> Use a local JSON blueprint
34
+
35
+ Options:
36
+ -f, --file <path> Path to a local blueprint JSON file
37
+ -s, --server <url> PageScrape server URL (default: http://localhost:3000)
38
+ -o, --output <dir> Output directory name (default: pagescrape-clone)
39
+ -v, --version Show version
40
+ -h, --help Show help
41
+ ```
42
+
43
+ ## Environment Variables
44
+
45
+ | Variable | Description | Default |
46
+ |----------|-------------|---------|
47
+ | `PAGESCRAPE_SERVER` | URL of your PageScrape server | `http://localhost:3000` |
48
+
49
+ ## Workflow
50
+
51
+ 1. Open the **PageScrape dashboard** and paste any URL
52
+ 2. Wait for the scrape to complete
53
+ 3. Copy the **Job ID** from the dashboard
54
+ 4. Run `npx pagescrape-init <job-id>` in your terminal
55
+ 5. Open the generated folder in **Cursor / Windsurf / your IDE**
56
+ 6. Say **"Build Phase 1"** — your AI reads `.cursorrules` and gets to work
57
+
58
+ ## Requirements
59
+
60
+ - **Node.js 18+** (uses built-in `fetch`)
61
+ - **npm** (for `create-next-app` scaffolding)
62
+
63
+ ## License
64
+
65
+ ISC
package/dist/index.js ADDED
@@ -0,0 +1,321 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const child_process_1 = require("child_process");
10
+ // ── Constants ────────────────────────────────────────────────
11
+ const VERSION = '1.0.0';
12
+ const TARGET_DIR_NAME = 'pagescrape-clone';
13
+ const DEFAULT_SERVER = process.env.PAGESCRAPE_SERVER || 'http://localhost:3000';
14
+ // ── Styled Console Helpers ───────────────────────────────────
15
+ const c = {
16
+ reset: '\x1b[0m',
17
+ bold: '\x1b[1m',
18
+ dim: '\x1b[2m',
19
+ red: '\x1b[31m',
20
+ green: '\x1b[32m',
21
+ yellow: '\x1b[33m',
22
+ blue: '\x1b[34m',
23
+ magenta: '\x1b[35m',
24
+ cyan: '\x1b[36m',
25
+ white: '\x1b[37m',
26
+ bgRed: '\x1b[41m',
27
+ bgGreen: '\x1b[42m',
28
+ bgBlue: '\x1b[44m',
29
+ bgMagenta: '\x1b[45m',
30
+ };
31
+ function banner() {
32
+ console.log(`
33
+ ${c.magenta}${c.bold} ╔══════════════════════════════════════════════════╗
34
+ ║ ║
35
+ ║ ▄▀▀▄ ▄▀▀▄ ▄▀▀▀▄ ▄▀▀▀▀ ▄▀▀▀▄ ▄▀▀▀▄ ▄▀▀▄ ║
36
+ ║ █ █ █ █ █ █ █ █ █ █ █ █ █ ║
37
+ ║ █▀▀ █▀▀█ █ █ █ ▀▀█ █▀▀▀ █▀▀▀ █▀▀ ║
38
+ ║ █ █ █ ▀▄▄▄▀ ▀▄▄▄▀ ▀▄▄▄▀ ▀ ▀▄▄▄ ║
39
+ ║ ║
40
+ ║${c.reset}${c.magenta} ${c.cyan}P A G E S C R A P E • I N I T${c.magenta} ║
41
+ ║${c.reset}${c.magenta} ${c.dim}Zero-Friction Site Cloning Engine${c.reset}${c.magenta} ║
42
+ ║ ║
43
+ ╚══════════════════════════════════════════════════╝${c.reset}
44
+ `);
45
+ }
46
+ function log(emoji, msg) {
47
+ console.log(` ${emoji} ${msg}`);
48
+ }
49
+ function logPhase(num, title) {
50
+ console.log(`\n${c.bold}${c.cyan} ┌─ Phase ${num} ─────────────────────────────────────${c.reset}`);
51
+ console.log(`${c.bold}${c.cyan} │${c.reset} ${c.bold}${title}${c.reset}`);
52
+ console.log(`${c.bold}${c.cyan} └──────────────────────────────────────────────${c.reset}`);
53
+ }
54
+ function logSuccess(msg) {
55
+ console.log(` ${c.green} ✓${c.reset} ${msg}`);
56
+ }
57
+ function logError(msg) {
58
+ console.error(`\n ${c.red}${c.bold}✖ ${msg}${c.reset}\n`);
59
+ }
60
+ // ── Core Functions ───────────────────────────────────────────
61
+ function runCommand(command, cwd = process.cwd()) {
62
+ console.log(`${c.dim} > ${command}${c.reset}`);
63
+ (0, child_process_1.execSync)(command, { stdio: 'inherit', cwd });
64
+ }
65
+ async function fetchBlueprint(jobId, server) {
66
+ const url = `${server}/api/blueprint/${jobId}`;
67
+ log('📡', `Fetching blueprint from ${c.cyan}${url}${c.reset}...`);
68
+ let res;
69
+ try {
70
+ res = await fetch(url);
71
+ }
72
+ catch {
73
+ logError(`Could not connect to server at ${server}`);
74
+ console.error(` ${c.dim}Make sure your PageScrape server is running (npm run dev)${c.reset}`);
75
+ console.error(` ${c.dim}Or set PAGESCRAPE_SERVER env variable to your server URL${c.reset}\n`);
76
+ process.exit(1);
77
+ }
78
+ if (res.status === 202) {
79
+ logError('The scrape is still running. Wait for it to finish on the dashboard, then try again.');
80
+ process.exit(1);
81
+ }
82
+ if (res.status === 404) {
83
+ logError(`No blueprint found for Job ID "${jobId}".`);
84
+ console.error(` ${c.dim}Check your Job ID on the PageScrape dashboard.${c.reset}\n`);
85
+ process.exit(1);
86
+ }
87
+ if (!res.ok) {
88
+ const errBody = await res.json().catch(() => ({ error: res.statusText }));
89
+ logError(`Server error: ${errBody.error || res.statusText}`);
90
+ process.exit(1);
91
+ }
92
+ return res.json();
93
+ }
94
+ function loadLocalFile(filePath) {
95
+ const resolved = path_1.default.resolve(process.cwd(), filePath);
96
+ if (!fs_1.default.existsSync(resolved)) {
97
+ logError(`File not found: ${resolved}`);
98
+ process.exit(1);
99
+ }
100
+ try {
101
+ return JSON.parse(fs_1.default.readFileSync(resolved, 'utf8'));
102
+ }
103
+ catch {
104
+ logError(`Failed to parse JSON from: ${resolved}`);
105
+ process.exit(1);
106
+ }
107
+ }
108
+ // ── Project Scaffolder ───────────────────────────────────────
109
+ async function initProject(report, outputDir) {
110
+ const dirName = outputDir || TARGET_DIR_NAME;
111
+ const targetDir = path_1.default.resolve(process.cwd(), dirName);
112
+ // Phase 1: Scaffold Next.js
113
+ logPhase(1, '🚀 Scaffolding Next.js Project');
114
+ if (!fs_1.default.existsSync(targetDir)) {
115
+ runCommand(`npx -y create-next-app@latest ./${dirName} --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm --yes`);
116
+ }
117
+ else {
118
+ log('⚠️', `${c.yellow}Directory "${dirName}" already exists — skipping scaffold.${c.reset}`);
119
+ }
120
+ // Phase 2: Install Dependencies
121
+ logPhase(2, '📦 Installing Blueprint Dependencies');
122
+ const deps = report.quickStart?.keyDependencies || [];
123
+ if (deps.length > 0) {
124
+ const safeDeps = deps
125
+ .join(' ')
126
+ .split(' ')
127
+ .filter((d) => d.trim() && !d.includes('npm') && d !== 'install' && d !== 'i');
128
+ if (safeDeps.length > 0) {
129
+ log('📋', `Dependencies: ${c.cyan}${safeDeps.join(', ')}${c.reset}`);
130
+ runCommand(`npm install ${safeDeps.join(' ')}`, targetDir);
131
+ }
132
+ }
133
+ else {
134
+ log('ℹ️', `${c.dim}No additional dependencies in blueprint.${c.reset}`);
135
+ }
136
+ // Phase 3: AI IDE Rules
137
+ logPhase(3, '🧠 Injecting AI Rules');
138
+ if (report.aiIdeRules) {
139
+ fs_1.default.writeFileSync(path_1.default.join(targetDir, '.cursorrules'), report.aiIdeRules);
140
+ logSuccess('.cursorrules created');
141
+ }
142
+ else {
143
+ log('ℹ️', `${c.dim}No AI rules in blueprint.${c.reset}`);
144
+ }
145
+ // Phase 4: Blueprint Components
146
+ logPhase(4, '🏗️ Writing Blueprint Components');
147
+ const componentsDir = path_1.default.join(targetDir, 'src', 'components');
148
+ const hooksDir = path_1.default.join(targetDir, 'src', 'hooks');
149
+ fs_1.default.mkdirSync(componentsDir, { recursive: true });
150
+ fs_1.default.mkdirSync(hooksDir, { recursive: true });
151
+ let filesWritten = 0;
152
+ if (report.sceneScaffoldCode && report.sceneScaffoldCode.includes('import')) {
153
+ fs_1.default.writeFileSync(path_1.default.join(componentsDir, 'BackgroundScene.tsx'), report.sceneScaffoldCode);
154
+ logSuccess('src/components/BackgroundScene.tsx');
155
+ filesWritten++;
156
+ }
157
+ if (report.gsapSequencerCode && report.gsapSequencerCode.includes('import')) {
158
+ fs_1.default.writeFileSync(path_1.default.join(hooksDir, 'useScrollAnimations.ts'), report.gsapSequencerCode);
159
+ logSuccess('src/hooks/useScrollAnimations.ts');
160
+ filesWritten++;
161
+ }
162
+ // Write design tokens as CSS variables
163
+ if (report.designSystem?.colors) {
164
+ const colors = report.designSystem.colors;
165
+ let cssVars = ':root {\n';
166
+ for (const [key, value] of Object.entries(colors)) {
167
+ cssVars += ` --color-${key}: ${value};\n`;
168
+ }
169
+ cssVars += '}\n';
170
+ const tokensPath = path_1.default.join(targetDir, 'src', 'styles', 'tokens.css');
171
+ fs_1.default.mkdirSync(path_1.default.dirname(tokensPath), { recursive: true });
172
+ fs_1.default.writeFileSync(tokensPath, cssVars);
173
+ logSuccess('src/styles/tokens.css (design tokens)');
174
+ filesWritten++;
175
+ }
176
+ if (filesWritten === 0) {
177
+ log('ℹ️', `${c.dim}No component code in blueprint.${c.reset}`);
178
+ }
179
+ // Phase 5: Build Roadmap
180
+ logPhase(5, '🗺️ Generating Build Roadmap');
181
+ if (report.buildRoadmap?.phases) {
182
+ let md = '# Build Roadmap\n\n';
183
+ md += '> Generated by [PageScrape](https://github.com/pagescrape) — Use this checklist with your AI assistant.\n\n';
184
+ report.buildRoadmap.phases.forEach((phase, i) => {
185
+ const emoji = ['🏗️', '🎨', '⚡', '🚀', '✨'][i] || '📋';
186
+ md += `## ${emoji} Phase ${i + 1}: ${phase.name}\n`;
187
+ md += `**Difficulty:** ${phase.difficulty}\n\n`;
188
+ if (phase.steps) {
189
+ phase.steps.forEach((step) => {
190
+ md += `- [ ] ${step}\n`;
191
+ });
192
+ }
193
+ md += '\n';
194
+ });
195
+ fs_1.default.writeFileSync(path_1.default.join(targetDir, 'tasks.md'), md);
196
+ logSuccess('tasks.md');
197
+ }
198
+ else {
199
+ log('ℹ️', `${c.dim}No build roadmap in blueprint.${c.reset}`);
200
+ }
201
+ // Write the raw blueprint for reference
202
+ fs_1.default.writeFileSync(path_1.default.join(targetDir, 'pagescrape-blueprint.json'), JSON.stringify(report, null, 2));
203
+ logSuccess('pagescrape-blueprint.json (raw blueprint)');
204
+ // ── Success Banner ─────────────────────────────────────────
205
+ console.log(`
206
+ ${c.green}${c.bold} ╔══════════════════════════════════════════════════╗
207
+ ║ ║
208
+ ║ 🎉 Zero-Friction Scaffold Complete! ║
209
+ ║ ║
210
+ ╚══════════════════════════════════════════════════╝${c.reset}
211
+
212
+ ${c.bold}Next steps:${c.reset}
213
+
214
+ ${c.cyan}1.${c.reset} cd ${c.bold}${dirName}${c.reset}
215
+ ${c.cyan}2.${c.reset} Open in ${c.bold}Cursor${c.reset} / ${c.bold}Windsurf${c.reset} / your AI-powered IDE
216
+ ${c.cyan}3.${c.reset} Your AI will auto-read ${c.bold}.cursorrules${c.reset} ✨
217
+ ${c.cyan}4.${c.reset} Say ${c.green}"Build Phase 1"${c.reset} and watch it work
218
+
219
+ ${c.dim}Blueprint saved to ${dirName}/pagescrape-blueprint.json${c.reset}
220
+ `);
221
+ }
222
+ // ── Argument Parser ──────────────────────────────────────────
223
+ function parseArgs(argv) {
224
+ const args = argv.slice(2); // strip node + script path
225
+ const parsed = {
226
+ help: false,
227
+ version: false,
228
+ server: DEFAULT_SERVER,
229
+ };
230
+ for (let i = 0; i < args.length; i++) {
231
+ const arg = args[i];
232
+ if (arg === '--help' || arg === '-h') {
233
+ parsed.help = true;
234
+ }
235
+ else if (arg === '--version' || arg === '-v') {
236
+ parsed.version = true;
237
+ }
238
+ else if (arg === '--file' || arg === '-f') {
239
+ parsed.file = args[++i];
240
+ }
241
+ else if (arg === '--server' || arg === '-s') {
242
+ parsed.server = args[++i];
243
+ }
244
+ else if (arg === '--output' || arg === '-o') {
245
+ parsed.output = args[++i];
246
+ }
247
+ else if (!arg.startsWith('-')) {
248
+ parsed.jobId = arg;
249
+ }
250
+ else {
251
+ logError(`Unknown flag: ${arg}`);
252
+ process.exit(1);
253
+ }
254
+ }
255
+ return parsed;
256
+ }
257
+ function showHelp() {
258
+ banner();
259
+ console.log(` ${c.bold}USAGE${c.reset}`);
260
+ console.log(` ${c.cyan}npx pagescrape-init${c.reset} <job-id> Fetch blueprint & scaffold`);
261
+ console.log(` ${c.cyan}npx pagescrape-init${c.reset} --file <path> Use a local JSON blueprint`);
262
+ console.log();
263
+ console.log(` ${c.bold}OPTIONS${c.reset}`);
264
+ console.log(` ${c.green}-f, --file${c.reset} <path> Path to a local blueprint JSON file`);
265
+ console.log(` ${c.green}-s, --server${c.reset} <url> PageScrape server URL (default: ${DEFAULT_SERVER})`);
266
+ console.log(` ${c.green}-o, --output${c.reset} <dir> Output directory name (default: ${TARGET_DIR_NAME})`);
267
+ console.log(` ${c.green}-v, --version${c.reset} Show version`);
268
+ console.log(` ${c.green}-h, --help${c.reset} Show this help message`);
269
+ console.log();
270
+ console.log(` ${c.bold}EXAMPLES${c.reset}`);
271
+ console.log(` ${c.dim}# Scaffold from a running PageScrape server${c.reset}`);
272
+ console.log(` ${c.cyan}npx pagescrape-init 42${c.reset}`);
273
+ console.log();
274
+ console.log(` ${c.dim}# Use a locally saved blueprint file${c.reset}`);
275
+ console.log(` ${c.cyan}npx pagescrape-init --file ./my-blueprint.json${c.reset}`);
276
+ console.log();
277
+ console.log(` ${c.dim}# Custom server and output directory${c.reset}`);
278
+ console.log(` ${c.cyan}npx pagescrape-init -s https://pagescrape.example.com -o my-clone 7${c.reset}`);
279
+ console.log();
280
+ console.log(` ${c.bold}ENVIRONMENT${c.reset}`);
281
+ console.log(` ${c.green}PAGESCRAPE_SERVER${c.reset} Override the default server URL`);
282
+ console.log();
283
+ }
284
+ // ── CLI Entry Point ──────────────────────────────────────────
285
+ async function main() {
286
+ const opts = parseArgs(process.argv);
287
+ if (opts.version) {
288
+ console.log(`pagescrape-init v${VERSION}`);
289
+ process.exit(0);
290
+ }
291
+ if (opts.help || (!opts.jobId && !opts.file)) {
292
+ showHelp();
293
+ process.exit(0);
294
+ }
295
+ banner();
296
+ let report;
297
+ if (opts.file) {
298
+ log('📖', `Loading local blueprint: ${c.cyan}${opts.file}${c.reset}`);
299
+ report = loadLocalFile(opts.file);
300
+ }
301
+ else if (opts.jobId) {
302
+ report = await fetchBlueprint(opts.jobId, opts.server);
303
+ }
304
+ log('✅', `${c.green}Blueprint loaded successfully${c.reset}`);
305
+ // Show quick summary
306
+ if (report.quickStart) {
307
+ console.log();
308
+ log('📊', `${c.bold}Blueprint Summary:${c.reset}`);
309
+ if (report.quickStart.techStack) {
310
+ console.log(` Tech: ${c.cyan}${report.quickStart.techStack.join(' • ')}${c.reset}`);
311
+ }
312
+ if (report.quickStart.difficultyLevel) {
313
+ console.log(` Difficulty: ${c.yellow}${report.quickStart.difficultyLevel}${c.reset}`);
314
+ }
315
+ }
316
+ await initProject(report, opts.output);
317
+ }
318
+ main().catch((err) => {
319
+ logError(`Fatal error: ${err.message || err}`);
320
+ process.exit(1);
321
+ });
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "pagescrape-init",
3
+ "version": "1.0.0",
4
+ "description": "Zero-friction CLI to scaffold a production-ready Next.js clone from a PageScrape blueprint.",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "pagescrape-init": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "prepublishOnly": "npm run build",
16
+ "dev": "tsx src/index.ts"
17
+ },
18
+ "engines": {
19
+ "node": ">=18.0.0"
20
+ },
21
+ "keywords": [
22
+ "pagescrape",
23
+ "web-scraping",
24
+ "scaffold",
25
+ "nextjs",
26
+ "cli",
27
+ "site-clone",
28
+ "reverse-engineer",
29
+ "vibe-coding"
30
+ ],
31
+ "author": "",
32
+ "license": "ISC",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": ""
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.0.0",
39
+ "tsx": "^4.21.0",
40
+ "typescript": "^5.7.0"
41
+ }
42
+ }