claude-brain 0.3.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 (200) hide show
  1. package/README.md +157 -0
  2. package/VERSION +1 -0
  3. package/assets/CLAUDE.md +307 -0
  4. package/bunfig.toml +8 -0
  5. package/package.json +74 -0
  6. package/src/automation/auto-context.ts +240 -0
  7. package/src/automation/decision-detector.ts +452 -0
  8. package/src/automation/index.ts +11 -0
  9. package/src/automation/proactive-recall.ts +373 -0
  10. package/src/automation/project-detector.ts +297 -0
  11. package/src/cli/auto-setup.ts +74 -0
  12. package/src/cli/bin.ts +110 -0
  13. package/src/cli/commands/install-mcp.ts +50 -0
  14. package/src/cli/commands/serve.ts +129 -0
  15. package/src/cli/diagnose.ts +4 -0
  16. package/src/cli/health-check.ts +4 -0
  17. package/src/cli/migrate-chroma.ts +106 -0
  18. package/src/cli/setup.ts +4 -0
  19. package/src/config/defaults.ts +47 -0
  20. package/src/config/home.ts +55 -0
  21. package/src/config/index.ts +7 -0
  22. package/src/config/loader.ts +166 -0
  23. package/src/config/migration.ts +76 -0
  24. package/src/config/schema.ts +257 -0
  25. package/src/config/validator.ts +184 -0
  26. package/src/config/watcher.ts +86 -0
  27. package/src/context/assembler.ts +398 -0
  28. package/src/context/cache-manager.ts +101 -0
  29. package/src/context/formatter.ts +84 -0
  30. package/src/context/hierarchy.ts +85 -0
  31. package/src/context/index.ts +83 -0
  32. package/src/context/progress-tracker.ts +174 -0
  33. package/src/context/standards-manager.ts +267 -0
  34. package/src/context/types.ts +252 -0
  35. package/src/context/validator.ts +58 -0
  36. package/src/cross-project/affinity.ts +162 -0
  37. package/src/cross-project/generalizer.ts +283 -0
  38. package/src/cross-project/index.ts +13 -0
  39. package/src/cross-project/transfer.ts +201 -0
  40. package/src/diagnostics/index.ts +123 -0
  41. package/src/health/index.ts +229 -0
  42. package/src/index.ts +7 -0
  43. package/src/knowledge/entity-extractor.ts +416 -0
  44. package/src/knowledge/graph/builder.ts +159 -0
  45. package/src/knowledge/graph/linker.ts +201 -0
  46. package/src/knowledge/graph/memory-graph.ts +359 -0
  47. package/src/knowledge/graph/schema.ts +99 -0
  48. package/src/knowledge/graph/search.ts +168 -0
  49. package/src/knowledge/relationship-extractor.ts +108 -0
  50. package/src/memory/chroma/client.ts +169 -0
  51. package/src/memory/chroma/collection-manager.ts +94 -0
  52. package/src/memory/chroma/config.ts +46 -0
  53. package/src/memory/chroma/embeddings.ts +153 -0
  54. package/src/memory/chroma/index.ts +82 -0
  55. package/src/memory/chroma/migration.ts +270 -0
  56. package/src/memory/chroma/schemas.ts +69 -0
  57. package/src/memory/chroma/search.ts +315 -0
  58. package/src/memory/chroma/store.ts +694 -0
  59. package/src/memory/consolidation/archiver.ts +164 -0
  60. package/src/memory/consolidation/merger.ts +186 -0
  61. package/src/memory/consolidation/scorer.ts +138 -0
  62. package/src/memory/context-builder.ts +236 -0
  63. package/src/memory/database.ts +169 -0
  64. package/src/memory/embedding-utils.ts +156 -0
  65. package/src/memory/embeddings.ts +226 -0
  66. package/src/memory/episodic/detector.ts +108 -0
  67. package/src/memory/episodic/manager.ts +334 -0
  68. package/src/memory/episodic/summarizer.ts +179 -0
  69. package/src/memory/episodic/types.ts +52 -0
  70. package/src/memory/index.ts +395 -0
  71. package/src/memory/knowledge-extractor.ts +455 -0
  72. package/src/memory/learning.ts +378 -0
  73. package/src/memory/patterns.ts +396 -0
  74. package/src/memory/schema.ts +56 -0
  75. package/src/memory/search.ts +309 -0
  76. package/src/memory/store.ts +344 -0
  77. package/src/memory/types.ts +121 -0
  78. package/src/optimization/index.ts +10 -0
  79. package/src/optimization/precompute.ts +202 -0
  80. package/src/optimization/semantic-cache.ts +207 -0
  81. package/src/orchestrator/coordinator.ts +272 -0
  82. package/src/orchestrator/decision-logger.ts +228 -0
  83. package/src/orchestrator/event-emitter.ts +198 -0
  84. package/src/orchestrator/event-queue.ts +184 -0
  85. package/src/orchestrator/handlers/base-handler.ts +70 -0
  86. package/src/orchestrator/handlers/context-handler.ts +73 -0
  87. package/src/orchestrator/handlers/decision-handler.ts +204 -0
  88. package/src/orchestrator/handlers/index.ts +10 -0
  89. package/src/orchestrator/handlers/status-handler.ts +131 -0
  90. package/src/orchestrator/handlers/task-handler.ts +171 -0
  91. package/src/orchestrator/index.ts +275 -0
  92. package/src/orchestrator/task-parser.ts +284 -0
  93. package/src/orchestrator/types.ts +98 -0
  94. package/src/phase12/index.ts +456 -0
  95. package/src/prediction/context-anticipator.ts +198 -0
  96. package/src/prediction/decision-predictor.ts +184 -0
  97. package/src/prediction/index.ts +13 -0
  98. package/src/prediction/recommender.ts +268 -0
  99. package/src/reasoning/chain-retrieval.ts +247 -0
  100. package/src/reasoning/counterfactual.ts +248 -0
  101. package/src/reasoning/index.ts +13 -0
  102. package/src/reasoning/synthesizer.ts +169 -0
  103. package/src/retrieval/bm25/index.ts +300 -0
  104. package/src/retrieval/bm25/tokenizer.ts +184 -0
  105. package/src/retrieval/feedback/adaptive.ts +223 -0
  106. package/src/retrieval/feedback/index.ts +16 -0
  107. package/src/retrieval/feedback/metrics.ts +223 -0
  108. package/src/retrieval/feedback/store.ts +283 -0
  109. package/src/retrieval/fusion/index.ts +194 -0
  110. package/src/retrieval/fusion/rrf.ts +163 -0
  111. package/src/retrieval/index.ts +12 -0
  112. package/src/retrieval/pipeline.ts +375 -0
  113. package/src/retrieval/query/expander.ts +198 -0
  114. package/src/retrieval/query/index.ts +27 -0
  115. package/src/retrieval/query/intent-classifier.ts +236 -0
  116. package/src/retrieval/query/temporal-parser.ts +295 -0
  117. package/src/retrieval/reranker/index.ts +188 -0
  118. package/src/retrieval/reranker/model.ts +95 -0
  119. package/src/retrieval/service.ts +125 -0
  120. package/src/retrieval/types.ts +162 -0
  121. package/src/scripts/health-check.ts +118 -0
  122. package/src/scripts/setup.ts +122 -0
  123. package/src/server/handlers/call-tool.ts +194 -0
  124. package/src/server/handlers/index.ts +9 -0
  125. package/src/server/handlers/list-tools.ts +18 -0
  126. package/src/server/handlers/tools/analyze-decision-evolution.ts +71 -0
  127. package/src/server/handlers/tools/auto-remember.ts +200 -0
  128. package/src/server/handlers/tools/create-project.ts +135 -0
  129. package/src/server/handlers/tools/detect-trends.ts +80 -0
  130. package/src/server/handlers/tools/find-cross-project-patterns.ts +73 -0
  131. package/src/server/handlers/tools/get-activity-log.ts +194 -0
  132. package/src/server/handlers/tools/get-code-standards.ts +124 -0
  133. package/src/server/handlers/tools/get-corrections.ts +154 -0
  134. package/src/server/handlers/tools/get-decision-timeline.ts +86 -0
  135. package/src/server/handlers/tools/get-episode.ts +93 -0
  136. package/src/server/handlers/tools/get-patterns.ts +158 -0
  137. package/src/server/handlers/tools/get-phase12-status.ts +63 -0
  138. package/src/server/handlers/tools/get-project-context.ts +75 -0
  139. package/src/server/handlers/tools/get-recommendations.ts +65 -0
  140. package/src/server/handlers/tools/index.ts +33 -0
  141. package/src/server/handlers/tools/init-project.ts +710 -0
  142. package/src/server/handlers/tools/list-episodes.ts +80 -0
  143. package/src/server/handlers/tools/list-projects.ts +125 -0
  144. package/src/server/handlers/tools/rate-memory.ts +95 -0
  145. package/src/server/handlers/tools/recall-similar.ts +87 -0
  146. package/src/server/handlers/tools/recognize-pattern.ts +126 -0
  147. package/src/server/handlers/tools/record-correction.ts +125 -0
  148. package/src/server/handlers/tools/remember-decision.ts +153 -0
  149. package/src/server/handlers/tools/schemas.ts +241 -0
  150. package/src/server/handlers/tools/search-knowledge-graph.ts +89 -0
  151. package/src/server/handlers/tools/smart-context.ts +124 -0
  152. package/src/server/handlers/tools/update-progress.ts +114 -0
  153. package/src/server/handlers/tools/what-if-analysis.ts +73 -0
  154. package/src/server/http-api.ts +474 -0
  155. package/src/server/index.ts +40 -0
  156. package/src/server/mcp-server.ts +283 -0
  157. package/src/server/providers/index.ts +7 -0
  158. package/src/server/providers/prompts.ts +327 -0
  159. package/src/server/providers/resources.ts +427 -0
  160. package/src/server/services.ts +388 -0
  161. package/src/server/types.ts +39 -0
  162. package/src/server/utils/error-handler.ts +155 -0
  163. package/src/server/utils/index.ts +13 -0
  164. package/src/server/utils/memory-indicator.ts +83 -0
  165. package/src/server/utils/request-context.ts +122 -0
  166. package/src/server/utils/response-formatter.ts +124 -0
  167. package/src/server/utils/validators.ts +210 -0
  168. package/src/setup/index.ts +22 -0
  169. package/src/setup/wizard.ts +321 -0
  170. package/src/temporal/evolution.ts +197 -0
  171. package/src/temporal/index.ts +16 -0
  172. package/src/temporal/query-processor.ts +190 -0
  173. package/src/temporal/timeline.ts +259 -0
  174. package/src/temporal/trends.ts +263 -0
  175. package/src/tools/index.ts +24 -0
  176. package/src/tools/registry.ts +106 -0
  177. package/src/tools/schemas.test.ts +30 -0
  178. package/src/tools/schemas.ts +907 -0
  179. package/src/tools/types.ts +412 -0
  180. package/src/utils/circuit-breaker.ts +130 -0
  181. package/src/utils/cleanup.ts +34 -0
  182. package/src/utils/error-handler.ts +132 -0
  183. package/src/utils/error-messages.ts +60 -0
  184. package/src/utils/fallback.ts +45 -0
  185. package/src/utils/index.ts +54 -0
  186. package/src/utils/logger-utils.ts +80 -0
  187. package/src/utils/logger.ts +88 -0
  188. package/src/utils/phase12-helper.ts +56 -0
  189. package/src/utils/retry.ts +94 -0
  190. package/src/utils/transaction.ts +63 -0
  191. package/src/vault/frontmatter.ts +264 -0
  192. package/src/vault/index.ts +318 -0
  193. package/src/vault/paths.ts +106 -0
  194. package/src/vault/query.ts +422 -0
  195. package/src/vault/reader.ts +264 -0
  196. package/src/vault/templates.ts +186 -0
  197. package/src/vault/types.ts +73 -0
  198. package/src/vault/watcher.ts +277 -0
  199. package/src/vault/writer.ts +393 -0
  200. package/tsconfig.json +30 -0
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Pattern Generalizer
3
+ * Finds common patterns across multiple projects
4
+ */
5
+
6
+ import type { Logger } from 'pino'
7
+ import type { CollectionManager } from '@/memory/chroma/collection-manager'
8
+ import type { EmbeddingProvider } from '@/memory/chroma/embeddings'
9
+
10
+ export interface GeneralizedPattern {
11
+ description: string
12
+ projects: string[]
13
+ occurrences: number
14
+ confidence: number
15
+ examples: Array<{
16
+ project: string
17
+ content: string
18
+ id: string
19
+ }>
20
+ }
21
+
22
+ export interface GeneralizationResult {
23
+ patterns: GeneralizedPattern[]
24
+ projectsAnalyzed: string[]
25
+ totalDecisionsAnalyzed: number
26
+ }
27
+
28
+ export class PatternGeneralizer {
29
+ private logger: Logger
30
+ private collections: CollectionManager
31
+ private embeddings?: EmbeddingProvider
32
+
33
+ constructor(logger: Logger, collections: CollectionManager, embeddings?: EmbeddingProvider) {
34
+ this.logger = logger.child({ component: 'pattern-generalizer' })
35
+ this.collections = collections
36
+ this.embeddings = embeddings
37
+ }
38
+
39
+ /**
40
+ * Find patterns that appear across multiple projects
41
+ */
42
+ async findCrossProjectPatterns(options: {
43
+ minProjects?: number
44
+ limit?: number
45
+ query?: string
46
+ } = {}): Promise<GeneralizationResult> {
47
+ const { minProjects = 2, limit = 20, query } = options
48
+
49
+ // Fetch all decisions
50
+ const allDecisions = await this.fetchAllDecisions(query)
51
+
52
+ // Group by project
53
+ const byProject = new Map<string, Array<{ id: string; content: string }>>()
54
+ for (const d of allDecisions) {
55
+ const project = d.project
56
+ if (!byProject.has(project)) byProject.set(project, [])
57
+ byProject.get(project)!.push({ id: d.id, content: d.content })
58
+ }
59
+
60
+ const projects = Array.from(byProject.keys())
61
+
62
+ if (projects.length < minProjects) {
63
+ return {
64
+ patterns: [],
65
+ projectsAnalyzed: projects,
66
+ totalDecisionsAnalyzed: allDecisions.length
67
+ }
68
+ }
69
+
70
+ // Find cross-project patterns using term overlap
71
+ const crossPatterns = this.findCommonTermPatterns(byProject, minProjects)
72
+
73
+ // If embeddings are available, also do semantic clustering
74
+ if (this.embeddings && allDecisions.length > 0) {
75
+ const semanticPatterns = await this.findSemanticClusters(allDecisions, minProjects)
76
+ crossPatterns.push(...semanticPatterns)
77
+ }
78
+
79
+ // Deduplicate and rank
80
+ const deduped = this.deduplicatePatterns(crossPatterns)
81
+ const ranked = deduped
82
+ .sort((a, b) => b.occurrences * b.confidence - a.occurrences * a.confidence)
83
+ .slice(0, limit)
84
+
85
+ return {
86
+ patterns: ranked,
87
+ projectsAnalyzed: projects,
88
+ totalDecisionsAnalyzed: allDecisions.length
89
+ }
90
+ }
91
+
92
+ private async fetchAllDecisions(query?: string): Promise<Array<{
93
+ id: string
94
+ content: string
95
+ project: string
96
+ }>> {
97
+ try {
98
+ const collection = await this.collections.getDecisions()
99
+
100
+ if (query && this.embeddings) {
101
+ const embedding = await this.embeddings.generate(query)
102
+ const results = await collection.query({
103
+ queryEmbeddings: [embedding],
104
+ nResults: 100,
105
+ include: ['documents', 'metadatas', 'distances']
106
+ })
107
+
108
+ if (!results.ids || !results.ids[0]) return []
109
+
110
+ return results.ids[0].map((id: string, i: number) => ({
111
+ id,
112
+ content: results.documents?.[0]?.[i] || '',
113
+ project: (results.metadatas?.[0]?.[i] as any)?.project || ''
114
+ }))
115
+ }
116
+
117
+ const results = await collection.get({
118
+ include: ['documents', 'metadatas']
119
+ })
120
+
121
+ return results.ids.map((id, i) => ({
122
+ id,
123
+ content: results.documents![i] as string || '',
124
+ project: (results.metadatas![i] as any)?.project || ''
125
+ }))
126
+ } catch (error) {
127
+ this.logger.warn({ error }, 'Failed to fetch decisions for generalization')
128
+ return []
129
+ }
130
+ }
131
+
132
+ private findCommonTermPatterns(
133
+ byProject: Map<string, Array<{ id: string; content: string }>>,
134
+ minProjects: number
135
+ ): GeneralizedPattern[] {
136
+ // Extract key terms per project
137
+ const projectTerms = new Map<string, Map<string, Array<{ id: string; content: string }>>>()
138
+
139
+ for (const [project, decisions] of byProject) {
140
+ const terms = new Map<string, Array<{ id: string; content: string }>>()
141
+
142
+ for (const d of decisions) {
143
+ const words = this.extractKeyTerms(d.content)
144
+
145
+ for (const word of words) {
146
+ if (!terms.has(word)) terms.set(word, [])
147
+ terms.get(word)!.push(d)
148
+ }
149
+ }
150
+
151
+ projectTerms.set(project, terms)
152
+ }
153
+
154
+ // Find terms that appear across multiple projects
155
+ const allTerms = new Map<string, { projects: Set<string>; examples: Array<{ project: string; content: string; id: string }> }>()
156
+
157
+ for (const [project, terms] of projectTerms) {
158
+ for (const [term, decisions] of terms) {
159
+ if (!allTerms.has(term)) {
160
+ allTerms.set(term, { projects: new Set(), examples: [] })
161
+ }
162
+ const entry = allTerms.get(term)!
163
+ entry.projects.add(project)
164
+ for (const d of decisions.slice(0, 2)) {
165
+ entry.examples.push({ project, content: d.content.slice(0, 150), id: d.id })
166
+ }
167
+ }
168
+ }
169
+
170
+ // Filter to terms appearing in multiple projects
171
+ const patterns: GeneralizedPattern[] = []
172
+
173
+ for (const [term, data] of allTerms) {
174
+ if (data.projects.size >= minProjects) {
175
+ patterns.push({
176
+ description: `Common pattern around: "${term}"`,
177
+ projects: Array.from(data.projects),
178
+ occurrences: data.examples.length,
179
+ confidence: Math.min(data.projects.size / byProject.size, 1),
180
+ examples: data.examples.slice(0, 5)
181
+ })
182
+ }
183
+ }
184
+
185
+ return patterns
186
+ }
187
+
188
+ private async findSemanticClusters(
189
+ decisions: Array<{ id: string; content: string; project: string }>,
190
+ minProjects: number
191
+ ): Promise<GeneralizedPattern[]> {
192
+ if (!this.embeddings) return []
193
+
194
+ const patterns: GeneralizedPattern[] = []
195
+
196
+ try {
197
+ // Simple approach: for each decision, find similar ones across projects
198
+ const sample = decisions.slice(0, 20) // Limit to avoid too many embedding calls
199
+
200
+ for (const decision of sample) {
201
+ const collection = await this.collections.getDecisions()
202
+ const embedding = await this.embeddings.generate(decision.content)
203
+
204
+ const results = await collection.query({
205
+ queryEmbeddings: [embedding],
206
+ nResults: 10,
207
+ include: ['documents', 'metadatas', 'distances']
208
+ })
209
+
210
+ if (!results.ids || !results.ids[0]) continue
211
+
212
+ const crossProjectMatches: Array<{ project: string; content: string; id: string }> = []
213
+ const matchProjects = new Set<string>()
214
+
215
+ for (let i = 0; i < results.ids[0].length; i++) {
216
+ const similarity = 1 - (results.distances?.[0]?.[i] || 0)
217
+ if (similarity < 0.6) continue
218
+
219
+ const matchProject = (results.metadatas?.[0]?.[i] as any)?.project || ''
220
+ if (matchProject && matchProject !== decision.project) {
221
+ matchProjects.add(matchProject)
222
+ crossProjectMatches.push({
223
+ project: matchProject,
224
+ content: (results.documents?.[0]?.[i] || '').slice(0, 150),
225
+ id: results.ids[0][i]
226
+ })
227
+ }
228
+ }
229
+
230
+ matchProjects.add(decision.project)
231
+
232
+ if (matchProjects.size >= minProjects) {
233
+ patterns.push({
234
+ description: `Similar approaches: "${decision.content.slice(0, 100)}..."`,
235
+ projects: Array.from(matchProjects),
236
+ occurrences: crossProjectMatches.length + 1,
237
+ confidence: 0.7,
238
+ examples: [
239
+ { project: decision.project, content: decision.content.slice(0, 150), id: decision.id },
240
+ ...crossProjectMatches.slice(0, 4)
241
+ ]
242
+ })
243
+ }
244
+ }
245
+ } catch (error) {
246
+ this.logger.debug({ error }, 'Failed to find semantic clusters')
247
+ }
248
+
249
+ return patterns
250
+ }
251
+
252
+ private deduplicatePatterns(patterns: GeneralizedPattern[]): GeneralizedPattern[] {
253
+ const seen = new Set<string>()
254
+ const deduped: GeneralizedPattern[] = []
255
+
256
+ for (const pattern of patterns) {
257
+ const key = pattern.description.toLowerCase().slice(0, 50)
258
+ if (!seen.has(key)) {
259
+ seen.add(key)
260
+ deduped.push(pattern)
261
+ }
262
+ }
263
+
264
+ return deduped
265
+ }
266
+
267
+ private extractKeyTerms(text: string): string[] {
268
+ const stopWords = new Set([
269
+ 'the', 'and', 'for', 'are', 'but', 'not', 'all', 'can', 'was',
270
+ 'has', 'had', 'been', 'have', 'with', 'this', 'that', 'from',
271
+ 'they', 'will', 'would', 'there', 'their', 'what', 'about',
272
+ 'which', 'when', 'make', 'like', 'just', 'also', 'should',
273
+ 'use', 'using', 'used', 'because', 'instead', 'project',
274
+ 'decision', 'decided', 'recommend', 'context', 'reasoning'
275
+ ])
276
+
277
+ return text
278
+ .toLowerCase()
279
+ .replace(/[^a-z0-9\s-]/g, ' ')
280
+ .split(/\s+/)
281
+ .filter(w => w.length > 3 && !stopWords.has(w))
282
+ }
283
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Cross-Project Intelligence Module
3
+ * Phase 15.4 - Pattern generalization, project affinity, knowledge transfer
4
+ */
5
+
6
+ export { PatternGeneralizer } from './generalizer'
7
+ export type { GeneralizedPattern, GeneralizationResult } from './generalizer'
8
+
9
+ export { AffinityCalculator } from './affinity'
10
+ export type { ProjectAffinity } from './affinity'
11
+
12
+ export { KnowledgeTransfer } from './transfer'
13
+ export type { TransferableKnowledge, TransferItem } from './transfer'
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Knowledge Transfer
3
+ * Transfer knowledge from one project to another based on similarity
4
+ */
5
+
6
+ import type { Logger } from 'pino'
7
+ import type { CollectionManager } from '@/memory/chroma/collection-manager'
8
+ import type { EmbeddingProvider } from '@/memory/chroma/embeddings'
9
+
10
+ export interface TransferableKnowledge {
11
+ sourceProject: string
12
+ targetProject: string
13
+ items: TransferItem[]
14
+ affinityScore: number
15
+ }
16
+
17
+ export interface TransferItem {
18
+ type: 'decision' | 'pattern' | 'correction'
19
+ id: string
20
+ content: string
21
+ relevance: number
22
+ reasoning: string
23
+ }
24
+
25
+ export class KnowledgeTransfer {
26
+ private logger: Logger
27
+ private collections: CollectionManager
28
+ private embeddings?: EmbeddingProvider
29
+
30
+ constructor(logger: Logger, collections: CollectionManager, embeddings?: EmbeddingProvider) {
31
+ this.logger = logger.child({ component: 'knowledge-transfer' })
32
+ this.collections = collections
33
+ this.embeddings = embeddings
34
+ }
35
+
36
+ /**
37
+ * Find transferable knowledge from source project to apply to target project
38
+ */
39
+ async findTransferable(sourceProject: string, targetProject: string, options: {
40
+ limit?: number
41
+ minRelevance?: number
42
+ } = {}): Promise<TransferableKnowledge> {
43
+ const { limit = 10, minRelevance = 0.4 } = options
44
+
45
+ // Get target project's decisions to understand its context
46
+ const targetContext = await this.getProjectContext(targetProject)
47
+
48
+ if (!targetContext) {
49
+ return {
50
+ sourceProject,
51
+ targetProject,
52
+ items: [],
53
+ affinityScore: 0
54
+ }
55
+ }
56
+
57
+ // Search source project for relevant knowledge
58
+ const items: TransferItem[] = []
59
+
60
+ // Decisions
61
+ const decisions = await this.searchSourceProject(
62
+ sourceProject,
63
+ targetContext,
64
+ 'decisions',
65
+ limit,
66
+ minRelevance
67
+ )
68
+ items.push(...decisions)
69
+
70
+ // Patterns
71
+ const patterns = await this.searchSourceProject(
72
+ sourceProject,
73
+ targetContext,
74
+ 'patterns',
75
+ limit,
76
+ minRelevance
77
+ )
78
+ items.push(...patterns)
79
+
80
+ // Corrections
81
+ const corrections = await this.searchSourceProject(
82
+ sourceProject,
83
+ targetContext,
84
+ 'corrections',
85
+ limit,
86
+ minRelevance
87
+ )
88
+ items.push(...corrections)
89
+
90
+ // Sort by relevance
91
+ items.sort((a, b) => b.relevance - a.relevance)
92
+
93
+ // Calculate affinity
94
+ const affinityScore = items.length > 0
95
+ ? items.reduce((sum, i) => sum + i.relevance, 0) / items.length
96
+ : 0
97
+
98
+ return {
99
+ sourceProject,
100
+ targetProject,
101
+ items: items.slice(0, limit),
102
+ affinityScore
103
+ }
104
+ }
105
+
106
+ private async getProjectContext(project: string): Promise<string | null> {
107
+ try {
108
+ const collection = await this.collections.getDecisions()
109
+
110
+ const results = await collection.get({
111
+ where: { project: { $eq: project } },
112
+ include: ['documents'],
113
+ limit: 20
114
+ })
115
+
116
+ if (results.ids.length === 0) return null
117
+
118
+ // Combine recent decisions into a context string
119
+ return results.documents!
120
+ .slice(0, 10)
121
+ .map(d => d as string)
122
+ .join(' ')
123
+ } catch (error) {
124
+ this.logger.debug({ error, project }, 'Failed to get project context')
125
+ return null
126
+ }
127
+ }
128
+
129
+ private async searchSourceProject(
130
+ sourceProject: string,
131
+ targetContext: string,
132
+ collectionType: 'decisions' | 'patterns' | 'corrections',
133
+ limit: number,
134
+ minRelevance: number
135
+ ): Promise<TransferItem[]> {
136
+ try {
137
+ let collection
138
+ let type: 'decision' | 'pattern' | 'correction'
139
+
140
+ switch (collectionType) {
141
+ case 'decisions':
142
+ collection = await this.collections.getDecisions()
143
+ type = 'decision'
144
+ break
145
+ case 'patterns':
146
+ collection = await this.collections.getPatterns()
147
+ type = 'pattern'
148
+ break
149
+ case 'corrections':
150
+ collection = await this.collections.getCorrections()
151
+ type = 'correction'
152
+ break
153
+ }
154
+
155
+ const where: any = { project: { $eq: sourceProject } }
156
+
157
+ let results: any
158
+
159
+ if (this.embeddings) {
160
+ // Use truncated target context for embedding
161
+ const queryText = targetContext.slice(0, 500)
162
+ const embedding = await this.embeddings.generate(queryText)
163
+ results = await collection.query({
164
+ queryEmbeddings: [embedding],
165
+ nResults: limit,
166
+ where,
167
+ include: ['documents', 'metadatas', 'distances']
168
+ })
169
+ } else {
170
+ results = await collection.query({
171
+ queryTexts: [targetContext.slice(0, 500)],
172
+ nResults: limit,
173
+ where,
174
+ include: ['documents', 'metadatas', 'distances']
175
+ })
176
+ }
177
+
178
+ if (!results.ids || !results.ids[0]) return []
179
+
180
+ const items: TransferItem[] = []
181
+
182
+ for (let i = 0; i < results.ids[0].length; i++) {
183
+ const similarity = 1 - (results.distances?.[0]?.[i] || 0)
184
+ if (similarity < minRelevance) continue
185
+
186
+ items.push({
187
+ type,
188
+ id: results.ids[0][i],
189
+ content: results.documents?.[0]?.[i] || '',
190
+ relevance: similarity,
191
+ reasoning: `Relevant ${type} from ${sourceProject} (${Math.round(similarity * 100)}% match)`
192
+ })
193
+ }
194
+
195
+ return items
196
+ } catch (error) {
197
+ this.logger.debug({ error, sourceProject, collectionType }, 'Failed to search source project')
198
+ return []
199
+ }
200
+ }
201
+ }
@@ -0,0 +1,123 @@
1
+ import os from 'os'
2
+ import fs from 'fs/promises'
3
+ import { loadConfig } from '@/config'
4
+ import { resolveHomePath } from '@/config/home'
5
+ import { createLogger } from '@/utils/logger'
6
+
7
+ export class DiagnosticTool {
8
+ private logger: any
9
+
10
+ constructor(logger: any) {
11
+ this.logger = logger.child({ component: 'diagnostics' })
12
+ }
13
+
14
+ async run(): Promise<void> {
15
+ console.log('šŸ” Running diagnostics...\n')
16
+
17
+ await this.printSystemInfo()
18
+ await this.printConfiguration()
19
+ await this.printRecentLogs()
20
+ await this.printCommonIssues()
21
+ }
22
+
23
+ private async printSystemInfo(): Promise<void> {
24
+ console.log('šŸ“Š System Information')
25
+ console.log('─'.repeat(50))
26
+ console.log(`OS: ${os.platform()} ${os.release()}`)
27
+ console.log(`Architecture: ${os.arch()}`)
28
+ console.log(`Node/Bun: ${process.version}`)
29
+ console.log(`Bun: ${process.versions.bun || 'Not detected'}`)
30
+ console.log(`Memory: ${Math.round(os.totalmem() / 1024 / 1024 / 1024)}GB total`)
31
+ console.log(`Free Memory: ${Math.round(os.freemem() / 1024 / 1024 / 1024)}GB`)
32
+ console.log(`CPUs: ${os.cpus().length}`)
33
+ console.log(`CWD: ${process.cwd()}`)
34
+ console.log()
35
+ }
36
+
37
+ private async printConfiguration(): Promise<void> {
38
+ console.log('āš™ļø Configuration')
39
+ console.log('─'.repeat(50))
40
+
41
+ try {
42
+ const config = await loadConfig()
43
+ console.log(`Vault Path: ${config.vaultPath}`)
44
+ console.log(`Database Path: ${config.dbPath}`)
45
+ console.log(`Log Level: ${config.logLevel}`)
46
+ console.log(`File Watch: ${config.enableFileWatch}`)
47
+ console.log(`Server Name: ${config.serverName}`)
48
+ console.log(`Server Version: ${config.serverVersion}`)
49
+
50
+ } catch (error) {
51
+ console.log(`Error loading config: ${error}`)
52
+ }
53
+
54
+ console.log()
55
+ }
56
+
57
+ private async printRecentLogs(): Promise<void> {
58
+ console.log('šŸ“ Recent Logs (last 20 lines)')
59
+ console.log('─'.repeat(50))
60
+
61
+ try {
62
+ const logContent = await fs.readFile(resolveHomePath('./logs/claude-brain.log'), 'utf-8')
63
+ const lines = logContent.split('\n').filter(l => l.trim())
64
+ const recentLines = lines.slice(-20)
65
+
66
+ for (const line of recentLines) {
67
+ console.log(line)
68
+ }
69
+
70
+ } catch (error) {
71
+ console.log('No logs found or unable to read logs')
72
+ }
73
+
74
+ console.log()
75
+ }
76
+
77
+ private async printCommonIssues(): Promise<void> {
78
+ console.log('šŸ’” Common Issues & Solutions')
79
+ console.log('─'.repeat(50))
80
+
81
+ const issues = [
82
+ {
83
+ symptom: 'MCP server not found in Claude Code',
84
+ solution: 'Check Claude Code config file has correct absolute path'
85
+ },
86
+ {
87
+ symptom: 'Vault path not found',
88
+ solution: 'Verify VAULT_PATH in .env points to valid Obsidian vault'
89
+ },
90
+ {
91
+ symptom: 'Embeddings fail',
92
+ solution: 'Check internet connection (first-time model download)'
93
+ },
94
+ {
95
+ symptom: 'Tools return stubs',
96
+ solution: 'Rebuild with: bun run build'
97
+ },
98
+ {
99
+ symptom: 'No projects found',
100
+ solution: 'Create project directory: Projects/your-project/'
101
+ }
102
+ ]
103
+
104
+ for (const issue of issues) {
105
+ console.log(`\nā“ ${issue.symptom}`)
106
+ console.log(` šŸ’” ${issue.solution}`)
107
+ }
108
+
109
+ console.log('\n\nšŸ“š For more help, check the documentation or logs.\n')
110
+ }
111
+ }
112
+
113
+ export async function runDiagnostics() {
114
+ const logger = createLogger('info', resolveHomePath('./logs/diagnostics.log'))
115
+ const tool = new DiagnosticTool(logger)
116
+
117
+ try {
118
+ await tool.run()
119
+ } catch (error) {
120
+ console.error('āŒ Diagnostics failed:', error)
121
+ process.exit(1)
122
+ }
123
+ }