@ai-lighthouse/cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.ai-lighthouse/audit_example.com_2025-12-15T12-10-43.json +183 -0
  2. package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-32-28.html +743 -0
  3. package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-33-02.html +757 -0
  4. package/.ai-lighthouse/audit_github.com_2025-12-15T11-53-21.json +168 -0
  5. package/.ai-lighthouse/audit_github.com_2025-12-15T12-04-06.json +168 -0
  6. package/.ai-lighthouse/audit_github.com_2025-12-15T12-05-10.json +168 -0
  7. package/.ai-lighthouse/audit_github.com_2025-12-15T12-09-45.json +168 -0
  8. package/.ai-lighthouse/audit_github.com_2025-12-15T12-11-07.json +168 -0
  9. package/.ai-lighthouse/audit_github.com_2025-12-15T12-13-28.json +168 -0
  10. package/.ai-lighthouse/audit_github.com_2025-12-15T12-14-59.json +205 -0
  11. package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-07.json +205 -0
  12. package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-44.json +205 -0
  13. package/.ai-lighthouse/audit_github.com_2025-12-15T12-21-38.json +205 -0
  14. package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-21.json +205 -0
  15. package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-46.json +205 -0
  16. package/.ai-lighthouse/audit_github.com_2025-12-15T12-23-18.json +205 -0
  17. package/.ai-lighthouse/audit_github.com_2025-12-15T12-24-43.json +205 -0
  18. package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-08.json +168 -0
  19. package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-57.json +168 -0
  20. package/.ai-lighthouse/audit_github.com_2025-12-17T12-17-11.json +168 -0
  21. package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-17.json +168 -0
  22. package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-42.json +168 -0
  23. package/.ai-lighthouse/audit_github.com_2025-12-17T12-23-56.json +168 -0
  24. package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-24.json +168 -0
  25. package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-40.json +168 -0
  26. package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-02.json +168 -0
  27. package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-20.json +168 -0
  28. package/.ai-lighthouse/audit_github.com_2025-12-17T12-29-56.json +168 -0
  29. package/.ai-lighthouse/audit_github.com_2025-12-17T12-32-27.json +168 -0
  30. package/.ai-lighthouse/audit_github.com_2025-12-17T12-33-00.json +168 -0
  31. package/.ai-lighthouse/audit_github.com_2025-12-17T12-34-49.json +168 -0
  32. package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-31.json +168 -0
  33. package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-45.json +168 -0
  34. package/.ai-lighthouse/audit_tailwindcss.com_2025-12-15T12-12-01.json +169 -0
  35. package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-08.json +24 -0
  36. package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-23.json +24 -0
  37. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-41-34.json +21 -0
  38. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-09.json +21 -0
  39. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-45.json +21 -0
  40. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-02.json +21 -0
  41. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-26.json +21 -0
  42. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-47-46.json +906 -0
  43. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-50-27.json +906 -0
  44. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-52-59.json +906 -0
  45. package/.ai-lighthouse/crawl_github.com_2025-12-15T12-03-33.json +28 -0
  46. package/CLI_UI_README.md +211 -0
  47. package/EXAMPLES.md +87 -0
  48. package/IMPLEMENTATION.md +215 -0
  49. package/README.md +166 -0
  50. package/USAGE.md +264 -0
  51. package/WIZARD_GUIDE.md +340 -0
  52. package/bin/cli.js +2 -0
  53. package/dist/commands/audit-interactive.d.ts +2 -0
  54. package/dist/commands/audit-interactive.js +106 -0
  55. package/dist/commands/audit-wizard.d.ts +2 -0
  56. package/dist/commands/audit-wizard.js +110 -0
  57. package/dist/commands/audit.d.ts +2 -0
  58. package/dist/commands/audit.js +940 -0
  59. package/dist/commands/crawl.d.ts +2 -0
  60. package/dist/commands/crawl.js +267 -0
  61. package/dist/commands/report.d.ts +2 -0
  62. package/dist/commands/report.js +304 -0
  63. package/dist/index.d.ts +1 -0
  64. package/dist/index.js +16 -0
  65. package/dist/ui/AuditReportUI.d.ts +10 -0
  66. package/dist/ui/AuditReportUI.js +76 -0
  67. package/dist/ui/SetupWizard.d.ts +18 -0
  68. package/dist/ui/SetupWizard.js +179 -0
  69. package/dist/ui/components/AIUnderstandingSection.d.ts +6 -0
  70. package/dist/ui/components/AIUnderstandingSection.js +87 -0
  71. package/dist/ui/components/HallucinationSection.d.ts +6 -0
  72. package/dist/ui/components/HallucinationSection.js +84 -0
  73. package/dist/ui/components/IssuesSection.d.ts +6 -0
  74. package/dist/ui/components/IssuesSection.js +84 -0
  75. package/dist/ui/components/MessageAlignmentSection.d.ts +6 -0
  76. package/dist/ui/components/MessageAlignmentSection.js +108 -0
  77. package/dist/ui/components/OverviewSection.d.ts +6 -0
  78. package/dist/ui/components/OverviewSection.js +107 -0
  79. package/dist/ui/components/ScoreDisplay.d.ts +8 -0
  80. package/dist/ui/components/ScoreDisplay.js +41 -0
  81. package/dist/ui/components/TechnicalSection.d.ts +7 -0
  82. package/dist/ui/components/TechnicalSection.js +110 -0
  83. package/dist/utils/comprehensive-formatter.d.ts +5 -0
  84. package/dist/utils/comprehensive-formatter.js +370 -0
  85. package/package.json +49 -0
  86. package/src/commands/audit-interactive.ts +149 -0
  87. package/src/commands/audit-wizard.ts +137 -0
  88. package/src/commands/audit.ts +1012 -0
  89. package/src/commands/crawl.ts +307 -0
  90. package/src/commands/report.ts +321 -0
  91. package/src/index.ts +22 -0
  92. package/src/ui/AuditReportUI.tsx +151 -0
  93. package/src/ui/SetupWizard.tsx +294 -0
  94. package/src/ui/components/AIUnderstandingSection.tsx +183 -0
  95. package/src/ui/components/HallucinationSection.tsx +172 -0
  96. package/src/ui/components/IssuesSection.tsx +140 -0
  97. package/src/ui/components/MessageAlignmentSection.tsx +203 -0
  98. package/src/ui/components/OverviewSection.tsx +157 -0
  99. package/src/ui/components/ScoreDisplay.tsx +58 -0
  100. package/src/ui/components/TechnicalSection.tsx +200 -0
  101. package/src/utils/comprehensive-formatter.ts +455 -0
  102. package/test.sh +31 -0
  103. package/tsconfig.json +25 -0
