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,15 @@
1
+ /**
2
+ * Daemon Method Handlers
3
+ *
4
+ * JSON-RPC method implementations for daemon IPC.
5
+ * Each handler receives params and a context with access to owner and server.
6
+ *
7
+ * Simplified for polling-based architecture:
8
+ * - Clients poll status() for state updates
9
+ * - No push notifications or subscriptions
10
+ */
11
+ import type { HandlerRegistry } from './server.js';
12
+ /**
13
+ * Create the handler registry.
14
+ */
15
+ export declare function createHandlers(): HandlerRegistry;
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Daemon Method Handlers
3
+ *
4
+ * JSON-RPC method implementations for daemon IPC.
5
+ * Each handler receives params and a context with access to owner and server.
6
+ *
7
+ * Simplified for polling-based architecture:
8
+ * - Clients poll status() for state updates
9
+ * - No push notifications or subscriptions
10
+ */
11
+ import { z } from 'zod';
12
+ import { PROTOCOL_VERSION } from './protocol.js';
13
+ import { daemonState } from './state.js';
14
+ // ============================================================================
15
+ // Parameter Schemas
16
+ // ============================================================================
17
+ const searchParamsSchema = z.object({
18
+ query: z.string().min(1),
19
+ mode: z
20
+ .enum(['semantic', 'exact', 'hybrid', 'definition', 'similar'])
21
+ .optional(),
22
+ limit: z.number().min(1).max(100).optional(),
23
+ bm25Weight: z.number().min(0).max(1).optional(),
24
+ minScore: z.number().min(0).max(1).optional(),
25
+ filters: z.record(z.string(), z.unknown()).optional(),
26
+ codeSnippet: z.string().optional(),
27
+ symbolName: z.string().optional(),
28
+ autoBoost: z.boolean().optional(),
29
+ autoBoostThreshold: z.number().min(0).max(1).optional(),
30
+ returnDebug: z.boolean().optional(),
31
+ });
32
+ const indexParamsSchema = z.object({
33
+ force: z.boolean().optional(),
34
+ });
35
+ const ACTIVE_INDEX_STATUSES = new Set([
36
+ 'initializing',
37
+ 'scanning',
38
+ 'chunking',
39
+ 'embedding',
40
+ ]);
41
+ const shutdownParamsSchema = z.object({
42
+ reason: z.string().optional(),
43
+ });
44
+ // ============================================================================
45
+ // Handlers
46
+ // ============================================================================
47
+ /**
48
+ * Search handler.
49
+ */
50
+ const searchHandler = async (params, ctx) => {
51
+ const validated = searchParamsSchema.parse(params ?? {});
52
+ const { query, ...options } = validated;
53
+ return ctx.owner.search(query, options);
54
+ };
55
+ /**
56
+ * Index handler.
57
+ * Simply runs indexing - clients poll status() for progress.
58
+ */
59
+ const indexHandler = async (params, ctx) => {
60
+ const validated = indexParamsSchema.parse(params ?? {});
61
+ // Run indexing - Redux state is updated by indexer directly
62
+ // Clients poll status() to see progress
63
+ const stats = await ctx.owner.index({ force: validated.force });
64
+ return stats;
65
+ };
66
+ /**
67
+ * Index async handler.
68
+ * Starts indexing in the background and returns immediately.
69
+ * Clients poll status() to see progress and completion.
70
+ */
71
+ const indexAsyncHandler = async (params, ctx) => {
72
+ const validated = indexParamsSchema.parse(params ?? {});
73
+ const currentStatus = daemonState.getSnapshot().indexing.status;
74
+ if (ACTIVE_INDEX_STATUSES.has(currentStatus)) {
75
+ return { started: false, reason: 'in_progress' };
76
+ }
77
+ void ctx.owner.index({ force: validated.force }).catch(error => {
78
+ console.error('[daemon] Async index failed:', error);
79
+ const logger = ctx.owner.getLogger();
80
+ if (logger) {
81
+ const err = error instanceof Error ? error : new Error(String(error));
82
+ logger.error('DaemonServer', 'Async index failed', err);
83
+ }
84
+ });
85
+ return { started: true };
86
+ };
87
+ /**
88
+ * Status handler.
89
+ */
90
+ const statusHandler = async (_params, ctx) => {
91
+ return ctx.owner.getStatus();
92
+ };
93
+ /**
94
+ * Watch status handler.
95
+ */
96
+ const watchStatusHandler = async (_params, ctx) => {
97
+ return ctx.owner.getWatcherStatus();
98
+ };
99
+ /**
100
+ * Shutdown handler.
101
+ * Schedules server stop.
102
+ */
103
+ const shutdownHandler = async (params, ctx) => {
104
+ const validated = shutdownParamsSchema.parse(params ?? {});
105
+ console.error(`[daemon] Shutdown requested: ${validated.reason ?? 'no reason'}`);
106
+ // Schedule shutdown after response is sent
107
+ setImmediate(async () => {
108
+ await ctx.owner.shutdown();
109
+ await ctx.server.stop();
110
+ process.exit(0);
111
+ });
112
+ return { success: true };
113
+ };
114
+ /**
115
+ * Ping handler.
116
+ * Simple health check.
117
+ */
118
+ const pingHandler = async (_params, _ctx) => {
119
+ return {
120
+ pong: true,
121
+ timestamp: Date.now(),
122
+ protocolVersion: PROTOCOL_VERSION,
123
+ };
124
+ };
125
+ /**
126
+ * Health handler.
127
+ * Returns detailed health information.
128
+ */
129
+ const healthHandler = async (_params, ctx) => {
130
+ const snapshot = daemonState.getSnapshot();
131
+ return {
132
+ healthy: true,
133
+ uptime: process.uptime(),
134
+ memoryUsage: process.memoryUsage(),
135
+ clients: ctx.server.getClientCount(),
136
+ indexStatus: snapshot.indexing.status,
137
+ protocolVersion: PROTOCOL_VERSION,
138
+ };
139
+ };
140
+ // ============================================================================
141
+ // Handler Registry
142
+ // ============================================================================
143
+ /**
144
+ * Create the handler registry.
145
+ */
146
+ export function createHandlers() {
147
+ return {
148
+ search: searchHandler,
149
+ index: indexHandler,
150
+ indexAsync: indexAsyncHandler,
151
+ status: statusHandler,
152
+ watchStatus: watchStatusHandler,
153
+ shutdown: shutdownHandler,
154
+ ping: pingHandler,
155
+ health: healthHandler,
156
+ };
157
+ }
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * VibeRAG Daemon Entry Point
4
+ *
5
+ * Single daemon per project, owns all mutable state.
6
+ * CLI and MCP connect as clients via Unix socket.
7
+ *
8
+ * Usage:
9
+ * npx viberag-daemon
10
+ *
11
+ * The daemon uses the current working directory as the project root.
12
+ * It creates a socket at .viberag/daemon.sock and a PID file at .viberag/daemon.pid.
13
+ *
14
+ * The daemon will:
15
+ * - Acquire exclusive lock to prevent multiple instances
16
+ * - Start the file watcher for auto-indexing
17
+ * - Warm up the embedding provider
18
+ * - Listen for IPC requests (search, index, status, etc.)
19
+ * - Auto-shutdown after 5 minutes of idle (no connected clients)
20
+ */
21
+ export {};
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * VibeRAG Daemon Entry Point
4
+ *
5
+ * Single daemon per project, owns all mutable state.
6
+ * CLI and MCP connect as clients via Unix socket.
7
+ *
8
+ * Usage:
9
+ * npx viberag-daemon
10
+ *
11
+ * The daemon uses the current working directory as the project root.
12
+ * It creates a socket at .viberag/daemon.sock and a PID file at .viberag/daemon.pid.
13
+ *
14
+ * The daemon will:
15
+ * - Acquire exclusive lock to prevent multiple instances
16
+ * - Start the file watcher for auto-indexing
17
+ * - Warm up the embedding provider
18
+ * - Listen for IPC requests (search, index, status, etc.)
19
+ * - Auto-shutdown after 5 minutes of idle (no connected clients)
20
+ */
21
+ import path from 'node:path';
22
+ import lockfile from 'proper-lockfile';
23
+ import { DaemonOwner } from './owner.js';
24
+ import { DaemonServer } from './server.js';
25
+ import { LifecycleManager } from './lifecycle.js';
26
+ import { createHandlers } from './handlers.js';
27
+ import { configExists, loadConfig } from './lib/config.js';
28
+ // Use current working directory as project root
29
+ const projectRoot = process.cwd();
30
+ // Lock file path - inside .viberag directory
31
+ const LOCK_FILE_PATH = path.join(projectRoot, '.viberag', 'daemon.lock');
32
+ // Lock release function - set when lock is acquired
33
+ let releaseLock = null;
34
+ /**
35
+ * Acquire exclusive daemon lock to prevent multiple instances.
36
+ * Uses proper-lockfile which is cross-platform and handles crash recovery.
37
+ */
38
+ async function acquireDaemonLock() {
39
+ try {
40
+ const release = await lockfile.lock(projectRoot, {
41
+ lockfilePath: LOCK_FILE_PATH,
42
+ stale: 30000, // Lock is stale after 30s without mtime update
43
+ update: 10000, // Update mtime every 10s to prove liveness
44
+ retries: 0, // Fail immediately if locked
45
+ onCompromised: err => {
46
+ // Another process stole our lock (very rare edge case)
47
+ console.error('[daemon] Lock compromised:', err.message);
48
+ console.error('[daemon] Another daemon may have started. Exiting.');
49
+ process.exit(1);
50
+ },
51
+ });
52
+ console.error('[daemon] Acquired exclusive lock');
53
+ return release;
54
+ }
55
+ catch (err) {
56
+ if (err instanceof Error && 'code' in err && err.code === 'ELOCKED') {
57
+ console.error('[daemon] Another daemon is already running');
58
+ console.error('[daemon] Only one daemon per project is allowed.');
59
+ process.exit(1);
60
+ }
61
+ throw err;
62
+ }
63
+ }
64
+ /**
65
+ * Release the daemon lock on exit.
66
+ */
67
+ function setupLockRelease(release) {
68
+ releaseLock = release;
69
+ // Release lock on clean exit
70
+ const cleanup = () => {
71
+ if (releaseLock) {
72
+ // Synchronous-ish cleanup - lockfile handles this gracefully
73
+ releaseLock().catch(() => { });
74
+ releaseLock = null;
75
+ }
76
+ };
77
+ process.on('exit', cleanup);
78
+ process.on('SIGINT', cleanup);
79
+ process.on('SIGTERM', cleanup);
80
+ }
81
+ /**
82
+ * Main daemon entry point.
83
+ */
84
+ async function main() {
85
+ console.error(`[daemon] Starting for ${projectRoot}`);
86
+ // Verify project is initialized (needed before lock since lock is in .viberag/)
87
+ if (!(await configExists(projectRoot))) {
88
+ console.error('[daemon] Project not initialized');
89
+ console.error('[daemon] Run "npx viberag" and use /init command to initialize.');
90
+ process.exit(1);
91
+ }
92
+ // Acquire exclusive lock BEFORE any other operations
93
+ // This prevents multiple daemons from running concurrently
94
+ const release = await acquireDaemonLock();
95
+ setupLockRelease(release);
96
+ // Load config
97
+ await loadConfig(projectRoot);
98
+ // Default idle timeout: 5 minutes (can be made configurable later)
99
+ const idleTimeoutMs = 5 * 60 * 1000;
100
+ // Create components
101
+ const owner = new DaemonOwner(projectRoot);
102
+ const server = new DaemonServer(owner);
103
+ const lifecycle = new LifecycleManager(server, owner, idleTimeoutMs);
104
+ // Register handlers
105
+ server.setHandlers(createHandlers());
106
+ // Initialize owner (starts watcher, begins warmup)
107
+ await owner.initialize();
108
+ // Start server
109
+ await server.start();
110
+ // Register signal handlers
111
+ lifecycle.registerSignalHandlers();
112
+ // Start idle timer (cancelled on first connect)
113
+ lifecycle.startInitialIdleTimer();
114
+ console.error(`[daemon] Ready`);
115
+ console.error(`[daemon] Socket: ${owner.getSocketPath()}`);
116
+ console.error(`[daemon] PID: ${process.pid}`);
117
+ }
118
+ // Run main with error handling
119
+ main().catch(error => {
120
+ // Pass Error object directly to preserve stack trace (ADR-011)
121
+ console.error('[daemon] Fatal error:', error);
122
+ process.exit(1);
123
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Bounded async channel with backpressure.
3
+ *
4
+ * Implements producer-consumer pattern where:
5
+ * - Producer blocks when buffer is full (backpressure)
6
+ * - Consumer blocks when buffer is empty
7
+ * - Provides memory-bounded streaming between async operations
8
+ *
9
+ * Used in indexing pipeline to decouple batch building from embedding.
10
+ */
11
+ /**
12
+ * Bounded async channel for producer-consumer communication.
13
+ *
14
+ * @template T - Type of items in the channel
15
+ */
16
+ export declare class BoundedChannel<T> {
17
+ private readonly capacity;
18
+ private buffer;
19
+ private closed;
20
+ private waitingPush;
21
+ private waitingPull;
22
+ /**
23
+ * Create a bounded channel.
24
+ * @param capacity - Maximum number of items in buffer (must be >= 1)
25
+ */
26
+ constructor(capacity: number);
27
+ /**
28
+ * Push an item to the channel.
29
+ * Blocks if buffer is full (backpressure).
30
+ *
31
+ * @throws Error if channel is closed
32
+ */
33
+ push(item: T): Promise<void>;
34
+ /**
35
+ * Pull an item from the channel.
36
+ * Blocks if buffer is empty.
37
+ * Returns null when channel is closed and empty.
38
+ */
39
+ pull(): Promise<T | null>;
40
+ /**
41
+ * Close the channel.
42
+ * Remaining items can still be pulled, but no new items can be pushed.
43
+ */
44
+ close(): void;
45
+ /** Current number of items in buffer */
46
+ get size(): number;
47
+ /** Whether channel is closed */
48
+ get isClosed(): boolean;
49
+ /** Maximum buffer capacity */
50
+ get maxCapacity(): number;
51
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Bounded async channel with backpressure.
3
+ *
4
+ * Implements producer-consumer pattern where:
5
+ * - Producer blocks when buffer is full (backpressure)
6
+ * - Consumer blocks when buffer is empty
7
+ * - Provides memory-bounded streaming between async operations
8
+ *
9
+ * Used in indexing pipeline to decouple batch building from embedding.
10
+ */
11
+ /**
12
+ * Bounded async channel for producer-consumer communication.
13
+ *
14
+ * @template T - Type of items in the channel
15
+ */
16
+ export class BoundedChannel {
17
+ /**
18
+ * Create a bounded channel.
19
+ * @param capacity - Maximum number of items in buffer (must be >= 1)
20
+ */
21
+ constructor(capacity) {
22
+ Object.defineProperty(this, "capacity", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: capacity
27
+ });
28
+ Object.defineProperty(this, "buffer", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: []
33
+ });
34
+ Object.defineProperty(this, "closed", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: false
39
+ });
40
+ Object.defineProperty(this, "waitingPush", {
41
+ enumerable: true,
42
+ configurable: true,
43
+ writable: true,
44
+ value: []
45
+ });
46
+ Object.defineProperty(this, "waitingPull", {
47
+ enumerable: true,
48
+ configurable: true,
49
+ writable: true,
50
+ value: []
51
+ });
52
+ if (capacity < 1) {
53
+ throw new Error('BoundedChannel capacity must be >= 1');
54
+ }
55
+ }
56
+ /**
57
+ * Push an item to the channel.
58
+ * Blocks if buffer is full (backpressure).
59
+ *
60
+ * @throws Error if channel is closed
61
+ */
62
+ async push(item) {
63
+ if (this.closed) {
64
+ throw new Error('Cannot push to closed channel');
65
+ }
66
+ // If there's a waiting consumer, deliver directly
67
+ const consumer = this.waitingPull.shift();
68
+ if (consumer) {
69
+ consumer.resolve(item);
70
+ return;
71
+ }
72
+ // If buffer has space, add to it
73
+ if (this.buffer.length < this.capacity) {
74
+ this.buffer.push(item);
75
+ return;
76
+ }
77
+ // Buffer full - wait for space (backpressure)
78
+ await new Promise((resolve, reject) => {
79
+ this.waitingPush.push({ resolve, reject });
80
+ });
81
+ // After waking, add to buffer
82
+ this.buffer.push(item);
83
+ }
84
+ /**
85
+ * Pull an item from the channel.
86
+ * Blocks if buffer is empty.
87
+ * Returns null when channel is closed and empty.
88
+ */
89
+ async pull() {
90
+ // If buffer has items, return one
91
+ if (this.buffer.length > 0) {
92
+ const item = this.buffer.shift();
93
+ // Wake a waiting producer
94
+ const producer = this.waitingPush.shift();
95
+ if (producer) {
96
+ producer.resolve();
97
+ }
98
+ return item;
99
+ }
100
+ // If closed and empty, return null (signal completion)
101
+ if (this.closed) {
102
+ return null;
103
+ }
104
+ // Wait for item
105
+ return new Promise(resolve => {
106
+ this.waitingPull.push({ resolve });
107
+ });
108
+ }
109
+ /**
110
+ * Close the channel.
111
+ * Remaining items can still be pulled, but no new items can be pushed.
112
+ */
113
+ close() {
114
+ this.closed = true;
115
+ // Reject all waiting producers
116
+ for (const producer of this.waitingPush) {
117
+ producer.reject(new Error('Channel closed'));
118
+ }
119
+ this.waitingPush = [];
120
+ // Resolve all waiting consumers with null
121
+ for (const consumer of this.waitingPull) {
122
+ consumer.resolve(null);
123
+ }
124
+ this.waitingPull = [];
125
+ }
126
+ /** Current number of items in buffer */
127
+ get size() {
128
+ return this.buffer.length;
129
+ }
130
+ /** Whether channel is closed */
131
+ get isClosed() {
132
+ return this.closed;
133
+ }
134
+ /** Maximum buffer capacity */
135
+ get maxCapacity() {
136
+ return this.capacity;
137
+ }
138
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Chunker - Tree-sitter based code chunking.
3
+ *
4
+ * Uses web-tree-sitter (WASM) to parse code and extract semantic chunks
5
+ * (functions, classes, methods) for embedding.
6
+ */
7
+ import { type Chunk, type SupportedLanguage } from './types.js';
8
+ /**
9
+ * Chunker that uses web-tree-sitter (WASM) to extract semantic code chunks.
10
+ * Provides 100% platform compatibility - no native compilation required.
11
+ */
12
+ export declare class Chunker {
13
+ private parser;
14
+ private languages;
15
+ private initialized;
16
+ private wasmBasePath;
17
+ constructor();
18
+ /**
19
+ * Initialize web-tree-sitter and load language grammars.
20
+ * Must be called before using chunkFile().
21
+ */
22
+ initialize(): Promise<void>;
23
+ /**
24
+ * Get the language for a file extension.
25
+ */
26
+ getLanguageForExtension(ext: string): SupportedLanguage | null;
27
+ /**
28
+ * Check if a language is supported.
29
+ */
30
+ isLanguageSupported(lang: SupportedLanguage): boolean;
31
+ /**
32
+ * Check if a file is a markdown file.
33
+ */
34
+ private isMarkdownFile;
35
+ /**
36
+ * Extract chunks from a file.
37
+ *
38
+ * @param filepath - Path to the file (used for extension detection and context headers)
39
+ * @param content - File content to parse
40
+ * @param maxChunkSize - Maximum chunk size in characters (default: 2000)
41
+ * @returns Array of extracted chunks
42
+ */
43
+ chunkFile(filepath: string, content: string, maxChunkSize?: number): Chunk[];
44
+ /**
45
+ * Extract chunks from a syntax tree.
46
+ */
47
+ private extractChunks;
48
+ /**
49
+ * Recursively traverse nodes to extract chunks.
50
+ * Tracks parent context (class name) for context headers.
51
+ */
52
+ private traverseNode;
53
+ /**
54
+ * Convert a syntax node to a chunk.
55
+ */
56
+ private nodeToChunk;
57
+ /**
58
+ * Extract the signature line (first line of function/class declaration).
59
+ */
60
+ private extractSignature;
61
+ /**
62
+ * Extract docstring from a function/class node.
63
+ */
64
+ private extractDocstring;
65
+ /**
66
+ * Check if a node is exported/public.
67
+ */
68
+ private extractIsExported;
69
+ /**
70
+ * Helper to check for visibility modifiers in a node.
71
+ */
72
+ private hasVisibilityModifier;
73
+ /**
74
+ * Extract decorator names from a node.
75
+ */
76
+ private extractDecoratorNames;
77
+ /**
78
+ * Helper to find a child node of specific types.
79
+ */
80
+ private findChildOfType;
81
+ /**
82
+ * Build a context header for a chunk.
83
+ */
84
+ private buildContextHeader;
85
+ /**
86
+ * Extract the name of a function/class/method from its node.
87
+ */
88
+ private extractName;
89
+ /**
90
+ * Create a module-level chunk for the entire file.
91
+ */
92
+ private createModuleChunk;
93
+ /**
94
+ * Chunk markdown files with heading-aware splitting and overlap.
95
+ *
96
+ * Strategy:
97
+ * 1. Try to split at heading boundaries (# lines)
98
+ * 2. Use sliding window with overlap between chunks
99
+ * 3. Merge small final chunks to avoid orphans
100
+ */
101
+ private chunkMarkdown;
102
+ /**
103
+ * Create a chunk from markdown content.
104
+ */
105
+ private createMarkdownChunk;
106
+ /**
107
+ * Enforce size limits: split oversized chunks and merge tiny ones.
108
+ *
109
+ * @param overlapLines - Number of lines to overlap between chunks (for context continuity)
110
+ */
111
+ private enforceSizeLimits;
112
+ /**
113
+ * Split an oversized chunk by lines.
114
+ * Tries to split at natural boundaries (empty lines, statement ends).
115
+ *
116
+ * @param overlapLines - Number of lines from previous chunk to include for context
117
+ */
118
+ private splitChunkByLines;
119
+ /**
120
+ * Create a chunk from a split portion.
121
+ */
122
+ private createSplitChunk;
123
+ /**
124
+ * Extract class name from a context header string.
125
+ */
126
+ private extractClassFromContext;
127
+ /**
128
+ * Merge small adjacent chunks of the same type to avoid fragment explosion.
129
+ */
130
+ private mergeSmallChunks;
131
+ /**
132
+ * Close the parser and free resources.
133
+ */
134
+ close(): void;
135
+ }