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.
- package/README.md +65 -0
- package/dist/index.js +321 -0
- 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
|
+
}
|