@timmeck/brain 1.8.1 → 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 (162) hide show
  1. package/BRAIN_PLAN.md +3324 -3324
  2. package/LICENSE +21 -21
  3. package/dist/cli/commands/dashboard.js +595 -595
  4. package/dist/dashboard/server.js +25 -25
  5. package/dist/db/migrations/001_core_schema.js +115 -115
  6. package/dist/db/migrations/002_learning_schema.js +33 -33
  7. package/dist/db/migrations/003_code_schema.js +48 -48
  8. package/dist/db/migrations/004_synapses_schema.js +52 -52
  9. package/dist/db/migrations/005_fts_indexes.js +73 -73
  10. package/dist/db/migrations/007_feedback.js +8 -8
  11. package/dist/db/migrations/008_git_integration.js +33 -33
  12. package/dist/db/migrations/009_embeddings.js +3 -3
  13. package/dist/db/repositories/antipattern.repository.js +3 -3
  14. package/dist/db/repositories/code-module.repository.js +32 -32
  15. package/dist/db/repositories/notification.repository.js +3 -3
  16. package/dist/db/repositories/project.repository.js +21 -21
  17. package/dist/db/repositories/rule.repository.js +24 -24
  18. package/dist/db/repositories/solution.repository.js +50 -50
  19. package/dist/db/repositories/synapse.repository.js +18 -18
  20. package/dist/db/repositories/terminal.repository.js +24 -24
  21. package/dist/ipc/server.d.ts +8 -0
  22. package/dist/ipc/server.js +67 -1
  23. package/dist/ipc/server.js.map +1 -1
  24. package/dist/matching/error-matcher.js +5 -5
  25. package/dist/matching/fingerprint.js +6 -1
  26. package/dist/matching/fingerprint.js.map +1 -1
  27. package/dist/services/error.service.js +4 -3
  28. package/dist/services/error.service.js.map +1 -1
  29. package/dist/services/git.service.js +14 -14
  30. package/package.json +49 -49
  31. package/src/api/server.ts +395 -395
  32. package/src/brain.ts +266 -266
  33. package/src/cli/colors.ts +116 -116
  34. package/src/cli/commands/config.ts +169 -169
  35. package/src/cli/commands/dashboard.ts +755 -755
  36. package/src/cli/commands/doctor.ts +118 -118
  37. package/src/cli/commands/explain.ts +83 -83
  38. package/src/cli/commands/export.ts +31 -31
  39. package/src/cli/commands/import.ts +199 -199
  40. package/src/cli/commands/insights.ts +65 -65
  41. package/src/cli/commands/learn.ts +24 -24
  42. package/src/cli/commands/modules.ts +53 -53
  43. package/src/cli/commands/network.ts +67 -67
  44. package/src/cli/commands/projects.ts +42 -42
  45. package/src/cli/commands/query.ts +120 -120
  46. package/src/cli/commands/start.ts +62 -62
  47. package/src/cli/commands/status.ts +75 -75
  48. package/src/cli/commands/stop.ts +34 -34
  49. package/src/cli/ipc-helper.ts +22 -22
  50. package/src/cli/update-check.ts +63 -63
  51. package/src/code/fingerprint.ts +87 -87
  52. package/src/code/parsers/generic.ts +29 -29
  53. package/src/code/parsers/python.ts +54 -54
  54. package/src/code/parsers/typescript.ts +65 -65
  55. package/src/code/registry.ts +60 -60
  56. package/src/dashboard/server.ts +142 -142
  57. package/src/db/connection.ts +22 -22
  58. package/src/db/migrations/001_core_schema.ts +120 -120
  59. package/src/db/migrations/002_learning_schema.ts +38 -38
  60. package/src/db/migrations/003_code_schema.ts +53 -53
  61. package/src/db/migrations/004_synapses_schema.ts +57 -57
  62. package/src/db/migrations/005_fts_indexes.ts +78 -78
  63. package/src/db/migrations/006_synapses_phase3.ts +17 -17
  64. package/src/db/migrations/007_feedback.ts +13 -13
  65. package/src/db/migrations/008_git_integration.ts +38 -38
  66. package/src/db/migrations/009_embeddings.ts +8 -8
  67. package/src/db/repositories/antipattern.repository.ts +66 -66
  68. package/src/db/repositories/code-module.repository.ts +142 -142
  69. package/src/db/repositories/notification.repository.ts +66 -66
  70. package/src/db/repositories/project.repository.ts +93 -93
  71. package/src/db/repositories/rule.repository.ts +108 -108
  72. package/src/db/repositories/solution.repository.ts +154 -154
  73. package/src/db/repositories/synapse.repository.ts +153 -153
  74. package/src/db/repositories/terminal.repository.ts +101 -101
  75. package/src/embeddings/engine.ts +238 -238
  76. package/src/index.ts +63 -63
  77. package/src/ipc/client.ts +118 -118
  78. package/src/ipc/protocol.ts +35 -35
  79. package/src/ipc/router.ts +133 -133
  80. package/src/ipc/server.ts +176 -110
  81. package/src/learning/decay.ts +46 -46
  82. package/src/learning/pattern-extractor.ts +90 -90
  83. package/src/learning/rule-generator.ts +74 -74
  84. package/src/matching/error-matcher.ts +5 -5
  85. package/src/matching/fingerprint.ts +34 -29
  86. package/src/matching/similarity.ts +61 -61
  87. package/src/matching/tfidf.ts +74 -74
  88. package/src/matching/tokenizer.ts +41 -41
  89. package/src/mcp/auto-detect.ts +93 -93
  90. package/src/mcp/http-server.ts +140 -140
  91. package/src/mcp/server.ts +73 -73
  92. package/src/parsing/error-parser.ts +28 -28
  93. package/src/parsing/parsers/compiler.ts +93 -93
  94. package/src/parsing/parsers/generic.ts +28 -28
  95. package/src/parsing/parsers/go.ts +97 -97
  96. package/src/parsing/parsers/node.ts +69 -69
  97. package/src/parsing/parsers/python.ts +62 -62
  98. package/src/parsing/parsers/rust.ts +50 -50
  99. package/src/parsing/parsers/shell.ts +42 -42
  100. package/src/parsing/types.ts +47 -47
  101. package/src/research/gap-analyzer.ts +135 -135
  102. package/src/research/insight-generator.ts +123 -123
  103. package/src/research/research-engine.ts +116 -116
  104. package/src/research/synergy-detector.ts +126 -126
  105. package/src/research/template-extractor.ts +130 -130
  106. package/src/research/trend-analyzer.ts +127 -127
  107. package/src/services/code.service.ts +271 -271
  108. package/src/services/error.service.ts +4 -3
  109. package/src/services/git.service.ts +132 -132
  110. package/src/services/notification.service.ts +41 -41
  111. package/src/services/synapse.service.ts +59 -59
  112. package/src/services/terminal.service.ts +81 -81
  113. package/src/synapses/activation.ts +80 -80
  114. package/src/synapses/decay.ts +38 -38
  115. package/src/synapses/hebbian.ts +69 -69
  116. package/src/synapses/pathfinder.ts +81 -81
  117. package/src/synapses/synapse-manager.ts +109 -109
  118. package/src/types/code.types.ts +52 -52
  119. package/src/types/error.types.ts +67 -67
  120. package/src/types/ipc.types.ts +8 -8
  121. package/src/types/mcp.types.ts +53 -53
  122. package/src/types/research.types.ts +28 -28
  123. package/src/types/solution.types.ts +30 -30
  124. package/src/utils/events.ts +45 -45
  125. package/src/utils/hash.ts +5 -5
  126. package/src/utils/logger.ts +48 -48
  127. package/src/utils/paths.ts +19 -19
  128. package/tests/e2e/test_code_intelligence.py +1015 -0
  129. package/tests/e2e/test_error_memory.py +451 -0
  130. package/tests/e2e/test_full_integration.py +534 -0
  131. package/tests/fixtures/code-modules/modules.ts +83 -83
  132. package/tests/fixtures/errors/go.ts +9 -9
  133. package/tests/fixtures/errors/node.ts +24 -24
  134. package/tests/fixtures/errors/python.ts +21 -21
  135. package/tests/fixtures/errors/rust.ts +25 -25
  136. package/tests/fixtures/errors/shell.ts +15 -15
  137. package/tests/fixtures/solutions/solutions.ts +27 -27
  138. package/tests/helpers/setup-db.ts +52 -52
  139. package/tests/integration/code-flow.test.ts +86 -86
  140. package/tests/integration/error-flow.test.ts +83 -83
  141. package/tests/integration/ipc-flow.test.ts +166 -166
  142. package/tests/integration/learning-cycle.test.ts +82 -82
  143. package/tests/integration/synapse-flow.test.ts +117 -117
  144. package/tests/unit/code/analyzer.test.ts +58 -58
  145. package/tests/unit/code/fingerprint.test.ts +51 -51
  146. package/tests/unit/code/scorer.test.ts +55 -55
  147. package/tests/unit/learning/confidence-scorer.test.ts +60 -60
  148. package/tests/unit/learning/decay.test.ts +45 -45
  149. package/tests/unit/learning/pattern-extractor.test.ts +50 -50
  150. package/tests/unit/matching/error-matcher.test.ts +69 -69
  151. package/tests/unit/matching/fingerprint.test.ts +47 -47
  152. package/tests/unit/matching/similarity.test.ts +65 -65
  153. package/tests/unit/matching/tfidf.test.ts +71 -71
  154. package/tests/unit/matching/tokenizer.test.ts +83 -83
  155. package/tests/unit/parsing/parsers.test.ts +113 -113
  156. package/tests/unit/research/gap-analyzer.test.ts +45 -45
  157. package/tests/unit/research/trend-analyzer.test.ts +45 -45
  158. package/tests/unit/synapses/activation.test.ts +80 -80
  159. package/tests/unit/synapses/decay.test.ts +27 -27
  160. package/tests/unit/synapses/hebbian.test.ts +96 -96
  161. package/tests/unit/synapses/pathfinder.test.ts +72 -72
  162. package/tsconfig.json +18 -18
