@timmeck/brain 1.9.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/README.md +33 -0
  2. package/brain.log +3876 -0
  3. package/{src/cli/commands/dashboard.ts → dashboard.html} +694 -807
  4. package/dist/api/server.d.ts +4 -18
  5. package/dist/api/server.js +4 -173
  6. package/dist/api/server.js.map +1 -1
  7. package/dist/brain.d.ts +2 -0
  8. package/dist/brain.js +15 -4
  9. package/dist/brain.js.map +1 -1
  10. package/dist/cli/colors.d.ts +4 -25
  11. package/dist/cli/colors.js +3 -89
  12. package/dist/cli/colors.js.map +1 -1
  13. package/dist/cli/commands/dashboard.js +21 -2
  14. package/dist/cli/commands/dashboard.js.map +1 -1
  15. package/dist/cli/commands/peers.d.ts +2 -0
  16. package/dist/cli/commands/peers.js +38 -0
  17. package/dist/cli/commands/peers.js.map +1 -0
  18. package/dist/cli/commands/status.js +0 -1
  19. package/dist/cli/commands/status.js.map +1 -1
  20. package/dist/config.js +2 -29
  21. package/dist/config.js.map +1 -1
  22. package/dist/db/connection.d.ts +1 -2
  23. package/dist/db/connection.js +1 -18
  24. package/dist/db/connection.js.map +1 -1
  25. package/dist/index.js +3 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
  28. package/dist/ipc/__tests__/protocol.test.js +117 -0
  29. package/dist/ipc/__tests__/protocol.test.js.map +1 -0
  30. package/dist/ipc/client.d.ts +1 -16
  31. package/dist/ipc/client.js +1 -100
  32. package/dist/ipc/client.js.map +1 -1
  33. package/dist/ipc/protocol.d.ts +1 -8
  34. package/dist/ipc/protocol.js +1 -28
  35. package/dist/ipc/protocol.js.map +1 -1
  36. package/dist/ipc/router.d.ts +2 -0
  37. package/dist/ipc/router.js +30 -0
  38. package/dist/ipc/router.js.map +1 -1
  39. package/dist/ipc/server.d.ts +1 -22
  40. package/dist/ipc/server.js +1 -163
  41. package/dist/ipc/server.js.map +1 -1
  42. package/dist/learning/confidence-scorer.d.ts +2 -5
  43. package/dist/learning/confidence-scorer.js +4 -19
  44. package/dist/learning/confidence-scorer.js.map +1 -1
  45. package/dist/learning/decay.js +2 -3
  46. package/dist/learning/decay.js.map +1 -1
  47. package/dist/learning/learning-engine.d.ts +2 -5
  48. package/dist/learning/learning-engine.js +3 -15
  49. package/dist/learning/learning-engine.js.map +1 -1
  50. package/dist/mcp/http-server.d.ts +1 -7
  51. package/dist/mcp/http-server.js +6 -117
  52. package/dist/mcp/http-server.js.map +1 -1
  53. package/dist/mcp/server.js +5 -61
  54. package/dist/mcp/server.js.map +1 -1
  55. package/dist/mcp/tools.js +36 -0
  56. package/dist/mcp/tools.js.map +1 -1
  57. package/dist/parsing/parsers/compiler.js +1 -1
  58. package/dist/parsing/parsers/compiler.js.map +1 -1
  59. package/dist/research/research-engine.d.ts +2 -6
  60. package/dist/research/research-engine.js +3 -23
  61. package/dist/research/research-engine.js.map +1 -1
  62. package/dist/services/synapse.service.d.ts +3 -3
  63. package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
  64. package/dist/signals/__tests__/fingerprint.test.js +118 -0
  65. package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
  66. package/dist/synapses/activation.d.ts +3 -13
  67. package/dist/synapses/activation.js +2 -49
  68. package/dist/synapses/activation.js.map +1 -1
  69. package/dist/synapses/decay.d.ts +2 -11
  70. package/dist/synapses/decay.js +2 -26
  71. package/dist/synapses/decay.js.map +1 -1
  72. package/dist/synapses/hebbian.d.ts +2 -13
  73. package/dist/synapses/hebbian.js +2 -35
  74. package/dist/synapses/hebbian.js.map +1 -1
  75. package/dist/synapses/pathfinder.d.ts +2 -14
  76. package/dist/synapses/pathfinder.js +2 -49
  77. package/dist/synapses/pathfinder.js.map +1 -1
  78. package/dist/synapses/synapse-manager.d.ts +7 -23
  79. package/dist/synapses/synapse-manager.js +6 -63
  80. package/dist/synapses/synapse-manager.js.map +1 -1
  81. package/dist/types/ipc.types.d.ts +1 -11
  82. package/dist/utils/__tests__/hash.test.d.ts +1 -0
  83. package/dist/utils/__tests__/hash.test.js +32 -0
  84. package/dist/utils/__tests__/hash.test.js.map +1 -0
  85. package/dist/utils/__tests__/paths.test.d.ts +1 -0
  86. package/dist/utils/__tests__/paths.test.js +75 -0
  87. package/dist/utils/__tests__/paths.test.js.map +1 -0
  88. package/dist/utils/events.d.ts +4 -8
  89. package/dist/utils/events.js +2 -14
  90. package/dist/utils/events.js.map +1 -1
  91. package/dist/utils/hash.d.ts +1 -1
  92. package/dist/utils/hash.js +1 -4
  93. package/dist/utils/hash.js.map +1 -1
  94. package/dist/utils/logger.d.ts +3 -2
  95. package/dist/utils/logger.js +8 -35
  96. package/dist/utils/logger.js.map +1 -1
  97. package/dist/utils/paths.d.ts +2 -1
  98. package/dist/utils/paths.js +4 -13
  99. package/dist/utils/paths.js.map +1 -1
  100. package/eslint.config.js +14 -0
  101. package/package.json +56 -49
  102. package/BRAIN_PLAN.md +0 -3324
  103. package/reddit_post.md +0 -45
  104. package/src/api/server.ts +0 -395
  105. package/src/brain.ts +0 -313
  106. package/src/cli/colors.ts +0 -116
  107. package/src/cli/commands/config.ts +0 -169
  108. package/src/cli/commands/doctor.ts +0 -124
  109. package/src/cli/commands/explain.ts +0 -83
  110. package/src/cli/commands/export.ts +0 -31
  111. package/src/cli/commands/import.ts +0 -199
  112. package/src/cli/commands/insights.ts +0 -65
  113. package/src/cli/commands/learn.ts +0 -24
  114. package/src/cli/commands/modules.ts +0 -53
  115. package/src/cli/commands/network.ts +0 -67
  116. package/src/cli/commands/projects.ts +0 -42
  117. package/src/cli/commands/query.ts +0 -120
  118. package/src/cli/commands/start.ts +0 -105
  119. package/src/cli/commands/status.ts +0 -75
  120. package/src/cli/commands/stop.ts +0 -34
  121. package/src/cli/ipc-helper.ts +0 -22
  122. package/src/cli/update-check.ts +0 -63
  123. package/src/code/analyzer.ts +0 -117
  124. package/src/code/fingerprint.ts +0 -87
  125. package/src/code/matcher.ts +0 -129
  126. package/src/code/parsers/generic.ts +0 -29
  127. package/src/code/parsers/python.ts +0 -54
  128. package/src/code/parsers/typescript.ts +0 -65
  129. package/src/code/registry.ts +0 -60
  130. package/src/code/scorer.ts +0 -120
  131. package/src/config.ts +0 -135
  132. package/src/dashboard/server.ts +0 -142
  133. package/src/db/connection.ts +0 -22
  134. package/src/db/migrations/001_core_schema.ts +0 -120
  135. package/src/db/migrations/002_learning_schema.ts +0 -38
  136. package/src/db/migrations/003_code_schema.ts +0 -53
  137. package/src/db/migrations/004_synapses_schema.ts +0 -57
  138. package/src/db/migrations/005_fts_indexes.ts +0 -78
  139. package/src/db/migrations/006_synapses_phase3.ts +0 -17
  140. package/src/db/migrations/007_feedback.ts +0 -13
  141. package/src/db/migrations/008_git_integration.ts +0 -38
  142. package/src/db/migrations/009_embeddings.ts +0 -8
  143. package/src/db/migrations/index.ts +0 -70
  144. package/src/db/repositories/antipattern.repository.ts +0 -66
  145. package/src/db/repositories/code-module.repository.ts +0 -142
  146. package/src/db/repositories/error.repository.ts +0 -189
  147. package/src/db/repositories/insight.repository.ts +0 -99
  148. package/src/db/repositories/notification.repository.ts +0 -66
  149. package/src/db/repositories/project.repository.ts +0 -93
  150. package/src/db/repositories/rule.repository.ts +0 -108
  151. package/src/db/repositories/solution.repository.ts +0 -154
  152. package/src/db/repositories/synapse.repository.ts +0 -163
  153. package/src/db/repositories/terminal.repository.ts +0 -101
  154. package/src/embeddings/engine.ts +0 -238
  155. package/src/hooks/post-tool-use.ts +0 -92
  156. package/src/hooks/post-write.ts +0 -129
  157. package/src/index.ts +0 -63
  158. package/src/ipc/client.ts +0 -118
  159. package/src/ipc/protocol.ts +0 -35
  160. package/src/ipc/router.ts +0 -133
  161. package/src/ipc/server.ts +0 -176
  162. package/src/learning/confidence-scorer.ts +0 -80
  163. package/src/learning/decay.ts +0 -46
  164. package/src/learning/learning-engine.ts +0 -170
  165. package/src/learning/pattern-extractor.ts +0 -90
  166. package/src/learning/rule-generator.ts +0 -74
  167. package/src/main.rs:10:5 +0 -0
  168. package/src/matching/error-matcher.ts +0 -166
  169. package/src/matching/fingerprint.ts +0 -34
  170. package/src/matching/similarity.ts +0 -61
  171. package/src/matching/tfidf.ts +0 -74
  172. package/src/matching/tokenizer.ts +0 -41
  173. package/src/mcp/auto-detect.ts +0 -93
  174. package/src/mcp/http-server.ts +0 -140
  175. package/src/mcp/server.ts +0 -73
  176. package/src/mcp/tools.ts +0 -328
  177. package/src/parsing/error-parser.ts +0 -28
  178. package/src/parsing/parsers/compiler.ts +0 -93
  179. package/src/parsing/parsers/generic.ts +0 -28
  180. package/src/parsing/parsers/go.ts +0 -97
  181. package/src/parsing/parsers/node.ts +0 -69
  182. package/src/parsing/parsers/python.ts +0 -62
  183. package/src/parsing/parsers/rust.ts +0 -50
  184. package/src/parsing/parsers/shell.ts +0 -42
  185. package/src/parsing/types.ts +0 -47
  186. package/src/research/gap-analyzer.ts +0 -135
  187. package/src/research/insight-generator.ts +0 -123
  188. package/src/research/research-engine.ts +0 -116
  189. package/src/research/synergy-detector.ts +0 -126
  190. package/src/research/template-extractor.ts +0 -130
  191. package/src/research/trend-analyzer.ts +0 -127
  192. package/src/services/analytics.service.ts +0 -226
  193. package/src/services/code.service.ts +0 -271
  194. package/src/services/error.service.ts +0 -266
  195. package/src/services/git.service.ts +0 -132
  196. package/src/services/notification.service.ts +0 -41
  197. package/src/services/prevention.service.ts +0 -159
  198. package/src/services/research.service.ts +0 -98
  199. package/src/services/solution.service.ts +0 -174
  200. package/src/services/synapse.service.ts +0 -59
  201. package/src/services/terminal.service.ts +0 -81
  202. package/src/synapses/activation.ts +0 -80
  203. package/src/synapses/decay.ts +0 -38
  204. package/src/synapses/hebbian.ts +0 -69
  205. package/src/synapses/pathfinder.ts +0 -81
  206. package/src/synapses/synapse-manager.ts +0 -113
  207. package/src/types/code.types.ts +0 -52
  208. package/src/types/config.types.ts +0 -103
  209. package/src/types/error.types.ts +0 -67
  210. package/src/types/ipc.types.ts +0 -8
  211. package/src/types/mcp.types.ts +0 -53
  212. package/src/types/research.types.ts +0 -28
  213. package/src/types/solution.types.ts +0 -30
  214. package/src/types/synapse.types.ts +0 -50
  215. package/src/utils/events.ts +0 -45
  216. package/src/utils/hash.ts +0 -5
  217. package/src/utils/logger.ts +0 -48
  218. package/src/utils/paths.ts +0 -19
  219. package/tests/e2e/test_code_intelligence.py +0 -1015
  220. package/tests/e2e/test_error_memory.py +0 -451
  221. package/tests/e2e/test_full_integration.py +0 -534
  222. package/tests/fixtures/code-modules/modules.ts +0 -83
  223. package/tests/fixtures/errors/go.ts +0 -9
  224. package/tests/fixtures/errors/node.ts +0 -24
  225. package/tests/fixtures/errors/python.ts +0 -21
  226. package/tests/fixtures/errors/rust.ts +0 -25
  227. package/tests/fixtures/errors/shell.ts +0 -15
  228. package/tests/fixtures/solutions/solutions.ts +0 -27
  229. package/tests/helpers/setup-db.ts +0 -52
  230. package/tests/integration/code-flow.test.ts +0 -86
  231. package/tests/integration/error-flow.test.ts +0 -83
  232. package/tests/integration/ipc-flow.test.ts +0 -166
  233. package/tests/integration/learning-cycle.test.ts +0 -82
  234. package/tests/integration/synapse-flow.test.ts +0 -117
  235. package/tests/unit/code/analyzer.test.ts +0 -58
  236. package/tests/unit/code/fingerprint.test.ts +0 -51
  237. package/tests/unit/code/scorer.test.ts +0 -55
  238. package/tests/unit/learning/confidence-scorer.test.ts +0 -60
  239. package/tests/unit/learning/decay.test.ts +0 -45
  240. package/tests/unit/learning/pattern-extractor.test.ts +0 -50
  241. package/tests/unit/matching/error-matcher.test.ts +0 -69
  242. package/tests/unit/matching/fingerprint.test.ts +0 -47
  243. package/tests/unit/matching/similarity.test.ts +0 -65
  244. package/tests/unit/matching/tfidf.test.ts +0 -71
  245. package/tests/unit/matching/tokenizer.test.ts +0 -83
  246. package/tests/unit/parsing/parsers.test.ts +0 -113
  247. package/tests/unit/research/gap-analyzer.test.ts +0 -45
  248. package/tests/unit/research/trend-analyzer.test.ts +0 -45
  249. package/tests/unit/synapses/activation.test.ts +0 -80
  250. package/tests/unit/synapses/decay.test.ts +0 -27
  251. package/tests/unit/synapses/hebbian.test.ts +0 -96
  252. package/tests/unit/synapses/pathfinder.test.ts +0 -72
  253. package/tsconfig.json +0 -18
