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,168 +1,168 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Find Cross-Project Patterns Handler
|
|
3
|
-
* Finds patterns that appear across multiple projects
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Logger } from 'pino'
|
|
7
|
-
import type { ToolResponse } from '@/tools/types'
|
|
8
|
-
import { getMemoryService } from '@/server/services'
|
|
9
|
-
import { ToolValidator } from '@/server/utils/validators'
|
|
10
|
-
import { ResponseFormatter } from '@/server/utils/response-formatter'
|
|
11
|
-
import { withMemoryIndicator, formatMemoryStats } from '@/server/utils/memory-indicator'
|
|
12
|
-
import { ErrorHandler } from '@/server/utils/error-handler'
|
|
13
|
-
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
|
14
|
-
import { FindCrossProjectPatternsSchema } from './schemas'
|
|
15
|
-
import { PatternGeneralizer } from '@/intelligence/cross-project/generalizer'
|
|
16
|
-
|
|
17
|
-
export async function handleFindCrossProjectPatterns(
|
|
18
|
-
args: unknown,
|
|
19
|
-
logger: Logger
|
|
20
|
-
): Promise<ToolResponse> {
|
|
21
|
-
try {
|
|
22
|
-
const input = ToolValidator.validate(args, FindCrossProjectPatternsSchema)
|
|
23
|
-
const { min_projects, limit, query } = input
|
|
24
|
-
|
|
25
|
-
const memory = getMemoryService()
|
|
26
|
-
|
|
27
|
-
if (!memory.isChromaDBEnabled()) {
|
|
28
|
-
// SQLite fallback: fetch all decisions, group by project, find shared terms
|
|
29
|
-
const decisions = await memory.fetchAllDecisions()
|
|
30
|
-
|
|
31
|
-
if (decisions.length === 0) {
|
|
32
|
-
return ResponseFormatter.text('No cross-project patterns found (no decisions in database).')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Group by project
|
|
36
|
-
const byProject: Record<string, any[]> = {}
|
|
37
|
-
for (const d of decisions) {
|
|
38
|
-
const proj = d.project || 'unknown'
|
|
39
|
-
if (!byProject[proj]) byProject[proj] = []
|
|
40
|
-
byProject[proj].push(d)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const projectNames = Object.keys(byProject)
|
|
44
|
-
const minProj = min_projects || 2
|
|
45
|
-
|
|
46
|
-
if (projectNames.length < minProj) {
|
|
47
|
-
return ResponseFormatter.text(
|
|
48
|
-
`Only ${projectNames.length} project(s) found (${projectNames.join(', ')}). Need at least ${minProj} for cross-project analysis.`
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Extract significant terms per project
|
|
53
|
-
const stopWords = new Set(['the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'and', 'but', 'or', 'not', 'so', 'if', 'when', 'where', 'how', 'what', 'which', 'who', 'this', 'that', 'use', 'using', 'used', 'decision', 'context', 'reasoning'])
|
|
54
|
-
const termProjects: Record<string, { projects: Set<string>; examples: Array<{ project: string; content: string }> }> = {}
|
|
55
|
-
|
|
56
|
-
for (const [proj, decs] of Object.entries(byProject)) {
|
|
57
|
-
const projectTerms = new Set<string>()
|
|
58
|
-
for (const d of decs) {
|
|
59
|
-
const text = `${d.decision || ''} ${d.context || ''}`.toLowerCase()
|
|
60
|
-
const words = text.match(/\b[a-z][a-z-]{2,}\b/g) || []
|
|
61
|
-
for (const word of words) {
|
|
62
|
-
if (stopWords.has(word)) continue
|
|
63
|
-
if (!projectTerms.has(word)) {
|
|
64
|
-
projectTerms.add(word)
|
|
65
|
-
if (!termProjects[word]) termProjects[word] = { projects: new Set(), examples: [] }
|
|
66
|
-
termProjects[word].projects.add(proj)
|
|
67
|
-
if (termProjects[word].examples.length < 3) {
|
|
68
|
-
termProjects[word].examples.push({ project: proj, content: d.decision?.slice(0, 100) || d.content?.slice(0, 100) || '' })
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Filter terms that appear in >= minProj projects
|
|
76
|
-
const crossPatterns = Object.entries(termProjects)
|
|
77
|
-
.filter(([, v]) => v.projects.size >= minProj)
|
|
78
|
-
.map(([term, v]) => ({
|
|
79
|
-
description: term,
|
|
80
|
-
projects: Array.from(v.projects),
|
|
81
|
-
occurrences: v.projects.size,
|
|
82
|
-
confidence: v.projects.size / projectNames.length,
|
|
83
|
-
examples: v.examples
|
|
84
|
-
}))
|
|
85
|
-
.sort((a, b) => b.occurrences - a.occurrences)
|
|
86
|
-
.slice(0, limit || 20)
|
|
87
|
-
|
|
88
|
-
if (crossPatterns.length === 0) {
|
|
89
|
-
return ResponseFormatter.text(
|
|
90
|
-
`No cross-project patterns found (analyzed ${decisions.length} decisions across ${projectNames.length} projects).`
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const parts: string[] = []
|
|
95
|
-
parts.push(`## Cross-Project Patterns (SQLite)`)
|
|
96
|
-
parts.push(`**Projects analyzed:** ${projectNames.join(', ')}`)
|
|
97
|
-
parts.push(`**Decisions analyzed:** ${decisions.length}`)
|
|
98
|
-
parts.push(`**Shared terms found:** ${crossPatterns.length}`)
|
|
99
|
-
parts.push('')
|
|
100
|
-
|
|
101
|
-
for (const pattern of crossPatterns) {
|
|
102
|
-
const confidence = Math.round(pattern.confidence * 100)
|
|
103
|
-
parts.push(`### ${pattern.description} (${confidence}% confidence)`)
|
|
104
|
-
parts.push(`**Projects:** ${pattern.projects.join(', ')}`)
|
|
105
|
-
parts.push(`**Occurrences:** ${pattern.occurrences} projects`)
|
|
106
|
-
if (pattern.examples.length > 0) {
|
|
107
|
-
parts.push('**Examples:**')
|
|
108
|
-
for (const ex of pattern.examples.slice(0, 3)) {
|
|
109
|
-
parts.push(`- [${ex.project}] ${ex.content}`)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
parts.push('')
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
parts.push('*Note: SQLite mode uses term-frequency analysis. Enable ChromaDB for semantic pattern generalization.*')
|
|
116
|
-
|
|
117
|
-
const statsLine = formatMemoryStats({ patterns: crossPatterns.length })
|
|
118
|
-
return ResponseFormatter.text(withMemoryIndicator(parts.join('\n'), crossPatterns.length) + '\n\n' + statsLine)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const generalizer = new PatternGeneralizer(
|
|
122
|
-
logger,
|
|
123
|
-
memory.chroma.collections,
|
|
124
|
-
memory.chroma.embeddings
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
const result = await generalizer.findCrossProjectPatterns({
|
|
128
|
-
minProjects: min_projects,
|
|
129
|
-
limit,
|
|
130
|
-
query
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
if (result.patterns.length === 0) {
|
|
134
|
-
return ResponseFormatter.text(
|
|
135
|
-
`No cross-project patterns found (analyzed ${result.totalDecisionsAnalyzed} decisions across ${result.projectsAnalyzed.length} projects).`
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const parts: string[] = []
|
|
140
|
-
parts.push(`## Cross-Project Patterns`)
|
|
141
|
-
parts.push(`**Projects analyzed:** ${result.projectsAnalyzed.join(', ')}`)
|
|
142
|
-
parts.push(`**Decisions analyzed:** ${result.totalDecisionsAnalyzed}`)
|
|
143
|
-
parts.push(`**Patterns found:** ${result.patterns.length}`)
|
|
144
|
-
parts.push('')
|
|
145
|
-
|
|
146
|
-
for (const pattern of result.patterns) {
|
|
147
|
-
const confidence = Math.round(pattern.confidence * 100)
|
|
148
|
-
parts.push(`### ${pattern.description} (${confidence}% confidence)`)
|
|
149
|
-
parts.push(`**Projects:** ${pattern.projects.join(', ')}`)
|
|
150
|
-
parts.push(`**Occurrences:** ${pattern.occurrences}`)
|
|
151
|
-
if (pattern.examples.length > 0) {
|
|
152
|
-
parts.push('**Examples:**')
|
|
153
|
-
for (const ex of pattern.examples.slice(0, 3)) {
|
|
154
|
-
parts.push(`- [${ex.project}] ${ex.content}`)
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
parts.push('')
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const statsLine = formatMemoryStats({ patterns: result.patterns.length })
|
|
161
|
-
return ResponseFormatter.text(withMemoryIndicator(parts.join('\n'), result.patterns.length) + '\n\n' + statsLine)
|
|
162
|
-
|
|
163
|
-
} catch (error) {
|
|
164
|
-
ErrorHandler.logError(logger, error, { tool: 'find_cross_project_patterns' })
|
|
165
|
-
if (error instanceof McpError) throw error
|
|
166
|
-
throw new McpError(ErrorCode.InternalError, `Cross-project patterns failed: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
167
|
-
}
|
|
168
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Find Cross-Project Patterns Handler
|
|
3
|
+
* Finds patterns that appear across multiple projects
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
import type { ToolResponse } from '@/tools/types'
|
|
8
|
+
import { getMemoryService } from '@/server/services'
|
|
9
|
+
import { ToolValidator } from '@/server/utils/validators'
|
|
10
|
+
import { ResponseFormatter } from '@/server/utils/response-formatter'
|
|
11
|
+
import { withMemoryIndicator, formatMemoryStats } from '@/server/utils/memory-indicator'
|
|
12
|
+
import { ErrorHandler } from '@/server/utils/error-handler'
|
|
13
|
+
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
|
14
|
+
import { FindCrossProjectPatternsSchema } from './schemas'
|
|
15
|
+
import { PatternGeneralizer } from '@/intelligence/cross-project/generalizer'
|
|
16
|
+
|
|
17
|
+
export async function handleFindCrossProjectPatterns(
|
|
18
|
+
args: unknown,
|
|
19
|
+
logger: Logger
|
|
20
|
+
): Promise<ToolResponse> {
|
|
21
|
+
try {
|
|
22
|
+
const input = ToolValidator.validate(args, FindCrossProjectPatternsSchema)
|
|
23
|
+
const { min_projects, limit, query } = input
|
|
24
|
+
|
|
25
|
+
const memory = getMemoryService()
|
|
26
|
+
|
|
27
|
+
if (!memory.isChromaDBEnabled()) {
|
|
28
|
+
// SQLite fallback: fetch all decisions, group by project, find shared terms
|
|
29
|
+
const decisions = await memory.fetchAllDecisions()
|
|
30
|
+
|
|
31
|
+
if (decisions.length === 0) {
|
|
32
|
+
return ResponseFormatter.text('No cross-project patterns found (no decisions in database).')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Group by project
|
|
36
|
+
const byProject: Record<string, any[]> = {}
|
|
37
|
+
for (const d of decisions) {
|
|
38
|
+
const proj = d.project || 'unknown'
|
|
39
|
+
if (!byProject[proj]) byProject[proj] = []
|
|
40
|
+
byProject[proj].push(d)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const projectNames = Object.keys(byProject)
|
|
44
|
+
const minProj = min_projects || 2
|
|
45
|
+
|
|
46
|
+
if (projectNames.length < minProj) {
|
|
47
|
+
return ResponseFormatter.text(
|
|
48
|
+
`Only ${projectNames.length} project(s) found (${projectNames.join(', ')}). Need at least ${minProj} for cross-project analysis.`
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Extract significant terms per project
|
|
53
|
+
const stopWords = new Set(['the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'and', 'but', 'or', 'not', 'so', 'if', 'when', 'where', 'how', 'what', 'which', 'who', 'this', 'that', 'use', 'using', 'used', 'decision', 'context', 'reasoning'])
|
|
54
|
+
const termProjects: Record<string, { projects: Set<string>; examples: Array<{ project: string; content: string }> }> = {}
|
|
55
|
+
|
|
56
|
+
for (const [proj, decs] of Object.entries(byProject)) {
|
|
57
|
+
const projectTerms = new Set<string>()
|
|
58
|
+
for (const d of decs) {
|
|
59
|
+
const text = `${d.decision || ''} ${d.context || ''}`.toLowerCase()
|
|
60
|
+
const words = text.match(/\b[a-z][a-z-]{2,}\b/g) || []
|
|
61
|
+
for (const word of words) {
|
|
62
|
+
if (stopWords.has(word)) continue
|
|
63
|
+
if (!projectTerms.has(word)) {
|
|
64
|
+
projectTerms.add(word)
|
|
65
|
+
if (!termProjects[word]) termProjects[word] = { projects: new Set(), examples: [] }
|
|
66
|
+
termProjects[word].projects.add(proj)
|
|
67
|
+
if (termProjects[word].examples.length < 3) {
|
|
68
|
+
termProjects[word].examples.push({ project: proj, content: d.decision?.slice(0, 100) || d.content?.slice(0, 100) || '' })
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Filter terms that appear in >= minProj projects
|
|
76
|
+
const crossPatterns = Object.entries(termProjects)
|
|
77
|
+
.filter(([, v]) => v.projects.size >= minProj)
|
|
78
|
+
.map(([term, v]) => ({
|
|
79
|
+
description: term,
|
|
80
|
+
projects: Array.from(v.projects),
|
|
81
|
+
occurrences: v.projects.size,
|
|
82
|
+
confidence: v.projects.size / projectNames.length,
|
|
83
|
+
examples: v.examples
|
|
84
|
+
}))
|
|
85
|
+
.sort((a, b) => b.occurrences - a.occurrences)
|
|
86
|
+
.slice(0, limit || 20)
|
|
87
|
+
|
|
88
|
+
if (crossPatterns.length === 0) {
|
|
89
|
+
return ResponseFormatter.text(
|
|
90
|
+
`No cross-project patterns found (analyzed ${decisions.length} decisions across ${projectNames.length} projects).`
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const parts: string[] = []
|
|
95
|
+
parts.push(`## Cross-Project Patterns (SQLite)`)
|
|
96
|
+
parts.push(`**Projects analyzed:** ${projectNames.join(', ')}`)
|
|
97
|
+
parts.push(`**Decisions analyzed:** ${decisions.length}`)
|
|
98
|
+
parts.push(`**Shared terms found:** ${crossPatterns.length}`)
|
|
99
|
+
parts.push('')
|
|
100
|
+
|
|
101
|
+
for (const pattern of crossPatterns) {
|
|
102
|
+
const confidence = Math.round(pattern.confidence * 100)
|
|
103
|
+
parts.push(`### ${pattern.description} (${confidence}% confidence)`)
|
|
104
|
+
parts.push(`**Projects:** ${pattern.projects.join(', ')}`)
|
|
105
|
+
parts.push(`**Occurrences:** ${pattern.occurrences} projects`)
|
|
106
|
+
if (pattern.examples.length > 0) {
|
|
107
|
+
parts.push('**Examples:**')
|
|
108
|
+
for (const ex of pattern.examples.slice(0, 3)) {
|
|
109
|
+
parts.push(`- [${ex.project}] ${ex.content}`)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
parts.push('')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
parts.push('*Note: SQLite mode uses term-frequency analysis. Enable ChromaDB for semantic pattern generalization.*')
|
|
116
|
+
|
|
117
|
+
const statsLine = formatMemoryStats({ patterns: crossPatterns.length })
|
|
118
|
+
return ResponseFormatter.text(withMemoryIndicator(parts.join('\n'), crossPatterns.length) + '\n\n' + statsLine)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const generalizer = new PatternGeneralizer(
|
|
122
|
+
logger,
|
|
123
|
+
memory.chroma.collections,
|
|
124
|
+
memory.chroma.embeddings
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
const result = await generalizer.findCrossProjectPatterns({
|
|
128
|
+
minProjects: min_projects,
|
|
129
|
+
limit,
|
|
130
|
+
query
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
if (result.patterns.length === 0) {
|
|
134
|
+
return ResponseFormatter.text(
|
|
135
|
+
`No cross-project patterns found (analyzed ${result.totalDecisionsAnalyzed} decisions across ${result.projectsAnalyzed.length} projects).`
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const parts: string[] = []
|
|
140
|
+
parts.push(`## Cross-Project Patterns`)
|
|
141
|
+
parts.push(`**Projects analyzed:** ${result.projectsAnalyzed.join(', ')}`)
|
|
142
|
+
parts.push(`**Decisions analyzed:** ${result.totalDecisionsAnalyzed}`)
|
|
143
|
+
parts.push(`**Patterns found:** ${result.patterns.length}`)
|
|
144
|
+
parts.push('')
|
|
145
|
+
|
|
146
|
+
for (const pattern of result.patterns) {
|
|
147
|
+
const confidence = Math.round(pattern.confidence * 100)
|
|
148
|
+
parts.push(`### ${pattern.description} (${confidence}% confidence)`)
|
|
149
|
+
parts.push(`**Projects:** ${pattern.projects.join(', ')}`)
|
|
150
|
+
parts.push(`**Occurrences:** ${pattern.occurrences}`)
|
|
151
|
+
if (pattern.examples.length > 0) {
|
|
152
|
+
parts.push('**Examples:**')
|
|
153
|
+
for (const ex of pattern.examples.slice(0, 3)) {
|
|
154
|
+
parts.push(`- [${ex.project}] ${ex.content}`)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
parts.push('')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const statsLine = formatMemoryStats({ patterns: result.patterns.length })
|
|
161
|
+
return ResponseFormatter.text(withMemoryIndicator(parts.join('\n'), result.patterns.length) + '\n\n' + statsLine)
|
|
162
|
+
|
|
163
|
+
} catch (error) {
|
|
164
|
+
ErrorHandler.logError(logger, error, { tool: 'find_cross_project_patterns' })
|
|
165
|
+
if (error instanceof McpError) throw error
|
|
166
|
+
throw new McpError(ErrorCode.InternalError, `Cross-project patterns failed: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
167
|
+
}
|
|
168
|
+
}
|