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,246 @@
1
+ /**
2
+ * Gitignore - File filtering based on .gitignore patterns.
3
+ *
4
+ * Uses the `ignore` package to parse .gitignore files and filter paths.
5
+ * This replaces the hardcoded excludePatterns approach.
6
+ */
7
+ import fs from 'node:fs/promises';
8
+ import path from 'node:path';
9
+ import { createRequire } from 'node:module';
10
+ // ignore is a CJS module, use createRequire to import it
11
+ const require = createRequire(import.meta.url);
12
+ const ignore = require('ignore');
13
+ // ============================================================================
14
+ // Always-Ignored Patterns
15
+ // ============================================================================
16
+ /**
17
+ * Patterns that should always be ignored, regardless of .gitignore.
18
+ * These are internal/system directories that should never be indexed.
19
+ */
20
+ const ALWAYS_IGNORED = [
21
+ '.git',
22
+ '.viberag',
23
+ 'node_modules', // Fallback in case not in .gitignore
24
+ ];
25
+ /**
26
+ * Lock files that should always be ignored.
27
+ * These are machine-generated and provide no value for code search.
28
+ */
29
+ const ALWAYS_IGNORED_FILES = [
30
+ // JavaScript/TypeScript
31
+ 'package-lock.json', // npm
32
+ 'yarn.lock', // Yarn
33
+ 'pnpm-lock.yaml', // pnpm
34
+ 'bun.lockb', // Bun
35
+ // Python
36
+ 'uv.lock', // UV
37
+ 'poetry.lock', // Poetry
38
+ 'Pipfile.lock', // Pipenv
39
+ // Ruby
40
+ 'Gemfile.lock', // Bundler
41
+ // PHP
42
+ 'composer.lock', // Composer
43
+ // Rust
44
+ 'Cargo.lock', // Cargo
45
+ // Go
46
+ 'go.sum', // Go modules
47
+ // Java/Kotlin
48
+ 'gradle.lockfile', // Gradle
49
+ // C#/.NET
50
+ 'packages.lock.json', // NuGet
51
+ // Dart
52
+ 'pubspec.lock', // Pub
53
+ // Swift
54
+ 'Package.resolved', // Swift PM
55
+ ];
56
+ /**
57
+ * File patterns that should always be ignored.
58
+ * These are build artifacts with no semantic value for code search.
59
+ */
60
+ const ALWAYS_IGNORED_PATTERNS = [
61
+ '*.min.js', // Minified JavaScript
62
+ '*.min.css', // Minified CSS
63
+ '*.map', // Source maps
64
+ '*.d.ts.map', // TypeScript declaration maps
65
+ ];
66
+ // ============================================================================
67
+ // Cache
68
+ // ============================================================================
69
+ /**
70
+ * Cache of Ignore instances per project root.
71
+ */
72
+ const ignoreCache = new Map();
73
+ // ============================================================================
74
+ // Core Functions
75
+ // ============================================================================
76
+ /**
77
+ * Load and parse .gitignore file from project root.
78
+ * Returns an Ignore instance that can filter paths.
79
+ *
80
+ * @param projectRoot - Project root directory
81
+ * @returns Ignore instance for filtering
82
+ */
83
+ export async function loadGitignore(projectRoot) {
84
+ // Check cache first
85
+ const cached = ignoreCache.get(projectRoot);
86
+ if (cached) {
87
+ return cached;
88
+ }
89
+ const ig = ignore();
90
+ // Add always-ignored patterns (directories)
91
+ ig.add(ALWAYS_IGNORED);
92
+ // Add always-ignored files (lock files)
93
+ ig.add(ALWAYS_IGNORED_FILES);
94
+ // Add always-ignored file patterns (minified, maps)
95
+ ig.add(ALWAYS_IGNORED_PATTERNS);
96
+ // Try to load .gitignore
97
+ const gitignorePath = path.join(projectRoot, '.gitignore');
98
+ try {
99
+ const content = await fs.readFile(gitignorePath, 'utf-8');
100
+ ig.add(content);
101
+ }
102
+ catch {
103
+ // .gitignore doesn't exist, that's fine
104
+ }
105
+ // Cache the instance
106
+ ignoreCache.set(projectRoot, ig);
107
+ return ig;
108
+ }
109
+ /**
110
+ * Check if a path should be ignored based on .gitignore rules.
111
+ *
112
+ * @param projectRoot - Project root directory
113
+ * @param relativePath - Path relative to project root
114
+ * @returns true if the path should be ignored
115
+ */
116
+ export async function shouldIgnore(projectRoot, relativePath) {
117
+ const ig = await loadGitignore(projectRoot);
118
+ return ig.ignores(relativePath);
119
+ }
120
+ /**
121
+ * Create a filter function for use with file listing.
122
+ * The filter returns true for files that should be INCLUDED (not ignored).
123
+ *
124
+ * @param projectRoot - Project root directory
125
+ * @returns Filter function that returns true for non-ignored files
126
+ */
127
+ export async function createGitignoreFilter(projectRoot) {
128
+ const ig = await loadGitignore(projectRoot);
129
+ return (relativePath) => !ig.ignores(relativePath);
130
+ }
131
+ // ============================================================================
132
+ // Cache Management
133
+ // ============================================================================
134
+ /**
135
+ * Clear the cache for a specific project root.
136
+ * Call this if .gitignore has been modified.
137
+ *
138
+ * @param projectRoot - Project root directory
139
+ */
140
+ export function clearGitignoreCache(projectRoot) {
141
+ ignoreCache.delete(projectRoot);
142
+ }
143
+ /**
144
+ * Clear all cached Ignore instances.
145
+ */
146
+ export function clearAllGitignoreCache() {
147
+ ignoreCache.clear();
148
+ }
149
+ // ============================================================================
150
+ // Glob Pattern Conversion
151
+ // ============================================================================
152
+ /**
153
+ * Convert gitignore patterns to fast-glob ignore patterns.
154
+ * This allows fast-glob to skip directories upfront instead of
155
+ * scanning them and filtering later.
156
+ *
157
+ * @param projectRoot - Project root directory
158
+ * @returns Array of fast-glob compatible ignore patterns
159
+ */
160
+ export async function getGlobIgnorePatterns(projectRoot) {
161
+ const patterns = [];
162
+ // Always exclude these directories (same as ALWAYS_IGNORED)
163
+ patterns.push('**/.git/**', '**/.viberag/**', '**/node_modules/**');
164
+ // Always exclude lock files (same as ALWAYS_IGNORED_FILES)
165
+ for (const file of ALWAYS_IGNORED_FILES) {
166
+ patterns.push(`**/${file}`);
167
+ }
168
+ // Always exclude file patterns (minified, maps)
169
+ for (const pattern of ALWAYS_IGNORED_PATTERNS) {
170
+ patterns.push(`**/${pattern}`);
171
+ }
172
+ // Try to load .gitignore
173
+ const gitignorePath = path.join(projectRoot, '.gitignore');
174
+ try {
175
+ const content = await fs.readFile(gitignorePath, 'utf-8');
176
+ const lines = content.split('\n');
177
+ for (const line of lines) {
178
+ const trimmed = line.trim();
179
+ // Skip empty lines and comments
180
+ if (!trimmed || trimmed.startsWith('#')) {
181
+ continue;
182
+ }
183
+ // Skip negation patterns (fast-glob handles these differently)
184
+ if (trimmed.startsWith('!')) {
185
+ continue;
186
+ }
187
+ // Convert gitignore pattern to fast-glob pattern
188
+ const globPattern = gitignoreToGlob(trimmed);
189
+ if (globPattern) {
190
+ patterns.push(globPattern);
191
+ }
192
+ }
193
+ }
194
+ catch {
195
+ // .gitignore doesn't exist, that's fine
196
+ }
197
+ return patterns;
198
+ }
199
+ /**
200
+ * Convert a single gitignore pattern to a fast-glob pattern.
201
+ *
202
+ * Gitignore patterns:
203
+ * - `foo` matches `foo` anywhere
204
+ * - `foo/` matches directory `foo` anywhere
205
+ * - `/foo` matches `foo` only at root
206
+ * - `*.log` matches `*.log` anywhere
207
+ *
208
+ * Fast-glob patterns:
209
+ * - Need double-star-slash prefix to match anywhere
210
+ * - Need "/**" suffix to match directory contents
211
+ */
212
+ function gitignoreToGlob(pattern) {
213
+ let result = pattern;
214
+ // Handle rooted patterns (start with /)
215
+ const isRooted = result.startsWith('/');
216
+ if (isRooted) {
217
+ result = result.slice(1);
218
+ }
219
+ // Handle directory patterns (end with /)
220
+ const isDirectory = result.endsWith('/');
221
+ if (isDirectory) {
222
+ result = result.slice(0, -1);
223
+ }
224
+ // Skip patterns that are already glob-like with **
225
+ const hasDoublestar = result.includes('**');
226
+ // Build the glob pattern
227
+ if (isRooted) {
228
+ // Rooted: match only at project root
229
+ result = isDirectory ? `${result}/**` : result;
230
+ }
231
+ else if (!hasDoublestar) {
232
+ // Non-rooted: match anywhere in tree
233
+ result = isDirectory ? `**/${result}/**` : `**/${result}`;
234
+ // If it doesn't look like a directory name (has extension or glob),
235
+ // don't add trailing /**
236
+ if (!isDirectory &&
237
+ (result.includes('.') || result.includes('*') || result.includes('?'))) {
238
+ // Keep as-is, it's likely a file pattern
239
+ }
240
+ else if (!isDirectory) {
241
+ // Bare name like "node_modules" - treat as directory
242
+ result = `**/${pattern}/**`;
243
+ }
244
+ }
245
+ return result;
246
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Logger - File-based logging with daily and hourly rotation.
3
+ *
4
+ * Provides multiple logger implementations:
5
+ * - createLogger: Daily log files in .viberag/logs/
6
+ * - createDebugLogger: Single debug.log file (deprecated)
7
+ * - createServiceLogger: Per-service hourly rotation
8
+ * - createNullLogger: No-op for testing
9
+ */
10
+ import { type ServiceName } from './constants.js';
11
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
12
+ export interface Logger {
13
+ debug(component: string, message: string, data?: object): void;
14
+ info(component: string, message: string, data?: object): void;
15
+ warn(component: string, message: string, data?: object): void;
16
+ error(component: string, message: string, error?: Error): void;
17
+ }
18
+ /**
19
+ * Get the path to today's log file.
20
+ */
21
+ export declare function getLogPath(projectRoot: string): string;
22
+ /**
23
+ * Create a logger that writes to daily log files.
24
+ * Log files are created in the .viberag/logs/ directory.
25
+ */
26
+ export declare function createLogger(projectRoot: string): Logger;
27
+ /**
28
+ * Create a no-op logger for testing or when logging is disabled.
29
+ */
30
+ export declare function createNullLogger(): Logger;
31
+ /**
32
+ * Create a debug logger that writes to .viberag/debug.log.
33
+ * This is always-on logging for troubleshooting.
34
+ * @deprecated Use createServiceLogger instead for per-service logging.
35
+ */
36
+ export declare function createDebugLogger(projectRoot: string): Logger;
37
+ /**
38
+ * Create a service-specific logger with hourly rotation.
39
+ *
40
+ * Logs are written to: .viberag/logs/{service}/YYYY-MM-DD-HH.log
41
+ *
42
+ * @param projectRoot - Project root directory
43
+ * @param service - Service name (daemon, mcp, cli, indexer)
44
+ *
45
+ * @example
46
+ * const logger = createServiceLogger('/path/to/project', 'daemon');
47
+ * logger.error('Handler', 'Request failed', error);
48
+ * // Writes to: .viberag/logs/daemon/2024-01-11-15.log
49
+ */
50
+ export declare function createServiceLogger(projectRoot: string, service: ServiceName): Logger;
51
+ export type { ServiceName };
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Logger - File-based logging with daily and hourly rotation.
3
+ *
4
+ * Provides multiple logger implementations:
5
+ * - createLogger: Daily log files in .viberag/logs/
6
+ * - createDebugLogger: Single debug.log file (deprecated)
7
+ * - createServiceLogger: Per-service hourly rotation
8
+ * - createNullLogger: No-op for testing
9
+ */
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { getLogsDir, getDebugLogPath, getViberagDir, getServiceLogsDir, getServiceLogPath, } from './constants.js';
13
+ // ============================================================================
14
+ // Formatting
15
+ // ============================================================================
16
+ /**
17
+ * Get the path to today's log file.
18
+ */
19
+ export function getLogPath(projectRoot) {
20
+ const logsDir = getLogsDir(projectRoot);
21
+ const date = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
22
+ return path.join(logsDir, `${date}.log`);
23
+ }
24
+ /**
25
+ * Format a log entry.
26
+ */
27
+ function formatEntry(level, component, message, extra) {
28
+ const timestamp = new Date().toISOString();
29
+ const levelStr = level.toUpperCase().padEnd(5);
30
+ let entry = `[${timestamp}] [${levelStr}] ${component}: ${message}`;
31
+ if (extra) {
32
+ if (extra instanceof Error) {
33
+ entry += `\n Error: ${extra.message}`;
34
+ if (extra.stack) {
35
+ entry += `\n Stack: ${extra.stack}`;
36
+ }
37
+ }
38
+ else {
39
+ entry += `\n ${JSON.stringify(extra)}`;
40
+ }
41
+ }
42
+ return entry;
43
+ }
44
+ // ============================================================================
45
+ // Logger Implementations
46
+ // ============================================================================
47
+ /**
48
+ * Create a logger that writes to daily log files.
49
+ * Log files are created in the .viberag/logs/ directory.
50
+ */
51
+ export function createLogger(projectRoot) {
52
+ const logsDir = getLogsDir(projectRoot);
53
+ let initialized = false;
54
+ function ensureDir() {
55
+ if (!initialized) {
56
+ fs.mkdirSync(logsDir, { recursive: true });
57
+ initialized = true;
58
+ }
59
+ }
60
+ function write(entry) {
61
+ ensureDir();
62
+ const logPath = getLogPath(projectRoot);
63
+ fs.appendFileSync(logPath, entry + '\n');
64
+ }
65
+ return {
66
+ debug(component, message, data) {
67
+ write(formatEntry('debug', component, message, data));
68
+ },
69
+ info(component, message, data) {
70
+ write(formatEntry('info', component, message, data));
71
+ },
72
+ warn(component, message, data) {
73
+ write(formatEntry('warn', component, message, data));
74
+ },
75
+ error(component, message, error) {
76
+ write(formatEntry('error', component, message, error));
77
+ },
78
+ };
79
+ }
80
+ /**
81
+ * Create a no-op logger for testing or when logging is disabled.
82
+ */
83
+ export function createNullLogger() {
84
+ return {
85
+ debug() { },
86
+ info() { },
87
+ warn() { },
88
+ error() { },
89
+ };
90
+ }
91
+ /**
92
+ * Create a debug logger that writes to .viberag/debug.log.
93
+ * This is always-on logging for troubleshooting.
94
+ * @deprecated Use createServiceLogger instead for per-service logging.
95
+ */
96
+ export function createDebugLogger(projectRoot) {
97
+ const viberagDir = getViberagDir(projectRoot);
98
+ let initialized = false;
99
+ function ensureDir() {
100
+ if (!initialized) {
101
+ fs.mkdirSync(viberagDir, { recursive: true });
102
+ initialized = true;
103
+ }
104
+ }
105
+ function write(entry) {
106
+ ensureDir();
107
+ const logPath = getDebugLogPath(projectRoot);
108
+ fs.appendFileSync(logPath, entry + '\n');
109
+ }
110
+ return {
111
+ debug(component, message, data) {
112
+ write(formatEntry('debug', component, message, data));
113
+ },
114
+ info(component, message, data) {
115
+ write(formatEntry('info', component, message, data));
116
+ },
117
+ warn(component, message, data) {
118
+ write(formatEntry('warn', component, message, data));
119
+ },
120
+ error(component, message, error) {
121
+ write(formatEntry('error', component, message, error));
122
+ },
123
+ };
124
+ }
125
+ /**
126
+ * Create a service-specific logger with hourly rotation.
127
+ *
128
+ * Logs are written to: .viberag/logs/{service}/YYYY-MM-DD-HH.log
129
+ *
130
+ * @param projectRoot - Project root directory
131
+ * @param service - Service name (daemon, mcp, cli, indexer)
132
+ *
133
+ * @example
134
+ * const logger = createServiceLogger('/path/to/project', 'daemon');
135
+ * logger.error('Handler', 'Request failed', error);
136
+ * // Writes to: .viberag/logs/daemon/2024-01-11-15.log
137
+ */
138
+ export function createServiceLogger(projectRoot, service) {
139
+ function write(entry) {
140
+ try {
141
+ // Always ensure directory exists before writing
142
+ // (handles case where .viberag/ was deleted during reinit)
143
+ const serviceDir = getServiceLogsDir(projectRoot, service);
144
+ fs.mkdirSync(serviceDir, { recursive: true });
145
+ // Get current hourly log path (recalculated each write for rotation)
146
+ const logPath = getServiceLogPath(projectRoot, service);
147
+ fs.appendFileSync(logPath, entry + '\n');
148
+ }
149
+ catch {
150
+ // Silently ignore logging failures - don't crash the app for logging
151
+ }
152
+ }
153
+ return {
154
+ debug(component, message, data) {
155
+ write(formatEntry('debug', component, message, data));
156
+ },
157
+ info(component, message, data) {
158
+ write(formatEntry('info', component, message, data));
159
+ },
160
+ warn(component, message, data) {
161
+ write(formatEntry('warn', component, message, data));
162
+ },
163
+ error(component, message, error) {
164
+ write(formatEntry('error', component, message, error));
165
+ },
166
+ };
167
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Manifest - Index manifest for tracking Merkle tree and stats.
3
+ *
4
+ * The manifest persists indexing state between runs:
5
+ * - Merkle tree for incremental change detection
6
+ * - Stats for status display
7
+ * - Schema version for migration detection
8
+ */
9
+ export interface ManifestStats {
10
+ totalFiles: number;
11
+ totalChunks: number;
12
+ }
13
+ export interface Manifest {
14
+ version: number;
15
+ schemaVersion: number;
16
+ createdAt: string;
17
+ updatedAt: string;
18
+ tree: object | null;
19
+ stats: ManifestStats;
20
+ }
21
+ /**
22
+ * Create an empty manifest with current schema version.
23
+ */
24
+ export declare function createEmptyManifest(): Manifest;
25
+ /**
26
+ * Check if manifest schema version is current.
27
+ */
28
+ export declare function isSchemaVersionCurrent(manifest: Manifest): boolean;
29
+ /**
30
+ * Get schema version mismatch info for display.
31
+ */
32
+ export declare function getSchemaVersionInfo(manifest: Manifest): {
33
+ current: number;
34
+ required: number;
35
+ needsReindex: boolean;
36
+ };
37
+ /**
38
+ * Load manifest from disk.
39
+ * Returns an empty manifest if no file exists.
40
+ */
41
+ export declare function loadManifest(projectRoot: string): Promise<Manifest>;
42
+ /**
43
+ * Save manifest to disk.
44
+ * Creates the .viberag directory if it doesn't exist.
45
+ */
46
+ export declare function saveManifest(projectRoot: string, manifest: Manifest): Promise<void>;
47
+ /**
48
+ * Check if a manifest file exists.
49
+ */
50
+ export declare function manifestExists(projectRoot: string): Promise<boolean>;
51
+ /**
52
+ * Update manifest stats.
53
+ */
54
+ export declare function updateManifestStats(manifest: Manifest, stats: ManifestStats): Manifest;
55
+ /**
56
+ * Update manifest tree.
57
+ */
58
+ export declare function updateManifestTree(manifest: Manifest, tree: object | null): Manifest;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Manifest - Index manifest for tracking Merkle tree and stats.
3
+ *
4
+ * The manifest persists indexing state between runs:
5
+ * - Merkle tree for incremental change detection
6
+ * - Stats for status display
7
+ * - Schema version for migration detection
8
+ */
9
+ import fs from 'node:fs/promises';
10
+ import { getManifestPath, getViberagDir, SCHEMA_VERSION } from './constants.js';
11
+ // ============================================================================
12
+ // Factory
13
+ // ============================================================================
14
+ /**
15
+ * Create an empty manifest with current schema version.
16
+ */
17
+ export function createEmptyManifest() {
18
+ const now = new Date().toISOString();
19
+ return {
20
+ version: 1,
21
+ schemaVersion: SCHEMA_VERSION,
22
+ createdAt: now,
23
+ updatedAt: now,
24
+ tree: null,
25
+ stats: {
26
+ totalFiles: 0,
27
+ totalChunks: 0,
28
+ },
29
+ };
30
+ }
31
+ // ============================================================================
32
+ // Schema Version
33
+ // ============================================================================
34
+ /**
35
+ * Check if manifest schema version is current.
36
+ */
37
+ export function isSchemaVersionCurrent(manifest) {
38
+ return manifest.schemaVersion === SCHEMA_VERSION;
39
+ }
40
+ /**
41
+ * Get schema version mismatch info for display.
42
+ */
43
+ export function getSchemaVersionInfo(manifest) {
44
+ return {
45
+ current: manifest.schemaVersion ?? 1, // Default to 1 for old manifests
46
+ required: SCHEMA_VERSION,
47
+ needsReindex: (manifest.schemaVersion ?? 1) < SCHEMA_VERSION,
48
+ };
49
+ }
50
+ // ============================================================================
51
+ // I/O
52
+ // ============================================================================
53
+ /**
54
+ * Load manifest from disk.
55
+ * Returns an empty manifest if no file exists.
56
+ */
57
+ export async function loadManifest(projectRoot) {
58
+ const manifestPath = getManifestPath(projectRoot);
59
+ try {
60
+ const content = await fs.readFile(manifestPath, 'utf-8');
61
+ return JSON.parse(content);
62
+ }
63
+ catch {
64
+ return createEmptyManifest();
65
+ }
66
+ }
67
+ /**
68
+ * Save manifest to disk.
69
+ * Creates the .viberag directory if it doesn't exist.
70
+ */
71
+ export async function saveManifest(projectRoot, manifest) {
72
+ const viberagDir = getViberagDir(projectRoot);
73
+ await fs.mkdir(viberagDir, { recursive: true });
74
+ const manifestPath = getManifestPath(projectRoot);
75
+ const updated = {
76
+ ...manifest,
77
+ updatedAt: new Date().toISOString(),
78
+ };
79
+ await fs.writeFile(manifestPath, JSON.stringify(updated, null, '\t') + '\n');
80
+ }
81
+ /**
82
+ * Check if a manifest file exists.
83
+ */
84
+ export async function manifestExists(projectRoot) {
85
+ const manifestPath = getManifestPath(projectRoot);
86
+ try {
87
+ await fs.access(manifestPath);
88
+ return true;
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
94
+ // ============================================================================
95
+ // Updates
96
+ // ============================================================================
97
+ /**
98
+ * Update manifest stats.
99
+ */
100
+ export function updateManifestStats(manifest, stats) {
101
+ return {
102
+ ...manifest,
103
+ stats,
104
+ updatedAt: new Date().toISOString(),
105
+ };
106
+ }
107
+ /**
108
+ * Update manifest tree.
109
+ */
110
+ export function updateManifestTree(manifest, tree) {
111
+ return {
112
+ ...manifest,
113
+ tree,
114
+ updatedAt: new Date().toISOString(),
115
+ };
116
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Merkle Diff - Tree comparison for change detection.
3
+ *
4
+ * Compares two Merkle trees to find new, modified, and deleted files.
5
+ * Uses hash comparison for efficient subtree comparison.
6
+ */
7
+ import type { MerkleNode } from './node.js';
8
+ /**
9
+ * Result of comparing two Merkle trees.
10
+ */
11
+ export interface TreeDiff {
12
+ /** Paths of new files */
13
+ new: string[];
14
+ /** Paths of modified files */
15
+ modified: string[];
16
+ /** Paths of deleted files */
17
+ deleted: string[];
18
+ /** Whether there are any changes */
19
+ hasChanges: boolean;
20
+ }
21
+ /**
22
+ * Create an empty TreeDiff.
23
+ */
24
+ export declare function createEmptyDiff(): TreeDiff;
25
+ /**
26
+ * Compare two Merkle trees and return the differences.
27
+ *
28
+ * @param oldRoot - The previous tree's root node (or null if no previous tree)
29
+ * @param newRoot - The current tree's root node
30
+ * @returns TreeDiff with new, modified, and deleted file paths
31
+ */
32
+ export declare function compareTrees(oldRoot: MerkleNode | null, newRoot: MerkleNode | null): TreeDiff;