@@ -1,271 +0,0 @@
1
- import type { CodeModuleRecord } from '../types/code.types.js';
2
- import type { CodeModuleRepository } from '../db/repositories/code-module.repository.js';
3
- import type { ProjectRepository } from '../db/repositories/project.repository.js';
4
- import type { SynapseManager } from '../synapses/synapse-manager.js';
5
- import { analyzeCode } from '../code/analyzer.js';
6
- import { fingerprintCode } from '../code/fingerprint.js';
7
- import { computeReusabilityScore } from '../code/scorer.js';
8
- import { detectGranularity } from '../code/registry.js';
9
- import { findExactMatches, findSemanticMatches, findStructuralMatches } from '../code/matcher.js';
10
- import { sha256 } from '../utils/hash.js';
11
- import { getEventBus } from '../utils/events.js';
12
- import { getLogger } from '../utils/logger.js';
13
- import type { EmbeddingEngine } from '../embeddings/engine.js';
14
-
15
- export interface AnalyzeInput {
16
- project: string;
17
- name: string;
18
- filePath: string;
19
- language: string;
20
- source: string;
21
- description?: string;
22
- }
23
-
24
- export interface FindReusableInput {
25
- query?: string;
26
- language?: string;
27
- projectId?: number;
28
- limit?: number;
29
- }
30
-
31
- export class CodeService {
32
- private logger = getLogger();
33
- private eventBus = getEventBus();
34
- private embeddingEngine: EmbeddingEngine | null = null;
35
-
36
- constructor(
37
- private codeModuleRepo: CodeModuleRepository,
38
- private projectRepo: ProjectRepository,
39
- private synapseManager: SynapseManager,
40
- ) {}
41
-
42
- setEmbeddingEngine(engine: EmbeddingEngine): void {
43
- this.embeddingEngine = engine;
44
- }
45
-
46
- analyzeAndRegister(input: AnalyzeInput): { moduleId: number; isNew: boolean; reusabilityScore: number } {
47
- // Ensure project exists
48
- let project = this.projectRepo.findByName(input.project);
49
- if (!project) {
50
- const id = this.projectRepo.create({ name: input.project, path: null, language: input.language, framework: null });
51
- project = this.projectRepo.getById(id)!;
52
- }
53
-
54
- // Analyze the code
55
- const analysis = analyzeCode(input.source, input.language);
56
- const fingerprint = fingerprintCode(input.source, input.language);
57
- const sourceHash = sha256(input.source);
58
-
59
- // Check if module already exists (by fingerprint)
60
- const existing = this.codeModuleRepo.findByFingerprint(fingerprint);
61
- if (existing) {
62
- // Source Hash Change Detection: compare hash to decide if re-analysis needed
63
- if (existing.source_hash === sourceHash) {
64
- // Unchanged — skip re-analysis
65
- this.logger.debug(`Module ${existing.name} unchanged (hash match), skipping`);
66
- this.synapseManager.strengthen(
67
- { type: 'code_module', id: existing.id },
68
- { type: 'project', id: project.id },
69
- 'uses_module',
70
- );
71
- return { moduleId: existing.id, isNew: false, reusabilityScore: existing.reusability_score };
72
- }
73
-
74
- // Hash changed — re-analyze
75
- this.logger.info(`Module ${existing.name} changed (hash drift), re-analyzing`);
76
- const reScore = computeReusabilityScore({
77
- source: input.source,
78
- filePath: input.filePath,
79
- exports: analysis.exports,
80
- internalDeps: analysis.internalDeps,
81
- hasTypeAnnotations: analysis.hasTypeAnnotations,
82
- complexity: analysis.complexity,
83
- });
84
-
85
- this.codeModuleRepo.update(existing.id, {
86
- source_hash: sourceHash,
87
- lines_of_code: analysis.linesOfCode,
88
- complexity: analysis.complexity,
89
- reusability_score: reScore,
90
- updated_at: new Date().toISOString(),
91
- });
92
-
93
- // Re-index dependency synapses on change
94
- this.indexDependencySynapses(existing.id, analysis.internalDeps, project.id);
95
-
96
- this.synapseManager.strengthen(
97
- { type: 'code_module', id: existing.id },
98
- { type: 'project', id: project.id },
99
- 'uses_module',
100
- );
101
-
102
- this.eventBus.emit('module:updated', { moduleId: existing.id });
103
-
104
- return { moduleId: existing.id, isNew: false, reusabilityScore: reScore };
105
- }
106
-
107
- // Compute reusability score (with complexity)
108
- const reusabilityScore = computeReusabilityScore({
109
- source: input.source,
110
- filePath: input.filePath,
111
- exports: analysis.exports,
112
- internalDeps: analysis.internalDeps,
113
- hasTypeAnnotations: analysis.hasTypeAnnotations,
114
- complexity: analysis.complexity,
115
- });
116
-
117
- const granularity = detectGranularity(input.source, input.language);
118
-
119
- const moduleId = this.codeModuleRepo.create({
120
- project_id: project.id,
121
- name: input.name,
122
- file_path: input.filePath,
123
- language: input.language,
124
- fingerprint,
125
- description: input.description ?? null,
126
- source_hash: sourceHash,
127
- lines_of_code: analysis.linesOfCode,
128
- complexity: analysis.complexity,
129
- reusability_score: reusabilityScore,
130
- });
131
-
132
- // Create synapse: module ↔ project
133
- this.synapseManager.strengthen(
134
- { type: 'code_module', id: moduleId },
135
- { type: 'project', id: project.id },
136
- 'uses_module',
137
- );
138
-
139
- // Create dependency synapses for internal imports
140
- this.indexDependencySynapses(moduleId, analysis.internalDeps, project.id);
141
-
142
- // Compute and store pairwise module similarities
143
- this.computeModuleSimilarities(moduleId, input.source, input.language);
144
-
145
- this.eventBus.emit('module:registered', { moduleId, projectId: project.id });
146
- this.logger.info(`Code module registered (id=${moduleId}, name=${input.name}, granularity=${granularity}, score=${reusabilityScore.toFixed(2)})`);
147
-
148
- return { moduleId, isNew: true, reusabilityScore };
149
- }
150
-
151
- findReusable(input: FindReusableInput): CodeModuleRecord[] {
152
- if (input.query) {
153
- return this.codeModuleRepo.search(input.query);
154
- }
155
- if (input.language) {
156
- return this.codeModuleRepo.findByLanguage(input.language, input.limit);
157
- }
158
- if (input.projectId) {
159
- return this.codeModuleRepo.findByProject(input.projectId);
160
- }
161
- return [];
162
- }
163
-
164
- checkSimilarity(source: string, language: string): Array<{ moduleId: number; score: number; matchType: string }> {
165
- const fingerprint = fingerprintCode(source, language);
166
- const allModules = this.codeModuleRepo.findByLanguage(language);
167
-
168
- const exact = findExactMatches(fingerprint, allModules);
169
- if (exact.length > 0) return exact;
170
-
171
- return findSemanticMatches(source, allModules, 0.5);
172
- }
173
-
174
- listModules(projectId?: number, language?: string, limit?: number): CodeModuleRecord[] {
175
- if (projectId) {
176
- return this.codeModuleRepo.findByProject(projectId);
177
- }
178
- if (language) {
179
- return this.codeModuleRepo.findByLanguage(language, limit);
180
- }
181
- return this.codeModuleRepo.findAll(limit);
182
- }
183
-
184
- getById(id: number): CodeModuleRecord | undefined {
185
- return this.codeModuleRepo.getById(id);
186
- }
187
-
188
- private computeModuleSimilarities(moduleId: number, source: string, language: string): void {
189
- const allModules = this.codeModuleRepo.findByLanguage(language, 100);
190
- const candidates = allModules.filter(m => m.id !== moduleId);
191
- if (candidates.length === 0) return;
192
-
193
- const matches = findStructuralMatches(source, language, candidates, 0.3);
194
-
195
- // Get vector similarity scores if embedding engine is ready
196
- const vectorScores = this.embeddingEngine?.isReady()
197
- ? this.embeddingEngine.computeModuleVectorScores(moduleId, language)
198
- : undefined;
199
-
200
- // Merge structural matches with vector boost
201
- const scoreMap = new Map<number, number>();
202
- for (const match of matches) {
203
- if (match.score >= 0.3 && match.moduleId !== moduleId) {
204
- scoreMap.set(match.moduleId, match.score);
205
- }
206
- }
207
-
208
- // Add vector-only matches that structural search missed
209
- if (vectorScores) {
210
- for (const [candId, vecScore] of vectorScores) {
211
- if (vecScore >= 0.5 && candId !== moduleId) {
212
- const existing = scoreMap.get(candId);
213
- if (existing) {
214
- // Boost structural score with vector similarity
215
- scoreMap.set(candId, Math.min(1.0, existing + vecScore * 0.15));
216
- } else {
217
- // Vector-only match
218
- scoreMap.set(candId, vecScore * 0.8);
219
- }
220
- }
221
- }
222
- }
223
-
224
- for (const [candId, score] of scoreMap) {
225
- this.codeModuleRepo.upsertSimilarity(moduleId, candId, score);
226
-
227
- // High similarity → create synapse
228
- if (score >= 0.7) {
229
- this.synapseManager.strengthen(
230
- { type: 'code_module', id: moduleId },
231
- { type: 'code_module', id: candId },
232
- 'similar_to',
233
- );
234
- }
235
- }
236
- }
237
-
238
- private indexDependencySynapses(moduleId: number, internalDeps: string[], projectId: number): void {
239
- const projectModules = this.codeModuleRepo.findByProject(projectId);
240
-
241
- for (const dep of internalDeps) {
242
- // Normalize the dep path to match against registered modules
243
- const depName = dep.replace(/^\.\//, '').replace(/\.\w+$/, '');
244
-
245
- const target = projectModules.find(m => {
246
- const modulePath = m.file_path.replace(/\\/g, '/').replace(/\.\w+$/, '');
247
- return modulePath.endsWith(depName) || m.name === depName;
248
- });
249
-
250
- if (target && target.id !== moduleId) {
251
- this.synapseManager.strengthen(
252
- { type: 'code_module', id: moduleId },
253
- { type: 'code_module', id: target.id },
254
- 'depends_on',
255
- );
256
- }
257
- }
258
- }
259
-
260
- listProjects(): Array<{ id: number; name: string; path: string | null; language: string | null; framework: string | null; moduleCount: number }> {
261
- const projects = this.projectRepo.getAll();
262
- return projects.map(p => ({
263
- id: p.id,
264
- name: p.name,
265
- path: p.path,
266
- language: p.language,
267
- framework: p.framework,
268
- moduleCount: this.codeModuleRepo.findByProject(p.id).length,
269
- }));
270
- }
271
- }
@@ -1,266 +0,0 @@
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 type { MatchingConfig } from '../types/config.types.js';
6
- import type { EmbeddingEngine } from '../embeddings/engine.js';
7
- import { parseError } from '../parsing/error-parser.js';
8
- import { generateFingerprint } from '../matching/fingerprint.js';
9
- import { matchError, type MatchResult } from '../matching/error-matcher.js';
10
- import { getEventBus } from '../utils/events.js';
11
- import { getLogger } from '../utils/logger.js';
12
-
13
- export interface ReportErrorInput {
14
- project: string;
15
- errorOutput: string;
16
- filePath?: string;
17
- terminalId?: number;
18
- taskContext?: string;
19
- workingDirectory?: string;
20
- command?: string;
21
- }
22
-
23
- export interface ErrorQueryInput {
24
- projectId?: number;
25
- resolved?: boolean;
26
- search?: string;
27
- limit?: number;
28
- }
29
-
30
- export class ErrorService {
31
- private logger = getLogger();
32
- private eventBus = getEventBus();
33
- private matchingConfig: MatchingConfig | null = null;
34
- private embeddingEngine: EmbeddingEngine | null = null;
35
-
36
- constructor(
37
- private errorRepo: ErrorRepository,
38
- private projectRepo: ProjectRepository,
39
- private synapseManager: SynapseManager,
40
- matchingConfig?: MatchingConfig,
41
- ) {
42
- this.matchingConfig = matchingConfig ?? null;
43
- }
44
-
45
- setEmbeddingEngine(engine: EmbeddingEngine): void {
46
- this.embeddingEngine = engine;
47
- }
48
-
49
- report(input: ReportErrorInput): { errorId: number; isNew: boolean; matches: MatchResult[]; crossProjectMatches?: MatchResult[] } {
50
- // 1. Ensure project exists
51
- let project = this.projectRepo.findByName(input.project);
52
- if (!project) {
53
- const id = this.projectRepo.create({ name: input.project, path: null, language: null, framework: null });
54
- project = this.projectRepo.getById(id)!;
55
- }
56
-
57
- // 2. Build context from available information
58
- const context = this.buildContext(input);
59
-
60
- // 3. Parse the error
61
- const parsed = parseError(input.errorOutput);
62
- if (!parsed) {
63
- this.logger.warn('Could not parse error output');
64
- const errorId = this.errorRepo.create({
65
- project_id: project.id,
66
- terminal_id: input.terminalId ?? null,
67
- fingerprint: '',
68
- type: 'UnknownError',
69
- message: input.errorOutput.split('\n')[0] ?? input.errorOutput,
70
- raw_output: input.errorOutput,
71
- context,
72
- file_path: input.filePath ?? null,
73
- line_number: null,
74
- column_number: null,
75
- });
76
- return { errorId, isNew: true, matches: [] };
77
- }
78
-
79
- // 4. Generate fingerprint
80
- const fingerprint = generateFingerprint(parsed.errorType, parsed.message, parsed.frames);
81
-
82
- // 5. Check for existing error with same fingerprint
83
- const existing = this.errorRepo.findByFingerprint(fingerprint);
84
- if (existing.length > 0) {
85
- const err = existing[0]!;
86
- this.errorRepo.incrementOccurrence(err.id);
87
- // Update context if previously null
88
- if (!err.context && context) {
89
- this.errorRepo.update(err.id, { context });
90
- }
91
- this.logger.info(`Known error (id=${err.id}), occurrence incremented`);
92
-
93
- // Strengthen synapse
94
- this.synapseManager.strengthen(
95
- { type: 'error', id: err.id },
96
- { type: 'project', id: project.id },
97
- 'co_occurs',
98
- );
99
-
100
- return { errorId: err.id, isNew: false, matches: [] };
101
- }
102
-
103
- // 6. Create new error record
104
- const errorId = this.errorRepo.create({
105
- project_id: project.id,
106
- terminal_id: input.terminalId ?? null,
107
- fingerprint,
108
- type: parsed.errorType,
109
- message: parsed.message,
110
- raw_output: input.errorOutput,
111
- context,
112
- file_path: parsed.sourceFile ?? input.filePath ?? null,
113
- line_number: parsed.sourceLine ?? null,
114
- column_number: null,
115
- });
116
-
117
- // 7. Create synapse: error ↔ project
118
- this.synapseManager.strengthen(
119
- { type: 'error', id: errorId },
120
- { type: 'project', id: project.id },
121
- 'co_occurs',
122
- );
123
-
124
- // 8. Find similar errors
125
- const candidates = this.errorRepo.findByProject(project.id)
126
- .filter(e => e.id !== errorId);
127
- const newError = this.errorRepo.getById(errorId)!;
128
- const matches = matchError(newError, candidates);
129
-
130
- // 9. Create similarity synapses for strong matches
131
- for (const match of matches.filter(m => m.isStrong)) {
132
- this.synapseManager.strengthen(
133
- { type: 'error', id: errorId },
134
- { type: 'error', id: match.errorId },
135
- 'similar_to',
136
- );
137
- }
138
-
139
- // 10. Error Chain Detection: link to recent unresolved errors in same project
140
- this.detectErrorChain(errorId, project.id);
141
-
142
- // 11. Cross-Project Transfer Learning
143
- let crossProjectMatches: MatchResult[] = [];
144
- if (this.matchingConfig?.crossProjectMatching) {
145
- crossProjectMatches = this.findCrossProjectMatches(newError, project.id);
146
- for (const match of crossProjectMatches.filter(m => m.isStrong)) {
147
- this.synapseManager.strengthen(
148
- { type: 'error', id: errorId },
149
- { type: 'error', id: match.errorId },
150
- 'cross_project',
151
- );
152
- }
153
- }
154
-
155
- this.eventBus.emit('error:reported', { errorId, projectId: project.id, fingerprint });
156
- this.logger.info(`New error reported (id=${errorId}, type=${parsed.errorType})`);
157
-
158
- return { errorId, isNew: true, matches, crossProjectMatches };
159
- }
160
-
161
- query(input: ErrorQueryInput): ErrorRecord[] {
162
- if (input.search && input.search.trim()) {
163
- return this.errorRepo.search(input.search.trim());
164
- }
165
- if (input.resolved === false) {
166
- return this.errorRepo.findUnresolved(input.projectId);
167
- }
168
- if (input.projectId) {
169
- return this.errorRepo.findByProject(input.projectId);
170
- }
171
- // Default: return recent errors (most recent first)
172
- return this.errorRepo.findAll(input.limit ?? 100);
173
- }
174
-
175
- matchSimilar(errorId: number): MatchResult[] {
176
- const error = this.errorRepo.getById(errorId);
177
- if (!error) return [];
178
-
179
- const candidates = this.errorRepo.findByProject(error.project_id)
180
- .filter(e => e.id !== errorId);
181
-
182
- // Hybrid search: include vector scores if embedding engine is available
183
- const vectorScores = this.embeddingEngine?.isReady()
184
- ? this.embeddingEngine.computeErrorVectorScores(errorId, error.project_id)
185
- : undefined;
186
-
187
- return matchError(error, candidates, vectorScores);
188
- }
189
-
190
- resolve(errorId: number, solutionId?: number): void {
191
- this.errorRepo.update(errorId, {
192
- resolved: 1,
193
- resolved_at: new Date().toISOString(),
194
- });
195
-
196
- if (solutionId) {
197
- this.eventBus.emit('error:resolved', { errorId, solutionId });
198
- }
199
- }
200
-
201
- getById(id: number): ErrorRecord | undefined {
202
- return this.errorRepo.getById(id);
203
- }
204
-
205
- countSince(since: string, projectId?: number): number {
206
- return this.errorRepo.countSince(since, projectId);
207
- }
208
-
209
- getErrorChain(errorId: number): { parents: ErrorRecord[]; children: ErrorRecord[] } {
210
- return {
211
- parents: this.errorRepo.findChainParents(errorId),
212
- children: this.errorRepo.findChainChildren(errorId),
213
- };
214
- }
215
-
216
- private findCrossProjectMatches(error: ErrorRecord, currentProjectId: number): MatchResult[] {
217
- const allProjects = this.projectRepo.getAll();
218
- const otherProjects = allProjects.filter(p => p.id !== currentProjectId);
219
- const weight = this.matchingConfig?.crossProjectWeight ?? 0.7;
220
-
221
- const allMatches: MatchResult[] = [];
222
- for (const project of otherProjects) {
223
- const candidates = this.errorRepo.findByProject(project.id)
224
- .filter(e => e.resolved === 1); // Only look at resolved errors from other projects
225
- const matches = matchError(error, candidates);
226
- // Apply cross-project weight discount
227
- for (const match of matches) {
228
- match.score *= weight;
229
- match.isStrong = match.score >= 0.90;
230
- allMatches.push(match);
231
- }
232
- }
233
-
234
- return allMatches.sort((a, b) => b.score - a.score).slice(0, 5);
235
- }
236
-
237
- private detectErrorChain(newErrorId: number, projectId: number): void {
238
- // Look for recent unresolved errors in the same project (last 10 minutes)
239
- const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000).toISOString();
240
- const recentErrors = this.errorRepo.findRecentByProject(projectId, tenMinutesAgo, 5);
241
-
242
- for (const recent of recentErrors) {
243
- if (recent.id === newErrorId) continue;
244
- if (recent.resolved === 0) {
245
- // This new error appeared while trying to fix a recent error → chain
246
- this.errorRepo.createChain(recent.id, newErrorId, 'caused_by_fix');
247
- this.synapseManager.strengthen(
248
- { type: 'error', id: recent.id },
249
- { type: 'error', id: newErrorId },
250
- 'causes',
251
- );
252
- this.logger.info(`Error chain: #${recent.id} → #${newErrorId} (caused_by_fix)`);
253
- break; // Link to most recent parent only
254
- }
255
- }
256
- }
257
-
258
- private buildContext(input: ReportErrorInput): string | null {
259
- const parts: string[] = [];
260
- if (input.taskContext) parts.push(`task: ${input.taskContext}`);
261
- if (input.command) parts.push(`command: ${input.command}`);
262
- if (input.workingDirectory) parts.push(`cwd: ${input.workingDirectory}`);
263
- if (input.filePath) parts.push(`file: ${input.filePath}`);
264
- return parts.length > 0 ? parts.join(' | ') : null;
265
- }
266
- }
@@ -1,132 +0,0 @@
1
- import { execSync } from 'node:child_process';
2
- import type Database from 'better-sqlite3';
3
- import type { SynapseManager } from '../synapses/synapse-manager.js';
4
- import { getLogger } from '../utils/logger.js';
5
-
6
- export interface GitCommitInfo {
7
- hash: string;
8
- message: string;
9
- author: string;
10
- timestamp: string;
11
- filesChanged: number;
12
- insertions: number;
13
- deletions: number;
14
- }
15
-
16
- export class GitService {
17
- private logger = getLogger();
18
-
19
- constructor(
20
- private db: Database.Database,
21
- private synapseManager: SynapseManager,
22
- ) {}
23
-
24
- /**
25
- * Get current git info for context enrichment
26
- */
27
- getGitContext(cwd?: string): { branch: string | null; diff: string | null; lastCommit: string | null } {
28
- try {
29
- const opts = cwd ? { cwd, timeout: 5000 } : { timeout: 5000 };
30
- const branch = execSync('git rev-parse --abbrev-ref HEAD', { ...opts, encoding: 'utf8' }).trim();
31
- const diff = execSync('git diff --stat HEAD', { ...opts, encoding: 'utf8' }).trim();
32
- const lastCommit = execSync('git log -1 --pretty=format:"%H %s"', { ...opts, encoding: 'utf8' }).trim();
33
- return { branch, diff: diff || null, lastCommit };
34
- } catch {
35
- return { branch: null, diff: null, lastCommit: null };
36
- }
37
- }
38
-
39
- /**
40
- * Store a git commit and link it to an error
41
- */
42
- linkErrorToCommit(errorId: number, projectId: number, commitHash: string, relationship: string = 'introduced_by'): void {
43
- try {
44
- // Store commit info
45
- const commitInfo = this.getCommitInfo(commitHash);
46
- if (commitInfo) {
47
- this.db.prepare(`
48
- INSERT OR IGNORE INTO git_commits (project_id, commit_hash, message, author, timestamp, files_changed, insertions, deletions)
49
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
50
- `).run(projectId, commitInfo.hash, commitInfo.message, commitInfo.author, commitInfo.timestamp,
51
- commitInfo.filesChanged, commitInfo.insertions, commitInfo.deletions);
52
- }
53
-
54
- // Link error to commit
55
- this.db.prepare(`
56
- INSERT OR IGNORE INTO error_commits (error_id, commit_hash, relationship)
57
- VALUES (?, ?, ?)
58
- `).run(errorId, commitHash, relationship);
59
-
60
- this.logger.info(`Linked error #${errorId} to commit ${commitHash.slice(0, 8)} (${relationship})`);
61
- } catch (err) {
62
- this.logger.warn(`Failed to link error to commit: ${err}`);
63
- }
64
- }
65
-
66
- /**
67
- * Find which commit introduced an error
68
- */
69
- findIntroducingCommit(errorId: number): Array<{ commitHash: string; message: string; relationship: string }> {
70
- const rows = this.db.prepare(`
71
- SELECT ec.commit_hash, ec.relationship, gc.message
72
- FROM error_commits ec
73
- LEFT JOIN git_commits gc ON ec.commit_hash = gc.commit_hash
74
- WHERE ec.error_id = ?
75
- ORDER BY ec.created_at DESC
76
- `).all(errorId) as Array<{ commit_hash: string; message: string | null; relationship: string }>;
77
-
78
- return rows.map(r => ({
79
- commitHash: r.commit_hash,
80
- message: r.message ?? 'unknown',
81
- relationship: r.relationship,
82
- }));
83
- }
84
-
85
- /**
86
- * Find errors introduced by a specific commit
87
- */
88
- findErrorsByCommit(commitHash: string): Array<{ errorId: number; relationship: string }> {
89
- const rows = this.db.prepare(`
90
- SELECT error_id, relationship FROM error_commits WHERE commit_hash = ?
91
- `).all(commitHash) as Array<{ error_id: number; relationship: string }>;
92
-
93
- return rows.map(r => ({ errorId: r.error_id, relationship: r.relationship }));
94
- }
95
-
96
- /**
97
- * Capture current git diff for error context
98
- */
99
- captureDiff(cwd?: string): string | null {
100
- try {
101
- const opts = cwd ? { cwd, timeout: 5000, encoding: 'utf8' as const } : { timeout: 5000, encoding: 'utf8' as const };
102
- const diff = execSync('git diff HEAD --no-color', opts).trim();
103
- // Truncate to avoid huge diffs
104
- return diff.length > 5000 ? diff.slice(0, 5000) + '\n... (truncated)' : diff || null;
105
- } catch {
106
- return null;
107
- }
108
- }
109
-
110
- private getCommitInfo(hash: string, cwd?: string): GitCommitInfo | null {
111
- try {
112
- const opts = cwd ? { cwd, timeout: 5000, encoding: 'utf8' as const } : { timeout: 5000, encoding: 'utf8' as const };
113
- const info = execSync(`git log -1 --pretty=format:"%H|||%s|||%an|||%aI" ${hash}`, opts).trim();
114
- const [commitHash, message, author, timestamp] = info.split('|||');
115
-
116
- const stat = execSync(`git diff --stat ${hash}~1..${hash}`, opts).trim();
117
- const statMatch = stat.match(/(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?/);
118
-
119
- return {
120
- hash: commitHash ?? hash,
121
- message: message ?? '',
122
- author: author ?? '',
123
- timestamp: timestamp ?? new Date().toISOString(),
124
- filesChanged: parseInt(statMatch?.[1] ?? '0', 10),
125
- insertions: parseInt(statMatch?.[2] ?? '0', 10),
126
- deletions: parseInt(statMatch?.[3] ?? '0', 10),
127
- };
128
- } catch {
129
- return null;
130
- }
131
- }
132
- }