@timmeck/brain 1.8.0 → 1.8.2

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 (177) hide show
  1. package/BRAIN_PLAN.md +3324 -3324
  2. package/LICENSE +21 -21
  3. package/dist/api/server.d.ts +4 -0
  4. package/dist/api/server.js +73 -0
  5. package/dist/api/server.js.map +1 -1
  6. package/dist/brain.js +2 -1
  7. package/dist/brain.js.map +1 -1
  8. package/dist/cli/commands/dashboard.js +606 -572
  9. package/dist/cli/commands/dashboard.js.map +1 -1
  10. package/dist/dashboard/server.js +25 -25
  11. package/dist/db/migrations/001_core_schema.js +115 -115
  12. package/dist/db/migrations/002_learning_schema.js +33 -33
  13. package/dist/db/migrations/003_code_schema.js +48 -48
  14. package/dist/db/migrations/004_synapses_schema.js +52 -52
  15. package/dist/db/migrations/005_fts_indexes.js +73 -73
  16. package/dist/db/migrations/007_feedback.js +8 -8
  17. package/dist/db/migrations/008_git_integration.js +33 -33
  18. package/dist/db/migrations/009_embeddings.js +3 -3
  19. package/dist/db/repositories/antipattern.repository.js +3 -3
  20. package/dist/db/repositories/code-module.repository.js +32 -32
  21. package/dist/db/repositories/notification.repository.js +3 -3
  22. package/dist/db/repositories/project.repository.js +21 -21
  23. package/dist/db/repositories/rule.repository.js +24 -24
  24. package/dist/db/repositories/solution.repository.js +50 -50
  25. package/dist/db/repositories/synapse.repository.js +18 -18
  26. package/dist/db/repositories/terminal.repository.js +24 -24
  27. package/dist/embeddings/engine.d.ts +2 -2
  28. package/dist/embeddings/engine.js +17 -4
  29. package/dist/embeddings/engine.js.map +1 -1
  30. package/dist/index.js +1 -1
  31. package/dist/ipc/server.d.ts +8 -0
  32. package/dist/ipc/server.js +67 -1
  33. package/dist/ipc/server.js.map +1 -1
  34. package/dist/matching/error-matcher.js +5 -5
  35. package/dist/matching/fingerprint.js +6 -1
  36. package/dist/matching/fingerprint.js.map +1 -1
  37. package/dist/mcp/http-server.js +8 -2
  38. package/dist/mcp/http-server.js.map +1 -1
  39. package/dist/services/code.service.d.ts +3 -0
  40. package/dist/services/code.service.js +33 -4
  41. package/dist/services/code.service.js.map +1 -1
  42. package/dist/services/error.service.js +4 -3
  43. package/dist/services/error.service.js.map +1 -1
  44. package/dist/services/git.service.js +14 -14
  45. package/package.json +49 -49
  46. package/src/api/server.ts +395 -321
  47. package/src/brain.ts +266 -265
  48. package/src/cli/colors.ts +116 -116
  49. package/src/cli/commands/config.ts +169 -169
  50. package/src/cli/commands/dashboard.ts +755 -720
  51. package/src/cli/commands/doctor.ts +118 -118
  52. package/src/cli/commands/explain.ts +83 -83
  53. package/src/cli/commands/export.ts +31 -31
  54. package/src/cli/commands/import.ts +199 -199
  55. package/src/cli/commands/insights.ts +65 -65
  56. package/src/cli/commands/learn.ts +24 -24
  57. package/src/cli/commands/modules.ts +53 -53
  58. package/src/cli/commands/network.ts +67 -67
  59. package/src/cli/commands/projects.ts +42 -42
  60. package/src/cli/commands/query.ts +120 -120
  61. package/src/cli/commands/start.ts +62 -62
  62. package/src/cli/commands/status.ts +75 -75
  63. package/src/cli/commands/stop.ts +34 -34
  64. package/src/cli/ipc-helper.ts +22 -22
  65. package/src/cli/update-check.ts +63 -63
  66. package/src/code/fingerprint.ts +87 -87
  67. package/src/code/parsers/generic.ts +29 -29
  68. package/src/code/parsers/python.ts +54 -54
  69. package/src/code/parsers/typescript.ts +65 -65
  70. package/src/code/registry.ts +60 -60
  71. package/src/dashboard/server.ts +142 -142
  72. package/src/db/connection.ts +22 -22
  73. package/src/db/migrations/001_core_schema.ts +120 -120
  74. package/src/db/migrations/002_learning_schema.ts +38 -38
  75. package/src/db/migrations/003_code_schema.ts +53 -53
  76. package/src/db/migrations/004_synapses_schema.ts +57 -57
  77. package/src/db/migrations/005_fts_indexes.ts +78 -78
  78. package/src/db/migrations/006_synapses_phase3.ts +17 -17
  79. package/src/db/migrations/007_feedback.ts +13 -13
  80. package/src/db/migrations/008_git_integration.ts +38 -38
  81. package/src/db/migrations/009_embeddings.ts +8 -8
  82. package/src/db/repositories/antipattern.repository.ts +66 -66
  83. package/src/db/repositories/code-module.repository.ts +142 -142
  84. package/src/db/repositories/notification.repository.ts +66 -66
  85. package/src/db/repositories/project.repository.ts +93 -93
  86. package/src/db/repositories/rule.repository.ts +108 -108
  87. package/src/db/repositories/solution.repository.ts +154 -154
  88. package/src/db/repositories/synapse.repository.ts +153 -153
  89. package/src/db/repositories/terminal.repository.ts +101 -101
  90. package/src/embeddings/engine.ts +238 -217
  91. package/src/index.ts +63 -63
  92. package/src/ipc/client.ts +118 -118
  93. package/src/ipc/protocol.ts +35 -35
  94. package/src/ipc/router.ts +133 -133
  95. package/src/ipc/server.ts +176 -110
  96. package/src/learning/decay.ts +46 -46
  97. package/src/learning/pattern-extractor.ts +90 -90
  98. package/src/learning/rule-generator.ts +74 -74
  99. package/src/matching/error-matcher.ts +5 -5
  100. package/src/matching/fingerprint.ts +34 -29
  101. package/src/matching/similarity.ts +61 -61
  102. package/src/matching/tfidf.ts +74 -74
  103. package/src/matching/tokenizer.ts +41 -41
  104. package/src/mcp/auto-detect.ts +93 -93
  105. package/src/mcp/http-server.ts +140 -137
  106. package/src/mcp/server.ts +73 -73
  107. package/src/parsing/error-parser.ts +28 -28
  108. package/src/parsing/parsers/compiler.ts +93 -93
  109. package/src/parsing/parsers/generic.ts +28 -28
  110. package/src/parsing/parsers/go.ts +97 -97
  111. package/src/parsing/parsers/node.ts +69 -69
  112. package/src/parsing/parsers/python.ts +62 -62
  113. package/src/parsing/parsers/rust.ts +50 -50
  114. package/src/parsing/parsers/shell.ts +42 -42
  115. package/src/parsing/types.ts +47 -47
  116. package/src/research/gap-analyzer.ts +135 -135
  117. package/src/research/insight-generator.ts +123 -123
  118. package/src/research/research-engine.ts +116 -116
  119. package/src/research/synergy-detector.ts +126 -126
  120. package/src/research/template-extractor.ts +130 -130
  121. package/src/research/trend-analyzer.ts +127 -127
  122. package/src/services/code.service.ts +271 -238
  123. package/src/services/error.service.ts +4 -3
  124. package/src/services/git.service.ts +132 -132
  125. package/src/services/notification.service.ts +41 -41
  126. package/src/services/synapse.service.ts +59 -59
  127. package/src/services/terminal.service.ts +81 -81
  128. package/src/synapses/activation.ts +80 -80
  129. package/src/synapses/decay.ts +38 -38
  130. package/src/synapses/hebbian.ts +69 -69
  131. package/src/synapses/pathfinder.ts +81 -81
  132. package/src/synapses/synapse-manager.ts +109 -109
  133. package/src/types/code.types.ts +52 -52
  134. package/src/types/error.types.ts +67 -67
  135. package/src/types/ipc.types.ts +8 -8
  136. package/src/types/mcp.types.ts +53 -53
  137. package/src/types/research.types.ts +28 -28
  138. package/src/types/solution.types.ts +30 -30
  139. package/src/utils/events.ts +45 -45
  140. package/src/utils/hash.ts +5 -5
  141. package/src/utils/logger.ts +48 -48
  142. package/src/utils/paths.ts +19 -19
  143. package/tests/e2e/test_code_intelligence.py +1015 -0
  144. package/tests/e2e/test_error_memory.py +451 -0
  145. package/tests/e2e/test_full_integration.py +534 -0
  146. package/tests/fixtures/code-modules/modules.ts +83 -83
  147. package/tests/fixtures/errors/go.ts +9 -9
  148. package/tests/fixtures/errors/node.ts +24 -24
  149. package/tests/fixtures/errors/python.ts +21 -21
  150. package/tests/fixtures/errors/rust.ts +25 -25
  151. package/tests/fixtures/errors/shell.ts +15 -15
  152. package/tests/fixtures/solutions/solutions.ts +27 -27
  153. package/tests/helpers/setup-db.ts +52 -52
  154. package/tests/integration/code-flow.test.ts +86 -86
  155. package/tests/integration/error-flow.test.ts +83 -83
  156. package/tests/integration/ipc-flow.test.ts +166 -166
  157. package/tests/integration/learning-cycle.test.ts +82 -82
  158. package/tests/integration/synapse-flow.test.ts +117 -117
  159. package/tests/unit/code/analyzer.test.ts +58 -58
  160. package/tests/unit/code/fingerprint.test.ts +51 -51
  161. package/tests/unit/code/scorer.test.ts +55 -55
  162. package/tests/unit/learning/confidence-scorer.test.ts +60 -60
  163. package/tests/unit/learning/decay.test.ts +45 -45
  164. package/tests/unit/learning/pattern-extractor.test.ts +50 -50
  165. package/tests/unit/matching/error-matcher.test.ts +69 -69
  166. package/tests/unit/matching/fingerprint.test.ts +47 -47
  167. package/tests/unit/matching/similarity.test.ts +65 -65
  168. package/tests/unit/matching/tfidf.test.ts +71 -71
  169. package/tests/unit/matching/tokenizer.test.ts +83 -83
  170. package/tests/unit/parsing/parsers.test.ts +113 -113
  171. package/tests/unit/research/gap-analyzer.test.ts +45 -45
  172. package/tests/unit/research/trend-analyzer.test.ts +45 -45
  173. package/tests/unit/synapses/activation.test.ts +80 -80
  174. package/tests/unit/synapses/decay.test.ts +27 -27
  175. package/tests/unit/synapses/hebbian.test.ts +96 -96
  176. package/tests/unit/synapses/pathfinder.test.ts +72 -72
  177. package/tsconfig.json +18 -18
