@timmeck/brain 1.9.0 → 2.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 (253) hide show
  1. package/README.md +33 -0
  2. package/brain.log +3876 -0
  3. package/{src/cli/commands/dashboard.ts → dashboard.html} +694 -807
  4. package/dist/api/server.d.ts +4 -18
  5. package/dist/api/server.js +4 -173
  6. package/dist/api/server.js.map +1 -1
  7. package/dist/brain.d.ts +2 -0
  8. package/dist/brain.js +15 -4
  9. package/dist/brain.js.map +1 -1
  10. package/dist/cli/colors.d.ts +4 -25
  11. package/dist/cli/colors.js +3 -89
  12. package/dist/cli/colors.js.map +1 -1
  13. package/dist/cli/commands/dashboard.js +21 -2
  14. package/dist/cli/commands/dashboard.js.map +1 -1
  15. package/dist/cli/commands/peers.d.ts +2 -0
  16. package/dist/cli/commands/peers.js +38 -0
  17. package/dist/cli/commands/peers.js.map +1 -0
  18. package/dist/cli/commands/status.js +0 -1
  19. package/dist/cli/commands/status.js.map +1 -1
  20. package/dist/config.js +2 -29
  21. package/dist/config.js.map +1 -1
  22. package/dist/db/connection.d.ts +1 -2
  23. package/dist/db/connection.js +1 -18
  24. package/dist/db/connection.js.map +1 -1
  25. package/dist/index.js +3 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
  28. package/dist/ipc/__tests__/protocol.test.js +117 -0
  29. package/dist/ipc/__tests__/protocol.test.js.map +1 -0
  30. package/dist/ipc/client.d.ts +1 -16
  31. package/dist/ipc/client.js +1 -100
  32. package/dist/ipc/client.js.map +1 -1
  33. package/dist/ipc/protocol.d.ts +1 -8
  34. package/dist/ipc/protocol.js +1 -28
  35. package/dist/ipc/protocol.js.map +1 -1
  36. package/dist/ipc/router.d.ts +2 -0
  37. package/dist/ipc/router.js +30 -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/learning/confidence-scorer.d.ts +2 -5
  43. package/dist/learning/confidence-scorer.js +4 -19
  44. package/dist/learning/confidence-scorer.js.map +1 -1
  45. package/dist/learning/decay.js +2 -3
  46. package/dist/learning/decay.js.map +1 -1
  47. package/dist/learning/learning-engine.d.ts +2 -5
  48. package/dist/learning/learning-engine.js +3 -15
  49. package/dist/learning/learning-engine.js.map +1 -1
  50. package/dist/mcp/http-server.d.ts +1 -7
  51. package/dist/mcp/http-server.js +6 -117
  52. package/dist/mcp/http-server.js.map +1 -1
  53. package/dist/mcp/server.js +5 -61
  54. package/dist/mcp/server.js.map +1 -1
  55. package/dist/mcp/tools.js +36 -0
  56. package/dist/mcp/tools.js.map +1 -1
  57. package/dist/parsing/parsers/compiler.js +1 -1
  58. package/dist/parsing/parsers/compiler.js.map +1 -1
  59. package/dist/research/research-engine.d.ts +2 -6
  60. package/dist/research/research-engine.js +3 -23
  61. package/dist/research/research-engine.js.map +1 -1
  62. package/dist/services/synapse.service.d.ts +3 -3
  63. package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
  64. package/dist/signals/__tests__/fingerprint.test.js +118 -0
  65. package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
  66. package/dist/synapses/activation.d.ts +3 -13
  67. package/dist/synapses/activation.js +2 -49
  68. package/dist/synapses/activation.js.map +1 -1
  69. package/dist/synapses/decay.d.ts +2 -11
  70. package/dist/synapses/decay.js +2 -26
  71. package/dist/synapses/decay.js.map +1 -1
  72. package/dist/synapses/hebbian.d.ts +2 -13
  73. package/dist/synapses/hebbian.js +2 -35
  74. package/dist/synapses/hebbian.js.map +1 -1
  75. package/dist/synapses/pathfinder.d.ts +2 -14
  76. package/dist/synapses/pathfinder.js +2 -49
  77. package/dist/synapses/pathfinder.js.map +1 -1
  78. package/dist/synapses/synapse-manager.d.ts +7 -23
  79. package/dist/synapses/synapse-manager.js +6 -63
  80. package/dist/synapses/synapse-manager.js.map +1 -1
  81. package/dist/types/ipc.types.d.ts +1 -11
  82. package/dist/utils/__tests__/hash.test.d.ts +1 -0
  83. package/dist/utils/__tests__/hash.test.js +32 -0
  84. package/dist/utils/__tests__/hash.test.js.map +1 -0
  85. package/dist/utils/__tests__/paths.test.d.ts +1 -0
  86. package/dist/utils/__tests__/paths.test.js +75 -0
  87. package/dist/utils/__tests__/paths.test.js.map +1 -0
  88. package/dist/utils/events.d.ts +4 -8
  89. package/dist/utils/events.js +2 -14
  90. package/dist/utils/events.js.map +1 -1
  91. package/dist/utils/hash.d.ts +1 -1
  92. package/dist/utils/hash.js +1 -4
  93. package/dist/utils/hash.js.map +1 -1
  94. package/dist/utils/logger.d.ts +3 -2
  95. package/dist/utils/logger.js +8 -35
  96. package/dist/utils/logger.js.map +1 -1
  97. package/dist/utils/paths.d.ts +2 -1
  98. package/dist/utils/paths.js +4 -13
  99. package/dist/utils/paths.js.map +1 -1
  100. package/eslint.config.js +14 -0
  101. package/package.json +56 -49
  102. package/BRAIN_PLAN.md +0 -3324
  103. package/reddit_post.md +0 -45
  104. package/src/api/server.ts +0 -395
  105. package/src/brain.ts +0 -313
  106. package/src/cli/colors.ts +0 -116
  107. package/src/cli/commands/config.ts +0 -169
  108. package/src/cli/commands/doctor.ts +0 -124
  109. package/src/cli/commands/explain.ts +0 -83
  110. package/src/cli/commands/export.ts +0 -31
  111. package/src/cli/commands/import.ts +0 -199
  112. package/src/cli/commands/insights.ts +0 -65
  113. package/src/cli/commands/learn.ts +0 -24
  114. package/src/cli/commands/modules.ts +0 -53
  115. package/src/cli/commands/network.ts +0 -67
  116. package/src/cli/commands/projects.ts +0 -42
  117. package/src/cli/commands/query.ts +0 -120
  118. package/src/cli/commands/start.ts +0 -105
  119. package/src/cli/commands/status.ts +0 -75
  120. package/src/cli/commands/stop.ts +0 -34
  121. package/src/cli/ipc-helper.ts +0 -22
  122. package/src/cli/update-check.ts +0 -63
  123. package/src/code/analyzer.ts +0 -117
  124. package/src/code/fingerprint.ts +0 -87
  125. package/src/code/matcher.ts +0 -129
  126. package/src/code/parsers/generic.ts +0 -29
  127. package/src/code/parsers/python.ts +0 -54
  128. package/src/code/parsers/typescript.ts +0 -65
  129. package/src/code/registry.ts +0 -60
  130. package/src/code/scorer.ts +0 -120
  131. package/src/config.ts +0 -135
  132. package/src/dashboard/server.ts +0 -142
  133. package/src/db/connection.ts +0 -22
  134. package/src/db/migrations/001_core_schema.ts +0 -120
  135. package/src/db/migrations/002_learning_schema.ts +0 -38
  136. package/src/db/migrations/003_code_schema.ts +0 -53
  137. package/src/db/migrations/004_synapses_schema.ts +0 -57
  138. package/src/db/migrations/005_fts_indexes.ts +0 -78
  139. package/src/db/migrations/006_synapses_phase3.ts +0 -17
  140. package/src/db/migrations/007_feedback.ts +0 -13
  141. package/src/db/migrations/008_git_integration.ts +0 -38
  142. package/src/db/migrations/009_embeddings.ts +0 -8
  143. package/src/db/migrations/index.ts +0 -70
  144. package/src/db/repositories/antipattern.repository.ts +0 -66
  145. package/src/db/repositories/code-module.repository.ts +0 -142
  146. package/src/db/repositories/error.repository.ts +0 -189
  147. package/src/db/repositories/insight.repository.ts +0 -99
  148. package/src/db/repositories/notification.repository.ts +0 -66
  149. package/src/db/repositories/project.repository.ts +0 -93
  150. package/src/db/repositories/rule.repository.ts +0 -108
  151. package/src/db/repositories/solution.repository.ts +0 -154
  152. package/src/db/repositories/synapse.repository.ts +0 -163
  153. package/src/db/repositories/terminal.repository.ts +0 -101
  154. package/src/embeddings/engine.ts +0 -238
  155. package/src/hooks/post-tool-use.ts +0 -92
  156. package/src/hooks/post-write.ts +0 -129
  157. package/src/index.ts +0 -63
  158. package/src/ipc/client.ts +0 -118
  159. package/src/ipc/protocol.ts +0 -35
  160. package/src/ipc/router.ts +0 -133
  161. package/src/ipc/server.ts +0 -176
  162. package/src/learning/confidence-scorer.ts +0 -80
  163. package/src/learning/decay.ts +0 -46
  164. package/src/learning/learning-engine.ts +0 -170
  165. package/src/learning/pattern-extractor.ts +0 -90
  166. package/src/learning/rule-generator.ts +0 -74
  167. package/src/main.rs:10:5 +0 -0
  168. package/src/matching/error-matcher.ts +0 -166
  169. package/src/matching/fingerprint.ts +0 -34
  170. package/src/matching/similarity.ts +0 -61
  171. package/src/matching/tfidf.ts +0 -74
  172. package/src/matching/tokenizer.ts +0 -41
  173. package/src/mcp/auto-detect.ts +0 -93
  174. package/src/mcp/http-server.ts +0 -140
  175. package/src/mcp/server.ts +0 -73
  176. package/src/mcp/tools.ts +0 -328
  177. package/src/parsing/error-parser.ts +0 -28
  178. package/src/parsing/parsers/compiler.ts +0 -93
  179. package/src/parsing/parsers/generic.ts +0 -28
  180. package/src/parsing/parsers/go.ts +0 -97
  181. package/src/parsing/parsers/node.ts +0 -69
  182. package/src/parsing/parsers/python.ts +0 -62
  183. package/src/parsing/parsers/rust.ts +0 -50
  184. package/src/parsing/parsers/shell.ts +0 -42
  185. package/src/parsing/types.ts +0 -47
  186. package/src/research/gap-analyzer.ts +0 -135
  187. package/src/research/insight-generator.ts +0 -123
  188. package/src/research/research-engine.ts +0 -116
  189. package/src/research/synergy-detector.ts +0 -126
  190. package/src/research/template-extractor.ts +0 -130
  191. package/src/research/trend-analyzer.ts +0 -127
  192. package/src/services/analytics.service.ts +0 -226
  193. package/src/services/code.service.ts +0 -271
  194. package/src/services/error.service.ts +0 -266
  195. package/src/services/git.service.ts +0 -132
  196. package/src/services/notification.service.ts +0 -41
  197. package/src/services/prevention.service.ts +0 -159
  198. package/src/services/research.service.ts +0 -98
  199. package/src/services/solution.service.ts +0 -174
  200. package/src/services/synapse.service.ts +0 -59
  201. package/src/services/terminal.service.ts +0 -81
  202. package/src/synapses/activation.ts +0 -80
  203. package/src/synapses/decay.ts +0 -38
  204. package/src/synapses/hebbian.ts +0 -69
  205. package/src/synapses/pathfinder.ts +0 -81
  206. package/src/synapses/synapse-manager.ts +0 -113
  207. package/src/types/code.types.ts +0 -52
  208. package/src/types/config.types.ts +0 -103
  209. package/src/types/error.types.ts +0 -67
  210. package/src/types/ipc.types.ts +0 -8
  211. package/src/types/mcp.types.ts +0 -53
  212. package/src/types/research.types.ts +0 -28
  213. package/src/types/solution.types.ts +0 -30
  214. package/src/types/synapse.types.ts +0 -50
  215. package/src/utils/events.ts +0 -45
  216. package/src/utils/hash.ts +0 -5
  217. package/src/utils/logger.ts +0 -48
  218. package/src/utils/paths.ts +0 -19
  219. package/tests/e2e/test_code_intelligence.py +0 -1015
  220. package/tests/e2e/test_error_memory.py +0 -451
  221. package/tests/e2e/test_full_integration.py +0 -534
  222. package/tests/fixtures/code-modules/modules.ts +0 -83
  223. package/tests/fixtures/errors/go.ts +0 -9
  224. package/tests/fixtures/errors/node.ts +0 -24
  225. package/tests/fixtures/errors/python.ts +0 -21
  226. package/tests/fixtures/errors/rust.ts +0 -25
  227. package/tests/fixtures/errors/shell.ts +0 -15
  228. package/tests/fixtures/solutions/solutions.ts +0 -27
  229. package/tests/helpers/setup-db.ts +0 -52
  230. package/tests/integration/code-flow.test.ts +0 -86
  231. package/tests/integration/error-flow.test.ts +0 -83
  232. package/tests/integration/ipc-flow.test.ts +0 -166
  233. package/tests/integration/learning-cycle.test.ts +0 -82
  234. package/tests/integration/synapse-flow.test.ts +0 -117
  235. package/tests/unit/code/analyzer.test.ts +0 -58
  236. package/tests/unit/code/fingerprint.test.ts +0 -51
  237. package/tests/unit/code/scorer.test.ts +0 -55
  238. package/tests/unit/learning/confidence-scorer.test.ts +0 -60
  239. package/tests/unit/learning/decay.test.ts +0 -45
  240. package/tests/unit/learning/pattern-extractor.test.ts +0 -50
  241. package/tests/unit/matching/error-matcher.test.ts +0 -69
  242. package/tests/unit/matching/fingerprint.test.ts +0 -47
  243. package/tests/unit/matching/similarity.test.ts +0 -65
  244. package/tests/unit/matching/tfidf.test.ts +0 -71
  245. package/tests/unit/matching/tokenizer.test.ts +0 -83
  246. package/tests/unit/parsing/parsers.test.ts +0 -113
  247. package/tests/unit/research/gap-analyzer.test.ts +0 -45
  248. package/tests/unit/research/trend-analyzer.test.ts +0 -45
  249. package/tests/unit/synapses/activation.test.ts +0 -80
  250. package/tests/unit/synapses/decay.test.ts +0 -27
  251. package/tests/unit/synapses/hebbian.test.ts +0 -96
  252. package/tests/unit/synapses/pathfinder.test.ts +0 -72
  253. package/tsconfig.json +0 -18
