claude-brain 0.14.2 → 0.14.4
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.
- package/README.md +191 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +11 -11
- package/bunfig.toml +8 -8
- package/package.json +80 -80
- package/packs/backend/node.json +173 -173
- package/packs/core/javascript.json +176 -176
- package/packs/core/typescript.json +222 -222
- package/packs/frontend/react.json +254 -254
- package/packs/meta/testing.json +172 -172
- package/src/automation/auto-context.ts +240 -240
- package/src/automation/decision-detector.ts +452 -452
- package/src/automation/index.ts +11 -11
- package/src/automation/phase12-manager.ts +456 -456
- package/src/automation/proactive-recall.ts +373 -373
- package/src/automation/project-detector.ts +310 -310
- package/src/automation/repo-scanner.ts +205 -205
- package/src/cli/auto-setup.ts +82 -82
- package/src/cli/bin.ts +202 -202
- package/src/cli/commands/chroma.ts +573 -573
- package/src/cli/commands/git-hook.ts +189 -189
- package/src/cli/commands/hooks.ts +213 -213
- package/src/cli/commands/init.ts +122 -122
- package/src/cli/commands/install-mcp.ts +92 -92
- package/src/cli/commands/pack.ts +197 -197
- package/src/cli/commands/serve.ts +167 -167
- package/src/cli/commands/start.ts +42 -42
- package/src/cli/commands/uninstall-mcp.ts +41 -41
- package/src/cli/commands/update.ts +121 -121
- package/src/cli/diagnose.ts +4 -4
- package/src/cli/health-check.ts +4 -4
- package/src/cli/migrate-chroma.ts +106 -106
- package/src/cli/setup.ts +4 -4
- package/src/cli/ui/animations.ts +80 -80
- package/src/cli/ui/components.ts +82 -82
- package/src/cli/ui/index.ts +4 -4
- package/src/cli/ui/logo.ts +36 -36
- package/src/cli/ui/theme.ts +55 -55
- package/src/config/defaults.ts +50 -50
- package/src/config/home.ts +55 -55
- package/src/config/index.ts +7 -7
- package/src/config/loader.ts +166 -166
- package/src/config/migration.ts +76 -76
- package/src/config/schema.ts +360 -360
- package/src/config/validator.ts +184 -184
- package/src/config/watcher.ts +86 -86
- package/src/context/assembler.ts +398 -398
- package/src/context/cache-manager.ts +101 -101
- package/src/context/formatter.ts +84 -84
- package/src/context/hierarchy.ts +85 -85
- package/src/context/index.ts +83 -83
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/types.ts +252 -252
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +123 -123
- package/src/health/index.ts +229 -229
- package/src/hooks/brain-hook.ts +112 -112
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +207 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +191 -194
- package/src/hooks/passive-classifier.ts +366 -366
- package/src/hooks/queue.ts +129 -129
- package/src/hooks/session-tracker.ts +275 -275
- package/src/hooks/types.ts +47 -47
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/affinity.ts +162 -162
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +13 -13
- package/src/intelligence/cross-project/transfer.ts +201 -201
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +207 -207
- package/src/intelligence/prediction/context-anticipator.ts +198 -198
- package/src/intelligence/prediction/decision-predictor.ts +184 -184
- package/src/intelligence/prediction/index.ts +13 -13
- package/src/intelligence/prediction/recommender.ts +268 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +247 -247
- package/src/intelligence/reasoning/counterfactual.ts +248 -248
- package/src/intelligence/reasoning/index.ts +13 -13
- package/src/intelligence/reasoning/synthesizer.ts +169 -169
- package/src/intelligence/temporal/evolution.ts +197 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +259 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/knowledge/entity-extractor.ts +416 -416
- package/src/knowledge/graph/builder.ts +185 -185
- package/src/knowledge/graph/linker.ts +201 -201
- package/src/knowledge/graph/memory-graph.ts +359 -359
- package/src/knowledge/graph/schema.ts +99 -99
- package/src/knowledge/graph/search.ts +168 -168
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +174 -174
- package/src/memory/chroma/collection-manager.ts +94 -94
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +153 -153
- package/src/memory/chroma/index.ts +82 -82
- package/src/memory/chroma/migration.ts +270 -270
- package/src/memory/chroma/schemas.ts +69 -69
- package/src/memory/chroma/search.ts +315 -315
- package/src/memory/chroma/store.ts +741 -741
- package/src/memory/consolidation/archiver.ts +164 -164
- package/src/memory/consolidation/merger.ts +186 -186
- package/src/memory/consolidation/scorer.ts +138 -138
- package/src/memory/context-builder.ts +236 -236
- package/src/memory/database.ts +169 -169
- package/src/memory/embedding-utils.ts +156 -156
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +351 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/index.ts +582 -582
- package/src/memory/knowledge-extractor.ts +455 -455
- package/src/memory/learning.ts +378 -378
- package/src/memory/patterns.ts +396 -396
- package/src/memory/schema.ts +88 -88
- package/src/memory/search.ts +309 -309
- package/src/memory/store.ts +787 -787
- package/src/memory/types.ts +121 -121
- package/src/orchestrator/coordinator.ts +272 -272
- package/src/orchestrator/decision-logger.ts +228 -228
- package/src/orchestrator/event-emitter.ts +198 -198
- package/src/orchestrator/event-queue.ts +184 -184
- package/src/orchestrator/handlers/base-handler.ts +70 -70
- package/src/orchestrator/handlers/context-handler.ts +73 -73
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- package/src/orchestrator/handlers/index.ts +10 -10
- package/src/orchestrator/handlers/status-handler.ts +131 -131
- package/src/orchestrator/handlers/task-handler.ts +171 -171
- package/src/orchestrator/index.ts +275 -275
- package/src/orchestrator/task-parser.ts +284 -284
- package/src/orchestrator/types.ts +98 -98
- package/src/packs/index.ts +9 -9
- package/src/packs/loader.ts +134 -134
- package/src/packs/manager.ts +204 -204
- package/src/packs/ranker.ts +78 -78
- package/src/packs/types.ts +81 -81
- package/src/phase12/index.ts +5 -5
- package/src/retrieval/bm25/index.ts +300 -300
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +223 -223
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +223 -223
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +163 -163
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +198 -198
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +236 -236
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +188 -188
- package/src/retrieval/reranker/model.ts +95 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +428 -428
- package/src/routing/intent-classifier.ts +436 -436
- package/src/routing/response-filter.ts +258 -254
- package/src/routing/router.ts +1322 -1314
- package/src/routing/search-engine.ts +475 -475
- package/src/routing/types.ts +94 -84
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/handlers/call-tool.ts +156 -156
- package/src/server/handlers/index.ts +9 -9
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/analyze-decision-evolution.ts +151 -151
- package/src/server/handlers/tools/auto-remember.ts +200 -200
- package/src/server/handlers/tools/brain.ts +85 -85
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/detect-trends.ts +144 -144
- package/src/server/handlers/tools/find-cross-project-patterns.ts +168 -168
- package/src/server/handlers/tools/get-activity-log.ts +194 -194
- package/src/server/handlers/tools/get-code-standards.ts +124 -124
- package/src/server/handlers/tools/get-corrections.ts +154 -154
- package/src/server/handlers/tools/get-decision-timeline.ts +172 -172
- package/src/server/handlers/tools/get-episode.ts +103 -103
- package/src/server/handlers/tools/get-patterns.ts +158 -158
- package/src/server/handlers/tools/get-phase12-status.ts +63 -63
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/get-recommendations.ts +145 -145
- package/src/server/handlers/tools/index.ts +31 -31
- package/src/server/handlers/tools/init-project.ts +757 -757
- package/src/server/handlers/tools/list-episodes.ts +90 -90
- package/src/server/handlers/tools/list-projects.ts +125 -125
- package/src/server/handlers/tools/rate-memory.ts +101 -101
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +126 -126
- package/src/server/handlers/tools/record-correction.ts +125 -125
- package/src/server/handlers/tools/remember-decision.ts +153 -153
- package/src/server/handlers/tools/schemas.ts +253 -253
- package/src/server/handlers/tools/search-knowledge-graph.ts +102 -102
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/handlers/tools/what-if-analysis.ts +135 -135
- package/src/server/http-api.ts +693 -693
- package/src/server/index.ts +40 -40
- package/src/server/mcp-server.ts +283 -283
- package/src/server/providers/index.ts +7 -7
- package/src/server/providers/prompts.ts +327 -327
- package/src/server/providers/resources.ts +622 -622
- package/src/server/services.ts +468 -468
- package/src/server/types.ts +39 -39
- package/src/server/utils/error-handler.ts +155 -155
- package/src/server/utils/index.ts +13 -13
- package/src/server/utils/memory-indicator.ts +83 -83
- package/src/server/utils/request-context.ts +122 -122
- package/src/server/utils/response-formatter.ts +129 -124
- package/src/server/utils/validators.ts +210 -210
- package/src/setup/index.ts +48 -48
- package/src/setup/wizard.ts +461 -461
- package/src/tools/index.ts +24 -24
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.test.ts +30 -30
- package/src/tools/schemas.ts +617 -617
- package/src/tools/types.ts +412 -412
- package/src/utils/circuit-breaker.ts +130 -130
- package/src/utils/cleanup.ts +34 -34
- package/src/utils/error-handler.ts +132 -132
- package/src/utils/error-messages.ts +60 -60
- package/src/utils/fallback.ts +45 -45
- package/src/utils/index.ts +54 -54
- package/src/utils/logger-utils.ts +80 -80
- package/src/utils/logger.ts +88 -88
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/retry.ts +94 -94
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/frontmatter.ts +264 -264
- package/src/vault/index.ts +318 -318
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +422 -422
- package/src/vault/reader.ts +264 -264
- package/src/vault/templates.ts +186 -186
- package/src/vault/types.ts +73 -73
- package/src/vault/watcher.ts +277 -277
- package/src/vault/writer.ts +413 -413
- package/tsconfig.json +30 -30
|
@@ -1,162 +1,162 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project Affinity
|
|
3
|
-
* Computes similarity between projects based on decisions, patterns, and tech stack
|
|
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 ProjectAffinity {
|
|
11
|
-
projectA: string
|
|
12
|
-
projectB: string
|
|
13
|
-
similarity: number
|
|
14
|
-
sharedTopics: string[]
|
|
15
|
-
sharedTechnologies: string[]
|
|
16
|
-
decisionOverlap: number
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class AffinityCalculator {
|
|
20
|
-
private logger: Logger
|
|
21
|
-
private collections: CollectionManager
|
|
22
|
-
private embeddings?: EmbeddingProvider
|
|
23
|
-
|
|
24
|
-
constructor(logger: Logger, collections: CollectionManager, embeddings?: EmbeddingProvider) {
|
|
25
|
-
this.logger = logger.child({ component: 'affinity-calculator' })
|
|
26
|
-
this.collections = collections
|
|
27
|
-
this.embeddings = embeddings
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Calculate affinity between all pairs of projects
|
|
32
|
-
*/
|
|
33
|
-
async calculateAffinities(options: {
|
|
34
|
-
minDecisions?: number
|
|
35
|
-
} = {}): Promise<ProjectAffinity[]> {
|
|
36
|
-
const { minDecisions = 2 } = options
|
|
37
|
-
|
|
38
|
-
// Get all decisions grouped by project
|
|
39
|
-
const byProject = await this.getDecisionsByProject()
|
|
40
|
-
|
|
41
|
-
// Filter out projects with too few decisions
|
|
42
|
-
const projects = Array.from(byProject.entries())
|
|
43
|
-
.filter(([_, decisions]) => decisions.length >= minDecisions)
|
|
44
|
-
|
|
45
|
-
const affinities: ProjectAffinity[] = []
|
|
46
|
-
|
|
47
|
-
// Compare all pairs
|
|
48
|
-
for (let i = 0; i < projects.length; i++) {
|
|
49
|
-
for (let j = i + 1; j < projects.length; j++) {
|
|
50
|
-
const [projectA, decisionsA] = projects[i]
|
|
51
|
-
const [projectB, decisionsB] = projects[j]
|
|
52
|
-
|
|
53
|
-
const affinity = this.compareProjects(projectA, decisionsA, projectB, decisionsB)
|
|
54
|
-
if (affinity.similarity > 0.1) {
|
|
55
|
-
affinities.push(affinity)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return affinities.sort((a, b) => b.similarity - a.similarity)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Find most similar projects to a given project
|
|
65
|
-
*/
|
|
66
|
-
async findSimilarProjects(project: string, limit: number = 5): Promise<ProjectAffinity[]> {
|
|
67
|
-
const affinities = await this.calculateAffinities()
|
|
68
|
-
|
|
69
|
-
return affinities
|
|
70
|
-
.filter(a => a.projectA === project || a.projectB === project)
|
|
71
|
-
.sort((a, b) => b.similarity - a.similarity)
|
|
72
|
-
.slice(0, limit)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private async getDecisionsByProject(): Promise<Map<string, Array<{ id: string; content: string; tags: string[] }>>> {
|
|
76
|
-
try {
|
|
77
|
-
const collection = await this.collections.getDecisions()
|
|
78
|
-
|
|
79
|
-
const results = await collection.get({
|
|
80
|
-
include: ['documents', 'metadatas']
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
const byProject = new Map<string, Array<{ id: string; content: string; tags: string[] }>>()
|
|
84
|
-
|
|
85
|
-
for (let i = 0; i < results.ids.length; i++) {
|
|
86
|
-
const project = (results.metadatas![i] as any)?.project || ''
|
|
87
|
-
if (!project) continue
|
|
88
|
-
|
|
89
|
-
if (!byProject.has(project)) byProject.set(project, [])
|
|
90
|
-
|
|
91
|
-
byProject.get(project)!.push({
|
|
92
|
-
id: results.ids[i],
|
|
93
|
-
content: results.documents![i] as string || '',
|
|
94
|
-
tags: String((results.metadatas![i] as any)?.tags || '').split(',').filter(t => t.trim())
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return byProject
|
|
99
|
-
} catch (error) {
|
|
100
|
-
this.logger.warn({ error }, 'Failed to get decisions by project')
|
|
101
|
-
return new Map()
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
private compareProjects(
|
|
106
|
-
projectA: string,
|
|
107
|
-
decisionsA: Array<{ id: string; content: string; tags: string[] }>,
|
|
108
|
-
projectB: string,
|
|
109
|
-
decisionsB: Array<{ id: string; content: string; tags: string[] }>
|
|
110
|
-
): ProjectAffinity {
|
|
111
|
-
// Term-based similarity
|
|
112
|
-
const termsA = this.extractAllTerms(decisionsA)
|
|
113
|
-
const termsB = this.extractAllTerms(decisionsB)
|
|
114
|
-
|
|
115
|
-
const sharedTopics = Array.from(termsA).filter(t => termsB.has(t))
|
|
116
|
-
const allTopics = new Set([...termsA, ...termsB])
|
|
117
|
-
|
|
118
|
-
const topicSimilarity = allTopics.size > 0 ? sharedTopics.length / allTopics.size : 0
|
|
119
|
-
|
|
120
|
-
// Tag-based similarity
|
|
121
|
-
const tagsA = new Set(decisionsA.flatMap(d => d.tags))
|
|
122
|
-
const tagsB = new Set(decisionsB.flatMap(d => d.tags))
|
|
123
|
-
|
|
124
|
-
const sharedTags = Array.from(tagsA).filter(t => tagsB.has(t))
|
|
125
|
-
const allTags = new Set([...tagsA, ...tagsB])
|
|
126
|
-
const tagSimilarity = allTags.size > 0 ? sharedTags.length / allTags.size : 0
|
|
127
|
-
|
|
128
|
-
// Combined similarity
|
|
129
|
-
const similarity = topicSimilarity * 0.7 + tagSimilarity * 0.3
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
projectA,
|
|
133
|
-
projectB,
|
|
134
|
-
similarity,
|
|
135
|
-
sharedTopics: sharedTopics.slice(0, 20),
|
|
136
|
-
sharedTechnologies: sharedTags.slice(0, 10),
|
|
137
|
-
decisionOverlap: sharedTopics.length
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
private extractAllTerms(decisions: Array<{ content: string }>): Set<string> {
|
|
142
|
-
const terms = new Set<string>()
|
|
143
|
-
const stopWords = new Set([
|
|
144
|
-
'the', 'and', 'for', 'are', 'but', 'not', 'all', 'can', 'was',
|
|
145
|
-
'has', 'had', 'been', 'have', 'with', 'this', 'that', 'from',
|
|
146
|
-
'use', 'using', 'used', 'will', 'would', 'should', 'also',
|
|
147
|
-
'decision', 'decided', 'recommend', 'instead', 'because',
|
|
148
|
-
'project', 'context', 'reasoning', 'about', 'which', 'when'
|
|
149
|
-
])
|
|
150
|
-
|
|
151
|
-
for (const d of decisions) {
|
|
152
|
-
const words = d.content.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').split(/\s+/)
|
|
153
|
-
for (const w of words) {
|
|
154
|
-
if (w.length > 3 && !stopWords.has(w)) {
|
|
155
|
-
terms.add(w)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return terms
|
|
161
|
-
}
|
|
162
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Project Affinity
|
|
3
|
+
* Computes similarity between projects based on decisions, patterns, and tech stack
|
|
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 ProjectAffinity {
|
|
11
|
+
projectA: string
|
|
12
|
+
projectB: string
|
|
13
|
+
similarity: number
|
|
14
|
+
sharedTopics: string[]
|
|
15
|
+
sharedTechnologies: string[]
|
|
16
|
+
decisionOverlap: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class AffinityCalculator {
|
|
20
|
+
private logger: Logger
|
|
21
|
+
private collections: CollectionManager
|
|
22
|
+
private embeddings?: EmbeddingProvider
|
|
23
|
+
|
|
24
|
+
constructor(logger: Logger, collections: CollectionManager, embeddings?: EmbeddingProvider) {
|
|
25
|
+
this.logger = logger.child({ component: 'affinity-calculator' })
|
|
26
|
+
this.collections = collections
|
|
27
|
+
this.embeddings = embeddings
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Calculate affinity between all pairs of projects
|
|
32
|
+
*/
|
|
33
|
+
async calculateAffinities(options: {
|
|
34
|
+
minDecisions?: number
|
|
35
|
+
} = {}): Promise<ProjectAffinity[]> {
|
|
36
|
+
const { minDecisions = 2 } = options
|
|
37
|
+
|
|
38
|
+
// Get all decisions grouped by project
|
|
39
|
+
const byProject = await this.getDecisionsByProject()
|
|
40
|
+
|
|
41
|
+
// Filter out projects with too few decisions
|
|
42
|
+
const projects = Array.from(byProject.entries())
|
|
43
|
+
.filter(([_, decisions]) => decisions.length >= minDecisions)
|
|
44
|
+
|
|
45
|
+
const affinities: ProjectAffinity[] = []
|
|
46
|
+
|
|
47
|
+
// Compare all pairs
|
|
48
|
+
for (let i = 0; i < projects.length; i++) {
|
|
49
|
+
for (let j = i + 1; j < projects.length; j++) {
|
|
50
|
+
const [projectA, decisionsA] = projects[i]
|
|
51
|
+
const [projectB, decisionsB] = projects[j]
|
|
52
|
+
|
|
53
|
+
const affinity = this.compareProjects(projectA, decisionsA, projectB, decisionsB)
|
|
54
|
+
if (affinity.similarity > 0.1) {
|
|
55
|
+
affinities.push(affinity)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return affinities.sort((a, b) => b.similarity - a.similarity)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Find most similar projects to a given project
|
|
65
|
+
*/
|
|
66
|
+
async findSimilarProjects(project: string, limit: number = 5): Promise<ProjectAffinity[]> {
|
|
67
|
+
const affinities = await this.calculateAffinities()
|
|
68
|
+
|
|
69
|
+
return affinities
|
|
70
|
+
.filter(a => a.projectA === project || a.projectB === project)
|
|
71
|
+
.sort((a, b) => b.similarity - a.similarity)
|
|
72
|
+
.slice(0, limit)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async getDecisionsByProject(): Promise<Map<string, Array<{ id: string; content: string; tags: string[] }>>> {
|
|
76
|
+
try {
|
|
77
|
+
const collection = await this.collections.getDecisions()
|
|
78
|
+
|
|
79
|
+
const results = await collection.get({
|
|
80
|
+
include: ['documents', 'metadatas']
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const byProject = new Map<string, Array<{ id: string; content: string; tags: string[] }>>()
|
|
84
|
+
|
|
85
|
+
for (let i = 0; i < results.ids.length; i++) {
|
|
86
|
+
const project = (results.metadatas![i] as any)?.project || ''
|
|
87
|
+
if (!project) continue
|
|
88
|
+
|
|
89
|
+
if (!byProject.has(project)) byProject.set(project, [])
|
|
90
|
+
|
|
91
|
+
byProject.get(project)!.push({
|
|
92
|
+
id: results.ids[i],
|
|
93
|
+
content: results.documents![i] as string || '',
|
|
94
|
+
tags: String((results.metadatas![i] as any)?.tags || '').split(',').filter(t => t.trim())
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return byProject
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.logger.warn({ error }, 'Failed to get decisions by project')
|
|
101
|
+
return new Map()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private compareProjects(
|
|
106
|
+
projectA: string,
|
|
107
|
+
decisionsA: Array<{ id: string; content: string; tags: string[] }>,
|
|
108
|
+
projectB: string,
|
|
109
|
+
decisionsB: Array<{ id: string; content: string; tags: string[] }>
|
|
110
|
+
): ProjectAffinity {
|
|
111
|
+
// Term-based similarity
|
|
112
|
+
const termsA = this.extractAllTerms(decisionsA)
|
|
113
|
+
const termsB = this.extractAllTerms(decisionsB)
|
|
114
|
+
|
|
115
|
+
const sharedTopics = Array.from(termsA).filter(t => termsB.has(t))
|
|
116
|
+
const allTopics = new Set([...termsA, ...termsB])
|
|
117
|
+
|
|
118
|
+
const topicSimilarity = allTopics.size > 0 ? sharedTopics.length / allTopics.size : 0
|
|
119
|
+
|
|
120
|
+
// Tag-based similarity
|
|
121
|
+
const tagsA = new Set(decisionsA.flatMap(d => d.tags))
|
|
122
|
+
const tagsB = new Set(decisionsB.flatMap(d => d.tags))
|
|
123
|
+
|
|
124
|
+
const sharedTags = Array.from(tagsA).filter(t => tagsB.has(t))
|
|
125
|
+
const allTags = new Set([...tagsA, ...tagsB])
|
|
126
|
+
const tagSimilarity = allTags.size > 0 ? sharedTags.length / allTags.size : 0
|
|
127
|
+
|
|
128
|
+
// Combined similarity
|
|
129
|
+
const similarity = topicSimilarity * 0.7 + tagSimilarity * 0.3
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
projectA,
|
|
133
|
+
projectB,
|
|
134
|
+
similarity,
|
|
135
|
+
sharedTopics: sharedTopics.slice(0, 20),
|
|
136
|
+
sharedTechnologies: sharedTags.slice(0, 10),
|
|
137
|
+
decisionOverlap: sharedTopics.length
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private extractAllTerms(decisions: Array<{ content: string }>): Set<string> {
|
|
142
|
+
const terms = new Set<string>()
|
|
143
|
+
const stopWords = new Set([
|
|
144
|
+
'the', 'and', 'for', 'are', 'but', 'not', 'all', 'can', 'was',
|
|
145
|
+
'has', 'had', 'been', 'have', 'with', 'this', 'that', 'from',
|
|
146
|
+
'use', 'using', 'used', 'will', 'would', 'should', 'also',
|
|
147
|
+
'decision', 'decided', 'recommend', 'instead', 'because',
|
|
148
|
+
'project', 'context', 'reasoning', 'about', 'which', 'when'
|
|
149
|
+
])
|
|
150
|
+
|
|
151
|
+
for (const d of decisions) {
|
|
152
|
+
const words = d.content.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').split(/\s+/)
|
|
153
|
+
for (const w of words) {
|
|
154
|
+
if (w.length > 3 && !stopWords.has(w)) {
|
|
155
|
+
terms.add(w)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return terms
|
|
161
|
+
}
|
|
162
|
+
}
|