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
package/README.md CHANGED
@@ -575,13 +575,13 @@ Choose your embedding provider during `/init`:
575
575
  - Initial indexing may take time; future updates are incremental
576
576
  - Works great for code and natural language (docs, docstrings, code comments, etc.)
577
577
 
578
- ### Cloud Providers - Fastest, Best Quality
578
+ ### Cloud Providers - Fastest, Best Quality, Largest Vocabulary
579
579
 
580
580
  | Provider | Model | Dims | Cost | Get API Key |
581
581
  | -------- | ---------------------- | ---- | --------- | ------------------------------------------------------- |
582
582
  | Gemini | gemini-embedding-001 | 1536 | Free tier | [Google AI Studio](https://aistudio.google.com) |
583
583
  | Mistral | codestral-embed | 1536 | $0.10/1M | [Mistral Console](https://console.mistral.ai/api-keys/) |
584
- | OpenAI | text-embedding-3-small | 1536 | $0.02/1M | [OpenAI Platform](https://platform.openai.com/api-keys) |
584
+ | OpenAI | text-embedding-3-large | 1536 | $0.13/1M | [OpenAI Platform](https://platform.openai.com/api-keys) |
585
585
 
586
586
  - **Gemini** - Free tier available, great for getting started
587
587
  - **Mistral** - Code-optimized embeddings for technical content
package/dist/cli/app.d.ts CHANGED
@@ -1,2 +1,5 @@
1
1
  import React from 'react';
2
+ /**
3
+ * Main App component with Redux Provider and DaemonStatusProvider.
4
+ */
2
5
  export default function App(): React.JSX.Element;
package/dist/cli/app.js CHANGED
@@ -1,9 +1,16 @@
1
- import React, { useState, useEffect, useCallback } from 'react';
1
+ import React, { useEffect, useCallback } from 'react';
2
2
  import { createRequire } from 'node:module';
3
3
  import { Box, Text, useStdout } from 'ink';
4
+ import { Provider } from 'react-redux';
5
+ import { store } from './store/store.js';
6
+ import { useAppDispatch, useAppSelector } from './store/hooks.js';
7
+ import { WizardActions } from './store/wizard/slice.js';
8
+ import { selectActiveWizard, selectInitStep, selectMcpStep, selectInitConfig, selectMcpConfig, selectIsReinit, selectShowMcpPrompt, selectExistingApiKey, selectExistingProvider, } from './store/wizard/selectors.js';
9
+ import { AppActions } from './store/app/slice.js';
10
+ import { selectIsInitialized, selectIndexStats, selectAppStatus, selectOutputItems, selectStartupLoaded, } from './store/app/selectors.js';
4
11
  // Common infrastructure
5
12
  import TextInput from '../common/components/TextInput.js';
6
- import StatusBar from '../common/components/StatusBar.js';
13
+ import StatusBar from './components/StatusBar.js';
7
14
  import StaticWithResize from '../common/components/StaticWithResize.js';
8
15
  import { useCtrlC } from '../common/hooks/useCtrlC.js';
9
16
  import { useCommandHistory } from '../common/hooks/useCommandHistory.js';
@@ -14,9 +21,11 @@ import SearchResultsDisplay from './components/SearchResultsDisplay.js';
14
21
  import InitWizard from './components/InitWizard.js';
15
22
  import McpSetupWizard from './components/McpSetupWizard.js';
16
23
  import CleanWizard from './components/CleanWizard.js';
17
- import { useRagCommands } from './commands/useRagCommands.js';
24
+ import { useCommands } from './commands/useCommands.js';
25
+ import { DaemonStatusProvider } from './contexts/DaemonStatusContext.js';
18
26
  import { checkInitialized, loadIndexStats, runInit, runIndex, formatIndexStats, } from './commands/handlers.js';
19
- import { getViberagDir, loadConfig } from '../rag/index.js';
27
+ import { getViberagDir } from '../daemon/lib/constants.js';
28
+ import { loadConfig } from '../daemon/lib/config.js';
20
29
  const require = createRequire(import.meta.url);
21
30
  // Path is relative from dist/ after compilation
22
31
  const { version } = require('../../package.json');
@@ -37,18 +46,28 @@ const COMMANDS = [
37
46
  { command: '/clean', description: 'Remove Viberag from project' },
38
47
  { command: '/quit', description: 'Exit the application' },
39
48
  ];
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);
49
+ /**
50
+ * Inner app content that uses Redux hooks.
51
+ * Must be rendered inside Provider.
52
+ */
53
+ function AppContent() {
54
+ const dispatch = useAppDispatch();
55
+ // Redux wizard state
56
+ const activeWizard = useAppSelector(selectActiveWizard);
57
+ const initStep = useAppSelector(selectInitStep);
58
+ const mcpStep = useAppSelector(selectMcpStep);
59
+ const initConfig = useAppSelector(selectInitConfig);
60
+ const mcpConfig = useAppSelector(selectMcpConfig);
61
+ const isReinit = useAppSelector(selectIsReinit);
62
+ const showMcpPrompt = useAppSelector(selectShowMcpPrompt);
63
+ const existingApiKey = useAppSelector(selectExistingApiKey);
64
+ const existingProvider = useAppSelector(selectExistingProvider);
65
+ // Redux app state (migrated from useState)
66
+ const isInitialized = useAppSelector(selectIsInitialized);
67
+ const indexStats = useAppSelector(selectIndexStats);
68
+ const appStatus = useAppSelector(selectAppStatus);
69
+ const outputItems = useAppSelector(selectOutputItems);
70
+ const startupLoaded = useAppSelector(selectStartupLoaded);
52
71
  const { stdout } = useStdout();
53
72
  // Enable Kitty keyboard protocol for Shift+Enter support in iTerm2/Kitty/WezTerm
54
73
  useKittyKeyboard();
@@ -57,144 +76,115 @@ export default function App() {
57
76
  // Check initialization status and load stats on mount
58
77
  useEffect(() => {
59
78
  checkInitialized(projectRoot).then(async (initialized) => {
60
- setIsInitialized(initialized);
79
+ dispatch(AppActions.setInitialized(initialized));
61
80
  // Load existing config for API key preservation during reinit
62
81
  if (initialized) {
63
82
  const config = await loadConfig(projectRoot);
64
- setExistingApiKey(config.apiKey);
65
- setExistingProvider(config.embeddingProvider);
83
+ dispatch(WizardActions.setExistingConfig({
84
+ apiKey: config.apiKey,
85
+ provider: config.embeddingProvider,
86
+ }));
66
87
  }
67
88
  });
68
- loadIndexStats(projectRoot).then(setIndexStats);
69
- }, [projectRoot]);
89
+ loadIndexStats(projectRoot).then(stats => dispatch(AppActions.setIndexStats(stats)));
90
+ }, [projectRoot, dispatch]);
70
91
  // Command history
71
92
  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
- };
93
+ const addOutput = useCallback((type, content) => {
94
+ dispatch(AppActions.addOutput({ type, content }));
95
+ }, [dispatch]);
96
+ const addSearchResults = useCallback((data) => {
97
+ dispatch(AppActions.addSearchResults(data));
98
+ }, [dispatch]);
94
99
  // Start the init wizard
95
100
  const startInitWizard = useCallback((isReinit) => {
96
- setWizardMode({ active: true, type: 'init', step: 0, config: {}, isReinit });
97
- }, []);
101
+ dispatch(WizardActions.startInit({
102
+ isReinit,
103
+ existingApiKey: existingApiKey ?? undefined,
104
+ existingProvider: existingProvider ?? undefined,
105
+ }));
106
+ }, [dispatch, existingApiKey, existingProvider]);
98
107
  // Start the MCP setup wizard
