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.
Files changed (209) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/app.d.ts +3 -0
  3. package/dist/cli/app.js +100 -102
  4. package/dist/cli/commands/handlers.d.ts +8 -6
  5. package/dist/cli/commands/handlers.js +90 -32
  6. package/dist/cli/commands/useCommands.d.ts +20 -0
  7. package/dist/cli/commands/useCommands.js +189 -0
  8. package/dist/cli/commands/useRagCommands.d.ts +2 -5
  9. package/dist/cli/commands/useRagCommands.js +11 -18
  10. package/dist/cli/components/InitWizard.js +66 -27
  11. package/dist/cli/components/McpSetupWizard.js +23 -4
  12. package/dist/cli/components/SlotRow.d.ts +22 -0
  13. package/dist/cli/components/SlotRow.js +55 -0
  14. package/dist/cli/components/StatusBar.d.ts +14 -0
  15. package/dist/cli/components/StatusBar.js +156 -0
  16. package/dist/cli/contexts/DaemonStatusContext.d.ts +38 -0
  17. package/dist/cli/contexts/DaemonStatusContext.js +106 -0
  18. package/dist/cli/hooks/useStatusPolling.d.ts +34 -0
  19. package/dist/cli/hooks/useStatusPolling.js +121 -0
  20. package/dist/cli/store/app/selectors.d.ts +87 -0
  21. package/dist/cli/store/app/selectors.js +28 -0
  22. package/dist/cli/store/app/slice.d.ts +1013 -0
  23. package/dist/cli/store/app/slice.js +112 -0
  24. package/dist/cli/store/hooks.d.ts +22 -0
  25. package/dist/cli/store/hooks.js +17 -0
  26. package/dist/cli/store/store.d.ts +17 -0
  27. package/dist/cli/store/store.js +18 -0
  28. package/dist/cli/store/wizard/selectors.d.ts +115 -0
  29. package/dist/cli/store/wizard/selectors.js +36 -0
  30. package/dist/cli/store/wizard/slice.d.ts +523 -0
  31. package/dist/cli/store/wizard/slice.js +119 -0
  32. package/dist/cli/utils/error-handler.d.ts +55 -0
  33. package/dist/cli/utils/error-handler.js +92 -0
  34. package/dist/client/auto-start.d.ts +42 -0
  35. package/dist/client/auto-start.js +250 -0
  36. package/dist/client/connection.d.ts +48 -0
  37. package/dist/client/connection.js +200 -0
  38. package/dist/client/index.d.ts +93 -0
  39. package/dist/client/index.js +209 -0
  40. package/dist/client/types.d.ts +105 -0
  41. package/dist/client/types.js +7 -0
  42. package/dist/common/components/SlotRow.d.ts +22 -0
  43. package/dist/common/components/SlotRow.js +53 -0
  44. package/dist/common/components/StatusBar.js +82 -31
  45. package/dist/common/types.d.ts +12 -13
  46. package/dist/daemon/handlers.d.ts +15 -0
  47. package/dist/daemon/handlers.js +157 -0
  48. package/dist/daemon/index.d.ts +21 -0
  49. package/dist/daemon/index.js +123 -0
  50. package/dist/daemon/lib/chunker/bounded-channel.d.ts +51 -0
  51. package/dist/daemon/lib/chunker/bounded-channel.js +138 -0
  52. package/dist/daemon/lib/chunker/index.d.ts +135 -0
  53. package/dist/daemon/lib/chunker/index.js +1370 -0
  54. package/dist/daemon/lib/chunker/types.d.ts +77 -0
  55. package/dist/daemon/lib/chunker/types.js +50 -0
  56. package/dist/daemon/lib/config.d.ts +73 -0
  57. package/dist/daemon/lib/config.js +149 -0
  58. package/dist/daemon/lib/constants.d.ts +75 -0
  59. package/dist/daemon/lib/constants.js +114 -0
  60. package/dist/daemon/lib/gitignore.d.ts +57 -0
  61. package/dist/daemon/lib/gitignore.js +246 -0
  62. package/dist/daemon/lib/logger.d.ts +51 -0
  63. package/dist/daemon/lib/logger.js +167 -0
  64. package/dist/daemon/lib/manifest.d.ts +58 -0
  65. package/dist/daemon/lib/manifest.js +116 -0
  66. package/dist/daemon/lib/merkle/diff.d.ts +32 -0
  67. package/dist/daemon/lib/merkle/diff.js +107 -0
  68. package/dist/daemon/lib/merkle/hash.d.ts +40 -0
  69. package/dist/daemon/lib/merkle/hash.js +180 -0
  70. package/dist/daemon/lib/merkle/index.d.ts +71 -0
  71. package/dist/daemon/lib/merkle/index.js +309 -0
  72. package/dist/daemon/lib/merkle/node.d.ts +55 -0
  73. package/dist/daemon/lib/merkle/node.js +82 -0
  74. package/dist/daemon/lifecycle.d.ts +50 -0
  75. package/dist/daemon/lifecycle.js +142 -0
  76. package/dist/daemon/owner.d.ts +175 -0
  77. package/dist/daemon/owner.js +609 -0
  78. package/dist/daemon/protocol.d.ts +100 -0
  79. package/dist/daemon/protocol.js +163 -0
  80. package/dist/daemon/providers/api-utils.d.ts +130 -0
  81. package/dist/daemon/providers/api-utils.js +248 -0
  82. package/dist/daemon/providers/gemini.d.ts +39 -0
  83. package/dist/daemon/providers/gemini.js +205 -0
  84. package/dist/daemon/providers/index.d.ts +14 -0
  85. package/dist/daemon/providers/index.js +14 -0
  86. package/dist/daemon/providers/local-4b.d.ts +28 -0
  87. package/dist/daemon/providers/local-4b.js +51 -0
  88. package/dist/daemon/providers/local.d.ts +36 -0
  89. package/dist/daemon/providers/local.js +166 -0
  90. package/dist/daemon/providers/mistral.d.ts +35 -0
  91. package/dist/daemon/providers/mistral.js +160 -0
  92. package/dist/daemon/providers/mock.d.ts +35 -0
  93. package/dist/daemon/providers/mock.js +69 -0
  94. package/dist/daemon/providers/openai.d.ts +41 -0
  95. package/dist/daemon/providers/openai.js +190 -0
  96. package/dist/daemon/providers/types.d.ts +68 -0
  97. package/dist/daemon/providers/types.js +6 -0
  98. package/dist/daemon/providers/validate.d.ts +30 -0
  99. package/dist/daemon/providers/validate.js +162 -0
  100. package/dist/daemon/server.d.ts +79 -0
  101. package/dist/daemon/server.js +293 -0
  102. package/dist/daemon/services/index.d.ts +11 -0
  103. package/dist/daemon/services/index.js +16 -0
  104. package/dist/daemon/services/indexing.d.ts +117 -0
  105. package/dist/daemon/services/indexing.js +573 -0
  106. package/dist/daemon/services/search/filters.d.ts +21 -0
  107. package/dist/daemon/services/search/filters.js +106 -0
  108. package/dist/daemon/services/search/fts.d.ts +32 -0
  109. package/dist/daemon/services/search/fts.js +61 -0
  110. package/dist/daemon/services/search/hybrid.d.ts +17 -0
  111. package/dist/daemon/services/search/hybrid.js +58 -0
  112. package/dist/daemon/services/search/index.d.ts +108 -0
  113. package/dist/daemon/services/search/index.js +417 -0
  114. package/dist/daemon/services/search/types.d.ts +126 -0
  115. package/dist/daemon/services/search/types.js +4 -0
  116. package/dist/daemon/services/search/vector.d.ts +25 -0
  117. package/dist/daemon/services/search/vector.js +44 -0
  118. package/dist/daemon/services/storage/index.d.ts +110 -0
  119. package/dist/daemon/services/storage/index.js +378 -0
  120. package/dist/daemon/services/storage/schema.d.ts +24 -0
  121. package/dist/daemon/services/storage/schema.js +51 -0
  122. package/dist/daemon/services/storage/types.d.ts +105 -0
  123. package/dist/daemon/services/storage/types.js +71 -0
  124. package/dist/daemon/services/types.d.ts +192 -0
  125. package/dist/daemon/services/types.js +53 -0
  126. package/dist/daemon/services/watcher.d.ts +98 -0
  127. package/dist/daemon/services/watcher.js +386 -0
  128. package/dist/daemon/state.d.ts +119 -0
  129. package/dist/daemon/state.js +161 -0
  130. package/dist/mcp/index.d.ts +1 -1
  131. package/dist/mcp/index.js +44 -60
  132. package/dist/mcp/server.d.ts +10 -14
  133. package/dist/mcp/server.js +75 -74
  134. package/dist/mcp/services/lazy-loader.d.ts +23 -0
  135. package/dist/mcp/services/lazy-loader.js +34 -0
  136. package/dist/mcp/warmup.d.ts +3 -3
  137. package/dist/mcp/warmup.js +39 -40
  138. package/dist/mcp/watcher.d.ts +5 -7
  139. package/dist/mcp/watcher.js +73 -64
  140. package/dist/rag/config/index.d.ts +2 -0
  141. package/dist/rag/constants.d.ts +30 -0
  142. package/dist/rag/constants.js +38 -0
  143. package/dist/rag/embeddings/api-utils.d.ts +121 -0
  144. package/dist/rag/embeddings/api-utils.js +259 -0
  145. package/dist/rag/embeddings/gemini.d.ts +4 -12
  146. package/dist/rag/embeddings/gemini.js +22 -72
  147. package/dist/rag/embeddings/index.d.ts +5 -3
  148. package/dist/rag/embeddings/index.js +5 -2
  149. package/dist/rag/embeddings/local-4b.d.ts +2 -2
  150. package/dist/rag/embeddings/local-4b.js +1 -1
  151. package/dist/rag/embeddings/local.d.ts +10 -3
  152. package/dist/rag/embeddings/local.js +58 -12
  153. package/dist/rag/embeddings/mistral.d.ts +4 -12
  154. package/dist/rag/embeddings/mistral.js +22 -72
  155. package/dist/rag/embeddings/mock.d.ts +35 -0
  156. package/dist/rag/embeddings/mock.js +69 -0
  157. package/dist/rag/embeddings/openai.d.ts +11 -13
  158. package/dist/rag/embeddings/openai.js +47 -75
  159. package/dist/rag/embeddings/types.d.ts +27 -1
  160. package/dist/rag/embeddings/validate.d.ts +9 -1
  161. package/dist/rag/embeddings/validate.js +17 -4
  162. package/dist/rag/index.d.ts +2 -2
  163. package/dist/rag/index.js +1 -1
  164. package/dist/rag/indexer/bounded-channel.d.ts +51 -0
  165. package/dist/rag/indexer/bounded-channel.js +138 -0
  166. package/dist/rag/indexer/indexer.d.ts +4 -14
  167. package/dist/rag/indexer/indexer.js +246 -169
  168. package/dist/rag/indexer/types.d.ts +1 -0
  169. package/dist/rag/logger/index.d.ts +22 -0
  170. package/dist/rag/logger/index.js +78 -1
  171. package/dist/rag/manifest/index.js +1 -2
  172. package/dist/rag/search/index.js +1 -1
  173. package/dist/rag/storage/schema.d.ts +2 -4
  174. package/dist/rag/storage/schema.js +3 -5
  175. package/dist/store/app/selectors.d.ts +87 -0
  176. package/dist/store/app/selectors.js +28 -0
  177. package/dist/store/app/slice.d.ts +1013 -0
  178. package/dist/store/app/slice.js +112 -0
  179. package/dist/store/hooks.d.ts +22 -0
  180. package/dist/store/hooks.js +17 -0
  181. package/dist/store/index.d.ts +12 -0
  182. package/dist/store/index.js +18 -0
  183. package/dist/store/indexing/listeners.d.ts +25 -0
  184. package/dist/store/indexing/listeners.js +46 -0
  185. package/dist/store/indexing/selectors.d.ts +195 -0
  186. package/dist/store/indexing/selectors.js +69 -0
  187. package/dist/store/indexing/slice.d.ts +309 -0
  188. package/dist/store/indexing/slice.js +113 -0
  189. package/dist/store/slot-progress/listeners.d.ts +23 -0
  190. package/dist/store/slot-progress/listeners.js +33 -0
  191. package/dist/store/slot-progress/selectors.d.ts +67 -0
  192. package/dist/store/slot-progress/selectors.js +36 -0
  193. package/dist/store/slot-progress/slice.d.ts +246 -0
  194. package/dist/store/slot-progress/slice.js +70 -0
  195. package/dist/store/store.d.ts +17 -0
  196. package/dist/store/store.js +18 -0
  197. package/dist/store/warmup/selectors.d.ts +109 -0
  198. package/dist/store/warmup/selectors.js +44 -0
  199. package/dist/store/warmup/slice.d.ts +137 -0
  200. package/dist/store/warmup/slice.js +72 -0
  201. package/dist/store/watcher/selectors.d.ts +115 -0
  202. package/dist/store/watcher/selectors.js +52 -0
  203. package/dist/store/watcher/slice.d.ts +269 -0
  204. package/dist/store/watcher/slice.js +100 -0
  205. package/dist/store/wizard/selectors.d.ts +115 -0
  206. package/dist/store/wizard/selectors.js +36 -0
  207. package/dist/store/wizard/slice.d.ts +523 -0
  208. package/dist/store/wizard/slice.js +119 -0
  209. 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 { type IndexDisplayStats } from './handlers.js';
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, setAppStatus, setIndexStats, projectRoot, stdout, startInitWizard, startMcpSetupWizard, startCleanWizard, isInitialized, }: RagCommandContext): {
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
- export function useRagCommands({ addOutput, addSearchResults, setAppStatus, setIndexStats, projectRoot, stdout, startInitWizard, startMcpSetupWizard, startCleanWizard, isInitialized, }) {
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
- setAppStatus({ state: 'indexing', current: 0, total: 0, stage: action });
58
- runIndex(projectRoot, force, (current, total, stage, throttleMessage, chunksProcessed) => setAppStatus({
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, setAppStatus, setIndexStats]);
70
+ }, [projectRoot, addOutput, dispatch]);
78
71
  const handleSearch = useCallback((query) => {
79
72
  addOutput('system', `Searching for "${query}"...`);
80
- setAppStatus({ state: 'searching' });
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
- setAppStatus({ state: 'ready' });
91
+ dispatch(AppActions.setReady());
99
92
  })
100
93
  .catch(err => {
101
94
  addOutput('system', `Search failed: ${err.message}`);
102
- setAppStatus({ state: 'ready' });
95
+ dispatch(AppActions.setReady());
103
96
  });
104
- }, [projectRoot, addOutput, addSearchResults, setAppStatus]);
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-sm',
70
- modelFull: 'text-embedding-3-small',
59
+ model: 'text-embed-3-lg',
60
+ modelFull: 'text-embedding-3-large',
71
61
  dims: '1536',
72
62
  context: '8K',
73
- cost: '$0.02/1M',
63
+ cost: '$0.13/1M',
74
64
  note: 'API key required',
75
- description: 'Fast and reliable API',
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: 'OpenAI - text-embedding-3-small', value: 'openai' },
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-small',
100
+ Model: 'text-embed-3-large',
113
101
  Dims: '1536',
114
- Cost: '$0.02/1M',
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, advance to confirmation
320
- onStepChange(normalizedStep + 1, { apiKey: existingApiKey });
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 + 1, { apiKey: key.trim() });
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
- result = await writeMcpConfig(currentEditor, config.selectedScope, projectRoot);
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
- const success = await addToGitignore(projectRoot, relativePath);
93
- if (success) {
94
- setGitignoreAdded(relativePath);
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 {};