@timmeck/brain 1.8.1 → 1.8.3

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 (164) 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/cli/commands/doctor.js +6 -1
  5. package/dist/cli/commands/doctor.js.map +1 -1
  6. package/dist/dashboard/server.js +25 -25
  7. package/dist/db/migrations/001_core_schema.js +115 -115
  8. package/dist/db/migrations/002_learning_schema.js +33 -33
  9. package/dist/db/migrations/003_code_schema.js +48 -48
  10. package/dist/db/migrations/004_synapses_schema.js +52 -52
  11. package/dist/db/migrations/005_fts_indexes.js +73 -73
  12. package/dist/db/migrations/007_feedback.js +8 -8
  13. package/dist/db/migrations/008_git_integration.js +33 -33
  14. package/dist/db/migrations/009_embeddings.js +3 -3
  15. package/dist/db/repositories/antipattern.repository.js +3 -3
  16. package/dist/db/repositories/code-module.repository.js +32 -32
  17. package/dist/db/repositories/notification.repository.js +3 -3
  18. package/dist/db/repositories/project.repository.js +21 -21
  19. package/dist/db/repositories/rule.repository.js +24 -24
  20. package/dist/db/repositories/solution.repository.js +50 -50
  21. package/dist/db/repositories/synapse.repository.js +18 -18
  22. package/dist/db/repositories/terminal.repository.js +24 -24
  23. package/dist/ipc/server.d.ts +8 -0
  24. package/dist/ipc/server.js +67 -1
  25. package/dist/ipc/server.js.map +1 -1
  26. package/dist/matching/error-matcher.js +5 -5
  27. package/dist/matching/fingerprint.js +6 -1
  28. package/dist/matching/fingerprint.js.map +1 -1
  29. package/dist/services/error.service.js +4 -3
  30. package/dist/services/error.service.js.map +1 -1
  31. package/dist/services/git.service.js +14 -14
  32. package/package.json +49 -49
  33. package/src/api/server.ts +395 -395
  34. package/src/brain.ts +266 -266
  35. package/src/cli/colors.ts +116 -116
  36. package/src/cli/commands/config.ts +169 -169
  37. package/src/cli/commands/dashboard.ts +755 -755
  38. package/src/cli/commands/doctor.ts +124 -118
  39. package/src/cli/commands/explain.ts +83 -83
  40. package/src/cli/commands/export.ts +31 -31
  41. package/src/cli/commands/import.ts +199 -199
  42. package/src/cli/commands/insights.ts +65 -65
  43. package/src/cli/commands/learn.ts +24 -24
  44. package/src/cli/commands/modules.ts +53 -53
  45. package/src/cli/commands/network.ts +67 -67
  46. package/src/cli/commands/projects.ts +42 -42
  47. package/src/cli/commands/query.ts +120 -120
  48. package/src/cli/commands/start.ts +62 -62
  49. package/src/cli/commands/status.ts +75 -75
  50. package/src/cli/commands/stop.ts +34 -34
  51. package/src/cli/ipc-helper.ts +22 -22
  52. package/src/cli/update-check.ts +63 -63
  53. package/src/code/fingerprint.ts +87 -87
  54. package/src/code/parsers/generic.ts +29 -29
  55. package/src/code/parsers/python.ts +54 -54
  56. package/src/code/parsers/typescript.ts +65 -65
  57. package/src/code/registry.ts +60 -60
  58. package/src/dashboard/server.ts +142 -142
  59. package/src/db/connection.ts +22 -22
  60. package/src/db/migrations/001_core_schema.ts +120 -120
  61. package/src/db/migrations/002_learning_schema.ts +38 -38
  62. package/src/db/migrations/003_code_schema.ts +53 -53
  63. package/src/db/migrations/004_synapses_schema.ts +57 -57
  64. package/src/db/migrations/005_fts_indexes.ts +78 -78
  65. package/src/db/migrations/006_synapses_phase3.ts +17 -17
  66. package/src/db/migrations/007_feedback.ts +13 -13
  67. package/src/db/migrations/008_git_integration.ts +38 -38
  68. package/src/db/migrations/009_embeddings.ts +8 -8
  69. package/src/db/repositories/antipattern.repository.ts +66 -66
  70. package/src/db/repositories/code-module.repository.ts +142 -142
  71. package/src/db/repositories/notification.repository.ts +66 -66
  72. package/src/db/repositories/project.repository.ts +93 -93
  73. package/src/db/repositories/rule.repository.ts +108 -108
  74. package/src/db/repositories/solution.repository.ts +154 -154
  75. package/src/db/repositories/synapse.repository.ts +153 -153
  76. package/src/db/repositories/terminal.repository.ts +101 -101
  77. package/src/embeddings/engine.ts +238 -238
  78. package/src/index.ts +63 -63
  79. package/src/ipc/client.ts +118 -118
  80. package/src/ipc/protocol.ts +35 -35
  81. package/src/ipc/router.ts +133 -133
  82. package/src/ipc/server.ts +176 -110
  83. package/src/learning/decay.ts +46 -46
  84. package/src/learning/pattern-extractor.ts +90 -90
  85. package/src/learning/rule-generator.ts +74 -74
  86. package/src/matching/error-matcher.ts +5 -5
  87. package/src/matching/fingerprint.ts +34 -29
  88. package/src/matching/similarity.ts +61 -61
  89. package/src/matching/tfidf.ts +74 -74
  90. package/src/matching/tokenizer.ts +41 -41
  91. package/src/mcp/auto-detect.ts +93 -93
  92. package/src/mcp/http-server.ts +140 -140
  93. package/src/mcp/server.ts +73 -73
  94. package/src/parsing/error-parser.ts +28 -28
  95. package/src/parsing/parsers/compiler.ts +93 -93
  96. package/src/parsing/parsers/generic.ts +28 -28
  97. package/src/parsing/parsers/go.ts +97 -97
  98. package/src/parsing/parsers/node.ts +69 -69
  99. package/src/parsing/parsers/python.ts +62 -62
  100. package/src/parsing/parsers/rust.ts +50 -50
  101. package/src/parsing/parsers/shell.ts +42 -42
  102. package/src/parsing/types.ts +47 -47
  103. package/src/research/gap-analyzer.ts +135 -135
  104. package/src/research/insight-generator.ts +123 -123
  105. package/src/research/research-engine.ts +116 -116
  106. package/src/research/synergy-detector.ts +126 -126
  107. package/src/research/template-extractor.ts +130 -130
  108. package/src/research/trend-analyzer.ts +127 -127
  109. package/src/services/code.service.ts +271 -271
  110. package/src/services/error.service.ts +4 -3
  111. package/src/services/git.service.ts +132 -132
  112. package/src/services/notification.service.ts +41 -41
  113. package/src/services/synapse.service.ts +59 -59
  114. package/src/services/terminal.service.ts +81 -81
  115. package/src/synapses/activation.ts +80 -80
  116. package/src/synapses/decay.ts +38 -38
  117. package/src/synapses/hebbian.ts +69 -69
  118. package/src/synapses/pathfinder.ts +81 -81
  119. package/src/synapses/synapse-manager.ts +109 -109
  120. package/src/types/code.types.ts +52 -52
  121. package/src/types/error.types.ts +67 -67
  122. package/src/types/ipc.types.ts +8 -8
  123. package/src/types/mcp.types.ts +53 -53
  124. package/src/types/research.types.ts +28 -28
  125. package/src/types/solution.types.ts +30 -30
  126. package/src/utils/events.ts +45 -45
  127. package/src/utils/hash.ts +5 -5
  128. package/src/utils/logger.ts +48 -48
  129. package/src/utils/paths.ts +19 -19
  130. package/tests/e2e/test_code_intelligence.py +1015 -0
  131. package/tests/e2e/test_error_memory.py +451 -0
  132. package/tests/e2e/test_full_integration.py +534 -0
  133. package/tests/fixtures/code-modules/modules.ts +83 -83
  134. package/tests/fixtures/errors/go.ts +9 -9
  135. package/tests/fixtures/errors/node.ts +24 -24
  136. package/tests/fixtures/errors/python.ts +21 -21
  137. package/tests/fixtures/errors/rust.ts +25 -25
  138. package/tests/fixtures/errors/shell.ts +15 -15
  139. package/tests/fixtures/solutions/solutions.ts +27 -27
  140. package/tests/helpers/setup-db.ts +52 -52
  141. package/tests/integration/code-flow.test.ts +86 -86
  142. package/tests/integration/error-flow.test.ts +83 -83
  143. package/tests/integration/ipc-flow.test.ts +166 -166
  144. package/tests/integration/learning-cycle.test.ts +82 -82
  145. package/tests/integration/synapse-flow.test.ts +117 -117
  146. package/tests/unit/code/analyzer.test.ts +58 -58
  147. package/tests/unit/code/fingerprint.test.ts +51 -51
  148. package/tests/unit/code/scorer.test.ts +55 -55
  149. package/tests/unit/learning/confidence-scorer.test.ts +60 -60
  150. package/tests/unit/learning/decay.test.ts +45 -45
  151. package/tests/unit/learning/pattern-extractor.test.ts +50 -50
  152. package/tests/unit/matching/error-matcher.test.ts +69 -69
  153. package/tests/unit/matching/fingerprint.test.ts +47 -47
  154. package/tests/unit/matching/similarity.test.ts +65 -65
  155. package/tests/unit/matching/tfidf.test.ts +71 -71
  156. package/tests/unit/matching/tokenizer.test.ts +83 -83
  157. package/tests/unit/parsing/parsers.test.ts +113 -113
  158. package/tests/unit/research/gap-analyzer.test.ts +45 -45
  159. package/tests/unit/research/trend-analyzer.test.ts +45 -45
  160. package/tests/unit/synapses/activation.test.ts +80 -80
  161. package/tests/unit/synapses/decay.test.ts +27 -27
  162. package/tests/unit/synapses/hebbian.test.ts +96 -96
  163. package/tests/unit/synapses/pathfinder.test.ts +72 -72
  164. 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
+ }