@timmeck/brain 1.8.5 → 2.0.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 (221) hide show
  1. package/.dockerignore +11 -0
  2. package/Dockerfile +21 -0
  3. package/README.md +19 -0
  4. package/assets/brain-avatar-256.png +0 -0
  5. package/assets/brain-avatar-512.png +0 -0
  6. package/assets/brain-avatar.png +0 -0
  7. package/brain.log +1164 -0
  8. package/{src/cli/commands/dashboard.ts → dashboard.html} +688 -807
  9. package/dist/api/server.d.ts +4 -18
  10. package/dist/api/server.js +4 -173
  11. package/dist/api/server.js.map +1 -1
  12. package/dist/brain.d.ts +6 -0
  13. package/dist/brain.js +56 -4
  14. package/dist/brain.js.map +1 -1
  15. package/dist/cli/colors.d.ts +4 -25
  16. package/dist/cli/colors.js +3 -89
  17. package/dist/cli/colors.js.map +1 -1
  18. package/dist/cli/commands/peers.d.ts +2 -0
  19. package/dist/cli/commands/peers.js +38 -0
  20. package/dist/cli/commands/peers.js.map +1 -0
  21. package/dist/cli/commands/start.js +54 -17
  22. package/dist/cli/commands/start.js.map +1 -1
  23. package/dist/db/connection.d.ts +1 -2
  24. package/dist/db/connection.js +1 -18
  25. package/dist/db/connection.js.map +1 -1
  26. package/dist/index.js +3 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
  29. package/dist/ipc/__tests__/protocol.test.js +117 -0
  30. package/dist/ipc/__tests__/protocol.test.js.map +1 -0
  31. package/dist/ipc/client.d.ts +1 -16
  32. package/dist/ipc/client.js +1 -100
  33. package/dist/ipc/client.js.map +1 -1
  34. package/dist/ipc/protocol.d.ts +1 -8
  35. package/dist/ipc/protocol.js +1 -28
  36. package/dist/ipc/protocol.js.map +1 -1
  37. package/dist/ipc/router.js +8 -0
  38. package/dist/ipc/router.js.map +1 -1
  39. package/dist/ipc/server.d.ts +1 -22
  40. package/dist/ipc/server.js +1 -163
  41. package/dist/ipc/server.js.map +1 -1
  42. package/dist/mcp/http-server.d.ts +1 -7
  43. package/dist/mcp/http-server.js +6 -117
  44. package/dist/mcp/http-server.js.map +1 -1
  45. package/dist/mcp/server.js +5 -61
  46. package/dist/mcp/server.js.map +1 -1
  47. package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
  48. package/dist/signals/__tests__/fingerprint.test.js +118 -0
  49. package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
  50. package/dist/types/ipc.types.d.ts +1 -11
  51. package/dist/utils/__tests__/hash.test.d.ts +1 -0
  52. package/dist/utils/__tests__/hash.test.js +32 -0
  53. package/dist/utils/__tests__/hash.test.js.map +1 -0
  54. package/dist/utils/__tests__/paths.test.d.ts +1 -0
  55. package/dist/utils/__tests__/paths.test.js +75 -0
  56. package/dist/utils/__tests__/paths.test.js.map +1 -0
  57. package/dist/utils/events.d.ts +4 -8
  58. package/dist/utils/events.js +2 -14
  59. package/dist/utils/events.js.map +1 -1
  60. package/dist/utils/hash.d.ts +1 -1
  61. package/dist/utils/hash.js +1 -4
  62. package/dist/utils/hash.js.map +1 -1
  63. package/dist/utils/logger.d.ts +3 -2
  64. package/dist/utils/logger.js +8 -35
  65. package/dist/utils/logger.js.map +1 -1
  66. package/dist/utils/paths.d.ts +2 -1
  67. package/dist/utils/paths.js +4 -13
  68. package/dist/utils/paths.js.map +1 -1
  69. package/gen_avatar.py +142 -0
  70. package/package.json +2 -1
  71. package/BRAIN_PLAN.md +0 -3324
  72. package/src/api/server.ts +0 -395
  73. package/src/brain.ts +0 -266
  74. package/src/cli/colors.ts +0 -116
  75. package/src/cli/commands/config.ts +0 -169
  76. package/src/cli/commands/doctor.ts +0 -124
  77. package/src/cli/commands/explain.ts +0 -83
  78. package/src/cli/commands/export.ts +0 -31
  79. package/src/cli/commands/import.ts +0 -199
  80. package/src/cli/commands/insights.ts +0 -65
  81. package/src/cli/commands/learn.ts +0 -24
  82. package/src/cli/commands/modules.ts +0 -53
  83. package/src/cli/commands/network.ts +0 -67
  84. package/src/cli/commands/projects.ts +0 -42
  85. package/src/cli/commands/query.ts +0 -120
  86. package/src/cli/commands/start.ts +0 -62
  87. package/src/cli/commands/status.ts +0 -75
  88. package/src/cli/commands/stop.ts +0 -34
  89. package/src/cli/ipc-helper.ts +0 -22
  90. package/src/cli/update-check.ts +0 -63
  91. package/src/code/analyzer.ts +0 -117
  92. package/src/code/fingerprint.ts +0 -87
  93. package/src/code/matcher.ts +0 -129
  94. package/src/code/parsers/generic.ts +0 -29
  95. package/src/code/parsers/python.ts +0 -54
  96. package/src/code/parsers/typescript.ts +0 -65
  97. package/src/code/registry.ts +0 -60
  98. package/src/code/scorer.ts +0 -120
  99. package/src/config.ts +0 -135
  100. package/src/dashboard/server.ts +0 -142
  101. package/src/db/connection.ts +0 -22
  102. package/src/db/migrations/001_core_schema.ts +0 -120
  103. package/src/db/migrations/002_learning_schema.ts +0 -38
  104. package/src/db/migrations/003_code_schema.ts +0 -53
  105. package/src/db/migrations/004_synapses_schema.ts +0 -57
  106. package/src/db/migrations/005_fts_indexes.ts +0 -78
  107. package/src/db/migrations/006_synapses_phase3.ts +0 -17
  108. package/src/db/migrations/007_feedback.ts +0 -13
  109. package/src/db/migrations/008_git_integration.ts +0 -38
  110. package/src/db/migrations/009_embeddings.ts +0 -8
  111. package/src/db/migrations/index.ts +0 -70
  112. package/src/db/repositories/antipattern.repository.ts +0 -66
  113. package/src/db/repositories/code-module.repository.ts +0 -142
  114. package/src/db/repositories/error.repository.ts +0 -189
  115. package/src/db/repositories/insight.repository.ts +0 -99
  116. package/src/db/repositories/notification.repository.ts +0 -66
  117. package/src/db/repositories/project.repository.ts +0 -93
  118. package/src/db/repositories/rule.repository.ts +0 -108
  119. package/src/db/repositories/solution.repository.ts +0 -154
  120. package/src/db/repositories/synapse.repository.ts +0 -163
  121. package/src/db/repositories/terminal.repository.ts +0 -101
  122. package/src/embeddings/engine.ts +0 -238
  123. package/src/hooks/post-tool-use.ts +0 -92
  124. package/src/hooks/post-write.ts +0 -129
  125. package/src/index.ts +0 -63
  126. package/src/ipc/client.ts +0 -118
  127. package/src/ipc/protocol.ts +0 -35
  128. package/src/ipc/router.ts +0 -133
  129. package/src/ipc/server.ts +0 -176
  130. package/src/learning/confidence-scorer.ts +0 -80
  131. package/src/learning/decay.ts +0 -46
  132. package/src/learning/learning-engine.ts +0 -170
  133. package/src/learning/pattern-extractor.ts +0 -90
  134. package/src/learning/rule-generator.ts +0 -74
  135. package/src/main.rs:10:5 +0 -0
  136. package/src/matching/error-matcher.ts +0 -166
  137. package/src/matching/fingerprint.ts +0 -34
  138. package/src/matching/similarity.ts +0 -61
  139. package/src/matching/tfidf.ts +0 -74
  140. package/src/matching/tokenizer.ts +0 -41
  141. package/src/mcp/auto-detect.ts +0 -93
  142. package/src/mcp/http-server.ts +0 -140
  143. package/src/mcp/server.ts +0 -73
  144. package/src/mcp/tools.ts +0 -328
  145. package/src/parsing/error-parser.ts +0 -28
  146. package/src/parsing/parsers/compiler.ts +0 -93
  147. package/src/parsing/parsers/generic.ts +0 -28
  148. package/src/parsing/parsers/go.ts +0 -97
  149. package/src/parsing/parsers/node.ts +0 -69
  150. package/src/parsing/parsers/python.ts +0 -62
  151. package/src/parsing/parsers/rust.ts +0 -50
  152. package/src/parsing/parsers/shell.ts +0 -42
  153. package/src/parsing/types.ts +0 -47
  154. package/src/research/gap-analyzer.ts +0 -135
  155. package/src/research/insight-generator.ts +0 -123
  156. package/src/research/research-engine.ts +0 -116
  157. package/src/research/synergy-detector.ts +0 -126
  158. package/src/research/template-extractor.ts +0 -130
  159. package/src/research/trend-analyzer.ts +0 -127
  160. package/src/services/analytics.service.ts +0 -226
  161. package/src/services/code.service.ts +0 -271
  162. package/src/services/error.service.ts +0 -266
  163. package/src/services/git.service.ts +0 -132
  164. package/src/services/notification.service.ts +0 -41
  165. package/src/services/prevention.service.ts +0 -159
  166. package/src/services/research.service.ts +0 -98
  167. package/src/services/solution.service.ts +0 -174
  168. package/src/services/synapse.service.ts +0 -59
  169. package/src/services/terminal.service.ts +0 -81
  170. package/src/synapses/activation.ts +0 -80
  171. package/src/synapses/decay.ts +0 -38
  172. package/src/synapses/hebbian.ts +0 -69
  173. package/src/synapses/pathfinder.ts +0 -81
  174. package/src/synapses/synapse-manager.ts +0 -113
  175. package/src/types/code.types.ts +0 -52
  176. package/src/types/config.types.ts +0 -103
  177. package/src/types/error.types.ts +0 -67
  178. package/src/types/ipc.types.ts +0 -8
  179. package/src/types/mcp.types.ts +0 -53
  180. package/src/types/research.types.ts +0 -28
  181. package/src/types/solution.types.ts +0 -30
  182. package/src/types/synapse.types.ts +0 -50
  183. package/src/utils/events.ts +0 -45
  184. package/src/utils/hash.ts +0 -5
  185. package/src/utils/logger.ts +0 -48
  186. package/src/utils/paths.ts +0 -19
  187. package/tests/e2e/test_code_intelligence.py +0 -1015
  188. package/tests/e2e/test_error_memory.py +0 -451
  189. package/tests/e2e/test_full_integration.py +0 -534
  190. package/tests/fixtures/code-modules/modules.ts +0 -83
  191. package/tests/fixtures/errors/go.ts +0 -9
  192. package/tests/fixtures/errors/node.ts +0 -24
  193. package/tests/fixtures/errors/python.ts +0 -21
  194. package/tests/fixtures/errors/rust.ts +0 -25
  195. package/tests/fixtures/errors/shell.ts +0 -15
  196. package/tests/fixtures/solutions/solutions.ts +0 -27
  197. package/tests/helpers/setup-db.ts +0 -52
  198. package/tests/integration/code-flow.test.ts +0 -86
  199. package/tests/integration/error-flow.test.ts +0 -83
  200. package/tests/integration/ipc-flow.test.ts +0 -166
  201. package/tests/integration/learning-cycle.test.ts +0 -82
  202. package/tests/integration/synapse-flow.test.ts +0 -117
  203. package/tests/unit/code/analyzer.test.ts +0 -58
  204. package/tests/unit/code/fingerprint.test.ts +0 -51
  205. package/tests/unit/code/scorer.test.ts +0 -55
  206. package/tests/unit/learning/confidence-scorer.test.ts +0 -60
  207. package/tests/unit/learning/decay.test.ts +0 -45
  208. package/tests/unit/learning/pattern-extractor.test.ts +0 -50
  209. package/tests/unit/matching/error-matcher.test.ts +0 -69
  210. package/tests/unit/matching/fingerprint.test.ts +0 -47
  211. package/tests/unit/matching/similarity.test.ts +0 -65
  212. package/tests/unit/matching/tfidf.test.ts +0 -71
  213. package/tests/unit/matching/tokenizer.test.ts +0 -83
  214. package/tests/unit/parsing/parsers.test.ts +0 -113
  215. package/tests/unit/research/gap-analyzer.test.ts +0 -45
  216. package/tests/unit/research/trend-analyzer.test.ts +0 -45
  217. package/tests/unit/synapses/activation.test.ts +0 -80
  218. package/tests/unit/synapses/decay.test.ts +0 -27
  219. package/tests/unit/synapses/hebbian.test.ts +0 -96
  220. package/tests/unit/synapses/pathfinder.test.ts +0 -72
  221. package/tsconfig.json +0 -18
