@timmeck/brain 1.8.5 → 2.0.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 (221) hide show
  1. package/.dockerignore +11 -0
  2. package/Dockerfile +21 -0
  3. package/README.md +19 -0
  4. package/assets/brain-avatar-256.png +0 -0
  5. package/assets/brain-avatar-512.png +0 -0
  6. package/assets/brain-avatar.png +0 -0
  7. package/brain.log +1164 -0
  8. package/{src/cli/commands/dashboard.ts → dashboard.html} +688 -807
  9. package/dist/api/server.d.ts +4 -18
  10. package/dist/api/server.js +4 -173
  11. package/dist/api/server.js.map +1 -1
  12. package/dist/brain.d.ts +6 -0
  13. package/dist/brain.js +56 -4
  14. package/dist/brain.js.map +1 -1
  15. package/dist/cli/colors.d.ts +4 -25
  16. package/dist/cli/colors.js +3 -89
  17. package/dist/cli/colors.js.map +1 -1
  18. package/dist/cli/commands/peers.d.ts +2 -0
  19. package/dist/cli/commands/peers.js +38 -0
  20. package/dist/cli/commands/peers.js.map +1 -0
  21. package/dist/cli/commands/start.js +54 -17
  22. package/dist/cli/commands/start.js.map +1 -1
  23. package/dist/db/connection.d.ts +1 -2
  24. package/dist/db/connection.js +1 -18
  25. package/dist/db/connection.js.map +1 -1
  26. package/dist/index.js +3 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
  29. package/dist/ipc/__tests__/protocol.test.js +117 -0
  30. package/dist/ipc/__tests__/protocol.test.js.map +1 -0
  31. package/dist/ipc/client.d.ts +1 -16
  32. package/dist/ipc/client.js +1 -100
  33. package/dist/ipc/client.js.map +1 -1
  34. package/dist/ipc/protocol.d.ts +1 -8
  35. package/dist/ipc/protocol.js +1 -28
  36. package/dist/ipc/protocol.js.map +1 -1
  37. package/dist/ipc/router.js +8 -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/mcp/http-server.d.ts +1 -7
  43. package/dist/mcp/http-server.js +6 -117
  44. package/dist/mcp/http-server.js.map +1 -1
  45. package/dist/mcp/server.js +5 -61
  46. package/dist/mcp/server.js.map +1 -1
  47. package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
  48. package/dist/signals/__tests__/fingerprint.test.js +118 -0
  49. package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
  50. package/dist/types/ipc.types.d.ts +1 -11
  51. package/dist/utils/__tests__/hash.test.d.ts +1 -0
  52. package/dist/utils/__tests__/hash.test.js +32 -0
  53. package/dist/utils/__tests__/hash.test.js.map +1 -0
  54. package/dist/utils/__tests__/paths.test.d.ts +1 -0
  55. package/dist/utils/__tests__/paths.test.js +75 -0
  56. package/dist/utils/__tests__/paths.test.js.map +1 -0
  57. package/dist/utils/events.d.ts +4 -8
  58. package/dist/utils/events.js +2 -14
  59. package/dist/utils/events.js.map +1 -1
  60. package/dist/utils/hash.d.ts +1 -1
  61. package/dist/utils/hash.js +1 -4
  62. package/dist/utils/hash.js.map +1 -1
  63. package/dist/utils/logger.d.ts +3 -2
  64. package/dist/utils/logger.js +8 -35
  65. package/dist/utils/logger.js.map +1 -1
  66. package/dist/utils/paths.d.ts +2 -1
  67. package/dist/utils/paths.js +4 -13
  68. package/dist/utils/paths.js.map +1 -1
  69. package/gen_avatar.py +142 -0
  70. package/package.json +2 -1
  71. package/BRAIN_PLAN.md +0 -3324
  72. package/src/api/server.ts +0 -395
  73. package/src/brain.ts +0 -266
  74. package/src/cli/colors.ts +0 -116
  75. package/src/cli/commands/config.ts +0 -169
  76. package/src/cli/commands/doctor.ts +0 -124
  77. package/src/cli/commands/explain.ts +0 -83
  78. package/src/cli/commands/export.ts +0 -31
  79. package/src/cli/commands/import.ts +0 -199
  80. package/src/cli/commands/insights.ts +0 -65
  81. package/src/cli/commands/learn.ts +0 -24
  82. package/src/cli/commands/modules.ts +0 -53
  83. package/src/cli/commands/network.ts +0 -67
  84. package/src/cli/commands/projects.ts +0 -42
  85. package/src/cli/commands/query.ts +0 -120
  86. package/src/cli/commands/start.ts +0 -62
  87. package/src/cli/commands/status.ts +0 -75
  88. package/src/cli/commands/stop.ts +0 -34
  89. package/src/cli/ipc-helper.ts +0 -22
  90. package/src/cli/update-check.ts +0 -63
  91. package/src/code/analyzer.ts +0 -117
  92. package/src/code/fingerprint.ts +0 -87
  93. package/src/code/matcher.ts +0 -129
  94. package/src/code/parsers/generic.ts +0 -29
  95. package/src/code/parsers/python.ts +0 -54
  96. package/src/code/parsers/typescript.ts +0 -65
  97. package/src/code/registry.ts +0 -60
  98. package/src/code/scorer.ts +0 -120
  99. package/src/config.ts +0 -135
  100. package/src/dashboard/server.ts +0 -142
  101. package/src/db/connection.ts +0 -22
  102. package/src/db/migrations/001_core_schema.ts +0 -120
  103. package/src/db/migrations/002_learning_schema.ts +0 -38
  104. package/src/db/migrations/003_code_schema.ts +0 -53
  105. package/src/db/migrations/004_synapses_schema.ts +0 -57
  106. package/src/db/migrations/005_fts_indexes.ts +0 -78
  107. package/src/db/migrations/006_synapses_phase3.ts +0 -17
  108. package/src/db/migrations/007_feedback.ts +0 -13
  109. package/src/db/migrations/008_git_integration.ts +0 -38
  110. package/src/db/migrations/009_embeddings.ts +0 -8
  111. package/src/db/migrations/index.ts +0 -70
  112. package/src/db/repositories/antipattern.repository.ts +0 -66
  113. package/src/db/repositories/code-module.repository.ts +0 -142
  114. package/src/db/repositories/error.repository.ts +0 -189
  115. package/src/db/repositories/insight.repository.ts +0 -99
  116. package/src/db/repositories/notification.repository.ts +0 -66
  117. package/src/db/repositories/project.repository.ts +0 -93
  118. package/src/db/repositories/rule.repository.ts +0 -108
  119. package/src/db/repositories/solution.repository.ts +0 -154
  120. package/src/db/repositories/synapse.repository.ts +0 -163
  121. package/src/db/repositories/terminal.repository.ts +0 -101
  122. package/src/embeddings/engine.ts +0 -238
  123. package/src/hooks/post-tool-use.ts +0 -92
  124. package/src/hooks/post-write.ts +0 -129
  125. package/src/index.ts +0 -63
  126. package/src/ipc/client.ts +0 -118
  127. package/src/ipc/protocol.ts +0 -35
  128. package/src/ipc/router.ts +0 -133
  129. package/src/ipc/server.ts +0 -176
  130. package/src/learning/confidence-scorer.ts +0 -80
  131. package/src/learning/decay.ts +0 -46
  132. package/src/learning/learning-engine.ts +0 -170
  133. package/src/learning/pattern-extractor.ts +0 -90
  134. package/src/learning/rule-generator.ts +0 -74
  135. package/src/main.rs:10:5 +0 -0
  136. package/src/matching/error-matcher.ts +0 -166
  137. package/src/matching/fingerprint.ts +0 -34
  138. package/src/matching/similarity.ts +0 -61
  139. package/src/matching/tfidf.ts +0 -74
  140. package/src/matching/tokenizer.ts +0 -41
  141. package/src/mcp/auto-detect.ts +0 -93
  142. package/src/mcp/http-server.ts +0 -140
  143. package/src/mcp/server.ts +0 -73
  144. package/src/mcp/tools.ts +0 -328
  145. package/src/parsing/error-parser.ts +0 -28
  146. package/src/parsing/parsers/compiler.ts +0 -93
  147. package/src/parsing/parsers/generic.ts +0 -28
  148. package/src/parsing/parsers/go.ts +0 -97
  149. package/src/parsing/parsers/node.ts +0 -69
  150. package/src/parsing/parsers/python.ts +0 -62
  151. package/src/parsing/parsers/rust.ts +0 -50
  152. package/src/parsing/parsers/shell.ts +0 -42
  153. package/src/parsing/types.ts +0 -47
  154. package/src/research/gap-analyzer.ts +0 -135
  155. package/src/research/insight-generator.ts +0 -123
  156. package/src/research/research-engine.ts +0 -116
  157. package/src/research/synergy-detector.ts +0 -126
  158. package/src/research/template-extractor.ts +0 -130
  159. package/src/research/trend-analyzer.ts +0 -127
  160. package/src/services/analytics.service.ts +0 -226
  161. package/src/services/code.service.ts +0 -271
  162. package/src/services/error.service.ts +0 -266
  163. package/src/services/git.service.ts +0 -132
  164. package/src/services/notification.service.ts +0 -41
  165. package/src/services/prevention.service.ts +0 -159
  166. package/src/services/research.service.ts +0 -98
  167. package/src/services/solution.service.ts +0 -174
  168. package/src/services/synapse.service.ts +0 -59
  169. package/src/services/terminal.service.ts +0 -81
  170. package/src/synapses/activation.ts +0 -80
  171. package/src/synapses/decay.ts +0 -38
  172. package/src/synapses/hebbian.ts +0 -69
  173. package/src/synapses/pathfinder.ts +0 -81
  174. package/src/synapses/synapse-manager.ts +0 -113
  175. package/src/types/code.types.ts +0 -52
  176. package/src/types/config.types.ts +0 -103
  177. package/src/types/error.types.ts +0 -67
  178. package/src/types/ipc.types.ts +0 -8
  179. package/src/types/mcp.types.ts +0 -53
  180. package/src/types/research.types.ts +0 -28
  181. package/src/types/solution.types.ts +0 -30
  182. package/src/types/synapse.types.ts +0 -50
  183. package/src/utils/events.ts +0 -45
  184. package/src/utils/hash.ts +0 -5
  185. package/src/utils/logger.ts +0 -48
  186. package/src/utils/paths.ts +0 -19
  187. package/tests/e2e/test_code_intelligence.py +0 -1015
  188. package/tests/e2e/test_error_memory.py +0 -451
  189. package/tests/e2e/test_full_integration.py +0 -534
  190. package/tests/fixtures/code-modules/modules.ts +0 -83
  191. package/tests/fixtures/errors/go.ts +0 -9
  192. package/tests/fixtures/errors/node.ts +0 -24
  193. package/tests/fixtures/errors/python.ts +0 -21
  194. package/tests/fixtures/errors/rust.ts +0 -25
  195. package/tests/fixtures/errors/shell.ts +0 -15
  196. package/tests/fixtures/solutions/solutions.ts +0 -27
  197. package/tests/helpers/setup-db.ts +0 -52
  198. package/tests/integration/code-flow.test.ts +0 -86
  199. package/tests/integration/error-flow.test.ts +0 -83
  200. package/tests/integration/ipc-flow.test.ts +0 -166
  201. package/tests/integration/learning-cycle.test.ts +0 -82
  202. package/tests/integration/synapse-flow.test.ts +0 -117
  203. package/tests/unit/code/analyzer.test.ts +0 -58
  204. package/tests/unit/code/fingerprint.test.ts +0 -51
  205. package/tests/unit/code/scorer.test.ts +0 -55
  206. package/tests/unit/learning/confidence-scorer.test.ts +0 -60
  207. package/tests/unit/learning/decay.test.ts +0 -45
  208. package/tests/unit/learning/pattern-extractor.test.ts +0 -50
  209. package/tests/unit/matching/error-matcher.test.ts +0 -69
  210. package/tests/unit/matching/fingerprint.test.ts +0 -47
  211. package/tests/unit/matching/similarity.test.ts +0 -65
  212. package/tests/unit/matching/tfidf.test.ts +0 -71
  213. package/tests/unit/matching/tokenizer.test.ts +0 -83
  214. package/tests/unit/parsing/parsers.test.ts +0 -113
  215. package/tests/unit/research/gap-analyzer.test.ts +0 -45
  216. package/tests/unit/research/trend-analyzer.test.ts +0 -45
  217. package/tests/unit/synapses/activation.test.ts +0 -80
  218. package/tests/unit/synapses/decay.test.ts +0 -27
  219. package/tests/unit/synapses/hebbian.test.ts +0 -96
  220. package/tests/unit/synapses/pathfinder.test.ts +0 -72
  221. package/tsconfig.json +0 -18