@@ -0,0 +1,151 @@
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
+
12
+ interface AuditReportUIProps {
13
+ url: string;
14
+ result: any;
15
+ aiReadiness: any;
16
+ loading?: boolean;
17
+ currentStep?: string;
18
+ }
19
+
20
+ type TabType = 'overview' | 'issues' | 'ai-understanding' | 'hallucination' | 'message-alignment' | 'technical';
21
+
22
+ export const AuditReportUI: React.FC<AuditReportUIProps> = ({
23
+ url,
24
+ result,
25
+ aiReadiness,
26
+ loading = false,
27
+ currentStep = '',
28
+ }) => {
29
+ const [currentTab, setCurrentTab] = useState<TabType>('overview');
30
+
31
+ const tabs: { key: TabType; label: string; icon: string }[] = [
32
+ { key: 'overview', label: 'Overview', icon: '📊' },
33
+ { key: 'issues', label: 'Issues', icon: '⚠️' },
34
+ { key: 'ai-understanding', label: 'AI Understanding', icon: '🧠' },
35
+ { key: 'hallucination', label: 'Hallucination Risk', icon: '⚠️' },
36
+ { key: 'message-alignment', label: 'Message Alignment', icon: '🔍' },
37
+ { key: 'technical', label: 'Technical', icon: '⚙️' },
38
+ ];
39
+
40
+ useInput((input, key) => {
41
+ if (loading) return;
42
+
43
+ if (key.leftArrow) {
44
+ const currentIndex = tabs.findIndex(t => t.key === currentTab);
45
+ const prevIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
46
+ setCurrentTab(tabs[prevIndex].key);
47
+ }
48
+
49
+ if (key.rightArrow) {
50
+ const currentIndex = tabs.findIndex(t => t.key === currentTab);
51
+ const nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
52
+ setCurrentTab(tabs[nextIndex].key);
53
+ }
54
+
55
+ // Number keys for quick tab switching
56
+ const num = parseInt(input);
57
+ if (num >= 1 && num <= tabs.length) {
58
+ setCurrentTab(tabs[num - 1].key);
59
+ }
60
+ });
61
+
62
+ if (loading) {
63
+ return (
64
+ <Box flexDirection="column" paddingY={1}>
65
+ <Box>
66
+ <Text color="green">
67
+ <Spinner type="dots" />
68
+ </Text>
69
+ <Text> {currentStep || 'Loading...'}</Text>
70
+ </Box>
71
+ </Box>
72
+ );
73
+ }
74
+
75
+ const overallScore = aiReadiness?.overall || 0;
76
+ const grade = aiReadiness?.grade || 'N/A';
77
+
78
+ return (
79
+ <Box flexDirection="column">
80
+ {/* Header */}
81
+ <Box
82
+ flexDirection="column"
83
+ borderStyle="round"
84
+ borderColor="cyan"
85
+ paddingX={2}
86
+ paddingY={1}
87
+ >
88
+ <Text bold color="cyan">
89
+ 🚨 AI Lighthouse Report
90
+ </Text>
91
+ </Box>
92
+
93
+ {/* Score Display */}
94
+ <ScoreDisplay score={overallScore} grade={grade} url={url} />
95
+
96
+ {/* Tab Navigation */}
97
+ <Box
98
+ flexDirection="row"
99
+ borderStyle="single"
100
+ borderColor="blue"
101
+ paddingX={1}
102
+ marginTop={1}
103
+ >
104
+ {tabs.map((tab, index) => {
105
+ const isActive = tab.key === currentTab;
106
+ return (
107
+ <Box key={tab.key} marginRight={1}>
108
+ <Text
109
+ bold={isActive}
110
+ color={isActive ? 'cyan' : 'gray'}
111
+ backgroundColor={isActive ? 'blue' : undefined}
112
+ >
113
+ {` ${index + 1}. ${tab.icon} ${tab.label} `}
114
+ </Text>
115
+ </Box>
116
+ );
117
+ })}
118
+ </Box>
119
+
120
+ {/* Help Text */}
121
+ <Box marginTop={1} marginBottom={1}>
122
+ <Text dimColor>
123
+ Use ← → arrow keys or numbers (1-{tabs.length}) to navigate tabs
124
+ </Text>
125
+ </Box>
126
+
127
+ {/* Tab Content */}
128
+ <Box flexDirection="column">
129
+ {currentTab === 'overview' && <OverviewSection aiReadiness={aiReadiness} />}
130
+ {currentTab === 'issues' && <IssuesSection issues={result.issues || []} />}
131
+ {currentTab === 'ai-understanding' && <AIUnderstandingSection llm={result.llm} />}
132
+ {currentTab === 'hallucination' && <HallucinationSection hallucinationReport={result.hallucinationReport} />}
133
+ {currentTab === 'message-alignment' && <MessageAlignmentSection mirrorReport={result.mirrorReport} />}
134
+ {currentTab === 'technical' && <TechnicalSection result={result} scoring={result.scoring} />}
135
+ </Box>
136
+
137
+ {/* Footer */}
138
+ <Box
139
+ marginTop={2}
140
+ borderStyle="round"
141
+ borderColor="gray"
142
+ paddingX={2}
143
+ paddingY={1}
144
+ >
145
+ <Text dimColor>
146
+ Press Ctrl+C to exit • Report generated at {new Date().toLocaleString()}
147
+ </Text>
148
+ </Box>
149
+ </Box>
150
+ );
151
+ };
@@ -0,0 +1,294 @@
1
+ import React, { useState } from 'react';
2
+ import { Box, Text, useApp } from 'ink';
3
+ import TextInput from 'ink-text-input';
4
+ import SelectInput from 'ink-select-input';
5
+
6
+ interface SetupWizardProps {
7
+ onComplete: (config: AuditConfig) => void;
8
+ initialUrl?: string;
9
+ }
10
+
11
+ export interface AuditConfig {
12
+ url: string;
13
+ enableLlm: boolean;
14
+ enableChunking: boolean;
15
+ enableExtractability: boolean;
16
+ enableHallucination: boolean;
17
+ llmProvider?: string;
18
+ llmModel?: string;
19
+ llmApiKey?: string;
20
+ llmBaseUrl?: string;
21
+ }
22
+
23
+ type Step = 'url' | 'features' | 'llm-provider' | 'llm-model' | 'llm-api-key' | 'llm-base-url';
24
+
25
+ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete, initialUrl }) => {
26
+ const { exit } = useApp();
27
+ const [step, setStep] = useState<Step>(initialUrl ? 'features' : 'url');
28
+ const [url, setUrl] = useState(initialUrl || '');
29
+ const [selectedFeatures, setSelectedFeatures] = useState<string[]>([]);
30
+ const [llmProvider, setLlmProvider] = useState<string>('');
31
+ const [llmModel, setLlmModel] = useState<string>('');
32
+ const [llmApiKey, setLlmApiKey] = useState<string>('');
33
+ const [llmBaseUrl, setLlmBaseUrl] = useState<string>('');
34
+
35
+ const featureOptions = [
36
+ { label: '🧠 AI Understanding (LLM Analysis)', value: 'llm' },
37
+ { label: '📄 Content Chunking Analysis', value: 'chunking' },
38
+ { label: '🔄 Extractability Analysis', value: 'extractability' },
39
+ { label: '⚠️ Hallucination Detection', value: 'hallucination' },
40
+ { label: '✅ Continue with selected features', value: 'done' },
41
+ ];
42
+
43
+ const llmProviderOptions = [
44
+ { label: 'OpenAI (GPT-4, GPT-3.5)', value: 'openai' },
45
+ { label: 'Anthropic (Claude)', value: 'anthropic' },
46
+ { label: 'Ollama (Local)', value: 'ollama' },
47
+ { label: 'Custom/Local Provider', value: 'local' },
48
+ ];
49
+
50
+ const handleUrlSubmit = (value: string) => {
51
+ setUrl(value);
52
+ setStep('features');
53
+ };
54
+
55
+ const handleFeatureSelect = (item: any) => {
56
+ if (item.value === 'done') {
57
+ // Check if LLM is needed
58
+ if (selectedFeatures.includes('llm') || selectedFeatures.includes('hallucination')) {
59
+ setStep('llm-provider');
60
+ } else {
61
+ completeSetup();
62
+ }
63
+ } else {
64
+ // Toggle feature selection
65
+ if (selectedFeatures.includes(item.value)) {
66
+ setSelectedFeatures(selectedFeatures.filter(f => f !== item.value));
67
+ } else {
68
+ setSelectedFeatures([...selectedFeatures, item.value]);
69
+ }
70
+ }
71
+ };
72
+
73
+ const handleLlmProviderSelect = (item: any) => {
74
+ setLlmProvider(item.value);
75
+
76
+ // Set default base URL for ollama
77
+ if (item.value === 'ollama') {
78
+ setLlmBaseUrl('http://localhost:11434');
79
+ }
80
+
81
+ setStep('llm-model');
82
+ };
83
+
84
+ const handleLlmModelSubmit = (value: string) => {
85
+ setLlmModel(value);
86
+
87
+ // If ollama, skip API key and go to base URL
88
+ if (llmProvider === 'ollama') {
89
+ setStep('llm-base-url');
90
+ } else {
91
+ setStep('llm-api-key');
92
+ }
93
+ };
94
+
95
+ const handleLlmApiKeySubmit = (value: string) => {
96
+ setLlmApiKey(value);
97
+
98
+ // If provider needs base URL, go there, otherwise complete
99
+ if (llmProvider === 'local' || llmProvider === 'anthropic') {
100
+ setStep('llm-base-url');
101
+ } else {
102
+ completeSetup();
103
+ }
104
+ };
105
+
106
+ const handleLlmBaseUrlSubmit = (value: string) => {
107
+ setLlmBaseUrl(value);
108
+ completeSetup();
109
+ };
110
+
111
+ const completeSetup = () => {
112
+ const config: AuditConfig = {
113
+ url,
114
+ enableLlm: selectedFeatures.includes('llm') || selectedFeatures.includes('hallucination'),
115
+ enableChunking: selectedFeatures.includes('chunking'),
116
+ enableExtractability: selectedFeatures.includes('extractability'),
117
+ enableHallucination: selectedFeatures.includes('hallucination'),
118
+ };
119
+
120
+ if (config.enableLlm) {
121
+ config.llmProvider = llmProvider;
122
+ config.llmModel = llmModel;
123
+ config.llmApiKey = llmApiKey || undefined;
124
+ config.llmBaseUrl = llmBaseUrl || undefined;
125
+ }
126
+
127
+ onComplete(config);
128
+ // Exit the wizard app
129
+ exit();
130
+ };
131
+
132
+ const getModelPlaceholder = () => {
133
+ switch (llmProvider) {
134
+ case 'openai':
135
+ return 'gpt-4o-mini (default)';
136
+ case 'anthropic':
137
+ return 'claude-3-5-sonnet-20241022 (default)';
138
+ case 'ollama':
139
+ return 'qwen2.5:0.5b';
140
+ default:
141
+ return 'model-name';
142
+ }
143
+ };
144
+
145
+ const getApiKeyPlaceholder = () => {
146
+ switch (llmProvider) {
147
+ case 'openai':
148
+ return 'sk-...';
149
+ case 'anthropic':
150
+ return 'sk-ant-...';
151
+ default:
152
+ return 'your-api-key';
153
+ }
154
+ };
155
+
156
+ return (
157
+ <Box flexDirection="column" paddingY={1}>
158
+ {/* Header */}
159
+ <Box
160
+ flexDirection="column"
161
+ borderStyle="round"
162
+ borderColor="cyan"
163
+ paddingX={2}
164
+ paddingY={1}
165
+ marginBottom={1}
166
+ >
167
+ <Text bold color="cyan">
168
+ 🚨 AI Lighthouse Setup Wizard
169
+ </Text>
170
+ <Text dimColor>Configure your audit settings</Text>
171
+ </Box>
172
+
173
+ {/* URL Input */}
174
+ {step === 'url' && (
175
+ <Box flexDirection="column">
176
+ <Text bold>Enter the URL to audit:</Text>
177
+ <Box marginTop={1}>
178
+ <Text color="cyan">URL: </Text>
179
+ <TextInput
180
+ value={url}
181
+ onChange={setUrl}
182
+ onSubmit={handleUrlSubmit}
183
+ placeholder="https://example.com"
184
+ />
185
+ </Box>
186
+ <Box marginTop={1}>
187
+ <Text dimColor>Press Enter to continue</Text>
188
+ </Box>
189
+ </Box>
190
+ )}
191
+
192
+ {/* Feature Selection */}
193
+ {step === 'features' && (
194
+ <Box flexDirection="column">
195
+ <Text bold>Select features to enable:</Text>
196
+ <Box marginTop={1}>
197
+ <Text dimColor>
198
+ Selected: {selectedFeatures.length === 0 ? 'None (basic audit only)' : selectedFeatures.map(f => {
199
+ const feature = featureOptions.find(opt => opt.value === f);
200
+ return feature?.label.split(' ')[0];
201
+ }).join(', ')}
202
+ </Text>
203
+ </Box>
204
+ <Box marginTop={1}>
205
+ <SelectInput
206
+ items={featureOptions.map(opt => ({
207
+ ...opt,
208
+ label: selectedFeatures.includes(opt.value) && opt.value !== 'done'
209
+ ? `✓ ${opt.label}`
210
+ : opt.label,
211
+ }))}
212
+ onSelect={handleFeatureSelect}
213
+ />
214
+ </Box>
215
+ <Box marginTop={1}>
216
+ <Text dimColor>Use ↑↓ to navigate, Enter to toggle/continue</Text>
217
+ </Box>
218
+ </Box>
219
+ )}
220
+
221
+ {/* LLM Provider Selection */}
222
+ {step === 'llm-provider' && (
223
+ <Box flexDirection="column">
224
+ <Text bold>Select LLM provider:</Text>
225
+ <Box marginTop={1}>
226
+ <SelectInput items={llmProviderOptions} onSelect={handleLlmProviderSelect} />
227
+ </Box>
228
+ <Box marginTop={1}>
229
+ <Text dimColor>Use ↑↓ to navigate, Enter to select</Text>
230
+ </Box>
231
+ </Box>
232
+ )}
233
+
234
+ {/* LLM Model Input */}
235
+ {step === 'llm-model' && (
236
+ <Box flexDirection="column">
237
+ <Text bold>Enter LLM model name:</Text>
238
+ <Box marginTop={1}>
239
+ <Text color="cyan">Model: </Text>
240
+ <TextInput
241
+ value={llmModel}
242
+ onChange={setLlmModel}
243
+ onSubmit={handleLlmModelSubmit}
244
+ placeholder={getModelPlaceholder()}
245
+ />
246
+ </Box>
247
+ <Box marginTop={1}>
248
+ <Text dimColor>Leave empty for default, or press Enter to continue</Text>
249
+ </Box>
250
+ </Box>
251
+ )}
252
+
253
+ {/* LLM API Key Input */}
254
+ {step === 'llm-api-key' && (
255
+ <Box flexDirection="column">
256
+ <Text bold>Enter API key:</Text>
257
+ <Box marginTop={1}>
258
+ <Text color="cyan">API Key: </Text>
259
+ <TextInput
260
+ value={llmApiKey}
261
+ onChange={setLlmApiKey}
262
+ onSubmit={handleLlmApiKeySubmit}
263
+ placeholder={getApiKeyPlaceholder()}
264
+ mask="*"
265
+ />
266
+ </Box>
267
+ <Box marginTop={1}>
268
+ <Text dimColor>Your API key will not be stored</Text>
269
+ </Box>
270
+ </Box>
271
+ )}
272
+
273
+ {/* LLM Base URL Input */}
274
+ {step === 'llm-base-url' && (
275
+ <Box flexDirection="column">
276
+ <Text bold>Enter API base URL (optional):</Text>
277
+ <Box marginTop={1}>
278
+ <Text color="cyan">Base URL: </Text>
279
+ <TextInput
280
+ value={llmBaseUrl}
281
+ onChange={setLlmBaseUrl}
282
+ onSubmit={handleLlmBaseUrlSubmit}
283
+ placeholder={llmProvider === 'ollama' ? 'http://localhost:11434' : 'https://api.example.com'}
284
+ />
285
+ </Box>
286
+ <Box marginTop={1}>
287
+ <Text dimColor>Press Enter to continue (leave empty for default)</Text>
288
+ </Box>
289
+ </Box>
290
+ )}
291
+
292
+ </Box>
293
+ );
294
+ };
@@ -0,0 +1,183 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+
4
+ interface AIUnderstandingSectionProps {
5
+ llm: any;
6
+ }
7
+
8
+ export const AIUnderstandingSection: React.FC<AIUnderstandingSectionProps> = ({ llm }) => {
9
+ if (!llm || Object.keys(llm).length === 0) {
10
+ return (
11
+ <Box flexDirection="column" paddingY={1}>
12
+ <Box
13
+ flexDirection="column"
14
+ borderStyle="round"
15
+ borderColor="yellow"
16
+ paddingX={2}
17
+ paddingY={1}
18
+ >
19
+ <Text bold color="yellow">
20
+ 💡 Enable AI Understanding Analysis
21
+ </Text>
22
+ <Box marginTop={1} flexDirection="column">
23
+ <Text>LLM analysis is not enabled. To see AI understanding insights, run:</Text>
24
+ <Box marginTop={1} borderStyle="single" paddingX={1}>
25
+ <Text color="cyan">
26
+ ai-lighthouse audit [URL] --enable-llm --llm-provider openai --llm-api-key YOUR_KEY
27
+ </Text>
28
+ </Box>
29
+ <Box marginTop={1}>
30
+ <Text dimColor>Supported providers: openai, anthropic, ollama, local</Text>
31
+ </Box>
32
+ </Box>
33
+ </Box>
34
+ </Box>
35
+ );
36
+ }
37
+
38
+ return (
39
+ <Box flexDirection="column" paddingY={1}>
40
+ {/* Page Type */}
41
+ {llm.pageType && (
42
+ <Box
43
+ flexDirection="column"
44
+ borderStyle="round"
45
+ borderColor="magenta"
46
+ paddingX={2}
47
+ paddingY={1}
48
+ marginBottom={1}
49
+ >
50
+ <Text bold color="magenta">
51
+ 📄 Inferred Page Type
52
+ </Text>
53
+ <Text color="cyan">{llm.pageType}</Text>
54
+ </Box>
55
+ )}
56
+
57
+ {/* AI-Generated Insights */}
58
+ {llm.pageTypeInsights && llm.pageTypeInsights.length > 0 && (
59
+ <Box flexDirection="column" marginBottom={1}>
60
+ <Text bold underline color="blue">
61
+ 💡 AI-Generated Insights
62
+ </Text>
63
+ {llm.pageTypeInsights.map((insight: string, idx: number) => (
64
+ <Box key={idx} marginTop={0.5}>
65
+ <Text color="cyan">• {insight}</Text>
66
+ </Box>
67
+ ))}
68
+ </Box>
69
+ )}
70
+
71
+ {/* Summary */}
72
+ {llm.summary && (
73
+ <Box flexDirection="column" marginBottom={1}>
74
+ <Text bold underline>
75
+ Summary
76
+ </Text>
77
+ <Box marginTop={0.5}>
78
+ <Text>{llm.summary}</Text>
79
+ </Box>
80
+ </Box>
81
+ )}
82
+
83
+ {/* Key Topics */}
84
+ {llm.keyTopics && llm.keyTopics.length > 0 && (
85
+ <Box flexDirection="column" marginBottom={1}>
86
+ <Text bold underline>
87
+ 🏷️ Key Topics
88
+ </Text>
89
+ <Box marginTop={0.5}>
90
+ <Text>{llm.keyTopics.map((t: string) => `[${t}]`).join(' ')}</Text>
91
+ </Box>
92
+ </Box>
93
+ )}
94
+
95
+ {/* Metadata Grid */}
96
+ <Box flexDirection="column" marginBottom={1}>
97
+ <Text bold underline>
98
+ Metadata
99
+ </Text>
100
+ <Box marginTop={0.5} flexDirection="column">
101
+ {llm.readingLevel && (
102
+ <Text>
103
+ <Text bold>Reading Level:</Text> {llm.readingLevel.description}
104
+ </Text>
105
+ )}
106
+ {llm.sentiment && (
107
+ <Text>
108
+ <Text bold>Sentiment:</Text> {llm.sentiment}
109
+ </Text>
110
+ )}
111
+ {llm.technicalDepth && (
112
+ <Text>
113
+ <Text bold>Technical Depth:</Text> {llm.technicalDepth}
114
+ </Text>
115
+ )}
116
+ </Box>
117
+ </Box>
118
+
119
+ {/* Top Entities */}
120
+ {llm.topEntities && llm.topEntities.length > 0 && (
121
+ <Box flexDirection="column" marginBottom={1}>
122
+ <Text bold underline>
123
+ 🔍 Key Entities
124
+ </Text>
125
+ {llm.topEntities.slice(0, 5).map((entity: any, idx: number) => (
126
+ <Box key={idx} marginTop={0.5}>
127
+ <Text>
128
+ • <Text bold>{entity.name}</Text>{' '}
129
+ <Text dimColor>
130
+ ({entity.type}) - {Math.round((entity.relevance || 0) * 100)}% relevance
131
+ </Text>
132
+ </Text>
133
+ </Box>
134
+ ))}
135
+ </Box>
136
+ )}
137
+
138
+ {/* Questions AI Can Answer */}
139
+ {llm.questions && llm.questions.length > 0 && (
140
+ <Box flexDirection="column" marginBottom={1}>
141
+ <Text bold underline>
142
+ ❓ Questions AI Can Answer
143
+ </Text>
144
+ {llm.questions.slice(0, 5).map((q: any, idx: number) => (
145
+ <Box key={idx} marginTop={0.5}>
146
+ <Text>
147
+ {idx + 1}. <Text dimColor>[{q.difficulty.toUpperCase()}]</Text> {q.question}
148
+ </Text>
149
+ </Box>
150
+ ))}
151
+ </Box>
152
+ )}
153
+
154
+ {/* Suggested FAQs */}
155
+ {llm.suggestedFAQ && llm.suggestedFAQ.length > 0 && (
156
+ <Box
157
+ flexDirection="column"
158
+ borderStyle="round"
159
+ borderColor="yellow"
160
+ paddingX={2}
161
+ paddingY={1}
162
+ >
163
+ <Text bold color="yellow">
164
+ 💡 Suggested FAQs
165
+ </Text>
166
+ {llm.suggestedFAQ
167
+ .filter((f: any) => f.importance === 'high')
168
+ .slice(0, 3)
169
+ .map((faq: any, idx: number) => (
170
+ <Box key={idx} flexDirection="column" marginTop={1}>
171
+ <Text>
172
+ <Text bold>Q:</Text> {faq.question}
173
+ </Text>
174
+ <Text dimColor>
175
+ <Text bold>A:</Text> {faq.suggestedAnswer}
176
+ </Text>
177
+ </Box>
178
+ ))}
179
+ </Box>
180
+ )}
181
+ </Box>
182
+ );
183
+ };