@ai-lighthouse/cli 1.0.0 → 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/crawl.d.ts
DELETED
package/dist/commands/crawl.js
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import ora from 'ora';
|
|
3
|
-
import { analyzeUrlWithRules } from 'scanner';
|
|
4
|
-
import { writeFile, mkdir } from 'fs/promises';
|
|
5
|
-
import { join, resolve } from 'path';
|
|
6
|
-
import { existsSync } from 'fs';
|
|
7
|
-
export function crawlCommand(program) {
|
|
8
|
-
program
|
|
9
|
-
.command('crawl')
|
|
10
|
-
.description('Crawl and audit multiple pages from a website')
|
|
11
|
-
.argument('<url>', 'Starting URL to crawl from')
|
|
12
|
-
.option('-d, --depth <number>', 'Maximum crawl depth', (val) => parseInt(val, 10), 2)
|
|
13
|
-
.option('--sitemap', 'Parse sitemap.xml for URLs', false)
|
|
14
|
-
.option('--max-pages <number>', 'Maximum number of pages to crawl', (val) => parseInt(val, 10), 50)
|
|
15
|
-
.option('-o, --output <format>', 'Output format: json, html', 'json')
|
|
16
|
-
.option('--follow-external', 'Follow external links', false)
|
|
17
|
-
.option('--respect-robots', 'Respect robots.txt rules', true)
|
|
18
|
-
.action(async (url, options) => {
|
|
19
|
-
const spinner = ora('Starting crawl...').start();
|
|
20
|
-
try {
|
|
21
|
-
const urlObj = new URL(url);
|
|
22
|
-
const baseUrl = `${urlObj.protocol}//${urlObj.host}`;
|
|
23
|
-
// Discover URLs
|
|
24
|
-
spinner.text = 'Discovering URLs...';
|
|
25
|
-
let urlsToCrawl = [];
|
|
26
|
-
if (options.sitemap) {
|
|
27
|
-
// Try to parse sitemap
|
|
28
|
-
const sitemapUrl = `${baseUrl}/sitemap.xml`;
|
|
29
|
-
spinner.text = `Fetching sitemap from ${sitemapUrl}...`;
|
|
30
|
-
urlsToCrawl = await parseSitemap(sitemapUrl);
|
|
31
|
-
spinner.succeed(`Found ${urlsToCrawl.length} URLs in sitemap`);
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
// Crawl by depth
|
|
35
|
-
urlsToCrawl = await crawlByDepth(url, options.depth, options.maxPages, options.followExternal);
|
|
36
|
-
spinner.succeed(`Discovered ${urlsToCrawl.length} URLs`);
|
|
37
|
-
}
|
|
38
|
-
// Limit pages
|
|
39
|
-
if (urlsToCrawl.length > options.maxPages) {
|
|
40
|
-
urlsToCrawl = urlsToCrawl.slice(0, options.maxPages);
|
|
41
|
-
}
|
|
42
|
-
// Audit each page
|
|
43
|
-
const results = [];
|
|
44
|
-
for (let i = 0; i < urlsToCrawl.length; i++) {
|
|
45
|
-
const pageUrl = urlsToCrawl[i];
|
|
46
|
-
spinner.text = `Auditing ${i + 1}/${urlsToCrawl.length}: ${pageUrl}`;
|
|
47
|
-
try {
|
|
48
|
-
const result = await analyzeUrlWithRules(pageUrl, {
|
|
49
|
-
maxChunkTokens: 1200,
|
|
50
|
-
enableChunking: false,
|
|
51
|
-
enableExtractability: false,
|
|
52
|
-
enableLLM: false,
|
|
53
|
-
minImpactScore: 8,
|
|
54
|
-
});
|
|
55
|
-
results.push(result);
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
console.error(chalk.yellow(`\n⚠️ Failed to audit ${pageUrl}: ${error}`));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
spinner.text = 'Generating crawl report...';
|
|
62
|
-
// Aggregate results
|
|
63
|
-
const crawlReport = {
|
|
64
|
-
crawl_id: generateId(),
|
|
65
|
-
crawled_at: new Date().toISOString(),
|
|
66
|
-
base_url: baseUrl,
|
|
67
|
-
start_url: url,
|
|
68
|
-
total_pages: results.length,
|
|
69
|
-
crawl_depth: options.depth,
|
|
70
|
-
pages: results,
|
|
71
|
-
summary: {
|
|
72
|
-
avgOverallScore: average(results.map(r => r.scoring.overallScore)),
|
|
73
|
-
avgAIReadinessScore: average(results.map(r => r.scoring.categoryScores.find(c => c.category === 'AI Readiness')?.score || 0)),
|
|
74
|
-
total_issues: results.reduce((sum, r) => sum + r.issues.length, 0),
|
|
75
|
-
issues_by_severity: aggregateIssuesBySeverity(results),
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
// Save results
|
|
79
|
-
const outputDir = resolve(process.cwd(), '.ai-lighthouse');
|
|
80
|
-
if (!existsSync(outputDir)) {
|
|
81
|
-
await mkdir(outputDir, { recursive: true });
|
|
82
|
-
}
|
|
83
|
-
const timestamp = new Date().toISOString().replace(/:/g, '-').split('.')[0];
|
|
84
|
-
const baseFilename = `crawl_${new URL(url).hostname}_${timestamp}`;
|
|
85
|
-
if (options.output === 'json') {
|
|
86
|
-
const jsonPath = join(outputDir, `${baseFilename}.json`);
|
|
87
|
-
await writeFile(jsonPath, JSON.stringify(crawlReport, null, 2));
|
|
88
|
-
spinner.succeed(chalk.green('Crawl complete!'));
|
|
89
|
-
console.log(chalk.dim(`Report saved to: ${jsonPath}`));
|
|
90
|
-
}
|
|
91
|
-
else if (options.output === 'html') {
|
|
92
|
-
const htmlPath = join(outputDir, `${baseFilename}.html`);
|
|
93
|
-
const html = generateCrawlHTML(crawlReport);
|
|
94
|
-
await writeFile(htmlPath, html);
|
|
95
|
-
spinner.succeed(chalk.green('Crawl complete!'));
|
|
96
|
-
console.log(chalk.dim(`HTML report saved to: ${htmlPath}`));
|
|
97
|
-
}
|
|
98
|
-
// Display summary
|
|
99
|
-
console.log('\n' + chalk.bold('🌐 Crawl Summary'));
|
|
100
|
-
console.log(`Pages audited: ${chalk.cyan(crawlReport.total_pages)}`);
|
|
101
|
-
console.log(`Average overall score: ${chalk.cyan(crawlReport.summary.avgOverallScore.toFixed(1))}`);
|
|
102
|
-
console.log(`Average AI readiness: ${chalk.cyan(crawlReport.summary.avgAIReadinessScore.toFixed(1))}`);
|
|
103
|
-
console.log(`Total issues found: ${chalk.yellow(crawlReport.summary.total_issues)}`);
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
spinner.fail(chalk.red('Crawl failed'));
|
|
107
|
-
if (error instanceof Error) {
|
|
108
|
-
console.error(chalk.red(error.message));
|
|
109
|
-
}
|
|
110
|
-
process.exit(1);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
async function parseSitemap(sitemapUrl) {
|
|
115
|
-
try {
|
|
116
|
-
const response = await fetch(sitemapUrl);
|
|
117
|
-
if (!response.ok) {
|
|
118
|
-
throw new Error(`Failed to fetch sitemap: ${response.statusText}`);
|
|
119
|
-
}
|
|
120
|
-
const text = await response.text();
|
|
121
|
-
const urlMatches = text.match(/<loc>(.*?)<\/loc>/g) || [];
|
|
122
|
-
return urlMatches.map(match => match.replace(/<\/?loc>/g, ''));
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
console.warn(chalk.yellow(`Could not parse sitemap: ${error}`));
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
async function crawlByDepth(startUrl, maxDepth, maxPages, followExternal) {
|
|
130
|
-
const visited = new Set();
|
|
131
|
-
const toVisit = [{ url: startUrl, depth: 0 }];
|
|
132
|
-
const baseHost = new URL(startUrl).host;
|
|
133
|
-
while (toVisit.length > 0 && visited.size < maxPages) {
|
|
134
|
-
const { url, depth } = toVisit.shift();
|
|
135
|
-
if (visited.has(url) || depth > maxDepth) {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
visited.add(url);
|
|
139
|
-
if (depth < maxDepth) {
|
|
140
|
-
try {
|
|
141
|
-
const links = await extractLinks(url);
|
|
142
|
-
for (const link of links) {
|
|
143
|
-
const linkHost = new URL(link).host;
|
|
144
|
-
if (followExternal || linkHost === baseHost) {
|
|
145
|
-
if (!visited.has(link)) {
|
|
146
|
-
toVisit.push({ url: link, depth: depth + 1 });
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
// Skip pages that fail to load
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return Array.from(visited);
|
|
157
|
-
}
|
|
158
|
-
async function extractLinks(url) {
|
|
159
|
-
try {
|
|
160
|
-
const response = await fetch(url);
|
|
161
|
-
const html = await response.text();
|
|
162
|
-
// Simple regex-based link extraction (in production, use a proper HTML parser)
|
|
163
|
-
const linkMatches = html.match(/href=["'](https?:\/\/[^"']+)["']/g) || [];
|
|
164
|
-
return linkMatches
|
|
165
|
-
.map(match => match.match(/href=["'](https?:\/\/[^"']+)["']/)[1])
|
|
166
|
-
.filter((link, idx, arr) => arr.indexOf(link) === idx); // unique
|
|
167
|
-
}
|
|
168
|
-
catch {
|
|
169
|
-
return [];
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
function average(numbers) {
|
|
173
|
-
if (numbers.length === 0)
|
|
174
|
-
return 0;
|
|
175
|
-
return numbers.reduce((sum, n) => sum + n, 0) / numbers.length;
|
|
176
|
-
}
|
|
177
|
-
function aggregateIssuesBySeverity(results) {
|
|
178
|
-
const counts = {
|
|
179
|
-
critical: 0,
|
|
180
|
-
high: 0,
|
|
181
|
-
medium: 0,
|
|
182
|
-
low: 0,
|
|
183
|
-
info: 0,
|
|
184
|
-
};
|
|
185
|
-
for (const result of results) {
|
|
186
|
-
for (const issue of result.issues) {
|
|
187
|
-
counts[issue.severity] = (counts[issue.severity] || 0) + 1;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return counts;
|
|
191
|
-
}
|
|
192
|
-
function generateId() {
|
|
193
|
-
return `crawl_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
194
|
-
}
|
|
195
|
-
function generateCrawlHTML(report) {
|
|
196
|
-
return `<!DOCTYPE html>
|
|
197
|
-
<html lang="en">
|
|
198
|
-
<head>
|
|
199
|
-
<meta charset="UTF-8">
|
|
200
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
201
|
-
<title>Crawl Report - ${report.base_url}</title>
|
|
202
|
-
<style>
|
|
203
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
204
|
-
body {
|
|
205
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
206
|
-
background: #f5f5f5;
|
|
207
|
-
padding: 20px;
|
|
208
|
-
}
|
|
209
|
-
.container { max-width: 1400px; margin: 0 auto; }
|
|
210
|
-
.header { background: white; padding: 30px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
|
211
|
-
h1 { color: #2563eb; margin-bottom: 10px; }
|
|
212
|
-
.summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 20px; }
|
|
213
|
-
.stat { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 8px; }
|
|
214
|
-
.stat-value { font-size: 2em; font-weight: bold; }
|
|
215
|
-
.stat-label { opacity: 0.9; font-size: 0.9em; }
|
|
216
|
-
.pages { display: grid; gap: 15px; }
|
|
217
|
-
.page-card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
218
|
-
.page-url { color: #1e40af; font-weight: 600; margin-bottom: 10px; word-break: break-all; }
|
|
219
|
-
.page-scores { display: flex; gap: 15px; margin-top: 10px; }
|
|
220
|
-
.score-badge { background: #dbeafe; color: #1e40af; padding: 5px 12px; border-radius: 4px; font-size: 0.9em; }
|
|
221
|
-
</style>
|
|
222
|
-
</head>
|
|
223
|
-
<body>
|
|
224
|
-
<div class="container">
|
|
225
|
-
<div class="header">
|
|
226
|
-
<h1>🌐 Crawl Report</h1>
|
|
227
|
-
<div style="color: #64748b; margin-top: 5px;">${report.base_url}</div>
|
|
228
|
-
<div style="color: #94a3b8; font-size: 0.85em; margin-top: 5px;">
|
|
229
|
-
Generated: ${new Date(report.crawled_at).toLocaleString()}
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
<div class="summary">
|
|
233
|
-
<div class="stat">
|
|
234
|
-
<div class="stat-label">Pages Audited</div>
|
|
235
|
-
<div class="stat-value">${report.total_pages}</div>
|
|
236
|
-
</div>
|
|
237
|
-
<div class="stat">
|
|
238
|
-
<div class="stat-label">Avg Overall Score</div>
|
|
239
|
-
<div class="stat-value">${report.summary.avgOverallScore.toFixed(1)}</div>
|
|
240
|
-
</div>
|
|
241
|
-
<div class="stat">
|
|
242
|
-
<div class="stat-label">Avg AI Readiness</div>
|
|
243
|
-
<div class="stat-value">${report.summary.avgAIReadinessScore.toFixed(1)}</div>
|
|
244
|
-
</div> avg
|
|
245
|
-
<div class="stat">
|
|
246
|
-
<div class="stat-label">Total Issues</div>
|
|
247
|
-
<div class="stat-value">${report.summary.total_issues}</div>
|
|
248
|
-
</div>
|
|
249
|
-
</div>
|
|
250
|
-
</div>
|
|
251
|
-
|
|
252
|
-
<div class="pages">
|
|
253
|
-
${report.pages.map((page) => `
|
|
254
|
-
<div class="page-card">
|
|
255
|
-
<div class="page-url">${page.input.requested_url}</div>
|
|
256
|
-
<div class="page-scores">
|
|
257
|
-
<div class="score-badge">Overall: ${page.scores.overall}</div>
|
|
258
|
-
<div class="score-badge">AI Readiness: ${page.scores.ai_readiness}</div>
|
|
259
|
-
<div class="score-badge">Issues: ${page.issues.length}</div>
|
|
260
|
-
</div>
|
|
261
|
-
</div>
|
|
262
|
-
`).join('')}
|
|
263
|
-
</div>
|
|
264
|
-
</div>
|
|
265
|
-
</body>
|
|
266
|
-
</html>`;
|
|
267
|
-
}
|
package/dist/commands/report.js
DELETED
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import ora from 'ora';
|
|
3
|
-
import open from 'open';
|
|
4
|
-
import { readFile, writeFile } from 'fs/promises';
|
|
5
|
-
import { resolve } from 'path';
|
|
6
|
-
import { existsSync } from 'fs';
|
|
7
|
-
export function reportCommand(program) {
|
|
8
|
-
program
|
|
9
|
-
.command('report')
|
|
10
|
-
.description('Generate and view reports from saved audit results')
|
|
11
|
-
.argument('<file>', 'Path to audit JSON file (e.g., ./last_run.json)')
|
|
12
|
-
.option('--open', 'Open the report in browser', false)
|
|
13
|
-
.option('-f, --format <format>', 'Output format: html, json, csv', 'html')
|
|
14
|
-
.action(async (file, options) => {
|
|
15
|
-
const spinner = ora('Loading audit results...').start();
|
|
16
|
-
try {
|
|
17
|
-
const filePath = resolve(process.cwd(), file);
|
|
18
|
-
if (!existsSync(filePath)) {
|
|
19
|
-
throw new Error(`File not found: ${filePath}`);
|
|
20
|
-
}
|
|
21
|
-
// Read the JSON report
|
|
22
|
-
const jsonContent = await readFile(filePath, 'utf-8');
|
|
23
|
-
const report = JSON.parse(jsonContent);
|
|
24
|
-
spinner.text = 'Generating report...';
|
|
25
|
-
// Determine if it's a crawl report or single audit
|
|
26
|
-
const isCrawlReport = 'pages' in report && Array.isArray(report.pages);
|
|
27
|
-
if (options.format === 'html') {
|
|
28
|
-
const htmlPath = filePath.replace(/\.json$/, '.html');
|
|
29
|
-
const html = isCrawlReport
|
|
30
|
-
? generateCrawlHTML(report)
|
|
31
|
-
: generateSingleHTML(report);
|
|
32
|
-
await writeFile(htmlPath, html);
|
|
33
|
-
spinner.succeed(chalk.green('Report generated!'));
|
|
34
|
-
console.log(chalk.dim(`HTML saved to: ${htmlPath}`));
|
|
35
|
-
if (options.open) {
|
|
36
|
-
spinner.text = 'Opening report in browser...';
|
|
37
|
-
await open(htmlPath);
|
|
38
|
-
spinner.succeed('Report opened in browser');
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
else if (options.format === 'json') {
|
|
42
|
-
// Pretty print to console
|
|
43
|
-
spinner.succeed('Report loaded');
|
|
44
|
-
console.log(JSON.stringify(report, null, 2));
|
|
45
|
-
}
|
|
46
|
-
else if (options.format === 'csv') {
|
|
47
|
-
const csvPath = filePath.replace(/\.json$/, '.csv');
|
|
48
|
-
const csv = generateCSV(report);
|
|
49
|
-
await writeFile(csvPath, csv);
|
|
50
|
-
spinner.succeed(chalk.green('CSV report generated!'));
|
|
51
|
-
console.log(chalk.dim(`CSV saved to: ${csvPath}`));
|
|
52
|
-
}
|
|
53
|
-
// Display summary
|
|
54
|
-
if (isCrawlReport) {
|
|
55
|
-
console.log('\n' + chalk.bold('🌐 Crawl Summary'));
|
|
56
|
-
console.log(`Pages: ${chalk.cyan(report.total_pages)}`);
|
|
57
|
-
console.log(`Avg Score: ${chalk.cyan(report.summary.avg_overall_score.toFixed(1))}`);
|
|
58
|
-
console.log(`Total Issues: ${chalk.yellow(report.summary.total_issues)}`);
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
console.log('\n' + chalk.bold('📊 Audit Summary'));
|
|
62
|
-
console.log(`URL: ${chalk.cyan(report.input.requested_url)}`);
|
|
63
|
-
console.log(`Overall Score: ${chalk.cyan(report.scores.overall)}`);
|
|
64
|
-
console.log(`Issues: ${chalk.yellow(report.issues.length)}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
spinner.fail(chalk.red('Failed to generate report'));
|
|
69
|
-
if (error instanceof Error) {
|
|
70
|
-
console.error(chalk.red(error.message));
|
|
71
|
-
}
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
function generateSingleHTML(report) {
|
|
77
|
-
return `<!DOCTYPE html>
|
|
78
|
-
<html lang="en">
|
|
79
|
-
<head>
|
|
80
|
-
<meta charset="UTF-8">
|
|
81
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
82
|
-
<title>AI Lighthouse Report - ${report.input.requested_url}</title>
|
|
83
|
-
<style>
|
|
84
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
85
|
-
body {
|
|
86
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
87
|
-
background: #f5f5f5;
|
|
88
|
-
padding: 20px;
|
|
89
|
-
line-height: 1.6;
|
|
90
|
-
}
|
|
91
|
-
.container { max-width: 1200px; margin: 0 auto; }
|
|
92
|
-
.header { background: white; padding: 40px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
|
93
|
-
h1 { color: #2563eb; margin-bottom: 10px; }
|
|
94
|
-
.scores { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 15px; margin: 25px 0; }
|
|
95
|
-
.score-card {
|
|
96
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
97
|
-
color: white;
|
|
98
|
-
padding: 20px;
|
|
99
|
-
border-radius: 8px;
|
|
100
|
-
text-align: center;
|
|
101
|
-
}
|
|
102
|
-
.score-value { font-size: 2.5em; font-weight: bold; }
|
|
103
|
-
.score-label { font-size: 0.9em; opacity: 0.9; margin-top: 5px; }
|
|
104
|
-
.section { background: white; padding: 30px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
105
|
-
.section-title { color: #1e40af; font-size: 1.5em; margin-bottom: 20px; border-bottom: 2px solid #dbeafe; padding-bottom: 10px; }
|
|
106
|
-
.issues { display: grid; gap: 15px; }
|
|
107
|
-
.issue {
|
|
108
|
-
background: #f8fafc;
|
|
109
|
-
border-left: 4px solid #cbd5e1;
|
|
110
|
-
padding: 15px;
|
|
111
|
-
border-radius: 4px;
|
|
112
|
-
}
|
|
113
|
-
.issue.critical { border-left-color: #dc2626; background: #fef2f2; }
|
|
114
|
-
.issue.high { border-left-color: #ea580c; background: #fff7ed; }
|
|
115
|
-
.issue.medium { border-left-color: #f59e0b; background: #fffbeb; }
|
|
116
|
-
.issue-title { font-weight: 600; color: #1e293b; margin-bottom: 8px; font-size: 1.05em; }
|
|
117
|
-
.issue-meta { font-size: 0.85em; color: #64748b; margin-bottom: 10px; }
|
|
118
|
-
.issue-fix { color: #0f766e; background: #f0fdfa; padding: 12px; border-radius: 4px; margin-top: 10px; }
|
|
119
|
-
.entity { background: #f0f9ff; border: 1px solid #bae6fd; padding: 15px; border-radius: 6px; margin-bottom: 15px; }
|
|
120
|
-
.entity-name { font-weight: 600; color: #0c4a6e; font-size: 1.1em; }
|
|
121
|
-
</style>
|
|
122
|
-
</head>
|
|
123
|
-
<body>
|
|
124
|
-
<div class="container">
|
|
125
|
-
<div class="header">
|
|
126
|
-
<h1>🚨 AI Lighthouse Report</h1>
|
|
127
|
-
<div style="color: #64748b; margin-top: 5px;">${report.input.requested_url}</div>
|
|
128
|
-
<div style="color: #94a3b8; font-size: 0.85em; margin-top: 5px;">
|
|
129
|
-
${new Date(report.scanned_at).toLocaleString()}
|
|
130
|
-
</div>
|
|
131
|
-
|
|
132
|
-
<div class="scores">
|
|
133
|
-
<div class="score-card">
|
|
134
|
-
<div class="score-value">${report.scores.overall}</div>
|
|
135
|
-
<div class="score-label">Overall</div>
|
|
136
|
-
</div>
|
|
137
|
-
<div class="score-card">
|
|
138
|
-
<div class="score-value">${report.scores.ai_readiness}</div>
|
|
139
|
-
<div class="score-label">AI Readiness</div>
|
|
140
|
-
</div>
|
|
141
|
-
<div class="score-card">
|
|
142
|
-
<div class="score-value">${report.scores.crawlability}</div>
|
|
143
|
-
<div class="score-label">Crawlability</div>
|
|
144
|
-
</div>
|
|
145
|
-
<div class="score-card">
|
|
146
|
-
<div class="score-value">${report.scores.content_clarity}</div>
|
|
147
|
-
<div class="score-label">Content Clarity</div>
|
|
148
|
-
</div>
|
|
149
|
-
<div class="score-card">
|
|
150
|
-
<div class="score-value">${report.scores.schema_coverage}</div>
|
|
151
|
-
<div class="score-label">Schema</div>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
|
-
</div>
|
|
155
|
-
|
|
156
|
-
${report.issues.length > 0 ? `
|
|
157
|
-
<div class="section">
|
|
158
|
-
<div class="section-title">⚠️ Issues (${report.issues.length})</div>
|
|
159
|
-
<div class="issues">
|
|
160
|
-
${report.issues.map((issue) => `
|
|
161
|
-
<div class="issue ${issue.severity}">
|
|
162
|
-
<div class="issue-title">${issue.message}</div>
|
|
163
|
-
<div class="issue-meta">
|
|
164
|
-
<span style="text-transform: uppercase; font-weight: 600; color: ${getSeverityColor(issue.severity)}">
|
|
165
|
-
${issue.severity}
|
|
166
|
-
</span>
|
|
167
|
-
· ${issue.category}
|
|
168
|
-
</div>
|
|
169
|
-
${issue.evidence ? `<div style="color: #475569; margin: 10px 0;">${issue.evidence}</div>` : ''}
|
|
170
|
-
<div class="issue-fix"><strong>💡 Fix:</strong> ${issue.suggested_fix}</div>
|
|
171
|
-
</div>
|
|
172
|
-
`).join('')}
|
|
173
|
-
</div>
|
|
174
|
-
</div>
|
|
175
|
-
` : ''}
|
|
176
|
-
|
|
177
|
-
${report.entities && report.entities.length > 0 ? `
|
|
178
|
-
<div class="section">
|
|
179
|
-
<div class="section-title">🏷️ Detected Entities (${report.entities.length})</div>
|
|
180
|
-
${report.entities.map((entity) => `
|
|
181
|
-
<div class="entity">
|
|
182
|
-
<div class="entity-name">${entity.name}</div>
|
|
183
|
-
<div style="color: #0369a1; font-size: 0.9em; margin-top: 3px;">
|
|
184
|
-
${entity.type} · ${entity.source}
|
|
185
|
-
</div>
|
|
186
|
-
${entity.description ? `<div style="margin-top: 10px; color: #334155;">${entity.description}</div>` : ''}
|
|
187
|
-
</div>
|
|
188
|
-
`).join('')}
|
|
189
|
-
</div>
|
|
190
|
-
` : ''}
|
|
191
|
-
</div>
|
|
192
|
-
</body>
|
|
193
|
-
</html>`;
|
|
194
|
-
}
|
|
195
|
-
function generateCrawlHTML(report) {
|
|
196
|
-
return `<!DOCTYPE html>
|
|
197
|
-
<html lang="en">
|
|
198
|
-
<head>
|
|
199
|
-
<meta charset="UTF-8">
|
|
200
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
201
|
-
<title>Crawl Report - ${report.base_url}</title>
|
|
202
|
-
<style>
|
|
203
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
204
|
-
body {
|
|
205
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
206
|
-
background: #f5f5f5;
|
|
207
|
-
padding: 20px;
|
|
208
|
-
}
|
|
209
|
-
.container { max-width: 1400px; margin: 0 auto; }
|
|
210
|
-
.header { background: white; padding: 30px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
|
211
|
-
h1 { color: #2563eb; }
|
|
212
|
-
.summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 20px; }
|
|
213
|
-
.stat { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 8px; text-align: center; }
|
|
214
|
-
.stat-value { font-size: 2em; font-weight: bold; }
|
|
215
|
-
.pages { display: grid; gap: 15px; }
|
|
216
|
-
.page-card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
217
|
-
.page-url { color: #1e40af; font-weight: 600; word-break: break-all; margin-bottom: 12px; }
|
|
218
|
-
.page-scores { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
219
|
-
.score-badge { background: #dbeafe; color: #1e40af; padding: 6px 12px; border-radius: 4px; font-size: 0.9em; }
|
|
220
|
-
</style>
|
|
221
|
-
</head>
|
|
222
|
-
<body>
|
|
223
|
-
<div class="container">
|
|
224
|
-
<div class="header">
|
|
225
|
-
<h1>🌐 Multi-Page Crawl Report</h1>
|
|
226
|
-
<div style="color: #64748b; margin-top: 8px;">${report.base_url}</div>
|
|
227
|
-
<div style="color: #94a3b8; font-size: 0.85em; margin-top: 5px;">
|
|
228
|
-
${new Date(report.crawled_at).toLocaleString()}
|
|
229
|
-
</div>
|
|
230
|
-
|
|
231
|
-
<div class="summary">
|
|
232
|
-
<div class="stat">
|
|
233
|
-
<div class="stat-value">${report.total_pages}</div>
|
|
234
|
-
<div style="opacity: 0.9; margin-top: 5px;">Pages</div>
|
|
235
|
-
</div>
|
|
236
|
-
<div class="stat">
|
|
237
|
-
<div class="stat-value">${report.summary.avg_overall_score.toFixed(1)}</div>
|
|
238
|
-
<div style="opacity: 0.9; margin-top: 5px;">Avg Score</div>
|
|
239
|
-
</div>
|
|
240
|
-
<div class="stat">
|
|
241
|
-
<div class="stat-value">${report.summary.avg_ai_readiness.toFixed(1)}</div>
|
|
242
|
-
<div style="opacity: 0.9; margin-top: 5px;">Avg AI Readiness</div>
|
|
243
|
-
</div>
|
|
244
|
-
<div class="stat">
|
|
245
|
-
<div class="stat-value">${report.summary.total_issues}</div>
|
|
246
|
-
<div style="opacity: 0.9; margin-top: 5px;">Total Issues</div>
|
|
247
|
-
</div>
|
|
248
|
-
</div>
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
<div class="pages">
|
|
252
|
-
${report.pages.map((page, idx) => `
|
|
253
|
-
<div class="page-card">
|
|
254
|
-
<div style="color: #94a3b8; font-size: 0.85em; margin-bottom: 5px;">Page ${idx + 1}</div>
|
|
255
|
-
<div class="page-url">${page.input.requested_url}</div>
|
|
256
|
-
<div class="page-scores">
|
|
257
|
-
<div class="score-badge">Overall: ${page.scores.overall}</div>
|
|
258
|
-
<div class="score-badge">AI Readiness: ${page.scores.ai_readiness}</div>
|
|
259
|
-
<div class="score-badge">Crawlability: ${page.scores.crawlability}</div>
|
|
260
|
-
<div class="score-badge">Issues: ${page.issues.length}</div>
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
263
|
-
`).join('')}
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
</body>
|
|
267
|
-
</html>`;
|
|
268
|
-
}
|
|
269
|
-
function generateCSV(report) {
|
|
270
|
-
const isCrawlReport = 'pages' in report && Array.isArray(report.pages);
|
|
271
|
-
if (isCrawlReport) {
|
|
272
|
-
const headers = ['Page', 'URL', 'Overall Score', 'AI Readiness', 'Crawlability', 'Issues'];
|
|
273
|
-
const rows = report.pages.map((page, idx) => [
|
|
274
|
-
idx + 1,
|
|
275
|
-
`"${page.input.requested_url}"`,
|
|
276
|
-
page.scores.overall,
|
|
277
|
-
page.scores.ai_readiness,
|
|
278
|
-
page.scores.crawlability,
|
|
279
|
-
page.issues.length,
|
|
280
|
-
]);
|
|
281
|
-
return [headers.join(','), ...rows.map((r) => r.join(','))].join('\n');
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
const headers = ['ID', 'Severity', 'Category', 'Message', 'Fix'];
|
|
285
|
-
const rows = report.issues.map((issue) => [
|
|
286
|
-
issue.id,
|
|
287
|
-
issue.severity,
|
|
288
|
-
issue.category,
|
|
289
|
-
`"${issue.message.replace(/"/g, '""')}"`,
|
|
290
|
-
`"${issue.suggested_fix.replace(/"/g, '""')}"`,
|
|
291
|
-
]);
|
|
292
|
-
return [headers.join(','), ...rows.map((r) => r.join(','))].join('\n');
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
function getSeverityColor(severity) {
|
|
296
|
-
const colors = {
|
|
297
|
-
critical: '#dc2626',
|
|
298
|
-
high: '#ea580c',
|
|
299
|
-
medium: '#f59e0b',
|
|
300
|
-
low: '#84cc16',
|
|
301
|
-
info: '#3b82f6',
|
|
302
|
-
};
|
|
303
|
-
return colors[severity] || '#64748b';
|
|
304
|
-
}
|
package/dist/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/ui/AuditReportUI.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { Box, Text, useInput } from 'ink';
|
|
3
|
-
import Spinner from 'ink-spinner';
|
|
4
|
-
import { ScoreDisplay } from './components/ScoreDisplay.js';
|
|
5
|
-
import { OverviewSection } from './components/OverviewSection.js';
|
|
6
|
-
import { IssuesSection } from './components/IssuesSection.js';
|
|
7
|
-
import { AIUnderstandingSection } from './components/AIUnderstandingSection.js';
|
|
8
|
-
import { HallucinationSection } from './components/HallucinationSection.js';
|
|
9
|
-
import { MessageAlignmentSection } from './components/MessageAlignmentSection.js';
|
|
10
|
-
import { TechnicalSection } from './components/TechnicalSection.js';
|
|
11
|
-
export const AuditReportUI = ({ url, result, aiReadiness, loading = false, currentStep = '', }) => {
|
|
12
|
-
const [currentTab, setCurrentTab] = useState('overview');
|
|
13
|
-
const tabs = [
|
|
14
|
-
{ key: 'overview', label: 'Overview', icon: '📊' },
|
|
15
|
-
{ key: 'issues', label: 'Issues', icon: '⚠️' },
|
|
16
|
-
{ key: 'ai-understanding', label: 'AI Understanding', icon: '🧠' },
|
|
17
|
-
{ key: 'hallucination', label: 'Hallucination Risk', icon: '⚠️' },
|
|
18
|
-
{ key: 'message-alignment', label: 'Message Alignment', icon: '🔍' },
|
|
19
|
-
{ key: 'technical', label: 'Technical', icon: '⚙️' },
|
|
20
|
-
];
|
|
21
|
-
useInput((input, key) => {
|
|
22
|
-
if (loading)
|
|
23
|
-
return;
|
|
24
|
-
if (key.leftArrow) {
|
|
25
|
-
const currentIndex = tabs.findIndex(t => t.key === currentTab);
|
|
26
|
-
const prevIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
|
|
27
|
-
setCurrentTab(tabs[prevIndex].key);
|
|
28
|
-
}
|
|
29
|
-
if (key.rightArrow) {
|
|
30
|
-
const currentIndex = tabs.findIndex(t => t.key === currentTab);
|
|
31
|
-
const nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
|
|
32
|
-
setCurrentTab(tabs[nextIndex].key);
|
|
33
|
-
}
|
|
34
|
-
// Number keys for quick tab switching
|
|
35
|
-
const num = parseInt(input);
|
|
36
|
-
if (num >= 1 && num <= tabs.length) {
|
|
37
|
-
setCurrentTab(tabs[num - 1].key);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
if (loading) {
|
|
41
|
-
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
|
|
42
|
-
React.createElement(Box, null,
|
|
43
|
-
React.createElement(Text, { color: "green" },
|
|
44
|
-
React.createElement(Spinner, { type: "dots" })),
|
|
45
|
-
React.createElement(Text, null,
|
|
46
|
-
" ",
|
|
47
|
-
currentStep || 'Loading...'))));
|
|
48
|
-
}
|
|
49
|
-
const overallScore = aiReadiness?.overall || 0;
|
|
50
|
-
const grade = aiReadiness?.grade || 'N/A';
|
|
51
|
-
return (React.createElement(Box, { flexDirection: "column" },
|
|
52
|
-
React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1 },
|
|
53
|
-
React.createElement(Text, { bold: true, color: "cyan" }, "\uD83D\uDEA8 AI Lighthouse Report")),
|
|
54
|
-
React.createElement(ScoreDisplay, { score: overallScore, grade: grade, url: url }),
|
|
55
|
-
React.createElement(Box, { flexDirection: "row", borderStyle: "single", borderColor: "blue", paddingX: 1, marginTop: 1 }, tabs.map((tab, index) => {
|
|
56
|
-
const isActive = tab.key === currentTab;
|
|
57
|
-
return (React.createElement(Box, { key: tab.key, marginRight: 1 },
|
|
58
|
-
React.createElement(Text, { bold: isActive, color: isActive ? 'cyan' : 'gray', backgroundColor: isActive ? 'blue' : undefined }, ` ${index + 1}. ${tab.icon} ${tab.label} `)));
|
|
59
|
-
})),
|
|
60
|
-
React.createElement(Box, { marginTop: 1, marginBottom: 1 },
|
|
61
|
-
React.createElement(Text, { dimColor: true },
|
|
62
|
-
"Use \u2190 \u2192 arrow keys or numbers (1-",
|
|
63
|
-
tabs.length,
|
|
64
|
-
") to navigate tabs")),
|
|
65
|
-
React.createElement(Box, { flexDirection: "column" },
|
|
66
|
-
currentTab === 'overview' && React.createElement(OverviewSection, { aiReadiness: aiReadiness }),
|
|
67
|
-
currentTab === 'issues' && React.createElement(IssuesSection, { issues: result.issues || [] }),
|
|
68
|
-
currentTab === 'ai-understanding' && React.createElement(AIUnderstandingSection, { llm: result.llm }),
|
|
69
|
-
currentTab === 'hallucination' && React.createElement(HallucinationSection, { hallucinationReport: result.hallucinationReport }),
|
|
70
|
-
currentTab === 'message-alignment' && React.createElement(MessageAlignmentSection, { mirrorReport: result.mirrorReport }),
|
|
71
|
-
currentTab === 'technical' && React.createElement(TechnicalSection, { result: result, scoring: result.scoring })),
|
|
72
|
-
React.createElement(Box, { marginTop: 2, borderStyle: "round", borderColor: "gray", paddingX: 2, paddingY: 1 },
|
|
73
|
-
React.createElement(Text, { dimColor: true },
|
|
74
|
-
"Press Ctrl+C to exit \u2022 Report generated at ",
|
|
75
|
-
new Date().toLocaleString()))));
|
|
76
|
-
};
|