99
108
  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
- }, []);
109
+ dispatch(WizardActions.startMcpSetup({ showPrompt }));
110
+ }, [dispatch]);
108
111
  // Start the clean wizard
109
112
  const startCleanWizard = useCallback(() => {
110
- setWizardMode({ active: true, type: 'clean' });
111
- }, []);
113
+ dispatch(WizardActions.startClean());
114
+ }, [dispatch]);
112
115
  // Handle clean wizard completion
113
116
  const handleCleanWizardComplete = useCallback(() => {
114
- setWizardMode({ active: false });
117
+ dispatch(WizardActions.close());
115
118
  // Reset app state to uninitialized after cleaning
116
- setIndexStats(null);
117
- setIsInitialized(false);
118
- }, []);
119
+ dispatch(AppActions.resetInitialized());
120
+ }, [dispatch]);
119
121
  // Handle init wizard step changes
120
122
  const handleInitWizardStep = useCallback((step, data) => {
121
- setWizardMode(prev => prev.active && prev.type === 'init'
122
- ? { ...prev, step, config: { ...prev.config, ...data } }
123
- : prev);
124
- }, []);
123
+ dispatch(WizardActions.setInitStep({ step, config: data }));
124
+ }, [dispatch]);
125
125
  // Handle MCP wizard step changes
