@timmeck/brain 1.0.0 → 1.1.1

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 (202) hide show
  1. package/BRAIN_PLAN.md +3324 -3324
  2. package/LICENSE +21 -21
  3. package/README.md +194 -188
  4. package/dist/brain.js +2 -0
  5. package/dist/brain.js.map +1 -1
  6. package/dist/cli/colors.d.ts +50 -0
  7. package/dist/cli/colors.js +106 -0
  8. package/dist/cli/colors.js.map +1 -0
  9. package/dist/cli/commands/config.d.ts +2 -0
  10. package/dist/cli/commands/config.js +165 -0
  11. package/dist/cli/commands/config.js.map +1 -0
  12. package/dist/cli/commands/dashboard.js +222 -8
  13. package/dist/cli/commands/dashboard.js.map +1 -1
  14. package/dist/cli/commands/export.js +3 -0
  15. package/dist/cli/commands/export.js.map +1 -1
  16. package/dist/cli/commands/import.js +24 -15
  17. package/dist/cli/commands/import.js.map +1 -1
  18. package/dist/cli/commands/insights.js +33 -6
  19. package/dist/cli/commands/insights.js.map +1 -1
  20. package/dist/cli/commands/learn.d.ts +2 -0
  21. package/dist/cli/commands/learn.js +22 -0
  22. package/dist/cli/commands/learn.js.map +1 -0
  23. package/dist/cli/commands/modules.js +25 -6
  24. package/dist/cli/commands/modules.js.map +1 -1
  25. package/dist/cli/commands/network.js +15 -9
  26. package/dist/cli/commands/network.js.map +1 -1
  27. package/dist/cli/commands/query.js +92 -25
  28. package/dist/cli/commands/query.js.map +1 -1
  29. package/dist/cli/commands/start.js +8 -5
  30. package/dist/cli/commands/start.js.map +1 -1
  31. package/dist/cli/commands/status.js +21 -16
  32. package/dist/cli/commands/status.js.map +1 -1
  33. package/dist/cli/commands/stop.js +5 -4
  34. package/dist/cli/commands/stop.js.map +1 -1
  35. package/dist/cli/ipc-helper.js +4 -3
  36. package/dist/cli/ipc-helper.js.map +1 -1
  37. package/dist/cli/update-check.d.ts +2 -0
  38. package/dist/cli/update-check.js +58 -0
  39. package/dist/cli/update-check.js.map +1 -0
  40. package/dist/db/migrations/001_core_schema.js +115 -115
  41. package/dist/db/migrations/002_learning_schema.js +33 -33
  42. package/dist/db/migrations/003_code_schema.js +48 -48
  43. package/dist/db/migrations/004_synapses_schema.js +52 -52
  44. package/dist/db/migrations/005_fts_indexes.js +73 -73
  45. package/dist/db/migrations/index.js +6 -6
  46. package/dist/db/repositories/antipattern.repository.js +3 -3
  47. package/dist/db/repositories/code-module.repository.d.ts +1 -0
  48. package/dist/db/repositories/code-module.repository.js +8 -0
  49. package/dist/db/repositories/code-module.repository.js.map +1 -1
  50. package/dist/db/repositories/error.repository.js +46 -46
  51. package/dist/db/repositories/insight.repository.js +3 -3
  52. package/dist/db/repositories/notification.repository.js +3 -3
  53. package/dist/db/repositories/project.repository.js +21 -21
  54. package/dist/db/repositories/rule.repository.js +24 -24
  55. package/dist/db/repositories/solution.repository.js +50 -50
  56. package/dist/db/repositories/synapse.repository.js +18 -18
  57. package/dist/db/repositories/terminal.repository.js +24 -24
  58. package/dist/index.js +4 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/ipc/router.d.ts +2 -0
  61. package/dist/ipc/router.js +7 -1
  62. package/dist/ipc/router.js.map +1 -1
  63. package/dist/services/code.service.d.ts +1 -1
  64. package/dist/services/code.service.js +5 -2
  65. package/dist/services/code.service.js.map +1 -1
  66. package/package.json +5 -4
  67. package/src/brain.ts +3 -0
  68. package/src/cli/colors.ts +116 -0
  69. package/src/cli/commands/config.ts +169 -0
  70. package/src/cli/commands/dashboard.ts +231 -8
  71. package/src/cli/commands/export.ts +4 -0
  72. package/src/cli/commands/import.ts +24 -15
  73. package/src/cli/commands/insights.ts +37 -5
  74. package/src/cli/commands/learn.ts +24 -0
  75. package/src/cli/commands/modules.ts +28 -5
  76. package/src/cli/commands/network.ts +15 -9
  77. package/src/cli/commands/query.ts +103 -26
  78. package/src/cli/commands/start.ts +8 -5
  79. package/src/cli/commands/status.ts +22 -16
  80. package/src/cli/commands/stop.ts +5 -4
  81. package/src/cli/ipc-helper.ts +4 -3
  82. package/src/cli/update-check.ts +63 -0
  83. package/src/code/analyzer.ts +77 -77
  84. package/src/code/fingerprint.ts +87 -87
  85. package/src/code/matcher.ts +64 -64
  86. package/src/code/parsers/generic.ts +29 -29
  87. package/src/code/parsers/python.ts +54 -54
  88. package/src/code/parsers/typescript.ts +65 -65
  89. package/src/code/registry.ts +60 -60
  90. package/src/code/scorer.ts +108 -108
  91. package/src/config.ts +111 -111
  92. package/src/db/connection.ts +22 -22
  93. package/src/db/migrations/001_core_schema.ts +120 -120
  94. package/src/db/migrations/002_learning_schema.ts +38 -38
  95. package/src/db/migrations/003_code_schema.ts +53 -53
  96. package/src/db/migrations/004_synapses_schema.ts +57 -57
  97. package/src/db/migrations/005_fts_indexes.ts +78 -78
  98. package/src/db/migrations/006_synapses_phase3.ts +17 -17
  99. package/src/db/migrations/index.ts +64 -64
  100. package/src/db/repositories/antipattern.repository.ts +66 -66
  101. package/src/db/repositories/code-module.repository.ts +9 -0
  102. package/src/db/repositories/error.repository.ts +149 -149
  103. package/src/db/repositories/insight.repository.ts +78 -78
  104. package/src/db/repositories/notification.repository.ts +66 -66
  105. package/src/db/repositories/project.repository.ts +93 -93
  106. package/src/db/repositories/rule.repository.ts +108 -108
  107. package/src/db/repositories/solution.repository.ts +154 -154
  108. package/src/db/repositories/synapse.repository.ts +153 -153
  109. package/src/db/repositories/terminal.repository.ts +101 -101
  110. package/src/hooks/post-tool-use.ts +90 -90
  111. package/src/hooks/post-write.ts +117 -117
  112. package/src/index.ts +4 -0
  113. package/src/ipc/client.ts +118 -118
  114. package/src/ipc/protocol.ts +35 -35
  115. package/src/ipc/router.ts +9 -1
  116. package/src/ipc/server.ts +110 -110
  117. package/src/learning/confidence-scorer.ts +47 -47
  118. package/src/learning/decay.ts +46 -46
  119. package/src/learning/learning-engine.ts +162 -162
  120. package/src/learning/pattern-extractor.ts +90 -90
  121. package/src/learning/rule-generator.ts +74 -74
  122. package/src/matching/error-matcher.ts +115 -115
  123. package/src/matching/fingerprint.ts +29 -29
  124. package/src/matching/similarity.ts +61 -61
  125. package/src/matching/tfidf.ts +74 -74
  126. package/src/matching/tokenizer.ts +41 -41
  127. package/src/mcp/auto-detect.ts +93 -93
  128. package/src/mcp/server.ts +73 -73
  129. package/src/mcp/tools.ts +290 -290
  130. package/src/parsing/error-parser.ts +28 -28
  131. package/src/parsing/parsers/compiler.ts +93 -93
  132. package/src/parsing/parsers/generic.ts +28 -28
  133. package/src/parsing/parsers/go.ts +97 -97
  134. package/src/parsing/parsers/node.ts +69 -69
  135. package/src/parsing/parsers/python.ts +62 -62
  136. package/src/parsing/parsers/rust.ts +50 -50
  137. package/src/parsing/parsers/shell.ts +42 -42
  138. package/src/parsing/types.ts +47 -47
  139. package/src/research/gap-analyzer.ts +135 -135
  140. package/src/research/insight-generator.ts +123 -123
  141. package/src/research/research-engine.ts +116 -116
  142. package/src/research/synergy-detector.ts +126 -126
  143. package/src/research/template-extractor.ts +130 -130
  144. package/src/research/trend-analyzer.ts +127 -127
  145. package/src/services/analytics.service.ts +87 -87
  146. package/src/services/code.service.ts +5 -2
  147. package/src/services/error.service.ts +164 -164
  148. package/src/services/notification.service.ts +41 -41
  149. package/src/services/prevention.service.ts +119 -119
  150. package/src/services/research.service.ts +93 -93
  151. package/src/services/solution.service.ts +116 -116
  152. package/src/services/synapse.service.ts +59 -59
  153. package/src/services/terminal.service.ts +81 -81
  154. package/src/synapses/activation.ts +80 -80
  155. package/src/synapses/decay.ts +38 -38
  156. package/src/synapses/hebbian.ts +69 -69
  157. package/src/synapses/pathfinder.ts +81 -81
  158. package/src/synapses/synapse-manager.ts +109 -109
  159. package/src/types/code.types.ts +52 -52
  160. package/src/types/config.types.ts +79 -79
  161. package/src/types/error.types.ts +67 -67
  162. package/src/types/ipc.types.ts +8 -8
  163. package/src/types/mcp.types.ts +53 -53
  164. package/src/types/research.types.ts +28 -28
  165. package/src/types/solution.types.ts +30 -30
  166. package/src/types/synapse.types.ts +49 -49
  167. package/src/utils/events.ts +45 -45
  168. package/src/utils/hash.ts +5 -5
  169. package/src/utils/logger.ts +48 -48
  170. package/src/utils/paths.ts +19 -19
  171. package/tests/fixtures/code-modules/modules.ts +83 -83
  172. package/tests/fixtures/errors/go.ts +9 -9
  173. package/tests/fixtures/errors/node.ts +24 -24
  174. package/tests/fixtures/errors/python.ts +21 -21
  175. package/tests/fixtures/errors/rust.ts +25 -25
  176. package/tests/fixtures/errors/shell.ts +15 -15
  177. package/tests/fixtures/solutions/solutions.ts +27 -27
  178. package/tests/helpers/setup-db.ts +52 -52
  179. package/tests/integration/code-flow.test.ts +86 -86
  180. package/tests/integration/error-flow.test.ts +83 -83
  181. package/tests/integration/ipc-flow.test.ts +166 -166
  182. package/tests/integration/learning-cycle.test.ts +82 -82
  183. package/tests/integration/synapse-flow.test.ts +117 -117
  184. package/tests/unit/code/analyzer.test.ts +58 -58
  185. package/tests/unit/code/fingerprint.test.ts +51 -51
  186. package/tests/unit/code/scorer.test.ts +55 -55
  187. package/tests/unit/learning/confidence-scorer.test.ts +60 -60
  188. package/tests/unit/learning/decay.test.ts +45 -45
  189. package/tests/unit/learning/pattern-extractor.test.ts +50 -50
  190. package/tests/unit/matching/error-matcher.test.ts +69 -69
  191. package/tests/unit/matching/fingerprint.test.ts +47 -47
  192. package/tests/unit/matching/similarity.test.ts +65 -65
  193. package/tests/unit/matching/tfidf.test.ts +71 -71
  194. package/tests/unit/matching/tokenizer.test.ts +83 -83
  195. package/tests/unit/parsing/parsers.test.ts +113 -113
  196. package/tests/unit/research/gap-analyzer.test.ts +45 -45
  197. package/tests/unit/research/trend-analyzer.test.ts +45 -45
  198. package/tests/unit/synapses/activation.test.ts +80 -80
  199. package/tests/unit/synapses/decay.test.ts +27 -27
  200. package/tests/unit/synapses/hebbian.test.ts +96 -96
  201. package/tests/unit/synapses/pathfinder.test.ts +72 -72
  202. package/tsconfig.json +18 -18
