@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.
- package/.ai-lighthouse/audit_example.com_2025-12-15T12-10-43.json +183 -0
- package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-32-28.html +743 -0
- package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-33-02.html +757 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T11-53-21.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-04-06.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-05-10.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-09-45.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-11-07.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-13-28.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-14-59.json +205 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-07.json +205 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-44.json +205 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-21-38.json +205 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-21.json +205 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-46.json +205 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-23-18.json +205 -0
- package/.ai-lighthouse/audit_github.com_2025-12-15T12-24-43.json +205 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-08.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-57.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-17-11.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-17.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-42.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-23-56.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-24.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-40.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-02.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-20.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-29-56.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-32-27.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-33-00.json +168 -0
- package/.ai-lighthouse/audit_github.com_2025-12-17T12-34-49.json +168 -0
- package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-31.json +168 -0
- package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-45.json +168 -0
- package/.ai-lighthouse/audit_tailwindcss.com_2025-12-15T12-12-01.json +169 -0
- package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-08.json +24 -0
- package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-23.json +24 -0
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-41-34.json +21 -0
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-09.json +21 -0
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-45.json +21 -0
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-02.json +21 -0
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-26.json +21 -0
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-47-46.json +906 -0
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-50-27.json +906 -0
- package/.ai-lighthouse/crawl_github.com_2025-12-15T11-52-59.json +906 -0
- package/.ai-lighthouse/crawl_github.com_2025-12-15T12-03-33.json +28 -0
- package/CLI_UI_README.md +211 -0
- package/EXAMPLES.md +87 -0
- package/IMPLEMENTATION.md +215 -0
- package/README.md +166 -0
- package/USAGE.md +264 -0
- package/WIZARD_GUIDE.md +340 -0
- package/bin/cli.js +2 -0
- package/dist/commands/audit-interactive.d.ts +2 -0
- package/dist/commands/audit-interactive.js +106 -0
- package/dist/commands/audit-wizard.d.ts +2 -0
- package/dist/commands/audit-wizard.js +110 -0
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.js +940 -0
- package/dist/commands/crawl.d.ts +2 -0
- package/dist/commands/crawl.js +267 -0
- package/dist/commands/report.d.ts +2 -0
- package/dist/commands/report.js +304 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +16 -0
- package/dist/ui/AuditReportUI.d.ts +10 -0
- package/dist/ui/AuditReportUI.js +76 -0
- package/dist/ui/SetupWizard.d.ts +18 -0
- package/dist/ui/SetupWizard.js +179 -0
- package/dist/ui/components/AIUnderstandingSection.d.ts +6 -0
- package/dist/ui/components/AIUnderstandingSection.js +87 -0
- package/dist/ui/components/HallucinationSection.d.ts +6 -0
- package/dist/ui/components/HallucinationSection.js +84 -0
- package/dist/ui/components/IssuesSection.d.ts +6 -0
- package/dist/ui/components/IssuesSection.js +84 -0
- package/dist/ui/components/MessageAlignmentSection.d.ts +6 -0
- package/dist/ui/components/MessageAlignmentSection.js +108 -0
- package/dist/ui/components/OverviewSection.d.ts +6 -0
- package/dist/ui/components/OverviewSection.js +107 -0
- package/dist/ui/components/ScoreDisplay.d.ts +8 -0
- package/dist/ui/components/ScoreDisplay.js +41 -0
- package/dist/ui/components/TechnicalSection.d.ts +7 -0
- package/dist/ui/components/TechnicalSection.js +110 -0
- package/dist/utils/comprehensive-formatter.d.ts +5 -0
- package/dist/utils/comprehensive-formatter.js +370 -0
- package/package.json +49 -0
- package/src/commands/audit-interactive.ts +149 -0
- package/src/commands/audit-wizard.ts +137 -0
- package/src/commands/audit.ts +1012 -0
- package/src/commands/crawl.ts +307 -0
- package/src/commands/report.ts +321 -0
- package/src/index.ts +22 -0
- package/src/ui/AuditReportUI.tsx +151 -0
- package/src/ui/SetupWizard.tsx +294 -0
- package/src/ui/components/AIUnderstandingSection.tsx +183 -0
- package/src/ui/components/HallucinationSection.tsx +172 -0
- package/src/ui/components/IssuesSection.tsx +140 -0
- package/src/ui/components/MessageAlignmentSection.tsx +203 -0
- package/src/ui/components/OverviewSection.tsx +157 -0
- package/src/ui/components/ScoreDisplay.tsx +58 -0
- package/src/ui/components/TechnicalSection.tsx +200 -0
- package/src/utils/comprehensive-formatter.ts +455 -0
- package/test.sh +31 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface SetupWizardProps {
|
|
3
|
+
onComplete: (config: AuditConfig) => void;
|
|
4
|
+
initialUrl?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface AuditConfig {
|
|
7
|
+
url: string;
|
|
8
|
+
enableLlm: boolean;
|
|
9
|
+
enableChunking: boolean;
|
|
10
|
+
enableExtractability: boolean;
|
|
11
|
+
enableHallucination: boolean;
|
|
12
|
+
llmProvider?: string;
|
|
13
|
+
llmModel?: string;
|
|
14
|
+
llmApiKey?: string;
|
|
15
|
+
llmBaseUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const SetupWizard: React.FC<SetupWizardProps>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,179 @@
|
|
|
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
|
+
export const SetupWizard = ({ onComplete, initialUrl }) => {
|
|
6
|
+
const { exit } = useApp();
|
|
7
|
+
const [step, setStep] = useState(initialUrl ? 'features' : 'url');
|
|
8
|
+
const [url, setUrl] = useState(initialUrl || '');
|
|
9
|
+
const [selectedFeatures, setSelectedFeatures] = useState([]);
|
|
10
|
+
const [llmProvider, setLlmProvider] = useState('');
|
|
11
|
+
const [llmModel, setLlmModel] = useState('');
|
|
12
|
+
const [llmApiKey, setLlmApiKey] = useState('');
|
|
13
|
+
const [llmBaseUrl, setLlmBaseUrl] = useState('');
|
|
14
|
+
const featureOptions = [
|
|
15
|
+
{ label: '🧠 AI Understanding (LLM Analysis)', value: 'llm' },
|
|
16
|
+
{ label: '📄 Content Chunking Analysis', value: 'chunking' },
|
|
17
|
+
{ label: '🔄 Extractability Analysis', value: 'extractability' },
|
|
18
|
+
{ label: '⚠️ Hallucination Detection', value: 'hallucination' },
|
|
19
|
+
{ label: '✅ Continue with selected features', value: 'done' },
|
|
20
|
+
];
|
|
21
|
+
const llmProviderOptions = [
|
|
22
|
+
{ label: 'OpenAI (GPT-4, GPT-3.5)', value: 'openai' },
|
|
23
|
+
{ label: 'Anthropic (Claude)', value: 'anthropic' },
|
|
24
|
+
{ label: 'Ollama (Local)', value: 'ollama' },
|
|
25
|
+
{ label: 'Custom/Local Provider', value: 'local' },
|
|
26
|
+
];
|
|
27
|
+
const handleUrlSubmit = (value) => {
|
|
28
|
+
setUrl(value);
|
|
29
|
+
setStep('features');
|
|
30
|
+
};
|
|
31
|
+
const handleFeatureSelect = (item) => {
|
|
32
|
+
if (item.value === 'done') {
|
|
33
|
+
// Check if LLM is needed
|
|
34
|
+
if (selectedFeatures.includes('llm') || selectedFeatures.includes('hallucination')) {
|
|
35
|
+
setStep('llm-provider');
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
completeSetup();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Toggle feature selection
|
|
43
|
+
if (selectedFeatures.includes(item.value)) {
|
|
44
|
+
setSelectedFeatures(selectedFeatures.filter(f => f !== item.value));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
setSelectedFeatures([...selectedFeatures, item.value]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const handleLlmProviderSelect = (item) => {
|
|
52
|
+
setLlmProvider(item.value);
|
|
53
|
+
// Set default base URL for ollama
|
|
54
|
+
if (item.value === 'ollama') {
|
|
55
|
+
setLlmBaseUrl('http://localhost:11434');
|
|
56
|
+
}
|
|
57
|
+
setStep('llm-model');
|
|
58
|
+
};
|
|
59
|
+
const handleLlmModelSubmit = (value) => {
|
|
60
|
+
setLlmModel(value);
|
|
61
|
+
// If ollama, skip API key and go to base URL
|
|
62
|
+
if (llmProvider === 'ollama') {
|
|
63
|
+
setStep('llm-base-url');
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
setStep('llm-api-key');
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const handleLlmApiKeySubmit = (value) => {
|
|
70
|
+
setLlmApiKey(value);
|
|
71
|
+
// If provider needs base URL, go there, otherwise complete
|
|
72
|
+
if (llmProvider === 'local' || llmProvider === 'anthropic') {
|
|
73
|
+
setStep('llm-base-url');
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
completeSetup();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const handleLlmBaseUrlSubmit = (value) => {
|
|
80
|
+
setLlmBaseUrl(value);
|
|
81
|
+
completeSetup();
|
|
82
|
+
};
|
|
83
|
+
const completeSetup = () => {
|
|
84
|
+
const config = {
|
|
85
|
+
url,
|
|
86
|
+
enableLlm: selectedFeatures.includes('llm') || selectedFeatures.includes('hallucination'),
|
|
87
|
+
enableChunking: selectedFeatures.includes('chunking'),
|
|
88
|
+
enableExtractability: selectedFeatures.includes('extractability'),
|
|
89
|
+
enableHallucination: selectedFeatures.includes('hallucination'),
|
|
90
|
+
};
|
|
91
|
+
if (config.enableLlm) {
|
|
92
|
+
config.llmProvider = llmProvider;
|
|
93
|
+
config.llmModel = llmModel;
|
|
94
|
+
config.llmApiKey = llmApiKey || undefined;
|
|
95
|
+
config.llmBaseUrl = llmBaseUrl || undefined;
|
|
96
|
+
}
|
|
97
|
+
onComplete(config);
|
|
98
|
+
// Exit the wizard app
|
|
99
|
+
exit();
|
|
100
|
+
};
|
|
101
|
+
const getModelPlaceholder = () => {
|
|
102
|
+
switch (llmProvider) {
|
|
103
|
+
case 'openai':
|
|
104
|
+
return 'gpt-4o-mini (default)';
|
|
105
|
+
case 'anthropic':
|
|
106
|
+
return 'claude-3-5-sonnet-20241022 (default)';
|
|
107
|
+
case 'ollama':
|
|
108
|
+
return 'qwen2.5:0.5b';
|
|
109
|
+
default:
|
|
110
|
+
return 'model-name';
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const getApiKeyPlaceholder = () => {
|
|
114
|
+
switch (llmProvider) {
|
|
115
|
+
case 'openai':
|
|
116
|
+
return 'sk-...';
|
|
117
|
+
case 'anthropic':
|
|
118
|
+
return 'sk-ant-...';
|
|
119
|
+
default:
|
|
120
|
+
return 'your-api-key';
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
|
|
124
|
+
React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, marginBottom: 1 },
|
|
125
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "\uD83D\uDEA8 AI Lighthouse Setup Wizard"),
|
|
126
|
+
React.createElement(Text, { dimColor: true }, "Configure your audit settings")),
|
|
127
|
+
step === 'url' && (React.createElement(Box, { flexDirection: "column" },
|
|
128
|
+
React.createElement(Text, { bold: true }, "Enter the URL to audit:"),
|
|
129
|
+
React.createElement(Box, { marginTop: 1 },
|
|
130
|
+
React.createElement(Text, { color: "cyan" }, "URL: "),
|
|
131
|
+
React.createElement(TextInput, { value: url, onChange: setUrl, onSubmit: handleUrlSubmit, placeholder: "https://example.com" })),
|
|
132
|
+
React.createElement(Box, { marginTop: 1 },
|
|
133
|
+
React.createElement(Text, { dimColor: true }, "Press Enter to continue")))),
|
|
134
|
+
step === 'features' && (React.createElement(Box, { flexDirection: "column" },
|
|
135
|
+
React.createElement(Text, { bold: true }, "Select features to enable:"),
|
|
136
|
+
React.createElement(Box, { marginTop: 1 },
|
|
137
|
+
React.createElement(Text, { dimColor: true },
|
|
138
|
+
"Selected: ",
|
|
139
|
+
selectedFeatures.length === 0 ? 'None (basic audit only)' : selectedFeatures.map(f => {
|
|
140
|
+
const feature = featureOptions.find(opt => opt.value === f);
|
|
141
|
+
return feature?.label.split(' ')[0];
|
|
142
|
+
}).join(', '))),
|
|
143
|
+
React.createElement(Box, { marginTop: 1 },
|
|
144
|
+
React.createElement(SelectInput, { items: featureOptions.map(opt => ({
|
|
145
|
+
...opt,
|
|
146
|
+
label: selectedFeatures.includes(opt.value) && opt.value !== 'done'
|
|
147
|
+
? `✓ ${opt.label}`
|
|
148
|
+
: opt.label,
|
|
149
|
+
})), onSelect: handleFeatureSelect })),
|
|
150
|
+
React.createElement(Box, { marginTop: 1 },
|
|
151
|
+
React.createElement(Text, { dimColor: true }, "Use \u2191\u2193 to navigate, Enter to toggle/continue")))),
|
|
152
|
+
step === 'llm-provider' && (React.createElement(Box, { flexDirection: "column" },
|
|
153
|
+
React.createElement(Text, { bold: true }, "Select LLM provider:"),
|
|
154
|
+
React.createElement(Box, { marginTop: 1 },
|
|
155
|
+
React.createElement(SelectInput, { items: llmProviderOptions, onSelect: handleLlmProviderSelect })),
|
|
156
|
+
React.createElement(Box, { marginTop: 1 },
|
|
157
|
+
React.createElement(Text, { dimColor: true }, "Use \u2191\u2193 to navigate, Enter to select")))),
|
|
158
|
+
step === 'llm-model' && (React.createElement(Box, { flexDirection: "column" },
|
|
159
|
+
React.createElement(Text, { bold: true }, "Enter LLM model name:"),
|
|
160
|
+
React.createElement(Box, { marginTop: 1 },
|
|
161
|
+
React.createElement(Text, { color: "cyan" }, "Model: "),
|
|
162
|
+
React.createElement(TextInput, { value: llmModel, onChange: setLlmModel, onSubmit: handleLlmModelSubmit, placeholder: getModelPlaceholder() })),
|
|
163
|
+
React.createElement(Box, { marginTop: 1 },
|
|
164
|
+
React.createElement(Text, { dimColor: true }, "Leave empty for default, or press Enter to continue")))),
|
|
165
|
+
step === 'llm-api-key' && (React.createElement(Box, { flexDirection: "column" },
|
|
166
|
+
React.createElement(Text, { bold: true }, "Enter API key:"),
|
|
167
|
+
React.createElement(Box, { marginTop: 1 },
|
|
168
|
+
React.createElement(Text, { color: "cyan" }, "API Key: "),
|
|
169
|
+
React.createElement(TextInput, { value: llmApiKey, onChange: setLlmApiKey, onSubmit: handleLlmApiKeySubmit, placeholder: getApiKeyPlaceholder(), mask: "*" })),
|
|
170
|
+
React.createElement(Box, { marginTop: 1 },
|
|
171
|
+
React.createElement(Text, { dimColor: true }, "Your API key will not be stored")))),
|
|
172
|
+
step === 'llm-base-url' && (React.createElement(Box, { flexDirection: "column" },
|
|
173
|
+
React.createElement(Text, { bold: true }, "Enter API base URL (optional):"),
|
|
174
|
+
React.createElement(Box, { marginTop: 1 },
|
|
175
|
+
React.createElement(Text, { color: "cyan" }, "Base URL: "),
|
|
176
|
+
React.createElement(TextInput, { value: llmBaseUrl, onChange: setLlmBaseUrl, onSubmit: handleLlmBaseUrlSubmit, placeholder: llmProvider === 'ollama' ? 'http://localhost:11434' : 'https://api.example.com' })),
|
|
177
|
+
React.createElement(Box, { marginTop: 1 },
|
|
178
|
+
React.createElement(Text, { dimColor: true }, "Press Enter to continue (leave empty for default)"))))));
|
|
179
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
export const AIUnderstandingSection = ({ llm }) => {
|
|
4
|
+
if (!llm || Object.keys(llm).length === 0) {
|
|
5
|
+
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
|
|
6
|
+
React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1 },
|
|
7
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "\uD83D\uDCA1 Enable AI Understanding Analysis"),
|
|
8
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
9
|
+
React.createElement(Text, null, "LLM analysis is not enabled. To see AI understanding insights, run:"),
|
|
10
|
+
React.createElement(Box, { marginTop: 1, borderStyle: "single", paddingX: 1 },
|
|
11
|
+
React.createElement(Text, { color: "cyan" }, "ai-lighthouse audit [URL] --enable-llm --llm-provider openai --llm-api-key YOUR_KEY")),
|
|
12
|
+
React.createElement(Box, { marginTop: 1 },
|
|
13
|
+
React.createElement(Text, { dimColor: true }, "Supported providers: openai, anthropic, ollama, local"))))));
|
|
14
|
+
}
|
|
15
|
+
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
|
|
16
|
+
llm.pageType && (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 1, marginBottom: 1 },
|
|
17
|
+
React.createElement(Text, { bold: true, color: "magenta" }, "\uD83D\uDCC4 Inferred Page Type"),
|
|
18
|
+
React.createElement(Text, { color: "cyan" }, llm.pageType))),
|
|
19
|
+
llm.pageTypeInsights && llm.pageTypeInsights.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
20
|
+
React.createElement(Text, { bold: true, underline: true, color: "blue" }, "\uD83D\uDCA1 AI-Generated Insights"),
|
|
21
|
+
llm.pageTypeInsights.map((insight, idx) => (React.createElement(Box, { key: idx, marginTop: 0.5 },
|
|
22
|
+
React.createElement(Text, { color: "cyan" },
|
|
23
|
+
"\u2022 ",
|
|
24
|
+
insight)))))),
|
|
25
|
+
llm.summary && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
26
|
+
React.createElement(Text, { bold: true, underline: true }, "Summary"),
|
|
27
|
+
React.createElement(Box, { marginTop: 0.5 },
|
|
28
|
+
React.createElement(Text, null, llm.summary)))),
|
|
29
|
+
llm.keyTopics && llm.keyTopics.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
30
|
+
React.createElement(Text, { bold: true, underline: true }, "\uD83C\uDFF7\uFE0F Key Topics"),
|
|
31
|
+
React.createElement(Box, { marginTop: 0.5 },
|
|
32
|
+
React.createElement(Text, null, llm.keyTopics.map((t) => `[${t}]`).join(' '))))),
|
|
33
|
+
React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
34
|
+
React.createElement(Text, { bold: true, underline: true }, "Metadata"),
|
|
35
|
+
React.createElement(Box, { marginTop: 0.5, flexDirection: "column" },
|
|
36
|
+
llm.readingLevel && (React.createElement(Text, null,
|
|
37
|
+
React.createElement(Text, { bold: true }, "Reading Level:"),
|
|
38
|
+
" ",
|
|
39
|
+
llm.readingLevel.description)),
|
|
40
|
+
llm.sentiment && (React.createElement(Text, null,
|
|
41
|
+
React.createElement(Text, { bold: true }, "Sentiment:"),
|
|
42
|
+
" ",
|
|
43
|
+
llm.sentiment)),
|
|
44
|
+
llm.technicalDepth && (React.createElement(Text, null,
|
|
45
|
+
React.createElement(Text, { bold: true }, "Technical Depth:"),
|
|
46
|
+
" ",
|
|
47
|
+
llm.technicalDepth)))),
|
|
48
|
+
llm.topEntities && llm.topEntities.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
49
|
+
React.createElement(Text, { bold: true, underline: true }, "\uD83D\uDD0D Key Entities"),
|
|
50
|
+
llm.topEntities.slice(0, 5).map((entity, idx) => (React.createElement(Box, { key: idx, marginTop: 0.5 },
|
|
51
|
+
React.createElement(Text, null,
|
|
52
|
+
"\u2022 ",
|
|
53
|
+
React.createElement(Text, { bold: true }, entity.name),
|
|
54
|
+
' ',
|
|
55
|
+
React.createElement(Text, { dimColor: true },
|
|
56
|
+
"(",
|
|
57
|
+
entity.type,
|
|
58
|
+
") - ",
|
|
59
|
+
Math.round((entity.relevance || 0) * 100),
|
|
60
|
+
"% relevance"))))))),
|
|
61
|
+
llm.questions && llm.questions.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
62
|
+
React.createElement(Text, { bold: true, underline: true }, "\u2753 Questions AI Can Answer"),
|
|
63
|
+
llm.questions.slice(0, 5).map((q, idx) => (React.createElement(Box, { key: idx, marginTop: 0.5 },
|
|
64
|
+
React.createElement(Text, null,
|
|
65
|
+
idx + 1,
|
|
66
|
+
". ",
|
|
67
|
+
React.createElement(Text, { dimColor: true },
|
|
68
|
+
"[",
|
|
69
|
+
q.difficulty.toUpperCase(),
|
|
70
|
+
"]"),
|
|
71
|
+
" ",
|
|
72
|
+
q.question)))))),
|
|
73
|
+
llm.suggestedFAQ && llm.suggestedFAQ.length > 0 && (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1 },
|
|
74
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "\uD83D\uDCA1 Suggested FAQs"),
|
|
75
|
+
llm.suggestedFAQ
|
|
76
|
+
.filter((f) => f.importance === 'high')
|
|
77
|
+
.slice(0, 3)
|
|
78
|
+
.map((faq, idx) => (React.createElement(Box, { key: idx, flexDirection: "column", marginTop: 1 },
|
|
79
|
+
React.createElement(Text, null,
|
|
80
|
+
React.createElement(Text, { bold: true }, "Q:"),
|
|
81
|
+
" ",
|
|
82
|
+
faq.question),
|
|
83
|
+
React.createElement(Text, { dimColor: true },
|
|
84
|
+
React.createElement(Text, { bold: true }, "A:"),
|
|
85
|
+
" ",
|
|
86
|
+
faq.suggestedAnswer))))))));
|
|
87
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
export const HallucinationSection = ({ hallucinationReport }) => {
|
|
4
|
+
if (!hallucinationReport || Object.keys(hallucinationReport).length === 0) {
|
|
5
|
+
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
|
|
6
|
+
React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1 },
|
|
7
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "\uD83D\uDCA1 Enable Hallucination Detection"),
|
|
8
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
9
|
+
React.createElement(Text, null, "Hallucination detection is not enabled. To see risk assessment, run:"),
|
|
10
|
+
React.createElement(Box, { marginTop: 1, borderStyle: "single", paddingX: 1 },
|
|
11
|
+
React.createElement(Text, { color: "cyan" }, "ai-lighthouse audit [URL] --enable-hallucination --enable-llm --llm-provider openai --llm-api-key YOUR_KEY")),
|
|
12
|
+
React.createElement(Box, { marginTop: 1 },
|
|
13
|
+
React.createElement(Text, { dimColor: true }, "Note: Hallucination detection requires LLM analysis to be enabled"))))));
|
|
14
|
+
}
|
|
15
|
+
const riskScore = hallucinationReport.hallucinationRiskScore;
|
|
16
|
+
const getRiskColor = (score) => {
|
|
17
|
+
if (score >= 70)
|
|
18
|
+
return 'red';
|
|
19
|
+
if (score >= 40)
|
|
20
|
+
return 'yellow';
|
|
21
|
+
return 'green';
|
|
22
|
+
};
|
|
23
|
+
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
|
|
24
|
+
React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: getRiskColor(riskScore), paddingX: 2, paddingY: 1, marginBottom: 1 },
|
|
25
|
+
React.createElement(Text, { bold: true, color: getRiskColor(riskScore) }, "\u26A0\uFE0F Hallucination Risk Assessment"),
|
|
26
|
+
React.createElement(Box, { marginTop: 1 },
|
|
27
|
+
React.createElement(Text, null,
|
|
28
|
+
React.createElement(Text, { bold: true }, "Risk Score: "),
|
|
29
|
+
React.createElement(Text, { bold: true, color: getRiskColor(riskScore) },
|
|
30
|
+
riskScore,
|
|
31
|
+
"/100")))),
|
|
32
|
+
hallucinationReport.factCheckSummary && (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "blue", paddingX: 2, paddingY: 1, marginBottom: 1 },
|
|
33
|
+
React.createElement(Text, { bold: true, color: "blue" }, "\uD83D\uDCCA Fact Check Summary"),
|
|
34
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
35
|
+
React.createElement(Text, null,
|
|
36
|
+
React.createElement(Text, { bold: true }, "Total Facts:"),
|
|
37
|
+
" ",
|
|
38
|
+
hallucinationReport.factCheckSummary.totalFacts),
|
|
39
|
+
React.createElement(Text, null,
|
|
40
|
+
React.createElement(Text, { bold: true, color: "green" }, "Verified:"),
|
|
41
|
+
" ",
|
|
42
|
+
hallucinationReport.factCheckSummary.verifiedFacts),
|
|
43
|
+
React.createElement(Text, null,
|
|
44
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "Unverified:"),
|
|
45
|
+
" ",
|
|
46
|
+
hallucinationReport.factCheckSummary.unverifiedFacts),
|
|
47
|
+
React.createElement(Text, null,
|
|
48
|
+
React.createElement(Text, { bold: true, color: "red" }, "Contradictions:"),
|
|
49
|
+
" ",
|
|
50
|
+
hallucinationReport.factCheckSummary.contradictions),
|
|
51
|
+
hallucinationReport.factCheckSummary.ambiguities !== undefined && (React.createElement(Text, null,
|
|
52
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "Ambiguities:"),
|
|
53
|
+
" ",
|
|
54
|
+
hallucinationReport.factCheckSummary.ambiguities))))),
|
|
55
|
+
hallucinationReport.factCheckSummary?.unverifiedFacts > 0 && (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, marginBottom: 1 },
|
|
56
|
+
React.createElement(Text, { color: "yellow" }, "\uD83D\uDCA1 Tip: Add citations and links to verify claims and reduce AI hallucination risk"))),
|
|
57
|
+
hallucinationReport.triggers && hallucinationReport.triggers.length > 0 && (React.createElement(React.Fragment, null, hallucinationReport.triggers.filter((t) => t.severity === 'high' || t.severity === 'critical').length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
58
|
+
React.createElement(Text, { bold: true, underline: true, color: "red" }, "\uD83D\uDEA8 High-Risk Triggers"),
|
|
59
|
+
hallucinationReport.triggers
|
|
60
|
+
.filter((t) => t.severity === 'high' || t.severity === 'critical')
|
|
61
|
+
.slice(0, 5)
|
|
62
|
+
.map((trigger, idx) => (React.createElement(Box, { key: idx, flexDirection: "column", borderStyle: "single", borderColor: "red", paddingX: 1, paddingY: 0.5, marginTop: 1 },
|
|
63
|
+
React.createElement(Text, null,
|
|
64
|
+
idx + 1,
|
|
65
|
+
". ",
|
|
66
|
+
React.createElement(Text, { bold: true, color: "red" },
|
|
67
|
+
"[",
|
|
68
|
+
trigger.severity.toUpperCase(),
|
|
69
|
+
"]"),
|
|
70
|
+
" ",
|
|
71
|
+
trigger.type),
|
|
72
|
+
React.createElement(Text, { dimColor: true }, trigger.description),
|
|
73
|
+
trigger.confidence && (React.createElement(Text, { dimColor: true },
|
|
74
|
+
"Confidence: ",
|
|
75
|
+
Math.round(trigger.confidence * 100),
|
|
76
|
+
"%"))))))))),
|
|
77
|
+
hallucinationReport.recommendations && hallucinationReport.recommendations.length > 0 && (React.createElement(Box, { flexDirection: "column" },
|
|
78
|
+
React.createElement(Text, { bold: true, underline: true, color: "cyan" }, "\uD83D\uDCA1 Recommendations"),
|
|
79
|
+
hallucinationReport.recommendations.slice(0, 3).map((rec, idx) => (React.createElement(Box, { key: idx, marginTop: 0.5 },
|
|
80
|
+
React.createElement(Text, { color: "cyan" },
|
|
81
|
+
idx + 1,
|
|
82
|
+
". ",
|
|
83
|
+
rec))))))));
|
|
84
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
export const IssuesSection = ({ issues }) => {
|
|
4
|
+
const [selectedSeverity, setSelectedSeverity] = useState('all');
|
|
5
|
+
const [expandedIssue, setExpandedIssue] = useState(null);
|
|
6
|
+
const getSeverityColor = (severity) => {
|
|
7
|
+
if (severity === 'critical')
|
|
8
|
+
return 'red';
|
|
9
|
+
if (severity === 'high')
|
|
10
|
+
return 'magenta';
|
|
11
|
+
if (severity === 'medium')
|
|
12
|
+
return 'yellow';
|
|
13
|
+
return 'blue';
|
|
14
|
+
};
|
|
15
|
+
const getSeverityIcon = (severity) => {
|
|
16
|
+
if (severity === 'critical')
|
|
17
|
+
return '🔴';
|
|
18
|
+
if (severity === 'high')
|
|
19
|
+
return '🟠';
|
|
20
|
+
if (severity === 'medium')
|
|
21
|
+
return '🟡';
|
|
22
|
+
return '🔵';
|
|
23
|
+
};
|
|
24
|
+
// Group issues by severity
|
|
25
|
+
const grouped = {
|
|
26
|
+
critical: issues.filter(i => i.severity === 'critical'),
|
|
27
|
+
high: issues.filter(i => i.severity === 'high'),
|
|
28
|
+
medium: issues.filter(i => i.severity === 'medium'),
|
|
29
|
+
low: issues.filter(i => i.severity === 'low'),
|
|
30
|
+
};
|
|
31
|
+
const filteredIssues = selectedSeverity === 'all' ? issues : grouped[selectedSeverity] || [];
|
|
32
|
+
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
|
|
33
|
+
React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "blue", paddingX: 2, paddingY: 1, marginBottom: 1 },
|
|
34
|
+
React.createElement(Text, { bold: true, color: "blue" }, "\u26A0\uFE0F Issues Summary"),
|
|
35
|
+
React.createElement(Box, { marginTop: 1 },
|
|
36
|
+
React.createElement(Box, { flexDirection: "column", width: "50%" },
|
|
37
|
+
React.createElement(Text, null,
|
|
38
|
+
React.createElement(Text, { color: "red", bold: true }, "Critical:"),
|
|
39
|
+
' ',
|
|
40
|
+
grouped.critical.length),
|
|
41
|
+
React.createElement(Text, null,
|
|
42
|
+
React.createElement(Text, { color: "magenta", bold: true }, "High:"),
|
|
43
|
+
' ',
|
|
44
|
+
grouped.high.length)),
|
|
45
|
+
React.createElement(Box, { flexDirection: "column", width: "50%" },
|
|
46
|
+
React.createElement(Text, null,
|
|
47
|
+
React.createElement(Text, { color: "yellow", bold: true }, "Medium:"),
|
|
48
|
+
' ',
|
|
49
|
+
grouped.medium.length),
|
|
50
|
+
React.createElement(Text, null,
|
|
51
|
+
React.createElement(Text, { color: "blue", bold: true }, "Low:"),
|
|
52
|
+
' ',
|
|
53
|
+
grouped.low.length)))),
|
|
54
|
+
React.createElement(Box, { flexDirection: "column" },
|
|
55
|
+
React.createElement(Text, { bold: true, underline: true },
|
|
56
|
+
selectedSeverity === 'all' ? 'All Issues' : `${selectedSeverity.toUpperCase()} Issues`,
|
|
57
|
+
" (",
|
|
58
|
+
filteredIssues.length,
|
|
59
|
+
")"),
|
|
60
|
+
filteredIssues.map((issue, idx) => (React.createElement(Box, { key: idx, flexDirection: "column", borderStyle: "round", borderColor: getSeverityColor(issue.severity), paddingX: 2, paddingY: 1, marginTop: 1 },
|
|
61
|
+
React.createElement(Box, null,
|
|
62
|
+
React.createElement(Text, null,
|
|
63
|
+
getSeverityIcon(issue.severity),
|
|
64
|
+
' ',
|
|
65
|
+
React.createElement(Text, { bold: true, color: getSeverityColor(issue.severity) }, issue.message || issue.title))),
|
|
66
|
+
React.createElement(Box, { marginTop: 0.5 },
|
|
67
|
+
React.createElement(Text, { dimColor: true },
|
|
68
|
+
"Category: ",
|
|
69
|
+
issue.category,
|
|
70
|
+
" \u2022 Impact: ",
|
|
71
|
+
issue.impact)),
|
|
72
|
+
issue.evidence && (React.createElement(Box, { marginTop: 0.5 },
|
|
73
|
+
React.createElement(Text, { dimColor: true },
|
|
74
|
+
typeof issue.evidence === 'string'
|
|
75
|
+
? issue.evidence.substring(0, 100)
|
|
76
|
+
: Array.isArray(issue.evidence)
|
|
77
|
+
? issue.evidence.join(', ').substring(0, 100)
|
|
78
|
+
: '',
|
|
79
|
+
(typeof issue.evidence === 'string' ? issue.evidence : issue.evidence?.join(', ') || '').length > 100 ? '...' : ''))),
|
|
80
|
+
React.createElement(Box, { marginTop: 1, borderStyle: "single", borderColor: "cyan", paddingX: 1, paddingY: 0.5 },
|
|
81
|
+
React.createElement(Text, { color: "cyan" },
|
|
82
|
+
"\uD83D\uDCA1 Fix: ",
|
|
83
|
+
issue.suggested_fix || issue.remediation))))))));
|
|
84
|
+
};
|