viberag 0.1.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/LICENSE +661 -0
- package/README.md +219 -0
- package/dist/cli/__tests__/mcp-setup.test.d.ts +6 -0
- package/dist/cli/__tests__/mcp-setup.test.js +597 -0
- package/dist/cli/app.d.ts +2 -0
- package/dist/cli/app.js +238 -0
- package/dist/cli/commands/handlers.d.ts +57 -0
- package/dist/cli/commands/handlers.js +231 -0
- package/dist/cli/commands/index.d.ts +2 -0
- package/dist/cli/commands/index.js +2 -0
- package/dist/cli/commands/mcp-setup.d.ts +107 -0
- package/dist/cli/commands/mcp-setup.js +509 -0
- package/dist/cli/commands/useRagCommands.d.ts +23 -0
- package/dist/cli/commands/useRagCommands.js +180 -0
- package/dist/cli/components/CleanWizard.d.ts +17 -0
- package/dist/cli/components/CleanWizard.js +169 -0
- package/dist/cli/components/InitWizard.d.ts +20 -0
- package/dist/cli/components/InitWizard.js +370 -0
- package/dist/cli/components/McpSetupWizard.d.ts +37 -0
- package/dist/cli/components/McpSetupWizard.js +387 -0
- package/dist/cli/components/SearchResultsDisplay.d.ts +13 -0
- package/dist/cli/components/SearchResultsDisplay.js +130 -0
- package/dist/cli/components/WelcomeBanner.d.ts +10 -0
- package/dist/cli/components/WelcomeBanner.js +26 -0
- package/dist/cli/components/index.d.ts +1 -0
- package/dist/cli/components/index.js +1 -0
- package/dist/cli/data/mcp-editors.d.ts +80 -0
- package/dist/cli/data/mcp-editors.js +270 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +26 -0
- package/dist/cli-bundle.cjs +5269 -0
- package/dist/common/commands/terminalSetup.d.ts +2 -0
- package/dist/common/commands/terminalSetup.js +144 -0
- package/dist/common/components/CommandSuggestions.d.ts +9 -0
- package/dist/common/components/CommandSuggestions.js +20 -0
- package/dist/common/components/StaticWithResize.d.ts +23 -0
- package/dist/common/components/StaticWithResize.js +62 -0
- package/dist/common/components/StatusBar.d.ts +8 -0
- package/dist/common/components/StatusBar.js +64 -0
- package/dist/common/components/TextInput.d.ts +12 -0
- package/dist/common/components/TextInput.js +239 -0
- package/dist/common/components/index.d.ts +3 -0
- package/dist/common/components/index.js +3 -0
- package/dist/common/hooks/index.d.ts +4 -0
- package/dist/common/hooks/index.js +4 -0
- package/dist/common/hooks/useCommandHistory.d.ts +7 -0
- package/dist/common/hooks/useCommandHistory.js +51 -0
- package/dist/common/hooks/useCtrlC.d.ts +9 -0
- package/dist/common/hooks/useCtrlC.js +40 -0
- package/dist/common/hooks/useKittyKeyboard.d.ts +10 -0
- package/dist/common/hooks/useKittyKeyboard.js +26 -0
- package/dist/common/hooks/useStaticOutputBuffer.d.ts +31 -0
- package/dist/common/hooks/useStaticOutputBuffer.js +58 -0
- package/dist/common/hooks/useTerminalResize.d.ts +28 -0
- package/dist/common/hooks/useTerminalResize.js +51 -0
- package/dist/common/hooks/useTextBuffer.d.ts +13 -0
- package/dist/common/hooks/useTextBuffer.js +165 -0
- package/dist/common/index.d.ts +13 -0
- package/dist/common/index.js +17 -0
- package/dist/common/types.d.ts +162 -0
- package/dist/common/types.js +1 -0
- package/dist/mcp/index.d.ts +12 -0
- package/dist/mcp/index.js +66 -0
- package/dist/mcp/server.d.ts +25 -0
- package/dist/mcp/server.js +837 -0
- package/dist/mcp/watcher.d.ts +86 -0
- package/dist/mcp/watcher.js +334 -0
- package/dist/rag/__tests__/grammar-smoke.test.d.ts +9 -0
- package/dist/rag/__tests__/grammar-smoke.test.js +161 -0
- package/dist/rag/__tests__/helpers.d.ts +30 -0
- package/dist/rag/__tests__/helpers.js +67 -0
- package/dist/rag/__tests__/merkle.test.d.ts +5 -0
- package/dist/rag/__tests__/merkle.test.js +161 -0
- package/dist/rag/__tests__/metadata-extraction.test.d.ts +10 -0
- package/dist/rag/__tests__/metadata-extraction.test.js +202 -0
- package/dist/rag/__tests__/multi-language.test.d.ts +13 -0
- package/dist/rag/__tests__/multi-language.test.js +535 -0
- package/dist/rag/__tests__/rag.test.d.ts +10 -0
- package/dist/rag/__tests__/rag.test.js +311 -0
- package/dist/rag/__tests__/search-exhaustive.test.d.ts +9 -0
- package/dist/rag/__tests__/search-exhaustive.test.js +87 -0
- package/dist/rag/__tests__/search-filters.test.d.ts +10 -0
- package/dist/rag/__tests__/search-filters.test.js +250 -0
- package/dist/rag/__tests__/search-modes.test.d.ts +8 -0
- package/dist/rag/__tests__/search-modes.test.js +133 -0
- package/dist/rag/config/index.d.ts +61 -0
- package/dist/rag/config/index.js +111 -0
- package/dist/rag/constants.d.ts +41 -0
- package/dist/rag/constants.js +57 -0
- package/dist/rag/embeddings/fastembed.d.ts +62 -0
- package/dist/rag/embeddings/fastembed.js +124 -0
- package/dist/rag/embeddings/gemini.d.ts +26 -0
- package/dist/rag/embeddings/gemini.js +116 -0
- package/dist/rag/embeddings/index.d.ts +10 -0
- package/dist/rag/embeddings/index.js +9 -0
- package/dist/rag/embeddings/local-4b.d.ts +28 -0
- package/dist/rag/embeddings/local-4b.js +51 -0
- package/dist/rag/embeddings/local.d.ts +29 -0
- package/dist/rag/embeddings/local.js +119 -0
- package/dist/rag/embeddings/mistral.d.ts +22 -0
- package/dist/rag/embeddings/mistral.js +85 -0
- package/dist/rag/embeddings/openai.d.ts +22 -0
- package/dist/rag/embeddings/openai.js +85 -0
- package/dist/rag/embeddings/types.d.ts +37 -0
- package/dist/rag/embeddings/types.js +1 -0
- package/dist/rag/gitignore/index.d.ts +57 -0
- package/dist/rag/gitignore/index.js +178 -0
- package/dist/rag/index.d.ts +15 -0
- package/dist/rag/index.js +25 -0
- package/dist/rag/indexer/chunker.d.ts +129 -0
- package/dist/rag/indexer/chunker.js +1352 -0
- package/dist/rag/indexer/index.d.ts +6 -0
- package/dist/rag/indexer/index.js +6 -0
- package/dist/rag/indexer/indexer.d.ts +73 -0
- package/dist/rag/indexer/indexer.js +356 -0
- package/dist/rag/indexer/types.d.ts +68 -0
- package/dist/rag/indexer/types.js +47 -0
- package/dist/rag/logger/index.d.ts +20 -0
- package/dist/rag/logger/index.js +75 -0
- package/dist/rag/manifest/index.d.ts +50 -0
- package/dist/rag/manifest/index.js +97 -0
- package/dist/rag/merkle/diff.d.ts +26 -0
- package/dist/rag/merkle/diff.js +95 -0
- package/dist/rag/merkle/hash.d.ts +34 -0
- package/dist/rag/merkle/hash.js +165 -0
- package/dist/rag/merkle/index.d.ts +68 -0
- package/dist/rag/merkle/index.js +298 -0
- package/dist/rag/merkle/node.d.ts +51 -0
- package/dist/rag/merkle/node.js +69 -0
- package/dist/rag/search/filters.d.ts +21 -0
- package/dist/rag/search/filters.js +100 -0
- package/dist/rag/search/fts.d.ts +32 -0
- package/dist/rag/search/fts.js +61 -0
- package/dist/rag/search/hybrid.d.ts +17 -0
- package/dist/rag/search/hybrid.js +58 -0
- package/dist/rag/search/index.d.ts +89 -0
- package/dist/rag/search/index.js +367 -0
- package/dist/rag/search/types.d.ts +130 -0
- package/dist/rag/search/types.js +4 -0
- package/dist/rag/search/vector.d.ts +25 -0
- package/dist/rag/search/vector.js +44 -0
- package/dist/rag/storage/index.d.ts +92 -0
- package/dist/rag/storage/index.js +287 -0
- package/dist/rag/storage/lancedb-native.d.ts +7 -0
- package/dist/rag/storage/lancedb-native.js +10 -0
- package/dist/rag/storage/schema.d.ts +23 -0
- package/dist/rag/storage/schema.js +50 -0
- package/dist/rag/storage/types.d.ts +100 -0
- package/dist/rag/storage/types.js +68 -0
- package/package.json +67 -0
- package/scripts/check-node-version.js +37 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clean Wizard Component
|
|
3
|
+
*
|
|
4
|
+
* Interactive wizard for cleaning up VibeRAG from a project,
|
|
5
|
+
* including MCP server configurations.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
type Props = {
|
|
9
|
+
projectRoot: string;
|
|
10
|
+
viberagDir: string;
|
|
11
|
+
onComplete: () => void;
|
|
12
|
+
onCancel: () => void;
|
|
13
|
+
/** For outputting status messages */
|
|
14
|
+
addOutput: (type: 'system' | 'user', content: string) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare function CleanWizard({ projectRoot, viberagDir, onComplete, onCancel, addOutput, }: Props): React.ReactElement;
|
|
17
|
+
export default CleanWizard;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clean Wizard Component
|
|
3
|
+
*
|
|
4
|
+
* Interactive wizard for cleaning up VibeRAG from a project,
|
|
5
|
+
* including MCP server configurations.
|
|
6
|
+
*/
|
|
7
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
8
|
+
import { Box, Text, useInput } from 'ink';
|
|
9
|
+
import SelectInput from 'ink-select-input';
|
|
10
|
+
import { EDITORS } from '../data/mcp-editors.js';
|
|
11
|
+
import { findConfiguredEditors, removeViberagConfig, } from '../commands/mcp-setup.js';
|
|
12
|
+
const CONFIRM_ITEMS = [
|
|
13
|
+
{ label: 'Yes, remove everything', value: 'continue' },
|
|
14
|
+
{ label: 'Cancel', value: 'cancel' },
|
|
15
|
+
];
|
|
16
|
+
const MCP_CLEANUP_ITEMS = [
|
|
17
|
+
{ label: 'Yes, remove from MCP configs too (Recommended)', value: 'yes' },
|
|
18
|
+
{ label: 'No, keep MCP configurations', value: 'no' },
|
|
19
|
+
];
|
|
20
|
+
export function CleanWizard({ projectRoot, viberagDir, onComplete, onCancel, addOutput, }) {
|
|
21
|
+
const [step, setStep] = useState('confirm');
|
|
22
|
+
const [projectScopeEditors, setProjectScopeEditors] = useState([]);
|
|
23
|
+
const [globalScopeEditors, setGlobalScopeEditors] = useState([]);
|
|
24
|
+
const [mcpResults, setMcpResults] = useState([]);
|
|
25
|
+
const [viberagRemoved, setViberagRemoved] = useState(false);
|
|
26
|
+
// Handle Escape to cancel
|
|
27
|
+
useInput((input, key) => {
|
|
28
|
+
if (key.escape || (key.ctrl && input === 'c')) {
|
|
29
|
+
onCancel();
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
// Find configured editors on mount
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
findConfiguredEditors(projectRoot).then(({ projectScope, globalScope }) => {
|
|
35
|
+
setProjectScopeEditors(projectScope);
|
|
36
|
+
setGlobalScopeEditors(globalScope);
|
|
37
|
+
});
|
|
38
|
+
}, [projectRoot]);
|
|
39
|
+
// Handle confirmation
|
|
40
|
+
const handleConfirm = useCallback((action) => {
|
|
41
|
+
if (action === 'cancel') {
|
|
42
|
+
onCancel();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// If there are project-scope MCP configs, ask about cleanup
|
|
46
|
+
if (projectScopeEditors.length > 0) {
|
|
47
|
+
setStep('mcp-cleanup');
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// No MCP configs to clean, go straight to processing
|
|
51
|
+
setStep('processing');
|
|
52
|
+
performCleanup(false);
|
|
53
|
+
}
|
|
54
|
+
}, [projectScopeEditors, onCancel]);
|
|
55
|
+
// Perform the actual cleanup
|
|
56
|
+
const performCleanup = useCallback(async (cleanMcp) => {
|
|
57
|
+
const fs = await import('node:fs/promises');
|
|
58
|
+
// Remove .viberag directory
|
|
59
|
+
try {
|
|
60
|
+
await fs.rm(viberagDir, { recursive: true, force: true });
|
|
61
|
+
setViberagRemoved(true);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
addOutput('system', `Failed to remove ${viberagDir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
|
+
}
|
|
66
|
+
// Clean MCP configs if requested
|
|
67
|
+
if (cleanMcp) {
|
|
68
|
+
const results = [];
|
|
69
|
+
for (const editor of projectScopeEditors) {
|
|
70
|
+
const result = await removeViberagConfig(editor, projectRoot);
|
|
71
|
+
results.push(result);
|
|
72
|
+
}
|
|
73
|
+
setMcpResults(results);
|
|
74
|
+
}
|
|
75
|
+
setStep('summary');
|
|
76
|
+
}, [viberagDir, projectScopeEditors, projectRoot, addOutput]);
|
|
77
|
+
// Handle MCP cleanup choice
|
|
78
|
+
const handleMcpCleanup = useCallback((action) => {
|
|
79
|
+
setStep('processing');
|
|
80
|
+
performCleanup(action === 'yes');
|
|
81
|
+
}, [performCleanup]);
|
|
82
|
+
// Step: Confirm
|
|
83
|
+
if (step === 'confirm') {
|
|
84
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
85
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "Clean VibeRAG"),
|
|
86
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
87
|
+
React.createElement(Text, null, "This will remove:"),
|
|
88
|
+
React.createElement(Text, { dimColor: true },
|
|
89
|
+
" \u2022 ",
|
|
90
|
+
viberagDir,
|
|
91
|
+
"/ (index and config)"),
|
|
92
|
+
projectScopeEditors.length > 0 && (React.createElement(Text, { dimColor: true },
|
|
93
|
+
' ',
|
|
94
|
+
"\u2022 MCP configs (",
|
|
95
|
+
projectScopeEditors.map(e => e.name).join(', '),
|
|
96
|
+
")"))),
|
|
97
|
+
globalScopeEditors.length > 0 && (React.createElement(Box, { marginTop: 1 },
|
|
98
|
+
React.createElement(Text, { color: "blue" },
|
|
99
|
+
"Note: Global MCP configs (",
|
|
100
|
+
globalScopeEditors.map(e => e.name).join(', '),
|
|
101
|
+
")",
|
|
102
|
+
'\n',
|
|
103
|
+
"will NOT be modified. Remove manually if needed."))),
|
|
104
|
+
React.createElement(Box, { marginTop: 1 },
|
|
105
|
+
React.createElement(SelectInput, { items: CONFIRM_ITEMS, onSelect: item => handleConfirm(item.value) })),
|
|
106
|
+
React.createElement(Text, { dimColor: true }, "\u2191/\u2193 navigate, Enter select, Esc cancel")));
|
|
107
|
+
}
|
|
108
|
+
// Step: MCP Cleanup choice
|
|
109
|
+
if (step === 'mcp-cleanup') {
|
|
110
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
111
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "Remove from MCP configs?"),
|
|
112
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
113
|
+
React.createElement(Text, null, "Found viberag in these project MCP configs:"),
|
|
114
|
+
projectScopeEditors.map(editor => (React.createElement(Text, { key: editor.id, dimColor: true },
|
|
115
|
+
' ',
|
|
116
|
+
"\u2022 ",
|
|
117
|
+
editor.name,
|
|
118
|
+
" (",
|
|
119
|
+
editor.configPath,
|
|
120
|
+
")")))),
|
|
121
|
+
React.createElement(Box, { marginTop: 1 },
|
|
122
|
+
React.createElement(SelectInput, { items: MCP_CLEANUP_ITEMS, onSelect: item => handleMcpCleanup(item.value) })),
|
|
123
|
+
React.createElement(Text, { dimColor: true }, "\u2191/\u2193 navigate, Enter select, Esc cancel")));
|
|
124
|
+
}
|
|
125
|
+
// Step: Processing
|
|
126
|
+
if (step === 'processing') {
|
|
127
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
128
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "Cleaning..."),
|
|
129
|
+
React.createElement(Box, { marginTop: 1 },
|
|
130
|
+
React.createElement(Text, null, "Removing VibeRAG from project..."))));
|
|
131
|
+
}
|
|
132
|
+
// Step: Summary
|
|
133
|
+
if (step === 'summary') {
|
|
134
|
+
const successfulRemovals = mcpResults.filter(r => r.success);
|
|
135
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
136
|
+
React.createElement(Text, { bold: true, color: "green" }, "Clean Complete"),
|
|
137
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
138
|
+
viberagRemoved && (React.createElement(Text, null,
|
|
139
|
+
React.createElement(Text, { color: "green" }, "\u2713"),
|
|
140
|
+
" Removed ",
|
|
141
|
+
viberagDir,
|
|
142
|
+
"/")),
|
|
143
|
+
successfulRemovals.map(r => {
|
|
144
|
+
const editor = EDITORS.find(e => e.id === r.editor);
|
|
145
|
+
return (React.createElement(Text, { key: r.editor },
|
|
146
|
+
React.createElement(Text, { color: "green" }, "\u2713"),
|
|
147
|
+
" ",
|
|
148
|
+
editor?.name ?? r.editor,
|
|
149
|
+
React.createElement(Text, { dimColor: true },
|
|
150
|
+
" Updated ",
|
|
151
|
+
r.configPath)));
|
|
152
|
+
})),
|
|
153
|
+
globalScopeEditors.length > 0 && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
154
|
+
React.createElement(Text, { bold: true, color: "blue" }, "Manual cleanup needed:"),
|
|
155
|
+
globalScopeEditors.map(editor => (React.createElement(Text, { key: editor.id, dimColor: true },
|
|
156
|
+
"\u2022 ",
|
|
157
|
+
editor.name,
|
|
158
|
+
": Remove \"viberag\" from ",
|
|
159
|
+
editor.configPath))))),
|
|
160
|
+
React.createElement(Box, { marginTop: 1 },
|
|
161
|
+
React.createElement(Text, { dimColor: true }, "Run /init to reinitialize VibeRAG.")),
|
|
162
|
+
React.createElement(Box, { marginTop: 1 },
|
|
163
|
+
React.createElement(SelectInput, { items: [{ label: 'Done', value: 'done' }], onSelect: () => onComplete() }))));
|
|
164
|
+
}
|
|
165
|
+
// Fallback
|
|
166
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
167
|
+
React.createElement(Text, { color: "red" }, "Unknown wizard step")));
|
|
168
|
+
}
|
|
169
|
+
export default CleanWizard;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-step initialization wizard component.
|
|
3
|
+
* Guides users through embedding provider selection and API key configuration.
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import type { InitWizardConfig, EmbeddingProviderType } from '../../common/types.js';
|
|
7
|
+
type Props = {
|
|
8
|
+
step: number;
|
|
9
|
+
config: Partial<InitWizardConfig>;
|
|
10
|
+
isReinit: boolean;
|
|
11
|
+
/** Existing API key from previous config (for reinit flow) */
|
|
12
|
+
existingApiKey?: string;
|
|
13
|
+
/** Existing provider from previous config (for reinit flow) */
|
|
14
|
+
existingProvider?: EmbeddingProviderType;
|
|
15
|
+
onStepChange: (step: number, data?: Partial<InitWizardConfig>) => void;
|
|
16
|
+
onComplete: (config: InitWizardConfig) => void;
|
|
17
|
+
onCancel: () => void;
|
|
18
|
+
};
|
|
19
|
+
export declare function InitWizard({ step, config, isReinit, existingApiKey, existingProvider, onStepChange, onComplete, onCancel, }: Props): React.ReactElement;
|
|
20
|
+
export default InitWizard;
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-step initialization wizard component.
|
|
3
|
+
* Guides users through embedding provider selection and API key configuration.
|
|
4
|
+
*/
|
|
5
|
+
import React, { useState } from 'react';
|
|
6
|
+
import { Box, Text, useInput } from 'ink';
|
|
7
|
+
import SelectInput from 'ink-select-input';
|
|
8
|
+
/**
|
|
9
|
+
* Cloud providers that require API keys.
|
|
10
|
+
*/
|
|
11
|
+
const CLOUD_PROVIDERS = ['gemini', 'mistral', 'openai'];
|
|
12
|
+
function isCloudProvider(provider) {
|
|
13
|
+
return CLOUD_PROVIDERS.includes(provider);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* URLs to get API keys for each cloud provider.
|
|
17
|
+
*/
|
|
18
|
+
const API_KEY_URLS = {
|
|
19
|
+
gemini: 'https://aistudio.google.com',
|
|
20
|
+
mistral: 'https://console.mistral.ai/api-keys/',
|
|
21
|
+
openai: 'https://platform.openai.com/api-keys',
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Provider configurations with specs and pricing.
|
|
25
|
+
*/
|
|
26
|
+
const PROVIDER_CONFIG = {
|
|
27
|
+
local: {
|
|
28
|
+
name: 'Local',
|
|
29
|
+
model: 'Qwen3-0.6B Q8',
|
|
30
|
+
modelFull: 'Qwen/Qwen3-Embedding-0.6B',
|
|
31
|
+
dims: '1024',
|
|
32
|
+
context: '32K',
|
|
33
|
+
cost: 'Free',
|
|
34
|
+
note: '~700MB download, ~1.2GB RAM',
|
|
35
|
+
description: 'Offline, no API key needed',
|
|
36
|
+
},
|
|
37
|
+
'local-4b': {
|
|
38
|
+
name: 'Local 4B',
|
|
39
|
+
model: 'Qwen3-4B FP32',
|
|
40
|
+
modelFull: 'Qwen/Qwen3-Embedding-4B',
|
|
41
|
+
dims: '2560',
|
|
42
|
+
context: '32K',
|
|
43
|
+
cost: 'Free',
|
|
44
|
+
note: '~8GB download, ~8GB RAM',
|
|
45
|
+
description: 'Offline, better quality (+5 MTEB)',
|
|
46
|
+
},
|
|
47
|
+
gemini: {
|
|
48
|
+
name: 'Gemini',
|
|
49
|
+
model: 'gemini-embedding-001',
|
|
50
|
+
modelFull: 'gemini-embedding-001',
|
|
51
|
+
dims: '768',
|
|
52
|
+
context: '2K',
|
|
53
|
+
cost: 'Free tier',
|
|
54
|
+
note: 'API key required',
|
|
55
|
+
description: 'Fast API, free tier available',
|
|
56
|
+
},
|
|
57
|
+
mistral: {
|
|
58
|
+
name: 'Mistral',
|
|
59
|
+
model: 'codestral-embed',
|
|
60
|
+
modelFull: 'codestral-embed',
|
|
61
|
+
dims: '1024',
|
|
62
|
+
context: '8K',
|
|
63
|
+
cost: '$0.10/1M',
|
|
64
|
+
note: 'API key required',
|
|
65
|
+
description: 'Code-optimized embeddings',
|
|
66
|
+
},
|
|
67
|
+
openai: {
|
|
68
|
+
name: 'OpenAI',
|
|
69
|
+
model: 'text-embed-3-sm',
|
|
70
|
+
modelFull: 'text-embedding-3-small',
|
|
71
|
+
dims: '1536',
|
|
72
|
+
context: '8K',
|
|
73
|
+
cost: '$0.02/1M',
|
|
74
|
+
note: 'API key required',
|
|
75
|
+
description: 'Fast and reliable API',
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
// Simple provider options for selection
|
|
79
|
+
// Note: local-4b exists in code but hidden from UI - no transformers.js-compatible ONNX available yet
|
|
80
|
+
const PROVIDER_ITEMS = [
|
|
81
|
+
{ label: 'Local - Qwen3-0.6B Q8 (~700MB, ~1.2GB RAM)', value: 'local' },
|
|
82
|
+
// {label: 'Local 4B - Qwen3-4B FP32 (~8GB, ~8GB RAM)', value: 'local-4b'}, // No ONNX available
|
|
83
|
+
{ label: 'Gemini - gemini-embedding-001 (Free tier)', value: 'gemini' },
|
|
84
|
+
{ label: 'Mistral - codestral-embed', value: 'mistral' },
|
|
85
|
+
{ label: 'OpenAI - text-embedding-3-small', value: 'openai' },
|
|
86
|
+
];
|
|
87
|
+
/**
|
|
88
|
+
* Local model info.
|
|
89
|
+
* Note: 4B not shown - no transformers.js-compatible ONNX available yet
|
|
90
|
+
*/
|
|
91
|
+
const LOCAL_MODELS_DATA = [
|
|
92
|
+
{ Model: 'Qwen3-0.6B', Quant: 'Q8', Download: '~700MB', RAM: '~1.2GB' },
|
|
93
|
+
];
|
|
94
|
+
/**
|
|
95
|
+
* Frontier/API models - fastest, best quality.
|
|
96
|
+
*/
|
|
97
|
+
const FRONTIER_MODELS_DATA = [
|
|
98
|
+
{
|
|
99
|
+
Provider: 'Gemini',
|
|
100
|
+
Model: 'gemini-embedding-001',
|
|
101
|
+
Dims: '768',
|
|
102
|
+
Cost: 'Free tier',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
Provider: 'Mistral',
|
|
106
|
+
Model: 'codestral-embed',
|
|
107
|
+
Dims: '1024',
|
|
108
|
+
Cost: '$0.10/1M',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
Provider: 'OpenAI',
|
|
112
|
+
Model: 'text-embed-3-small',
|
|
113
|
+
Dims: '1536',
|
|
114
|
+
Cost: '$0.02/1M',
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
function SimpleTable({ data, columns, }) {
|
|
118
|
+
const dataRow = (row, isHeader = false) => {
|
|
119
|
+
const cells = columns.map(col => {
|
|
120
|
+
const value = row[col.key] ?? '';
|
|
121
|
+
return ' ' + value.padEnd(col.width - 1);
|
|
122
|
+
});
|
|
123
|
+
return (React.createElement(Text, { bold: isHeader, color: isHeader ? 'cyan' : undefined }, '│' + cells.join('│') + '│'));
|
|
124
|
+
};
|
|
125
|
+
// Header row from column keys
|
|
126
|
+
const headerRow = {};
|
|
127
|
+
for (const col of columns) {
|
|
128
|
+
headerRow[col.key] = col.key;
|
|
129
|
+
}
|
|
130
|
+
return (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
131
|
+
React.createElement(Text, null, '┌' + columns.map(col => '─'.repeat(col.width)).join('┬') + '┐'),
|
|
132
|
+
dataRow(headerRow, true),
|
|
133
|
+
React.createElement(Text, null, '├' + columns.map(col => '─'.repeat(col.width)).join('┼') + '┤'),
|
|
134
|
+
data.map((row, i) => (React.createElement(React.Fragment, { key: i }, dataRow(row)))),
|
|
135
|
+
React.createElement(Text, null, '└' + columns.map(col => '─'.repeat(col.width)).join('┴') + '┘')));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Comparison table component showing local and frontier models.
|
|
139
|
+
*/
|
|
140
|
+
function ComparisonTable() {
|
|
141
|
+
const localColumns = [
|
|
142
|
+
{ key: 'Model', width: 12 },
|
|
143
|
+
{ key: 'Quant', width: 6 },
|
|
144
|
+
{ key: 'Download', width: 10 },
|
|
145
|
+
{ key: 'RAM', width: 8 },
|
|
146
|
+
];
|
|
147
|
+
const frontierColumns = [
|
|
148
|
+
{ key: 'Provider', width: 10 },
|
|
149
|
+
{ key: 'Model', width: 20 },
|
|
150
|
+
{ key: 'Dims', width: 6 },
|
|
151
|
+
{ key: 'Cost', width: 11 },
|
|
152
|
+
];
|
|
153
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
154
|
+
React.createElement(Box, { marginTop: 1 },
|
|
155
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "Local Models"),
|
|
156
|
+
React.createElement(Text, { dimColor: true }, " - Offline, Slower, Free")),
|
|
157
|
+
React.createElement(SimpleTable, { data: LOCAL_MODELS_DATA, columns: localColumns }),
|
|
158
|
+
React.createElement(Text, { dimColor: true, italic: true }, "* Initial indexing may take time. Future updates are incremental."),
|
|
159
|
+
React.createElement(Box, { marginTop: 1 },
|
|
160
|
+
React.createElement(Text, { bold: true, color: "green" }, "Frontier Models"),
|
|
161
|
+
React.createElement(Text, { dimColor: true }, " - Fastest, Best Quality")),
|
|
162
|
+
React.createElement(SimpleTable, { data: FRONTIER_MODELS_DATA, columns: frontierColumns })));
|
|
163
|
+
}
|
|
164
|
+
// Reinit confirmation options
|
|
165
|
+
const REINIT_ITEMS = [
|
|
166
|
+
{ label: 'Continue (reinitialize)', value: 'continue' },
|
|
167
|
+
{ label: 'Cancel', value: 'cancel' },
|
|
168
|
+
];
|
|
169
|
+
// Final confirmation options
|
|
170
|
+
const CONFIRM_ITEMS = [
|
|
171
|
+
{ label: 'Initialize', value: 'init' },
|
|
172
|
+
{ label: 'Cancel', value: 'cancel' },
|
|
173
|
+
];
|
|
174
|
+
// API key action options for reinit
|
|
175
|
+
const API_KEY_ACTION_ITEMS = [
|
|
176
|
+
{ label: 'Keep existing API key', value: 'keep' },
|
|
177
|
+
{ label: 'Enter new API key', value: 'new' },
|
|
178
|
+
];
|
|
179
|
+
/**
|
|
180
|
+
* Simple text input component for API key entry.
|
|
181
|
+
*/
|
|
182
|
+
function ApiKeyInputStep({ providerName, apiKeyInput, setApiKeyInput, onSubmit, }) {
|
|
183
|
+
// Handle text input (supports paste)
|
|
184
|
+
useInput((input, key) => {
|
|
185
|
+
if (key.return) {
|
|
186
|
+
onSubmit(apiKeyInput);
|
|
187
|
+
}
|
|
188
|
+
else if (key.backspace || key.delete) {
|
|
189
|
+
setApiKeyInput(apiKeyInput.slice(0, -1));
|
|
190
|
+
}
|
|
191
|
+
else if (!key.ctrl && !key.meta && input) {
|
|
192
|
+
// Add printable characters (supports multi-char paste)
|
|
193
|
+
setApiKeyInput(apiKeyInput + input);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
return (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
197
|
+
React.createElement(Text, null,
|
|
198
|
+
"Enter your ",
|
|
199
|
+
providerName,
|
|
200
|
+
" API key:"),
|
|
201
|
+
React.createElement(Box, { marginTop: 1 },
|
|
202
|
+
React.createElement(Text, { color: "blue" }, "> "),
|
|
203
|
+
React.createElement(Text, null, apiKeyInput),
|
|
204
|
+
React.createElement(Text, { color: "gray" }, "\u2588")),
|
|
205
|
+
apiKeyInput.trim() === '' && (React.createElement(Text, { color: "yellow", dimColor: true }, "API key is required")),
|
|
206
|
+
React.createElement(Text, { dimColor: true }, "Press Enter to continue")));
|
|
207
|
+
}
|
|
208
|
+
export function InitWizard({ step, config, isReinit, existingApiKey, existingProvider, onStepChange, onComplete, onCancel, }) {
|
|
209
|
+
// State for API key input
|
|
210
|
+
const [apiKeyInput, setApiKeyInput] = useState('');
|
|
211
|
+
const [apiKeyAction, setApiKeyAction] = useState(null);
|
|
212
|
+
// Handle Escape to cancel
|
|
213
|
+
useInput((input, key) => {
|
|
214
|
+
if (key.escape || (key.ctrl && input === 'c')) {
|
|
215
|
+
onCancel();
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
// Normalize props with defensive defaults
|
|
219
|
+
const normalizedStep = typeof step === 'number' && !isNaN(step) ? step : 0;
|
|
220
|
+
const normalizedIsReinit = typeof isReinit === 'boolean' ? isReinit : false;
|
|
221
|
+
// Check if current provider is a cloud provider
|
|
222
|
+
const currentProvider = config.provider ?? 'local';
|
|
223
|
+
const needsApiKey = isCloudProvider(currentProvider);
|
|
224
|
+
// Check if we have an existing API key for the same provider
|
|
225
|
+
const hasExistingKeyForProvider = existingApiKey && existingProvider === currentProvider;
|
|
226
|
+
// Compute effective step (adjusted for non-reinit flow)
|
|
227
|
+
// Steps: 0=reinit confirm, 1=provider select, 2=api key (cloud only), 3=final confirm
|
|
228
|
+
const effectiveStep = normalizedIsReinit
|
|
229
|
+
? normalizedStep
|
|
230
|
+
: normalizedStep + 1;
|
|
231
|
+
// Step 0 (reinit only): Confirmation
|
|
232
|
+
if (normalizedIsReinit && normalizedStep === 0) {
|
|
233
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
234
|
+
React.createElement(Text, { bold: true, color: "yellow" }, "Viberag is already initialized"),
|
|
235
|
+
React.createElement(Text, { dimColor: true }, "This will reset your configuration and reindex the codebase."),
|
|
236
|
+
React.createElement(Box, { marginTop: 1 },
|
|
237
|
+
React.createElement(SelectInput, { items: REINIT_ITEMS, onSelect: item => {
|
|
238
|
+
if (item.value === 'continue') {
|
|
239
|
+
onStepChange(1);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
onCancel();
|
|
243
|
+
}
|
|
244
|
+
} })),
|
|
245
|
+
React.createElement(Text, { dimColor: true },
|
|
246
|
+
'\n',
|
|
247
|
+
"Use arrow keys to navigate, Enter to select, Esc to cancel")));
|
|
248
|
+
}
|
|
249
|
+
// Step 1: Provider selection
|
|
250
|
+
// Fresh init: step=0 (effectiveStep=1), Reinit: step=1 (effectiveStep=1)
|
|
251
|
+
if (effectiveStep === 1) {
|
|
252
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
253
|
+
React.createElement(Text, { bold: true }, "Choose Embedding Provider"),
|
|
254
|
+
React.createElement(Box, { marginTop: 1 },
|
|
255
|
+
React.createElement(SelectInput, { items: PROVIDER_ITEMS, onSelect: item => {
|
|
256
|
+
// Reset API key state when provider changes
|
|
257
|
+
setApiKeyInput('');
|
|
258
|
+
setApiKeyAction(null);
|
|
259
|
+
// Use relative increment: step + 1
|
|
260
|
+
onStepChange(normalizedStep + 1, { provider: item.value });
|
|
261
|
+
} })),
|
|
262
|
+
React.createElement(ComparisonTable, null),
|
|
263
|
+
React.createElement(Box, { marginTop: 1 },
|
|
264
|
+
React.createElement(Text, { dimColor: true }, "\u2191/\u2193 navigate, Enter select, Esc cancel"))));
|
|
265
|
+
}
|
|
266
|
+
// Step 2: API Key input (cloud providers only)
|
|
267
|
+
// For local providers, skip to step 3 (confirmation)
|
|
268
|
+
if (effectiveStep === 2) {
|
|
269
|
+
// Skip API key step for local providers
|
|
270
|
+
if (!needsApiKey) {
|
|
271
|
+
// Auto-advance to confirmation
|
|
272
|
+
onStepChange(normalizedStep + 1);
|
|
273
|
+
return (React.createElement(Box, null,
|
|
274
|
+
React.createElement(Text, { dimColor: true }, "Loading...")));
|
|
275
|
+
}
|
|
276
|
+
const provider = currentProvider;
|
|
277
|
+
const info = PROVIDER_CONFIG[provider];
|
|
278
|
+
const apiKeyUrl = API_KEY_URLS[provider];
|
|
279
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
280
|
+
React.createElement(Text, { bold: true },
|
|
281
|
+
"Configure ",
|
|
282
|
+
info.name,
|
|
283
|
+
" API Key"),
|
|
284
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
285
|
+
React.createElement(Text, null,
|
|
286
|
+
"Get your API key:",
|
|
287
|
+
' ',
|
|
288
|
+
React.createElement(Text, { color: "cyan", underline: true }, apiKeyUrl))),
|
|
289
|
+
hasExistingKeyForProvider && apiKeyAction === null ? (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
290
|
+
React.createElement(Text, { color: "green" },
|
|
291
|
+
"An API key is already configured for ",
|
|
292
|
+
info.name,
|
|
293
|
+
"."),
|
|
294
|
+
React.createElement(Box, { marginTop: 1 },
|
|
295
|
+
React.createElement(SelectInput, { items: API_KEY_ACTION_ITEMS, onSelect: item => {
|
|
296
|
+
if (item.value === 'keep') {
|
|
297
|
+
// Keep existing key, advance to confirmation
|
|
298
|
+
onStepChange(normalizedStep + 1, { apiKey: existingApiKey });
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
// Show text input for new key
|
|
302
|
+
setApiKeyAction('new');
|
|
303
|
+
}
|
|
304
|
+
} })))) : (React.createElement(ApiKeyInputStep, { providerName: info.name, apiKeyInput: apiKeyInput, setApiKeyInput: setApiKeyInput, onSubmit: key => {
|
|
305
|
+
if (key.trim()) {
|
|
306
|
+
onStepChange(normalizedStep + 1, { apiKey: key.trim() });
|
|
307
|
+
}
|
|
308
|
+
} })),
|
|
309
|
+
React.createElement(Box, { marginTop: 1 },
|
|
310
|
+
React.createElement(Text, { dimColor: true }, "Esc to cancel"))));
|
|
311
|
+
}
|
|
312
|
+
// Step 3: Confirmation
|
|
313
|
+
if (effectiveStep === 3) {
|
|
314
|
+
const provider = config.provider ?? 'gemini';
|
|
315
|
+
const info = PROVIDER_CONFIG[provider];
|
|
316
|
+
const hasApiKey = !!config.apiKey;
|
|
317
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
318
|
+
React.createElement(Text, { bold: true }, "Ready to Initialize"),
|
|
319
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
320
|
+
React.createElement(Text, null,
|
|
321
|
+
React.createElement(Text, { dimColor: true }, "Provider: "),
|
|
322
|
+
React.createElement(Text, { bold: true }, info.name),
|
|
323
|
+
React.createElement(Text, { dimColor: true },
|
|
324
|
+
" - ",
|
|
325
|
+
info.description)),
|
|
326
|
+
React.createElement(Text, null,
|
|
327
|
+
React.createElement(Text, { dimColor: true }, "Model: "),
|
|
328
|
+
info.modelFull),
|
|
329
|
+
React.createElement(Text, null,
|
|
330
|
+
React.createElement(Text, { dimColor: true }, "Specs: "),
|
|
331
|
+
info.dims,
|
|
332
|
+
"d, ",
|
|
333
|
+
info.context,
|
|
334
|
+
" context"),
|
|
335
|
+
React.createElement(Text, null,
|
|
336
|
+
React.createElement(Text, { dimColor: true }, "Cost: "),
|
|
337
|
+
info.cost),
|
|
338
|
+
isCloudProvider(provider) && (React.createElement(Text, null,
|
|
339
|
+
React.createElement(Text, { dimColor: true }, "API Key: "),
|
|
340
|
+
hasApiKey ? (React.createElement(Text, { color: "green" }, "Configured")) : (React.createElement(Text, { color: "red" }, "Missing")))),
|
|
341
|
+
React.createElement(Text, null,
|
|
342
|
+
React.createElement(Text, { dimColor: true }, "Directory:"),
|
|
343
|
+
" .viberag/")),
|
|
344
|
+
React.createElement(Box, { marginTop: 1 },
|
|
345
|
+
React.createElement(SelectInput, { items: CONFIRM_ITEMS, onSelect: item => {
|
|
346
|
+
if (item.value === 'init') {
|
|
347
|
+
onComplete({ provider, apiKey: config.apiKey });
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
onCancel();
|
|
351
|
+
}
|
|
352
|
+
} })),
|
|
353
|
+
React.createElement(Text, { dimColor: true }, "\u2191/\u2193 navigate, Enter select, Esc cancel")));
|
|
354
|
+
}
|
|
355
|
+
// Fallback (shouldn't happen)
|
|
356
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
357
|
+
React.createElement(Text, { color: "red" }, "Unknown wizard step"),
|
|
358
|
+
React.createElement(Text, { dimColor: true },
|
|
359
|
+
"Debug: step=",
|
|
360
|
+
step,
|
|
361
|
+
" (",
|
|
362
|
+
typeof step,
|
|
363
|
+
"), isReinit=",
|
|
364
|
+
String(isReinit),
|
|
365
|
+
" (",
|
|
366
|
+
typeof isReinit,
|
|
367
|
+
"), effectiveStep=",
|
|
368
|
+
effectiveStep)));
|
|
369
|
+
}
|
|
370
|
+
export default InitWizard;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Setup Wizard Component
|
|
3
|
+
*
|
|
4
|
+
* Multi-step wizard for configuring VibeRAG's MCP server
|
|
5
|
+
* across various AI coding tools.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { type EditorId } from '../data/mcp-editors.js';
|
|
9
|
+
import { type McpSetupResult } from '../commands/mcp-setup.js';
|
|
10
|
+
/**
|
|
11
|
+
* Wizard step types.
|
|
12
|
+
*/
|
|
13
|
+
export type McpSetupStep = 'prompt' | 'select' | 'configure' | 'summary';
|
|
14
|
+
/**
|
|
15
|
+
* Wizard configuration.
|
|
16
|
+
*/
|
|
17
|
+
export interface McpSetupWizardConfig {
|
|
18
|
+
selectedEditors: EditorId[];
|
|
19
|
+
results: McpSetupResult[];
|
|
20
|
+
}
|
|
21
|
+
type Props = {
|
|
22
|
+
step: McpSetupStep;
|
|
23
|
+
config: Partial<McpSetupWizardConfig>;
|
|
24
|
+
projectRoot: string;
|
|
25
|
+
/** Whether this is shown after /init (shows prompt step) */
|
|
26
|
+
showPrompt: boolean;
|
|
27
|
+
onStepChange: (step: McpSetupStep, data?: Partial<McpSetupWizardConfig>) => void;
|
|
28
|
+
onComplete: (config: McpSetupWizardConfig) => void;
|
|
29
|
+
onCancel: () => void;
|
|
30
|
+
/** For outputting instructions to console */
|
|
31
|
+
addOutput?: (type: 'system' | 'user', content: string) => void;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* MCP Setup Wizard main component.
|
|
35
|
+
*/
|
|
36
|
+
export declare function McpSetupWizard({ step, config, projectRoot, showPrompt, onStepChange, onComplete, onCancel, addOutput, }: Props): React.ReactElement;
|
|
37
|
+
export default McpSetupWizard;
|