@@ -1,164 +1,164 @@
1
- import type { ErrorRecord } from '../types/error.types.js';
2
- import type { ErrorRepository } from '../db/repositories/error.repository.js';
3
- import type { ProjectRepository } from '../db/repositories/project.repository.js';
4
- import type { SynapseManager } from '../synapses/synapse-manager.js';
5
- import { parseError } from '../parsing/error-parser.js';
6
- import { generateFingerprint } from '../matching/fingerprint.js';
7
- import { matchError, type MatchResult } from '../matching/error-matcher.js';
8
- import { getEventBus } from '../utils/events.js';
9
- import { getLogger } from '../utils/logger.js';
10
-
11
- export interface ReportErrorInput {
12
- project: string;
13
- errorOutput: string;
14
- filePath?: string;
15
- terminalId?: number;
16
- }
17
-
18
- export interface ErrorQueryInput {
19
- projectId?: number;
20
- resolved?: boolean;
21
- search?: string;
22
- limit?: number;
23
- }
24
-
25
- export class ErrorService {
26
- private logger = getLogger();
27
- private eventBus = getEventBus();
28
-
29
- constructor(
30
- private errorRepo: ErrorRepository,
31
- private projectRepo: ProjectRepository,
32
- private synapseManager: SynapseManager,
33
- ) {}
34
-
35
- report(input: ReportErrorInput): { errorId: number; isNew: boolean; matches: MatchResult[] } {
36
- // 1. Ensure project exists
37
- let project = this.projectRepo.findByName(input.project);
38
- if (!project) {
39
- const id = this.projectRepo.create({ name: input.project, path: null, language: null, framework: null });
40
- project = this.projectRepo.getById(id)!;
41
- }
42
-
43
- // 2. Parse the error
44
- const parsed = parseError(input.errorOutput);
45
- if (!parsed) {
46
- this.logger.warn('Could not parse error output');
47
- const errorId = this.errorRepo.create({
48
- project_id: project.id,
49
- terminal_id: input.terminalId ?? null,
50
- fingerprint: '',
51
- type: 'UnknownError',
52
- message: input.errorOutput.split('\n')[0] ?? input.errorOutput,
53
- raw_output: input.errorOutput,
54
- context: null,
55
- file_path: input.filePath ?? null,
56
- line_number: null,
57
- column_number: null,
58
- });
59
- return { errorId, isNew: true, matches: [] };
60
- }
61
-
62
- // 3. Generate fingerprint
63
- const fingerprint = generateFingerprint(parsed.errorType, parsed.message, parsed.frames);
64
-
65
- // 4. Check for existing error with same fingerprint
66
- const existing = this.errorRepo.findByFingerprint(fingerprint);
67
- if (existing.length > 0) {
68
- const err = existing[0]!;
69
- this.errorRepo.incrementOccurrence(err.id);
70
- this.logger.info(`Known error (id=${err.id}), occurrence incremented`);
71
-
72
- // Strengthen synapse
73
- this.synapseManager.strengthen(
74
- { type: 'error', id: err.id },
75
- { type: 'project', id: project.id },
76
- 'co_occurs',
77
- );
78
-
79
- return { errorId: err.id, isNew: false, matches: [] };
80
- }
81
-
82
- // 5. Create new error record
83
- const errorId = this.errorRepo.create({
84
- project_id: project.id,
85
- terminal_id: input.terminalId ?? null,
86
- fingerprint,
87
- type: parsed.errorType,
88
- message: parsed.message,
89
- raw_output: input.errorOutput,
90
- context: null,
91
- file_path: parsed.sourceFile ?? input.filePath ?? null,
92
- line_number: parsed.sourceLine ?? null,
93
- column_number: null,
94
- });
95
-
96
- // 6. Create synapse: error ↔ project
97
- this.synapseManager.strengthen(
98
- { type: 'error', id: errorId },
99
- { type: 'project', id: project.id },
100
- 'co_occurs',
101
- );
102
-
103
- // 7. Find similar errors
104
- const candidates = this.errorRepo.findByProject(project.id)
105
- .filter(e => e.id !== errorId);
106
- const newError = this.errorRepo.getById(errorId)!;
107
- const matches = matchError(newError, candidates);
108
-
109
- // 8. Create similarity synapses for strong matches
110
- for (const match of matches.filter(m => m.isStrong)) {
111
- this.synapseManager.strengthen(
112
- { type: 'error', id: errorId },
113
- { type: 'error', id: match.errorId },
114
- 'similar_to',
115
- );
116
- }
117
-
118
- this.eventBus.emit('error:reported', { errorId, projectId: project.id, fingerprint });
119
- this.logger.info(`New error reported (id=${errorId}, type=${parsed.errorType})`);
120
-
121
- return { errorId, isNew: true, matches };
122
- }
123
-
124
- query(input: ErrorQueryInput): ErrorRecord[] {
125
- if (input.search) {
126
- return this.errorRepo.search(input.search);
127
- }
128
- if (input.resolved === false) {
129
- return this.errorRepo.findUnresolved(input.projectId);
130
- }
131
- if (input.projectId) {
132
- return this.errorRepo.findByProject(input.projectId);
133
- }
134
- return [];
135
- }
136
-
137
- matchSimilar(errorId: number): MatchResult[] {
138
- const error = this.errorRepo.getById(errorId);
139
- if (!error) return [];
140
-
141
- const candidates = this.errorRepo.findByProject(error.project_id)
142
- .filter(e => e.id !== errorId);
143
- return matchError(error, candidates);
144
- }
145
-
146
- resolve(errorId: number, solutionId?: number): void {
147
- this.errorRepo.update(errorId, {
148
- resolved: 1,
149
- resolved_at: new Date().toISOString(),
150
- });
151
-
152
- if (solutionId) {
153
- this.eventBus.emit('error:resolved', { errorId, solutionId });
154
- }
155
- }
156
-
157
- getById(id: number): ErrorRecord | undefined {
158
- return this.errorRepo.getById(id);
159
- }
160
-
161
- countSince(since: string, projectId?: number): number {
162
- return this.errorRepo.countSince(since, projectId);
163
- }
164
- }
1
+ import type { ErrorRecord } from '../types/error.types.js';
2
+ import type { ErrorRepository } from '../db/repositories/error.repository.js';
3
+ import type { ProjectRepository } from '../db/repositories/project.repository.js';
4
+ import type { SynapseManager } from '../synapses/synapse-manager.js';
5
+ import { parseError } from '../parsing/error-parser.js';
6
+ import { generateFingerprint } from '../matching/fingerprint.js';
7
+ import { matchError, type MatchResult } from '../matching/error-matcher.js';
8
+ import { getEventBus } from '../utils/events.js';
9
+ import { getLogger } from '../utils/logger.js';
10
+
11
+ export interface ReportErrorInput {
12
+ project: string;
13
+ errorOutput: string;
14
+ filePath?: string;
15
+ terminalId?: number;
16
+ }
17
+
18
+ export interface ErrorQueryInput {
19
+ projectId?: number;
20
+ resolved?: boolean;
21
+ search?: string;
22
+ limit?: number;
23
+ }
24
+
25
+ export class ErrorService {
26
+ private logger = getLogger();
27
+ private eventBus = getEventBus();
28
+
29
+ constructor(
30
+ private errorRepo: ErrorRepository,
31
+ private projectRepo: ProjectRepository,
32
+ private synapseManager: SynapseManager,
33
+ ) {}
34
+
35
+ report(input: ReportErrorInput): { errorId: number; isNew: boolean; matches: MatchResult[] } {
36
+ // 1. Ensure project exists
37
+ let project = this.projectRepo.findByName(input.project);
38
+ if (!project) {
39
+ const id = this.projectRepo.create({ name: input.project, path: null, language: null, framework: null });
40
+ project = this.projectRepo.getById(id)!;
41
+ }
42
+
43
+ // 2. Parse the error
44
+ const parsed = parseError(input.errorOutput);
45
+ if (!parsed) {
46
+ this.logger.warn('Could not parse error output');
47
+ const errorId = this.errorRepo.create({
48
+ project_id: project.id,
49
+ terminal_id: input.terminalId ?? null,
50
+ fingerprint: '',
51
+ type: 'UnknownError',
52
+ message: input.errorOutput.split('\n')[0] ?? input.errorOutput,
53
+ raw_output: input.errorOutput,
54
+ context: null,
55
+ file_path: input.filePath ?? null,
56
+ line_number: null,
57
+ column_number: null,
58
+ });
59
+ return { errorId, isNew: true, matches: [] };
60
+ }
61
+
62
+ // 3. Generate fingerprint
63
+ const fingerprint = generateFingerprint(parsed.errorType, parsed.message, parsed.frames);
64
+
65
+ // 4. Check for existing error with same fingerprint
66
+ const existing = this.errorRepo.findByFingerprint(fingerprint);
67
+ if (existing.length > 0) {
68
+ const err = existing[0]!;
69
+ this.errorRepo.incrementOccurrence(err.id);
70
+ this.logger.info(`Known error (id=${err.id}), occurrence incremented`);
71
+
72
+ // Strengthen synapse
73
+ this.synapseManager.strengthen(
74
+ { type: 'error', id: err.id },
75
+ { type: 'project', id: project.id },
76
+ 'co_occurs',
77
+ );
78
+
79
+ return { errorId: err.id, isNew: false, matches: [] };
80
+ }
81
+
82
+ // 5. Create new error record
83
+ const errorId = this.errorRepo.create({
84
+ project_id: project.id,
85
+ terminal_id: input.terminalId ?? null,
86
+ fingerprint,
87
+ type: parsed.errorType,
88
+ message: parsed.message,
89
+ raw_output: input.errorOutput,
90
+ context: null,
91
+ file_path: parsed.sourceFile ?? input.filePath ?? null,
92
+ line_number: parsed.sourceLine ?? null,
93
+ column_number: null,
94
+ });
95
+
96
+ // 6. Create synapse: error ↔ project
97
+ this.synapseManager.strengthen(
98
+ { type: 'error', id: errorId },
99
+ { type: 'project', id: project.id },
100
+ 'co_occurs',
101
+ );
102
+
103
+ // 7. Find similar errors
104
+ const candidates = this.errorRepo.findByProject(project.id)
105
+ .filter(e => e.id !== errorId);
106
+ const newError = this.errorRepo.getById(errorId)!;
107
+ const matches = matchError(newError, candidates);
108
+
109
+ // 8. Create similarity synapses for strong matches
110
+ for (const match of matches.filter(m => m.isStrong)) {
111
+ this.synapseManager.strengthen(
112
+ { type: 'error', id: errorId },
113
+ { type: 'error', id: match.errorId },
114
+ 'similar_to',
115
+ );
116
+ }
117
+
118
+ this.eventBus.emit('error:reported', { errorId, projectId: project.id, fingerprint });
119
+ this.logger.info(`New error reported (id=${errorId}, type=${parsed.errorType})`);
120
+
121
+ return { errorId, isNew: true, matches };
122
+ }
123
+
124
+ query(input: ErrorQueryInput): ErrorRecord[] {
125
+ if (input.search) {
126
+ return this.errorRepo.search(input.search);
127
+ }
128
+ if (input.resolved === false) {
129
+ return this.errorRepo.findUnresolved(input.projectId);
130
+ }
131
+ if (input.projectId) {
132
+ return this.errorRepo.findByProject(input.projectId);
133
+ }
134
+ return [];
135
+ }
136
+
137
+ matchSimilar(errorId: number): MatchResult[] {
138
+ const error = this.errorRepo.getById(errorId);
139
+ if (!error) return [];
140
+
141
+ const candidates = this.errorRepo.findByProject(error.project_id)
142
+ .filter(e => e.id !== errorId);
143
+ return matchError(error, candidates);
144
+ }
145
+
146
+ resolve(errorId: number, solutionId?: number): void {
147
+ this.errorRepo.update(errorId, {
148
+ resolved: 1,
149
+ resolved_at: new Date().toISOString(),
150
+ });
151
+
152
+ if (solutionId) {
153
+ this.eventBus.emit('error:resolved', { errorId, solutionId });
154
+ }
155
+ }
156
+
157
+ getById(id: number): ErrorRecord | undefined {
158
+ return this.errorRepo.getById(id);
159
+ }
160
+
161
+ countSince(since: string, projectId?: number): number {
162
+ return this.errorRepo.countSince(since, projectId);
163
+ }
164
+ }
@@ -1,41 +1,41 @@
1
- import type { NotificationRepository, NotificationRecord } from '../db/repositories/notification.repository.js';
2
- import { getLogger } from '../utils/logger.js';
3
-
4
- export interface CreateNotificationInput {
5
- type: string;
6
- title: string;
7
- message: string;
8
- priority?: number;
9
- projectId?: number;
10
- }
11
-
12
- export class NotificationService {
13
- private logger = getLogger();
14
-
15
- constructor(private notificationRepo: NotificationRepository) {}
16
-
17
- create(input: CreateNotificationInput): number {
18
- const id = this.notificationRepo.create({
19
- type: input.type,
20
- title: input.title,
21
- message: input.message,
22
- priority: input.priority ?? 0,
23
- project_id: input.projectId ?? null,
24
- });
25
-
26
- this.logger.info(`Notification created (id=${id}, type=${input.type})`);
27
- return id;
28
- }
29
-
30
- list(projectId?: number): NotificationRecord[] {
31
- return this.notificationRepo.findUnacknowledged(projectId);
32
- }
33
-
34
- acknowledge(id: number): void {
35
- this.notificationRepo.acknowledge(id);
36
- }
37
-
38
- getById(id: number): NotificationRecord | undefined {
39
- return this.notificationRepo.getById(id);
40
- }
41
- }
1
+ import type { NotificationRepository, NotificationRecord } from '../db/repositories/notification.repository.js';
2
+ import { getLogger } from '../utils/logger.js';
3
+
4
+ export interface CreateNotificationInput {
5
+ type: string;
6
+ title: string;
7
+ message: string;
8
+ priority?: number;
9
+ projectId?: number;
10
+ }
11
+
12
+ export class NotificationService {
13
+ private logger = getLogger();
14
+
15
+ constructor(private notificationRepo: NotificationRepository) {}
16
+
17
+ create(input: CreateNotificationInput): number {
18
+ const id = this.notificationRepo.create({
19
+ type: input.type,
20
+ title: input.title,
21
+ message: input.message,
22
+ priority: input.priority ?? 0,
23
+ project_id: input.projectId ?? null,
24
+ });
25
+
26
+ this.logger.info(`Notification created (id=${id}, type=${input.type})`);
27
+ return id;
28
+ }
29
+
30
+ list(projectId?: number): NotificationRecord[] {
31
+ return this.notificationRepo.findUnacknowledged(projectId);
32
+ }
33
+
34
+ acknowledge(id: number): void {
35
+ this.notificationRepo.acknowledge(id);
36
+ }
37
+
38
+ getById(id: number): NotificationRecord | undefined {
39
+ return this.notificationRepo.getById(id);
40
+ }
41
+ }
@@ -1,119 +1,119 @@
1
- import type { RuleRepository } from '../db/repositories/rule.repository.js';
2
- import type { AntipatternRepository } from '../db/repositories/antipattern.repository.js';
3
- import type { SynapseManager } from '../synapses/synapse-manager.js';
4
- import { getLogger } from '../utils/logger.js';
5
-
6
- export interface RuleCheckResult {
7
- matched: boolean;
8
- ruleId: number;
9
- action: string;
10
- description: string | null;
11
- confidence: number;
12
- }
13
-
14
- export interface AntipatternCheckResult {
15
- matched: boolean;
16
- antipatternId: number;
17
- pattern: string;
18
- description: string;
19
- severity: string;
20
- suggestion: string | null;
21
- }
22
-
23
- export class PreventionService {
24
- private logger = getLogger();
25
-
26
- constructor(
27
- private ruleRepo: RuleRepository,
28
- private antipatternRepo: AntipatternRepository,
29
- private synapseManager: SynapseManager,
30
- ) {}
31
-
32
- checkRules(errorType: string, message: string, projectId?: number): RuleCheckResult[] {
33
- const rules = this.ruleRepo.findActive(projectId);
34
- const results: RuleCheckResult[] = [];
35
-
36
- for (const rule of rules) {
37
- try {
38
- const pattern = new RegExp(rule.pattern, 'i');
39
- const input = `${errorType}: ${message}`;
40
-
41
- if (pattern.test(input)) {
42
- results.push({
43
- matched: true,
44
- ruleId: rule.id,
45
- action: rule.action,
46
- description: rule.description,
47
- confidence: rule.confidence,
48
- });
49
-
50
- this.logger.debug(`Rule ${rule.id} matched: ${rule.pattern}`);
51
- }
52
- } catch {
53
- // Invalid regex in rule pattern, skip
54
- this.logger.warn(`Invalid regex in rule ${rule.id}: ${rule.pattern}`);
55
- }
56
- }
57
-
58
- return results.sort((a, b) => b.confidence - a.confidence);
59
- }
60
-
61
- checkAntipatterns(errorType: string, message: string, projectId?: number): AntipatternCheckResult[] {
62
- const antipatterns = projectId
63
- ? [...this.antipatternRepo.findByProject(projectId), ...this.antipatternRepo.findGlobal()]
64
- : this.antipatternRepo.findGlobal();
65
-
66
- const results: AntipatternCheckResult[] = [];
67
- const input = `${errorType}: ${message}`;
68
-
69
- for (const ap of antipatterns) {
70
- try {
71
- const pattern = new RegExp(ap.pattern, 'i');
72
- if (pattern.test(input)) {
73
- results.push({
74
- matched: true,
75
- antipatternId: ap.id,
76
- pattern: ap.pattern,
77
- description: ap.description,
78
- severity: ap.severity,
79
- suggestion: ap.suggestion,
80
- });
81
- }
82
- } catch {
83
- this.logger.warn(`Invalid regex in antipattern ${ap.id}: ${ap.pattern}`);
84
- }
85
- }
86
-
87
- return results;
88
- }
89
-
90
- createRule(data: {
91
- pattern: string;
92
- action: string;
93
- description?: string;
94
- confidence?: number;
95
- projectId?: number;
96
- }): number {
97
- return this.ruleRepo.create({
98
- pattern: data.pattern,
99
- action: data.action,
100
- description: data.description ?? null,
101
- confidence: data.confidence ?? 0.5,
102
- occurrences: 0,
103
- active: 1,
104
- project_id: data.projectId ?? null,
105
- });
106
- }
107
-
108
- reportPrevention(ruleId: number, errorId: number): void {
109
- const rule = this.ruleRepo.getById(ruleId);
110
- if (rule) {
111
- this.ruleRepo.update(ruleId, { occurrences: rule.occurrences + 1 });
112
- this.synapseManager.strengthen(
113
- { type: 'rule', id: ruleId },
114
- { type: 'error', id: errorId },
115
- 'prevents',
116
- );
117
- }
118
- }
119
- }
1
+ import type { RuleRepository } from '../db/repositories/rule.repository.js';
2
+ import type { AntipatternRepository } from '../db/repositories/antipattern.repository.js';
3
+ import type { SynapseManager } from '../synapses/synapse-manager.js';
4
+ import { getLogger } from '../utils/logger.js';
5
+
6
+ export interface RuleCheckResult {
7
+ matched: boolean;
8
+ ruleId: number;
9
+ action: string;
10
+ description: string | null;
11
+ confidence: number;
12
+ }
13
+
14
+ export interface AntipatternCheckResult {
15
+ matched: boolean;
16
+ antipatternId: number;
17
+ pattern: string;
18
+ description: string;
19
+ severity: string;
20
+ suggestion: string | null;
21
+ }
22
+
23
+ export class PreventionService {
24
+ private logger = getLogger();
25
+
26
+ constructor(
27
+ private ruleRepo: RuleRepository,
28
+ private antipatternRepo: AntipatternRepository,
29
+ private synapseManager: SynapseManager,
30
+ ) {}
31
+
32
+ checkRules(errorType: string, message: string, projectId?: number): RuleCheckResult[] {
33
+ const rules = this.ruleRepo.findActive(projectId);
34
+ const results: RuleCheckResult[] = [];
35
+
36
+ for (const rule of rules) {
37
+ try {
38
+ const pattern = new RegExp(rule.pattern, 'i');
39
+ const input = `${errorType}: ${message}`;
40
+
41
+ if (pattern.test(input)) {
42
+ results.push({
43
+ matched: true,
44
+ ruleId: rule.id,
45
+ action: rule.action,
46
+ description: rule.description,
47
+ confidence: rule.confidence,
48
+ });
49
+
50
+ this.logger.debug(`Rule ${rule.id} matched: ${rule.pattern}`);
51
+ }
52
+ } catch {
53
+ // Invalid regex in rule pattern, skip
54
+ this.logger.warn(`Invalid regex in rule ${rule.id}: ${rule.pattern}`);
55
+ }
56
+ }
57
+
58
+ return results.sort((a, b) => b.confidence - a.confidence);
59
+ }
60
+
61
+ checkAntipatterns(errorType: string, message: string, projectId?: number): AntipatternCheckResult[] {
62
+ const antipatterns = projectId
63
+ ? [...this.antipatternRepo.findByProject(projectId), ...this.antipatternRepo.findGlobal()]
64
+ : this.antipatternRepo.findGlobal();
65
+
66
+ const results: AntipatternCheckResult[] = [];
67
+ const input = `${errorType}: ${message}`;
68
+
69
+ for (const ap of antipatterns) {
70
+ try {
71
+ const pattern = new RegExp(ap.pattern, 'i');
72
+ if (pattern.test(input)) {
73
+ results.push({
74
+ matched: true,
75
+ antipatternId: ap.id,
76
+ pattern: ap.pattern,
77
+ description: ap.description,
78
+ severity: ap.severity,
79
+ suggestion: ap.suggestion,
80
+ });
81
+ }
82
+ } catch {
83
+ this.logger.warn(`Invalid regex in antipattern ${ap.id}: ${ap.pattern}`);
84
+ }
85
+ }
86
+
87
+ return results;
88
+ }
89
+
90
+ createRule(data: {
91
+ pattern: string;
92
+ action: string;
93
+ description?: string;
94
+ confidence?: number;
95
+ projectId?: number;
96
+ }): number {
97
+ return this.ruleRepo.create({
98
+ pattern: data.pattern,
99
+ action: data.action,
100
+ description: data.description ?? null,
101
+ confidence: data.confidence ?? 0.5,
102
+ occurrences: 0,
103
+ active: 1,
104
+ project_id: data.projectId ?? null,
105
+ });
106
+ }
107
+
108
+ reportPrevention(ruleId: number, errorId: number): void {
109
+ const rule = this.ruleRepo.getById(ruleId);
110
+ if (rule) {
111
+ this.ruleRepo.update(ruleId, { occurrences: rule.occurrences + 1 });
112
+ this.synapseManager.strengthen(
113
+ { type: 'rule', id: ruleId },
114
+ { type: 'error', id: errorId },
115
+ 'prevents',
116
+ );
117
+ }
118
+ }
119
+ }