package/reddit_post.md DELETED
@@ -1,45 +0,0 @@
1
- # Reddit Post for r/ClaudeAI
2
-
3
- ## Title:
4
- I built a persistent memory system for Claude Code — it remembers every error, solution, and code module across all your projects
5
-
6
- ## Body:
7
-
8
- Claude Code starts fresh every session. After months of hitting the same errors and rewriting the same code, I built **Brain** — an MCP server that gives Claude Code a persistent memory.
9
-
10
- ### What it does
11
-
12
- - **Error Memory** — Brain remembers every error you've encountered and every solution that worked. Next time you hit the same bug, it suggests the fix with a confidence score.
13
- - **Cross-Project Learning** — Fixed a bug in project A? Brain suggests the same fix when a similar error appears in project B.
14
- - **Code Intelligence** — Before writing new code, Brain checks if similar modules already exist across your 36 projects. No more reinventing the wheel.
15
- - **Hebbian Synapse Network** — 37,000+ weighted connections between errors, solutions, and code modules. Connections strengthen with use, like biological synapses.
16
- - **Proactive Prevention** — Post-write hooks check your code against known antipatterns *before* errors occur.
17
- - **Auto-Detect** — Hooks catch errors in real-time from Bash output and report them to Brain automatically. You don't have to do anything.
18
-
19
- ### The stack
20
-
21
- - TypeScript, better-sqlite3, MCP SDK
22
- - 13 MCP tools for Claude Code
23
- - REST API (30+ endpoints) + MCP over HTTP/SSE (works with Cursor, Windsurf, Cline)
24
- - Local embeddings (all-MiniLM-L6-v2) for hybrid search — no cloud, no API keys
25
- - Interactive dashboard with live synapse network visualization
26
- - Full CLI: `brain status`, `brain query`, `brain explain`, `brain dashboard`
27
-
28
- ### Real numbers from my setup
29
-
30
- - 18,160 code modules indexed across 36 projects
31
- - 37,277 synapse connections
32
- - 4,902 research insights generated automatically
33
- - 139 tests, all passing
34
-
35
- ### Install
36
-
37
- ```
38
- npm install -g @timmeck/brain
39
- ```
40
-
41
- Setup takes 2 minutes — add the MCP server + hooks to your Claude Code settings and run `brain start`. That's it.
42
-
43
- GitHub: https://github.com/timmeck/brain
44
-
45
- MIT licensed, open source. Would love feedback — especially on what MCP tools would be most useful to add next.
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
- }