real-prototypes-skill 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/.claude/skills/agent-browser-skill/SKILL.md +252 -0
- package/.claude/skills/real-prototypes-skill/.gitignore +188 -0
- package/.claude/skills/real-prototypes-skill/ACCESSIBILITY.md +668 -0
- package/.claude/skills/real-prototypes-skill/INSTALL.md +259 -0
- package/.claude/skills/real-prototypes-skill/LICENSE +21 -0
- package/.claude/skills/real-prototypes-skill/PUBLISH.md +310 -0
- package/.claude/skills/real-prototypes-skill/QUICKSTART.md +240 -0
- package/.claude/skills/real-prototypes-skill/README.md +442 -0
- package/.claude/skills/real-prototypes-skill/SKILL.md +375 -0
- package/.claude/skills/real-prototypes-skill/capture/capture-engine.js +1153 -0
- package/.claude/skills/real-prototypes-skill/capture/config.schema.json +170 -0
- package/.claude/skills/real-prototypes-skill/cli.js +596 -0
- package/.claude/skills/real-prototypes-skill/docs/TROUBLESHOOTING.md +278 -0
- package/.claude/skills/real-prototypes-skill/docs/schemas/capture-config.md +167 -0
- package/.claude/skills/real-prototypes-skill/docs/schemas/design-tokens.md +183 -0
- package/.claude/skills/real-prototypes-skill/docs/schemas/manifest.md +169 -0
- package/.claude/skills/real-prototypes-skill/examples/CLAUDE.md.example +73 -0
- package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/CLAUDE.md +136 -0
- package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/FEATURES.md +222 -0
- package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/README.md +82 -0
- package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/design-tokens.json +87 -0
- package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/screenshots/homepage-viewport.png +0 -0
- package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/screenshots/prototype-chatbot-final.png +0 -0
- package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/screenshots/prototype-fullpage-v2.png +0 -0
- package/.claude/skills/real-prototypes-skill/references/accessibility-fixes.md +298 -0
- package/.claude/skills/real-prototypes-skill/references/accessibility-report.json +253 -0
- package/.claude/skills/real-prototypes-skill/scripts/CAPTURE-ENHANCEMENTS.md +344 -0
- package/.claude/skills/real-prototypes-skill/scripts/IMPLEMENTATION-SUMMARY.md +517 -0
- package/.claude/skills/real-prototypes-skill/scripts/QUICK-START.md +229 -0
- package/.claude/skills/real-prototypes-skill/scripts/QUICKSTART-layout-analysis.md +148 -0
- package/.claude/skills/real-prototypes-skill/scripts/README-analyze-layout.md +407 -0
- package/.claude/skills/real-prototypes-skill/scripts/analyze-layout.js +880 -0
- package/.claude/skills/real-prototypes-skill/scripts/capture-platform.js +203 -0
- package/.claude/skills/real-prototypes-skill/scripts/comprehensive-capture.js +597 -0
- package/.claude/skills/real-prototypes-skill/scripts/create-manifest.js +338 -0
- package/.claude/skills/real-prototypes-skill/scripts/enterprise-pipeline.js +428 -0
- package/.claude/skills/real-prototypes-skill/scripts/extract-tokens.js +468 -0
- package/.claude/skills/real-prototypes-skill/scripts/full-site-capture.js +738 -0
- package/.claude/skills/real-prototypes-skill/scripts/generate-tailwind-config.js +296 -0
- package/.claude/skills/real-prototypes-skill/scripts/integrate-accessibility.sh +161 -0
- package/.claude/skills/real-prototypes-skill/scripts/manifest-schema.json +302 -0
- package/.claude/skills/real-prototypes-skill/scripts/setup-prototype.sh +167 -0
- package/.claude/skills/real-prototypes-skill/scripts/test-analyze-layout.js +338 -0
- package/.claude/skills/real-prototypes-skill/scripts/test-validation.js +307 -0
- package/.claude/skills/real-prototypes-skill/scripts/validate-accessibility.js +598 -0
- package/.claude/skills/real-prototypes-skill/scripts/validate-manifest.js +499 -0
- package/.claude/skills/real-prototypes-skill/scripts/validate-output.js +361 -0
- package/.claude/skills/real-prototypes-skill/scripts/validate-prerequisites.js +319 -0
- package/.claude/skills/real-prototypes-skill/scripts/verify-layout-analysis.sh +77 -0
- package/.claude/skills/real-prototypes-skill/templates/dashboard-widget.tsx.template +91 -0
- package/.claude/skills/real-prototypes-skill/templates/data-table.tsx.template +193 -0
- package/.claude/skills/real-prototypes-skill/templates/form-section.tsx.template +250 -0
- package/.claude/skills/real-prototypes-skill/templates/modal-dialog.tsx.template +239 -0
- package/.claude/skills/real-prototypes-skill/templates/nav-item.tsx.template +265 -0
- package/.claude/skills/real-prototypes-skill/validation/validation-engine.js +559 -0
- package/.env.example +74 -0
- package/LICENSE +21 -0
- package/README.md +444 -0
- package/bin/cli.js +319 -0
- package/package.json +59 -0
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Full Site Capture Script - Enhanced with Robust Error Handling
|
|
5
|
+
*
|
|
6
|
+
* Automatically discovers and captures all pages of a platform with:
|
|
7
|
+
* - Wait for networkidle0 (all network requests complete)
|
|
8
|
+
* - Pre-screenshot validation (status codes, page load, element checks)
|
|
9
|
+
* - Retry logic with exponential backoff
|
|
10
|
+
* - Comprehensive error logging
|
|
11
|
+
* - Success validation (file sizes, dimensions)
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* node full-site-capture.js [claude-md-path] [output-dir]
|
|
15
|
+
*
|
|
16
|
+
* What it does:
|
|
17
|
+
* 1. Reads credentials from CLAUDE.md
|
|
18
|
+
* 2. Logs into the platform
|
|
19
|
+
* 3. Crawls to discover all internal pages
|
|
20
|
+
* 4. Captures screenshot + HTML for each page (with validation)
|
|
21
|
+
* 5. Extracts design tokens
|
|
22
|
+
* 6. Generates manifest.json
|
|
23
|
+
* 7. Creates error log for any failures
|
|
24
|
+
*
|
|
25
|
+
* Output: Generates agent-browser commands for the full capture workflow
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
|
|
31
|
+
// Default configuration
|
|
32
|
+
const DEFAULT_CONFIG = {
|
|
33
|
+
maxPages: 50,
|
|
34
|
+
viewportWidth: 1920,
|
|
35
|
+
viewportHeight: 1080,
|
|
36
|
+
waitAfterLoad: 5000, // Increased default to 5s
|
|
37
|
+
maxWaitTimeout: 10000, // Maximum wait timeout
|
|
38
|
+
captureMode: 'full',
|
|
39
|
+
maxRetries: 3, // For 404 errors
|
|
40
|
+
timeoutRetries: 2, // For timeout errors
|
|
41
|
+
retryDelayBase: 1000, // Base delay for exponential backoff (1s)
|
|
42
|
+
minScreenshotSize: 102400, // 100KB minimum
|
|
43
|
+
minHtmlSize: 10240, // 10KB minimum
|
|
44
|
+
minPageHeight: 500 // Minimum page height in pixels
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Parse CLAUDE.md for configuration
|
|
49
|
+
*/
|
|
50
|
+
function parseClaudeMd(filePath) {
|
|
51
|
+
if (!fs.existsSync(filePath)) {
|
|
52
|
+
console.error(`Error: CLAUDE.md not found at ${filePath}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
57
|
+
const config = { ...DEFAULT_CONFIG };
|
|
58
|
+
|
|
59
|
+
// Extract configuration values
|
|
60
|
+
const patterns = {
|
|
61
|
+
PLATFORM_URL: /PLATFORM_URL=(.+)/,
|
|
62
|
+
PLATFORM_EMAIL: /PLATFORM_EMAIL=(.+)/,
|
|
63
|
+
PLATFORM_PASSWORD: /PLATFORM_PASSWORD=(.+)/,
|
|
64
|
+
PAGES_TO_CAPTURE: /PAGES_TO_CAPTURE=(.+)/,
|
|
65
|
+
CAPTURE_MODE: /CAPTURE_MODE=(.+)/,
|
|
66
|
+
MAX_PAGES: /MAX_PAGES=(\d+)/,
|
|
67
|
+
VIEWPORT_WIDTH: /VIEWPORT_WIDTH=(\d+)/,
|
|
68
|
+
VIEWPORT_HEIGHT: /VIEWPORT_HEIGHT=(\d+)/,
|
|
69
|
+
WAIT_AFTER_LOAD: /WAIT_AFTER_LOAD=(\d+)/,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
for (const [key, pattern] of Object.entries(patterns)) {
|
|
73
|
+
const match = content.match(pattern);
|
|
74
|
+
if (match) {
|
|
75
|
+
const value = match[1].trim();
|
|
76
|
+
if (['MAX_PAGES', 'VIEWPORT_WIDTH', 'VIEWPORT_HEIGHT', 'WAIT_AFTER_LOAD'].includes(key)) {
|
|
77
|
+
config[key] = parseInt(value, 10);
|
|
78
|
+
} else {
|
|
79
|
+
config[key] = value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return config;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Generate validation script for page load
|
|
89
|
+
*/
|
|
90
|
+
function generateValidationScript() {
|
|
91
|
+
return `
|
|
92
|
+
// Pre-screenshot validation
|
|
93
|
+
const validation = {
|
|
94
|
+
status: false,
|
|
95
|
+
errors: [],
|
|
96
|
+
checks: {}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
// Check 1: Response status
|
|
101
|
+
validation.checks.statusOk = true; // Will be set by response check
|
|
102
|
+
|
|
103
|
+
// Check 2: Page title exists and not empty
|
|
104
|
+
const title = document.title;
|
|
105
|
+
validation.checks.titleExists = title && title.trim().length > 0;
|
|
106
|
+
if (!validation.checks.titleExists) {
|
|
107
|
+
validation.errors.push('Page title is empty');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check 3: Document body exists
|
|
111
|
+
validation.checks.bodyExists = !!document.body;
|
|
112
|
+
if (!validation.checks.bodyExists) {
|
|
113
|
+
validation.errors.push('Document body does not exist');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check 4: Key elements loaded (check for common landmarks)
|
|
117
|
+
const hasMain = !!document.querySelector('main, [role="main"], #main, .main');
|
|
118
|
+
const hasNav = !!document.querySelector('nav, [role="navigation"], #nav, .nav, header');
|
|
119
|
+
const hasContent = !!document.querySelector('[data-testid], .content, #content, main, article');
|
|
120
|
+
validation.checks.keyElementsLoaded = hasMain || hasNav || hasContent;
|
|
121
|
+
if (!validation.checks.keyElementsLoaded) {
|
|
122
|
+
validation.errors.push('No key elements found (main, nav, or content areas)');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Check 5: Page height validation
|
|
126
|
+
const pageHeight = Math.max(
|
|
127
|
+
document.body.scrollHeight,
|
|
128
|
+
document.body.offsetHeight,
|
|
129
|
+
document.documentElement.clientHeight,
|
|
130
|
+
document.documentElement.scrollHeight,
|
|
131
|
+
document.documentElement.offsetHeight
|
|
132
|
+
);
|
|
133
|
+
validation.checks.heightValid = pageHeight > 500;
|
|
134
|
+
validation.checks.pageHeight = pageHeight;
|
|
135
|
+
if (!validation.checks.heightValid) {
|
|
136
|
+
validation.errors.push(\`Page height too small: \${pageHeight}px\`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Check 6: No error messages visible
|
|
140
|
+
const errorSelectors = [
|
|
141
|
+
'.error', '[class*="error"]',
|
|
142
|
+
'.alert-error', '.alert-danger',
|
|
143
|
+
'[role="alert"]',
|
|
144
|
+
'#error', '#error-message'
|
|
145
|
+
];
|
|
146
|
+
let hasErrorMessage = false;
|
|
147
|
+
for (const selector of errorSelectors) {
|
|
148
|
+
const el = document.querySelector(selector);
|
|
149
|
+
if (el && el.offsetParent !== null) { // visible check
|
|
150
|
+
const text = el.textContent.toLowerCase();
|
|
151
|
+
if (text.includes('error') || text.includes('404') || text.includes('not found')) {
|
|
152
|
+
hasErrorMessage = true;
|
|
153
|
+
validation.errors.push(\`Error message detected: \${text.substring(0, 100)}\`);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
validation.checks.noErrorMessages = !hasErrorMessage;
|
|
159
|
+
|
|
160
|
+
// Overall validation status
|
|
161
|
+
validation.status = validation.checks.titleExists &&
|
|
162
|
+
validation.checks.bodyExists &&
|
|
163
|
+
validation.checks.keyElementsLoaded &&
|
|
164
|
+
validation.checks.heightValid &&
|
|
165
|
+
validation.checks.noErrorMessages;
|
|
166
|
+
|
|
167
|
+
} catch (error) {
|
|
168
|
+
validation.errors.push(\`Validation script error: \${error.message}\`);
|
|
169
|
+
validation.status = false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
JSON.stringify(validation);
|
|
173
|
+
`.trim();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Generate retry logic with exponential backoff
|
|
178
|
+
*/
|
|
179
|
+
function generateRetryLogic(config) {
|
|
180
|
+
const { maxRetries, timeoutRetries, retryDelayBase } = config;
|
|
181
|
+
|
|
182
|
+
return `
|
|
183
|
+
# Retry function with exponential backoff
|
|
184
|
+
retry_capture() {
|
|
185
|
+
local PAGE_PATH=$1
|
|
186
|
+
local ATTEMPT=0
|
|
187
|
+
local MAX_ATTEMPTS=${maxRetries}
|
|
188
|
+
local TIMEOUT_ATTEMPTS=${timeoutRetries}
|
|
189
|
+
local DELAY=${retryDelayBase}
|
|
190
|
+
local SUCCESS=false
|
|
191
|
+
|
|
192
|
+
while [ $ATTEMPT -lt $MAX_ATTEMPTS ] && [ "$SUCCESS" = "false" ]; do
|
|
193
|
+
ATTEMPT=$((ATTEMPT + 1))
|
|
194
|
+
|
|
195
|
+
if [ $ATTEMPT -gt 1 ]; then
|
|
196
|
+
echo " Retry attempt $ATTEMPT for $PAGE_PATH (waiting \${DELAY}ms)..."
|
|
197
|
+
sleep $(echo "scale=3; $DELAY/1000" | bc)
|
|
198
|
+
DELAY=$((DELAY * 2)) # Exponential backoff
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# Attempt capture
|
|
202
|
+
if capture_page_with_validation "$PAGE_PATH"; then
|
|
203
|
+
SUCCESS=true
|
|
204
|
+
echo " ✓ Successfully captured $PAGE_PATH"
|
|
205
|
+
else
|
|
206
|
+
echo " ✗ Failed attempt $ATTEMPT for $PAGE_PATH"
|
|
207
|
+
fi
|
|
208
|
+
done
|
|
209
|
+
|
|
210
|
+
if [ "$SUCCESS" = "false" ]; then
|
|
211
|
+
log_error "$PAGE_PATH" "capture_failed" "Failed after $MAX_ATTEMPTS attempts"
|
|
212
|
+
return 1
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
return 0
|
|
216
|
+
}
|
|
217
|
+
`.trim();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Generate error logging function
|
|
222
|
+
*/
|
|
223
|
+
function generateErrorLogging(outputDir) {
|
|
224
|
+
const errorLogPath = path.join(outputDir, 'capture-errors.log');
|
|
225
|
+
|
|
226
|
+
return `
|
|
227
|
+
# Error logging function
|
|
228
|
+
ERROR_LOG="${errorLogPath}"
|
|
229
|
+
echo "=== Capture Error Log ===" > "$ERROR_LOG"
|
|
230
|
+
echo "Started: $(date -Iseconds)" >> "$ERROR_LOG"
|
|
231
|
+
echo "" >> "$ERROR_LOG"
|
|
232
|
+
|
|
233
|
+
log_error() {
|
|
234
|
+
local PAGE_PATH=$1
|
|
235
|
+
local ERROR_TYPE=$2
|
|
236
|
+
local ERROR_MESSAGE=$3
|
|
237
|
+
local TIMESTAMP=$(date -Iseconds)
|
|
238
|
+
|
|
239
|
+
echo "[$TIMESTAMP] ERROR: $PAGE_PATH" >> "$ERROR_LOG"
|
|
240
|
+
echo " Type: $ERROR_TYPE" >> "$ERROR_LOG"
|
|
241
|
+
echo " Message: $ERROR_MESSAGE" >> "$ERROR_LOG"
|
|
242
|
+
echo "" >> "$ERROR_LOG"
|
|
243
|
+
|
|
244
|
+
# Also log to console
|
|
245
|
+
echo " ⚠️ ERROR logged for $PAGE_PATH: $ERROR_MESSAGE"
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
log_summary() {
|
|
249
|
+
echo "" >> "$ERROR_LOG"
|
|
250
|
+
echo "=== Capture Summary ===" >> "$ERROR_LOG"
|
|
251
|
+
echo "Completed: $(date -Iseconds)" >> "$ERROR_LOG"
|
|
252
|
+
echo "Total Pages Attempted: $PAGES_ATTEMPTED" >> "$ERROR_LOG"
|
|
253
|
+
echo "Successful Captures: $PAGES_SUCCESS" >> "$ERROR_LOG"
|
|
254
|
+
echo "Failed Captures: $PAGES_FAILED" >> "$ERROR_LOG"
|
|
255
|
+
echo "Success Rate: $PAGES_SUCCESS_RATE%" >> "$ERROR_LOG"
|
|
256
|
+
}
|
|
257
|
+
`.trim();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Generate the crawl and capture script
|
|
262
|
+
*/
|
|
263
|
+
function generateFullCaptureScript(config, outputDir) {
|
|
264
|
+
const {
|
|
265
|
+
PLATFORM_URL,
|
|
266
|
+
PLATFORM_EMAIL,
|
|
267
|
+
PLATFORM_PASSWORD,
|
|
268
|
+
PAGES_TO_CAPTURE,
|
|
269
|
+
CAPTURE_MODE,
|
|
270
|
+
MAX_PAGES,
|
|
271
|
+
VIEWPORT_WIDTH,
|
|
272
|
+
VIEWPORT_HEIGHT,
|
|
273
|
+
WAIT_AFTER_LOAD,
|
|
274
|
+
} = config;
|
|
275
|
+
|
|
276
|
+
if (!PLATFORM_URL) {
|
|
277
|
+
console.error('Error: PLATFORM_URL not found in CLAUDE.md');
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const isAutoMode = PAGES_TO_CAPTURE === 'auto' || CAPTURE_MODE === 'full';
|
|
282
|
+
const baseUrl = PLATFORM_URL.replace(/\/$/, '');
|
|
283
|
+
|
|
284
|
+
const lines = [];
|
|
285
|
+
|
|
286
|
+
// Header
|
|
287
|
+
lines.push('#!/bin/bash');
|
|
288
|
+
lines.push('# ===========================================');
|
|
289
|
+
lines.push('# FULL SITE CAPTURE SCRIPT - ENHANCED');
|
|
290
|
+
lines.push('# ===========================================');
|
|
291
|
+
lines.push(`# Platform: ${baseUrl}`);
|
|
292
|
+
lines.push(`# Mode: ${isAutoMode ? 'Auto-discover all pages' : 'Manual page list'}`);
|
|
293
|
+
lines.push(`# Max Pages: ${MAX_PAGES}`);
|
|
294
|
+
lines.push(`# Max Retries: ${config.maxRetries}`);
|
|
295
|
+
lines.push(`# Timeout Retries: ${config.timeoutRetries}`);
|
|
296
|
+
lines.push(`# Wait After Load: ${WAIT_AFTER_LOAD}ms`);
|
|
297
|
+
lines.push(`# Max Wait Timeout: ${config.maxWaitTimeout}ms`);
|
|
298
|
+
lines.push('# ');
|
|
299
|
+
lines.push('# Features:');
|
|
300
|
+
lines.push('# - Wait for networkidle0 (all requests complete)');
|
|
301
|
+
lines.push('# - Pre-screenshot validation');
|
|
302
|
+
lines.push('# - Retry with exponential backoff');
|
|
303
|
+
lines.push('# - Comprehensive error logging');
|
|
304
|
+
lines.push('# - Success validation');
|
|
305
|
+
lines.push('# ');
|
|
306
|
+
lines.push('# This is a ONE-TIME capture. Run once to capture');
|
|
307
|
+
lines.push('# the entire platform for future prototyping.');
|
|
308
|
+
lines.push('# ===========================================');
|
|
309
|
+
lines.push('');
|
|
310
|
+
|
|
311
|
+
// Initialize counters
|
|
312
|
+
lines.push('# Initialize tracking variables');
|
|
313
|
+
lines.push('PAGES_ATTEMPTED=0');
|
|
314
|
+
lines.push('PAGES_SUCCESS=0');
|
|
315
|
+
lines.push('PAGES_FAILED=0');
|
|
316
|
+
lines.push('PAGES_SUCCESS_RATE=0');
|
|
317
|
+
lines.push('');
|
|
318
|
+
|
|
319
|
+
// Error logging setup
|
|
320
|
+
lines.push('# Step 1: Setup error logging');
|
|
321
|
+
lines.push(generateErrorLogging(outputDir));
|
|
322
|
+
lines.push('');
|
|
323
|
+
|
|
324
|
+
// Create directories
|
|
325
|
+
lines.push('# Step 2: Create directory structure');
|
|
326
|
+
lines.push(`mkdir -p ${outputDir}/screenshots`);
|
|
327
|
+
lines.push(`mkdir -p ${outputDir}/html`);
|
|
328
|
+
lines.push(`mkdir -p ${outputDir}/styles`);
|
|
329
|
+
lines.push('echo "Created reference directories"');
|
|
330
|
+
lines.push('');
|
|
331
|
+
|
|
332
|
+
// Set viewport
|
|
333
|
+
lines.push('# Step 3: Configure browser');
|
|
334
|
+
lines.push(`agent-browser set viewport ${VIEWPORT_WIDTH} ${VIEWPORT_HEIGHT}`);
|
|
335
|
+
lines.push('');
|
|
336
|
+
|
|
337
|
+
// Authentication
|
|
338
|
+
if (PLATFORM_EMAIL && PLATFORM_PASSWORD) {
|
|
339
|
+
lines.push('# Step 4: Authenticate');
|
|
340
|
+
lines.push(`echo "Navigating to login page..."`);
|
|
341
|
+
lines.push(`agent-browser open ${baseUrl}/login`);
|
|
342
|
+
lines.push(`agent-browser wait ${WAIT_AFTER_LOAD}`);
|
|
343
|
+
lines.push('agent-browser snapshot -i');
|
|
344
|
+
lines.push('echo ""');
|
|
345
|
+
lines.push('echo "=== LOGIN REQUIRED ==="');
|
|
346
|
+
lines.push('echo "Look at the snapshot above and find the element refs for:"');
|
|
347
|
+
lines.push('echo " - Email/username input field"');
|
|
348
|
+
lines.push('echo " - Password input field"');
|
|
349
|
+
lines.push('echo " - Login/Submit button"');
|
|
350
|
+
lines.push('echo ""');
|
|
351
|
+
lines.push('echo "Then run these commands (replace @eX with actual refs):"');
|
|
352
|
+
lines.push(`echo " agent-browser fill @e1 \\"${PLATFORM_EMAIL}\\""`);
|
|
353
|
+
lines.push(`echo " agent-browser fill @e2 \\"${PLATFORM_PASSWORD}\\""`);
|
|
354
|
+
lines.push('echo " agent-browser click @e3"');
|
|
355
|
+
lines.push('echo " agent-browser wait --load networkidle"');
|
|
356
|
+
lines.push('echo ""');
|
|
357
|
+
lines.push('echo "After login succeeds, continue with Step 4"');
|
|
358
|
+
lines.push('echo "======================');
|
|
359
|
+
lines.push('');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (isAutoMode) {
|
|
363
|
+
// Auto-discovery mode
|
|
364
|
+
lines.push('# Step 5: Discover all pages');
|
|
365
|
+
lines.push('echo "Starting page discovery..."');
|
|
366
|
+
lines.push('');
|
|
367
|
+
lines.push('# Get all internal links from current page');
|
|
368
|
+
lines.push(`DISCOVER_SCRIPT='
|
|
369
|
+
const links = new Set();
|
|
370
|
+
const baseUrl = "${baseUrl}";
|
|
371
|
+
const baseDomain = new URL(baseUrl).hostname;
|
|
372
|
+
|
|
373
|
+
document.querySelectorAll("a[href]").forEach(a => {
|
|
374
|
+
try {
|
|
375
|
+
const url = new URL(a.href, baseUrl);
|
|
376
|
+
// Only internal links, no query params or hashes
|
|
377
|
+
if (url.hostname === baseDomain) {
|
|
378
|
+
const cleanPath = url.pathname.replace(/\\/$/, "") || "/";
|
|
379
|
+
links.add(cleanPath);
|
|
380
|
+
}
|
|
381
|
+
} catch(e) {}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Also check for common navigation patterns
|
|
385
|
+
document.querySelectorAll("[data-href], [data-link], [onclick]").forEach(el => {
|
|
386
|
+
const href = el.dataset.href || el.dataset.link;
|
|
387
|
+
if (href && href.startsWith("/")) {
|
|
388
|
+
links.add(href.split("?")[0]);
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
JSON.stringify([...links].slice(0, ${MAX_PAGES}));
|
|
393
|
+
'`);
|
|
394
|
+
lines.push('');
|
|
395
|
+
lines.push('# Navigate to main page first to discover links');
|
|
396
|
+
lines.push(`agent-browser open ${baseUrl}`);
|
|
397
|
+
lines.push(`agent-browser wait ${WAIT_AFTER_LOAD}`);
|
|
398
|
+
lines.push('agent-browser wait --load networkidle');
|
|
399
|
+
lines.push('');
|
|
400
|
+
lines.push('# Extract all internal links');
|
|
401
|
+
lines.push('echo "Extracting internal links..."');
|
|
402
|
+
lines.push('agent-browser eval "$DISCOVER_SCRIPT"');
|
|
403
|
+
lines.push('');
|
|
404
|
+
lines.push('# The above command outputs a JSON array of paths');
|
|
405
|
+
lines.push('# Save that output and iterate through each path');
|
|
406
|
+
lines.push('');
|
|
407
|
+
lines.push('echo ""');
|
|
408
|
+
lines.push('echo "=== DISCOVERED PAGES ==="');
|
|
409
|
+
lines.push('echo "The eval command above outputs a JSON array of paths."');
|
|
410
|
+
lines.push('echo "For each path, run the capture commands in Step 5."');
|
|
411
|
+
lines.push('echo "========================"');
|
|
412
|
+
lines.push('');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Validation script
|
|
416
|
+
lines.push('# Validation script');
|
|
417
|
+
lines.push(`VALIDATION_SCRIPT='${generateValidationScript()}'`);
|
|
418
|
+
lines.push('');
|
|
419
|
+
|
|
420
|
+
// Capture function with validation
|
|
421
|
+
lines.push('# Step 6: Robust capture function with validation');
|
|
422
|
+
lines.push('# For each page path, run these commands:');
|
|
423
|
+
lines.push('');
|
|
424
|
+
lines.push('capture_page_with_validation() {');
|
|
425
|
+
lines.push(' local PAGE_PATH=$1');
|
|
426
|
+
lines.push(' local PAGE_NAME=$(echo "$PAGE_PATH" | sed "s/^\\/*//" | sed "s/\\//-/g")');
|
|
427
|
+
lines.push(' [ -z "$PAGE_NAME" ] && PAGE_NAME="home"');
|
|
428
|
+
lines.push(' ');
|
|
429
|
+
lines.push(' echo "Capturing: $PAGE_PATH -> $PAGE_NAME"');
|
|
430
|
+
lines.push(' ');
|
|
431
|
+
lines.push(' # Navigate to page');
|
|
432
|
+
lines.push(` agent-browser open ${baseUrl}\$PAGE_PATH`);
|
|
433
|
+
lines.push(' ');
|
|
434
|
+
lines.push(' # Wait strategies (multiple layers)');
|
|
435
|
+
lines.push(` # 1. Initial wait after load`);
|
|
436
|
+
lines.push(` agent-browser wait ${WAIT_AFTER_LOAD}`);
|
|
437
|
+
lines.push(' ');
|
|
438
|
+
lines.push(' # 2. Wait for network idle (all requests complete)');
|
|
439
|
+
lines.push(' agent-browser wait --load networkidle');
|
|
440
|
+
lines.push(' ');
|
|
441
|
+
lines.push(' # 3. Wait for load event');
|
|
442
|
+
lines.push(' agent-browser wait --load load');
|
|
443
|
+
lines.push(' ');
|
|
444
|
+
lines.push(' # 4. Wait for DOM content loaded');
|
|
445
|
+
lines.push(' agent-browser wait --load domcontentloaded');
|
|
446
|
+
lines.push(' ');
|
|
447
|
+
lines.push(' # Pre-screenshot validation');
|
|
448
|
+
lines.push(' VALIDATION_RESULT=$(agent-browser eval "$VALIDATION_SCRIPT")');
|
|
449
|
+
lines.push(' VALIDATION_STATUS=$(echo "$VALIDATION_RESULT" | jq -r ".status")');
|
|
450
|
+
lines.push(' ');
|
|
451
|
+
lines.push(' if [ "$VALIDATION_STATUS" != "true" ]; then');
|
|
452
|
+
lines.push(' VALIDATION_ERRORS=$(echo "$VALIDATION_RESULT" | jq -r ".errors | join(\\", \\")")');
|
|
453
|
+
lines.push(' log_error "$PAGE_PATH" "validation_failed" "$VALIDATION_ERRORS"');
|
|
454
|
+
lines.push(' return 1');
|
|
455
|
+
lines.push(' fi');
|
|
456
|
+
lines.push(' ');
|
|
457
|
+
lines.push(' # Capture screenshot');
|
|
458
|
+
lines.push(` agent-browser screenshot --full ${outputDir}/screenshots/\$PAGE_NAME.png`);
|
|
459
|
+
lines.push(' ');
|
|
460
|
+
lines.push(' # Capture HTML');
|
|
461
|
+
lines.push(` agent-browser eval "document.documentElement.outerHTML" > ${outputDir}/html/\$PAGE_NAME.html`);
|
|
462
|
+
lines.push(' ');
|
|
463
|
+
lines.push(' # Post-capture validation');
|
|
464
|
+
lines.push(` SCREENSHOT_SIZE=$(stat -f%z "${outputDir}/screenshots/\$PAGE_NAME.png" 2>/dev/null || stat -c%s "${outputDir}/screenshots/\$PAGE_NAME.png" 2>/dev/null)`);
|
|
465
|
+
lines.push(` HTML_SIZE=$(stat -f%z "${outputDir}/html/\$PAGE_NAME.html" 2>/dev/null || stat -c%s "${outputDir}/html/\$PAGE_NAME.html" 2>/dev/null)`);
|
|
466
|
+
lines.push(' ');
|
|
467
|
+
lines.push(` if [ "$SCREENSHOT_SIZE" -lt ${config.minScreenshotSize} ]; then`);
|
|
468
|
+
lines.push(' log_error "$PAGE_PATH" "screenshot_too_small" "Screenshot size: $SCREENSHOT_SIZE bytes"');
|
|
469
|
+
lines.push(' return 1');
|
|
470
|
+
lines.push(' fi');
|
|
471
|
+
lines.push(' ');
|
|
472
|
+
lines.push(` if [ "$HTML_SIZE" -lt ${config.minHtmlSize} ]; then`);
|
|
473
|
+
lines.push(' log_error "$PAGE_PATH" "html_too_small" "HTML size: $HTML_SIZE bytes"');
|
|
474
|
+
lines.push(' return 1');
|
|
475
|
+
lines.push(' fi');
|
|
476
|
+
lines.push(' ');
|
|
477
|
+
lines.push(' # Get screenshot dimensions');
|
|
478
|
+
lines.push(' PAGE_HEIGHT=$(echo "$VALIDATION_RESULT" | jq -r ".checks.pageHeight")');
|
|
479
|
+
lines.push(` if [ "$PAGE_HEIGHT" -lt ${config.minPageHeight} ]; then`);
|
|
480
|
+
lines.push(' log_error "$PAGE_PATH" "page_too_short" "Page height: $PAGE_HEIGHT pixels"');
|
|
481
|
+
lines.push(' return 1');
|
|
482
|
+
lines.push(' fi');
|
|
483
|
+
lines.push(' ');
|
|
484
|
+
lines.push(' echo " ✓ Validated: Screenshot=$SCREENSHOT_SIZE bytes, HTML=$HTML_SIZE bytes, Height=$PAGE_HEIGHT px"');
|
|
485
|
+
lines.push(' return 0');
|
|
486
|
+
lines.push('}');
|
|
487
|
+
lines.push('');
|
|
488
|
+
|
|
489
|
+
// Retry logic
|
|
490
|
+
lines.push('# Retry logic with exponential backoff');
|
|
491
|
+
lines.push(generateRetryLogic(config));
|
|
492
|
+
lines.push('');
|
|
493
|
+
|
|
494
|
+
// Wrapper function that increments counters
|
|
495
|
+
lines.push('# Capture wrapper with statistics');
|
|
496
|
+
lines.push('capture_page() {');
|
|
497
|
+
lines.push(' local PAGE_PATH=$1');
|
|
498
|
+
lines.push(' PAGES_ATTEMPTED=$((PAGES_ATTEMPTED + 1))');
|
|
499
|
+
lines.push(' ');
|
|
500
|
+
lines.push(' if retry_capture "$PAGE_PATH"; then');
|
|
501
|
+
lines.push(' PAGES_SUCCESS=$((PAGES_SUCCESS + 1))');
|
|
502
|
+
lines.push(' else');
|
|
503
|
+
lines.push(' PAGES_FAILED=$((PAGES_FAILED + 1))');
|
|
504
|
+
lines.push(' fi');
|
|
505
|
+
lines.push(' ');
|
|
506
|
+
lines.push(' # Update success rate');
|
|
507
|
+
lines.push(' if [ $PAGES_ATTEMPTED -gt 0 ]; then');
|
|
508
|
+
lines.push(' PAGES_SUCCESS_RATE=$(echo "scale=2; ($PAGES_SUCCESS * 100) / $PAGES_ATTEMPTED" | bc)');
|
|
509
|
+
lines.push(' fi');
|
|
510
|
+
lines.push('}');
|
|
511
|
+
lines.push('');
|
|
512
|
+
|
|
513
|
+
// Example captures
|
|
514
|
+
if (!isAutoMode && PAGES_TO_CAPTURE && PAGES_TO_CAPTURE !== 'auto') {
|
|
515
|
+
const pages = PAGES_TO_CAPTURE.split(',').map(p => p.trim());
|
|
516
|
+
lines.push('# Capture specified pages:');
|
|
517
|
+
for (const page of pages) {
|
|
518
|
+
lines.push(`capture_page "${page}"`);
|
|
519
|
+
}
|
|
520
|
+
} else {
|
|
521
|
+
lines.push('# Example: Capture common pages');
|
|
522
|
+
lines.push('# Uncomment and modify based on discovered pages:');
|
|
523
|
+
lines.push('# capture_page "/"');
|
|
524
|
+
lines.push('# capture_page "/dashboard"');
|
|
525
|
+
lines.push('# capture_page "/settings"');
|
|
526
|
+
lines.push('# capture_page "/profile"');
|
|
527
|
+
}
|
|
528
|
+
lines.push('');
|
|
529
|
+
|
|
530
|
+
// Extract design tokens
|
|
531
|
+
lines.push('# Step 7: Extract design tokens');
|
|
532
|
+
lines.push('echo "Extracting design tokens..."');
|
|
533
|
+
lines.push(`TOKENS_SCRIPT='
|
|
534
|
+
const styles = getComputedStyle(document.body);
|
|
535
|
+
const root = getComputedStyle(document.documentElement);
|
|
536
|
+
|
|
537
|
+
// Extract CSS variables
|
|
538
|
+
const cssVars = {};
|
|
539
|
+
for (let i = 0; i < root.length; i++) {
|
|
540
|
+
const prop = root[i];
|
|
541
|
+
if (prop.startsWith("--")) {
|
|
542
|
+
cssVars[prop] = root.getPropertyValue(prop).trim();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Extract common style properties
|
|
547
|
+
const tokens = {
|
|
548
|
+
colors: {
|
|
549
|
+
background: styles.backgroundColor,
|
|
550
|
+
text: styles.color,
|
|
551
|
+
cssVariables: cssVars
|
|
552
|
+
},
|
|
553
|
+
typography: {
|
|
554
|
+
fontFamily: styles.fontFamily,
|
|
555
|
+
fontSize: styles.fontSize,
|
|
556
|
+
lineHeight: styles.lineHeight
|
|
557
|
+
},
|
|
558
|
+
spacing: {
|
|
559
|
+
padding: styles.padding,
|
|
560
|
+
margin: styles.margin
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
JSON.stringify(tokens, null, 2);
|
|
565
|
+
'`);
|
|
566
|
+
lines.push('');
|
|
567
|
+
lines.push(`agent-browser eval "$TOKENS_SCRIPT" > ${outputDir}/styles/design-tokens.json`);
|
|
568
|
+
lines.push('');
|
|
569
|
+
|
|
570
|
+
// Generate manifest
|
|
571
|
+
lines.push('# Step 8: Generate manifest');
|
|
572
|
+
lines.push('echo "Generating manifest..."');
|
|
573
|
+
lines.push(`node "$(dirname "$0")/create-manifest.js" "${new URL(baseUrl).hostname}" "${baseUrl}" "${path.dirname(outputDir)}"`);
|
|
574
|
+
lines.push('');
|
|
575
|
+
|
|
576
|
+
// Generate summary
|
|
577
|
+
lines.push('# Step 9: Generate capture summary');
|
|
578
|
+
lines.push('log_summary');
|
|
579
|
+
lines.push('');
|
|
580
|
+
|
|
581
|
+
// Close browser
|
|
582
|
+
lines.push('# Step 10: Close browser');
|
|
583
|
+
lines.push('agent-browser close');
|
|
584
|
+
lines.push('');
|
|
585
|
+
|
|
586
|
+
// Final summary
|
|
587
|
+
lines.push('echo ""');
|
|
588
|
+
lines.push('echo "=== CAPTURE COMPLETE ==="');
|
|
589
|
+
lines.push('echo "Statistics:"');
|
|
590
|
+
lines.push('echo " Pages Attempted: $PAGES_ATTEMPTED"');
|
|
591
|
+
lines.push('echo " Successful: $PAGES_SUCCESS"');
|
|
592
|
+
lines.push('echo " Failed: $PAGES_FAILED"');
|
|
593
|
+
lines.push('echo " Success Rate: $PAGES_SUCCESS_RATE%"');
|
|
594
|
+
lines.push('echo ""');
|
|
595
|
+
lines.push('echo "Output:"');
|
|
596
|
+
lines.push(`echo " Screenshots: ${outputDir}/screenshots/"`);
|
|
597
|
+
lines.push(`echo " HTML files: ${outputDir}/html/"`);
|
|
598
|
+
lines.push(`echo " Styles: ${outputDir}/styles/"`);
|
|
599
|
+
lines.push(`echo " Manifest: ${path.dirname(outputDir)}/manifest.json"`);
|
|
600
|
+
lines.push(`echo " Error Log: ${outputDir}/capture-errors.log"`);
|
|
601
|
+
lines.push('echo ""');
|
|
602
|
+
lines.push('if [ $PAGES_FAILED -gt 0 ]; then');
|
|
603
|
+
lines.push(` echo "⚠️ WARNING: $PAGES_FAILED pages failed to capture. Check ${outputDir}/capture-errors.log for details."`);
|
|
604
|
+
lines.push('else');
|
|
605
|
+
lines.push(' echo "✓ All pages captured successfully!"');
|
|
606
|
+
lines.push('fi');
|
|
607
|
+
lines.push('echo ""');
|
|
608
|
+
lines.push('echo "You can now prototype features using these references!"');
|
|
609
|
+
lines.push('echo "========================="');
|
|
610
|
+
|
|
611
|
+
return lines.join('\n');
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Generate step-by-step instructions for manual execution
|
|
616
|
+
*/
|
|
617
|
+
function generateInstructions(config, outputDir) {
|
|
618
|
+
const {
|
|
619
|
+
PLATFORM_URL,
|
|
620
|
+
PLATFORM_EMAIL,
|
|
621
|
+
PLATFORM_PASSWORD,
|
|
622
|
+
MAX_PAGES,
|
|
623
|
+
VIEWPORT_WIDTH,
|
|
624
|
+
VIEWPORT_HEIGHT,
|
|
625
|
+
WAIT_AFTER_LOAD,
|
|
626
|
+
} = config;
|
|
627
|
+
|
|
628
|
+
const baseUrl = PLATFORM_URL.replace(/\/$/, '');
|
|
629
|
+
|
|
630
|
+
return `
|
|
631
|
+
# Full Site Capture - Step by Step Instructions
|
|
632
|
+
|
|
633
|
+
## Overview
|
|
634
|
+
This captures your entire platform in one session. Run these commands in order.
|
|
635
|
+
|
|
636
|
+
## Step 1: Setup
|
|
637
|
+
\`\`\`bash
|
|
638
|
+
mkdir -p references/screenshots references/html references/styles
|
|
639
|
+
agent-browser set viewport ${VIEWPORT_WIDTH} ${VIEWPORT_HEIGHT}
|
|
640
|
+
\`\`\`
|
|
641
|
+
|
|
642
|
+
## Step 2: Login
|
|
643
|
+
\`\`\`bash
|
|
644
|
+
agent-browser open ${baseUrl}/login
|
|
645
|
+
agent-browser wait ${WAIT_AFTER_LOAD}
|
|
646
|
+
agent-browser snapshot -i
|
|
647
|
+
\`\`\`
|
|
648
|
+
|
|
649
|
+
Look at the snapshot output and find element refs, then:
|
|
650
|
+
\`\`\`bash
|
|
651
|
+
agent-browser fill @e<email_ref> "${PLATFORM_EMAIL}"
|
|
652
|
+
agent-browser fill @e<password_ref> "${PLATFORM_PASSWORD}"
|
|
653
|
+
agent-browser click @e<submit_ref>
|
|
654
|
+
agent-browser wait --load networkidle
|
|
655
|
+
\`\`\`
|
|
656
|
+
|
|
657
|
+
## Step 3: Discover All Pages
|
|
658
|
+
\`\`\`bash
|
|
659
|
+
agent-browser open ${baseUrl}
|
|
660
|
+
agent-browser wait --load networkidle
|
|
661
|
+
agent-browser eval "JSON.stringify([...new Set([...document.querySelectorAll('a[href]')].map(a => { try { const u = new URL(a.href, '${baseUrl}'); return u.hostname === '${new URL(baseUrl).hostname}' ? u.pathname : null; } catch(e) { return null; }}).filter(Boolean))].slice(0, ${MAX_PAGES}))"
|
|
662
|
+
\`\`\`
|
|
663
|
+
|
|
664
|
+
Save the output - this is your list of pages to capture.
|
|
665
|
+
|
|
666
|
+
## Step 4: Capture Each Page
|
|
667
|
+
For each page path from Step 3:
|
|
668
|
+
\`\`\`bash
|
|
669
|
+
# Replace /path with actual path
|
|
670
|
+
agent-browser open ${baseUrl}/path
|
|
671
|
+
agent-browser wait ${WAIT_AFTER_LOAD}
|
|
672
|
+
agent-browser wait --load networkidle
|
|
673
|
+
agent-browser screenshot --full references/screenshots/path.png
|
|
674
|
+
agent-browser eval "document.documentElement.outerHTML" > references/html/path.html
|
|
675
|
+
\`\`\`
|
|
676
|
+
|
|
677
|
+
## Step 5: Extract Design Tokens
|
|
678
|
+
\`\`\`bash
|
|
679
|
+
agent-browser eval "JSON.stringify({colors:{bg:getComputedStyle(document.body).backgroundColor,text:getComputedStyle(document.body).color},fonts:{family:getComputedStyle(document.body).fontFamily}},null,2)" > references/styles/tokens.json
|
|
680
|
+
\`\`\`
|
|
681
|
+
|
|
682
|
+
## Step 6: Generate Manifest
|
|
683
|
+
\`\`\`bash
|
|
684
|
+
node .claude/skills/real-prototypes-skill/scripts/create-manifest.js "${new URL(baseUrl).hostname}" "${baseUrl}" .
|
|
685
|
+
\`\`\`
|
|
686
|
+
|
|
687
|
+
## Step 7: Cleanup
|
|
688
|
+
\`\`\`bash
|
|
689
|
+
agent-browser close
|
|
690
|
+
\`\`\`
|
|
691
|
+
|
|
692
|
+
## Done!
|
|
693
|
+
Your platform is now captured. Use these references for all future prototyping.
|
|
694
|
+
`;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Main
|
|
698
|
+
function main() {
|
|
699
|
+
const args = process.argv.slice(2);
|
|
700
|
+
const claudeMdPath = args[0] || path.join(process.cwd(), 'CLAUDE.md');
|
|
701
|
+
const outputDir = args[1] || path.join(process.cwd(), 'references');
|
|
702
|
+
|
|
703
|
+
console.log('===========================================');
|
|
704
|
+
console.log('FULL SITE CAPTURE - Configuration');
|
|
705
|
+
console.log('===========================================');
|
|
706
|
+
console.log(`Config file: ${claudeMdPath}`);
|
|
707
|
+
console.log(`Output dir: ${outputDir}`);
|
|
708
|
+
console.log('');
|
|
709
|
+
|
|
710
|
+
const config = parseClaudeMd(claudeMdPath);
|
|
711
|
+
|
|
712
|
+
console.log('Platform Settings:');
|
|
713
|
+
console.log(` URL: ${config.PLATFORM_URL || 'NOT SET'}`);
|
|
714
|
+
console.log(` Email: ${config.PLATFORM_EMAIL ? '****' : 'NOT SET'}`);
|
|
715
|
+
console.log(` Password: ${config.PLATFORM_PASSWORD ? '****' : 'NOT SET'}`);
|
|
716
|
+
console.log(` Mode: ${config.CAPTURE_MODE || 'full'}`);
|
|
717
|
+
console.log(` Max Pages: ${config.MAX_PAGES}`);
|
|
718
|
+
console.log('');
|
|
719
|
+
|
|
720
|
+
// Generate and output the capture script
|
|
721
|
+
const script = generateFullCaptureScript(config, outputDir);
|
|
722
|
+
console.log('===========================================');
|
|
723
|
+
console.log('CAPTURE SCRIPT (save as capture.sh and run)');
|
|
724
|
+
console.log('===========================================');
|
|
725
|
+
console.log(script);
|
|
726
|
+
|
|
727
|
+
// Also generate instructions
|
|
728
|
+
console.log('');
|
|
729
|
+
console.log(generateInstructions(config, outputDir));
|
|
730
|
+
|
|
731
|
+
// Save the script
|
|
732
|
+
const scriptPath = path.join(path.dirname(claudeMdPath), 'capture-site.sh');
|
|
733
|
+
fs.writeFileSync(scriptPath, script);
|
|
734
|
+
console.log(`\nScript saved to: ${scriptPath}`);
|
|
735
|
+
console.log('Run with: bash capture-site.sh');
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
main();
|