@@ -1,137 +1,140 @@
1
- import http from 'node:http';
2
- import { randomUUID } from 'node:crypto';
3
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
- import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
5
- import { getLogger } from '../utils/logger.js';
6
- import type { IpcRouter } from '../ipc/router.js';
7
- import { registerToolsDirect } from './tools.js';
8
-
9
- export class McpHttpServer {
10
- private server: http.Server | null = null;
11
- private transports = new Map<string, SSEServerTransport>();
12
- private logger = getLogger();
13
-
14
- constructor(
15
- private port: number,
16
- private router: IpcRouter,
17
- ) {}
18
-
19
- start(): void {
20
- this.server = http.createServer((req, res) => {
21
- res.setHeader('Access-Control-Allow-Origin', '*');
22
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
23
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
24
-
25
- if (req.method === 'OPTIONS') {
26
- res.writeHead(204);
27
- res.end();
28
- return;
29
- }
30
-
31
- const url = new URL(req.url ?? '/', `http://localhost:${this.port}`);
32
-
33
- if (url.pathname === '/sse' && req.method === 'GET') {
34
- this.handleSSE(res);
35
- return;
36
- }
37
-
38
- if (url.pathname === '/messages' && req.method === 'POST') {
39
- this.handleMessage(req, res, url);
40
- return;
41
- }
42
-
43
- if (url.pathname === '/' && req.method === 'GET') {
44
- res.writeHead(200, { 'Content-Type': 'application/json' });
45
- res.end(JSON.stringify({
46
- name: 'brain',
47
- version: '1.7.0',
48
- protocol: 'MCP',
49
- transport: 'sse',
50
- endpoints: {
51
- sse: '/sse',
52
- messages: '/messages',
53
- },
54
- clients: this.transports.size,
55
- }));
56
- return;
57
- }
58
-
59
- res.writeHead(404, { 'Content-Type': 'text/plain' });
60
- res.end('Not Found');
61
- });
62
-
63
- this.server.listen(this.port, () => {
64
- this.logger.info(`MCP HTTP server (SSE) started on http://localhost:${this.port}`);
65
- });
66
- }
67
-
68
- stop(): void {
69
- this.transports.clear();
70
- this.server?.close();
71
- this.server = null;
72
- this.logger.info('MCP HTTP server stopped');
73
- }
74
-
75
- getClientCount(): number {
76
- return this.transports.size;
77
- }
78
-
79
- private async handleSSE(res: http.ServerResponse): Promise<void> {
80
- try {
81
- const transport = new SSEServerTransport('/messages', res);
82
- const sessionId = transport.sessionId ?? randomUUID();
83
- this.transports.set(sessionId, transport);
84
-
85
- const server = new McpServer({
86
- name: 'brain',
87
- version: '1.7.0',
88
- });
89
-
90
- registerToolsDirect(server, this.router);
91
-
92
- res.on('close', () => {
93
- this.transports.delete(sessionId);
94
- this.logger.debug(`MCP SSE client disconnected: ${sessionId}`);
95
- });
96
-
97
- await server.connect(transport);
98
- this.logger.info(`MCP SSE client connected: ${sessionId}`);
99
- } catch (err) {
100
- this.logger.error('MCP SSE connection error:', err);
101
- if (!res.headersSent) {
102
- res.writeHead(500, { 'Content-Type': 'text/plain' });
103
- res.end('Internal Server Error');
104
- }
105
- }
106
- }
107
-
108
- private async handleMessage(
109
- req: http.IncomingMessage,
110
- res: http.ServerResponse,
111
- url: URL,
112
- ): Promise<void> {
113
- try {
114
- const sessionId = url.searchParams.get('sessionId');
115
- if (!sessionId) {
116
- res.writeHead(400, { 'Content-Type': 'text/plain' });
117
- res.end('Missing sessionId parameter');
118
- return;
119
- }
120
-
121
- const transport = this.transports.get(sessionId);
122
- if (!transport) {
123
- res.writeHead(404, { 'Content-Type': 'text/plain' });
124
- res.end('Session not found. Connect to /sse first.');
125
- return;
126
- }
127
-
128
- await transport.handlePostMessage(req, res);
129
- } catch (err) {
130
- this.logger.error('MCP message error:', err);
131
- if (!res.headersSent) {
132
- res.writeHead(500, { 'Content-Type': 'text/plain' });
133
- res.end('Internal Server Error');
134
- }
135
- }
136
- }
137
- }
1
+ import http from 'node:http';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
5
+ import { getLogger } from '../utils/logger.js';
6
+ import type { IpcRouter } from '../ipc/router.js';
7
+ import { registerToolsDirect } from './tools.js';
8
+
9
+ export class McpHttpServer {
10
+ private server: http.Server | null = null;
11
+ private transports = new Map<string, SSEServerTransport>();
12
+ private logger = getLogger();
13
+
14
+ constructor(
15
+ private port: number,
16
+ private router: IpcRouter,
17
+ ) {}
18
+
19
+ start(): void {
20
+ this.server = http.createServer((req, res) => {
21
+ res.setHeader('Access-Control-Allow-Origin', '*');
22
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
23
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
24
+
25
+ if (req.method === 'OPTIONS') {
26
+ res.writeHead(204);
27
+ res.end();
28
+ return;
29
+ }
30
+
31
+ const url = new URL(req.url ?? '/', `http://localhost:${this.port}`);
32
+
33
+ if (url.pathname === '/sse' && req.method === 'GET') {
34
+ this.handleSSE(res);
35
+ return;
36
+ }
37
+
38
+ if (url.pathname === '/messages' && req.method === 'POST') {
39
+ this.handleMessage(req, res, url);
40
+ return;
41
+ }
42
+
43
+ if (url.pathname === '/' && req.method === 'GET') {
44
+ res.writeHead(200, { 'Content-Type': 'application/json' });
45
+ res.end(JSON.stringify({
46
+ name: 'brain',
47
+ version: '1.8.0',
48
+ protocol: 'MCP',
49
+ transport: 'sse',
50
+ endpoints: {
51
+ sse: '/sse',
52
+ messages: '/messages',
53
+ },
54
+ clients: this.transports.size,
55
+ }));
56
+ return;
57
+ }
58
+
59
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
60
+ res.end('Not Found');
61
+ });
62
+
63
+ this.server.listen(this.port, () => {
64
+ this.logger.info(`MCP HTTP server (SSE) started on http://localhost:${this.port}`);
65
+ });
66
+ }
67
+
68
+ stop(): void {
69
+ for (const transport of this.transports.values()) {
70
+ try { transport.close?.(); } catch { /* ignore */ }
71
+ }
72
+ this.transports.clear();
73
+ this.server?.close();
74
+ this.server = null;
75
+ this.logger.info('MCP HTTP server stopped');
76
+ }
77
+
78
+ getClientCount(): number {
79
+ return this.transports.size;
80
+ }
81
+
82
+ private async handleSSE(res: http.ServerResponse): Promise<void> {
83
+ try {
84
+ const transport = new SSEServerTransport('/messages', res);
85
+ const sessionId = transport.sessionId ?? randomUUID();
86
+ this.transports.set(sessionId, transport);
87
+
88
+ const server = new McpServer({
89
+ name: 'brain',
90
+ version: '1.8.0',
91
+ });
92
+
93
+ registerToolsDirect(server, this.router);
94
+
95
+ res.on('close', () => {
96
+ this.transports.delete(sessionId);
97
+ this.logger.debug(`MCP SSE client disconnected: ${sessionId}`);
98
+ });
99
+
100
+ await server.connect(transport);
101
+ this.logger.info(`MCP SSE client connected: ${sessionId}`);
102
+ } catch (err) {
103
+ this.logger.error('MCP SSE connection error:', err);
104
+ if (!res.headersSent) {
105
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
106
+ res.end('Internal Server Error');
107
+ }
108
+ }
109
+ }
110
+
111
+ private async handleMessage(
112
+ req: http.IncomingMessage,
113
+ res: http.ServerResponse,
114
+ url: URL,
115
+ ): Promise<void> {
116
+ try {
117
+ const sessionId = url.searchParams.get('sessionId');
118
+ if (!sessionId) {
119
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
120
+ res.end('Missing sessionId parameter');
121
+ return;
122
+ }
123
+
124
+ const transport = this.transports.get(sessionId);
125
+ if (!transport) {
126
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
127
+ res.end('Session not found. Connect to /sse first.');
128
+ return;
129
+ }
130
+
131
+ await transport.handlePostMessage(req, res);
132
+ } catch (err) {
133
+ this.logger.error('MCP message error:', err);
134
+ if (!res.headersSent) {
135
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
136
+ res.end('Internal Server Error');
137
+ }
138
+ }
139
+ }
140
+ }
package/src/mcp/server.ts CHANGED
@@ -1,73 +1,73 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
- import { spawn } from 'node:child_process';
4
- import path from 'node:path';
5
- import { IpcClient } from '../ipc/client.js';
6
- import { getPipeName } from '../utils/paths.js';
7
- import { registerTools } from './tools.js';
8
-
9
- function spawnDaemon(): void {
10
- const entryPoint = path.resolve(import.meta.dirname, '../index.ts');
11
- const child = spawn(process.execPath, [
12
- '--import', 'tsx',
13
- entryPoint, 'daemon',
14
- ], {
15
- detached: true,
16
- stdio: 'ignore',
17
- cwd: path.resolve(import.meta.dirname, '../..'),
18
- });
19
- child.unref();
20
- process.stderr.write(`Brain: Auto-started daemon (PID: ${child.pid})\n`);
21
- }
22
-
23
- async function connectWithRetry(ipc: IpcClient, retries: number, delayMs: number): Promise<void> {
24
- for (let i = 0; i < retries; i++) {
25
- try {
26
- await ipc.connect();
27
- return;
28
- } catch {
29
- if (i < retries - 1) {
30
- await new Promise(r => setTimeout(r, delayMs));
31
- }
32
- }
33
- }
34
- throw new Error('Could not connect to daemon after retries');
35
- }
36
-
37
- export async function startMcpServer(): Promise<void> {
38
- const server = new McpServer({
39
- name: 'brain',
40
- version: '1.0.0',
41
- });
42
-
43
- const ipc = new IpcClient(getPipeName());
44
-
45
- try {
46
- await ipc.connect();
47
- } catch {
48
- // Daemon not running — auto-start it
49
- process.stderr.write('Brain: Daemon not running, starting automatically...\n');
50
- spawnDaemon();
51
- try {
52
- await connectWithRetry(ipc, 10, 500);
53
- } catch {
54
- process.stderr.write('Brain: Could not connect to daemon after auto-start. Check logs.\n');
55
- process.exit(1);
56
- }
57
- }
58
-
59
- registerTools(server, ipc);
60
-
61
- const transport = new StdioServerTransport();
62
- await server.connect(transport);
63
-
64
- process.on('SIGINT', () => {
65
- ipc.disconnect();
66
- process.exit(0);
67
- });
68
-
69
- process.on('SIGTERM', () => {
70
- ipc.disconnect();
71
- process.exit(0);
72
- });
73
- }
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { spawn } from 'node:child_process';
4
+ import path from 'node:path';
5
+ import { IpcClient } from '../ipc/client.js';
6
+ import { getPipeName } from '../utils/paths.js';
7
+ import { registerTools } from './tools.js';
8
+
9
+ function spawnDaemon(): void {
10
+ const entryPoint = path.resolve(import.meta.dirname, '../index.ts');
11
+ const child = spawn(process.execPath, [
12
+ '--import', 'tsx',
13
+ entryPoint, 'daemon',
14
+ ], {
15
+ detached: true,
16
+ stdio: 'ignore',
17
+ cwd: path.resolve(import.meta.dirname, '../..'),
18
+ });
19
+ child.unref();
20
+ process.stderr.write(`Brain: Auto-started daemon (PID: ${child.pid})\n`);
21
+ }
22
+
23
+ async function connectWithRetry(ipc: IpcClient, retries: number, delayMs: number): Promise<void> {
24
+ for (let i = 0; i < retries; i++) {
25
+ try {
26
+ await ipc.connect();
27
+ return;
28
+ } catch {
29
+ if (i < retries - 1) {
30
+ await new Promise(r => setTimeout(r, delayMs));
31
+ }
32
+ }
33
+ }
34
+ throw new Error('Could not connect to daemon after retries');
35
+ }
36
+
37
+ export async function startMcpServer(): Promise<void> {
38
+ const server = new McpServer({
39
+ name: 'brain',
40
+ version: '1.0.0',
41
+ });
42
+
43
+ const ipc = new IpcClient(getPipeName());
44
+
45
+ try {
46
+ await ipc.connect();
47
+ } catch {
48
+ // Daemon not running — auto-start it
49
+ process.stderr.write('Brain: Daemon not running, starting automatically...\n');
50
+ spawnDaemon();
51
+ try {
52
+ await connectWithRetry(ipc, 10, 500);
53
+ } catch {
54
+ process.stderr.write('Brain: Could not connect to daemon after auto-start. Check logs.\n');
55
+ process.exit(1);
56
+ }
57
+ }
58
+
59
+ registerTools(server, ipc);
60
+
61
+ const transport = new StdioServerTransport();
62
+ await server.connect(transport);
63
+
64
+ process.on('SIGINT', () => {
65
+ ipc.disconnect();
66
+ process.exit(0);
67
+ });
68
+
69
+ process.on('SIGTERM', () => {
70
+ ipc.disconnect();
71
+ process.exit(0);
72
+ });
73
+ }
@@ -1,28 +1,28 @@
1
- import { ErrorParserRegistry } from './types.js';
2
- import { nodeParser } from './parsers/node.js';
3
- import { pythonParser } from './parsers/python.js';
4
- import { rustParser } from './parsers/rust.js';
5
- import { goParser } from './parsers/go.js';
6
- import { shellParser } from './parsers/shell.js';
7
- import { compilerParser } from './parsers/compiler.js';
8
- import { genericParser } from './parsers/generic.js';
9
-
10
- let registryInstance: ErrorParserRegistry | null = null;
11
-
12
- export function getParserRegistry(): ErrorParserRegistry {
13
- if (!registryInstance) {
14
- registryInstance = new ErrorParserRegistry();
15
- registryInstance.register(nodeParser);
16
- registryInstance.register(pythonParser);
17
- registryInstance.register(rustParser);
18
- registryInstance.register(goParser);
19
- registryInstance.register(shellParser);
20
- registryInstance.register(compilerParser);
21
- registryInstance.register(genericParser);
22
- }
23
- return registryInstance;
24
- }
25
-
26
- export function parseError(input: string) {
27
- return getParserRegistry().parse(input);
28
- }
1
+ import { ErrorParserRegistry } from './types.js';
2
+ import { nodeParser } from './parsers/node.js';
3
+ import { pythonParser } from './parsers/python.js';
4
+ import { rustParser } from './parsers/rust.js';
5
+ import { goParser } from './parsers/go.js';
6
+ import { shellParser } from './parsers/shell.js';
7
+ import { compilerParser } from './parsers/compiler.js';
8
+ import { genericParser } from './parsers/generic.js';
9
+
10
+ let registryInstance: ErrorParserRegistry | null = null;
11
+
12
+ export function getParserRegistry(): ErrorParserRegistry {
13
+ if (!registryInstance) {
14
+ registryInstance = new ErrorParserRegistry();
15
+ registryInstance.register(nodeParser);
16
+ registryInstance.register(pythonParser);
17
+ registryInstance.register(rustParser);
18
+ registryInstance.register(goParser);
19
+ registryInstance.register(shellParser);
20
+ registryInstance.register(compilerParser);
21
+ registryInstance.register(genericParser);
22
+ }
23
+ return registryInstance;
24
+ }
25
+
26
+ export function parseError(input: string) {
27
+ return getParserRegistry().parse(input);
28
+ }
@@ -1,93 +1,93 @@
1
- import type { ErrorParser, ParsedError, StackFrame } from '../types.js';
2
- import path from 'node:path';
3
-
4
- const GCC_RE = /^(.+?):(\d+):(\d+): (error|warning|fatal error): (.+)/m;
5
- const JAVAC_RE = /^(.+\.java):(\d+): error: (.+)/m;
6
- const GENERIC_COMPILER_RE = /^(.+?):(\d+)(?::(\d+))?: (?:error|fatal): (.+)/m;
7
-
8
- export const compilerParser: ErrorParser = {
9
- name: 'compiler',
10
- priority: 7,
11
-
12
- canParse(input: string): boolean {
13
- return (
14
- GCC_RE.test(input) ||
15
- JAVAC_RE.test(input) ||
16
- /compilation failed|fatal error:/.test(input)
17
- );
18
- },
19
-
20
- parse(input: string): ParsedError | null {
21
- let gccMatch = GCC_RE.exec(input);
22
- if (gccMatch) {
23
- const filePath = gccMatch[1]!;
24
- const frame: StackFrame = {
25
- function_name: null,
26
- file_path: filePath,
27
- line_number: parseInt(gccMatch[2]!, 10),
28
- column_number: parseInt(gccMatch[3]!, 10),
29
- normalized: `<compiler>@${path.basename(filePath)}`,
30
- };
31
- return {
32
- errorType: gccMatch[4] === 'warning' ? 'CompilerWarning' : 'CompilerError',
33
- message: gccMatch[5]!,
34
- stackTrace: input,
35
- frames: [frame],
36
- sourceFile: filePath,
37
- sourceLine: frame.line_number,
38
- language: detectLanguage(filePath),
39
- };
40
- }
41
-
42
- const javacMatch = JAVAC_RE.exec(input);
43
- if (javacMatch) {
44
- const filePath = javacMatch[1]!;
45
- return {
46
- errorType: 'CompilerError',
47
- message: javacMatch[3]!,
48
- stackTrace: input,
49
- frames: [{
50
- function_name: null,
51
- file_path: filePath,
52
- line_number: parseInt(javacMatch[2]!, 10),
53
- column_number: null,
54
- normalized: `<compiler>@${path.basename(filePath)}`,
55
- }],
56
- sourceFile: filePath,
57
- sourceLine: parseInt(javacMatch[2]!, 10),
58
- language: 'java',
59
- };
60
- }
61
-
62
- const genericMatch = GENERIC_COMPILER_RE.exec(input);
63
- if (genericMatch) {
64
- const filePath = genericMatch[1]!;
65
- return {
66
- errorType: 'CompilerError',
67
- message: genericMatch[4]!,
68
- stackTrace: input,
69
- frames: [{
70
- function_name: null,
71
- file_path: filePath,
72
- line_number: parseInt(genericMatch[2]!, 10),
73
- column_number: genericMatch[3] ? parseInt(genericMatch[3], 10) : null,
74
- normalized: `<compiler>@${path.basename(filePath)}`,
75
- }],
76
- sourceFile: filePath,
77
- sourceLine: parseInt(genericMatch[2]!, 10),
78
- language: detectLanguage(filePath),
79
- };
80
- }
81
-
82
- return null;
83
- },
84
- };
85
-
86
- function detectLanguage(filePath: string): string {
87
- const ext = path.extname(filePath).toLowerCase();
88
- const map: Record<string, string> = {
89
- '.c': 'c', '.h': 'c', '.cpp': 'cpp', '.cc': 'cpp', '.cxx': 'cpp',
90
- '.java': 'java', '.rs': 'rust', '.go': 'go', '.swift': 'swift',
91
- };
92
- return map[ext] ?? 'unknown';
93
- }
1
+ import type { ErrorParser, ParsedError, StackFrame } from '../types.js';
2
+ import path from 'node:path';
3
+
4
+ const GCC_RE = /^(.+?):(\d+):(\d+): (error|warning|fatal error): (.+)/m;
5
+ const JAVAC_RE = /^(.+\.java):(\d+): error: (.+)/m;
6
+ const GENERIC_COMPILER_RE = /^(.+?):(\d+)(?::(\d+))?: (?:error|fatal): (.+)/m;
7
+
8
+ export const compilerParser: ErrorParser = {
9
+ name: 'compiler',
10
+ priority: 7,
11
+
12
+ canParse(input: string): boolean {
13
+ return (
14
+ GCC_RE.test(input) ||
15
+ JAVAC_RE.test(input) ||
16
+ /compilation failed|fatal error:/.test(input)
17
+ );
18
+ },
19
+
20
+ parse(input: string): ParsedError | null {
21
+ let gccMatch = GCC_RE.exec(input);
22
+ if (gccMatch) {
23
+ const filePath = gccMatch[1]!;
24
+ const frame: StackFrame = {
25
+ function_name: null,
26
+ file_path: filePath,
27
+ line_number: parseInt(gccMatch[2]!, 10),
28
+ column_number: parseInt(gccMatch[3]!, 10),
29
+ normalized: `<compiler>@${path.basename(filePath)}`,
30
+ };
31
+ return {
32
+ errorType: gccMatch[4] === 'warning' ? 'CompilerWarning' : 'CompilerError',
33
+ message: gccMatch[5]!,
34
+ stackTrace: input,
35
+ frames: [frame],
36
+ sourceFile: filePath,
37
+ sourceLine: frame.line_number,
38
+ language: detectLanguage(filePath),
39
+ };
40
+ }
41
+
42
+ const javacMatch = JAVAC_RE.exec(input);
43
+ if (javacMatch) {
44
+ const filePath = javacMatch[1]!;
45
+ return {
46
+ errorType: 'CompilerError',
47
+ message: javacMatch[3]!,
48
+ stackTrace: input,
49
+ frames: [{
50
+ function_name: null,
51
+ file_path: filePath,
52
+ line_number: parseInt(javacMatch[2]!, 10),
53
+ column_number: null,
54
+ normalized: `<compiler>@${path.basename(filePath)}`,
55
+ }],
56
+ sourceFile: filePath,
57
+ sourceLine: parseInt(javacMatch[2]!, 10),
58
+ language: 'java',
59
+ };
60
+ }
61
+
62
+ const genericMatch = GENERIC_COMPILER_RE.exec(input);
63
+ if (genericMatch) {
64
+ const filePath = genericMatch[1]!;
65
+ return {
66
+ errorType: 'CompilerError',
67
+ message: genericMatch[4]!,
68
+ stackTrace: input,
69
+ frames: [{
70
+ function_name: null,
71
+ file_path: filePath,
72
+ line_number: parseInt(genericMatch[2]!, 10),
73
+ column_number: genericMatch[3] ? parseInt(genericMatch[3], 10) : null,
74
+ normalized: `<compiler>@${path.basename(filePath)}`,
75
+ }],
76
+ sourceFile: filePath,
77
+ sourceLine: parseInt(genericMatch[2]!, 10),
78
+ language: detectLanguage(filePath),
79
+ };
80
+ }
81
+
82
+ return null;
83
+ },
84
+ };
85
+
86
+ function detectLanguage(filePath: string): string {
87
+ const ext = path.extname(filePath).toLowerCase();
88
+ const map: Record<string, string> = {
89
+ '.c': 'c', '.h': 'c', '.cpp': 'cpp', '.cc': 'cpp', '.cxx': 'cpp',
90
+ '.java': 'java', '.rs': 'rust', '.go': 'go', '.swift': 'swift',
91
+ };
92
+ return map[ext] ?? 'unknown';
93
+ }