126
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
- }, []);
127
+ dispatch(WizardActions.setMcpStep({ step, config: data }));
128
+ }, [dispatch]);
131
129
  // Handle init wizard completion
132
130
  const handleInitWizardComplete = useCallback(async (config) => {
133
131
  // Close wizard first, then run init after a tick to ensure proper re-render
134
- setWizardMode({ active: false });
132
+ dispatch(WizardActions.close());
135
133
  // Wait for next tick so wizard unmounts before we add output
136
134
  await new Promise(resolve => setTimeout(resolve, 50));
137
135
  addOutput('system', 'Initializing Viberag...');
138
- setAppStatus({ state: 'warning', message: 'Initializing...' });
136
+ dispatch(AppActions.setWarning('Initializing...'));
139
137
  try {
140
138
  const result = await runInit(projectRoot, isInitialized ?? false, config);
141
139
  addOutput('system', result);
142
- setIsInitialized(true);
140
+ dispatch(AppActions.setInitialized(true));
143
141
  // Automatically start indexing after init
144
142
  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, throttleMessage, chunksProcessed) => setAppStatus({
152
- state: 'indexing',
153
- current,
154
- total,
155
- stage,
156
- throttleMessage,
157
- chunksProcessed,
158
- }));
159
- addOutput('system', formatIndexStats(stats));
143
+ // Progress is synced via DaemonStatusContext polling
144
+ const stats = await runIndex(projectRoot, true);
145
+ if (stats) {
146
+ addOutput('system', formatIndexStats(stats));
147
+ }
148
+ else {
149
+ addOutput('system', 'Index complete.');
150
+ }
160
151
  // Reload stats after indexing
161
152
  const newStats = await loadIndexStats(projectRoot);
162
- setIndexStats(newStats);
163
- setAppStatus({ state: 'ready' });
153
+ dispatch(AppActions.setIndexStats(newStats));
164
154
  // Prompt for MCP setup after init completes
165
155
  await new Promise(resolve => setTimeout(resolve, 100));
166
156
  startMcpSetupWizard(true); // showPrompt = true for post-init flow
167
157
  }
168
158
  catch (err) {
169
159
  addOutput('system', `Init failed: ${err instanceof Error ? err.message : String(err)}`);
170
- setAppStatus({ state: 'ready' });
171
160
  }
172
- }, [projectRoot, isInitialized, startMcpSetupWizard]);
161
+ finally {
162
+ dispatch(AppActions.setReady());
163
+ }
164
+ }, [projectRoot, isInitialized, startMcpSetupWizard, dispatch, addOutput]);
173
165
  // Handle MCP wizard completion
174
166
  const handleMcpWizardComplete = useCallback((_config) => {
175
- setWizardMode({ active: false });
167
+ dispatch(WizardActions.close());
176
168
  // Results are already shown in the wizard summary
177
- }, []);
169
+ }, [dispatch]);
178
170
  // Handle wizard cancellation
179
171
  const handleWizardCancel = useCallback(() => {
180
- const wasInit = wizardMode.active && wizardMode.type === 'init';
181
- setWizardMode({ active: false });
172
+ const wasInit = activeWizard === 'init';
173
+ dispatch(WizardActions.close());
182
174
  if (wasInit) {
183
175
  addOutput('system', 'Initialization cancelled.');
184
176
  }
185
177
  // MCP wizard cancel just closes silently
186
- }, [wizardMode]);
178
+ }, [dispatch, activeWizard]);
187
179
  // Handle Ctrl+C with status message callback
188
180
  const { handleCtrlC } = useCtrlC({
189
- onFirstPress: () => setAppStatus({ state: 'warning', message: 'Press Ctrl+C again to quit' }),
190
- onStatusClear: () => setAppStatus({ state: 'ready' }),
181
+ onFirstPress: () => dispatch(AppActions.setWarning('Press Ctrl+C again to quit')),
182
+ onStatusClear: () => dispatch(AppActions.setReady()),
191
183
  });
