@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.
Files changed (102) hide show
  1. package/dist/index.js +2573 -12
  2. package/package.json +10 -4
  3. package/.ai-lighthouse/audit_example.com_2025-12-15T12-10-43.json +0 -183
  4. package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-32-28.html +0 -743
  5. package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-33-02.html +0 -757
  6. package/.ai-lighthouse/audit_github.com_2025-12-15T11-53-21.json +0 -168
  7. package/.ai-lighthouse/audit_github.com_2025-12-15T12-04-06.json +0 -168
  8. package/.ai-lighthouse/audit_github.com_2025-12-15T12-05-10.json +0 -168
  9. package/.ai-lighthouse/audit_github.com_2025-12-15T12-09-45.json +0 -168
  10. package/.ai-lighthouse/audit_github.com_2025-12-15T12-11-07.json +0 -168
  11. package/.ai-lighthouse/audit_github.com_2025-12-15T12-13-28.json +0 -168
  12. package/.ai-lighthouse/audit_github.com_2025-12-15T12-14-59.json +0 -205
  13. package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-07.json +0 -205
  14. package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-44.json +0 -205
  15. package/.ai-lighthouse/audit_github.com_2025-12-15T12-21-38.json +0 -205
  16. package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-21.json +0 -205
  17. package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-46.json +0 -205
  18. package/.ai-lighthouse/audit_github.com_2025-12-15T12-23-18.json +0 -205
  19. package/.ai-lighthouse/audit_github.com_2025-12-15T12-24-43.json +0 -205
  20. package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-08.json +0 -168
  21. package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-57.json +0 -168
  22. package/.ai-lighthouse/audit_github.com_2025-12-17T12-17-11.json +0 -168
  23. package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-17.json +0 -168
  24. package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-42.json +0 -168
  25. package/.ai-lighthouse/audit_github.com_2025-12-17T12-23-56.json +0 -168
  26. package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-24.json +0 -168
  27. package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-40.json +0 -168
  28. package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-02.json +0 -168
  29. package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-20.json +0 -168
  30. package/.ai-lighthouse/audit_github.com_2025-12-17T12-29-56.json +0 -168
  31. package/.ai-lighthouse/audit_github.com_2025-12-17T12-32-27.json +0 -168
  32. package/.ai-lighthouse/audit_github.com_2025-12-17T12-33-00.json +0 -168
  33. package/.ai-lighthouse/audit_github.com_2025-12-17T12-34-49.json +0 -168
  34. package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-31.json +0 -168
  35. package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-45.json +0 -168
  36. package/.ai-lighthouse/audit_tailwindcss.com_2025-12-15T12-12-01.json +0 -169
  37. package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-08.json +0 -24
  38. package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-23.json +0 -24
  39. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-41-34.json +0 -21
  40. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-09.json +0 -21
  41. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-45.json +0 -21
  42. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-02.json +0 -21
  43. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-26.json +0 -21
  44. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-47-46.json +0 -906
  45. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-50-27.json +0 -906
  46. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-52-59.json +0 -906
  47. package/.ai-lighthouse/crawl_github.com_2025-12-15T12-03-33.json +0 -28
  48. package/CLI_UI_README.md +0 -211
  49. package/EXAMPLES.md +0 -87
  50. package/IMPLEMENTATION.md +0 -215
  51. package/USAGE.md +0 -264
  52. package/WIZARD_GUIDE.md +0 -340
  53. package/bin/cli.js +0 -2
  54. package/dist/commands/audit-interactive.d.ts +0 -2
  55. package/dist/commands/audit-interactive.js +0 -106
  56. package/dist/commands/audit-wizard.d.ts +0 -2
  57. package/dist/commands/audit-wizard.js +0 -110
  58. package/dist/commands/audit.d.ts +0 -2
  59. package/dist/commands/audit.js +0 -940
  60. package/dist/commands/crawl.d.ts +0 -2
  61. package/dist/commands/crawl.js +0 -267
  62. package/dist/commands/report.d.ts +0 -2
  63. package/dist/commands/report.js +0 -304
  64. package/dist/index.d.ts +0 -1
  65. package/dist/ui/AuditReportUI.d.ts +0 -10
  66. package/dist/ui/AuditReportUI.js +0 -76
  67. package/dist/ui/SetupWizard.d.ts +0 -18
  68. package/dist/ui/SetupWizard.js +0 -179
  69. package/dist/ui/components/AIUnderstandingSection.d.ts +0 -6
  70. package/dist/ui/components/AIUnderstandingSection.js +0 -87
  71. package/dist/ui/components/HallucinationSection.d.ts +0 -6
  72. package/dist/ui/components/HallucinationSection.js +0 -84
  73. package/dist/ui/components/IssuesSection.d.ts +0 -6
  74. package/dist/ui/components/IssuesSection.js +0 -84
  75. package/dist/ui/components/MessageAlignmentSection.d.ts +0 -6
  76. package/dist/ui/components/MessageAlignmentSection.js +0 -108
  77. package/dist/ui/components/OverviewSection.d.ts +0 -6
  78. package/dist/ui/components/OverviewSection.js +0 -107
  79. package/dist/ui/components/ScoreDisplay.d.ts +0 -8
  80. package/dist/ui/components/ScoreDisplay.js +0 -41
  81. package/dist/ui/components/TechnicalSection.d.ts +0 -7
  82. package/dist/ui/components/TechnicalSection.js +0 -110
  83. package/dist/utils/comprehensive-formatter.d.ts +0 -5
  84. package/dist/utils/comprehensive-formatter.js +0 -370
  85. package/src/commands/audit-interactive.ts +0 -149
  86. package/src/commands/audit-wizard.ts +0 -137
  87. package/src/commands/audit.ts +0 -1012
  88. package/src/commands/crawl.ts +0 -307
  89. package/src/commands/report.ts +0 -321
  90. package/src/index.ts +0 -22
  91. package/src/ui/AuditReportUI.tsx +0 -151
  92. package/src/ui/SetupWizard.tsx +0 -294
  93. package/src/ui/components/AIUnderstandingSection.tsx +0 -183
  94. package/src/ui/components/HallucinationSection.tsx +0 -172
  95. package/src/ui/components/IssuesSection.tsx +0 -140
  96. package/src/ui/components/MessageAlignmentSection.tsx +0 -203
  97. package/src/ui/components/OverviewSection.tsx +0 -157
  98. package/src/ui/components/ScoreDisplay.tsx +0 -58
  99. package/src/ui/components/TechnicalSection.tsx +0 -200
  100. package/src/utils/comprehensive-formatter.ts +0 -455
  101. package/test.sh +0 -31
  102. package/tsconfig.json +0 -25
@@ -1,2 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function crawlCommand(program: Command): void;
@@ -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
- }
@@ -1,2 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function reportCommand(program: Command): void;
@@ -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 {};
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- interface AuditReportUIProps {
3
- url: string;
4
- result: any;
5
- aiReadiness: any;
6
- loading?: boolean;
7
- currentStep?: string;
8
- }
9
- export declare const AuditReportUI: React.FC<AuditReportUIProps>;
10
- export {};
@@ -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
- };