claude-brain 0.30.2 → 0.30.3
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 +241 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +29 -29
- package/package.json +7 -3
- 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/scripts/postinstall.mjs +531 -531
- package/src/automation/decision-detector.ts +452 -452
- 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 +210 -205
- package/src/cli/auto-setup.ts +75 -75
- package/src/cli/auto-start.ts +266 -266
- package/src/cli/bin.ts +264 -264
- package/src/cli/commands/autostart.ts +90 -90
- package/src/cli/commands/chroma.ts +578 -577
- package/src/cli/commands/export-training.ts +70 -70
- package/src/cli/commands/export.ts +130 -130
- package/src/cli/commands/git-hook.ts +183 -183
- package/src/cli/commands/hooks.ts +217 -217
- package/src/cli/commands/init.ts +123 -123
- package/src/cli/commands/install-mcp.ts +122 -111
- package/src/cli/commands/models.ts +979 -979
- package/src/cli/commands/pack.ts +200 -200
- package/src/cli/commands/refresh.ts +344 -339
- package/src/cli/commands/reindex.ts +120 -120
- package/src/cli/commands/serve.ts +466 -463
- package/src/cli/commands/start.ts +44 -44
- package/src/cli/commands/status.ts +220 -203
- package/src/cli/commands/uninstall-mcp.ts +45 -41
- package/src/cli/commands/update.ts +130 -124
- package/src/cli/migrate-chroma.ts +106 -106
- 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/code-intelligence/indexer.ts +352 -352
- package/src/code-intelligence/linker.ts +178 -178
- package/src/code-intelligence/parser.ts +484 -484
- package/src/code-intelligence/query.ts +291 -291
- package/src/code-intelligence/schema.ts +83 -83
- package/src/code-intelligence/types.ts +95 -95
- package/src/config/defaults.ts +52 -52
- package/src/config/home.ts +56 -56
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +192 -192
- package/src/config/schema.ts +446 -415
- package/src/config/validator.ts +182 -182
- package/src/context/assembler.ts +407 -400
- package/src/context/index.ts +79 -79
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +122 -121
- package/src/health/index.ts +233 -232
- package/src/hooks/brain-hook.ts +134 -131
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/claude-code-mastery.md +112 -112
- package/src/hooks/context-hook.ts +260 -245
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +211 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +306 -288
- package/src/hooks/interceptor-hook.ts +204 -201
- package/src/hooks/passive-classifier.ts +397 -397
- package/src/hooks/queue.ts +160 -129
- package/src/hooks/session-tracker.ts +312 -312
- package/src/hooks/types.ts +52 -52
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +7 -7
- package/src/intelligence/hf-downloader.ts +222 -222
- package/src/intelligence/hf-manifest.json +78 -78
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/inference-router.ts +762 -762
- package/src/intelligence/model-manager.ts +263 -245
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +213 -207
- package/src/intelligence/prediction/index.ts +7 -7
- package/src/intelligence/prediction/recommender.ts +276 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +243 -247
- package/src/intelligence/reasoning/index.ts +7 -7
- package/src/intelligence/temporal/evolution.ts +193 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +272 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/intelligence/tokenizer.ts +118 -118
- package/src/knowledge/entity-extractor.ts +447 -443
- 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 +166 -166
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +211 -192
- package/src/memory/chroma/collection-manager.ts +92 -92
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +177 -175
- 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 +319 -315
- package/src/memory/chroma/store.ts +755 -747
- package/src/memory/compression.ts +121 -121
- package/src/memory/consolidation/archiver.ts +162 -165
- package/src/memory/consolidation/merger.ts +182 -186
- package/src/memory/consolidation/scorer.ts +136 -136
- package/src/memory/database.ts +9 -0
- package/src/memory/dual-write.ts +145 -0
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +347 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/fts5-search.ts +692 -633
- package/src/memory/index.ts +943 -1060
- package/src/memory/migrations/add-fts5.ts +118 -108
- package/src/memory/patterns.ts +438 -438
- package/src/memory/pruning.ts +60 -60
- package/src/memory/schema.ts +88 -88
- package/src/memory/store.ts +911 -787
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- 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 -297
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +221 -221
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +221 -221
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +165 -165
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +203 -203
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +252 -252
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +189 -188
- package/src/retrieval/reranker/model.ts +99 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +454 -454
- package/src/routing/handlers/exploration-handler.ts +369 -0
- package/src/routing/handlers/index.ts +19 -0
- package/src/routing/handlers/memory-handler.ts +273 -0
- package/src/routing/handlers/mutation-handler.ts +241 -0
- package/src/routing/handlers/recall-handler.ts +642 -0
- package/src/routing/handlers/shared.ts +515 -0
- package/src/routing/handlers/types.ts +48 -0
- package/src/routing/intent-classifier.ts +552 -552
- package/src/routing/response-filter.ts +399 -391
- package/src/routing/router.ts +245 -2193
- package/src/routing/search-engine.ts +521 -514
- package/src/routing/types.ts +104 -94
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/auto-updater.ts +283 -276
- package/src/server/handlers/call-tool.ts +159 -159
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/auto-remember.ts +165 -165
- package/src/server/handlers/tools/brain.ts +86 -86
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/get-code-standards.ts +123 -123
- package/src/server/handlers/tools/get-corrections.ts +152 -152
- package/src/server/handlers/tools/get-patterns.ts +156 -156
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/index.ts +30 -30
- package/src/server/handlers/tools/init-project.ts +756 -756
- package/src/server/handlers/tools/list-projects.ts +126 -126
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +132 -132
- package/src/server/handlers/tools/record-correction.ts +131 -131
- package/src/server/handlers/tools/remember-decision.ts +168 -168
- package/src/server/handlers/tools/schemas.ts +179 -179
- package/src/server/handlers/tools/search-code.ts +122 -122
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/http-api.ts +215 -1229
- package/src/server/mcp-proxy.ts +85 -84
- package/src/server/mcp-server.ts +285 -284
- package/src/server/middleware/auth.ts +39 -0
- package/src/server/middleware/error-handler.ts +37 -0
- package/src/server/middleware/rate-limit.ts +53 -0
- package/src/server/middleware/validate.ts +42 -0
- package/src/server/pid-manager.ts +137 -136
- package/src/server/providers/resources.ts +581 -581
- package/src/server/routes/code.ts +228 -0
- package/src/server/routes/context.ts +26 -0
- package/src/server/routes/health.ts +19 -0
- package/src/server/routes/helpers.ts +100 -0
- package/src/server/routes/hooks.ts +197 -0
- package/src/server/routes/mcp.ts +47 -0
- package/src/server/routes/memory.ts +397 -0
- package/src/server/routes/models.ts +96 -0
- package/src/server/routes/projects.ts +89 -0
- package/src/server/routes/types.ts +21 -0
- package/src/server/schemas/api-schemas.ts +202 -0
- package/src/server/services.ts +720 -720
- package/src/server/utils/memory-indicator.ts +84 -84
- package/src/server/utils/response-formatter.ts +129 -129
- package/src/server/web-viewer.ts +1145 -1115
- package/src/setup/index.ts +38 -38
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.ts +666 -666
- package/src/tools/types.ts +412 -412
- package/src/training/data-store.ts +320 -298
- package/src/training/retrain-pipeline.ts +399 -394
- package/src/utils/error-handler.ts +136 -136
- package/src/utils/index.ts +58 -58
- package/src/utils/kill-port.ts +55 -53
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/safe-path.ts +43 -0
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/index.ts +4 -3
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +4 -1
- package/src/vault/reader.ts +44 -1
- package/src/vault/watcher.ts +24 -1
- package/src/vault/writer.ts +487 -413
- package/skills/persistent-memory/SKILL.md +0 -148
- package/skills/persistent-memory/references/tool-reference.md +0 -90
|
@@ -1,283 +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
|
-
}
|
|
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
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-Project Intelligence Module
|
|
3
|
-
* Phase 15.4 - Pattern generalization
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export { PatternGeneralizer } from './generalizer'
|
|
7
|
-
export type { GeneralizedPattern, GeneralizationResult } from './generalizer'
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Project Intelligence Module
|
|
3
|
+
* Phase 15.4 - Pattern generalization
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { PatternGeneralizer } from './generalizer'
|
|
7
|
+
export type { GeneralizedPattern, GeneralizationResult } from './generalizer'
|