package/src/api/server.ts DELETED
@@ -1,395 +0,0 @@
1
- import http from 'node:http';
2
- import { getLogger } from '../utils/logger.js';
3
- import { getEventBus } from '../utils/events.js';
4
- import type { IpcRouter } from '../ipc/router.js';
5
-
6
- export interface ApiServerOptions {
7
- port: number;
8
- router: IpcRouter;
9
- apiKey?: string;
10
- }
11
-
12
- interface RouteDefinition {
13
- method: string;
14
- pattern: RegExp;
15
- ipcMethod: string;
16
- extractParams: (match: RegExpMatchArray, query: URLSearchParams, body?: unknown) => unknown;
17
- }
18
-
19
- export class ApiServer {
20
- private server: http.Server | null = null;
21
- private logger = getLogger();
22
- private routes: RouteDefinition[];
23
- private sseClients: Set<http.ServerResponse> = new Set();
24
- private statsTimer: ReturnType<typeof setInterval> | null = null;
25
-
26
- constructor(private options: ApiServerOptions) {
27
- this.routes = this.buildRoutes();
28
- }
29
-
30
- start(): void {
31
- const { port, apiKey } = this.options;
32
-
33
- this.server = http.createServer((req, res) => {
34
- res.setHeader('Access-Control-Allow-Origin', '*');
35
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
36
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-API-Key');
37
-
38
- if (req.method === 'OPTIONS') {
39
- res.writeHead(204);
40
- res.end();
41
- return;
42
- }
43
-
44
- if (apiKey) {
45
- const provided = (req.headers['x-api-key'] as string) ??
46
- req.headers.authorization?.replace('Bearer ', '');
47
- if (provided !== apiKey) {
48
- this.json(res, 401, { error: 'Unauthorized', message: 'Invalid or missing API key' });
49
- return;
50
- }
51
- }
52
-
53
- this.handleRequest(req, res).catch((err) => {
54
- this.logger.error('API error:', err);
55
- this.json(res, 500, {
56
- error: 'Internal Server Error',
57
- message: err instanceof Error ? err.message : String(err),
58
- });
59
- });
60
- });
61
-
62
- this.server.listen(port, () => {
63
- this.logger.info(`REST API server started on http://localhost:${port}`);
64
- });
65
-
66
- this.setupSSE();
67
- }
68
-
69
- stop(): void {
70
- if (this.statsTimer) {
71
- clearInterval(this.statsTimer);
72
- this.statsTimer = null;
73
- }
74
- for (const client of this.sseClients) {
75
- try { client.end(); } catch { /* ignore */ }
76
- }
77
- this.sseClients.clear();
78
- this.server?.close();
79
- this.server = null;
80
- this.logger.info('REST API server stopped');
81
- }
82
-
83
- private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
84
- const url = new URL(req.url ?? '/', 'http://localhost');
85
- const pathname = url.pathname;
86
- const method = req.method ?? 'GET';
87
- const query = url.searchParams;
88
-
89
- // Health check
90
- if (pathname === '/api/v1/health') {
91
- this.json(res, 200, { status: 'ok', timestamp: new Date().toISOString() });
92
- return;
93
- }
94
-
95
- // SSE event stream (for live dashboard)
96
- if (pathname === '/api/v1/events' && method === 'GET') {
97
- res.writeHead(200, {
98
- 'Content-Type': 'text/event-stream',
99
- 'Cache-Control': 'no-cache',
100
- 'Connection': 'keep-alive',
101
- });
102
- res.write('data: {"type":"connected"}\n\n');
103
- this.sseClients.add(res);
104
- req.on('close', () => this.sseClients.delete(res));
105
- return;
106
- }
107
-
108
- // List all available methods
109
- if (pathname === '/api/v1/methods' && method === 'GET') {
110
- const methods = this.options.router.listMethods();
111
- this.json(res, 200, {
112
- methods,
113
- rpcEndpoint: '/api/v1/rpc',
114
- usage: 'POST /api/v1/rpc with body { "method": "<method>", "params": {...} }',
115
- });
116
- return;
117
- }
118
-
119
- // Generic RPC endpoint — the universal gateway
120
- if (pathname === '/api/v1/rpc' && method === 'POST') {
121
- const body = await this.readBody(req);
122
- if (!body) {
123
- this.json(res, 400, { error: 'Bad Request', message: 'Empty request body' });
124
- return;
125
- }
126
-
127
- const parsed = JSON.parse(body);
128
-
129
- // Batch RPC support
130
- if (Array.isArray(parsed)) {
131
- const results = parsed.map((call: { method: string; params?: unknown; id?: string | number }) => {
132
- try {
133
- const result = this.options.router.handle(call.method, call.params ?? {});
134
- return { id: call.id, result };
135
- } catch (err) {
136
- return { id: call.id, error: err instanceof Error ? err.message : String(err) };
137
- }
138
- });
139
- this.json(res, 200, results);
140
- return;
141
- }
142
-
143
- if (!parsed.method) {
144
- this.json(res, 400, { error: 'Bad Request', message: 'Missing "method" field' });
145
- return;
146
- }
147
-
148
- try {
149
- const result = this.options.router.handle(parsed.method, parsed.params ?? {});
150
- this.json(res, 200, { result });
151
- } catch (err) {
152
- this.json(res, 400, { error: err instanceof Error ? err.message : String(err) });
153
- }
154
- return;
155
- }
156
-
157
- // RESTful routes
158
- let body: unknown = undefined;
159
- if (method === 'POST' || method === 'PUT') {
160
- try {
161
- const raw = await this.readBody(req);
162
- body = raw ? JSON.parse(raw) : {};
163
- } catch {
164
- this.json(res, 400, { error: 'Bad Request', message: 'Invalid JSON body' });
165
- return;
166
- }
167
- }
168
-
169
- for (const route of this.routes) {
170
- if (route.method !== method) continue;
171
- const match = pathname.match(route.pattern);
172
- if (!match) continue;
173
-
174
- try {
175
- const params = route.extractParams(match, query, body);
176
- const result = this.options.router.handle(route.ipcMethod, params);
177
- this.json(res, method === 'POST' ? 201 : 200, { result });
178
- } catch (err) {
179
- const msg = err instanceof Error ? err.message : String(err);
180
- const status = msg.startsWith('Unknown method') ? 404 : 400;
181
- this.json(res, status, { error: msg });
182
- }
183
- return;
184
- }
185
-
186
- this.json(res, 404, { error: 'Not Found', message: `No route for ${method} ${pathname}` });
187
- }
188
-
189
- private buildRoutes(): RouteDefinition[] {
190
- return [
191
- // ─── Errors ────────────────────────────────────────────
192
- { method: 'POST', pattern: /^\/api\/v1\/errors$/, ipcMethod: 'error.report',
193
- extractParams: (_m, _q, body) => body },
194
- { method: 'GET', pattern: /^\/api\/v1\/errors$/, ipcMethod: 'error.query',
195
- extractParams: (_m, q) => ({
196
- search: q.get('search') ?? '',
197
- projectId: q.get('projectId') ? Number(q.get('projectId')) : undefined,
198
- }) },
199
- { method: 'GET', pattern: /^\/api\/v1\/errors\/(\d+)$/, ipcMethod: 'error.get',
200
- extractParams: (m) => ({ id: Number(m[1]) }) },
201
- { method: 'GET', pattern: /^\/api\/v1\/errors\/(\d+)\/match$/, ipcMethod: 'error.match',
202
- extractParams: (m) => ({ errorId: Number(m[1]) }) },
203
- { method: 'GET', pattern: /^\/api\/v1\/errors\/(\d+)\/chain$/, ipcMethod: 'error.chain',
204
- extractParams: (m) => ({ errorId: Number(m[1]) }) },
205
- { method: 'POST', pattern: /^\/api\/v1\/errors\/(\d+)\/resolve$/, ipcMethod: 'error.resolve',
206
- extractParams: (m, _q, body) => ({ errorId: Number(m[1]), ...(body as object) }) },
207
-
208
- // ─── Solutions ─────────────────────────────────────────
209
- { method: 'POST', pattern: /^\/api\/v1\/solutions$/, ipcMethod: 'solution.report',
210
- extractParams: (_m, _q, body) => body },
211
- { method: 'GET', pattern: /^\/api\/v1\/solutions$/, ipcMethod: 'solution.query',
212
- extractParams: (_m, q) => ({
213
- errorId: q.get('errorId') ? Number(q.get('errorId')) : undefined,
214
- }) },
215
- { method: 'POST', pattern: /^\/api\/v1\/solutions\/rate$/, ipcMethod: 'solution.rate',
216
- extractParams: (_m, _q, body) => body },
217
- { method: 'GET', pattern: /^\/api\/v1\/solutions\/efficiency$/, ipcMethod: 'solution.efficiency',
218
- extractParams: () => ({}) },
219
-
220
- // ─── Projects ──────────────────────────────────────────
221
- { method: 'GET', pattern: /^\/api\/v1\/projects$/, ipcMethod: 'project.list',
222
- extractParams: () => ({}) },
223
-
224
- // ─── Code ──────────────────────────────────────────────
225
- { method: 'POST', pattern: /^\/api\/v1\/code\/analyze$/, ipcMethod: 'code.analyze',
226
- extractParams: (_m, _q, body) => body },
227
- { method: 'POST', pattern: /^\/api\/v1\/code\/find$/, ipcMethod: 'code.find',
228
- extractParams: (_m, _q, body) => body },
229
- { method: 'POST', pattern: /^\/api\/v1\/code\/similarity$/, ipcMethod: 'code.similarity',
230
- extractParams: (_m, _q, body) => body },
231
- { method: 'GET', pattern: /^\/api\/v1\/code\/modules$/, ipcMethod: 'code.modules',
232
- extractParams: (_m, q) => ({
233
- projectId: q.get('projectId') ? Number(q.get('projectId')) : undefined,
234
- language: q.get('language') ?? undefined,
235
- limit: q.get('limit') ? Number(q.get('limit')) : undefined,
236
- }) },
237
- { method: 'GET', pattern: /^\/api\/v1\/code\/(\d+)$/, ipcMethod: 'code.get',
238
- extractParams: (m) => ({ id: Number(m[1]) }) },
239
-
240
- // ─── Prevention ────────────────────────────────────────
241
- { method: 'POST', pattern: /^\/api\/v1\/prevention\/check$/, ipcMethod: 'prevention.check',
242
- extractParams: (_m, _q, body) => body },
243
- { method: 'POST', pattern: /^\/api\/v1\/prevention\/antipatterns$/, ipcMethod: 'prevention.antipatterns',
244
- extractParams: (_m, _q, body) => body },
245
- { method: 'POST', pattern: /^\/api\/v1\/prevention\/code$/, ipcMethod: 'prevention.checkCode',
246
- extractParams: (_m, _q, body) => body },
247
-
248
- // ─── Synapses ─────────────────────────────────────────
249
- { method: 'GET', pattern: /^\/api\/v1\/synapses\/context\/(\d+)$/, ipcMethod: 'synapse.context',
250
- extractParams: (m) => ({ errorId: Number(m[1]) }) },
251
- { method: 'POST', pattern: /^\/api\/v1\/synapses\/path$/, ipcMethod: 'synapse.path',
252
- extractParams: (_m, _q, body) => body },
253
- { method: 'POST', pattern: /^\/api\/v1\/synapses\/related$/, ipcMethod: 'synapse.related',
254
- extractParams: (_m, _q, body) => body },
255
- { method: 'GET', pattern: /^\/api\/v1\/synapses\/stats$/, ipcMethod: 'synapse.stats',
256
- extractParams: () => ({}) },
257
-
258
- // ─── Research ──────────────────────────────────────────
259
- { method: 'GET', pattern: /^\/api\/v1\/research\/insights$/, ipcMethod: 'research.insights',
260
- extractParams: (_m, q) => ({
261
- type: q.get('type') ?? undefined,
262
- limit: q.get('limit') ? Number(q.get('limit')) : 20,
263
- activeOnly: q.get('activeOnly') !== 'false',
264
- }) },
265
- { method: 'POST', pattern: /^\/api\/v1\/research\/insights\/(\d+)\/rate$/, ipcMethod: 'insight.rate',
266
- extractParams: (m, _q, body) => ({ id: Number(m[1]), ...(body as object) }) },
267
- { method: 'GET', pattern: /^\/api\/v1\/research\/suggest$/, ipcMethod: 'research.suggest',
268
- extractParams: (_m, q) => ({
269
- context: q.get('context') ?? '',
270
- limit: 10,
271
- activeOnly: true,
272
- }) },
273
- { method: 'GET', pattern: /^\/api\/v1\/research\/trends$/, ipcMethod: 'research.trends',
274
- extractParams: (_m, q) => ({
275
- projectId: q.get('projectId') ? Number(q.get('projectId')) : undefined,
276
- windowDays: q.get('windowDays') ? Number(q.get('windowDays')) : undefined,
277
- }) },
278
-
279
- // ─── Notifications ────────────────────────────────────
280
- { method: 'GET', pattern: /^\/api\/v1\/notifications$/, ipcMethod: 'notification.list',
281
- extractParams: (_m, q) => ({
282
- projectId: q.get('projectId') ? Number(q.get('projectId')) : undefined,
283
- }) },
284
- { method: 'POST', pattern: /^\/api\/v1\/notifications\/(\d+)\/ack$/, ipcMethod: 'notification.ack',
285
- extractParams: (m) => ({ id: Number(m[1]) }) },
286
-
287
- // ─── Analytics ─────────────────────────────────────────
288
- { method: 'GET', pattern: /^\/api\/v1\/analytics\/summary$/, ipcMethod: 'analytics.summary',
289
- extractParams: (_m, q) => ({
290
- projectId: q.get('projectId') ? Number(q.get('projectId')) : undefined,
291
- }) },
292
- { method: 'GET', pattern: /^\/api\/v1\/analytics\/network$/, ipcMethod: 'analytics.network',
293
- extractParams: (_m, q) => ({
294
- limit: q.get('limit') ? Number(q.get('limit')) : undefined,
295
- }) },
296
- { method: 'GET', pattern: /^\/api\/v1\/analytics\/health$/, ipcMethod: 'analytics.health',
297
- extractParams: (_m, q) => ({
298
- projectId: q.get('projectId') ? Number(q.get('projectId')) : undefined,
299
- }) },
300
- { method: 'GET', pattern: /^\/api\/v1\/analytics\/timeline$/, ipcMethod: 'analytics.timeline',
301
- extractParams: (_m, q) => ({
302
- projectId: q.get('projectId') ? Number(q.get('projectId')) : undefined,
303
- days: q.get('days') ? Number(q.get('days')) : undefined,
304
- }) },
305
- { method: 'GET', pattern: /^\/api\/v1\/analytics\/explain\/(\d+)$/, ipcMethod: 'analytics.explain',
306
- extractParams: (m) => ({ errorId: Number(m[1]) }) },
307
-
308
- // ─── Git ───────────────────────────────────────────────
309
- { method: 'GET', pattern: /^\/api\/v1\/git\/context$/, ipcMethod: 'git.context',
310
- extractParams: (_m, q) => ({ cwd: q.get('cwd') ?? undefined }) },
311
- { method: 'POST', pattern: /^\/api\/v1\/git\/link-error$/, ipcMethod: 'git.linkError',
312
- extractParams: (_m, _q, body) => body },
313
- { method: 'GET', pattern: /^\/api\/v1\/git\/errors\/(\d+)\/commits$/, ipcMethod: 'git.errorCommits',
314
- extractParams: (m) => ({ errorId: Number(m[1]) }) },
315
- { method: 'GET', pattern: /^\/api\/v1\/git\/commits\/([a-f0-9]+)\/errors$/, ipcMethod: 'git.commitErrors',
316
- extractParams: (m) => ({ commitHash: m[1] }) },
317
- { method: 'GET', pattern: /^\/api\/v1\/git\/diff$/, ipcMethod: 'git.diff',
318
- extractParams: (_m, q) => ({ cwd: q.get('cwd') ?? undefined }) },
319
-
320
- // ─── Terminal ──────────────────────────────────────────
321
- { method: 'POST', pattern: /^\/api\/v1\/terminal\/register$/, ipcMethod: 'terminal.register',
322
- extractParams: (_m, _q, body) => body },
323
- { method: 'POST', pattern: /^\/api\/v1\/terminal\/heartbeat$/, ipcMethod: 'terminal.heartbeat',
324
- extractParams: (_m, _q, body) => body },
325
- { method: 'POST', pattern: /^\/api\/v1\/terminal\/disconnect$/, ipcMethod: 'terminal.disconnect',
326
- extractParams: (_m, _q, body) => body },
327
-
328
- // ─── Learning ──────────────────────────────────────────
329
- { method: 'POST', pattern: /^\/api\/v1\/learning\/run$/, ipcMethod: 'learning.run',
330
- extractParams: () => ({}) },
331
- ];
332
- }
333
-
334
- private json(res: http.ServerResponse, status: number, data: unknown): void {
335
- res.writeHead(status, { 'Content-Type': 'application/json' });
336
- res.end(JSON.stringify(data));
337
- }
338
-
339
- private readBody(req: http.IncomingMessage): Promise<string> {
340
- return new Promise((resolve, reject) => {
341
- const chunks: Buffer[] = [];
342
- req.on('data', (chunk: Buffer) => chunks.push(chunk));
343
- req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
344
- req.on('error', reject);
345
- });
346
- }
347
-
348
- private setupSSE(): void {
349
- const bus = getEventBus();
350
- const eventNames = [
351
- 'error:reported', 'error:resolved', 'solution:applied',
352
- 'solution:created', 'module:registered', 'module:updated',
353
- 'synapse:created', 'synapse:strengthened',
354
- 'insight:created', 'rule:learned',
355
- ] as const;
356
-
357
- for (const eventName of eventNames) {
358
- bus.on(eventName, (data: unknown) => {
359
- this.broadcastSSE({ type: 'event', event: eventName, data });
360
- });
361
- }
362
-
363
- // Periodic stats broadcast every 30s
364
- this.statsTimer = setInterval(() => {
365
- if (this.sseClients.size > 0) {
366
- try {
367
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
368
- const summary = this.options.router.handle('analytics.summary', {}) as any;
369
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
370
- const network = this.options.router.handle('synapse.stats', {}) as any;
371
- const stats = {
372
- modules: summary?.modules?.total ?? 0,
373
- synapses: network?.totalSynapses ?? 0,
374
- errors: summary?.errors?.total ?? 0,
375
- solutions: summary?.solutions?.total ?? 0,
376
- rules: summary?.rules?.active ?? 0,
377
- insights: summary?.insights?.total ?? 0,
378
- };
379
- this.broadcastSSE({ type: 'stats_update', stats });
380
- } catch { /* ignore stats errors */ }
381
- }
382
- }, 30_000);
383
- }
384
-
385
- private broadcastSSE(data: unknown): void {
386
- const msg = `data: ${JSON.stringify(data)}\n\n`;
387
- for (const client of this.sseClients) {
388
- try {
389
- client.write(msg);
390
- } catch {
391
- this.sseClients.delete(client);
392
- }
393
- }
394
- }
395
- }
package/src/brain.ts DELETED
@@ -1,266 +0,0 @@
1
- import path from 'node:path';
2
- import fs from 'node:fs';
3
- import type Database from 'better-sqlite3';
4
- import { loadConfig } from './config.js';
5
- import type { BrainConfig } from './types/config.types.js';
6
- import { createLogger, getLogger } from './utils/logger.js';
7
- import { getEventBus } from './utils/events.js';
8
- import { createConnection } from './db/connection.js';
9
- import { runMigrations } from './db/migrations/index.js';
10
-
11
- // Repositories
12
- import { ProjectRepository } from './db/repositories/project.repository.js';
13
- import { ErrorRepository } from './db/repositories/error.repository.js';
14
- import { SolutionRepository } from './db/repositories/solution.repository.js';
15
- import { RuleRepository } from './db/repositories/rule.repository.js';
16
- import { AntipatternRepository } from './db/repositories/antipattern.repository.js';
17
- import { TerminalRepository } from './db/repositories/terminal.repository.js';
18
- import { CodeModuleRepository } from './db/repositories/code-module.repository.js';
19
- import { SynapseRepository } from './db/repositories/synapse.repository.js';
20
- import { NotificationRepository } from './db/repositories/notification.repository.js';
21
- import { InsightRepository } from './db/repositories/insight.repository.js';
22
-
23
- // Services
24
- import { ErrorService } from './services/error.service.js';
25
- import { SolutionService } from './services/solution.service.js';
26
- import { TerminalService } from './services/terminal.service.js';
27
- import { PreventionService } from './services/prevention.service.js';
28
- import { CodeService } from './services/code.service.js';
29
- import { SynapseService } from './services/synapse.service.js';
30
- import { ResearchService } from './services/research.service.js';
31
- import { NotificationService } from './services/notification.service.js';
32
- import { AnalyticsService } from './services/analytics.service.js';
33
- import { GitService } from './services/git.service.js';
34
-
35
- // Synapses
36
- import { SynapseManager } from './synapses/synapse-manager.js';
37
-
38
- // Engines
39
- import { LearningEngine } from './learning/learning-engine.js';
40
- import { ResearchEngine } from './research/research-engine.js';
41
-
42
- // IPC
43
- import { IpcRouter, type Services } from './ipc/router.js';
44
- import { IpcServer } from './ipc/server.js';
45
-
46
- // API & MCP HTTP
47
- import { ApiServer } from './api/server.js';
48
- import { McpHttpServer } from './mcp/http-server.js';
49
-
50
- // Embeddings
51
- import { EmbeddingEngine } from './embeddings/engine.js';
52
-
53
- export class BrainCore {
54
- private db: Database.Database | null = null;
55
- private ipcServer: IpcServer | null = null;
56
- private apiServer: ApiServer | null = null;
57
- private mcpHttpServer: McpHttpServer | null = null;
58
- private embeddingEngine: EmbeddingEngine | null = null;
59
- private learningEngine: LearningEngine | null = null;
60
- private researchEngine: ResearchEngine | null = null;
61
- private cleanupTimer: ReturnType<typeof setInterval> | null = null;
62
- private config: BrainConfig | null = null;
63
-
64
- start(configPath?: string): void {
65
- // 1. Config
66
- this.config = loadConfig(configPath);
67
- const config = this.config;
68
-
69
- // 2. Ensure data dir
70
- fs.mkdirSync(path.dirname(config.dbPath), { recursive: true });
71
-
72
- // 3. Logger
73
- createLogger({
74
- level: config.log.level,
75
- file: config.log.file,
76
- maxSize: config.log.maxSize,
77
- maxFiles: config.log.maxFiles,
78
- });
79
- const logger = getLogger();
80
-
81
- // 4. Database
82
- this.db = createConnection(config.dbPath);
83
- runMigrations(this.db);
84
- logger.info(`Database initialized: ${config.dbPath}`);
85
-
86
- // 5. Repositories
87
- const projectRepo = new ProjectRepository(this.db);
88
- const errorRepo = new ErrorRepository(this.db);
89
- const solutionRepo = new SolutionRepository(this.db);
90
- const ruleRepo = new RuleRepository(this.db);
91
- const antipatternRepo = new AntipatternRepository(this.db);
92
- const terminalRepo = new TerminalRepository(this.db);
93
- const codeModuleRepo = new CodeModuleRepository(this.db);
94
- const synapseRepo = new SynapseRepository(this.db);
95
- const notificationRepo = new NotificationRepository(this.db);
96
- const insightRepo = new InsightRepository(this.db);
97
-
98
- // 6. Synapse Manager
99
- const synapseManager = new SynapseManager(synapseRepo, config.synapses);
100
-
101
- // 7. Services
102
- const services: Services = {
103
- error: new ErrorService(errorRepo, projectRepo, synapseManager, config.matching),
104
- solution: new SolutionService(solutionRepo, synapseManager),
105
- terminal: new TerminalService(terminalRepo, config.terminal.staleTimeout),
106
- prevention: new PreventionService(ruleRepo, antipatternRepo, synapseManager),
107
- code: new CodeService(codeModuleRepo, projectRepo, synapseManager),
108
- synapse: new SynapseService(synapseManager),
109
- research: new ResearchService(insightRepo, errorRepo, synapseManager),
110
- notification: new NotificationService(notificationRepo),
111
- analytics: new AnalyticsService(
112
- errorRepo, solutionRepo, codeModuleRepo,
113
- ruleRepo, antipatternRepo, insightRepo,
114
- synapseManager,
115
- ),
116
- git: new GitService(this.db!, synapseManager),
117
- };
118
-
119
- // 8. Embedding Engine (local vector search)
120
- if (config.embeddings.enabled) {
121
- this.embeddingEngine = new EmbeddingEngine(config.embeddings, this.db!);
122
- this.embeddingEngine.start();
123
- // Wire embedding engine into services for hybrid search
124
- services.error.setEmbeddingEngine(this.embeddingEngine);
125
- services.code.setEmbeddingEngine(this.embeddingEngine);
126
- logger.info('Embedding engine started (model will load in background)');
127
- }
128
-
129
- // 9. Learning Engine
130
- this.learningEngine = new LearningEngine(
131
- config.learning, errorRepo, solutionRepo,
132
- ruleRepo, antipatternRepo, synapseManager,
133
- );
134
- this.learningEngine.start();
135
- logger.info(`Learning engine started (interval: ${config.learning.intervalMs}ms)`);
136
-
137
- // 10. Research Engine
138
- this.researchEngine = new ResearchEngine(
139
- config.research, errorRepo, solutionRepo, projectRepo,
140
- codeModuleRepo, synapseRepo, insightRepo, synapseManager,
141
- );
142
- this.researchEngine.start();
143
- logger.info(`Research engine started (interval: ${config.research.intervalMs}ms)`);
144
-
145
- // Expose learning engine to IPC
146
- services.learning = this.learningEngine;
147
-
148
- // 11. IPC Server
149
- const router = new IpcRouter(services);
150
- this.ipcServer = new IpcServer(router, config.ipc.pipeName);
151
- this.ipcServer.start();
152
-
153
- // 11a. REST API Server
154
- if (config.api.enabled) {
155
- this.apiServer = new ApiServer({
156
- port: config.api.port,
157
- router,
158
- apiKey: config.api.apiKey,
159
- });
160
- this.apiServer.start();
161
- logger.info(`REST API enabled on port ${config.api.port}`);
162
- }
163
-
164
- // 11b. MCP HTTP Server (SSE transport for Cursor, Windsurf, Cline, Continue)
165
- if (config.mcpHttp.enabled) {
166
- this.mcpHttpServer = new McpHttpServer(config.mcpHttp.port, router);
167
- this.mcpHttpServer.start();
168
- logger.info(`MCP HTTP (SSE) enabled on port ${config.mcpHttp.port}`);
169
- }
170
-
171
- // 12. Terminal cleanup timer
172
- this.cleanupTimer = setInterval(() => {
173
- services.terminal.cleanup();
174
- }, 60_000);
175
-
176
- // 13. Event listeners (synapse wiring)
177
- this.setupEventListeners(services, synapseManager);
178
-
179
- // 14. PID file
180
- const pidPath = path.join(path.dirname(config.dbPath), 'brain.pid');
181
- fs.writeFileSync(pidPath, String(process.pid));
182
-
183
- // 15. Graceful shutdown
184
- process.on('SIGINT', () => this.stop());
185
- process.on('SIGTERM', () => this.stop());
186
-
187
- logger.info(`Brain daemon started (PID: ${process.pid})`);
188
- }
189
-
190
- stop(): void {
191
- const logger = getLogger();
192
- logger.info('Shutting down...');
193
-
194
- if (this.cleanupTimer) {
195
- clearInterval(this.cleanupTimer);
196
- this.cleanupTimer = null;
197
- }
198
-
199
- this.researchEngine?.stop();
200
- this.embeddingEngine?.stop();
201
- this.learningEngine?.stop();
202
- this.mcpHttpServer?.stop();
203
- this.apiServer?.stop();
204
- this.ipcServer?.stop();
205
- this.db?.close();
206
-
207
- // Remove PID file
208
- if (this.config) {
209
- const pidPath = path.join(path.dirname(this.config.dbPath), 'brain.pid');
210
- try { fs.unlinkSync(pidPath); } catch { /* ignore */ }
211
- }
212
-
213
- logger.info('Brain daemon stopped');
214
- process.exit(0);
215
- }
216
-
217
- private setupEventListeners(services: Services, synapseManager: SynapseManager): void {
218
- const bus = getEventBus();
219
-
220
- // Error → Project synapse
221
- bus.on('error:reported', ({ errorId, projectId }) => {
222
- synapseManager.strengthen(
223
- { type: 'error', id: errorId },
224
- { type: 'project', id: projectId },
225
- 'co_occurs',
226
- );
227
- });
228
-
229
- // Solution applied → strengthen or weaken
230
- bus.on('solution:applied', ({ errorId, solutionId, success }) => {
231
- if (success) {
232
- synapseManager.strengthen(
233
- { type: 'solution', id: solutionId },
234
- { type: 'error', id: errorId },
235
- 'solves',
236
- );
237
- } else {
238
- const synapse = synapseManager.find(
239
- { type: 'solution', id: solutionId },
240
- { type: 'error', id: errorId },
241
- 'solves',
242
- );
243
- if (synapse) synapseManager.weaken(synapse.id, 0.7);
244
- }
245
- });
246
-
247
- // Module registered → link to project
248
- bus.on('module:registered', ({ moduleId, projectId }) => {
249
- synapseManager.strengthen(
250
- { type: 'code_module', id: moduleId },
251
- { type: 'project', id: projectId },
252
- 'co_occurs',
253
- );
254
- });
255
-
256
- // Rule learned → log
257
- bus.on('rule:learned', ({ ruleId, pattern }) => {
258
- getLogger().info(`New rule #${ruleId} learned: ${pattern}`);
259
- });
260
-
261
- // Insight created → log
262
- bus.on('insight:created', ({ insightId, type }) => {
263
- getLogger().info(`New insight #${insightId} (${type})`);
264
- });
265
- }
266
- }