package/src/ipc/client.ts CHANGED
@@ -1,118 +1,118 @@
1
- import net from 'node:net';
2
- import { randomUUID } from 'node:crypto';
3
- import type { IpcMessage } from '../types/ipc.types.js';
4
- import { encodeMessage, MessageDecoder } from './protocol.js';
5
- import { getPipeName } from '../utils/paths.js';
6
-
7
- interface PendingRequest {
8
- resolve: (result: unknown) => void;
9
- reject: (err: Error) => void;
10
- timer: ReturnType<typeof setTimeout>;
11
- }
12
-
13
- export class IpcClient {
14
- private socket: net.Socket | null = null;
15
- private decoder = new MessageDecoder();
16
- private pending = new Map<string, PendingRequest>();
17
- private onNotification?: (msg: IpcMessage) => void;
18
-
19
- constructor(
20
- private pipeName: string = getPipeName(),
21
- private timeout: number = 5000,
22
- ) {}
23
-
24
- connect(): Promise<void> {
25
- return new Promise((resolve, reject) => {
26
- this.socket = net.createConnection(this.pipeName, () => {
27
- resolve();
28
- });
29
-
30
- this.socket.on('data', (chunk) => {
31
- const messages = this.decoder.feed(chunk);
32
- for (const msg of messages) {
33
- this.handleMessage(msg);
34
- }
35
- });
36
-
37
- this.socket.on('error', (err) => {
38
- reject(err);
39
- // Reject all pending requests
40
- for (const [id, req] of this.pending) {
41
- clearTimeout(req.timer);
42
- req.reject(new Error(`Connection error: ${err.message}`));
43
- this.pending.delete(id);
44
- }
45
- });
46
-
47
- this.socket.on('close', () => {
48
- for (const [id, req] of this.pending) {
49
- clearTimeout(req.timer);
50
- req.reject(new Error('Connection closed'));
51
- this.pending.delete(id);
52
- }
53
- this.socket = null;
54
- });
55
- });
56
- }
57
-
58
- request(method: string, params?: unknown): Promise<unknown> {
59
- return new Promise((resolve, reject) => {
60
- if (!this.socket || this.socket.destroyed) {
61
- return reject(new Error('Not connected'));
62
- }
63
-
64
- const id = randomUUID();
65
- const timer = setTimeout(() => {
66
- this.pending.delete(id);
67
- reject(new Error(`Request timeout: ${method} (${this.timeout}ms)`));
68
- }, this.timeout);
69
-
70
- this.pending.set(id, { resolve, reject, timer });
71
-
72
- const msg: IpcMessage = {
73
- id,
74
- type: 'request',
75
- method,
76
- params,
77
- };
78
- this.socket.write(encodeMessage(msg));
79
- });
80
- }
81
-
82
- setNotificationHandler(handler: (msg: IpcMessage) => void): void {
83
- this.onNotification = handler;
84
- }
85
-
86
- disconnect(): void {
87
- for (const [id, req] of this.pending) {
88
- clearTimeout(req.timer);
89
- req.reject(new Error('Client disconnecting'));
90
- this.pending.delete(id);
91
- }
92
- this.socket?.destroy();
93
- this.socket = null;
94
- this.decoder.reset();
95
- }
96
-
97
- get connected(): boolean {
98
- return this.socket !== null && !this.socket.destroyed;
99
- }
100
-
101
- private handleMessage(msg: IpcMessage): void {
102
- if (msg.type === 'response') {
103
- const req = this.pending.get(msg.id);
104
- if (!req) return;
105
-
106
- clearTimeout(req.timer);
107
- this.pending.delete(msg.id);
108
-
109
- if (msg.error) {
110
- req.reject(new Error(msg.error.message));
111
- } else {
112
- req.resolve(msg.result);
113
- }
114
- } else if (msg.type === 'notification') {
115
- this.onNotification?.(msg);
116
- }
117
- }
118
- }
1
+ import net from 'node:net';
2
+ import { randomUUID } from 'node:crypto';
3
+ import type { IpcMessage } from '../types/ipc.types.js';
4
+ import { encodeMessage, MessageDecoder } from './protocol.js';
5
+ import { getPipeName } from '../utils/paths.js';
6
+
7
+ interface PendingRequest {
8
+ resolve: (result: unknown) => void;
9
+ reject: (err: Error) => void;
10
+ timer: ReturnType<typeof setTimeout>;
11
+ }
12
+
13
+ export class IpcClient {
14
+ private socket: net.Socket | null = null;
15
+ private decoder = new MessageDecoder();
16
+ private pending = new Map<string, PendingRequest>();
17
+ private onNotification?: (msg: IpcMessage) => void;
18
+
19
+ constructor(
20
+ private pipeName: string = getPipeName(),
21
+ private timeout: number = 5000,
22
+ ) {}
23
+
24
+ connect(): Promise<void> {
25
+ return new Promise((resolve, reject) => {
26
+ this.socket = net.createConnection(this.pipeName, () => {
27
+ resolve();
28
+ });
29
+
30
+ this.socket.on('data', (chunk) => {
31
+ const messages = this.decoder.feed(chunk);
32
+ for (const msg of messages) {
33
+ this.handleMessage(msg);
34
+ }
35
+ });
36
+
37
+ this.socket.on('error', (err) => {
38
+ reject(err);
39
+ // Reject all pending requests
40
+ for (const [id, req] of this.pending) {
41
+ clearTimeout(req.timer);
42
+ req.reject(new Error(`Connection error: ${err.message}`));
43
+ this.pending.delete(id);
44
+ }
45
+ });
46
+
47
+ this.socket.on('close', () => {
48
+ for (const [id, req] of this.pending) {
49
+ clearTimeout(req.timer);
50
+ req.reject(new Error('Connection closed'));
51
+ this.pending.delete(id);
52
+ }
53
+ this.socket = null;
54
+ });
55
+ });
56
+ }
57
+
58
+ request(method: string, params?: unknown): Promise<unknown> {
59
+ return new Promise((resolve, reject) => {
60
+ if (!this.socket || this.socket.destroyed) {
61
+ return reject(new Error('Not connected'));
62
+ }
63
+
64
+ const id = randomUUID();
65
+ const timer = setTimeout(() => {
66
+ this.pending.delete(id);
67
+ reject(new Error(`Request timeout: ${method} (${this.timeout}ms)`));
68
+ }, this.timeout);
69
+
70
+ this.pending.set(id, { resolve, reject, timer });
71
+
72
+ const msg: IpcMessage = {
73
+ id,
74
+ type: 'request',
75
+ method,
76
+ params,
77
+ };
78
+ this.socket.write(encodeMessage(msg));
79
+ });
80
+ }
81
+
82
+ setNotificationHandler(handler: (msg: IpcMessage) => void): void {
83
+ this.onNotification = handler;
84
+ }
85
+
86
+ disconnect(): void {
87
+ for (const [id, req] of this.pending) {
88
+ clearTimeout(req.timer);
89
+ req.reject(new Error('Client disconnecting'));
90
+ this.pending.delete(id);
91
+ }
92
+ this.socket?.destroy();
93
+ this.socket = null;
94
+ this.decoder.reset();
95
+ }
96
+
97
+ get connected(): boolean {
98
+ return this.socket !== null && !this.socket.destroyed;
99
+ }
100
+
101
+ private handleMessage(msg: IpcMessage): void {
102
+ if (msg.type === 'response') {
103
+ const req = this.pending.get(msg.id);
104
+ if (!req) return;
105
+
106
+ clearTimeout(req.timer);
107
+ this.pending.delete(msg.id);
108
+
109
+ if (msg.error) {
110
+ req.reject(new Error(msg.error.message));
111
+ } else {
112
+ req.resolve(msg.result);
113
+ }
114
+ } else if (msg.type === 'notification') {
115
+ this.onNotification?.(msg);
116
+ }
117
+ }
118
+ }
@@ -1,35 +1,35 @@
1
- import { Buffer } from 'node:buffer';
2
- import type { IpcMessage } from '../types/ipc.types.js';
3
-
4
- export function encodeMessage(msg: IpcMessage): Buffer {
5
- const json = JSON.stringify(msg);
6
- const payload = Buffer.from(json, 'utf8');
7
- const frame = Buffer.alloc(4 + payload.length);
8
- frame.writeUInt32BE(payload.length, 0);
9
- payload.copy(frame, 4);
10
- return frame;
11
- }
12
-
13
- export class MessageDecoder {
14
- private buffer = Buffer.alloc(0);
15
-
16
- feed(chunk: Buffer): IpcMessage[] {
17
- this.buffer = Buffer.concat([this.buffer, chunk]);
18
- const messages: IpcMessage[] = [];
19
-
20
- while (this.buffer.length >= 4) {
21
- const length = this.buffer.readUInt32BE(0);
22
- if (this.buffer.length < 4 + length) break;
23
-
24
- const json = this.buffer.subarray(4, 4 + length).toString('utf8');
25
- this.buffer = this.buffer.subarray(4 + length);
26
- messages.push(JSON.parse(json) as IpcMessage);
27
- }
28
-
29
- return messages;
30
- }
31
-
32
- reset(): void {
33
- this.buffer = Buffer.alloc(0);
34
- }
35
- }
1
+ import { Buffer } from 'node:buffer';
2
+ import type { IpcMessage } from '../types/ipc.types.js';
3
+
4
+ export function encodeMessage(msg: IpcMessage): Buffer {
5
+ const json = JSON.stringify(msg);
6
+ const payload = Buffer.from(json, 'utf8');
7
+ const frame = Buffer.alloc(4 + payload.length);
8
+ frame.writeUInt32BE(payload.length, 0);
9
+ payload.copy(frame, 4);
10
+ return frame;
11
+ }
12
+
13
+ export class MessageDecoder {
14
+ private buffer = Buffer.alloc(0);
15
+
16
+ feed(chunk: Buffer): IpcMessage[] {
17
+ this.buffer = Buffer.concat([this.buffer, chunk]);
18
+ const messages: IpcMessage[] = [];
19
+
20
+ while (this.buffer.length >= 4) {
21
+ const length = this.buffer.readUInt32BE(0);
22
+ if (this.buffer.length < 4 + length) break;
23
+
24
+ const json = this.buffer.subarray(4, 4 + length).toString('utf8');
25
+ this.buffer = this.buffer.subarray(4 + length);
26
+ messages.push(JSON.parse(json) as IpcMessage);
27
+ }
28
+
29
+ return messages;
30
+ }
31
+
32
+ reset(): void {
33
+ this.buffer = Buffer.alloc(0);
34
+ }
35
+ }
package/src/ipc/router.ts CHANGED
@@ -1,133 +1,133 @@
1
- import { getLogger } from '../utils/logger.js';
2
-
3
- const logger = getLogger();
4
- import type { ErrorService } from '../services/error.service.js';
5
- import type { SolutionService } from '../services/solution.service.js';
6
- import type { TerminalService } from '../services/terminal.service.js';
7
- import type { PreventionService } from '../services/prevention.service.js';
8
- import type { CodeService } from '../services/code.service.js';
9
- import type { SynapseService } from '../services/synapse.service.js';
10
- import type { ResearchService } from '../services/research.service.js';
11
- import type { NotificationService } from '../services/notification.service.js';
12
- import type { AnalyticsService } from '../services/analytics.service.js';
13
- import type { GitService } from '../services/git.service.js';
14
- import type { LearningEngine, LearningCycleResult } from '../learning/learning-engine.js';
15
-
16
- export interface Services {
17
- error: ErrorService;
18
- solution: SolutionService;
19
- terminal: TerminalService;
20
- prevention: PreventionService;
21
- code: CodeService;
22
- synapse: SynapseService;
23
- research: ResearchService;
24
- notification: NotificationService;
25
- analytics: AnalyticsService;
26
- git: GitService;
27
- learning?: LearningEngine;
28
- }
29
-
30
- type MethodHandler = (params: unknown) => unknown;
31
-
32
- export class IpcRouter {
33
- private methods: Map<string, MethodHandler>;
34
-
35
- constructor(private services: Services) {
36
- this.methods = this.buildMethodMap();
37
- }
38
-
39
- handle(method: string, params: unknown): unknown {
40
- const handler = this.methods.get(method);
41
- if (!handler) {
42
- throw new Error(`Unknown method: ${method}`);
43
- }
44
-
45
- logger.debug(`IPC: ${method}`, { params });
46
- const result = handler(params);
47
- logger.debug(`IPC: ${method} → done`);
48
- return result;
49
- }
50
-
51
- listMethods(): string[] {
52
- return [...this.methods.keys()];
53
- }
54
-
55
- private buildMethodMap(): Map<string, MethodHandler> {
56
- const s = this.services;
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
- const p = (params: unknown) => params as any;
59
-
60
- return new Map<string, MethodHandler>([
61
- // Terminal Lifecycle
62
- ['terminal.register', (params) => s.terminal.register(p(params))],
63
- ['terminal.heartbeat', (params) => s.terminal.heartbeat(p(params).uuid)],
64
- ['terminal.disconnect', (params) => s.terminal.disconnect(p(params).uuid)],
65
-
66
- // Error Brain
67
- ['error.report', (params) => s.error.report(p(params))],
68
- ['error.query', (params) => s.error.query(p(params))],
69
- ['error.match', (params) => s.error.matchSimilar(p(params).error_id ?? p(params).errorId)],
70
- ['error.resolve', (params) => s.error.resolve(p(params).error_id ?? p(params).errorId, p(params).solution_id ?? p(params).solutionId)],
71
- ['error.get', (params) => s.error.getById(p(params).id)],
72
- ['error.chain', (params) => s.error.getErrorChain(p(params).error_id ?? p(params).errorId ?? p(params).id)],
73
-
74
- // Solutions
75
- ['solution.report', (params) => s.solution.report(p(params))],
76
- ['solution.query', (params) => s.solution.findForError(p(params).error_id ?? p(params).errorId)],
77
- ['solution.rate', (params) => s.solution.rateOutcome(p(params))],
78
- ['solution.attempt', (params) => s.solution.rateOutcome({ ...p(params), success: false })],
79
- ['solution.efficiency', () => s.solution.analyzeEfficiency()],
80
-
81
- // Projects
82
- ['project.list', () => s.code.listProjects()],
83
-
84
- // Code Brain
85
- ['code.analyze', (params) => s.code.analyzeAndRegister(p(params))],
86
- ['code.find', (params) => s.code.findReusable(p(params))],
87
- ['code.similarity', (params) => s.code.checkSimilarity(p(params).source, p(params).language)],
88
- ['code.modules', (params) => s.code.listModules(p(params)?.projectId, p(params)?.language, p(params)?.limit)],
89
- ['code.get', (params) => s.code.getById(p(params).id)],
90
-
91
- // Prevention
92
- ['prevention.check', (params) => s.prevention.checkRules(p(params).errorType, p(params).message, p(params).projectId)],
93
- ['prevention.antipatterns', (params) => s.prevention.checkAntipatterns(p(params).errorType ?? '', p(params).message ?? p(params).error_output ?? '', p(params).projectId)],
94
- ['prevention.checkCode', (params) => s.prevention.checkCodeForPatterns(p(params).source, p(params).filePath)],
95
-
96
- // Synapses
97
- ['synapse.context', (params) => s.synapse.getErrorContext(p(params).errorId ?? p(params).error_id ?? p(params).node_id)],
98
- ['synapse.path', (params) => s.synapse.findPath(p(params).from_type ?? p(params).fromType, p(params).from_id ?? p(params).fromId, p(params).to_type ?? p(params).toType, p(params).to_id ?? p(params).toId)],
99
- ['synapse.related', (params) => s.synapse.getRelated(p(params))],
100
- ['synapse.stats', () => s.synapse.getNetworkStats()],
101
-
102
- // Research / Insights
103
- ['research.insights', (params) => s.research.getInsights(p(params))],
104
- ['insight.rate', (params) => s.research.rateInsight(p(params).id, p(params).rating, p(params).comment)],
105
- ['research.suggest', (params) => s.research.getInsights({ limit: 10, activeOnly: true, ...p(params) })],
106
- ['research.trends', (params) => s.research.getTrends(p(params)?.projectId, p(params)?.windowDays)],
107
-
108
- // Notifications
109
- ['notification.list', (params) => s.notification.list(p(params)?.projectId)],
110
- ['notification.ack', (params) => s.notification.acknowledge(p(params).id)],
111
-
112
- // Analytics
113
- ['analytics.summary', (params) => s.analytics.getSummary(p(params)?.projectId)],
114
- ['analytics.network', (params) => s.analytics.getNetworkOverview(p(params)?.limit)],
115
- ['analytics.health', (params) => s.analytics.computeHealthScore(p(params)?.projectId)],
116
- ['analytics.timeline', (params) => s.analytics.getTimeSeries(p(params)?.projectId, p(params)?.days)],
117
- ['analytics.explain', (params) => s.analytics.explainError(p(params).errorId ?? p(params).error_id)],
118
-
119
- // Git
120
- ['git.context', (params) => s.git.getGitContext(p(params)?.cwd)],
121
- ['git.linkError', (params) => s.git.linkErrorToCommit(p(params).errorId, p(params).projectId, p(params).commitHash, p(params).relationship)],
122
- ['git.errorCommits', (params) => s.git.findIntroducingCommit(p(params).errorId ?? p(params).error_id)],
123
- ['git.commitErrors', (params) => s.git.findErrorsByCommit(p(params).commitHash ?? p(params).commit_hash)],
124
- ['git.diff', (params) => s.git.captureDiff(p(params)?.cwd)],
125
-
126
- // Learning
127
- ['learning.run', () => {
128
- if (!s.learning) throw new Error('Learning engine not available');
129
- return s.learning.runCycle();
130
- }],
131
- ]);
132
- }
133
- }
1
+ import { getLogger } from '../utils/logger.js';
2
+
3
+ const logger = getLogger();
4
+ import type { ErrorService } from '../services/error.service.js';
5
+ import type { SolutionService } from '../services/solution.service.js';
6
+ import type { TerminalService } from '../services/terminal.service.js';
7
+ import type { PreventionService } from '../services/prevention.service.js';
8
+ import type { CodeService } from '../services/code.service.js';
9
+ import type { SynapseService } from '../services/synapse.service.js';
10
+ import type { ResearchService } from '../services/research.service.js';
11
+ import type { NotificationService } from '../services/notification.service.js';
12
+ import type { AnalyticsService } from '../services/analytics.service.js';
13
+ import type { GitService } from '../services/git.service.js';
14
+ import type { LearningEngine, LearningCycleResult } from '../learning/learning-engine.js';
15
+
16
+ export interface Services {
17
+ error: ErrorService;
18
+ solution: SolutionService;
19
+ terminal: TerminalService;
20
+ prevention: PreventionService;
21
+ code: CodeService;
22
+ synapse: SynapseService;
23
+ research: ResearchService;
24
+ notification: NotificationService;
25
+ analytics: AnalyticsService;
26
+ git: GitService;
27
+ learning?: LearningEngine;
28
+ }
29
+
30
+ type MethodHandler = (params: unknown) => unknown;
31
+
32
+ export class IpcRouter {
33
+ private methods: Map<string, MethodHandler>;
34
+
35
+ constructor(private services: Services) {
36
+ this.methods = this.buildMethodMap();
37
+ }
38
+
39
+ handle(method: string, params: unknown): unknown {
40
+ const handler = this.methods.get(method);
41
+ if (!handler) {
42
+ throw new Error(`Unknown method: ${method}`);
43
+ }
44
+
45
+ logger.debug(`IPC: ${method}`, { params });
46
+ const result = handler(params);
47
+ logger.debug(`IPC: ${method} → done`);
48
+ return result;
49
+ }
50
+
51
+ listMethods(): string[] {
52
+ return [...this.methods.keys()];
53
+ }
54
+
55
+ private buildMethodMap(): Map<string, MethodHandler> {
56
+ const s = this.services;
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ const p = (params: unknown) => params as any;
59
+
60
+ return new Map<string, MethodHandler>([
61
+ // Terminal Lifecycle
62
+ ['terminal.register', (params) => s.terminal.register(p(params))],
63
+ ['terminal.heartbeat', (params) => s.terminal.heartbeat(p(params).uuid)],
64
+ ['terminal.disconnect', (params) => s.terminal.disconnect(p(params).uuid)],
65
+
66
+ // Error Brain
67
+ ['error.report', (params) => s.error.report(p(params))],
68
+ ['error.query', (params) => s.error.query(p(params))],
69
+ ['error.match', (params) => s.error.matchSimilar(p(params).error_id ?? p(params).errorId)],
70
+ ['error.resolve', (params) => s.error.resolve(p(params).error_id ?? p(params).errorId, p(params).solution_id ?? p(params).solutionId)],
71
+ ['error.get', (params) => s.error.getById(p(params).id)],
72
+ ['error.chain', (params) => s.error.getErrorChain(p(params).error_id ?? p(params).errorId ?? p(params).id)],
73
+
74
+ // Solutions
75
+ ['solution.report', (params) => s.solution.report(p(params))],
76
+ ['solution.query', (params) => s.solution.findForError(p(params).error_id ?? p(params).errorId)],
77
+ ['solution.rate', (params) => s.solution.rateOutcome(p(params))],
78
+ ['solution.attempt', (params) => s.solution.rateOutcome({ ...p(params), success: false })],
79
+ ['solution.efficiency', () => s.solution.analyzeEfficiency()],
80
+
81
+ // Projects
82
+ ['project.list', () => s.code.listProjects()],
83
+
84
+ // Code Brain
85
+ ['code.analyze', (params) => s.code.analyzeAndRegister(p(params))],
86
+ ['code.find', (params) => s.code.findReusable(p(params))],
87
+ ['code.similarity', (params) => s.code.checkSimilarity(p(params).source, p(params).language)],
88
+ ['code.modules', (params) => s.code.listModules(p(params)?.projectId, p(params)?.language, p(params)?.limit)],
89
+ ['code.get', (params) => s.code.getById(p(params).id)],
90
+
91
+ // Prevention
92
+ ['prevention.check', (params) => s.prevention.checkRules(p(params).errorType, p(params).message, p(params).projectId)],
93
+ ['prevention.antipatterns', (params) => s.prevention.checkAntipatterns(p(params).errorType ?? '', p(params).message ?? p(params).error_output ?? '', p(params).projectId)],
94
+ ['prevention.checkCode', (params) => s.prevention.checkCodeForPatterns(p(params).source, p(params).filePath)],
95
+
96
+ // Synapses
97
+ ['synapse.context', (params) => s.synapse.getErrorContext(p(params).errorId ?? p(params).error_id ?? p(params).node_id)],
98
+ ['synapse.path', (params) => s.synapse.findPath(p(params).from_type ?? p(params).fromType, p(params).from_id ?? p(params).fromId, p(params).to_type ?? p(params).toType, p(params).to_id ?? p(params).toId)],
99
+ ['synapse.related', (params) => s.synapse.getRelated(p(params))],
100
+ ['synapse.stats', () => s.synapse.getNetworkStats()],
101
+
102
+ // Research / Insights
103
+ ['research.insights', (params) => s.research.getInsights(p(params))],
104
+ ['insight.rate', (params) => s.research.rateInsight(p(params).id, p(params).rating, p(params).comment)],
105
+ ['research.suggest', (params) => s.research.getInsights({ limit: 10, activeOnly: true, ...p(params) })],
106
+ ['research.trends', (params) => s.research.getTrends(p(params)?.projectId, p(params)?.windowDays)],
107
+
108
+ // Notifications
109
+ ['notification.list', (params) => s.notification.list(p(params)?.projectId)],
110
+ ['notification.ack', (params) => s.notification.acknowledge(p(params).id)],
111
+
112
+ // Analytics
113
+ ['analytics.summary', (params) => s.analytics.getSummary(p(params)?.projectId)],
114
+ ['analytics.network', (params) => s.analytics.getNetworkOverview(p(params)?.limit)],
115
+ ['analytics.health', (params) => s.analytics.computeHealthScore(p(params)?.projectId)],
116
+ ['analytics.timeline', (params) => s.analytics.getTimeSeries(p(params)?.projectId, p(params)?.days)],
117
+ ['analytics.explain', (params) => s.analytics.explainError(p(params).errorId ?? p(params).error_id)],
118
+
119
+ // Git
120
+ ['git.context', (params) => s.git.getGitContext(p(params)?.cwd)],
121
+ ['git.linkError', (params) => s.git.linkErrorToCommit(p(params).errorId, p(params).projectId, p(params).commitHash, p(params).relationship)],
122
+ ['git.errorCommits', (params) => s.git.findIntroducingCommit(p(params).errorId ?? p(params).error_id)],
123
+ ['git.commitErrors', (params) => s.git.findErrorsByCommit(p(params).commitHash ?? p(params).commit_hash)],
124
+ ['git.diff', (params) => s.git.captureDiff(p(params)?.cwd)],
125
+
126
+ // Learning
127
+ ['learning.run', () => {
128
+ if (!s.learning) throw new Error('Learning engine not available');
129
+ return s.learning.runCycle();
130
+ }],
131
+ ]);
132
+ }
133
+ }