@ai-lighthouse/cli 1.0.1 → 1.0.3

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