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.
- package/README.md +157 -0
- package/VERSION +1 -0
- package/assets/CLAUDE.md +307 -0
- package/bunfig.toml +8 -0
- package/package.json +74 -0
- package/src/automation/auto-context.ts +240 -0
- package/src/automation/decision-detector.ts +452 -0
- package/src/automation/index.ts +11 -0
- package/src/automation/proactive-recall.ts +373 -0
- package/src/automation/project-detector.ts +297 -0
- package/src/cli/auto-setup.ts +74 -0
- package/src/cli/bin.ts +110 -0
- package/src/cli/commands/install-mcp.ts +50 -0
- package/src/cli/commands/serve.ts +129 -0
- package/src/cli/diagnose.ts +4 -0
- package/src/cli/health-check.ts +4 -0
- package/src/cli/migrate-chroma.ts +106 -0
- package/src/cli/setup.ts +4 -0
- package/src/config/defaults.ts +47 -0
- package/src/config/home.ts +55 -0
- package/src/config/index.ts +7 -0
- package/src/config/loader.ts +166 -0
- package/src/config/migration.ts +76 -0
- package/src/config/schema.ts +257 -0
- package/src/config/validator.ts +184 -0
- package/src/config/watcher.ts +86 -0
- package/src/context/assembler.ts +398 -0
- package/src/context/cache-manager.ts +101 -0
- package/src/context/formatter.ts +84 -0
- package/src/context/hierarchy.ts +85 -0
- package/src/context/index.ts +83 -0
- package/src/context/progress-tracker.ts +174 -0
- package/src/context/standards-manager.ts +267 -0
- package/src/context/types.ts +252 -0
- package/src/context/validator.ts +58 -0
- package/src/cross-project/affinity.ts +162 -0
- package/src/cross-project/generalizer.ts +283 -0
- package/src/cross-project/index.ts +13 -0
- package/src/cross-project/transfer.ts +201 -0
- package/src/diagnostics/index.ts +123 -0
- package/src/health/index.ts +229 -0
- package/src/index.ts +7 -0
- package/src/knowledge/entity-extractor.ts +416 -0
- package/src/knowledge/graph/builder.ts +159 -0
- package/src/knowledge/graph/linker.ts +201 -0
- package/src/knowledge/graph/memory-graph.ts +359 -0
- package/src/knowledge/graph/schema.ts +99 -0
- package/src/knowledge/graph/search.ts +168 -0
- package/src/knowledge/relationship-extractor.ts +108 -0
- package/src/memory/chroma/client.ts +169 -0
- package/src/memory/chroma/collection-manager.ts +94 -0
- package/src/memory/chroma/config.ts +46 -0
- package/src/memory/chroma/embeddings.ts +153 -0
- package/src/memory/chroma/index.ts +82 -0
- package/src/memory/chroma/migration.ts +270 -0
- package/src/memory/chroma/schemas.ts +69 -0
- package/src/memory/chroma/search.ts +315 -0
- package/src/memory/chroma/store.ts +694 -0
- package/src/memory/consolidation/archiver.ts +164 -0
- package/src/memory/consolidation/merger.ts +186 -0
- package/src/memory/consolidation/scorer.ts +138 -0
- package/src/memory/context-builder.ts +236 -0
- package/src/memory/database.ts +169 -0
- package/src/memory/embedding-utils.ts +156 -0
- package/src/memory/embeddings.ts +226 -0
- package/src/memory/episodic/detector.ts +108 -0
- package/src/memory/episodic/manager.ts +334 -0
- package/src/memory/episodic/summarizer.ts +179 -0
- package/src/memory/episodic/types.ts +52 -0
- package/src/memory/index.ts +395 -0
- package/src/memory/knowledge-extractor.ts +455 -0
- package/src/memory/learning.ts +378 -0
- package/src/memory/patterns.ts +396 -0
- package/src/memory/schema.ts +56 -0
- package/src/memory/search.ts +309 -0
- package/src/memory/store.ts +344 -0
- package/src/memory/types.ts +121 -0
- package/src/optimization/index.ts +10 -0
- package/src/optimization/precompute.ts +202 -0
- package/src/optimization/semantic-cache.ts +207 -0
- package/src/orchestrator/coordinator.ts +272 -0
- package/src/orchestrator/decision-logger.ts +228 -0
- package/src/orchestrator/event-emitter.ts +198 -0
- package/src/orchestrator/event-queue.ts +184 -0
- package/src/orchestrator/handlers/base-handler.ts +70 -0
- package/src/orchestrator/handlers/context-handler.ts +73 -0
- package/src/orchestrator/handlers/decision-handler.ts +204 -0
- package/src/orchestrator/handlers/index.ts +10 -0
- package/src/orchestrator/handlers/status-handler.ts +131 -0
- package/src/orchestrator/handlers/task-handler.ts +171 -0
- package/src/orchestrator/index.ts +275 -0
- package/src/orchestrator/task-parser.ts +284 -0
- package/src/orchestrator/types.ts +98 -0
- package/src/phase12/index.ts +456 -0
- package/src/prediction/context-anticipator.ts +198 -0
- package/src/prediction/decision-predictor.ts +184 -0
- package/src/prediction/index.ts +13 -0
- package/src/prediction/recommender.ts +268 -0
- package/src/reasoning/chain-retrieval.ts +247 -0
- package/src/reasoning/counterfactual.ts +248 -0
- package/src/reasoning/index.ts +13 -0
- package/src/reasoning/synthesizer.ts +169 -0
- package/src/retrieval/bm25/index.ts +300 -0
- package/src/retrieval/bm25/tokenizer.ts +184 -0
- package/src/retrieval/feedback/adaptive.ts +223 -0
- package/src/retrieval/feedback/index.ts +16 -0
- package/src/retrieval/feedback/metrics.ts +223 -0
- package/src/retrieval/feedback/store.ts +283 -0
- package/src/retrieval/fusion/index.ts +194 -0
- package/src/retrieval/fusion/rrf.ts +163 -0
- package/src/retrieval/index.ts +12 -0
- package/src/retrieval/pipeline.ts +375 -0
- package/src/retrieval/query/expander.ts +198 -0
- package/src/retrieval/query/index.ts +27 -0
- package/src/retrieval/query/intent-classifier.ts +236 -0
- package/src/retrieval/query/temporal-parser.ts +295 -0
- package/src/retrieval/reranker/index.ts +188 -0
- package/src/retrieval/reranker/model.ts +95 -0
- package/src/retrieval/service.ts +125 -0
- package/src/retrieval/types.ts +162 -0
- package/src/scripts/health-check.ts +118 -0
- package/src/scripts/setup.ts +122 -0
- package/src/server/handlers/call-tool.ts +194 -0
- package/src/server/handlers/index.ts +9 -0
- package/src/server/handlers/list-tools.ts +18 -0
- package/src/server/handlers/tools/analyze-decision-evolution.ts +71 -0
- package/src/server/handlers/tools/auto-remember.ts +200 -0
- package/src/server/handlers/tools/create-project.ts +135 -0
- package/src/server/handlers/tools/detect-trends.ts +80 -0
- package/src/server/handlers/tools/find-cross-project-patterns.ts +73 -0
- package/src/server/handlers/tools/get-activity-log.ts +194 -0
- package/src/server/handlers/tools/get-code-standards.ts +124 -0
- package/src/server/handlers/tools/get-corrections.ts +154 -0
- package/src/server/handlers/tools/get-decision-timeline.ts +86 -0
- package/src/server/handlers/tools/get-episode.ts +93 -0
- package/src/server/handlers/tools/get-patterns.ts +158 -0
- package/src/server/handlers/tools/get-phase12-status.ts +63 -0
- package/src/server/handlers/tools/get-project-context.ts +75 -0
- package/src/server/handlers/tools/get-recommendations.ts +65 -0
- package/src/server/handlers/tools/index.ts +33 -0
- package/src/server/handlers/tools/init-project.ts +710 -0
- package/src/server/handlers/tools/list-episodes.ts +80 -0
- package/src/server/handlers/tools/list-projects.ts +125 -0
- package/src/server/handlers/tools/rate-memory.ts +95 -0
- package/src/server/handlers/tools/recall-similar.ts +87 -0
- package/src/server/handlers/tools/recognize-pattern.ts +126 -0
- package/src/server/handlers/tools/record-correction.ts +125 -0
- package/src/server/handlers/tools/remember-decision.ts +153 -0
- package/src/server/handlers/tools/schemas.ts +241 -0
- package/src/server/handlers/tools/search-knowledge-graph.ts +89 -0
- package/src/server/handlers/tools/smart-context.ts +124 -0
- package/src/server/handlers/tools/update-progress.ts +114 -0
- package/src/server/handlers/tools/what-if-analysis.ts +73 -0
- package/src/server/http-api.ts +474 -0
- package/src/server/index.ts +40 -0
- package/src/server/mcp-server.ts +283 -0
- package/src/server/providers/index.ts +7 -0
- package/src/server/providers/prompts.ts +327 -0
- package/src/server/providers/resources.ts +427 -0
- package/src/server/services.ts +388 -0
- package/src/server/types.ts +39 -0
- package/src/server/utils/error-handler.ts +155 -0
- package/src/server/utils/index.ts +13 -0
- package/src/server/utils/memory-indicator.ts +83 -0
- package/src/server/utils/request-context.ts +122 -0
- package/src/server/utils/response-formatter.ts +124 -0
- package/src/server/utils/validators.ts +210 -0
- package/src/setup/index.ts +22 -0
- package/src/setup/wizard.ts +321 -0
- package/src/temporal/evolution.ts +197 -0
- package/src/temporal/index.ts +16 -0
- package/src/temporal/query-processor.ts +190 -0
- package/src/temporal/timeline.ts +259 -0
- package/src/temporal/trends.ts +263 -0
- package/src/tools/index.ts +24 -0
- package/src/tools/registry.ts +106 -0
- package/src/tools/schemas.test.ts +30 -0
- package/src/tools/schemas.ts +907 -0
- package/src/tools/types.ts +412 -0
- package/src/utils/circuit-breaker.ts +130 -0
- package/src/utils/cleanup.ts +34 -0
- package/src/utils/error-handler.ts +132 -0
- package/src/utils/error-messages.ts +60 -0
- package/src/utils/fallback.ts +45 -0
- package/src/utils/index.ts +54 -0
- package/src/utils/logger-utils.ts +80 -0
- package/src/utils/logger.ts +88 -0
- package/src/utils/phase12-helper.ts +56 -0
- package/src/utils/retry.ts +94 -0
- package/src/utils/transaction.ts +63 -0
- package/src/vault/frontmatter.ts +264 -0
- package/src/vault/index.ts +318 -0
- package/src/vault/paths.ts +106 -0
- package/src/vault/query.ts +422 -0
- package/src/vault/reader.ts +264 -0
- package/src/vault/templates.ts +186 -0
- package/src/vault/types.ts +73 -0
- package/src/vault/watcher.ts +277 -0
- package/src/vault/writer.ts +393 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Management System Types
|
|
3
|
+
* Phase 5: Intelligent context assembly for Claude Brain
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A single context layer with priority
|
|
8
|
+
*/
|
|
9
|
+
export interface ContextLayer {
|
|
10
|
+
level: 'global' | 'project' | 'file' | 'function'
|
|
11
|
+
priority: number
|
|
12
|
+
content: Record<string, unknown>
|
|
13
|
+
metadata: ContextMetadata
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Metadata for context entries
|
|
18
|
+
*/
|
|
19
|
+
export interface ContextMetadata {
|
|
20
|
+
source: string
|
|
21
|
+
timestamp: Date
|
|
22
|
+
version: number
|
|
23
|
+
tags: string[]
|
|
24
|
+
relevance?: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Global context shared across all projects
|
|
29
|
+
*/
|
|
30
|
+
export interface GlobalContext {
|
|
31
|
+
standards: CodeStandards
|
|
32
|
+
patterns: DesignPatterns
|
|
33
|
+
bestPractices: BestPractices
|
|
34
|
+
commonPitfalls: string[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Project-specific context
|
|
39
|
+
*/
|
|
40
|
+
export interface ProjectContext {
|
|
41
|
+
name: string
|
|
42
|
+
description: string
|
|
43
|
+
techStack: string[]
|
|
44
|
+
architecture: string
|
|
45
|
+
goals: string[]
|
|
46
|
+
constraints: string[]
|
|
47
|
+
currentPhase: string
|
|
48
|
+
progress: ProgressState
|
|
49
|
+
decisions: DecisionSummary[]
|
|
50
|
+
standards: CodeStandards
|
|
51
|
+
teamPreferences: Record<string, unknown>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* File-level context
|
|
56
|
+
*/
|
|
57
|
+
export interface FileContext {
|
|
58
|
+
path: string
|
|
59
|
+
purpose: string
|
|
60
|
+
dependencies: string[]
|
|
61
|
+
relatedFiles: string[]
|
|
62
|
+
lastModified: Date
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Complete code standards definition
|
|
67
|
+
*/
|
|
68
|
+
export interface CodeStandards {
|
|
69
|
+
languages: LanguageStandards[]
|
|
70
|
+
frameworks: FrameworkStandards[]
|
|
71
|
+
naming: NamingConventions
|
|
72
|
+
formatting: FormattingRules
|
|
73
|
+
testing: TestingStandards
|
|
74
|
+
documentation: DocumentationStandards
|
|
75
|
+
security: SecurityStandards
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Language-specific coding standards
|
|
80
|
+
*/
|
|
81
|
+
export interface LanguageStandards {
|
|
82
|
+
language: string
|
|
83
|
+
version: string
|
|
84
|
+
style: string
|
|
85
|
+
linting: Record<string, unknown>
|
|
86
|
+
conventions: string[]
|
|
87
|
+
antiPatterns: string[]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Framework-specific standards
|
|
92
|
+
*/
|
|
93
|
+
export interface FrameworkStandards {
|
|
94
|
+
framework: string
|
|
95
|
+
version: string
|
|
96
|
+
patterns: string[]
|
|
97
|
+
bestPractices: string[]
|
|
98
|
+
conventions: string[]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Project progress tracking
|
|
103
|
+
*/
|
|
104
|
+
export interface ProgressState {
|
|
105
|
+
currentPhase: string
|
|
106
|
+
completionPercentage: number
|
|
107
|
+
completedTasks: TaskSummary[]
|
|
108
|
+
currentTasks: TaskSummary[]
|
|
109
|
+
nextSteps: string[]
|
|
110
|
+
blockers: Blocker[]
|
|
111
|
+
velocity: number
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Task summary for progress tracking
|
|
116
|
+
*/
|
|
117
|
+
export interface TaskSummary {
|
|
118
|
+
id: string
|
|
119
|
+
title: string
|
|
120
|
+
status: string
|
|
121
|
+
completedAt?: Date
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Project blocker
|
|
126
|
+
*/
|
|
127
|
+
export interface Blocker {
|
|
128
|
+
description: string
|
|
129
|
+
severity: 'low' | 'medium' | 'high' | 'critical'
|
|
130
|
+
since: Date
|
|
131
|
+
owner?: string
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Decision summary for context
|
|
136
|
+
*/
|
|
137
|
+
export interface DecisionSummary {
|
|
138
|
+
id: string
|
|
139
|
+
title: string
|
|
140
|
+
decision: string
|
|
141
|
+
date: Date
|
|
142
|
+
impact: string
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Completely assembled context for a project
|
|
147
|
+
*/
|
|
148
|
+
export interface AssembledContext {
|
|
149
|
+
project: ProjectContext
|
|
150
|
+
global: GlobalContext
|
|
151
|
+
memories: MemoryContext[]
|
|
152
|
+
progress: ProgressState
|
|
153
|
+
standards: CodeStandards
|
|
154
|
+
metadata: {
|
|
155
|
+
assembledAt: Date
|
|
156
|
+
tokenCount: number
|
|
157
|
+
sources: string[]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Memory context with relevance score
|
|
163
|
+
*/
|
|
164
|
+
export interface MemoryContext {
|
|
165
|
+
content: string
|
|
166
|
+
similarity: number
|
|
167
|
+
date: Date
|
|
168
|
+
tags: string[]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Options for context assembly
|
|
173
|
+
*/
|
|
174
|
+
export interface ContextOptions {
|
|
175
|
+
includeMemories: boolean
|
|
176
|
+
includeProgress: boolean
|
|
177
|
+
includeStandards: boolean
|
|
178
|
+
maxTokens?: number
|
|
179
|
+
relevanceThreshold?: number
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Design patterns and their usage
|
|
184
|
+
*/
|
|
185
|
+
export interface DesignPatterns {
|
|
186
|
+
[patternName: string]: {
|
|
187
|
+
description: string
|
|
188
|
+
whenToUse: string[]
|
|
189
|
+
examples: string[]
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Best practices organized by category
|
|
195
|
+
*/
|
|
196
|
+
export interface BestPractices {
|
|
197
|
+
[category: string]: string[]
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Naming conventions
|
|
202
|
+
*/
|
|
203
|
+
export interface NamingConventions {
|
|
204
|
+
variables: string
|
|
205
|
+
functions: string
|
|
206
|
+
classes: string
|
|
207
|
+
constants: string
|
|
208
|
+
interfaces: string
|
|
209
|
+
types: string
|
|
210
|
+
files: string
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Formatting rules
|
|
215
|
+
*/
|
|
216
|
+
export interface FormattingRules {
|
|
217
|
+
indentSize: number
|
|
218
|
+
indentStyle: 'spaces' | 'tabs'
|
|
219
|
+
maxLineLength: number
|
|
220
|
+
trailingCommas: boolean
|
|
221
|
+
semicolons: boolean
|
|
222
|
+
quotes: 'single' | 'double' | 'backtick'
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Testing standards
|
|
227
|
+
*/
|
|
228
|
+
export interface TestingStandards {
|
|
229
|
+
framework: string
|
|
230
|
+
coverageTarget: number
|
|
231
|
+
testFilePattern: string
|
|
232
|
+
namingConvention: string
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Documentation standards
|
|
237
|
+
*/
|
|
238
|
+
export interface DocumentationStandards {
|
|
239
|
+
jsdoc: boolean
|
|
240
|
+
readmeSections: string[]
|
|
241
|
+
apiDocs: boolean
|
|
242
|
+
inlineComments: boolean
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Security standards
|
|
247
|
+
*/
|
|
248
|
+
export interface SecurityStandards {
|
|
249
|
+
auditCommands: string[]
|
|
250
|
+
sensitivePatterns: string[]
|
|
251
|
+
dependencyChecks: boolean
|
|
252
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Schema Validator
|
|
3
|
+
* Validates context structures using Zod schemas
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
|
|
8
|
+
export const ProjectContextSchema = z.object({
|
|
9
|
+
name: z.string().min(1),
|
|
10
|
+
description: z.string(),
|
|
11
|
+
techStack: z.array(z.string()),
|
|
12
|
+
architecture: z.string(),
|
|
13
|
+
goals: z.array(z.string()),
|
|
14
|
+
constraints: z.array(z.string()),
|
|
15
|
+
currentPhase: z.string(),
|
|
16
|
+
progress: z.object({
|
|
17
|
+
currentPhase: z.string(),
|
|
18
|
+
completionPercentage: z.number().min(0).max(100),
|
|
19
|
+
completedTasks: z.array(z.any()),
|
|
20
|
+
currentTasks: z.array(z.any()),
|
|
21
|
+
nextSteps: z.array(z.string()),
|
|
22
|
+
blockers: z.array(z.any()),
|
|
23
|
+
velocity: z.number()
|
|
24
|
+
}),
|
|
25
|
+
decisions: z.array(z.any()),
|
|
26
|
+
standards: z.any(),
|
|
27
|
+
teamPreferences: z.record(z.any())
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export const CodeStandardsSchema = z.object({
|
|
31
|
+
languages: z.array(z.any()),
|
|
32
|
+
frameworks: z.array(z.any()),
|
|
33
|
+
naming: z.any(),
|
|
34
|
+
formatting: z.any(),
|
|
35
|
+
testing: z.any(),
|
|
36
|
+
documentation: z.any(),
|
|
37
|
+
security: z.any()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export class ContextValidator {
|
|
41
|
+
static validateProjectContext(data: unknown): boolean {
|
|
42
|
+
try {
|
|
43
|
+
ProjectContextSchema.parse(data)
|
|
44
|
+
return true
|
|
45
|
+
} catch {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static validateCodeStandards(data: unknown): boolean {
|
|
51
|
+
try {
|
|
52
|
+
CodeStandardsSchema.parse(data)
|
|
53
|
+
return true
|
|
54
|
+
} catch {
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +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
|
+
}
|