192
184
  // Command handling (all logic consolidated in useRagCommands)
193
- const { isCommand, executeCommand } = useRagCommands({
185
+ const { isCommand, executeCommand } = useCommands({
194
186
  addOutput,
195
187
  addSearchResults,
196
- setAppStatus,
197
- setIndexStats,
198
188
  projectRoot,
199
189
  stdout,
200
190
  startInitWizard,
@@ -218,8 +208,7 @@ export default function App() {
218
208
  }
219
209
  };
220
210
  // Prepend welcome banner as first static item (only after BOTH init status AND stats are loaded)
221
- // This prevents race condition where banner shows stale "Run /index" while stats are loading
222
- const startupLoaded = isInitialized !== undefined && indexStats !== undefined;
211
+ // startupLoaded is computed via Redux selector
223
212
  const staticItems = [
224
213
  ...(startupLoaded
225
214
  ? [{ id: 'welcome', type: 'welcome', content: '' }]
@@ -241,5 +230,14 @@ export default function App() {
241
230
  item.content)) : (React.createElement(Text, null, item.content))));
242
231
  }),
243
232
  React.createElement(StatusBar, { status: appStatus, stats: indexStats }),
244
- 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 }))));
233
+ activeWizard === 'init' ? (React.createElement(InitWizard, { step: initStep, config: initConfig, isReinit: isReinit, existingApiKey: existingApiKey ?? undefined, existingProvider: existingProvider ?? undefined, onStepChange: handleInitWizardStep, onComplete: handleInitWizardComplete, onCancel: handleWizardCancel })) : activeWizard === 'mcp-setup' ? (React.createElement(McpSetupWizard, { step: mcpStep, config: mcpConfig, projectRoot: projectRoot, showPrompt: showMcpPrompt, onStepChange: handleMcpWizardStep, onComplete: handleMcpWizardComplete, onCancel: handleWizardCancel, addOutput: addOutput })) : activeWizard === '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 }))));
234
+ }
235
+ /**
236
+ * Main App component with Redux Provider and DaemonStatusProvider.
237
+ */
238
+ export default function App() {
239
+ const projectRoot = process.cwd();
240
+ return (React.createElement(Provider, { store: store },
241
+ React.createElement(DaemonStatusProvider, { projectRoot: projectRoot },
242
+ React.createElement(AppContent, null))));
245
243
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * RAG commands for the CLI.
3
3
  */
4
- import { type IndexStats, type SearchResults } from '../../rag/index.js';
4
+ import type { IndexStats, SearchResults } from '../../client/types.js';
5
5
  import type { InitWizardConfig } from '../../common/types.js';
6
6
  /**
7
7
  * Index display stats type (re-exported for convenience).
@@ -21,21 +21,23 @@ export declare function loadIndexStats(projectRoot: string): Promise<IndexDispla
21
21
  /**
22
22
  * Initialize a project for Viberag.
23
23
  * Creates .viberag/ directory with config.json.
24
- * With isReinit=true, deletes everything and starts fresh.
24
+ * With isReinit=true, shuts down daemon and deletes everything first.
25
25
  */
26
26
  export declare function runInit(projectRoot: string, isReinit?: boolean, wizardConfig?: InitWizardConfig): Promise<string>;
27
27
  /**
28
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).
29
+ * Delegates to daemon which handles dimension sync internally.
30
+ *
31
+ * Note: Indexing starts asynchronously; completion is detected via status polling.
31
32
  */
32
- export declare function runIndex(projectRoot: string, force?: boolean, onProgress?: (current: number, total: number, stage: string, throttleMessage?: string | null, chunksProcessed?: number) => void): Promise<IndexStats>;
33
+ export declare function runIndex(projectRoot: string, force?: boolean): Promise<IndexStats | null>;
33
34
  /**
34
35
  * Format index stats for display.
35
36
  */
36
37
  export declare function formatIndexStats(stats: IndexStats): string;
37
38
  /**
38
39
  * Run a search query and return results.
40
+ * Delegates to daemon for search.
39
41
  */
40
42
  export declare function runSearch(projectRoot: string, query: string, limit?: number): Promise<SearchResults>;
41
43
  /**
@@ -48,7 +50,7 @@ export declare function formatSearchResults(results: SearchResults): string;
48
50
  export declare function getStatus(projectRoot: string): Promise<string>;
49
51
  /**
50
52
  * Clean/uninstall Viberag from a project.
51
- * Removes the entire .viberag/ directory.
53
+ * Shuts down daemon first, then removes the entire .viberag/ directory.
52
54
  */
53
55
  export declare function runClean(projectRoot: string): Promise<string>;
54
56
  /**
@@ -4,7 +4,10 @@
4
4
  import fs from 'node:fs/promises';
5
5
  import path from 'node:path';
6
6
  import chalk from 'chalk';
7
- import { Indexer, SearchEngine, loadManifest, manifestExists, configExists, saveConfig, DEFAULT_CONFIG, PROVIDER_CONFIGS, getViberagDir, } from '../../rag/index.js';
7
+ import { loadManifest, manifestExists } from '../../daemon/lib/manifest.js';
8
+ import { configExists, saveConfig, DEFAULT_CONFIG, PROVIDER_CONFIGS, } from '../../daemon/lib/config.js';
9
+ import { getViberagDir } from '../../daemon/lib/constants.js';
10
+ import { DaemonClient } from '../../client/index.js';
8
11
  /**
9
12
  * Check if project is initialized.
10
13
  */
@@ -27,13 +30,28 @@ export async function loadIndexStats(projectRoot) {
27
30
  /**
28
31
  * Initialize a project for Viberag.
29
32
  * Creates .viberag/ directory with config.json.
30
- * With isReinit=true, deletes everything and starts fresh.
33
+ * With isReinit=true, shuts down daemon and deletes everything first.
31
34
  */
32
35
  export async function runInit(projectRoot, isReinit = false, wizardConfig) {
33
36
  const viberagDir = getViberagDir(projectRoot);
34
37
  const isExisting = await configExists(projectRoot);
35
- // If reinit, delete entire .viberag directory first
38
+ // If reinit, shutdown daemon and delete entire .viberag directory first
36
39
  if (isReinit && isExisting) {
40
+ const client = new DaemonClient(projectRoot);
41
+ try {
42
+ if (await client.isRunning()) {
43
+ await client.connect();
44
+ await client.shutdown('reinit');
45
+ // Wait for daemon to exit
46
+ await new Promise(r => setTimeout(r, 500));
47
+ }
48
+ }
49
+ catch {
50
+ // Ignore errors - daemon may not be running
51
+ }
52
+ finally {
53
+ await client.disconnect();
54
+ }
37
55
  await fs.rm(viberagDir, { recursive: true, force: true });
38
56
  }
39
57
  // Create .viberag directory
@@ -41,12 +59,21 @@ export async function runInit(projectRoot, isReinit = false, wizardConfig) {
41
59
  // Build config from wizard choices
42
60
  const provider = wizardConfig?.provider ?? 'gemini';
43
61
  const { model, dimensions } = PROVIDER_CONFIGS[provider];
62
+ // Map OpenAI region to base URL
63
+ const openaiBaseUrl = wizardConfig?.openaiRegion
64
+ ? {
65
+ default: undefined,
66
+ us: 'https://us.api.openai.com/v1',
67
+ eu: 'https://eu.api.openai.com/v1',
68
+ }[wizardConfig.openaiRegion]
69
+ : undefined;
44
70
  const config = {
45
71
  ...DEFAULT_CONFIG,
46
72
  embeddingProvider: provider,
47
73
  embeddingModel: model,
48
74
  embeddingDimensions: dimensions,
49
75
  ...(wizardConfig?.apiKey && { apiKey: wizardConfig.apiKey }),
76
+ ...(openaiBaseUrl && { openaiBaseUrl }),
50
77
  };
51
78
  // Save config
52
79
  await saveConfig(projectRoot, config);
@@ -68,35 +95,48 @@ export async function runInit(projectRoot, isReinit = false, wizardConfig) {
68
95
  }
69
96
  /**
70
97
  * 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).
98
+ * Delegates to daemon which handles dimension sync internally.
99
+ *
100
+ * Note: Indexing starts asynchronously; completion is detected via status polling.
73
101
  */
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);
102
+ export async function runIndex(projectRoot, force = false) {
103
+ const client = new DaemonClient(projectRoot);
91
104
  try {
92
- const stats = await indexer.index({
93
- force,
94
- progressCallback: onProgress,
95
- });
96
- return stats;
105
+ await client.connect();
106
+ const initialStatus = await client.status();
107
+ const previousCompletion = initialStatus.indexing.lastCompleted;
108
+ await client.indexAsync({ force });
109
+ return await waitForIndexCompletion(client, previousCompletion);
97
110
  }
98
111
  finally {
99
- indexer.close();
112
+ await client.disconnect();
113
+ }
114
+ }
115
+ /**
116
+ * Poll daemon status until a new index completion is observed.
117
+ */
118
+ async function waitForIndexCompletion(client, previousLastCompleted) {
119
+ const pollIntervalMs = 500;
120
+ const statsGraceMs = 5000;
121
+ let completionDetectedAt = null;
122
+ for (;;) {
123
+ const status = await client.status();
124
+ if (status.indexing.status === 'error') {
125
+ throw new Error(status.indexing.error ?? 'Index failed');
126
+ }
127
+ const lastCompleted = status.indexing.lastCompleted;
128
+ if (lastCompleted && lastCompleted !== previousLastCompleted) {
129
+ if (status.indexing.lastStats) {
130
+ return status.indexing.lastStats;
131
+ }
132
+ if (completionDetectedAt === null) {
133
+ completionDetectedAt = Date.now();
134
+ }
135
+ else if (Date.now() - completionDetectedAt > statsGraceMs) {
136
+ return null;
137
+ }
138
+ }
139
+ await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
100
140
  }
101
141
  }
102
142
  /**
@@ -118,14 +158,16 @@ export function formatIndexStats(stats) {
118
158
  }
119
159
  /**
120
160
  * Run a search query and return results.
161
+ * Delegates to daemon for search.
121
162
  */
122
163
  export async function runSearch(projectRoot, query, limit = 10) {
123
- const engine = new SearchEngine(projectRoot);
164
+ const client = new DaemonClient(projectRoot);
124
165
  try {
125
- return await engine.search(query, { limit });
166
+ await client.connect();
167
+ return await client.search(query, { limit });
126
168
  }
127
169
  finally {
128
- engine.close();
170
+ await client.disconnect();
129
171
  }
130
172
  }
131
173
  /**
@@ -198,7 +240,7 @@ export async function getStatus(projectRoot) {
198
240
  }
199
241
  /**
200
242
  * Clean/uninstall Viberag from a project.
201
- * Removes the entire .viberag/ directory.
243
+ * Shuts down daemon first, then removes the entire .viberag/ directory.
202
244
  */
203
245
  export async function runClean(projectRoot) {
204
246
  const viberagDir = getViberagDir(projectRoot);
@@ -206,6 +248,22 @@ export async function runClean(projectRoot) {
206
248
  if (!exists) {
207
249
  return 'Viberag is not initialized in this project. Nothing to clean.';
208
250
  }
251
+ // Shutdown daemon if running
252
+ const client = new DaemonClient(projectRoot);
253
+ try {
254
+ if (await client.isRunning()) {
255
+ await client.connect();
256
+ await client.shutdown('clean');
257
+ // Wait for daemon to exit
258
+ await new Promise(r => setTimeout(r, 500));
259
+ }
260
+ }
261
+ catch {
262
+ // Ignore errors - daemon may not be running
263
+ }
264
+ finally {
265
+ await client.disconnect();
266
+ }
209
267
  await fs.rm(viberagDir, { recursive: true, force: true });
210
268
  return `Removed ${viberagDir}\nViberag has been uninstalled from this project.\nRun /init to reinitialize.`;
211
269
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * CLI command handling hook.
3
+ * Consolidates all command routing and handler implementations.
4
+ */
5
+ import type { SearchResultsData } from '../../common/types.js';
6
+ type CommandContext = {
7
+ addOutput: (type: 'user' | 'system', content: string) => void;
8
+ addSearchResults: (data: SearchResultsData) => void;
9
+ projectRoot: string;
10
+ stdout: NodeJS.WriteStream;
11
+ startInitWizard: (isReinit: boolean) => void;
12
+ startMcpSetupWizard: (showPrompt?: boolean) => void;
13
+ startCleanWizard: () => void;
14
+ isInitialized: boolean;
15
+ };
16
+ export declare function useCommands({ addOutput, addSearchResults, projectRoot, stdout, startInitWizard, startMcpSetupWizard, startCleanWizard, isInitialized, }: CommandContext): {
17
+ isCommand: (text: string) => boolean;
18
+ executeCommand: (text: string) => void;
19
+ };
20
+ export {};