@@ -1,226 +0,0 @@
1
- import type { ErrorRecord } from '../types/error.types.js';
2
- import type { ErrorRepository } from '../db/repositories/error.repository.js';
3
- import type { SolutionRepository } from '../db/repositories/solution.repository.js';
4
- import type { CodeModuleRepository } from '../db/repositories/code-module.repository.js';
5
- import type { RuleRepository } from '../db/repositories/rule.repository.js';
6
- import type { AntipatternRepository } from '../db/repositories/antipattern.repository.js';
7
- import type { InsightRepository } from '../db/repositories/insight.repository.js';
8
- import type { SynapseManager } from '../synapses/synapse-manager.js';
9
- import type { NetworkStats } from '../types/synapse.types.js';
10
-
11
- export interface ProjectSummary {
12
- errors: { total: number; unresolved: number; last7d: number };
13
- solutions: { total: number };
14
- rules: { active: number };
15
- antipatterns: { total: number };
16
- modules: { total: number };
17
- insights: { active: number };
18
- healthScore?: number;
19
- }
20
-
21
- export interface NetworkOverview {
22
- stats: NetworkStats;
23
- strongestSynapses: Array<{
24
- id: number;
25
- source: string;
26
- target: string;
27
- type: string;
28
- weight: number;
29
- }>;
30
- }
31
-
32
- export class AnalyticsService {
33
- constructor(
34
- private errorRepo: ErrorRepository,
35
- private solutionRepo: SolutionRepository,
36
- private codeModuleRepo: CodeModuleRepository,
37
- private ruleRepo: RuleRepository,
38
- private antipatternRepo: AntipatternRepository,
39
- private insightRepo: InsightRepository,
40
- private synapseManager: SynapseManager,
41
- ) {}
42
-
43
- getSummary(projectId?: number): ProjectSummary {
44
- const sevenDaysAgo = new Date(Date.now() - 7 * 86400000).toISOString();
45
-
46
- const unresolvedErrors = this.errorRepo.findUnresolved(projectId);
47
- const allErrors = projectId ? this.errorRepo.findByProject(projectId) : [];
48
- const last7dCount = this.errorRepo.countSince(sevenDaysAgo, projectId);
49
-
50
- const rules = this.ruleRepo.findActive(projectId);
51
- const antipatterns = projectId
52
- ? this.antipatternRepo.findByProject(projectId)
53
- : this.antipatternRepo.findGlobal();
54
-
55
- const moduleCount = projectId
56
- ? this.codeModuleRepo.findByProject(projectId).length
57
- : this.codeModuleRepo.countAll();
58
- const insights = this.insightRepo.findActive(projectId);
59
-
60
- return {
61
- errors: {
62
- total: allErrors.length,
63
- unresolved: unresolvedErrors.length,
64
- last7d: last7dCount,
65
- },
66
- solutions: { total: 0 }, // solutions are global, not per-project
67
- rules: { active: rules.length },
68
- antipatterns: { total: antipatterns.length },
69
- modules: { total: moduleCount },
70
- insights: { active: insights.length },
71
- };
72
- }
73
-
74
- computeHealthScore(projectId?: number): number {
75
- const summary = this.getSummary(projectId);
76
- const networkStats = this.synapseManager.getNetworkStats();
77
-
78
- let score = 0;
79
- let maxScore = 0;
80
-
81
- // Data Volume (30 points)
82
- maxScore += 30;
83
- const dataVolume = summary.errors.total + (summary.modules.total * 2) + summary.solutions.total;
84
- score += Math.min(30, dataVolume * 0.3);
85
-
86
- // Synapse Density (20 points) - more connections = richer network
87
- maxScore += 20;
88
- const synapseDensity = networkStats.totalSynapses / Math.max(1, networkStats.totalNodes);
89
- score += Math.min(20, synapseDensity * 5);
90
-
91
- // Solution Coverage (20 points) - resolved errors vs total
92
- maxScore += 20;
93
- if (summary.errors.total > 0) {
94
- const resolvedRate = 1 - (summary.errors.unresolved / summary.errors.total);
95
- score += resolvedRate * 20;
96
- } else {
97
- score += 10; // No errors = neutral
98
- }
99
-
100
- // Learning Activity (15 points) - active rules + insights
101
- maxScore += 15;
102
- const learningActivity = summary.rules.active + summary.insights.active;
103
- score += Math.min(15, learningActivity * 1.5);
104
-
105
- // Error Trend (15 points) - fewer recent errors = better
106
- maxScore += 15;
107
- if (summary.errors.total > 0) {
108
- const recentRatio = summary.errors.last7d / Math.max(1, summary.errors.total);
109
- // Low recent ratio = health is good (errors decreasing)
110
- score += Math.max(0, 15 - recentRatio * 30);
111
- } else {
112
- score += 15;
113
- }
114
-
115
- return Math.round((score / maxScore) * 100);
116
- }
117
-
118
- getTimeSeries(projectId?: number, days: number = 30): Array<{ date: string; errors: number; solutions: number }> {
119
- const series: Array<{ date: string; errors: number; solutions: number }> = [];
120
-
121
- for (let i = days - 1; i >= 0; i--) {
122
- const dayStart = new Date(Date.now() - (i + 1) * 86400000).toISOString();
123
- const dayEnd = new Date(Date.now() - i * 86400000).toISOString();
124
-
125
- const errorsInDay = this.errorRepo.countSince(dayStart, projectId) - this.errorRepo.countSince(dayEnd, projectId);
126
-
127
- series.push({
128
- date: dayStart.split('T')[0]!,
129
- errors: Math.max(0, errorsInDay),
130
- solutions: 0, // Approximation
131
- });
132
- }
133
-
134
- return series;
135
- }
136
-
137
- explainError(errorId: number): {
138
- error: ErrorRecord | undefined;
139
- solutions: Array<{ id: number; description: string; confidence: number; successRate: number }>;
140
- chain: { parents: ErrorRecord[]; children: ErrorRecord[] };
141
- relatedErrors: Array<{ id: number; type: string; message: string; similarity: number }>;
142
- rules: Array<{ id: number; pattern: string; action: string; confidence: number }>;
143
- insights: Array<{ id: number; type: string; title: string }>;
144
- synapseConnections: number;
145
- } {
146
- const error = this.errorRepo.getById(errorId);
147
- if (!error) {
148
- return {
149
- error: undefined, solutions: [], chain: { parents: [], children: [] },
150
- relatedErrors: [], rules: [], insights: [], synapseConnections: 0,
151
- };
152
- }
153
-
154
- // Solutions
155
- const solutions = this.solutionRepo.findForError(errorId).map(s => ({
156
- id: s.id,
157
- description: s.description,
158
- confidence: s.confidence,
159
- successRate: this.solutionRepo.successRate(s.id),
160
- }));
161
-
162
- // Error chain
163
- const parents = this.errorRepo.findChainParents(errorId);
164
- const children = this.errorRepo.findChainChildren(errorId);
165
-
166
- // Related via synapses
167
- const context = this.synapseManager.getErrorContext(errorId);
168
- const relatedErrors = context.relatedErrors.map(r => {
169
- const e = this.errorRepo.getById(r.node.id);
170
- return {
171
- id: r.node.id,
172
- type: e?.type ?? 'unknown',
173
- message: e?.message ?? '',
174
- similarity: r.activation,
175
- };
176
- });
177
-
178
- // Prevention rules
179
- const matchedRules = this.ruleRepo.findActive(error.project_id);
180
- const rules = matchedRules
181
- .filter(r => {
182
- try { return new RegExp(r.pattern, 'i').test(`${error.type}: ${error.message}`); }
183
- catch { return false; }
184
- })
185
- .map(r => ({ id: r.id, pattern: r.pattern, action: r.action, confidence: r.confidence }));
186
-
187
- // Related insights
188
- const insights = context.insights.map(i => ({
189
- id: i.node.id,
190
- type: i.node.type,
191
- title: `Insight #${i.node.id}`,
192
- }));
193
-
194
- // Total synapse connections
195
- const allConnections = this.synapseManager.activate({ type: 'error', id: errorId });
196
-
197
- return {
198
- error,
199
- solutions,
200
- chain: { parents, children },
201
- relatedErrors,
202
- rules,
203
- insights,
204
- synapseConnections: allConnections.length,
205
- };
206
- }
207
-
208
- getNetworkOverview(limit: number = 10): NetworkOverview {
209
- const stats = this.synapseManager.getNetworkStats();
210
- // Use diverse sampling for dashboard (spread across synapse types)
211
- const diverse = limit >= 30
212
- ? this.synapseManager.getDiverseSynapses(Math.ceil(limit / 3))
213
- : this.synapseManager.getStrongestSynapses(limit);
214
-
215
- return {
216
- stats,
217
- strongestSynapses: diverse.map(s => ({
218
- id: s.id,
219
- source: `${s.source_type}:${s.source_id}`,
220
- target: `${s.target_type}:${s.target_id}`,
221
- type: s.synapse_type,
222
- weight: s.weight,
223
- })),
224
- };
225
- }
226
- }
@@ -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
- }