ragcode-context-engine 0.1.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 (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +366 -0
  3. package/README.zh-CN.md +363 -0
  4. package/dist/src/cli/configure/app.d.ts +6 -0
  5. package/dist/src/cli/configure/app.js +81 -0
  6. package/dist/src/cli/configure/run.d.ts +5 -0
  7. package/dist/src/cli/configure/run.js +85 -0
  8. package/dist/src/cli/configure/state.d.ts +42 -0
  9. package/dist/src/cli/configure/state.js +174 -0
  10. package/dist/src/cli/configure.d.ts +31 -0
  11. package/dist/src/cli/configure.js +101 -0
  12. package/dist/src/cli/index.d.ts +2 -0
  13. package/dist/src/cli/index.js +503 -0
  14. package/dist/src/cli/tui/index-progress.d.ts +12 -0
  15. package/dist/src/cli/tui/index-progress.js +49 -0
  16. package/dist/src/cli/tui/watch-status.d.ts +10 -0
  17. package/dist/src/cli/tui/watch-status.js +27 -0
  18. package/dist/src/cli/update.d.ts +18 -0
  19. package/dist/src/cli/update.js +111 -0
  20. package/dist/src/config/dotenv.d.ts +1 -0
  21. package/dist/src/config/dotenv.js +14 -0
  22. package/dist/src/config/graph-runtime.d.ts +13 -0
  23. package/dist/src/config/graph-runtime.js +29 -0
  24. package/dist/src/config/runtime-config.d.ts +87 -0
  25. package/dist/src/config/runtime-config.js +215 -0
  26. package/dist/src/config/semantic-runtime.d.ts +24 -0
  27. package/dist/src/config/semantic-runtime.js +89 -0
  28. package/dist/src/context/context-builder.d.ts +20 -0
  29. package/dist/src/context/context-builder.js +277 -0
  30. package/dist/src/context/expansion-policy.d.ts +6 -0
  31. package/dist/src/context/expansion-policy.js +49 -0
  32. package/dist/src/context/skeletonizer.d.ts +2 -0
  33. package/dist/src/context/skeletonizer.js +79 -0
  34. package/dist/src/context/snippet-renderer.d.ts +2 -0
  35. package/dist/src/context/snippet-renderer.js +67 -0
  36. package/dist/src/core/contracts.d.ts +74 -0
  37. package/dist/src/core/contracts.js +1 -0
  38. package/dist/src/core/engine.d.ts +64 -0
  39. package/dist/src/core/engine.js +442 -0
  40. package/dist/src/core/types.d.ts +490 -0
  41. package/dist/src/core/types.js +1 -0
  42. package/dist/src/diagnostics/doctor.d.ts +66 -0
  43. package/dist/src/diagnostics/doctor.js +193 -0
  44. package/dist/src/diagnostics/embedding-test.d.ts +24 -0
  45. package/dist/src/diagnostics/embedding-test.js +83 -0
  46. package/dist/src/graph/diff-files.d.ts +1 -0
  47. package/dist/src/graph/diff-files.js +14 -0
  48. package/dist/src/graph/impact-report.d.ts +10 -0
  49. package/dist/src/graph/impact-report.js +173 -0
  50. package/dist/src/graph/in-memory-graph-store.d.ts +36 -0
  51. package/dist/src/graph/in-memory-graph-store.js +395 -0
  52. package/dist/src/graph/owner-ranking.d.ts +2 -0
  53. package/dist/src/graph/owner-ranking.js +41 -0
  54. package/dist/src/graph/sqlite-graph-store.d.ts +51 -0
  55. package/dist/src/graph/sqlite-graph-store.js +724 -0
  56. package/dist/src/graph/sqlite-statements.d.ts +36 -0
  57. package/dist/src/graph/sqlite-statements.js +105 -0
  58. package/dist/src/graph/target-matcher.d.ts +13 -0
  59. package/dist/src/graph/target-matcher.js +64 -0
  60. package/dist/src/index.d.ts +32 -0
  61. package/dist/src/index.js +32 -0
  62. package/dist/src/indexing/analyzers/fallback-analyzer.d.ts +6 -0
  63. package/dist/src/indexing/analyzers/fallback-analyzer.js +45 -0
  64. package/dist/src/indexing/analyzers/go-treesitter-analyzer.d.ts +2 -0
  65. package/dist/src/indexing/analyzers/go-treesitter-analyzer.js +87 -0
  66. package/dist/src/indexing/analyzers/java-treesitter-analyzer.d.ts +2 -0
  67. package/dist/src/indexing/analyzers/java-treesitter-analyzer.js +88 -0
  68. package/dist/src/indexing/analyzers/python-treesitter-analyzer.d.ts +2 -0
  69. package/dist/src/indexing/analyzers/python-treesitter-analyzer.js +96 -0
  70. package/dist/src/indexing/analyzers/registry.d.ts +5 -0
  71. package/dist/src/indexing/analyzers/registry.js +23 -0
  72. package/dist/src/indexing/analyzers/rust-treesitter-analyzer.d.ts +2 -0
  73. package/dist/src/indexing/analyzers/rust-treesitter-analyzer.js +96 -0
  74. package/dist/src/indexing/analyzers/tree-sitter-base.d.ts +30 -0
  75. package/dist/src/indexing/analyzers/tree-sitter-base.js +163 -0
  76. package/dist/src/indexing/analyzers/types.d.ts +17 -0
  77. package/dist/src/indexing/analyzers/types.js +1 -0
  78. package/dist/src/indexing/analyzers/typescript-analyzer.d.ts +5 -0
  79. package/dist/src/indexing/analyzers/typescript-analyzer.js +199 -0
  80. package/dist/src/indexing/ast-analyzer.d.ts +11 -0
  81. package/dist/src/indexing/ast-analyzer.js +11 -0
  82. package/dist/src/indexing/chunker.d.ts +11 -0
  83. package/dist/src/indexing/chunker.js +157 -0
  84. package/dist/src/indexing/ignore-policy.d.ts +6 -0
  85. package/dist/src/indexing/ignore-policy.js +40 -0
  86. package/dist/src/indexing/indexer.d.ts +13 -0
  87. package/dist/src/indexing/indexer.js +189 -0
  88. package/dist/src/indexing/language.d.ts +3 -0
  89. package/dist/src/indexing/language.js +24 -0
  90. package/dist/src/indexing/scanner.d.ts +13 -0
  91. package/dist/src/indexing/scanner.js +87 -0
  92. package/dist/src/lsp/definition-resolver.d.ts +6 -0
  93. package/dist/src/lsp/definition-resolver.js +60 -0
  94. package/dist/src/lsp/typescript-language-service.d.ts +21 -0
  95. package/dist/src/lsp/typescript-language-service.js +82 -0
  96. package/dist/src/mcp/server.d.ts +11 -0
  97. package/dist/src/mcp/server.js +64 -0
  98. package/dist/src/mcp/tools.d.ts +266 -0
  99. package/dist/src/mcp/tools.js +309 -0
  100. package/dist/src/project/project-identity.d.ts +2 -0
  101. package/dist/src/project/project-identity.js +24 -0
  102. package/dist/src/project/project-registry.d.ts +12 -0
  103. package/dist/src/project/project-registry.js +49 -0
  104. package/dist/src/project/workspace-resolver.d.ts +20 -0
  105. package/dist/src/project/workspace-resolver.js +62 -0
  106. package/dist/src/retrieval/graph-reranker.d.ts +11 -0
  107. package/dist/src/retrieval/graph-reranker.js +0 -0
  108. package/dist/src/retrieval/hybrid-retriever.d.ts +31 -0
  109. package/dist/src/retrieval/hybrid-retriever.js +111 -0
  110. package/dist/src/retrieval/path-classification.d.ts +6 -0
  111. package/dist/src/retrieval/path-classification.js +22 -0
  112. package/dist/src/retrieval/query-matching.d.ts +22 -0
  113. package/dist/src/retrieval/query-matching.js +166 -0
  114. package/dist/src/retrieval/query-planner.d.ts +5 -0
  115. package/dist/src/retrieval/query-planner.js +77 -0
  116. package/dist/src/retrieval/ranking-signals.d.ts +19 -0
  117. package/dist/src/retrieval/ranking-signals.js +97 -0
  118. package/dist/src/retrieval/topology-distance.d.ts +21 -0
  119. package/dist/src/retrieval/topology-distance.js +116 -0
  120. package/dist/src/reuse/reuse-detector.d.ts +12 -0
  121. package/dist/src/reuse/reuse-detector.js +564 -0
  122. package/dist/src/semantic/deterministic-embedding.d.ts +7 -0
  123. package/dist/src/semantic/deterministic-embedding.js +31 -0
  124. package/dist/src/semantic/in-memory-semantic-store.d.ts +11 -0
  125. package/dist/src/semantic/in-memory-semantic-store.js +65 -0
  126. package/dist/src/semantic/lance-semantic-store.d.ts +131 -0
  127. package/dist/src/semantic/lance-semantic-store.js +623 -0
  128. package/dist/src/semantic/openai-compatible-embedding.d.ts +19 -0
  129. package/dist/src/semantic/openai-compatible-embedding.js +75 -0
  130. package/dist/src/service/service-identity.d.ts +13 -0
  131. package/dist/src/service/service-identity.js +48 -0
  132. package/dist/src/service/service-manager.d.ts +29 -0
  133. package/dist/src/service/service-manager.js +231 -0
  134. package/dist/src/service/service-templates.d.ts +22 -0
  135. package/dist/src/service/service-templates.js +101 -0
  136. package/dist/src/subgraph/impact-explainer.d.ts +2 -0
  137. package/dist/src/subgraph/impact-explainer.js +54 -0
  138. package/dist/src/subgraph/node-expander.d.ts +13 -0
  139. package/dist/src/subgraph/node-expander.js +139 -0
  140. package/dist/src/subgraph/output-preset.d.ts +3 -0
  141. package/dist/src/subgraph/output-preset.js +102 -0
  142. package/dist/src/subgraph/subgraph-builder.d.ts +17 -0
  143. package/dist/src/subgraph/subgraph-builder.js +688 -0
  144. package/dist/src/topology/export-index.d.ts +7 -0
  145. package/dist/src/topology/export-index.js +14 -0
  146. package/dist/src/topology/framework-topology.d.ts +3 -0
  147. package/dist/src/topology/framework-topology.js +460 -0
  148. package/dist/src/topology/import-resolver.d.ts +2 -0
  149. package/dist/src/topology/import-resolver.js +29 -0
  150. package/dist/src/topology/orm-topology.d.ts +3 -0
  151. package/dist/src/topology/orm-topology.js +200 -0
  152. package/dist/src/topology/runtime-topology.d.ts +3 -0
  153. package/dist/src/topology/runtime-topology.js +204 -0
  154. package/dist/src/topology/symbol-resolver.d.ts +6 -0
  155. package/dist/src/topology/symbol-resolver.js +74 -0
  156. package/dist/src/topology/test-topology.d.ts +2 -0
  157. package/dist/src/topology/test-topology.js +82 -0
  158. package/dist/src/utils/hash.d.ts +2 -0
  159. package/dist/src/utils/hash.js +7 -0
  160. package/dist/src/utils/path.d.ts +2 -0
  161. package/dist/src/utils/path.js +7 -0
  162. package/dist/src/watch/event-journal.d.ts +17 -0
  163. package/dist/src/watch/event-journal.js +81 -0
  164. package/dist/src/watch/file-event-coalescer.d.ts +9 -0
  165. package/dist/src/watch/file-event-coalescer.js +39 -0
  166. package/dist/src/watch/index-scheduler.d.ts +52 -0
  167. package/dist/src/watch/index-scheduler.js +190 -0
  168. package/dist/src/watch/watch-daemon.d.ts +73 -0
  169. package/dist/src/watch/watch-daemon.js +368 -0
  170. package/dist/src/watch/watcher-liveness.d.ts +47 -0
  171. package/dist/src/watch/watcher-liveness.js +168 -0
  172. package/dist/src/web/server.d.ts +1 -0
  173. package/dist/src/web/server.js +375 -0
  174. package/package.json +94 -0
@@ -0,0 +1,375 @@
1
+ import path from 'node:path';
2
+ import express from 'express';
3
+ import cors from 'cors';
4
+ import { WebSocketServer } from 'ws';
5
+ import { RagCodeEngine } from '../core/engine.js';
6
+ import { createRuntimeComponentsForRepo, loadRuntimeConfig, readRuntimeConfigFile, redactRuntimeConfig, writeRuntimeConfigFile } from '../config/runtime-config.js';
7
+ import { FileWatchDaemon } from '../watch/watch-daemon.js';
8
+ import { readWatcherLiveness } from '../watch/watcher-liveness.js';
9
+ const app = express();
10
+ const port = process.env.RAGCODE_WEB_PORT ? parseInt(process.env.RAGCODE_WEB_PORT) : 3000;
11
+ const defaultRepo = path.resolve(process.env.RAGCODE_REPO_ROOT ?? process.cwd());
12
+ const configPath = path.join(defaultRepo, '.ragcode', 'config.json');
13
+ app.use(cors());
14
+ app.use(express.json({ limit: '4mb' }));
15
+ // ===== Shared engine + watch daemon =====
16
+ let engine = null;
17
+ let activeRepo = null;
18
+ let daemon = null;
19
+ let lastDaemonStatus = null;
20
+ const sockets = new Set();
21
+ function broadcast(payload) {
22
+ const message = JSON.stringify(payload);
23
+ for (const ws of sockets) {
24
+ if (ws.readyState === ws.OPEN)
25
+ ws.send(message);
26
+ }
27
+ }
28
+ function buildEngine(repoPath = activeRepo ?? defaultRepo) {
29
+ const runtime = createRuntimeComponentsForRepo({ cwd: repoPath, overrides: { repoRoot: repoPath } });
30
+ return new RagCodeEngine({
31
+ cwd: repoPath,
32
+ graphStore: runtime.graphStore,
33
+ semanticStore: runtime.semanticStore,
34
+ embeddingProvider: runtime.embeddingProvider,
35
+ });
36
+ }
37
+ async function ensureEngine(repoPath) {
38
+ const target = repoPath ? path.resolve(repoPath) : (activeRepo ?? defaultRepo);
39
+ if (engine && activeRepo === target)
40
+ return engine;
41
+ engine = buildEngine();
42
+ await engine.indexRepo(target);
43
+ activeRepo = target;
44
+ return engine;
45
+ }
46
+ function requireEngine(res) {
47
+ if (!engine) {
48
+ res.status(409).json({ error: 'Engine not initialized. Index a repository first via POST /api/index.' });
49
+ return null;
50
+ }
51
+ return engine;
52
+ }
53
+ function fail(res, error) {
54
+ const message = error instanceof Error ? error.message : String(error);
55
+ res.status(500).json({ error: message });
56
+ }
57
+ // ===== Config (persisted to .ragcode/config.json) =====
58
+ app.get('/api/config', (_req, res) => {
59
+ const repoRoot = activeRepo ?? defaultRepo;
60
+ const runtime = redactRuntimeConfig(loadRuntimeConfig({ cwd: repoRoot, overrides: { repoRoot } }));
61
+ res.json(runtime);
62
+ });
63
+ app.post('/api/config', (req, res) => {
64
+ try {
65
+ const repoRoot = activeRepo ?? defaultRepo;
66
+ const body = req.body;
67
+ const merged = { ...readRuntimeConfigFile(path.join(repoRoot, '.ragcode', 'config.json')), ...body };
68
+ const writtenPath = writeRuntimeConfigFile(repoRoot, merged);
69
+ res.json({ success: true, configPath: writtenPath, note: 'Restart the server or re-index the repository for store/provider changes to take effect.' });
70
+ }
71
+ catch (error) {
72
+ fail(res, error);
73
+ }
74
+ });
75
+ // ===== Index management =====
76
+ app.post('/api/index', async (req, res) => {
77
+ const repoPath = req.body?.repoPath ?? defaultRepo;
78
+ try {
79
+ const resolvedRepo = path.resolve(repoPath);
80
+ const eng = buildEngine(resolvedRepo);
81
+ await eng.indexRepo(resolvedRepo);
82
+ engine = eng;
83
+ activeRepo = resolvedRepo;
84
+ const status = await eng.indexStatus(undefined);
85
+ res.json({ success: true, repoRoot: activeRepo, status });
86
+ }
87
+ catch (error) {
88
+ fail(res, error);
89
+ }
90
+ });
91
+ app.post('/api/refresh', async (_req, res) => {
92
+ const eng = requireEngine(res);
93
+ if (!eng)
94
+ return;
95
+ try {
96
+ await eng.refreshIndex(undefined);
97
+ res.json({ success: true, status: await eng.indexStatus(undefined) });
98
+ }
99
+ catch (error) {
100
+ fail(res, error);
101
+ }
102
+ });
103
+ app.get('/api/status', async (_req, res) => {
104
+ const eng = requireEngine(res);
105
+ if (!eng)
106
+ return;
107
+ try {
108
+ const status = await eng.indexStatus(undefined);
109
+ res.json(status);
110
+ }
111
+ catch (error) {
112
+ fail(res, error);
113
+ }
114
+ });
115
+ app.get('/api/languages', async (_req, res) => {
116
+ const eng = requireEngine(res);
117
+ if (!eng)
118
+ return;
119
+ try {
120
+ const { symbols } = await eng.graphSnapshot(undefined);
121
+ const byLanguage = new Map();
122
+ for (const sym of symbols) {
123
+ byLanguage.set(sym.language, (byLanguage.get(sym.language) ?? 0) + 1);
124
+ }
125
+ res.json({ languages: [...byLanguage.entries()].map(([language, count]) => ({ language, count })) });
126
+ }
127
+ catch (error) {
128
+ fail(res, error);
129
+ }
130
+ });
131
+ // ===== Context retrieval (full ContextPack) =====
132
+ app.post('/api/context', async (req, res) => {
133
+ const eng = requireEngine(res);
134
+ if (!eng)
135
+ return;
136
+ const { query, mode, budgetChars } = req.body;
137
+ if (!query?.trim())
138
+ return res.status(400).json({ error: 'query required' });
139
+ try {
140
+ const pack = await eng.getContext({
141
+ query,
142
+ mode: mode ?? 'auto',
143
+ budgetChars: budgetChars ?? 16000,
144
+ });
145
+ res.json(pack);
146
+ }
147
+ catch (error) {
148
+ fail(res, error);
149
+ }
150
+ });
151
+ // ===== Raw search hits =====
152
+ app.post('/api/search', async (req, res) => {
153
+ const eng = requireEngine(res);
154
+ if (!eng)
155
+ return;
156
+ const { query, mode, limit } = req.body;
157
+ try {
158
+ const hits = await eng.searchCode({ query: query ?? '', mode, limit: limit ?? 30 });
159
+ res.json({ hits });
160
+ }
161
+ catch (error) {
162
+ fail(res, error);
163
+ }
164
+ });
165
+ // ===== Code graph (real nodes + edges) =====
166
+ app.get('/api/graph', async (req, res) => {
167
+ const eng = requireEngine(res);
168
+ if (!eng)
169
+ return;
170
+ const language = req.query.language;
171
+ const kind = req.query.kind;
172
+ const limit = req.query.limit ? parseInt(req.query.limit) : 400;
173
+ try {
174
+ const { symbols, edges } = await eng.graphSnapshot(undefined);
175
+ let filtered = symbols;
176
+ if (language)
177
+ filtered = filtered.filter((s) => s.language === language);
178
+ if (kind)
179
+ filtered = filtered.filter((s) => s.kind === kind);
180
+ filtered = filtered.slice(0, limit);
181
+ const nodeIds = new Set(filtered.map((s) => s.id));
182
+ const nodes = filtered.map((s) => toGraphNode(s));
183
+ const scopedEdges = edges.filter((edge) => nodeIds.has(edge.sourceId) && nodeIds.has(edge.targetId));
184
+ res.json({ nodes, edges: scopedEdges, total: symbols.length, shown: nodes.length, totalEdges: edges.length });
185
+ }
186
+ catch (error) {
187
+ fail(res, error);
188
+ }
189
+ });
190
+ function toGraphNode(symbol) {
191
+ return {
192
+ id: symbol.id,
193
+ label: symbol.name,
194
+ kind: symbol.kind,
195
+ language: symbol.language,
196
+ filePath: symbol.filePath,
197
+ startLine: symbol.startLine,
198
+ endLine: symbol.endLine,
199
+ exported: symbol.exported ?? false,
200
+ signature: symbol.signature,
201
+ };
202
+ }
203
+ app.get('/api/symbol/:name', async (req, res) => {
204
+ const eng = requireEngine(res);
205
+ if (!eng)
206
+ return;
207
+ try {
208
+ res.json({ symbols: await eng.findSymbol(undefined, req.params.name) });
209
+ }
210
+ catch (error) {
211
+ fail(res, error);
212
+ }
213
+ });
214
+ app.get('/api/file', async (req, res) => {
215
+ const eng = requireEngine(res);
216
+ if (!eng)
217
+ return;
218
+ const filePath = req.query.path;
219
+ if (!filePath)
220
+ return res.status(400).json({ error: 'path required' });
221
+ try {
222
+ res.json(await eng.explainFile(undefined, filePath));
223
+ }
224
+ catch (error) {
225
+ fail(res, error);
226
+ }
227
+ });
228
+ // ===== Impact / trace / tests / reuse / subgraph =====
229
+ app.post('/api/impact', async (req, res) => {
230
+ const eng = requireEngine(res);
231
+ if (!eng)
232
+ return;
233
+ const { target } = req.body;
234
+ if (!target?.trim())
235
+ return res.status(400).json({ error: 'target required' });
236
+ try {
237
+ res.json(await eng.impactAnalysis(undefined, target));
238
+ }
239
+ catch (error) {
240
+ fail(res, error);
241
+ }
242
+ });
243
+ app.post('/api/trace', async (req, res) => {
244
+ const eng = requireEngine(res);
245
+ if (!eng)
246
+ return;
247
+ const { entry, maxSteps } = req.body;
248
+ if (!entry?.trim())
249
+ return res.status(400).json({ error: 'entry required' });
250
+ try {
251
+ res.json(await eng.traceFlow(undefined, entry, maxSteps));
252
+ }
253
+ catch (error) {
254
+ fail(res, error);
255
+ }
256
+ });
257
+ app.post('/api/related-tests', async (req, res) => {
258
+ const eng = requireEngine(res);
259
+ if (!eng)
260
+ return;
261
+ const { target } = req.body;
262
+ if (!target?.trim())
263
+ return res.status(400).json({ error: 'target required' });
264
+ try {
265
+ res.json(await eng.relatedTests(undefined, target));
266
+ }
267
+ catch (error) {
268
+ fail(res, error);
269
+ }
270
+ });
271
+ app.post('/api/reuse', async (req, res) => {
272
+ const eng = requireEngine(res);
273
+ if (!eng)
274
+ return;
275
+ const { query, limit } = req.body;
276
+ if (!query?.trim())
277
+ return res.status(400).json({ error: 'query required' });
278
+ try {
279
+ res.json(await eng.findReuseCandidates({ query, limit: limit ?? 8 }));
280
+ }
281
+ catch (error) {
282
+ fail(res, error);
283
+ }
284
+ });
285
+ app.post('/api/subgraph', async (req, res) => {
286
+ const eng = requireEngine(res);
287
+ if (!eng)
288
+ return;
289
+ const { query, mode, maxHops, budgetChars } = req.body;
290
+ if (!query?.trim())
291
+ return res.status(400).json({ error: 'query required' });
292
+ try {
293
+ res.json(await eng.verifiedSubgraph({ query, mode: mode ?? 'impact', maxHops, budgetChars }));
294
+ }
295
+ catch (error) {
296
+ fail(res, error);
297
+ }
298
+ });
299
+ // ===== Watch daemon control =====
300
+ app.get('/api/watch/status', async (_req, res) => {
301
+ // Report both the dashboard's in-process observation daemon (if started here) and the on-disk
302
+ // liveness of any OS-service watcher, so the UI shows freshness even when the watcher is owned by
303
+ // systemd/launchd/Task Scheduler rather than this process.
304
+ const liveness = await readWatcherLiveness(activeRepo ?? defaultRepo);
305
+ if (!daemon)
306
+ return res.json({ running: false, status: null, liveness });
307
+ res.json({ running: true, status: await daemon.status(), liveness });
308
+ });
309
+ app.post('/api/watch/start', async (_req, res) => {
310
+ const eng = requireEngine(res);
311
+ if (!eng)
312
+ return;
313
+ if (daemon)
314
+ return res.json({ success: true, alreadyRunning: true });
315
+ try {
316
+ daemon = new FileWatchDaemon(eng, activeRepo ?? defaultRepo, {
317
+ indexOnStart: false,
318
+ // This is an in-process observation daemon for the dashboard, not the canonical watcher. Don't
319
+ // acquire the per-repo lock or publish a heartbeat — that belongs to `ragcode watch` / the OS
320
+ // service. Without this, starting observation here would collide with a live service watcher.
321
+ manageLifecycleFiles: false,
322
+ onEvent: (event) => {
323
+ broadcast({ type: 'file-event', event: event.event, path: event.filePath, timestamp: event.observedAtMs });
324
+ },
325
+ onStatus: (status) => {
326
+ lastDaemonStatus = status;
327
+ broadcast({ type: 'watch-status', status, timestamp: Date.now() });
328
+ },
329
+ });
330
+ await daemon.start();
331
+ res.json({ success: true, status: await daemon.status() });
332
+ }
333
+ catch (error) {
334
+ daemon = null;
335
+ fail(res, error);
336
+ }
337
+ });
338
+ app.post('/api/watch/stop', async (_req, res) => {
339
+ if (!daemon)
340
+ return res.json({ success: true, alreadyStopped: true });
341
+ try {
342
+ await daemon.stop();
343
+ daemon = null;
344
+ broadcast({ type: 'watch-stopped', timestamp: Date.now() });
345
+ res.json({ success: true });
346
+ }
347
+ catch (error) {
348
+ fail(res, error);
349
+ }
350
+ });
351
+ // ===== Server bootstrap =====
352
+ const server = app.listen(port, () => {
353
+ console.log(`RagCode Dashboard API running at http://localhost:${port}`);
354
+ // Auto-index the default repo so the dashboard has data on first load.
355
+ ensureEngine().catch((error) => {
356
+ console.error('Initial indexing failed:', error instanceof Error ? error.message : error);
357
+ });
358
+ });
359
+ const wss = new WebSocketServer({ server, path: '/ws' });
360
+ wss.on('connection', (ws) => {
361
+ sockets.add(ws);
362
+ ws.send(JSON.stringify({ type: 'connected', timestamp: Date.now(), watchRunning: daemon !== null }));
363
+ if (lastDaemonStatus) {
364
+ ws.send(JSON.stringify({ type: 'watch-status', status: lastDaemonStatus, timestamp: Date.now() }));
365
+ }
366
+ ws.on('close', () => sockets.delete(ws));
367
+ ws.on('error', () => sockets.delete(ws));
368
+ });
369
+ async function shutdown() {
370
+ await daemon?.stop().catch(() => undefined);
371
+ engine?.close();
372
+ server.close(() => process.exit(0));
373
+ }
374
+ process.on('SIGTERM', () => void shutdown());
375
+ process.on('SIGINT', () => void shutdown());
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "ragcode-context-engine",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "Local code intelligence foundation: structural code graph, LanceDB semantic layer, retrieval, context packing, and MCP tools.",
7
+ "keywords": [
8
+ "code-intelligence",
9
+ "mcp",
10
+ "model-context-protocol",
11
+ "semantic-search",
12
+ "code-graph",
13
+ "lancedb",
14
+ "rag",
15
+ "ai-agents",
16
+ "claude",
17
+ "code-analysis"
18
+ ],
19
+ "author": "RagCode Team",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/MarshallEriksen-Neura/ragcode.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/MarshallEriksen-Neura/ragcode/issues"
27
+ },
28
+ "homepage": "https://github.com/MarshallEriksen-Neura/ragcode#readme",
29
+ "bin": {
30
+ "ragcode": "dist/src/cli/index.js"
31
+ },
32
+ "files": [
33
+ "dist/src",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.build.json",
39
+ "prepublishOnly": "npm run build",
40
+ "audit:plan": "tsx scripts/audit-plan.ts",
41
+ "check": "tsc -p tsconfig.json --noEmit",
42
+ "test": "vitest run",
43
+ "test:watcher": "node --import tsx ./node_modules/vitest/vitest.mjs run tests/watcher-state.test.ts tests/watch-daemon.test.ts",
44
+ "eval:context": "tsx scripts/eval-context.ts",
45
+ "dev": "tsx src/cli/index.ts",
46
+ "web:server": "tsx src/web/server.ts",
47
+ "web:client": "cd web && npm run dev",
48
+ "benchmark": "tsx scripts/benchmark.ts",
49
+ "benchmark:assert": "tsx scripts/benchmark.ts --assert",
50
+ "benchmark:list": "tsx scripts/benchmark.ts --list",
51
+ "benchmark:all": "tsx scripts/benchmark.ts --all",
52
+ "benchmark:observation": "tsx scripts/benchmark.ts --suite observation",
53
+ "benchmark:perf-smoke": "tsx scripts/benchmark-perf-smoke.ts",
54
+ "benchmark:incremental-stress": "tsx scripts/benchmark-incremental-stress.ts",
55
+ "setup-mcp": "tsx scripts/setup-mcp.ts",
56
+ "init-config": "tsx scripts/init-config.ts"
57
+ },
58
+ "dependencies": {
59
+ "@lancedb/lancedb": "^0.30.0",
60
+ "@modelcontextprotocol/sdk": "^1.29.0",
61
+ "@types/cors": "^2.8.19",
62
+ "@types/express": "^5.0.6",
63
+ "@types/ws": "^8.18.1",
64
+ "apache-arrow": "^18.1.0",
65
+ "better-sqlite3": "^12.10.0",
66
+ "chokidar": "^5.0.0",
67
+ "commander": "^14.0.2",
68
+ "cors": "^2.8.6",
69
+ "express": "^4.22.2",
70
+ "ink": "^7.0.5",
71
+ "parse-diff": "^0.12.0",
72
+ "react": "^19.2.7",
73
+ "smol-toml": "^1.6.1",
74
+ "snowball-stemmers": "^0.6.0",
75
+ "tree-sitter": "^0.21.1",
76
+ "tree-sitter-go": "^0.23.4",
77
+ "tree-sitter-java": "^0.23.5",
78
+ "tree-sitter-python": "^0.21.0",
79
+ "tree-sitter-rust": "^0.21.0",
80
+ "typescript": "^5.9.3",
81
+ "ws": "^8.21.0",
82
+ "zod": "^4.1.13"
83
+ },
84
+ "devDependencies": {
85
+ "@types/node": "^24.10.2",
86
+ "@types/react": "^19.2.17",
87
+ "ink-testing-library": "^4.0.0",
88
+ "tsx": "^4.21.0",
89
+ "vitest": "^4.1.8"
90
+ },
91
+ "engines": {
92
+ "node": ">=24.0.0"
93
+ }
94
+ }