claude-brain 0.15.2 → 0.16.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 +191 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +29 -11
- package/bunfig.toml +8 -8
- package/package.json +82 -82
- 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 +341 -341
- 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 +209 -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/refresh.ts +323 -0
- package/src/cli/commands/serve.ts +167 -173
- package/src/cli/commands/start.ts +42 -42
- package/src/cli/commands/uninstall-mcp.ts +41 -41
- package/src/cli/commands/update.ts +124 -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 +128 -112
- package/src/hooks/capture.ts +168 -205
- 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 +194 -194
- package/src/hooks/passive-classifier.ts +404 -723
- package/src/hooks/queue.ts +129 -129
- package/src/hooks/session-tracker.ts +312 -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 +155 -155
- 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 +450 -436
- package/src/routing/response-filter.ts +261 -258
- package/src/routing/router.ts +1441 -1322
- package/src/routing/search-engine.ts +515 -475
- package/src/routing/types.ts +94 -94
- 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 -129
- 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
- package/src/cli/auto-update.ts +0 -157
|
@@ -1,236 +1,236 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memory Context Builder
|
|
3
|
-
* Phase 3: Memory and Embedding System
|
|
4
|
-
*
|
|
5
|
-
* Formats retrieved memories for Claude Code consumption
|
|
6
|
-
* with token budget management
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { Logger } from 'pino'
|
|
10
|
-
import type { MemorySearchResult } from './types'
|
|
11
|
-
|
|
12
|
-
export interface ContextOptions {
|
|
13
|
-
maxTokens?: number
|
|
14
|
-
includeMetadata?: boolean
|
|
15
|
-
format?: 'detailed' | 'concise' | 'summary'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class MemoryContextBuilder {
|
|
19
|
-
private logger: Logger
|
|
20
|
-
|
|
21
|
-
constructor(logger: Logger) {
|
|
22
|
-
this.logger = logger.child({ component: 'memory-context-builder' })
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Build formatted context from search results
|
|
27
|
-
*/
|
|
28
|
-
buildContext(results: MemorySearchResult[], options: ContextOptions = {}): string {
|
|
29
|
-
if (results.length === 0) {
|
|
30
|
-
return 'No relevant memories found.'
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const format = options.format || 'detailed'
|
|
34
|
-
const includeMetadata = options.includeMetadata ?? true
|
|
35
|
-
|
|
36
|
-
let context = `## Relevant Past Context (${results.length} items)\n\n`
|
|
37
|
-
|
|
38
|
-
for (let i = 0; i < results.length; i++) {
|
|
39
|
-
const result = results[i]!
|
|
40
|
-
const index = i + 1
|
|
41
|
-
|
|
42
|
-
if (format === 'detailed') {
|
|
43
|
-
context += this.formatDetailed(result, index, includeMetadata)
|
|
44
|
-
} else if (format === 'concise') {
|
|
45
|
-
context += this.formatConcise(result, index)
|
|
46
|
-
} else {
|
|
47
|
-
context += this.formatSummary(result, index)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
context += '\n'
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Estimate token count (rough: 1 token ~ 4 characters)
|
|
54
|
-
const estimatedTokens = Math.ceil(context.length / 4)
|
|
55
|
-
|
|
56
|
-
if (options.maxTokens && estimatedTokens > options.maxTokens) {
|
|
57
|
-
this.logger.warn(
|
|
58
|
-
{ estimatedTokens, maxTokens: options.maxTokens },
|
|
59
|
-
'Context exceeds token budget, truncating'
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
// Truncate context
|
|
63
|
-
const maxChars = options.maxTokens * 4
|
|
64
|
-
context = context.slice(0, maxChars) + '\n\n[Context truncated due to length]'
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return context
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Format detailed context entry
|
|
72
|
-
*/
|
|
73
|
-
private formatDetailed(
|
|
74
|
-
result: MemorySearchResult,
|
|
75
|
-
index: number,
|
|
76
|
-
includeMetadata: boolean
|
|
77
|
-
): string {
|
|
78
|
-
let text = `### ${index}. `
|
|
79
|
-
|
|
80
|
-
if (result.decision) {
|
|
81
|
-
text += `Decision (Similarity: ${(result.similarity * 100).toFixed(1)}%)\n\n`
|
|
82
|
-
text += `**Context:** ${result.decision.context}\n\n`
|
|
83
|
-
text += `**Decision:** ${result.decision.decision}\n\n`
|
|
84
|
-
text += `**Reasoning:** ${result.decision.reasoning}\n\n`
|
|
85
|
-
|
|
86
|
-
if (result.decision.alternatives) {
|
|
87
|
-
text += `**Alternatives Considered:** ${result.decision.alternatives}\n\n`
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (result.decision.outcome) {
|
|
91
|
-
text += `**Outcome:** ${result.decision.outcome}\n\n`
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (includeMetadata && result.decision.tags) {
|
|
95
|
-
const tags = Array.isArray(result.decision.tags)
|
|
96
|
-
? result.decision.tags.join(', ')
|
|
97
|
-
: result.decision.tags
|
|
98
|
-
text += `**Tags:** ${tags}\n\n`
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
text += `Memory (Similarity: ${(result.similarity * 100).toFixed(1)}%)\n\n`
|
|
102
|
-
text += result.memory.content + '\n\n'
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (includeMetadata) {
|
|
106
|
-
text += `*Project: ${result.memory.project}, Created: ${result.memory.createdAt.toLocaleDateString()}*\n\n`
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return text
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Format concise context entry
|
|
114
|
-
*/
|
|
115
|
-
private formatConcise(result: MemorySearchResult, index: number): string {
|
|
116
|
-
if (result.decision) {
|
|
117
|
-
const contextPreview = result.decision.context.slice(0, 100)
|
|
118
|
-
return (
|
|
119
|
-
`${index}. **${result.decision.decision}** (${(result.similarity * 100).toFixed(0)}% match)\n` +
|
|
120
|
-
` Context: ${contextPreview}${result.decision.context.length > 100 ? '...' : ''}\n\n`
|
|
121
|
-
)
|
|
122
|
-
} else {
|
|
123
|
-
const preview = result.memory.content.slice(0, 150)
|
|
124
|
-
return `${index}. ${preview}${result.memory.content.length > 150 ? '...' : ''} (${(result.similarity * 100).toFixed(0)}% match)\n\n`
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Format summary context entry
|
|
130
|
-
*/
|
|
131
|
-
private formatSummary(result: MemorySearchResult, index: number): string {
|
|
132
|
-
if (result.decision) {
|
|
133
|
-
return `${index}. ${result.decision.decision} (${result.memory.project})\n`
|
|
134
|
-
} else {
|
|
135
|
-
return `${index}. ${result.memory.content.slice(0, 80)}... (${result.memory.project})\n`
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Build context for decision recall
|
|
141
|
-
*/
|
|
142
|
-
buildDecisionContext(results: MemorySearchResult[]): string {
|
|
143
|
-
const decisions = results.filter((r) => r.decision)
|
|
144
|
-
|
|
145
|
-
if (decisions.length === 0) {
|
|
146
|
-
return 'No similar past decisions found.'
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return this.buildContext(decisions, {
|
|
150
|
-
format: 'detailed',
|
|
151
|
-
includeMetadata: true,
|
|
152
|
-
maxTokens: 2000
|
|
153
|
-
})
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Build context for recommendations
|
|
158
|
-
*/
|
|
159
|
-
buildRecommendationContext(results: MemorySearchResult[]): string {
|
|
160
|
-
if (results.length === 0) {
|
|
161
|
-
return 'No recommendations available based on past context.'
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
let context = `## Recommendations Based on Past Experience\n\n`
|
|
165
|
-
context += `Found ${results.length} relevant past experiences:\n\n`
|
|
166
|
-
|
|
167
|
-
context += this.buildContext(results, {
|
|
168
|
-
format: 'concise',
|
|
169
|
-
includeMetadata: false,
|
|
170
|
-
maxTokens: 1000
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
return context
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Build context for a single decision
|
|
178
|
-
*/
|
|
179
|
-
buildSingleDecisionContext(result: MemorySearchResult): string {
|
|
180
|
-
if (!result.decision) {
|
|
181
|
-
return 'No decision data available.'
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
let context = `## Related Decision\n\n`
|
|
185
|
-
context += `**Context:** ${result.decision.context}\n\n`
|
|
186
|
-
context += `**Decision Made:** ${result.decision.decision}\n\n`
|
|
187
|
-
context += `**Reasoning:** ${result.decision.reasoning}\n\n`
|
|
188
|
-
|
|
189
|
-
if (result.decision.alternatives) {
|
|
190
|
-
context += `**Alternatives Considered:** ${result.decision.alternatives}\n\n`
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (result.decision.outcome) {
|
|
194
|
-
context += `**Outcome:** ${result.decision.outcome}\n\n`
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
context += `---\n`
|
|
198
|
-
context += `*Similarity: ${(result.similarity * 100).toFixed(1)}% | `
|
|
199
|
-
context += `Project: ${result.memory.project} | `
|
|
200
|
-
context += `Date: ${result.memory.createdAt.toLocaleDateString()}*\n`
|
|
201
|
-
|
|
202
|
-
return context
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Estimate token count for text
|
|
207
|
-
*/
|
|
208
|
-
estimateTokens(text: string): number {
|
|
209
|
-
// Rough estimate: 1 token ~ 4 characters
|
|
210
|
-
return Math.ceil(text.length / 4)
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Truncate text to fit within token budget
|
|
215
|
-
*/
|
|
216
|
-
truncateToTokens(text: string, maxTokens: number): string {
|
|
217
|
-
const maxChars = maxTokens * 4
|
|
218
|
-
if (text.length <= maxChars) {
|
|
219
|
-
return text
|
|
220
|
-
}
|
|
221
|
-
return text.slice(0, maxChars - 20) + '\n\n[Truncated]'
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Combine multiple contexts with a separator
|
|
226
|
-
*/
|
|
227
|
-
combineContexts(contexts: string[], maxTokens?: number): string {
|
|
228
|
-
const combined = contexts.join('\n---\n\n')
|
|
229
|
-
|
|
230
|
-
if (maxTokens) {
|
|
231
|
-
return this.truncateToTokens(combined, maxTokens)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return combined
|
|
235
|
-
}
|
|
236
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Memory Context Builder
|
|
3
|
+
* Phase 3: Memory and Embedding System
|
|
4
|
+
*
|
|
5
|
+
* Formats retrieved memories for Claude Code consumption
|
|
6
|
+
* with token budget management
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Logger } from 'pino'
|
|
10
|
+
import type { MemorySearchResult } from './types'
|
|
11
|
+
|
|
12
|
+
export interface ContextOptions {
|
|
13
|
+
maxTokens?: number
|
|
14
|
+
includeMetadata?: boolean
|
|
15
|
+
format?: 'detailed' | 'concise' | 'summary'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class MemoryContextBuilder {
|
|
19
|
+
private logger: Logger
|
|
20
|
+
|
|
21
|
+
constructor(logger: Logger) {
|
|
22
|
+
this.logger = logger.child({ component: 'memory-context-builder' })
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build formatted context from search results
|
|
27
|
+
*/
|
|
28
|
+
buildContext(results: MemorySearchResult[], options: ContextOptions = {}): string {
|
|
29
|
+
if (results.length === 0) {
|
|
30
|
+
return 'No relevant memories found.'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const format = options.format || 'detailed'
|
|
34
|
+
const includeMetadata = options.includeMetadata ?? true
|
|
35
|
+
|
|
36
|
+
let context = `## Relevant Past Context (${results.length} items)\n\n`
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < results.length; i++) {
|
|
39
|
+
const result = results[i]!
|
|
40
|
+
const index = i + 1
|
|
41
|
+
|
|
42
|
+
if (format === 'detailed') {
|
|
43
|
+
context += this.formatDetailed(result, index, includeMetadata)
|
|
44
|
+
} else if (format === 'concise') {
|
|
45
|
+
context += this.formatConcise(result, index)
|
|
46
|
+
} else {
|
|
47
|
+
context += this.formatSummary(result, index)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
context += '\n'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Estimate token count (rough: 1 token ~ 4 characters)
|
|
54
|
+
const estimatedTokens = Math.ceil(context.length / 4)
|
|
55
|
+
|
|
56
|
+
if (options.maxTokens && estimatedTokens > options.maxTokens) {
|
|
57
|
+
this.logger.warn(
|
|
58
|
+
{ estimatedTokens, maxTokens: options.maxTokens },
|
|
59
|
+
'Context exceeds token budget, truncating'
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
// Truncate context
|
|
63
|
+
const maxChars = options.maxTokens * 4
|
|
64
|
+
context = context.slice(0, maxChars) + '\n\n[Context truncated due to length]'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return context
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Format detailed context entry
|
|
72
|
+
*/
|
|
73
|
+
private formatDetailed(
|
|
74
|
+
result: MemorySearchResult,
|
|
75
|
+
index: number,
|
|
76
|
+
includeMetadata: boolean
|
|
77
|
+
): string {
|
|
78
|
+
let text = `### ${index}. `
|
|
79
|
+
|
|
80
|
+
if (result.decision) {
|
|
81
|
+
text += `Decision (Similarity: ${(result.similarity * 100).toFixed(1)}%)\n\n`
|
|
82
|
+
text += `**Context:** ${result.decision.context}\n\n`
|
|
83
|
+
text += `**Decision:** ${result.decision.decision}\n\n`
|
|
84
|
+
text += `**Reasoning:** ${result.decision.reasoning}\n\n`
|
|
85
|
+
|
|
86
|
+
if (result.decision.alternatives) {
|
|
87
|
+
text += `**Alternatives Considered:** ${result.decision.alternatives}\n\n`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (result.decision.outcome) {
|
|
91
|
+
text += `**Outcome:** ${result.decision.outcome}\n\n`
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (includeMetadata && result.decision.tags) {
|
|
95
|
+
const tags = Array.isArray(result.decision.tags)
|
|
96
|
+
? result.decision.tags.join(', ')
|
|
97
|
+
: result.decision.tags
|
|
98
|
+
text += `**Tags:** ${tags}\n\n`
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
text += `Memory (Similarity: ${(result.similarity * 100).toFixed(1)}%)\n\n`
|
|
102
|
+
text += result.memory.content + '\n\n'
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (includeMetadata) {
|
|
106
|
+
text += `*Project: ${result.memory.project}, Created: ${result.memory.createdAt.toLocaleDateString()}*\n\n`
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return text
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format concise context entry
|
|
114
|
+
*/
|
|
115
|
+
private formatConcise(result: MemorySearchResult, index: number): string {
|
|
116
|
+
if (result.decision) {
|
|
117
|
+
const contextPreview = result.decision.context.slice(0, 100)
|
|
118
|
+
return (
|
|
119
|
+
`${index}. **${result.decision.decision}** (${(result.similarity * 100).toFixed(0)}% match)\n` +
|
|
120
|
+
` Context: ${contextPreview}${result.decision.context.length > 100 ? '...' : ''}\n\n`
|
|
121
|
+
)
|
|
122
|
+
} else {
|
|
123
|
+
const preview = result.memory.content.slice(0, 150)
|
|
124
|
+
return `${index}. ${preview}${result.memory.content.length > 150 ? '...' : ''} (${(result.similarity * 100).toFixed(0)}% match)\n\n`
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Format summary context entry
|
|
130
|
+
*/
|
|
131
|
+
private formatSummary(result: MemorySearchResult, index: number): string {
|
|
132
|
+
if (result.decision) {
|
|
133
|
+
return `${index}. ${result.decision.decision} (${result.memory.project})\n`
|
|
134
|
+
} else {
|
|
135
|
+
return `${index}. ${result.memory.content.slice(0, 80)}... (${result.memory.project})\n`
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Build context for decision recall
|
|
141
|
+
*/
|
|
142
|
+
buildDecisionContext(results: MemorySearchResult[]): string {
|
|
143
|
+
const decisions = results.filter((r) => r.decision)
|
|
144
|
+
|
|
145
|
+
if (decisions.length === 0) {
|
|
146
|
+
return 'No similar past decisions found.'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return this.buildContext(decisions, {
|
|
150
|
+
format: 'detailed',
|
|
151
|
+
includeMetadata: true,
|
|
152
|
+
maxTokens: 2000
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Build context for recommendations
|
|
158
|
+
*/
|
|
159
|
+
buildRecommendationContext(results: MemorySearchResult[]): string {
|
|
160
|
+
if (results.length === 0) {
|
|
161
|
+
return 'No recommendations available based on past context.'
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let context = `## Recommendations Based on Past Experience\n\n`
|
|
165
|
+
context += `Found ${results.length} relevant past experiences:\n\n`
|
|
166
|
+
|
|
167
|
+
context += this.buildContext(results, {
|
|
168
|
+
format: 'concise',
|
|
169
|
+
includeMetadata: false,
|
|
170
|
+
maxTokens: 1000
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
return context
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Build context for a single decision
|
|
178
|
+
*/
|
|
179
|
+
buildSingleDecisionContext(result: MemorySearchResult): string {
|
|
180
|
+
if (!result.decision) {
|
|
181
|
+
return 'No decision data available.'
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let context = `## Related Decision\n\n`
|
|
185
|
+
context += `**Context:** ${result.decision.context}\n\n`
|
|
186
|
+
context += `**Decision Made:** ${result.decision.decision}\n\n`
|
|
187
|
+
context += `**Reasoning:** ${result.decision.reasoning}\n\n`
|
|
188
|
+
|
|
189
|
+
if (result.decision.alternatives) {
|
|
190
|
+
context += `**Alternatives Considered:** ${result.decision.alternatives}\n\n`
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (result.decision.outcome) {
|
|
194
|
+
context += `**Outcome:** ${result.decision.outcome}\n\n`
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
context += `---\n`
|
|
198
|
+
context += `*Similarity: ${(result.similarity * 100).toFixed(1)}% | `
|
|
199
|
+
context += `Project: ${result.memory.project} | `
|
|
200
|
+
context += `Date: ${result.memory.createdAt.toLocaleDateString()}*\n`
|
|
201
|
+
|
|
202
|
+
return context
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Estimate token count for text
|
|
207
|
+
*/
|
|
208
|
+
estimateTokens(text: string): number {
|
|
209
|
+
// Rough estimate: 1 token ~ 4 characters
|
|
210
|
+
return Math.ceil(text.length / 4)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Truncate text to fit within token budget
|
|
215
|
+
*/
|
|
216
|
+
truncateToTokens(text: string, maxTokens: number): string {
|
|
217
|
+
const maxChars = maxTokens * 4
|
|
218
|
+
if (text.length <= maxChars) {
|
|
219
|
+
return text
|
|
220
|
+
}
|
|
221
|
+
return text.slice(0, maxChars - 20) + '\n\n[Truncated]'
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Combine multiple contexts with a separator
|
|
226
|
+
*/
|
|
227
|
+
combineContexts(contexts: string[], maxTokens?: number): string {
|
|
228
|
+
const combined = contexts.join('\n---\n\n')
|
|
229
|
+
|
|
230
|
+
if (maxTokens) {
|
|
231
|
+
return this.truncateToTokens(combined, maxTokens)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return combined
|
|
235
|
+
}
|
|
236
|
+
}
|