viberag 0.3.2 → 0.4.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/README.md +2 -2
- package/dist/cli/app.d.ts +3 -0
- package/dist/cli/app.js +100 -102
- package/dist/cli/commands/handlers.d.ts +8 -6
- package/dist/cli/commands/handlers.js +90 -32
- package/dist/cli/commands/useCommands.d.ts +20 -0
- package/dist/cli/commands/useCommands.js +189 -0
- package/dist/cli/commands/useRagCommands.d.ts +2 -5
- package/dist/cli/commands/useRagCommands.js +11 -18
- package/dist/cli/components/InitWizard.js +66 -27
- package/dist/cli/components/McpSetupWizard.js +23 -4
- package/dist/cli/components/SlotRow.d.ts +22 -0
- package/dist/cli/components/SlotRow.js +55 -0
- package/dist/cli/components/StatusBar.d.ts +14 -0
- package/dist/cli/components/StatusBar.js +156 -0
- package/dist/cli/contexts/DaemonStatusContext.d.ts +38 -0
- package/dist/cli/contexts/DaemonStatusContext.js +106 -0
- package/dist/cli/hooks/useStatusPolling.d.ts +34 -0
- package/dist/cli/hooks/useStatusPolling.js +121 -0
- package/dist/cli/store/app/selectors.d.ts +87 -0
- package/dist/cli/store/app/selectors.js +28 -0
- package/dist/cli/store/app/slice.d.ts +1013 -0
- package/dist/cli/store/app/slice.js +112 -0
- package/dist/cli/store/hooks.d.ts +22 -0
- package/dist/cli/store/hooks.js +17 -0
- package/dist/cli/store/store.d.ts +17 -0
- package/dist/cli/store/store.js +18 -0
- package/dist/cli/store/wizard/selectors.d.ts +115 -0
- package/dist/cli/store/wizard/selectors.js +36 -0
- package/dist/cli/store/wizard/slice.d.ts +523 -0
- package/dist/cli/store/wizard/slice.js +119 -0
- package/dist/cli/utils/error-handler.d.ts +55 -0
- package/dist/cli/utils/error-handler.js +92 -0
- package/dist/client/auto-start.d.ts +42 -0
- package/dist/client/auto-start.js +250 -0
- package/dist/client/connection.d.ts +48 -0
- package/dist/client/connection.js +200 -0
- package/dist/client/index.d.ts +93 -0
- package/dist/client/index.js +209 -0
- package/dist/client/types.d.ts +105 -0
- package/dist/client/types.js +7 -0
- package/dist/common/components/SlotRow.d.ts +22 -0
- package/dist/common/components/SlotRow.js +53 -0
- package/dist/common/components/StatusBar.js +82 -31
- package/dist/common/types.d.ts +12 -13
- package/dist/daemon/handlers.d.ts +15 -0
- package/dist/daemon/handlers.js +157 -0
- package/dist/daemon/index.d.ts +21 -0
- package/dist/daemon/index.js +123 -0
- package/dist/daemon/lib/chunker/bounded-channel.d.ts +51 -0
- package/dist/daemon/lib/chunker/bounded-channel.js +138 -0
- package/dist/daemon/lib/chunker/index.d.ts +135 -0
- package/dist/daemon/lib/chunker/index.js +1370 -0
- package/dist/daemon/lib/chunker/types.d.ts +77 -0
- package/dist/daemon/lib/chunker/types.js +50 -0
- package/dist/daemon/lib/config.d.ts +73 -0
- package/dist/daemon/lib/config.js +149 -0
- package/dist/daemon/lib/constants.d.ts +75 -0
- package/dist/daemon/lib/constants.js +114 -0
- package/dist/daemon/lib/gitignore.d.ts +57 -0
- package/dist/daemon/lib/gitignore.js +246 -0
- package/dist/daemon/lib/logger.d.ts +51 -0
- package/dist/daemon/lib/logger.js +167 -0
- package/dist/daemon/lib/manifest.d.ts +58 -0
- package/dist/daemon/lib/manifest.js +116 -0
- package/dist/daemon/lib/merkle/diff.d.ts +32 -0
- package/dist/daemon/lib/merkle/diff.js +107 -0
- package/dist/daemon/lib/merkle/hash.d.ts +40 -0
- package/dist/daemon/lib/merkle/hash.js +180 -0
- package/dist/daemon/lib/merkle/index.d.ts +71 -0
- package/dist/daemon/lib/merkle/index.js +309 -0
- package/dist/daemon/lib/merkle/node.d.ts +55 -0
- package/dist/daemon/lib/merkle/node.js +82 -0
- package/dist/daemon/lifecycle.d.ts +50 -0
- package/dist/daemon/lifecycle.js +142 -0
- package/dist/daemon/owner.d.ts +175 -0
- package/dist/daemon/owner.js +609 -0
- package/dist/daemon/protocol.d.ts +100 -0
- package/dist/daemon/protocol.js +163 -0
- package/dist/daemon/providers/api-utils.d.ts +130 -0
- package/dist/daemon/providers/api-utils.js +248 -0
- package/dist/daemon/providers/gemini.d.ts +39 -0
- package/dist/daemon/providers/gemini.js +205 -0
- package/dist/daemon/providers/index.d.ts +14 -0
- package/dist/daemon/providers/index.js +14 -0
- package/dist/daemon/providers/local-4b.d.ts +28 -0
- package/dist/daemon/providers/local-4b.js +51 -0
- package/dist/daemon/providers/local.d.ts +36 -0
- package/dist/daemon/providers/local.js +166 -0
- package/dist/daemon/providers/mistral.d.ts +35 -0
- package/dist/daemon/providers/mistral.js +160 -0
- package/dist/daemon/providers/mock.d.ts +35 -0
- package/dist/daemon/providers/mock.js +69 -0
- package/dist/daemon/providers/openai.d.ts +41 -0
- package/dist/daemon/providers/openai.js +190 -0
- package/dist/daemon/providers/types.d.ts +68 -0
- package/dist/daemon/providers/types.js +6 -0
- package/dist/daemon/providers/validate.d.ts +30 -0
- package/dist/daemon/providers/validate.js +162 -0
- package/dist/daemon/server.d.ts +79 -0
- package/dist/daemon/server.js +293 -0
- package/dist/daemon/services/index.d.ts +11 -0
- package/dist/daemon/services/index.js +16 -0
- package/dist/daemon/services/indexing.d.ts +117 -0
- package/dist/daemon/services/indexing.js +573 -0
- package/dist/daemon/services/search/filters.d.ts +21 -0
- package/dist/daemon/services/search/filters.js +106 -0
- package/dist/daemon/services/search/fts.d.ts +32 -0
- package/dist/daemon/services/search/fts.js +61 -0
- package/dist/daemon/services/search/hybrid.d.ts +17 -0
- package/dist/daemon/services/search/hybrid.js +58 -0
- package/dist/daemon/services/search/index.d.ts +108 -0
- package/dist/daemon/services/search/index.js +417 -0
- package/dist/daemon/services/search/types.d.ts +126 -0
- package/dist/daemon/services/search/types.js +4 -0
- package/dist/daemon/services/search/vector.d.ts +25 -0
- package/dist/daemon/services/search/vector.js +44 -0
- package/dist/daemon/services/storage/index.d.ts +110 -0
- package/dist/daemon/services/storage/index.js +378 -0
- package/dist/daemon/services/storage/schema.d.ts +24 -0
- package/dist/daemon/services/storage/schema.js +51 -0
- package/dist/daemon/services/storage/types.d.ts +105 -0
- package/dist/daemon/services/storage/types.js +71 -0
- package/dist/daemon/services/types.d.ts +192 -0
- package/dist/daemon/services/types.js +53 -0
- package/dist/daemon/services/watcher.d.ts +98 -0
- package/dist/daemon/services/watcher.js +386 -0
- package/dist/daemon/state.d.ts +119 -0
- package/dist/daemon/state.js +161 -0
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +44 -60
- package/dist/mcp/server.d.ts +10 -14
- package/dist/mcp/server.js +75 -74
- package/dist/mcp/services/lazy-loader.d.ts +23 -0
- package/dist/mcp/services/lazy-loader.js +34 -0
- package/dist/mcp/warmup.d.ts +3 -3
- package/dist/mcp/warmup.js +39 -40
- package/dist/mcp/watcher.d.ts +5 -7
- package/dist/mcp/watcher.js +73 -64
- package/dist/rag/config/index.d.ts +2 -0
- package/dist/rag/constants.d.ts +30 -0
- package/dist/rag/constants.js +38 -0
- package/dist/rag/embeddings/api-utils.d.ts +121 -0
- package/dist/rag/embeddings/api-utils.js +259 -0
- package/dist/rag/embeddings/gemini.d.ts +4 -12
- package/dist/rag/embeddings/gemini.js +22 -72
- package/dist/rag/embeddings/index.d.ts +5 -3
- package/dist/rag/embeddings/index.js +5 -2
- package/dist/rag/embeddings/local-4b.d.ts +2 -2
- package/dist/rag/embeddings/local-4b.js +1 -1
- package/dist/rag/embeddings/local.d.ts +10 -3
- package/dist/rag/embeddings/local.js +58 -12
- package/dist/rag/embeddings/mistral.d.ts +4 -12
- package/dist/rag/embeddings/mistral.js +22 -72
- package/dist/rag/embeddings/mock.d.ts +35 -0
- package/dist/rag/embeddings/mock.js +69 -0
- package/dist/rag/embeddings/openai.d.ts +11 -13
- package/dist/rag/embeddings/openai.js +47 -75
- package/dist/rag/embeddings/types.d.ts +27 -1
- package/dist/rag/embeddings/validate.d.ts +9 -1
- package/dist/rag/embeddings/validate.js +17 -4
- package/dist/rag/index.d.ts +2 -2
- package/dist/rag/index.js +1 -1
- package/dist/rag/indexer/bounded-channel.d.ts +51 -0
- package/dist/rag/indexer/bounded-channel.js +138 -0
- package/dist/rag/indexer/indexer.d.ts +4 -14
- package/dist/rag/indexer/indexer.js +246 -169
- package/dist/rag/indexer/types.d.ts +1 -0
- package/dist/rag/logger/index.d.ts +22 -0
- package/dist/rag/logger/index.js +78 -1
- package/dist/rag/manifest/index.js +1 -2
- package/dist/rag/search/index.js +1 -1
- package/dist/rag/storage/schema.d.ts +2 -4
- package/dist/rag/storage/schema.js +3 -5
- package/dist/store/app/selectors.d.ts +87 -0
- package/dist/store/app/selectors.js +28 -0
- package/dist/store/app/slice.d.ts +1013 -0
- package/dist/store/app/slice.js +112 -0
- package/dist/store/hooks.d.ts +22 -0
- package/dist/store/hooks.js +17 -0
- package/dist/store/index.d.ts +12 -0
- package/dist/store/index.js +18 -0
- package/dist/store/indexing/listeners.d.ts +25 -0
- package/dist/store/indexing/listeners.js +46 -0
- package/dist/store/indexing/selectors.d.ts +195 -0
- package/dist/store/indexing/selectors.js +69 -0
- package/dist/store/indexing/slice.d.ts +309 -0
- package/dist/store/indexing/slice.js +113 -0
- package/dist/store/slot-progress/listeners.d.ts +23 -0
- package/dist/store/slot-progress/listeners.js +33 -0
- package/dist/store/slot-progress/selectors.d.ts +67 -0
- package/dist/store/slot-progress/selectors.js +36 -0
- package/dist/store/slot-progress/slice.d.ts +246 -0
- package/dist/store/slot-progress/slice.js +70 -0
- package/dist/store/store.d.ts +17 -0
- package/dist/store/store.js +18 -0
- package/dist/store/warmup/selectors.d.ts +109 -0
- package/dist/store/warmup/selectors.js +44 -0
- package/dist/store/warmup/slice.d.ts +137 -0
- package/dist/store/warmup/slice.js +72 -0
- package/dist/store/watcher/selectors.d.ts +115 -0
- package/dist/store/watcher/selectors.js +52 -0
- package/dist/store/watcher/slice.d.ts +269 -0
- package/dist/store/watcher/slice.js +100 -0
- package/dist/store/wizard/selectors.d.ts +115 -0
- package/dist/store/wizard/selectors.js +36 -0
- package/dist/store/wizard/slice.d.ts +523 -0
- package/dist/store/wizard/slice.js +119 -0
- package/package.json +10 -2
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command handling hook.
|
|
3
|
+
* Consolidates all command routing and handler implementations.
|
|
4
|
+
*/
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
|
+
import { useApp } from 'ink';
|
|
7
|
+
import { runIndex, formatIndexStats, runSearch, getStatus, loadIndexStats, } from './handlers.js';
|
|
8
|
+
import { setupVSCodeTerminal } from '../../common/commands/terminalSetup.js';
|
|
9
|
+
import { useAppDispatch } from '../store/hooks.js';
|
|
10
|
+
import { AppActions } from '../store/app/slice.js';
|
|
11
|
+
export function useCommands({ addOutput, addSearchResults, projectRoot, stdout, startInitWizard, startMcpSetupWizard, startCleanWizard, isInitialized, }) {
|
|
12
|
+
const dispatch = useAppDispatch();
|
|
13
|
+
const { exit } = useApp();
|
|
14
|
+
// Command handlers
|
|
15
|
+
const handleHelp = useCallback(() => {
|
|
16
|
+
addOutput('system', `Commands:
|
|
17
|
+
/help - Show this help
|
|
18
|
+
/clear - Clear the screen
|
|
19
|
+
/terminal-setup - Configure terminal for Shift+Enter
|
|
20
|
+
/init - Initialize Viberag (interactive wizard)
|
|
21
|
+
/index - Index the codebase
|
|
22
|
+
/reindex - Force full reindex
|
|
23
|
+
/search <query> - Search the codebase
|
|
24
|
+
/status - Show index status
|
|
25
|
+
/mcp-setup - Configure MCP server for AI coding tools
|
|
26
|
+
/clean - Remove Viberag from project (delete .viberag/)
|
|
27
|
+
/quit - Exit
|
|
28
|
+
|
|
29
|
+
Multi-line input:
|
|
30
|
+
Shift+Enter - iTerm2, Kitty, WezTerm (automatic)
|
|
31
|
+
Shift+Enter - VS Code (requires /terminal-setup)
|
|
32
|
+
Option+Enter - Most terminals
|
|
33
|
+
Ctrl+J - All terminals
|
|
34
|
+
\\ then Enter - All terminals
|
|
35
|
+
|
|
36
|
+
Tips:
|
|
37
|
+
Ctrl+C - Clear input (twice to quit)
|
|
38
|
+
Escape - Clear input
|
|
39
|
+
Up/Down - Command history
|
|
40
|
+
|
|
41
|
+
Manual MCP Setup:
|
|
42
|
+
https://github.com/mdrideout/viberag?tab=readme-ov-file#manual-setup-instructions`);
|
|
43
|
+
}, [addOutput]);
|
|
44
|
+
const handleClear = useCallback(() => {
|
|
45
|
+
// Clear screen (\x1B[2J), clear scrollback buffer (\x1B[3J), move cursor home (\x1B[H)
|
|
46
|
+
stdout.write('\x1B[2J\x1B[3J\x1B[H');
|
|
47
|
+
}, [stdout]);
|
|
48
|
+
const handleTerminalSetup = useCallback(() => {
|
|
49
|
+
setupVSCodeTerminal()
|
|
50
|
+
.then(result => addOutput('system', result))
|
|
51
|
+
.catch(err => addOutput('system', `Error: ${err.message}`));
|
|
52
|
+
}, [addOutput]);
|
|
53
|
+
// Trigger init wizard
|
|
54
|
+
const handleInit = useCallback(() => {
|
|
55
|
+
startInitWizard(isInitialized);
|
|
56
|
+
}, [startInitWizard, isInitialized]);
|
|
57
|
+
const handleIndex = useCallback((force) => {
|
|
58
|
+
const action = force ? 'Reindexing' : 'Indexing';
|
|
59
|
+
addOutput('system', `${action} codebase...`);
|
|
60
|
+
// Progress is synced via DaemonStatusContext polling
|
|
61
|
+
runIndex(projectRoot, force)
|
|
62
|
+
.then(async (stats) => {
|
|
63
|
+
if (stats) {
|
|
64
|
+
addOutput('system', formatIndexStats(stats));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
addOutput('system', 'Index complete.');
|
|
68
|
+
}
|
|
69
|
+
// Reload stats after indexing
|
|
70
|
+
const newStats = await loadIndexStats(projectRoot);
|
|
71
|
+
dispatch(AppActions.setIndexStats(newStats));
|
|
72
|
+
})
|
|
73
|
+
.catch(err => {
|
|
74
|
+
addOutput('system', `Index failed: ${err.message}`);
|
|
75
|
+
});
|
|
76
|
+
}, [projectRoot, addOutput, dispatch]);
|
|
77
|
+
const handleSearch = useCallback((query) => {
|
|
78
|
+
addOutput('system', `Searching for "${query}"...`);
|
|
79
|
+
dispatch(AppActions.setSearching());
|
|
80
|
+
runSearch(projectRoot, query)
|
|
81
|
+
.then(results => {
|
|
82
|
+
// Use the component-based display with syntax highlighting
|
|
83
|
+
addSearchResults({
|
|
84
|
+
query: results.query,
|
|
85
|
+
elapsedMs: results.elapsedMs,
|
|
86
|
+
results: results.results.map(r => ({
|
|
87
|
+
type: r.type,
|
|
88
|
+
name: r.name,
|
|
89
|
+
filepath: r.filepath,
|
|
90
|
+
filename: r.filename,
|
|
91
|
+
startLine: r.startLine,
|
|
92
|
+
endLine: r.endLine,
|
|
93
|
+
score: r.score,
|
|
94
|
+
text: r.text,
|
|
95
|
+
})),
|
|
96
|
+
});
|
|
97
|
+
dispatch(AppActions.setReady());
|
|
98
|
+
})
|
|
99
|
+
.catch(err => {
|
|
100
|
+
addOutput('system', `Search failed: ${err.message}`);
|
|
101
|
+
dispatch(AppActions.setReady());
|
|
102
|
+
});
|
|
103
|
+
}, [projectRoot, addOutput, addSearchResults, dispatch]);
|
|
104
|
+
const handleStatus = useCallback(() => {
|
|
105
|
+
getStatus(projectRoot)
|
|
106
|
+
.then(status => addOutput('system', status))
|
|
107
|
+
.catch(err => addOutput('system', `Status failed: ${err.message}`));
|
|
108
|
+
}, [projectRoot, addOutput]);
|
|
109
|
+
const handleMcpSetup = useCallback(() => {
|
|
110
|
+
startMcpSetupWizard(false); // showPrompt = false for direct /mcp-setup command
|
|
111
|
+
}, [startMcpSetupWizard]);
|
|
112
|
+
const handleClean = useCallback(() => {
|
|
113
|
+
startCleanWizard();
|
|
114
|
+
}, [startCleanWizard]);
|
|
115
|
+
const handleUnknown = useCallback((command) => {
|
|
116
|
+
addOutput('system', `Unknown command: ${command}. Type /help for available commands.`);
|
|
117
|
+
}, [addOutput]);
|
|
118
|
+
// Command detection
|
|
119
|
+
const isCommand = useCallback((text) => {
|
|
120
|
+
return text.trim().startsWith('/');
|
|
121
|
+
}, []);
|
|
122
|
+
// Command routing
|
|
123
|
+
const executeCommand = useCallback((text) => {
|
|
124
|
+
const trimmed = text.trim();
|
|
125
|
+
const command = trimmed.toLowerCase();
|
|
126
|
+
// Handle commands with arguments
|
|
127
|
+
if (command.startsWith('/search ')) {
|
|
128
|
+
const query = trimmed.slice('/search '.length).trim();
|
|
129
|
+
if (query) {
|
|
130
|
+
handleSearch(query);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
handleUnknown('/search (missing query)');
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
switch (command) {
|
|
138
|
+
case '/help':
|
|
139
|
+
handleHelp();
|
|
140
|
+
break;
|
|
141
|
+
case '/clear':
|
|
142
|
+
handleClear();
|
|
143
|
+
break;
|
|
144
|
+
case '/terminal-setup':
|
|
145
|
+
handleTerminalSetup();
|
|
146
|
+
break;
|
|
147
|
+
case '/init':
|
|
148
|
+
handleInit();
|
|
149
|
+
break;
|
|
150
|
+
case '/index':
|
|
151
|
+
handleIndex(false);
|
|
152
|
+
break;
|
|
153
|
+
case '/reindex':
|
|
154
|
+
handleIndex(true);
|
|
155
|
+
break;
|
|
156
|
+
case '/status':
|
|
157
|
+
handleStatus();
|
|
158
|
+
break;
|
|
159
|
+
case '/mcp-setup':
|
|
160
|
+
handleMcpSetup();
|
|
161
|
+
break;
|
|
162
|
+
case '/clean':
|
|
163
|
+
case '/uninstall':
|
|
164
|
+
handleClean();
|
|
165
|
+
break;
|
|
166
|
+
case '/quit':
|
|
167
|
+
case '/exit':
|
|
168
|
+
case '/q':
|
|
169
|
+
exit();
|
|
170
|
+
break;
|
|
171
|
+
default:
|
|
172
|
+
handleUnknown(command);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}, [
|
|
176
|
+
exit,
|
|
177
|
+
handleHelp,
|
|
178
|
+
handleClear,
|
|
179
|
+
handleTerminalSetup,
|
|
180
|
+
handleInit,
|
|
181
|
+
handleIndex,
|
|
182
|
+
handleSearch,
|
|
183
|
+
handleStatus,
|
|
184
|
+
handleMcpSetup,
|
|
185
|
+
handleClean,
|
|
186
|
+
handleUnknown,
|
|
187
|
+
]);
|
|
188
|
+
return { isCommand, executeCommand };
|
|
189
|
+
}
|
|
@@ -2,13 +2,10 @@
|
|
|
2
2
|
* CLI command handling hook.
|
|
3
3
|
* Consolidates all command routing and handler implementations.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import type { AppStatus, SearchResultsData } from '../../common/types.js';
|
|
5
|
+
import type { SearchResultsData } from '../../common/types.js';
|
|
7
6
|
type RagCommandContext = {
|
|
8
7
|
addOutput: (type: 'user' | 'system', content: string) => void;
|
|
9
8
|
addSearchResults: (data: SearchResultsData) => void;
|
|
10
|
-
setAppStatus: (status: AppStatus) => void;
|
|
11
|
-
setIndexStats: (stats: IndexDisplayStats | null) => void;
|
|
12
9
|
projectRoot: string;
|
|
13
10
|
stdout: NodeJS.WriteStream;
|
|
14
11
|
startInitWizard: (isReinit: boolean) => void;
|
|
@@ -16,7 +13,7 @@ type RagCommandContext = {
|
|
|
16
13
|
startCleanWizard: () => void;
|
|
17
14
|
isInitialized: boolean;
|
|
18
15
|
};
|
|
19
|
-
export declare function useRagCommands({ addOutput, addSearchResults,
|
|
16
|
+
export declare function useRagCommands({ addOutput, addSearchResults, projectRoot, stdout, startInitWizard, startMcpSetupWizard, startCleanWizard, isInitialized, }: RagCommandContext): {
|
|
20
17
|
isCommand: (text: string) => boolean;
|
|
21
18
|
executeCommand: (text: string) => void;
|
|
22
19
|
};
|
|
@@ -6,7 +6,9 @@ import { useCallback } from 'react';
|
|
|
6
6
|
import { useApp } from 'ink';
|
|
7
7
|
import { runIndex, formatIndexStats, runSearch, getStatus, loadIndexStats, } from './handlers.js';
|
|
8
8
|
import { setupVSCodeTerminal } from '../../common/commands/terminalSetup.js';
|
|
9
|
-
|
|
9
|
+
import { useAppDispatch, AppActions } from '../../store/index.js';
|
|
10
|
+
export function useRagCommands({ addOutput, addSearchResults, projectRoot, stdout, startInitWizard, startMcpSetupWizard, startCleanWizard, isInitialized, }) {
|
|
11
|
+
const dispatch = useAppDispatch();
|
|
10
12
|
const { exit } = useApp();
|
|
11
13
|
// Command handlers
|
|
12
14
|
const handleHelp = useCallback(() => {
|
|
@@ -54,30 +56,21 @@ Manual MCP Setup:
|
|
|
54
56
|
const handleIndex = useCallback((force) => {
|
|
55
57
|
const action = force ? 'Reindexing' : 'Indexing';
|
|
56
58
|
addOutput('system', `${action} codebase...`);
|
|
57
|
-
|
|
58
|
-
runIndex(projectRoot, force
|
|
59
|
-
state: 'indexing',
|
|
60
|
-
current,
|
|
61
|
-
total,
|
|
62
|
-
stage,
|
|
63
|
-
throttleMessage,
|
|
64
|
-
chunksProcessed,
|
|
65
|
-
}))
|
|
59
|
+
// Progress is synced via DaemonStatusContext polling
|
|
60
|
+
runIndex(projectRoot, force)
|
|
66
61
|
.then(async (stats) => {
|
|
67
62
|
addOutput('system', formatIndexStats(stats));
|
|
68
63
|
// Reload stats after indexing
|
|
69
64
|
const newStats = await loadIndexStats(projectRoot);
|
|
70
|
-
setIndexStats(newStats);
|
|
71
|
-
setAppStatus({ state: 'ready' });
|
|
65
|
+
dispatch(AppActions.setIndexStats(newStats));
|
|
72
66
|
})
|
|
73
67
|
.catch(err => {
|
|
74
68
|
addOutput('system', `Index failed: ${err.message}`);
|
|
75
|
-
setAppStatus({ state: 'ready' });
|
|
76
69
|
});
|
|
77
|
-
}, [projectRoot, addOutput,
|
|
70
|
+
}, [projectRoot, addOutput, dispatch]);
|
|
78
71
|
const handleSearch = useCallback((query) => {
|
|
79
72
|
addOutput('system', `Searching for "${query}"...`);
|
|
80
|
-
|
|
73
|
+
dispatch(AppActions.setSearching());
|
|
81
74
|
runSearch(projectRoot, query)
|
|
82
75
|
.then(results => {
|
|
83
76
|
// Use the component-based display with syntax highlighting
|
|
@@ -95,13 +88,13 @@ Manual MCP Setup:
|
|
|
95
88
|
text: r.text,
|
|
96
89
|
})),
|
|
97
90
|
});
|
|
98
|
-
|
|
91
|
+
dispatch(AppActions.setReady());
|
|
99
92
|
})
|
|
100
93
|
.catch(err => {
|
|
101
94
|
addOutput('system', `Search failed: ${err.message}`);
|
|
102
|
-
|
|
95
|
+
dispatch(AppActions.setReady());
|
|
103
96
|
});
|
|
104
|
-
}, [projectRoot, addOutput, addSearchResults,
|
|
97
|
+
}, [projectRoot, addOutput, addSearchResults, dispatch]);
|
|
105
98
|
const handleStatus = useCallback(() => {
|
|
106
99
|
getStatus(projectRoot)
|
|
107
100
|
.then(status => addOutput('system', status))
|
|
@@ -34,16 +34,6 @@ const PROVIDER_CONFIG = {
|
|
|
34
34
|
note: '~700MB download, ~1.2GB RAM',
|
|
35
35
|
description: 'Offline, no API key needed',
|
|
36
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
37
|
gemini: {
|
|
48
38
|
name: 'Gemini',
|
|
49
39
|
model: 'gemini-embedding-001',
|
|
@@ -66,23 +56,21 @@ const PROVIDER_CONFIG = {
|
|
|
66
56
|
},
|
|
67
57
|
openai: {
|
|
68
58
|
name: 'OpenAI',
|
|
69
|
-
model: 'text-embed-3-
|
|
70
|
-
modelFull: 'text-embedding-3-
|
|
59
|
+
model: 'text-embed-3-lg',
|
|
60
|
+
modelFull: 'text-embedding-3-large',
|
|
71
61
|
dims: '1536',
|
|
72
62
|
context: '8K',
|
|
73
|
-
cost: '$0.
|
|
63
|
+
cost: '$0.13/1M',
|
|
74
64
|
note: 'API key required',
|
|
75
|
-
description: '
|
|
65
|
+
description: 'Highest quality embeddings',
|
|
76
66
|
},
|
|
77
67
|
};
|
|
78
|
-
// Simple provider options for selection
|
|
79
|
-
// Note: local-4b exists in code but hidden from UI - no transformers.js-compatible ONNX available yet
|
|
68
|
+
// Simple provider options for selection (ordered by speed/ease of use)
|
|
80
69
|
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
70
|
{ label: 'Gemini - gemini-embedding-001 (Free tier)', value: 'gemini' },
|
|
71
|
+
{ label: 'OpenAI - text-embedding-3-large', value: 'openai' },
|
|
84
72
|
{ label: 'Mistral - codestral-embed', value: 'mistral' },
|
|
85
|
-
{ label: '
|
|
73
|
+
{ label: 'Local - Qwen3-0.6B Q8 (slow)', value: 'local' },
|
|
86
74
|
];
|
|
87
75
|
/**
|
|
88
76
|
* Local model info.
|
|
@@ -109,9 +97,9 @@ const FRONTIER_MODELS_DATA = [
|
|
|
109
97
|
},
|
|
110
98
|
{
|
|
111
99
|
Provider: 'OpenAI',
|
|
112
|
-
Model: 'text-embed-3-
|
|
100
|
+
Model: 'text-embed-3-large',
|
|
113
101
|
Dims: '1536',
|
|
114
|
-
Cost: '$0.
|
|
102
|
+
Cost: '$0.13/1M',
|
|
115
103
|
},
|
|
116
104
|
];
|
|
117
105
|
function SimpleTable({ data, columns, }) {
|
|
@@ -158,7 +146,7 @@ function ComparisonTable() {
|
|
|
158
146
|
React.createElement(Text, { dimColor: true, italic: true }, "* Initial indexing may take time. Future updates are incremental."),
|
|
159
147
|
React.createElement(Box, { marginTop: 1 },
|
|
160
148
|
React.createElement(Text, { bold: true, color: "green" }, "Frontier Models"),
|
|
161
|
-
React.createElement(Text, { dimColor: true }, " - Fastest, Best Quality")),
|
|
149
|
+
React.createElement(Text, { dimColor: true }, " - Fastest, Best Quality, Largest Vocabulary")),
|
|
162
150
|
React.createElement(SimpleTable, { data: FRONTIER_MODELS_DATA, columns: frontierColumns })));
|
|
163
151
|
}
|
|
164
152
|
// Reinit confirmation options
|
|
@@ -176,6 +164,21 @@ const API_KEY_ACTION_ITEMS = [
|
|
|
176
164
|
{ label: 'Keep existing API key', value: 'keep' },
|
|
177
165
|
{ label: 'Enter new API key', value: 'new' },
|
|
178
166
|
];
|
|
167
|
+
// OpenAI region options for data residency
|
|
168
|
+
const OPENAI_REGION_ITEMS = [
|
|
169
|
+
{
|
|
170
|
+
label: 'Default (api.openai.com) - Recommended',
|
|
171
|
+
value: 'default',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
label: 'US (us.api.openai.com) - US Data Residency',
|
|
175
|
+
value: 'us',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
label: 'EU (eu.api.openai.com) - EU Data Residency',
|
|
179
|
+
value: 'eu',
|
|
180
|
+
},
|
|
181
|
+
];
|
|
179
182
|
/**
|
|
180
183
|
* Simple text input component for API key entry.
|
|
181
184
|
* Uses a ref to accumulate input, which handles paste better than
|
|
@@ -227,6 +230,8 @@ export function InitWizard({ step, config, isReinit, existingApiKey, existingPro
|
|
|
227
230
|
// State for API key input
|
|
228
231
|
const [apiKeyInput, setApiKeyInput] = useState('');
|
|
229
232
|
const [apiKeyAction, setApiKeyAction] = useState(null);
|
|
233
|
+
// State for OpenAI region selection (shown after API key for OpenAI)
|
|
234
|
+
const [showRegionSelect, setShowRegionSelect] = useState(false);
|
|
230
235
|
// Handle Escape to cancel
|
|
231
236
|
useInput((input, key) => {
|
|
232
237
|
if (key.escape || (key.ctrl && input === 'c')) {
|
|
@@ -275,15 +280,16 @@ export function InitWizard({ step, config, isReinit, existingApiKey, existingPro
|
|
|
275
280
|
if (effectiveStep === 1) {
|
|
276
281
|
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
277
282
|
React.createElement(Text, { bold: true }, "Choose Embedding Provider"),
|
|
283
|
+
React.createElement(ComparisonTable, null),
|
|
278
284
|
React.createElement(Box, { marginTop: 1 },
|
|
279
285
|
React.createElement(SelectInput, { items: PROVIDER_ITEMS, onSelect: item => {
|
|
280
|
-
// Reset API key state when provider changes
|
|
286
|
+
// Reset API key and region state when provider changes
|
|
281
287
|
setApiKeyInput('');
|
|
282
288
|
setApiKeyAction(null);
|
|
289
|
+
setShowRegionSelect(false);
|
|
283
290
|
// Use relative increment: step + 1
|
|
284
291
|
onStepChange(normalizedStep + 1, { provider: item.value });
|
|
285
292
|
} })),
|
|
286
|
-
React.createElement(ComparisonTable, null),
|
|
287
293
|
React.createElement(Box, { marginTop: 1 },
|
|
288
294
|
React.createElement(Text, { dimColor: true }, "\u2191/\u2193 navigate, Enter select, Esc cancel"))));
|
|
289
295
|
}
|
|
@@ -298,6 +304,21 @@ export function InitWizard({ step, config, isReinit, existingApiKey, existingPro
|
|
|
298
304
|
const provider = currentProvider;
|
|
299
305
|
const info = PROVIDER_CONFIG[provider];
|
|
300
306
|
const apiKeyUrl = API_KEY_URLS[provider];
|
|
307
|
+
const isOpenAI = provider === 'openai';
|
|
308
|
+
// Show OpenAI region selection after API key is entered
|
|
309
|
+
if (isOpenAI && showRegionSelect) {
|
|
310
|
+
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
311
|
+
React.createElement(Text, { bold: true }, "Select OpenAI API Region"),
|
|
312
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
313
|
+
React.createElement(Text, { dimColor: true }, "Corporate accounts with data residency require regional endpoints."),
|
|
314
|
+
React.createElement(Text, { dimColor: true }, "Most users should select Default.")),
|
|
315
|
+
React.createElement(Box, { marginTop: 1 },
|
|
316
|
+
React.createElement(SelectInput, { items: OPENAI_REGION_ITEMS, onSelect: item => {
|
|
317
|
+
onStepChange(normalizedStep + 1, { openaiRegion: item.value });
|
|
318
|
+
} })),
|
|
319
|
+
React.createElement(Box, { marginTop: 1 },
|
|
320
|
+
React.createElement(Text, { dimColor: true }, "\u2191/\u2193 navigate, Enter select, Esc cancel"))));
|
|
321
|
+
}
|
|
301
322
|
return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 },
|
|
302
323
|
React.createElement(Text, { bold: true },
|
|
303
324
|
"Configure ",
|
|
@@ -316,8 +337,18 @@ export function InitWizard({ step, config, isReinit, existingApiKey, existingPro
|
|
|
316
337
|
React.createElement(Box, { marginTop: 1 },
|
|
317
338
|
React.createElement(SelectInput, { items: API_KEY_ACTION_ITEMS, onSelect: item => {
|
|
318
339
|
if (item.value === 'keep') {
|
|
319
|
-
// Keep existing key
|
|
320
|
-
onStepChange(normalizedStep
|
|
340
|
+
// Keep existing key
|
|
341
|
+
onStepChange(normalizedStep, { apiKey: existingApiKey });
|
|
342
|
+
if (isOpenAI) {
|
|
343
|
+
// Show region selection for OpenAI
|
|
344
|
+
setShowRegionSelect(true);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// Advance to confirmation for other providers
|
|
348
|
+
onStepChange(normalizedStep + 1, {
|
|
349
|
+
apiKey: existingApiKey,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
321
352
|
}
|
|
322
353
|
else {
|
|
323
354
|
// Show text input for new key
|
|
@@ -325,7 +356,15 @@ export function InitWizard({ step, config, isReinit, existingApiKey, existingPro
|
|
|
325
356
|
}
|
|
326
357
|
} })))) : (React.createElement(ApiKeyInputStep, { providerName: info.name, apiKeyInput: apiKeyInput, setApiKeyInput: setApiKeyInput, onSubmit: key => {
|
|
327
358
|
if (key.trim()) {
|
|
328
|
-
onStepChange(normalizedStep
|
|
359
|
+
onStepChange(normalizedStep, { apiKey: key.trim() });
|
|
360
|
+
if (isOpenAI) {
|
|
361
|
+
// Show region selection for OpenAI
|
|
362
|
+
setShowRegionSelect(true);
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
// Advance to confirmation for other providers
|
|
366
|
+
onStepChange(normalizedStep + 1, { apiKey: key.trim() });
|
|
367
|
+
}
|
|
329
368
|
}
|
|
330
369
|
} })),
|
|
331
370
|
React.createElement(Box, { marginTop: 1 },
|
|
@@ -77,7 +77,21 @@ export function McpSetupWizard({ step, config, projectRoot, showPrompt, onStepCh
|
|
|
77
77
|
}
|
|
78
78
|
else {
|
|
79
79
|
// Auto setup
|
|
80
|
-
|
|
80
|
+
try {
|
|
81
|
+
result = await writeMcpConfig(currentEditor, config.selectedScope, projectRoot);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
85
|
+
result = {
|
|
86
|
+
success: false,
|
|
87
|
+
editor: currentEditor.id,
|
|
88
|
+
method: 'file-created',
|
|
89
|
+
error: `Failed to write MCP config: ${message}`,
|
|
90
|
+
};
|
|
91
|
+
if (addOutput) {
|
|
92
|
+
addOutput('system', `Error: ${message}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
81
95
|
}
|
|
82
96
|
onStepChange('summary', { result });
|
|
83
97
|
setIsProcessing(false);
|
|
@@ -89,9 +103,14 @@ export function McpSetupWizard({ step, config, projectRoot, showPrompt, onStepCh
|
|
|
89
103
|
const relativePath = config.result.configPath.startsWith(projectRoot)
|
|
90
104
|
? config.result.configPath.slice(projectRoot.length + 1)
|
|
91
105
|
: config.result.configPath;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
try {
|
|
107
|
+
const success = await addToGitignore(projectRoot, relativePath);
|
|
108
|
+
if (success) {
|
|
109
|
+
setGitignoreAdded(relativePath);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Silently fail - gitignore is optional and non-critical
|
|
95
114
|
}
|
|
96
115
|
}
|
|
97
116
|
setGitignoreHandled(true);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Individual slot row component for displaying batch processing progress.
|
|
3
|
+
*
|
|
4
|
+
* Each SlotRow reads its slot from DaemonStatusContext and renders independently.
|
|
5
|
+
* This follows the Single Responsibility Principle - each row manages its own
|
|
6
|
+
* rendering logic based on its slot index.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
type Props = {
|
|
10
|
+
/** The slot index (0 to CONCURRENCY-1) */
|
|
11
|
+
slotIndex: number;
|
|
12
|
+
/** Whether this is the last visible slot (for tree-drawing) */
|
|
13
|
+
isLast: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Renders a single slot's progress status.
|
|
17
|
+
*
|
|
18
|
+
* Always renders a fixed-height row to prevent UI jumping when slots
|
|
19
|
+
* become active/idle. Idle slots show an empty placeholder.
|
|
20
|
+
*/
|
|
21
|
+
export declare function SlotRow({ slotIndex, isLast }: Props): React.ReactElement;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Individual slot row component for displaying batch processing progress.
|
|
3
|
+
*
|
|
4
|
+
* Each SlotRow reads its slot from DaemonStatusContext and renders independently.
|
|
5
|
+
* This follows the Single Responsibility Principle - each row manages its own
|
|
6
|
+
* rendering logic based on its slot index.
|
|
7
|
+
*/
|
|
8
|
+
import React, { useState, useEffect } from 'react';
|
|
9
|
+
import { Box, Text } from 'ink';
|
|
10
|
+
import { useDaemonStatus } from '../contexts/DaemonStatusContext.js';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Constants
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/** Braille dots spinner frames */
|
|
15
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
16
|
+
/** Default idle slot state */
|
|
17
|
+
const IDLE_SLOT = { state: 'idle', batchInfo: null, retryInfo: null };
|
|
18
|
+
/**
|
|
19
|
+
* Renders a single slot's progress status.
|
|
20
|
+
*
|
|
21
|
+
* Always renders a fixed-height row to prevent UI jumping when slots
|
|
22
|
+
* become active/idle. Idle slots show an empty placeholder.
|
|
23
|
+
*/
|
|
24
|
+
export function SlotRow({ slotIndex, isLast }) {
|
|
25
|
+
const daemonStatus = useDaemonStatus();
|
|
26
|
+
const slot = daemonStatus?.slots[slotIndex] ?? IDLE_SLOT;
|
|
27
|
+
const [frame, setFrame] = useState(0);
|
|
28
|
+
// Spinner animation - only runs when processing
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (slot.state !== 'processing')
|
|
31
|
+
return;
|
|
32
|
+
const timer = setInterval(() => {
|
|
33
|
+
setFrame(f => (f + 1) % SPINNER_FRAMES.length);
|
|
34
|
+
}, 80);
|
|
35
|
+
return () => clearInterval(timer);
|
|
36
|
+
}, [slot.state]);
|
|
37
|
+
// Tree-drawing prefix
|
|
38
|
+
const prefix = isLast ? '└ ' : '├ ';
|
|
39
|
+
// Idle slots render empty placeholder to maintain fixed height
|
|
40
|
+
if (slot.state === 'idle') {
|
|
41
|
+
return (React.createElement(Box, null,
|
|
42
|
+
React.createElement(Text, { dimColor: true }, prefix),
|
|
43
|
+
React.createElement(Text, { dimColor: true }, "\u00B7")));
|
|
44
|
+
}
|
|
45
|
+
return (React.createElement(Box, null,
|
|
46
|
+
React.createElement(Text, { dimColor: true }, prefix),
|
|
47
|
+
slot.state === 'rate-limited' ? (React.createElement(Text, { color: "yellow" },
|
|
48
|
+
"\u26A0 ",
|
|
49
|
+
slot.batchInfo,
|
|
50
|
+
slot.retryInfo && ` · ${slot.retryInfo}`)) : (React.createElement(React.Fragment, null,
|
|
51
|
+
React.createElement(Text, { color: "cyan" },
|
|
52
|
+
SPINNER_FRAMES[frame],
|
|
53
|
+
" "),
|
|
54
|
+
React.createElement(Text, { dimColor: true }, slot.batchInfo)))));
|
|
55
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBar Component
|
|
3
|
+
*
|
|
4
|
+
* Displays current status and indexing progress.
|
|
5
|
+
* Uses DaemonStatusContext for daemon state instead of Redux.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import type { AppStatus, IndexDisplayStats } from '../../common/types.js';
|
|
9
|
+
type Props = {
|
|
10
|
+
status: AppStatus;
|
|
11
|
+
stats: IndexDisplayStats | null | undefined;
|
|
12
|
+
};
|
|
13
|
+
export default function StatusBar({ status, stats }: Props): React.JSX.Element;
|
|
14
|
+
export {};
|