@ai-lighthouse/cli 1.0.1 โ 1.0.2
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/dist/index.js +2573 -12
- package/package.json +10 -4
- package/.ai-lighthouse/audit_example.com_2025-12-15T12-10-43.json +0 -183
- package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-32-28.html +0 -743
- package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-33-02.html +0 -757
- package/.ai-lighthouse/audit_github.com_2025-12-15T11-53-21.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-04-06.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-05-10.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-09-45.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-11-07.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-13-28.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-14-59.json +0 -205
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-07.json +0 -205
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-44.json +0 -205
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-21-38.json +0 -205
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-21.json +0 -205
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-46.json +0 -205
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-23-18.json +0 -205
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-24-43.json +0 -205
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-08.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-57.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-17-11.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-17.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-42.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-23-56.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-24.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-40.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-02.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-20.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-29-56.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-32-27.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-33-00.json +0 -168
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-34-49.json +0 -168
- package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-31.json +0 -168
- package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-45.json +0 -168
- package/.ai-lighthouse/audit_tailwindcss.com_2025-12-15T12-12-01.json +0 -169
- package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-08.json +0 -24
- package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-23.json +0 -24
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-41-34.json +0 -21
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-09.json +0 -21
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-45.json +0 -21
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-02.json +0 -21
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-26.json +0 -21
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-47-46.json +0 -906
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-50-27.json +0 -906
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-52-59.json +0 -906
- package/.ai-lighthouse/crawl_github.com_2025-12-15T12-03-33.json +0 -28
- package/CLI_UI_README.md +0 -211
- package/EXAMPLES.md +0 -87
- package/IMPLEMENTATION.md +0 -215
- package/USAGE.md +0 -264
- package/WIZARD_GUIDE.md +0 -340
- package/bin/cli.js +0 -2
- package/dist/commands/audit-interactive.d.ts +0 -2
- package/dist/commands/audit-interactive.js +0 -106
- package/dist/commands/audit-wizard.d.ts +0 -2
- package/dist/commands/audit-wizard.js +0 -110
- package/dist/commands/audit.d.ts +0 -2
- package/dist/commands/audit.js +0 -940
- package/dist/commands/crawl.d.ts +0 -2
- package/dist/commands/crawl.js +0 -267
- package/dist/commands/report.d.ts +0 -2
- package/dist/commands/report.js +0 -304
- package/dist/index.d.ts +0 -1
- package/dist/ui/AuditReportUI.d.ts +0 -10
- package/dist/ui/AuditReportUI.js +0 -76
- package/dist/ui/SetupWizard.d.ts +0 -18
- package/dist/ui/SetupWizard.js +0 -179
- package/dist/ui/components/AIUnderstandingSection.d.ts +0 -6
- package/dist/ui/components/AIUnderstandingSection.js +0 -87
- package/dist/ui/components/HallucinationSection.d.ts +0 -6
- package/dist/ui/components/HallucinationSection.js +0 -84
- package/dist/ui/components/IssuesSection.d.ts +0 -6
- package/dist/ui/components/IssuesSection.js +0 -84
- package/dist/ui/components/MessageAlignmentSection.d.ts +0 -6
- package/dist/ui/components/MessageAlignmentSection.js +0 -108
- package/dist/ui/components/OverviewSection.d.ts +0 -6
- package/dist/ui/components/OverviewSection.js +0 -107
- package/dist/ui/components/ScoreDisplay.d.ts +0 -8
- package/dist/ui/components/ScoreDisplay.js +0 -41
- package/dist/ui/components/TechnicalSection.d.ts +0 -7
- package/dist/ui/components/TechnicalSection.js +0 -110
- package/dist/utils/comprehensive-formatter.d.ts +0 -5
- package/dist/utils/comprehensive-formatter.js +0 -370
- package/src/commands/audit-interactive.ts +0 -149
- package/src/commands/audit-wizard.ts +0 -137
- package/src/commands/audit.ts +0 -1012
- package/src/commands/crawl.ts +0 -307
- package/src/commands/report.ts +0 -321
- package/src/index.ts +0 -22
- package/src/ui/AuditReportUI.tsx +0 -151
- package/src/ui/SetupWizard.tsx +0 -294
- package/src/ui/components/AIUnderstandingSection.tsx +0 -183
- package/src/ui/components/HallucinationSection.tsx +0 -172
- package/src/ui/components/IssuesSection.tsx +0 -140
- package/src/ui/components/MessageAlignmentSection.tsx +0 -203
- package/src/ui/components/OverviewSection.tsx +0 -157
- package/src/ui/components/ScoreDisplay.tsx +0 -58
- package/src/ui/components/TechnicalSection.tsx +0 -200
- package/src/utils/comprehensive-formatter.ts +0 -455
- package/test.sh +0 -31
- package/tsconfig.json +0 -25
package/dist/commands/audit.js
DELETED
|
@@ -1,940 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import ora from 'ora';
|
|
3
|
-
import { analyzeUrlWithRules } from 'scanner';
|
|
4
|
-
import { calculateAIReadiness, formatAIReadinessReport } from 'scanner';
|
|
5
|
-
import { exportAuditReport, generateScoringSummary } from 'scanner';
|
|
6
|
-
import { writeFile, mkdir } from 'fs/promises';
|
|
7
|
-
import { join, resolve } from 'path';
|
|
8
|
-
import { existsSync } from 'fs';
|
|
9
|
-
import html_to_pdf from 'html-pdf-node';
|
|
10
|
-
import { formatComprehensiveReport, formatDetailedIssues } from '../utils/comprehensive-formatter.js';
|
|
11
|
-
import { render } from 'ink';
|
|
12
|
-
import React from 'react';
|
|
13
|
-
import { AuditReportUI } from '../ui/AuditReportUI.js';
|
|
14
|
-
import { SetupWizard } from '../ui/SetupWizard.js';
|
|
15
|
-
export function auditCommand(program) {
|
|
16
|
-
program
|
|
17
|
-
.command('audit')
|
|
18
|
-
.description('Audit a website for AI readiness')
|
|
19
|
-
.argument('<url>', 'URL to audit')
|
|
20
|
-
.option('-o, --output <format>', 'Output format: json, html, pdf, lhr, csv, interactive', 'interactive')
|
|
21
|
-
.option('-r, --rules <preset>', 'Rule preset: default, strict, minimal', 'default')
|
|
22
|
-
.option('-d, --depth <number>', 'Crawl depth (for multi-page audits)', parseInt, 1)
|
|
23
|
-
.option('-p, --pages <urls>', 'Comma-separated list of specific pages to audit')
|
|
24
|
-
.option('--cache-ttl <seconds>', 'Cache TTL in seconds to avoid re-fetching', parseInt)
|
|
25
|
-
.option('--threshold <score>', 'Minimum score threshold (exit 1 if below)', parseInt)
|
|
26
|
-
.option('--max-chunk-tokens <number>', 'Maximum tokens per content chunk', parseInt, 1200)
|
|
27
|
-
.option('--chunking-strategy <strategy>', 'Chunking strategy: auto, heading-based, paragraph-based', 'auto')
|
|
28
|
-
.option('--enable-chunking', 'Enable detailed content chunking analysis', false)
|
|
29
|
-
.option('--enable-extractability', 'Enable extractability mapping', false)
|
|
30
|
-
.option('--enable-hallucination', 'Enable hallucination detection', false)
|
|
31
|
-
.option('--enable-llm', 'Enable LLM comprehension analysis', false)
|
|
32
|
-
.option('--min-impact <number>', 'Minimum impact score to include', parseInt, 8)
|
|
33
|
-
.option('--min-confidence <number>', 'Minimum confidence to include (0-1)', parseFloat, 0.7)
|
|
34
|
-
.option('--max-issues <number>', 'Maximum issues to return', parseInt, 20)
|
|
35
|
-
.option('--llm-provider <provider>', 'LLM provider: openai, anthropic, ollama, local')
|
|
36
|
-
.option('--llm-model <model>', 'LLM model name')
|
|
37
|
-
.option('--llm-base-url <url>', 'LLM API base URL')
|
|
38
|
-
.option('--llm-api-key <key>', 'LLM API key')
|
|
39
|
-
.action(async (url, options) => {
|
|
40
|
-
// Detect if user wants wizard (no feature flags provided)
|
|
41
|
-
const hasFeatureFlags = options.enableChunking ||
|
|
42
|
-
options.enableExtractability ||
|
|
43
|
-
options.enableHallucination ||
|
|
44
|
-
options.enableLlm ||
|
|
45
|
-
options.llmProvider;
|
|
46
|
-
// If interactive mode and no feature flags, show wizard
|
|
47
|
-
if (options.output === 'interactive' && !hasFeatureFlags) {
|
|
48
|
-
const originalConsoleError = console.error;
|
|
49
|
-
const originalConsoleWarn = console.warn;
|
|
50
|
-
console.error = () => { };
|
|
51
|
-
console.warn = () => { };
|
|
52
|
-
let auditConfig = null;
|
|
53
|
-
// Show setup wizard
|
|
54
|
-
const wizardRender = render(React.createElement(SetupWizard, {
|
|
55
|
-
initialUrl: url,
|
|
56
|
-
onComplete: (config) => {
|
|
57
|
-
auditConfig = config;
|
|
58
|
-
},
|
|
59
|
-
}));
|
|
60
|
-
// Wait for wizard to complete
|
|
61
|
-
await wizardRender.waitUntilExit();
|
|
62
|
-
if (!auditConfig) {
|
|
63
|
-
console.error = originalConsoleError;
|
|
64
|
-
console.warn = originalConsoleWarn;
|
|
65
|
-
console.log('\nAudit cancelled.');
|
|
66
|
-
process.exit(0);
|
|
67
|
-
}
|
|
68
|
-
// Continue with audit using wizard config
|
|
69
|
-
url = auditConfig.url;
|
|
70
|
-
options.enableChunking = auditConfig.enableChunking;
|
|
71
|
-
options.enableExtractability = auditConfig.enableExtractability;
|
|
72
|
-
options.enableHallucination = auditConfig.enableHallucination;
|
|
73
|
-
options.enableLlm = auditConfig.enableLlm;
|
|
74
|
-
options.llmProvider = auditConfig.llmProvider;
|
|
75
|
-
options.llmModel = auditConfig.llmModel;
|
|
76
|
-
options.llmApiKey = auditConfig.llmApiKey;
|
|
77
|
-
options.llmBaseUrl = auditConfig.llmBaseUrl;
|
|
78
|
-
}
|
|
79
|
-
// Check if interactive mode
|
|
80
|
-
if (options.output === 'interactive') {
|
|
81
|
-
// Suppress console.error and console.warn in interactive mode for cleaner UI
|
|
82
|
-
const originalConsoleError = console.error;
|
|
83
|
-
const originalConsoleWarn = console.warn;
|
|
84
|
-
console.error = () => { }; // Suppress all console.error calls
|
|
85
|
-
console.warn = () => { }; // Suppress all console.warn calls
|
|
86
|
-
// Show loading UI
|
|
87
|
-
const { waitUntilExit, clear, rerender } = render(React.createElement(AuditReportUI, {
|
|
88
|
-
url,
|
|
89
|
-
result: {},
|
|
90
|
-
aiReadiness: {},
|
|
91
|
-
loading: true,
|
|
92
|
-
currentStep: 'Starting audit...',
|
|
93
|
-
}));
|
|
94
|
-
try {
|
|
95
|
-
// Validate URL
|
|
96
|
-
const urlObj = new URL(url);
|
|
97
|
-
// Build scan options
|
|
98
|
-
const scanOptions = {
|
|
99
|
-
maxChunkTokens: options.maxChunkTokens,
|
|
100
|
-
chunkingStrategy: options.chunkingStrategy,
|
|
101
|
-
enableChunking: options.enableChunking,
|
|
102
|
-
enableExtractability: options.enableExtractability,
|
|
103
|
-
enableHallucinationDetection: options.enableHallucination,
|
|
104
|
-
enableLLM: options.enableLlm,
|
|
105
|
-
minImpactScore: options.minImpact,
|
|
106
|
-
minConfidence: options.minConfidence,
|
|
107
|
-
maxIssues: options.maxIssues,
|
|
108
|
-
};
|
|
109
|
-
// Configure LLM if enabled
|
|
110
|
-
if (options.enableLlm && options.llmProvider) {
|
|
111
|
-
scanOptions.llmConfig = {
|
|
112
|
-
provider: options.llmProvider,
|
|
113
|
-
model: options.llmModel,
|
|
114
|
-
baseUrl: options.llmBaseUrl,
|
|
115
|
-
apiKey: options.llmApiKey,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
// Update loading step
|
|
119
|
-
rerender(React.createElement(AuditReportUI, {
|
|
120
|
-
url: urlObj.href,
|
|
121
|
-
result: {},
|
|
122
|
-
aiReadiness: {},
|
|
123
|
-
loading: true,
|
|
124
|
-
currentStep: 'Scanning page...',
|
|
125
|
-
}));
|
|
126
|
-
// Run the audit
|
|
127
|
-
const result = await analyzeUrlWithRules(url, scanOptions);
|
|
128
|
-
// Update loading step
|
|
129
|
-
rerender(React.createElement(AuditReportUI, {
|
|
130
|
-
url: urlObj.href,
|
|
131
|
-
result,
|
|
132
|
-
aiReadiness: {},
|
|
133
|
-
loading: true,
|
|
134
|
-
currentStep: 'Calculating AI readiness scores...',
|
|
135
|
-
}));
|
|
136
|
-
// Calculate AI readiness
|
|
137
|
-
const aiReadiness = calculateAIReadiness(result);
|
|
138
|
-
// Clear loading and show results
|
|
139
|
-
clear();
|
|
140
|
-
const finalRender = render(React.createElement(AuditReportUI, {
|
|
141
|
-
url: urlObj.href,
|
|
142
|
-
result,
|
|
143
|
-
aiReadiness,
|
|
144
|
-
loading: false,
|
|
145
|
-
}));
|
|
146
|
-
// Wait for user to exit
|
|
147
|
-
await finalRender.waitUntilExit();
|
|
148
|
-
// Restore console methods
|
|
149
|
-
console.error = originalConsoleError;
|
|
150
|
-
console.warn = originalConsoleWarn;
|
|
151
|
-
// Check threshold
|
|
152
|
-
if (options.threshold !== undefined) {
|
|
153
|
-
const overallScore = aiReadiness.overall;
|
|
154
|
-
if (overallScore !== undefined && overallScore < options.threshold) {
|
|
155
|
-
process.exit(1);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
// Restore console methods before showing error
|
|
161
|
-
console.error = originalConsoleError;
|
|
162
|
-
console.warn = originalConsoleWarn;
|
|
163
|
-
clear();
|
|
164
|
-
// Show error in UI format instead of console.error
|
|
165
|
-
console.log('\n' + chalk.bold.red('โ Audit Failed'));
|
|
166
|
-
console.log(chalk.red('โ'.repeat(70)));
|
|
167
|
-
console.log(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
168
|
-
console.log('\n' + chalk.dim('Please check the URL and your configuration.'));
|
|
169
|
-
process.exit(1);
|
|
170
|
-
}
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
// Non-interactive mode - original logic
|
|
174
|
-
const spinner = ora('Starting audit...').start();
|
|
175
|
-
try {
|
|
176
|
-
// Validate URL
|
|
177
|
-
const urlObj = new URL(url);
|
|
178
|
-
spinner.text = `Auditing ${chalk.cyan(urlObj.href)}...`;
|
|
179
|
-
// Build scan options
|
|
180
|
-
const scanOptions = {
|
|
181
|
-
maxChunkTokens: options.maxChunkTokens,
|
|
182
|
-
chunkingStrategy: options.chunkingStrategy,
|
|
183
|
-
enableChunking: options.enableChunking,
|
|
184
|
-
enableExtractability: options.enableExtractability,
|
|
185
|
-
enableHallucinationDetection: options.enableHallucination,
|
|
186
|
-
enableLLM: options.enableLlm,
|
|
187
|
-
minImpactScore: options.minImpact,
|
|
188
|
-
minConfidence: options.minConfidence,
|
|
189
|
-
maxIssues: options.maxIssues,
|
|
190
|
-
};
|
|
191
|
-
// Configure LLM if enabled
|
|
192
|
-
if (options.enableLlm && options.llmProvider) {
|
|
193
|
-
scanOptions.llmConfig = {
|
|
194
|
-
provider: options.llmProvider,
|
|
195
|
-
model: options.llmModel,
|
|
196
|
-
baseUrl: options.llmBaseUrl,
|
|
197
|
-
apiKey: options.llmApiKey,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
// Run the audit
|
|
201
|
-
spinner.text = 'Scanning page...';
|
|
202
|
-
const result = await analyzeUrlWithRules(url, scanOptions);
|
|
203
|
-
spinner.text = 'Calculating scores...';
|
|
204
|
-
const aiReadiness = calculateAIReadiness(result);
|
|
205
|
-
// Format the report
|
|
206
|
-
spinner.text = 'Generating report...';
|
|
207
|
-
const auditReportJson = exportAuditReport(result);
|
|
208
|
-
const auditReport = JSON.parse(auditReportJson);
|
|
209
|
-
// Save results
|
|
210
|
-
const outputDir = resolve(process.cwd(), '.ai-lighthouse');
|
|
211
|
-
if (!existsSync(outputDir)) {
|
|
212
|
-
await mkdir(outputDir, { recursive: true });
|
|
213
|
-
}
|
|
214
|
-
const timestamp = new Date().toISOString().replace(/:/g, '-').split('.')[0];
|
|
215
|
-
const baseFilename = `audit_${new URL(url).hostname}_${timestamp}`;
|
|
216
|
-
// Handle different output formats
|
|
217
|
-
if (options.output === 'json') {
|
|
218
|
-
const jsonPath = join(outputDir, `${baseFilename}.json`);
|
|
219
|
-
await writeFile(jsonPath, auditReportJson);
|
|
220
|
-
spinner.succeed(chalk.green('Audit complete!'));
|
|
221
|
-
console.log(chalk.dim(`Report saved to: ${jsonPath}`));
|
|
222
|
-
}
|
|
223
|
-
else if (options.output === 'html') {
|
|
224
|
-
const htmlPath = join(outputDir, `${baseFilename}.html`);
|
|
225
|
-
const html = generateHTMLReport(auditReport, aiReadiness, result);
|
|
226
|
-
await writeFile(htmlPath, html);
|
|
227
|
-
spinner.succeed(chalk.green('Audit complete!'));
|
|
228
|
-
console.log(chalk.dim(`HTML report saved to: ${htmlPath}`));
|
|
229
|
-
console.log(chalk.dim(`๐ก Tip: Open the HTML file and use your browser's "Print > Save as PDF" to export as PDF`));
|
|
230
|
-
}
|
|
231
|
-
else if (options.output === 'pdf') {
|
|
232
|
-
spinner.text = 'Generating PDF...';
|
|
233
|
-
const pdfPath = join(outputDir, `${baseFilename}.pdf`);
|
|
234
|
-
const html = generateHTMLReport(auditReport, aiReadiness, result);
|
|
235
|
-
const file = { content: html };
|
|
236
|
-
const pdfOptions = {
|
|
237
|
-
format: 'A4',
|
|
238
|
-
printBackground: false,
|
|
239
|
-
};
|
|
240
|
-
const pdfBuffer = await html_to_pdf.generatePdf(file, pdfOptions);
|
|
241
|
-
await writeFile(pdfPath, pdfBuffer);
|
|
242
|
-
spinner.succeed(chalk.green('PDF generated!'));
|
|
243
|
-
console.log(chalk.dim(`PDF report saved to: ${pdfPath}`));
|
|
244
|
-
}
|
|
245
|
-
else if (options.output === 'lhr') {
|
|
246
|
-
const lhrPath = join(outputDir, `${baseFilename}.lhr.json`);
|
|
247
|
-
const lhr = convertToLighthouseFormat(auditReport);
|
|
248
|
-
await writeFile(lhrPath, JSON.stringify(lhr, null, 2));
|
|
249
|
-
spinner.succeed(chalk.green('Audit complete!'));
|
|
250
|
-
console.log(chalk.dim(`Lighthouse-compatible report saved to: ${lhrPath}`));
|
|
251
|
-
}
|
|
252
|
-
else if (options.output === 'csv') {
|
|
253
|
-
const csvPath = join(outputDir, `${baseFilename}.csv`);
|
|
254
|
-
const csv = generateCSVReport(auditReport);
|
|
255
|
-
await writeFile(csvPath, csv);
|
|
256
|
-
spinner.succeed(chalk.green('Audit complete!'));
|
|
257
|
-
console.log(chalk.dim(`CSV report saved to: ${csvPath}`));
|
|
258
|
-
}
|
|
259
|
-
// Display summary
|
|
260
|
-
console.log('\n' + chalk.bold('๐ AI Readiness Summary'));
|
|
261
|
-
console.log(formatAIReadinessReport(aiReadiness));
|
|
262
|
-
console.log('\n' + chalk.bold('๐ Technical Scores'));
|
|
263
|
-
console.log(generateScoringSummary(result.scoring));
|
|
264
|
-
// Display comprehensive report with all website data
|
|
265
|
-
console.log('\n' + chalk.bold('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
266
|
-
console.log(chalk.bold.cyan(' COMPREHENSIVE ANALYSIS '));
|
|
267
|
-
console.log(chalk.bold('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
268
|
-
console.log(formatComprehensiveReport(result, aiReadiness));
|
|
269
|
-
// Display all issues
|
|
270
|
-
if (result.issues && result.issues.length > 0) {
|
|
271
|
-
console.log('\n' + formatDetailedIssues(result.issues));
|
|
272
|
-
}
|
|
273
|
-
// Check threshold
|
|
274
|
-
if (options.threshold !== undefined) {
|
|
275
|
-
const overallScore = auditReport?.scores?.overall;
|
|
276
|
-
if (overallScore !== undefined && overallScore < options.threshold) {
|
|
277
|
-
console.log(chalk.red(`\nโ Score ${overallScore} is below threshold ${options.threshold}`));
|
|
278
|
-
process.exit(1);
|
|
279
|
-
}
|
|
280
|
-
else if (overallScore !== undefined) {
|
|
281
|
-
console.log(chalk.green(`\nโ
Score ${overallScore} meets threshold ${options.threshold}`));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
catch (error) {
|
|
286
|
-
spinner.fail(chalk.red('Audit failed'));
|
|
287
|
-
if (error instanceof Error) {
|
|
288
|
-
console.error(chalk.red(error.message));
|
|
289
|
-
}
|
|
290
|
-
process.exit(1);
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
function generateHTMLReport(report, aiReadiness, scanResult) {
|
|
295
|
-
return `<!DOCTYPE html>
|
|
296
|
-
<html lang="en">
|
|
297
|
-
<head>
|
|
298
|
-
<meta charset="UTF-8">
|
|
299
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
300
|
-
<title>AI Lighthouse Report - ${report.input?.requested_url}</title>
|
|
301
|
-
<style>
|
|
302
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
303
|
-
body {
|
|
304
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
305
|
-
line-height: 1.6;
|
|
306
|
-
color: #333;
|
|
307
|
-
background: #f5f5f5;
|
|
308
|
-
padding: 20px;
|
|
309
|
-
}
|
|
310
|
-
.container { color: #333; max-width: 1200px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; }
|
|
311
|
-
h1 { color: #2563eb; margin-bottom: 10px; font-size: 2em; }
|
|
312
|
-
h2 { color: #1e40af; margin-top: 30px; margin-bottom: 15px; border-bottom: 2px solid #dbeafe; padding-bottom: 10px; }
|
|
313
|
-
h3 { color: #1e40af; margin-top: 20px; margin-bottom: 10px; font-size: 1.2em; }
|
|
314
|
-
.header { margin-bottom: 30px; }
|
|
315
|
-
.url { color: #64748b; font-size: 0.95em; }
|
|
316
|
-
.timestamp { color: #94a3b8; font-size: 0.85em; }
|
|
317
|
-
|
|
318
|
-
.ai-readiness-banner {
|
|
319
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
320
|
-
color: white;
|
|
321
|
-
padding: 30px;
|
|
322
|
-
border-radius: 8px;
|
|
323
|
-
margin: 20px 0;
|
|
324
|
-
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
325
|
-
}
|
|
326
|
-
.ai-readiness-banner h2 { color: white; border: none; margin: 0 0 20px 0; }
|
|
327
|
-
.overall-score { font-size: 3em; font-weight: bold; margin: 10px 0; }
|
|
328
|
-
.grade-badge { background: rgba(255,255,255,0.2); padding: 8px 16px; border-radius: 20px; display: inline-block; margin: 10px 0; }
|
|
329
|
-
.agent-perspective { background: rgba(255,255,255,0.1); padding: 20px; border-radius: 8px; margin: 20px 0; }
|
|
330
|
-
.agent-status { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 15px 0; }
|
|
331
|
-
.agent-status-item { display: flex; align-items: center; gap: 10px; }
|
|
332
|
-
|
|
333
|
-
.scores { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; }
|
|
334
|
-
.score-card {
|
|
335
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
336
|
-
color: white;
|
|
337
|
-
padding: 20px;
|
|
338
|
-
border-radius: 8px;
|
|
339
|
-
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
340
|
-
}
|
|
341
|
-
.score-card h3 { font-size: 0.9em; opacity: 0.9; margin-bottom: 10px; }
|
|
342
|
-
.score-value { font-size: 2.5em; font-weight: bold; }
|
|
343
|
-
.grade { font-size: 1.2em; opacity: 0.8; margin-left: 10px; }
|
|
344
|
-
|
|
345
|
-
.dimensions { display: grid; gap: 15px; margin: 20px 0; }
|
|
346
|
-
.dimension { background: #f8fafc; border-left: 4px solid #cbd5e1; padding: 20px; border-radius: 4px; }
|
|
347
|
-
.dimension.excellent { border-left-color: #10b981; }
|
|
348
|
-
.dimension.good { border-left-color: #84cc16; }
|
|
349
|
-
.dimension.needs-work { border-left-color: #f59e0b; }
|
|
350
|
-
.dimension.critical { border-left-color: #dc2626; }
|
|
351
|
-
.dimension-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
|
|
352
|
-
.dimension-name { font-weight: 600; font-size: 1.1em; color: #1e293b; }
|
|
353
|
-
.dimension-score { font-size: 1.5em; font-weight: bold; }
|
|
354
|
-
.dimension-level { color: #64748b; font-size: 0.9em; margin-bottom: 10px; text-transform: uppercase; }
|
|
355
|
-
.dimension-recommendations { margin-top: 10px; padding-left: 20px; }
|
|
356
|
-
.dimension-recommendations li { margin: 5px 0; color: #475569; }
|
|
357
|
-
|
|
358
|
-
.quick-wins { background: #fffbeb; border: 2px solid #fbbf24; padding: 20px; border-radius: 8px; margin: 20px 0; }
|
|
359
|
-
.quick-wins h3 { color: #92400e; margin-top: 0; }
|
|
360
|
-
.quick-win { background: white; padding: 15px; margin: 10px 0; border-radius: 4px; border-left: 3px solid #f59e0b; }
|
|
361
|
-
.quick-win-header { font-weight: 600; color: #1e293b; margin-bottom: 8px; }
|
|
362
|
-
.quick-win-meta { font-size: 0.85em; color: #64748b; }
|
|
363
|
-
|
|
364
|
-
.priorities { margin: 20px 0; }
|
|
365
|
-
.priority-section { background: #f8fafc; padding: 20px; border-radius: 8px; margin: 15px 0; }
|
|
366
|
-
.priority-section.immediate { border-left: 4px solid #dc2626; }
|
|
367
|
-
.priority-section.short-term { border-left: 4px solid #f59e0b; }
|
|
368
|
-
.priority-section.long-term { border-left: 4px solid #3b82f6; }
|
|
369
|
-
.priority-item { margin: 10px 0; padding-left: 20px; }
|
|
370
|
-
|
|
371
|
-
.issues { margin: 20px 0; }
|
|
372
|
-
.issue {
|
|
373
|
-
background: #f8fafc;
|
|
374
|
-
border-left: 4px solid #cbd5e1;
|
|
375
|
-
padding: 15px;
|
|
376
|
-
margin-bottom: 15px;
|
|
377
|
-
border-radius: 4px;
|
|
378
|
-
}
|
|
379
|
-
.issue.critical { border-left-color: #dc2626; background: #fef2f2; }
|
|
380
|
-
.issue.high { border-left-color: #ea580c; background: #fff7ed; }
|
|
381
|
-
.issue.medium { border-left-color: #f59e0b; background: #fffbeb; }
|
|
382
|
-
.issue.low { border-left-color: #84cc16; background: #f7fee7; }
|
|
383
|
-
.issue-title { font-weight: 600; color: #1e293b; margin-bottom: 8px; }
|
|
384
|
-
.issue-meta { font-size: 0.85em; color: #64748b; margin-bottom: 8px; }
|
|
385
|
-
.issue-desc { color: #475569; margin-bottom: 8px; }
|
|
386
|
-
.issue-fix { color: #0f766e; background: #f0fdfa; padding: 10px; border-radius: 4px; font-size: 0.9em; }
|
|
387
|
-
|
|
388
|
-
.entity-list { display: grid; gap: 15px; }
|
|
389
|
-
.entity { background: #f0f9ff; border: 1px solid #bae6fd; padding: 15px; border-radius: 4px; }
|
|
390
|
-
.entity-name { font-weight: 600; color: #0c4a6e; }
|
|
391
|
-
.entity-type { color: #0369a1; font-size: 0.85em; }
|
|
392
|
-
|
|
393
|
-
.print-button {
|
|
394
|
-
background: #2563eb;
|
|
395
|
-
color: white;
|
|
396
|
-
padding: 12px 24px;
|
|
397
|
-
border: none;
|
|
398
|
-
border-radius: 6px;
|
|
399
|
-
cursor: pointer;
|
|
400
|
-
font-size: 1em;
|
|
401
|
-
margin: 20px 0;
|
|
402
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
403
|
-
}
|
|
404
|
-
.print-button:hover { background: #1e40af; }
|
|
405
|
-
|
|
406
|
-
@media print {
|
|
407
|
-
body { background: white; padding: 0; }
|
|
408
|
-
.container { box-shadow: none; }
|
|
409
|
-
.print-button { display: none; }
|
|
410
|
-
}
|
|
411
|
-
</style>
|
|
412
|
-
</head>
|
|
413
|
-
<body>
|
|
414
|
-
<div class="container">
|
|
415
|
-
<div class="header">
|
|
416
|
-
<h1>๐จ AI Lighthouse Report</h1>
|
|
417
|
-
<div class="url">${report.input?.requested_url}</div>
|
|
418
|
-
<div class="timestamp">Generated: ${new Date(report.scanned_at).toLocaleString()}</div>
|
|
419
|
-
</div>
|
|
420
|
-
|
|
421
|
-
${aiReadiness ? `
|
|
422
|
-
<div class="ai-readiness-banner">
|
|
423
|
-
<h2>๐ค AI Readiness Assessment</h2>
|
|
424
|
-
<div class="overall-score">${Math.round(aiReadiness.overall)}/100</div>
|
|
425
|
-
<div class="grade-badge">Grade: ${aiReadiness.grade}</div>
|
|
426
|
-
<div style="margin: 10px 0; opacity: 0.9;">
|
|
427
|
-
Top ${aiReadiness.benchmark?.topPercentile || 0}% of sites โข
|
|
428
|
-
Improvement potential: +${Math.round(aiReadiness.benchmark?.improvement || 0)} points to best-in-class
|
|
429
|
-
</div>
|
|
430
|
-
|
|
431
|
-
<div class="agent-perspective">
|
|
432
|
-
<strong>AI Agent Perspective:</strong>
|
|
433
|
-
<div class="agent-status">
|
|
434
|
-
<div class="agent-status-item">
|
|
435
|
-
<span>${aiReadiness.aiPerspective?.canUnderstand ? 'โ
' : 'โ'}</span>
|
|
436
|
-
<span>Can Understand</span>
|
|
437
|
-
</div>
|
|
438
|
-
<div class="agent-status-item">
|
|
439
|
-
<span>${aiReadiness.aiPerspective?.canExtract ? 'โ
' : 'โ'}</span>
|
|
440
|
-
<span>Can Extract</span>
|
|
441
|
-
</div>
|
|
442
|
-
<div class="agent-status-item">
|
|
443
|
-
<span>${aiReadiness.aiPerspective?.canIndex ? 'โ
' : 'โ'}</span>
|
|
444
|
-
<span>Can Index</span>
|
|
445
|
-
</div>
|
|
446
|
-
<div class="agent-status-item">
|
|
447
|
-
<span>${aiReadiness.aiPerspective?.canAnswer ? 'โ
' : 'โ'}</span>
|
|
448
|
-
<span>Can Answer Questions</span>
|
|
449
|
-
</div>
|
|
450
|
-
</div>
|
|
451
|
-
<div style="margin-top: 15px;">
|
|
452
|
-
<strong>Confidence Level:</strong> ${Math.round((aiReadiness.aiPerspective?.confidence || 0) * 100)}%
|
|
453
|
-
</div>
|
|
454
|
-
</div>
|
|
455
|
-
|
|
456
|
-
${(aiReadiness.aiPerspective?.mainBlockers || []).length > 0 ? `
|
|
457
|
-
<div style="margin-top: 20px;">
|
|
458
|
-
<strong>Main Blockers:</strong>
|
|
459
|
-
<ul style="margin: 10px 0; padding-left: 20px;">
|
|
460
|
-
${(aiReadiness.aiPerspective?.mainBlockers || []).map((blocker) => `<li>${blocker}</li>`).join('')}
|
|
461
|
-
</ul>
|
|
462
|
-
</div>
|
|
463
|
-
` : ''}
|
|
464
|
-
</div>
|
|
465
|
-
` : ''}
|
|
466
|
-
|
|
467
|
-
<h2>๐ Technical Scores</h2>
|
|
468
|
-
<div class="scores">
|
|
469
|
-
<div class="score-card">
|
|
470
|
-
<h3>Overall Score</h3>
|
|
471
|
-
<div>
|
|
472
|
-
<span class="score-value">${report.scores?.overall || 0}</span>
|
|
473
|
-
<span class="grade">${getLetterGrade(report.scores?.overall || 0)}</span>
|
|
474
|
-
</div>
|
|
475
|
-
</div>
|
|
476
|
-
<div class="score-card">
|
|
477
|
-
<h3>AI Readiness</h3>
|
|
478
|
-
<div class="score-value">${report.scores?.ai_readiness || 0}</div>
|
|
479
|
-
</div>
|
|
480
|
-
<div class="score-card">
|
|
481
|
-
<h3>Crawlability</h3>
|
|
482
|
-
<div class="score-value">${report.scores?.crawlability || 0}</div>
|
|
483
|
-
</div>
|
|
484
|
-
<div class="score-card">
|
|
485
|
-
<h3>Content Clarity</h3>
|
|
486
|
-
<div class="score-value">${report.scores?.content_clarity || 0}</div>
|
|
487
|
-
</div>
|
|
488
|
-
<div class="score-card">
|
|
489
|
-
<h3>Schema Coverage</h3>
|
|
490
|
-
<div class="score-value">${report.scores?.schema_coverage || 0}</div>
|
|
491
|
-
</div>
|
|
492
|
-
<div class="score-card">
|
|
493
|
-
<h3>Structure</h3>
|
|
494
|
-
<div class="score-value">${report.scores?.structure || 0}</div>
|
|
495
|
-
</div>
|
|
496
|
-
</div>
|
|
497
|
-
|
|
498
|
-
${aiReadiness?.dimensions ? `
|
|
499
|
-
<h2>๐ฏ Dimension Analysis</h2>
|
|
500
|
-
<div class="dimensions">
|
|
501
|
-
${Object.entries(aiReadiness.dimensions).map(([key, dim]) => `
|
|
502
|
-
<div class="dimension ${dim.status}">
|
|
503
|
-
<div class="dimension-header">
|
|
504
|
-
<div class="dimension-name">${getEmojiForDimension(key)} ${formatDimensionName(key)}</div>
|
|
505
|
-
<div class="dimension-score">${Math.round(dim.score)}/100</div>
|
|
506
|
-
</div>
|
|
507
|
-
<div class="dimension-level">${dim.status}</div>
|
|
508
|
-
${dim.strengths && dim.strengths.length > 0 ? `
|
|
509
|
-
<div><strong>Strengths:</strong> ${dim.strengths.join(', ')}</div>
|
|
510
|
-
` : ''}
|
|
511
|
-
${dim.weaknesses && dim.weaknesses.length > 0 ? `
|
|
512
|
-
<div style="margin-top: 8px;"><strong>Weaknesses:</strong> ${dim.weaknesses.join(', ')}</div>
|
|
513
|
-
` : ''}
|
|
514
|
-
${dim.recommendation ? `
|
|
515
|
-
<div class="dimension-recommendations">
|
|
516
|
-
<div style="margin-top: 10px;">โ ${dim.recommendation}</div>
|
|
517
|
-
</div>
|
|
518
|
-
` : ''}
|
|
519
|
-
</div>
|
|
520
|
-
`).join('')}
|
|
521
|
-
</div>
|
|
522
|
-
` : ''}
|
|
523
|
-
|
|
524
|
-
${aiReadiness?.quickWins && aiReadiness.quickWins.length > 0 ? `
|
|
525
|
-
<div class="quick-wins">
|
|
526
|
-
<h3>โก Quick Wins (High Impact, Low Effort)</h3>
|
|
527
|
-
${aiReadiness.quickWins.slice(0, 5).map((win, idx) => `
|
|
528
|
-
<div class="quick-win">
|
|
529
|
-
<div class="quick-win-header">${idx + 1}. ${win.issue}</div>
|
|
530
|
-
<div class="quick-win-meta">Impact: ${win.impact} | Effort: ${win.effort}</div>
|
|
531
|
-
<div style="margin-top: 8px; color: #0f766e;">โ ${win.fix}</div>
|
|
532
|
-
</div>
|
|
533
|
-
`).join('')}
|
|
534
|
-
</div>
|
|
535
|
-
` : ''}
|
|
536
|
-
|
|
537
|
-
${aiReadiness?.roadmap ? `
|
|
538
|
-
<h2>๐ Priority Roadmap</h2>
|
|
539
|
-
<div class="priorities">
|
|
540
|
-
${aiReadiness.roadmap.immediate && aiReadiness.roadmap.immediate.length > 0 ? `
|
|
541
|
-
<div class="priority-section immediate">
|
|
542
|
-
<h3>๐ด Immediate (< 1 day)</h3>
|
|
543
|
-
${aiReadiness.roadmap.immediate.slice(0, 5).map((item) => `
|
|
544
|
-
<div class="priority-item">โข ${item}</div>
|
|
545
|
-
`).join('')}
|
|
546
|
-
</div>
|
|
547
|
-
` : ''}
|
|
548
|
-
|
|
549
|
-
${aiReadiness.roadmap.shortTerm && aiReadiness.roadmap.shortTerm.length > 0 ? `
|
|
550
|
-
<div class="priority-section short-term">
|
|
551
|
-
<h3>๐ก Short-term (1-7 days)</h3>
|
|
552
|
-
${aiReadiness.roadmap.shortTerm.slice(0, 5).map((item) => `
|
|
553
|
-
<div class="priority-item">โข ${item}</div>
|
|
554
|
-
`).join('')}
|
|
555
|
-
</div>
|
|
556
|
-
` : ''}
|
|
557
|
-
|
|
558
|
-
${aiReadiness.roadmap.longTerm && aiReadiness.roadmap.longTerm.length > 0 ? `
|
|
559
|
-
<div class="priority-section long-term">
|
|
560
|
-
<h3>๐ต Long-term (> 7 days)</h3>
|
|
561
|
-
${aiReadiness.roadmap.longTerm.slice(0, 5).map((item) => `
|
|
562
|
-
<div class="priority-item">โข ${item}</div>
|
|
563
|
-
`).join('')}
|
|
564
|
-
</div>
|
|
565
|
-
` : ''}
|
|
566
|
-
</div>
|
|
567
|
-
` : ''}
|
|
568
|
-
|
|
569
|
-
${scanResult?.llm ? `
|
|
570
|
-
<h2>๐ AI Understanding Analysis</h2>
|
|
571
|
-
<div style="background: #f0f9ff; border: 2px solid #0ea5e9; padding: 20px; border-radius: 8px; margin: 20px 0;">
|
|
572
|
-
<div style="margin-bottom: 15px;">
|
|
573
|
-
<strong>Summary:</strong> ${scanResult.llm.summary || 'N/A'}
|
|
574
|
-
</div>
|
|
575
|
-
${scanResult.llm.pageType ? `
|
|
576
|
-
<div style="margin-bottom: 15px;">
|
|
577
|
-
<strong>๐ Page Type:</strong> ${scanResult.llm.pageType}
|
|
578
|
-
</div>
|
|
579
|
-
` : ''}
|
|
580
|
-
${scanResult.llm.keyTopics && scanResult.llm.keyTopics.length > 0 ? `
|
|
581
|
-
<div style="margin-bottom: 15px;">
|
|
582
|
-
<strong>๐ท๏ธ Key Topics:</strong> ${scanResult.llm.keyTopics.join(', ')}
|
|
583
|
-
</div>
|
|
584
|
-
` : ''}
|
|
585
|
-
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 15px;">
|
|
586
|
-
${scanResult.llm.readingLevel ? `
|
|
587
|
-
<div><strong>๐ Reading Level:</strong> ${scanResult.llm.readingLevel.description}</div>
|
|
588
|
-
` : ''}
|
|
589
|
-
${scanResult.llm.sentiment ? `
|
|
590
|
-
<div><strong>๐ญ Sentiment:</strong> ${scanResult.llm.sentiment}</div>
|
|
591
|
-
` : ''}
|
|
592
|
-
${scanResult.llm.technicalDepth ? `
|
|
593
|
-
<div><strong>๐ฏ Technical Depth:</strong> ${scanResult.llm.technicalDepth}</div>
|
|
594
|
-
` : ''}
|
|
595
|
-
</div>
|
|
596
|
-
|
|
597
|
-
${scanResult.llm.pageTypeInsights && scanResult.llm.pageTypeInsights.length > 0 ? `
|
|
598
|
-
<div style="margin-top: 20px; background: #dbeafe; padding: 15px; border-radius: 4px; border-left: 4px solid #0ea5e9;">
|
|
599
|
-
<div style="font-weight: 600; margin-bottom: 10px;">๐ก AI-Generated Insights for ${scanResult.llm.pageType || 'This Page'}</div>
|
|
600
|
-
<ul style="margin: 0; padding-left: 20px;">
|
|
601
|
-
${scanResult.llm.pageTypeInsights.map((insight) => `<li style="margin: 5px 0;">${insight}</li>`).join('')}
|
|
602
|
-
</ul>
|
|
603
|
-
</div>
|
|
604
|
-
` : ''}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
${scanResult.llm.topEntities && scanResult.llm.topEntities.length > 0 ? `
|
|
608
|
-
<div style="margin-top: 20px;">
|
|
609
|
-
<strong>๐ Key Entities:</strong>
|
|
610
|
-
<div style="margin-top: 10px; display: grid; gap: 10px;">
|
|
611
|
-
${scanResult.llm.topEntities.slice(0, 5).map((entity) => `
|
|
612
|
-
<div style="background: white; padding: 10px; border-radius: 4px; border-left: 3px solid #0ea5e9;">
|
|
613
|
-
<strong>${entity.name}</strong> (${entity.type}) - ${Math.round((entity.relevance || 0) * 100)}% relevance
|
|
614
|
-
</div>
|
|
615
|
-
`).join('')}
|
|
616
|
-
</div>
|
|
617
|
-
</div>
|
|
618
|
-
` : ''}
|
|
619
|
-
|
|
620
|
-
${scanResult.llm.questions && scanResult.llm.questions.length > 0 ? `
|
|
621
|
-
<div style="margin-top: 20px;">
|
|
622
|
-
<strong>โ Key Questions AI Can Answer:</strong>
|
|
623
|
-
<ol style="margin-top: 10px; padding-left: 25px;">
|
|
624
|
-
${scanResult.llm.questions.slice(0, 5).map((q) => `
|
|
625
|
-
<li style="margin: 8px 0;"><span style="text-transform: uppercase; font-size: 0.8em; background: #dbeafe; padding: 2px 6px; border-radius: 3px;">${q.difficulty}</span> ${q.question}</li>
|
|
626
|
-
`).join('')}
|
|
627
|
-
</ol>
|
|
628
|
-
</div>
|
|
629
|
-
` : ''}
|
|
630
|
-
|
|
631
|
-
${scanResult.llm.suggestedFAQ && scanResult.llm.suggestedFAQ.length > 0 ? `
|
|
632
|
-
<div style="margin-top: 20px;">
|
|
633
|
-
<strong>๐ก Suggested FAQs:</strong>
|
|
634
|
-
<div style="margin-top: 10px; display: grid; gap: 15px;">
|
|
635
|
-
${scanResult.llm.suggestedFAQ.filter((f) => f.importance === 'high').slice(0, 3).map((faq) => `
|
|
636
|
-
<div style="background: white; padding: 15px; border-radius: 4px; border-left: 3px solid #f59e0b;">
|
|
637
|
-
<div style="font-weight: 600; margin-bottom: 5px;">Q: ${faq.question}</div>
|
|
638
|
-
<div style="color: #64748b;">A: ${faq.suggestedAnswer}</div>
|
|
639
|
-
</div>
|
|
640
|
-
`).join('')}
|
|
641
|
-
</div>
|
|
642
|
-
</div>
|
|
643
|
-
` : ''}
|
|
644
|
-
</div>
|
|
645
|
-
` : ''}
|
|
646
|
-
|
|
647
|
-
${scanResult?.chunking ? `
|
|
648
|
-
<h2>๐ Content Chunking Analysis</h2>
|
|
649
|
-
<div style="background: #f0fdf4; border: 2px solid #10b981; padding: 20px; border-radius: 8px; margin: 20px 0;">
|
|
650
|
-
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
|
|
651
|
-
<div><strong>Strategy:</strong> ${scanResult.chunking.chunkingStrategy}</div>
|
|
652
|
-
<div><strong>Total Chunks:</strong> ${scanResult.chunking.totalChunks}</div>
|
|
653
|
-
<div><strong>Avg Tokens/Chunk:</strong> ${scanResult.chunking.averageTokensPerChunk}</div>
|
|
654
|
-
<div><strong>Avg Noise:</strong> ${(scanResult.chunking.averageNoiseRatio * 100).toFixed(1)}%</div>
|
|
655
|
-
</div>
|
|
656
|
-
</div>
|
|
657
|
-
` : ''}
|
|
658
|
-
|
|
659
|
-
${scanResult?.extractability ? `
|
|
660
|
-
<h2>๐ Extractability Analysis</h2>
|
|
661
|
-
<div style="background: #fef3c7; border: 2px solid #f59e0b; padding: 20px; border-radius: 8px; margin: 20px 0;">
|
|
662
|
-
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 15px;">
|
|
663
|
-
<div><strong>Overall Score:</strong> ${scanResult.extractability.score.extractabilityScore}/100</div>
|
|
664
|
-
<div><strong>Server-Rendered:</strong> ${scanResult.extractability.score.serverRenderedPercent}%</div>
|
|
665
|
-
</div>
|
|
666
|
-
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
|
|
667
|
-
<div><strong>Text Extractable:</strong> ${scanResult.extractability.contentTypes.text.percentage}%</div>
|
|
668
|
-
<div><strong>Images Extractable:</strong> ${scanResult.extractability.contentTypes.images.percentage}%</div>
|
|
669
|
-
</div>
|
|
670
|
-
</div>
|
|
671
|
-
` : ''}
|
|
672
|
-
|
|
673
|
-
${scanResult?.hallucinationReport ? `
|
|
674
|
-
<h2>โ ๏ธ Hallucination Risk Assessment</h2>
|
|
675
|
-
<div style="background: #fef2f2; border: 2px solid #ef4444; padding: 20px; border-radius: 8px; margin: 20px 0;">
|
|
676
|
-
<div style="font-size: 1.5em; font-weight: bold; margin-bottom: 15px;">
|
|
677
|
-
Risk Score: ${scanResult.hallucinationReport.hallucinationRiskScore}/100
|
|
678
|
-
</div>
|
|
679
|
-
|
|
680
|
-
${scanResult.hallucinationReport.factCheckSummary ? `
|
|
681
|
-
<div style="margin-top: 20px; background: white; padding: 15px; border-radius: 4px;">
|
|
682
|
-
<strong>๐ Fact Check Summary:</strong>
|
|
683
|
-
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-top: 10px;">
|
|
684
|
-
<div>โ
Verified: ${scanResult.hallucinationReport.factCheckSummary.verifiedFacts}</div>
|
|
685
|
-
<div>โ Unverified: ${scanResult.hallucinationReport.factCheckSummary.unverifiedFacts}</div>
|
|
686
|
-
<div>โ ๏ธ Contradictions: ${scanResult.hallucinationReport.factCheckSummary.contradictions}</div>
|
|
687
|
-
<div>๐ค Ambiguities: ${scanResult.hallucinationReport.factCheckSummary.ambiguities}</div>
|
|
688
|
-
</div>
|
|
689
|
-
</div>
|
|
690
|
-
` : ''}
|
|
691
|
-
|
|
692
|
-
${scanResult.hallucinationReport.factCheckSummary && scanResult.hallucinationReport.factCheckSummary.unverifiedFacts > 0 ? `
|
|
693
|
-
<div style="margin-top: 15px; background: #fef9c3; border-left: 4px solid #eab308; padding: 15px; border-radius: 4px;">
|
|
694
|
-
<div style="display: flex; align-items: start; gap: 10px;">
|
|
695
|
-
<span style="font-size: 1.5em;">๐ก</span>
|
|
696
|
-
<div>
|
|
697
|
-
<div style="font-weight: 600; margin-bottom: 8px;">Why Some Facts Can't Be Verified</div>
|
|
698
|
-
<div style="color: #854d0e; margin-bottom: 8px;">
|
|
699
|
-
AI systems need external sources to verify claims. When information lacks citations, links, or
|
|
700
|
-
references, it becomes difficult to confirm accuracy, increasing the risk of AI hallucination or misinformation.
|
|
701
|
-
</div>
|
|
702
|
-
<div style="font-weight: 600; margin-top: 12px; margin-bottom: 6px;">Best Practices:</div>
|
|
703
|
-
<ul style="margin: 0; padding-left: 20px; color: #854d0e;">
|
|
704
|
-
<li>Add source links directly in or near claim text</li>
|
|
705
|
-
<li>Include dates for time-sensitive information</li>
|
|
706
|
-
<li>Link to authoritative sources (research, official docs)</li>
|
|
707
|
-
<li>Provide context for statistics and data points</li>
|
|
708
|
-
<li>Use schema.org markup to specify citations</li>
|
|
709
|
-
</ul>
|
|
710
|
-
</div>
|
|
711
|
-
</div>
|
|
712
|
-
</div>
|
|
713
|
-
` : ''}
|
|
714
|
-
|
|
715
|
-
${scanResult.hallucinationReport.triggers && scanResult.hallucinationReport.triggers.length > 0 ? `
|
|
716
|
-
<div style="margin-top: 20px;">
|
|
717
|
-
<strong>๐จ Identified Triggers:</strong>
|
|
718
|
-
<div style="margin-top: 10px; display: grid; gap: 10px;">
|
|
719
|
-
${scanResult.hallucinationReport.triggers.filter((t) => t.severity === 'high' || t.severity === 'critical').slice(0, 5).map((trigger) => `
|
|
720
|
-
<div style="background: white; padding: 15px; border-radius: 4px; border-left: 3px solid #dc2626;">
|
|
721
|
-
<div style="font-weight: 600; text-transform: uppercase; font-size: 0.85em; color: #dc2626;">${trigger.type} - ${trigger.severity}</div>
|
|
722
|
-
<div style="margin-top: 5px;">${trigger.description}</div>
|
|
723
|
-
<div style="margin-top: 5px; font-size: 0.9em; color: #64748b;">Confidence: ${Math.round((trigger.confidence || 0) * 100)}%</div>
|
|
724
|
-
</div>
|
|
725
|
-
`).join('')}
|
|
726
|
-
</div>
|
|
727
|
-
</div>
|
|
728
|
-
` : ''}
|
|
729
|
-
|
|
730
|
-
${scanResult.hallucinationReport.recommendations && scanResult.hallucinationReport.recommendations.length > 0 ? `
|
|
731
|
-
<div style="margin-top: 20px;">
|
|
732
|
-
<strong>๐ก Recommendations:</strong>
|
|
733
|
-
<ul style="margin-top: 10px; padding-left: 25px;">
|
|
734
|
-
${scanResult.hallucinationReport.recommendations.slice(0, 3).map((rec) => `<li style="margin: 5px 0;">${rec}</li>`).join('')}
|
|
735
|
-
</ul>
|
|
736
|
-
</div>
|
|
737
|
-
` : ''}
|
|
738
|
-
</div>
|
|
739
|
-
` : ''}
|
|
740
|
-
|
|
741
|
-
${scanResult?.mirrorReport ? `
|
|
742
|
-
<h2>๐ AI Misunderstanding Check</h2>
|
|
743
|
-
<div style="background: #faf5ff; border: 2px solid #a855f7; padding: 20px; border-radius: 8px; margin: 20px 0;">
|
|
744
|
-
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 15px;">
|
|
745
|
-
<div><strong>๐ฏ Alignment Score:</strong> ${scanResult.mirrorReport.summary.alignmentScore}/100</div>
|
|
746
|
-
<div><strong>๐ Clarity Score:</strong> ${scanResult.mirrorReport.summary.clarityScore}/100</div>
|
|
747
|
-
<div><strong>โ ๏ธ Critical Issues:</strong> ${scanResult.mirrorReport.summary.critical}</div>
|
|
748
|
-
<div><strong>๐ก Major Issues:</strong> ${scanResult.mirrorReport.summary.major}</div>
|
|
749
|
-
</div>
|
|
750
|
-
|
|
751
|
-
${scanResult.mirrorReport.llmInterpretation ? `
|
|
752
|
-
<div style="background: #dbeafe; padding: 16px; border-radius: 8px; margin-bottom: 16px; border: 1px solid #3b82f6;">
|
|
753
|
-
<div style="font-weight: bold; margin-bottom: 12px; color: #1e40af;">
|
|
754
|
-
๐ค What AI Actually Understood (${Math.round(scanResult.mirrorReport.llmInterpretation.confidence * 100)}% confident)
|
|
755
|
-
</div>
|
|
756
|
-
|
|
757
|
-
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
|
|
758
|
-
${scanResult.mirrorReport.llmInterpretation.productName ? `
|
|
759
|
-
<div>
|
|
760
|
-
<div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">PRODUCT NAME</div>
|
|
761
|
-
<div style="font-size: 14px;">${scanResult.mirrorReport.llmInterpretation.productName}</div>
|
|
762
|
-
</div>
|
|
763
|
-
` : ''}
|
|
764
|
-
|
|
765
|
-
${scanResult.mirrorReport.llmInterpretation.purpose ? `
|
|
766
|
-
<div style="grid-column: 1 / -1;">
|
|
767
|
-
<div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">MAIN PURPOSE</div>
|
|
768
|
-
<div style="font-size: 14px;">${scanResult.mirrorReport.llmInterpretation.purpose}</div>
|
|
769
|
-
</div>
|
|
770
|
-
` : ''}
|
|
771
|
-
|
|
772
|
-
${scanResult.mirrorReport.llmInterpretation.valueProposition ? `
|
|
773
|
-
<div style="grid-column: 1 / -1;">
|
|
774
|
-
<div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">๐ UNIQUE VALUE</div>
|
|
775
|
-
<div style="font-size: 14px; font-weight: 600; color: #7c3aed;">${scanResult.mirrorReport.llmInterpretation.valueProposition}</div>
|
|
776
|
-
</div>
|
|
777
|
-
` : ''}
|
|
778
|
-
|
|
779
|
-
${scanResult.mirrorReport.llmInterpretation.keyBenefits && scanResult.mirrorReport.llmInterpretation.keyBenefits.length > 0 ? `
|
|
780
|
-
<div>
|
|
781
|
-
<div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">KEY BENEFITS</div>
|
|
782
|
-
<ul style="font-size: 14px; margin: 0; padding-left: 20px;">
|
|
783
|
-
${scanResult.mirrorReport.llmInterpretation.keyBenefits.map((b) => `<li style="margin: 4px 0;">${b}</li>`).join('')}
|
|
784
|
-
</ul>
|
|
785
|
-
</div>
|
|
786
|
-
` : ''}
|
|
787
|
-
|
|
788
|
-
${scanResult.mirrorReport.llmInterpretation.keyFeatures && scanResult.mirrorReport.llmInterpretation.keyFeatures.length > 0 ? `
|
|
789
|
-
<div>
|
|
790
|
-
<div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">KEY FEATURES</div>
|
|
791
|
-
<ul style="font-size: 14px; margin: 0; padding-left: 20px;">
|
|
792
|
-
${scanResult.mirrorReport.llmInterpretation.keyFeatures.slice(0, 3).map((f) => `<li style="margin: 4px 0;">${f}</li>`).join('')}
|
|
793
|
-
</ul>
|
|
794
|
-
</div>
|
|
795
|
-
` : ''}
|
|
796
|
-
</div>
|
|
797
|
-
</div>
|
|
798
|
-
` : ''}
|
|
799
|
-
|
|
800
|
-
${scanResult.mirrorReport.mismatches && scanResult.mirrorReport.mismatches.length > 0 ? `
|
|
801
|
-
<div style="margin-top: 20px;">
|
|
802
|
-
<strong>Priority Mismatches:</strong>
|
|
803
|
-
<div style="margin-top: 10px; display: grid; gap: 10px;">
|
|
804
|
-
${scanResult.mirrorReport.mismatches.filter((m) => m.severity === 'critical' || m.severity === 'major').slice(0, 5).map((mismatch) => `
|
|
805
|
-
<div style="background: white; padding: 15px; border-radius: 4px; border-left: 3px solid ${mismatch.severity === 'critical' ? '#dc2626' : '#f59e0b'};">
|
|
806
|
-
<div style="font-weight: 600;">${mismatch.severity === 'critical' ? '๐ด' : '๐ก'} ${mismatch.field}</div>
|
|
807
|
-
<div style="margin-top: 5px; color: #64748b;">${mismatch.description}</div>
|
|
808
|
-
<div style="margin-top: 10px; padding: 10px; background: #f0fdfa; border-radius: 4px;">
|
|
809
|
-
<strong>Fix:</strong> ${mismatch.recommendation}
|
|
810
|
-
</div>
|
|
811
|
-
</div>
|
|
812
|
-
`).join('')}
|
|
813
|
-
</div>
|
|
814
|
-
</div>
|
|
815
|
-
` : ''}
|
|
816
|
-
|
|
817
|
-
${scanResult.mirrorReport.recommendations && scanResult.mirrorReport.recommendations.length > 0 ? `
|
|
818
|
-
<div style="margin-top: 20px;">
|
|
819
|
-
<strong>๐ก Top Recommendations:</strong>
|
|
820
|
-
<ul style="margin-top: 10px; padding-left: 25px;">
|
|
821
|
-
${scanResult.mirrorReport.recommendations.slice(0, 3).map((rec) => `<li style="margin: 5px 0;">${rec}</li>`).join('')}
|
|
822
|
-
</ul>
|
|
823
|
-
</div>
|
|
824
|
-
` : ''}
|
|
825
|
-
</div>
|
|
826
|
-
` : ''}
|
|
827
|
-
|
|
828
|
-
<h2>โ ๏ธ All Issues (${report.issues?.length || 0})</h2>
|
|
829
|
-
<div class="issues">
|
|
830
|
-
${(report.issues || []).map((issue) => `
|
|
831
|
-
<div class="issue ${issue.severity}">
|
|
832
|
-
<div class="issue-title">${issue.message}</div>
|
|
833
|
-
<div class="issue-meta">
|
|
834
|
-
<span style="text-transform: uppercase; font-weight: 600;">${issue.severity}</span>
|
|
835
|
-
ยท Impact: ${issue.impact}
|
|
836
|
-
ยท Category: ${issue.category}
|
|
837
|
-
</div>
|
|
838
|
-
<div class="issue-desc">${issue.evidence || 'No additional evidence'}</div>
|
|
839
|
-
<div class="issue-fix"><strong>Fix:</strong> ${issue.suggested_fix}</div>
|
|
840
|
-
</div>
|
|
841
|
-
`).join('')}
|
|
842
|
-
</div>
|
|
843
|
-
|
|
844
|
-
${(report.entities || []).length > 0 ? `
|
|
845
|
-
<h2>๐ท๏ธ Detected Entities (${(report.entities || []).length})</h2>
|
|
846
|
-
<div class="entity-list">
|
|
847
|
-
${(report.entities || []).map((entity) => `
|
|
848
|
-
<div class="entity">
|
|
849
|
-
<div class="entity-name">${entity.name}</div>
|
|
850
|
-
<div class="entity-type">${entity.type} ยท Source: ${entity.source}</div>
|
|
851
|
-
${entity.description ? `<div style="margin-top: 8px; color: #334155;">${entity.description}</div>` : ''}
|
|
852
|
-
</div>
|
|
853
|
-
`).join('')}
|
|
854
|
-
</div>
|
|
855
|
-
` : ''}
|
|
856
|
-
</div>
|
|
857
|
-
</body>
|
|
858
|
-
</html>`;
|
|
859
|
-
}
|
|
860
|
-
function getLetterGrade(score) {
|
|
861
|
-
if (score >= 90)
|
|
862
|
-
return 'A';
|
|
863
|
-
if (score >= 80)
|
|
864
|
-
return 'B';
|
|
865
|
-
if (score >= 70)
|
|
866
|
-
return 'C';
|
|
867
|
-
if (score >= 60)
|
|
868
|
-
return 'D';
|
|
869
|
-
return 'F';
|
|
870
|
-
}
|
|
871
|
-
function getEmojiForDimension(name) {
|
|
872
|
-
const emojiMap = {
|
|
873
|
-
'contentQuality': '๐',
|
|
874
|
-
'discoverability': '๐',
|
|
875
|
-
'extractability': '๐',
|
|
876
|
-
'comprehensibility': '๐ง ',
|
|
877
|
-
'trustworthiness': 'โ
',
|
|
878
|
-
'structured': '๐',
|
|
879
|
-
'semantic': '๐ท๏ธ'
|
|
880
|
-
};
|
|
881
|
-
return emojiMap[name] || '๐';
|
|
882
|
-
}
|
|
883
|
-
function formatDimensionName(key) {
|
|
884
|
-
const nameMap = {
|
|
885
|
-
'contentQuality': 'Content Quality',
|
|
886
|
-
'discoverability': 'Discoverability',
|
|
887
|
-
'extractability': 'Extractability',
|
|
888
|
-
'comprehensibility': 'Comprehensibility',
|
|
889
|
-
'trustworthiness': 'Trustworthiness'
|
|
890
|
-
};
|
|
891
|
-
return nameMap[key] || key;
|
|
892
|
-
}
|
|
893
|
-
function convertToLighthouseFormat(report) {
|
|
894
|
-
return {
|
|
895
|
-
lighthouseVersion: '1.0.0',
|
|
896
|
-
userAgent: 'AI-Lighthouse/1.0.0',
|
|
897
|
-
fetchTime: report.scanned_at,
|
|
898
|
-
requestedUrl: report.input?.requested_url,
|
|
899
|
-
finalUrl: report.input?.final_url,
|
|
900
|
-
categories: {
|
|
901
|
-
'ai-readiness': {
|
|
902
|
-
id: 'ai-readiness',
|
|
903
|
-
title: 'AI Readiness',
|
|
904
|
-
score: (report.scores?.ai_readiness || 0) / 100,
|
|
905
|
-
},
|
|
906
|
-
'crawlability': {
|
|
907
|
-
id: 'crawlability',
|
|
908
|
-
title: 'Crawlability',
|
|
909
|
-
score: (report.scores?.crawlability || 0) / 100,
|
|
910
|
-
},
|
|
911
|
-
'content-clarity': {
|
|
912
|
-
id: 'content-clarity',
|
|
913
|
-
title: 'Content Clarity',
|
|
914
|
-
score: (report.scores?.content_clarity || 0) / 100,
|
|
915
|
-
},
|
|
916
|
-
},
|
|
917
|
-
audits: (report.issues || []).reduce((acc, issue, idx) => {
|
|
918
|
-
acc[`issue-${idx}`] = {
|
|
919
|
-
id: issue.id,
|
|
920
|
-
title: issue.message,
|
|
921
|
-
description: issue.evidence || '',
|
|
922
|
-
score: issue.severity === 'critical' ? 0 : issue.severity === 'high' ? 0.25 : 0.5,
|
|
923
|
-
displayValue: issue.suggested_fix,
|
|
924
|
-
};
|
|
925
|
-
return acc;
|
|
926
|
-
}, {}),
|
|
927
|
-
};
|
|
928
|
-
}
|
|
929
|
-
function generateCSVReport(report) {
|
|
930
|
-
const headers = ['ID', 'Severity', 'Category', 'Message', 'Impact', 'Suggested Fix'];
|
|
931
|
-
const rows = (report.issues || []).map((issue) => [
|
|
932
|
-
issue.id,
|
|
933
|
-
issue.severity,
|
|
934
|
-
issue.category,
|
|
935
|
-
`"${issue.message.replace(/"/g, '""')}"`,
|
|
936
|
-
issue.impact,
|
|
937
|
-
`"${issue.suggested_fix.replace(/"/g, '""')}"`,
|
|
938
|
-
]);
|
|
939
|
-
return [headers.join(','), ...rows.map((r) => r.join(','))].join('\n');
|
|
940
|
-
}
|