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
package/dist/cli/app.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { Box, Text, useStdout } from 'ink';
|
|
4
|
+
// Common infrastructure
|
|
5
|
+
import TextInput from '../common/components/TextInput.js';
|
|
6
|
+
import StatusBar from '../common/components/StatusBar.js';
|
|
7
|
+
import StaticWithResize from '../common/components/StaticWithResize.js';
|
|
8
|
+
import { useCtrlC } from '../common/hooks/useCtrlC.js';
|
|
9
|
+
import { useCommandHistory } from '../common/hooks/useCommandHistory.js';
|
|
10
|
+
import { useKittyKeyboard } from '../common/hooks/useKittyKeyboard.js';
|
|
11
|
+
// CLI-specific components and commands
|
|
12
|
+
import WelcomeBanner from './components/WelcomeBanner.js';
|
|
13
|
+
import SearchResultsDisplay from './components/SearchResultsDisplay.js';
|
|
14
|
+
import InitWizard from './components/InitWizard.js';
|
|
15
|
+
import McpSetupWizard from './components/McpSetupWizard.js';
|
|
16
|
+
import CleanWizard from './components/CleanWizard.js';
|
|
17
|
+
import { useRagCommands } from './commands/useRagCommands.js';
|
|
18
|
+
import { checkInitialized, loadIndexStats, runInit, runIndex, formatIndexStats, } from './commands/handlers.js';
|
|
19
|
+
import { getViberagDir, loadConfig } from '../rag/index.js';
|
|
20
|
+
const require = createRequire(import.meta.url);
|
|
21
|
+
// Path is relative from dist/ after compilation
|
|
22
|
+
const { version } = require('../../package.json');
|
|
23
|
+
// Available slash commands for autocomplete with descriptions
|
|
24
|
+
const COMMANDS = [
|
|
25
|
+
{ command: '/help', description: 'Show available commands' },
|
|
26
|
+
{ command: '/clear', description: 'Clear the screen' },
|
|
27
|
+
{
|
|
28
|
+
command: '/terminal-setup',
|
|
29
|
+
description: 'Configure Shift+Enter for VS Code',
|
|
30
|
+
},
|
|
31
|
+
{ command: '/init', description: 'Initialize Viberag (provider wizard)' },
|
|
32
|
+
{ command: '/index', description: 'Index the codebase' },
|
|
33
|
+
{ command: '/reindex', description: 'Force full reindex' },
|
|
34
|
+
{ command: '/search', description: 'Search codebase semantically' },
|
|
35
|
+
{ command: '/status', description: 'Show index statistics' },
|
|
36
|
+
{ command: '/mcp-setup', description: 'Configure MCP for AI tools' },
|
|
37
|
+
{ command: '/clean', description: 'Remove Viberag from project' },
|
|
38
|
+
{ command: '/quit', description: 'Exit the application' },
|
|
39
|
+
];
|
|
40
|
+
// Module-level counter for unique IDs
|
|
41
|
+
let nextId = 0;
|
|
42
|
+
export default function App() {
|
|
43
|
+
const [outputItems, setOutputItems] = useState([]);
|
|
44
|
+
const [appStatus, setAppStatus] = useState({ state: 'ready' });
|
|
45
|
+
// undefined = not loaded yet, null = loaded but no manifest, {...} = loaded with stats
|
|
46
|
+
const [indexStats, setIndexStats] = useState(undefined);
|
|
47
|
+
const [isInitialized, setIsInitialized] = useState(undefined);
|
|
48
|
+
const [wizardMode, setWizardMode] = useState({ active: false });
|
|
49
|
+
// Existing config for reinit flow (API key preservation)
|
|
50
|
+
const [existingApiKey, setExistingApiKey] = useState(undefined);
|
|
51
|
+
const [existingProvider, setExistingProvider] = useState(undefined);
|
|
52
|
+
const { stdout } = useStdout();
|
|
53
|
+
// Enable Kitty keyboard protocol for Shift+Enter support in iTerm2/Kitty/WezTerm
|
|
54
|
+
useKittyKeyboard();
|
|
55
|
+
// Get project root (current working directory)
|
|
56
|
+
const projectRoot = process.cwd();
|
|
57
|
+
// Check initialization status and load stats on mount
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
checkInitialized(projectRoot).then(async (initialized) => {
|
|
60
|
+
setIsInitialized(initialized);
|
|
61
|
+
// Load existing config for API key preservation during reinit
|
|
62
|
+
if (initialized) {
|
|
63
|
+
const config = await loadConfig(projectRoot);
|
|
64
|
+
setExistingApiKey(config.apiKey);
|
|
65
|
+
setExistingProvider(config.embeddingProvider);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
loadIndexStats(projectRoot).then(setIndexStats);
|
|
69
|
+
}, [projectRoot]);
|
|
70
|
+
// Command history
|
|
71
|
+
const { addToHistory, navigateUp, navigateDown, resetIndex } = useCommandHistory();
|
|
72
|
+
const addOutput = (type, content) => {
|
|
73
|
+
const id = String(nextId++);
|
|
74
|
+
setOutputItems(prev => [
|
|
75
|
+
...prev,
|
|
76
|
+
{
|
|
77
|
+
id,
|
|
78
|
+
type,
|
|
79
|
+
content,
|
|
80
|
+
},
|
|
81
|
+
]);
|
|
82
|
+
};
|
|
83
|
+
const addSearchResults = (data) => {
|
|
84
|
+
const id = String(nextId++);
|
|
85
|
+
setOutputItems(prev => [
|
|
86
|
+
...prev,
|
|
87
|
+
{
|
|
88
|
+
id,
|
|
89
|
+
type: 'search-results',
|
|
90
|
+
data,
|
|
91
|
+
},
|
|
92
|
+
]);
|
|
93
|
+
};
|
|
94
|
+
// Start the init wizard
|
|
95
|
+
const startInitWizard = useCallback((isReinit) => {
|
|
96
|
+
setWizardMode({ active: true, type: 'init', step: 0, config: {}, isReinit });
|
|
97
|
+
}, []);
|
|
98
|
+
// Start the MCP setup wizard
|
|
99
|
+
const startMcpSetupWizard = useCallback((showPrompt = false) => {
|
|
100
|
+
setWizardMode({
|
|
101
|
+
active: true,
|
|
102
|
+
type: 'mcp-setup',
|
|
103
|
+
step: showPrompt ? 'prompt' : 'select',
|
|
104
|
+
config: {},
|
|
105
|
+
showPrompt,
|
|
106
|
+
});
|
|
107
|
+
}, []);
|
|
108
|
+
// Start the clean wizard
|
|
109
|
+
const startCleanWizard = useCallback(() => {
|
|
110
|
+
setWizardMode({ active: true, type: 'clean' });
|
|
111
|
+
}, []);
|
|
112
|
+
// Handle clean wizard completion
|
|
113
|
+
const handleCleanWizardComplete = useCallback(() => {
|
|
114
|
+
setWizardMode({ active: false });
|
|
115
|
+
// Reset app state to uninitialized after cleaning
|
|
116
|
+
setIndexStats(null);
|
|
117
|
+
setIsInitialized(false);
|
|
118
|
+
}, []);
|
|
119
|
+
// Handle init wizard step changes
|
|
120
|
+
const handleInitWizardStep = useCallback((step, data) => {
|
|
121
|
+
setWizardMode(prev => prev.active && prev.type === 'init'
|
|
122
|
+
? { ...prev, step, config: { ...prev.config, ...data } }
|
|
123
|
+
: prev);
|
|
124
|
+
}, []);
|
|
125
|
+
// Handle MCP wizard step changes
|
|
126
|
+
const handleMcpWizardStep = useCallback((step, data) => {
|
|
127
|
+
setWizardMode(prev => prev.active && prev.type === 'mcp-setup'
|
|
128
|
+
? { ...prev, step, config: { ...prev.config, ...data } }
|
|
129
|
+
: prev);
|
|
130
|
+
}, []);
|
|
131
|
+
// Handle init wizard completion
|
|
132
|
+
const handleInitWizardComplete = useCallback(async (config) => {
|
|
133
|
+
// Close wizard first, then run init after a tick to ensure proper re-render
|
|
134
|
+
setWizardMode({ active: false });
|
|
135
|
+
// Wait for next tick so wizard unmounts before we add output
|
|
136
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
137
|
+
addOutput('system', 'Initializing Viberag...');
|
|
138
|
+
setAppStatus({ state: 'warning', message: 'Initializing...' });
|
|
139
|
+
try {
|
|
140
|
+
const result = await runInit(projectRoot, isInitialized ?? false, config);
|
|
141
|
+
addOutput('system', result);
|
|
142
|
+
setIsInitialized(true);
|
|
143
|
+
// Automatically start indexing after init
|
|
144
|
+
addOutput('system', 'Indexing codebase...');
|
|
145
|
+
setAppStatus({
|
|
146
|
+
state: 'indexing',
|
|
147
|
+
current: 0,
|
|
148
|
+
total: 0,
|
|
149
|
+
stage: 'Indexing',
|
|
150
|
+
});
|
|
151
|
+
const stats = await runIndex(projectRoot, true, (current, total, stage) => setAppStatus({ state: 'indexing', current, total, stage }));
|
|
152
|
+
addOutput('system', formatIndexStats(stats));
|
|
153
|
+
// Reload stats after indexing
|
|
154
|
+
const newStats = await loadIndexStats(projectRoot);
|
|
155
|
+
setIndexStats(newStats);
|
|
156
|
+
setAppStatus({ state: 'ready' });
|
|
157
|
+
// Prompt for MCP setup after init completes
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
159
|
+
startMcpSetupWizard(true); // showPrompt = true for post-init flow
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
addOutput('system', `Init failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
163
|
+
setAppStatus({ state: 'ready' });
|
|
164
|
+
}
|
|
165
|
+
}, [projectRoot, isInitialized, startMcpSetupWizard]);
|
|
166
|
+
// Handle MCP wizard completion
|
|
167
|
+
const handleMcpWizardComplete = useCallback((_config) => {
|
|
168
|
+
setWizardMode({ active: false });
|
|
169
|
+
// Results are already shown in the wizard summary
|
|
170
|
+
}, []);
|
|
171
|
+
// Handle wizard cancellation
|
|
172
|
+
const handleWizardCancel = useCallback(() => {
|
|
173
|
+
const wasInit = wizardMode.active && wizardMode.type === 'init';
|
|
174
|
+
setWizardMode({ active: false });
|
|
175
|
+
if (wasInit) {
|
|
176
|
+
addOutput('system', 'Initialization cancelled.');
|
|
177
|
+
}
|
|
178
|
+
// MCP wizard cancel just closes silently
|
|
179
|
+
}, [wizardMode]);
|
|
180
|
+
// Handle Ctrl+C with status message callback
|
|
181
|
+
const { handleCtrlC } = useCtrlC({
|
|
182
|
+
onFirstPress: () => setAppStatus({ state: 'warning', message: 'Press Ctrl+C again to quit' }),
|
|
183
|
+
onStatusClear: () => setAppStatus({ state: 'ready' }),
|
|
184
|
+
});
|
|
185
|
+
// Command handling (all logic consolidated in useRagCommands)
|
|
186
|
+
const { isCommand, executeCommand } = useRagCommands({
|
|
187
|
+
addOutput,
|
|
188
|
+
addSearchResults,
|
|
189
|
+
setAppStatus,
|
|
190
|
+
setIndexStats,
|
|
191
|
+
projectRoot,
|
|
192
|
+
stdout,
|
|
193
|
+
startInitWizard,
|
|
194
|
+
startMcpSetupWizard,
|
|
195
|
+
startCleanWizard,
|
|
196
|
+
isInitialized: isInitialized ?? false,
|
|
197
|
+
});
|
|
198
|
+
const handleSubmit = (text) => {
|
|
199
|
+
if (!text.trim())
|
|
200
|
+
return;
|
|
201
|
+
// Add to history
|
|
202
|
+
addToHistory(text);
|
|
203
|
+
// Always show user input
|
|
204
|
+
addOutput('user', text);
|
|
205
|
+
if (isCommand(text)) {
|
|
206
|
+
executeCommand(text);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Placeholder for actual processing - just echo for now
|
|
210
|
+
addOutput('system', `Echo: ${text}`);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
// Prepend welcome banner as first static item (only after BOTH init status AND stats are loaded)
|
|
214
|
+
// This prevents race condition where banner shows stale "Run /index" while stats are loading
|
|
215
|
+
const startupLoaded = isInitialized !== undefined && indexStats !== undefined;
|
|
216
|
+
const staticItems = [
|
|
217
|
+
...(startupLoaded
|
|
218
|
+
? [{ id: 'welcome', type: 'welcome', content: '' }]
|
|
219
|
+
: []),
|
|
220
|
+
...outputItems,
|
|
221
|
+
];
|
|
222
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
223
|
+
React.createElement(StaticWithResize, { items: staticItems }, item => {
|
|
224
|
+
if (item.type === 'welcome') {
|
|
225
|
+
return (React.createElement(Box, { key: item.id, marginBottom: 1 },
|
|
226
|
+
React.createElement(WelcomeBanner, { version: version, cwd: process.cwd(), isInitialized: isInitialized, indexStats: indexStats })));
|
|
227
|
+
}
|
|
228
|
+
if (item.type === 'search-results') {
|
|
229
|
+
return (React.createElement(Box, { key: item.id, paddingX: 1, marginBottom: 1 },
|
|
230
|
+
React.createElement(SearchResultsDisplay, { data: item.data })));
|
|
231
|
+
}
|
|
232
|
+
return (React.createElement(Box, { key: item.id, paddingX: 1, marginBottom: 1 }, item.type === 'user' ? (React.createElement(Text, { color: "cyan" },
|
|
233
|
+
"> ",
|
|
234
|
+
item.content)) : (React.createElement(Text, null, item.content))));
|
|
235
|
+
}),
|
|
236
|
+
React.createElement(StatusBar, { status: appStatus, stats: indexStats }),
|
|
237
|
+
wizardMode.active && wizardMode.type === 'init' ? (React.createElement(InitWizard, { step: wizardMode.step, config: wizardMode.config, isReinit: wizardMode.isReinit, existingApiKey: existingApiKey, existingProvider: existingProvider, onStepChange: handleInitWizardStep, onComplete: handleInitWizardComplete, onCancel: handleWizardCancel })) : wizardMode.active && wizardMode.type === 'mcp-setup' ? (React.createElement(McpSetupWizard, { step: wizardMode.step, config: wizardMode.config, projectRoot: projectRoot, showPrompt: wizardMode.showPrompt, onStepChange: handleMcpWizardStep, onComplete: handleMcpWizardComplete, onCancel: handleWizardCancel, addOutput: addOutput })) : wizardMode.active && wizardMode.type === 'clean' ? (React.createElement(CleanWizard, { projectRoot: projectRoot, viberagDir: getViberagDir(projectRoot), onComplete: handleCleanWizardComplete, onCancel: handleWizardCancel, addOutput: addOutput })) : (React.createElement(TextInput, { onSubmit: handleSubmit, onCtrlC: handleCtrlC, commands: COMMANDS, navigateHistoryUp: navigateUp, navigateHistoryDown: navigateDown, resetHistoryIndex: resetIndex }))));
|
|
238
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RAG commands for the CLI.
|
|
3
|
+
*/
|
|
4
|
+
import { type IndexStats, type SearchResults } from '../../rag/index.js';
|
|
5
|
+
import type { InitWizardConfig } from '../../common/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Index display stats type (re-exported for convenience).
|
|
8
|
+
*/
|
|
9
|
+
export type IndexDisplayStats = {
|
|
10
|
+
totalFiles: number;
|
|
11
|
+
totalChunks: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Check if project is initialized.
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkInitialized(projectRoot: string): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* Load index stats for display in status bar.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadIndexStats(projectRoot: string): Promise<IndexDisplayStats | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Initialize a project for Viberag.
|
|
23
|
+
* Creates .viberag/ directory with config.json.
|
|
24
|
+
* With isReinit=true, deletes everything and starts fresh.
|
|
25
|
+
*/
|
|
26
|
+
export declare function runInit(projectRoot: string, isReinit?: boolean, wizardConfig?: InitWizardConfig): Promise<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Run the indexer and return stats.
|
|
29
|
+
* When force=true, also updates config dimensions to match current PROVIDER_CONFIGS
|
|
30
|
+
* (handles dimension changes after viberag upgrades).
|
|
31
|
+
*/
|
|
32
|
+
export declare function runIndex(projectRoot: string, force?: boolean, onProgress?: (current: number, total: number, stage: string) => void): Promise<IndexStats>;
|
|
33
|
+
/**
|
|
34
|
+
* Format index stats for display.
|
|
35
|
+
*/
|
|
36
|
+
export declare function formatIndexStats(stats: IndexStats): string;
|
|
37
|
+
/**
|
|
38
|
+
* Run a search query and return results.
|
|
39
|
+
*/
|
|
40
|
+
export declare function runSearch(projectRoot: string, query: string, limit?: number): Promise<SearchResults>;
|
|
41
|
+
/**
|
|
42
|
+
* Format search results for display with colors.
|
|
43
|
+
*/
|
|
44
|
+
export declare function formatSearchResults(results: SearchResults): string;
|
|
45
|
+
/**
|
|
46
|
+
* Get index status.
|
|
47
|
+
*/
|
|
48
|
+
export declare function getStatus(projectRoot: string): Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* Clean/uninstall Viberag from a project.
|
|
51
|
+
* Removes the entire .viberag/ directory.
|
|
52
|
+
*/
|
|
53
|
+
export declare function runClean(projectRoot: string): Promise<string>;
|
|
54
|
+
/**
|
|
55
|
+
* Get MCP setup instructions for Claude Code.
|
|
56
|
+
*/
|
|
57
|
+
export declare function getMcpSetupInstructions(): string;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RAG commands for the CLI.
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { Indexer, SearchEngine, loadManifest, manifestExists, configExists, saveConfig, DEFAULT_CONFIG, PROVIDER_CONFIGS, getViberagDir, } from '../../rag/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Check if project is initialized.
|
|
10
|
+
*/
|
|
11
|
+
export async function checkInitialized(projectRoot) {
|
|
12
|
+
return configExists(projectRoot);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Load index stats for display in status bar.
|
|
16
|
+
*/
|
|
17
|
+
export async function loadIndexStats(projectRoot) {
|
|
18
|
+
if (!(await manifestExists(projectRoot))) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const manifest = await loadManifest(projectRoot);
|
|
22
|
+
return {
|
|
23
|
+
totalFiles: manifest.stats.totalFiles,
|
|
24
|
+
totalChunks: manifest.stats.totalChunks,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Initialize a project for Viberag.
|
|
29
|
+
* Creates .viberag/ directory with config.json.
|
|
30
|
+
* With isReinit=true, deletes everything and starts fresh.
|
|
31
|
+
*/
|
|
32
|
+
export async function runInit(projectRoot, isReinit = false, wizardConfig) {
|
|
33
|
+
const viberagDir = getViberagDir(projectRoot);
|
|
34
|
+
const isExisting = await configExists(projectRoot);
|
|
35
|
+
// If reinit, delete entire .viberag directory first
|
|
36
|
+
if (isReinit && isExisting) {
|
|
37
|
+
await fs.rm(viberagDir, { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
// Create .viberag directory
|
|
40
|
+
await fs.mkdir(viberagDir, { recursive: true });
|
|
41
|
+
// Build config from wizard choices
|
|
42
|
+
const provider = wizardConfig?.provider ?? 'gemini';
|
|
43
|
+
const { model, dimensions } = PROVIDER_CONFIGS[provider];
|
|
44
|
+
const config = {
|
|
45
|
+
...DEFAULT_CONFIG,
|
|
46
|
+
embeddingProvider: provider,
|
|
47
|
+
embeddingModel: model,
|
|
48
|
+
embeddingDimensions: dimensions,
|
|
49
|
+
...(wizardConfig?.apiKey && { apiKey: wizardConfig.apiKey }),
|
|
50
|
+
};
|
|
51
|
+
// Save config
|
|
52
|
+
await saveConfig(projectRoot, config);
|
|
53
|
+
// Add .viberag/ to .gitignore if not present
|
|
54
|
+
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
55
|
+
try {
|
|
56
|
+
const content = await fs.readFile(gitignorePath, 'utf-8');
|
|
57
|
+
if (!content.includes('.viberag')) {
|
|
58
|
+
await fs.appendFile(gitignorePath, '\n# Viberag index\n.viberag/\n');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// .gitignore doesn't exist, create it
|
|
63
|
+
await fs.writeFile(gitignorePath, '# Viberag index\n.viberag/\n');
|
|
64
|
+
}
|
|
65
|
+
const action = isExisting ? 'Reinitialized' : 'Initialized';
|
|
66
|
+
const providerLabel = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
67
|
+
return `${action} Viberag in ${viberagDir}\nProvider: ${providerLabel}\nModel: ${model} (${dimensions}d)\nRun /index to build the code index.`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Run the indexer and return stats.
|
|
71
|
+
* When force=true, also updates config dimensions to match current PROVIDER_CONFIGS
|
|
72
|
+
* (handles dimension changes after viberag upgrades).
|
|
73
|
+
*/
|
|
74
|
+
export async function runIndex(projectRoot, force = false, onProgress) {
|
|
75
|
+
// When forcing reindex, sync config dimensions with current provider settings
|
|
76
|
+
// This handles cases where PROVIDER_CONFIGS dimensions changed (e.g., Gemini 768→1536)
|
|
77
|
+
if (force) {
|
|
78
|
+
const { loadConfig } = await import('../../rag/config/index.js');
|
|
79
|
+
const config = await loadConfig(projectRoot);
|
|
80
|
+
const currentDimensions = PROVIDER_CONFIGS[config.embeddingProvider]?.dimensions;
|
|
81
|
+
if (currentDimensions && config.embeddingDimensions !== currentDimensions) {
|
|
82
|
+
const updatedConfig = {
|
|
83
|
+
...config,
|
|
84
|
+
embeddingDimensions: currentDimensions,
|
|
85
|
+
embeddingModel: PROVIDER_CONFIGS[config.embeddingProvider].model,
|
|
86
|
+
};
|
|
87
|
+
await saveConfig(projectRoot, updatedConfig);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const indexer = new Indexer(projectRoot);
|
|
91
|
+
try {
|
|
92
|
+
const stats = await indexer.index({
|
|
93
|
+
force,
|
|
94
|
+
progressCallback: onProgress,
|
|
95
|
+
});
|
|
96
|
+
return stats;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
indexer.close();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Format index stats for display.
|
|
104
|
+
*/
|
|
105
|
+
export function formatIndexStats(stats) {
|
|
106
|
+
const lines = [
|
|
107
|
+
'Index complete:',
|
|
108
|
+
` Files scanned: ${stats.filesScanned}`,
|
|
109
|
+
` New files: ${stats.filesNew}`,
|
|
110
|
+
` Modified files: ${stats.filesModified}`,
|
|
111
|
+
` Deleted files: ${stats.filesDeleted}`,
|
|
112
|
+
` Chunks added: ${stats.chunksAdded}`,
|
|
113
|
+
` Chunks deleted: ${stats.chunksDeleted}`,
|
|
114
|
+
` Embeddings computed: ${stats.embeddingsComputed}`,
|
|
115
|
+
` Embeddings cached: ${stats.embeddingsCached}`,
|
|
116
|
+
];
|
|
117
|
+
return lines.join('\n');
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Run a search query and return results.
|
|
121
|
+
*/
|
|
122
|
+
export async function runSearch(projectRoot, query, limit = 10) {
|
|
123
|
+
const engine = new SearchEngine(projectRoot);
|
|
124
|
+
try {
|
|
125
|
+
return await engine.search(query, { limit });
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
engine.close();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Color mapping for chunk types.
|
|
133
|
+
*/
|
|
134
|
+
const TYPE_COLORS = {
|
|
135
|
+
function: chalk.cyan,
|
|
136
|
+
class: chalk.magenta,
|
|
137
|
+
method: chalk.blue,
|
|
138
|
+
module: chalk.dim,
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Get score color based on value.
|
|
142
|
+
*/
|
|
143
|
+
function getScoreColor(score) {
|
|
144
|
+
if (score > 0.8)
|
|
145
|
+
return chalk.green;
|
|
146
|
+
if (score > 0.5)
|
|
147
|
+
return chalk.yellow;
|
|
148
|
+
return chalk.red;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Format search results for display with colors.
|
|
152
|
+
*/
|
|
153
|
+
export function formatSearchResults(results) {
|
|
154
|
+
if (results.results.length === 0) {
|
|
155
|
+
return chalk.dim(`No results found for "${results.query}" (${results.elapsedMs}ms)`);
|
|
156
|
+
}
|
|
157
|
+
const lines = [
|
|
158
|
+
chalk.bold(`Found ${results.results.length} results for `) +
|
|
159
|
+
chalk.cyan(`"${results.query}"`) +
|
|
160
|
+
chalk.dim(` (${results.elapsedMs}ms):`),
|
|
161
|
+
'',
|
|
162
|
+
];
|
|
163
|
+
for (const result of results.results) {
|
|
164
|
+
const typeColor = TYPE_COLORS[result.type] ?? chalk.white;
|
|
165
|
+
const scoreColor = getScoreColor(result.score);
|
|
166
|
+
// Type badge and name
|
|
167
|
+
lines.push(typeColor(`[${result.type}]`) +
|
|
168
|
+
(result.name ? ` ${chalk.white(result.name)}` : ''));
|
|
169
|
+
// File path and line numbers
|
|
170
|
+
lines.push(` ${chalk.green(result.filepath)}` +
|
|
171
|
+
chalk.dim(`:${result.startLine}-${result.endLine}`));
|
|
172
|
+
// Score
|
|
173
|
+
lines.push(` Score: ${scoreColor(result.score.toFixed(4))}`);
|
|
174
|
+
// Snippet (first 100 chars, dimmed)
|
|
175
|
+
const snippet = result.text.slice(0, 100).replace(/\n/g, ' ');
|
|
176
|
+
lines.push(chalk.dim(` ${snippet}${result.text.length > 100 ? '...' : ''}`));
|
|
177
|
+
lines.push('');
|
|
178
|
+
}
|
|
179
|
+
return lines.join('\n');
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get index status.
|
|
183
|
+
*/
|
|
184
|
+
export async function getStatus(projectRoot) {
|
|
185
|
+
if (!(await manifestExists(projectRoot))) {
|
|
186
|
+
return 'No index found. Run /index to create one.';
|
|
187
|
+
}
|
|
188
|
+
const manifest = await loadManifest(projectRoot);
|
|
189
|
+
const lines = [
|
|
190
|
+
'Index status:',
|
|
191
|
+
` Version: ${manifest.version}`,
|
|
192
|
+
` Created: ${manifest.createdAt}`,
|
|
193
|
+
` Updated: ${manifest.updatedAt}`,
|
|
194
|
+
` Total files: ${manifest.stats.totalFiles}`,
|
|
195
|
+
` Total chunks: ${manifest.stats.totalChunks}`,
|
|
196
|
+
];
|
|
197
|
+
return lines.join('\n');
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Clean/uninstall Viberag from a project.
|
|
201
|
+
* Removes the entire .viberag/ directory.
|
|
202
|
+
*/
|
|
203
|
+
export async function runClean(projectRoot) {
|
|
204
|
+
const viberagDir = getViberagDir(projectRoot);
|
|
205
|
+
const exists = await configExists(projectRoot);
|
|
206
|
+
if (!exists) {
|
|
207
|
+
return 'Viberag is not initialized in this project. Nothing to clean.';
|
|
208
|
+
}
|
|
209
|
+
await fs.rm(viberagDir, { recursive: true, force: true });
|
|
210
|
+
return `Removed ${viberagDir}\nViberag has been uninstalled from this project.\nRun /init to reinitialize.`;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get MCP setup instructions for Claude Code.
|
|
214
|
+
*/
|
|
215
|
+
export function getMcpSetupInstructions() {
|
|
216
|
+
return `To add VibeRAG to Claude Code, run:
|
|
217
|
+
|
|
218
|
+
claude mcp add viberag -- npx viberag-mcp
|
|
219
|
+
|
|
220
|
+
This registers VibeRAG as an MCP server. After adding:
|
|
221
|
+
|
|
222
|
+
1. Restart Claude Code (or run: claude mcp restart viberag)
|
|
223
|
+
2. The following tools will be available:
|
|
224
|
+
- codebase_search Search the codebase semantically
|
|
225
|
+
- codebase_parallel_search Run multiple searches in parallel
|
|
226
|
+
- viberag_index Index or reindex the codebase
|
|
227
|
+
- viberag_status Get index statistics
|
|
228
|
+
|
|
229
|
+
Note: The project must be initialized first (run /init in the CLI).
|
|
230
|
+
The MCP server uses the current working directory as the project root.`;
|
|
231
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Setup Logic
|
|
3
|
+
*
|
|
4
|
+
* Functions for generating, writing, and merging MCP configurations
|
|
5
|
+
* for various AI coding tools.
|
|
6
|
+
*/
|
|
7
|
+
import { type EditorConfig, type EditorId } from '../data/mcp-editors.js';
|
|
8
|
+
/**
|
|
9
|
+
* Result of an MCP setup operation.
|
|
10
|
+
*/
|
|
11
|
+
export interface McpSetupResult {
|
|
12
|
+
success: boolean;
|
|
13
|
+
editor: EditorId;
|
|
14
|
+
method: 'file-created' | 'file-merged' | 'cli-command' | 'instructions-shown';
|
|
15
|
+
configPath?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate the viberag MCP server configuration object.
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateViberagConfig(): object;
|
|
22
|
+
/**
|
|
23
|
+
* Generate OpenCode-specific viberag MCP server configuration.
|
|
24
|
+
* OpenCode requires: type="local", command as array, no args key.
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateOpenCodeViberagConfig(): object;
|
|
27
|
+
/**
|
|
28
|
+
* Generate complete MCP config for an editor.
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateMcpConfig(editor: EditorConfig): object;
|
|
31
|
+
/**
|
|
32
|
+
* Generate TOML config for OpenAI Codex.
|
|
33
|
+
*/
|
|
34
|
+
export declare function generateTomlConfig(): string;
|
|
35
|
+
/**
|
|
36
|
+
* Check if a config file exists.
|
|
37
|
+
*/
|
|
38
|
+
export declare function configExists(configPath: string): Promise<boolean>;
|
|
39
|
+
/**
|
|
40
|
+
* Read existing config file as JSON.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readJsonConfig(configPath: string): Promise<object | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Merge viberag config into existing config.
|
|
45
|
+
*/
|
|
46
|
+
export declare function mergeConfig(existing: object, editor: EditorConfig): object;
|
|
47
|
+
/**
|
|
48
|
+
* Check if viberag is already configured in a config file.
|
|
49
|
+
*/
|
|
50
|
+
export declare function hasViberagConfig(config: object, editor: EditorConfig): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Write MCP config to file, creating directories as needed.
|
|
53
|
+
*/
|
|
54
|
+
export declare function writeMcpConfig(editor: EditorConfig, projectRoot: string): Promise<McpSetupResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Generate manual setup instructions for an editor.
|
|
57
|
+
*/
|
|
58
|
+
export declare function getManualInstructions(editor: EditorConfig, projectRoot: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Get a diff preview of the merge operation.
|
|
61
|
+
*/
|
|
62
|
+
export declare function getMergeDiff(editor: EditorConfig, projectRoot: string): Promise<{
|
|
63
|
+
before: string;
|
|
64
|
+
after: string;
|
|
65
|
+
configPath: string;
|
|
66
|
+
} | null>;
|
|
67
|
+
/**
|
|
68
|
+
* Check if viberag is already configured for an editor.
|
|
69
|
+
*/
|
|
70
|
+
export declare function isAlreadyConfigured(editor: EditorConfig, projectRoot: string): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Remove viberag from an existing config object.
|
|
73
|
+
* Returns the modified config, or null if nothing to remove.
|
|
74
|
+
*/
|
|
75
|
+
export declare function removeViberagFromConfig(existing: object, editor: EditorConfig): object | null;
|
|
76
|
+
/**
|
|
77
|
+
* Result of an MCP removal operation.
|
|
78
|
+
*/
|
|
79
|
+
export interface McpRemovalResult {
|
|
80
|
+
success: boolean;
|
|
81
|
+
editor: EditorId;
|
|
82
|
+
configPath?: string;
|
|
83
|
+
fileDeleted?: boolean;
|
|
84
|
+
error?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Remove viberag from an editor's MCP config.
|
|
88
|
+
* Always keeps the config file, even if it becomes empty (no other servers).
|
|
89
|
+
*/
|
|
90
|
+
export declare function removeViberagConfig(editor: EditorConfig, projectRoot: string): Promise<McpRemovalResult>;
|
|
91
|
+
/**
|
|
92
|
+
* Find all editors that have viberag configured.
|
|
93
|
+
* Returns both project-scope and global-scope editors.
|
|
94
|
+
*/
|
|
95
|
+
export declare function findConfiguredEditors(projectRoot: string): Promise<{
|
|
96
|
+
projectScope: EditorConfig[];
|
|
97
|
+
globalScope: EditorConfig[];
|
|
98
|
+
}>;
|
|
99
|
+
/**
|
|
100
|
+
* Add an entry to .gitignore if not already present.
|
|
101
|
+
*/
|
|
102
|
+
export declare function addToGitignore(projectRoot: string, entry: string): Promise<boolean>;
|
|
103
|
+
/**
|
|
104
|
+
* Get all project-scope config paths that were created.
|
|
105
|
+
* Used for gitignore prompt.
|
|
106
|
+
*/
|
|
107
|
+
export declare function getProjectConfigPaths(results: McpSetupResult[]): string[];
|