claude-brain 0.9.3 → 0.13.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/VERSION +1 -1
- package/assets/CLAUDE.md +9 -1
- package/package.json +1 -1
- package/src/automation/phase12-manager.ts +456 -0
- package/src/automation/project-detector.ts +13 -0
- package/src/automation/repo-scanner.ts +205 -0
- package/src/cli/bin.ts +30 -0
- package/src/cli/commands/git-hook.ts +189 -0
- package/src/cli/commands/hooks.ts +8 -9
- package/src/cli/commands/init.ts +98 -0
- package/src/cli/commands/serve.ts +7 -20
- package/src/cli/commands/update.ts +3 -3
- package/src/config/defaults.ts +4 -1
- package/src/config/schema.ts +27 -7
- package/src/hooks/brain-hook.ts +8 -6
- package/src/hooks/capture.ts +9 -2
- package/src/hooks/git-capture.ts +94 -0
- package/src/hooks/git-hook-installer.ts +197 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/session-tracker.ts +79 -3
- package/src/hooks/types.ts +1 -1
- package/src/intelligence/index.ts +24 -0
- package/src/knowledge/graph/builder.ts +26 -0
- package/src/memory/chroma/store.ts +18 -2
- package/src/memory/episodic/manager.ts +17 -0
- package/src/memory/index.ts +48 -18
- package/src/phase12/index.ts +3 -454
- package/src/routing/intent-classifier.ts +107 -9
- package/src/routing/response-filter.ts +50 -17
- package/src/routing/router.ts +472 -224
- package/src/routing/search-engine.ts +464 -0
- package/src/routing/types.ts +84 -0
- package/src/server/handlers/call-tool.ts +4 -49
- package/src/server/handlers/tools/analyze-decision-evolution.ts +1 -1
- package/src/server/handlers/tools/detect-trends.ts +1 -1
- package/src/server/handlers/tools/find-cross-project-patterns.ts +1 -1
- package/src/server/handlers/tools/get-decision-timeline.ts +2 -2
- package/src/server/handlers/tools/get-recommendations.ts +1 -1
- package/src/server/handlers/tools/index.ts +5 -7
- package/src/server/handlers/tools/what-if-analysis.ts +1 -1
- package/src/server/providers/resources.ts +195 -0
- package/src/server/services.ts +81 -6
- package/src/tools/schemas.ts +7 -329
- package/src/utils/phase12-helper.ts +2 -2
- package/src/utils/timing.ts +47 -0
- package/src/vault/writer.ts +22 -2
- /package/src/{cross-project → intelligence/cross-project}/affinity.ts +0 -0
- /package/src/{cross-project → intelligence/cross-project}/generalizer.ts +0 -0
- /package/src/{cross-project → intelligence/cross-project}/index.ts +0 -0
- /package/src/{cross-project → intelligence/cross-project}/transfer.ts +0 -0
- /package/src/{optimization → intelligence/optimization}/index.ts +0 -0
- /package/src/{optimization → intelligence/optimization}/precompute.ts +0 -0
- /package/src/{optimization → intelligence/optimization}/semantic-cache.ts +0 -0
- /package/src/{prediction → intelligence/prediction}/context-anticipator.ts +0 -0
- /package/src/{prediction → intelligence/prediction}/decision-predictor.ts +0 -0
- /package/src/{prediction → intelligence/prediction}/index.ts +0 -0
- /package/src/{prediction → intelligence/prediction}/recommender.ts +0 -0
- /package/src/{reasoning → intelligence/reasoning}/chain-retrieval.ts +0 -0
- /package/src/{reasoning → intelligence/reasoning}/counterfactual.ts +0 -0
- /package/src/{reasoning → intelligence/reasoning}/index.ts +0 -0
- /package/src/{reasoning → intelligence/reasoning}/synthesizer.ts +0 -0
- /package/src/{temporal → intelligence/temporal}/evolution.ts +0 -0
- /package/src/{temporal → intelligence/temporal}/index.ts +0 -0
- /package/src/{temporal → intelligence/temporal}/query-processor.ts +0 -0
- /package/src/{temporal → intelligence/temporal}/timeline.ts +0 -0
- /package/src/{temporal → intelligence/temporal}/trends.ts +0 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.12.0
|
package/assets/CLAUDE.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
# Claude Brain
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Brain works automatically in the background — you don't need to call it.
|
|
4
|
+
|
|
5
|
+
Use the `brain` tool ONLY when you want to:
|
|
6
|
+
- Explicitly store a decision: "I decided to use Redis for caching"
|
|
7
|
+
- Ask a specific question: "What did we decide about auth?"
|
|
8
|
+
- Update something: "Changed my mind, use Postgres instead"
|
|
9
|
+
- Delete something: "Remove the note about migrations"
|
|
10
|
+
|
|
11
|
+
Everything else (session tracking, file captures, git commits, context loading) happens automatically.
|
package/package.json
CHANGED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 12 Manager
|
|
3
|
+
* Advanced Memory & Intelligent Automation
|
|
4
|
+
*
|
|
5
|
+
* Orchestrates all Phase 12 features:
|
|
6
|
+
* - Pattern Recognition
|
|
7
|
+
* - Learning System
|
|
8
|
+
* - Knowledge Extraction
|
|
9
|
+
* - Auto-Context Loading
|
|
10
|
+
* - Decision Detection
|
|
11
|
+
* - Proactive Recall
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { Logger } from 'pino'
|
|
15
|
+
import type { VaultManager } from '@/vault'
|
|
16
|
+
import type { MemoryManager } from '@/memory'
|
|
17
|
+
import type { ContextManager } from '@/context'
|
|
18
|
+
import { PatternRecognizer, type Pattern } from '@/memory/patterns'
|
|
19
|
+
import { LearningSystem, type LearningInsights } from '@/memory/learning'
|
|
20
|
+
import { KnowledgeExtractor, type ExtractedKnowledge } from '@/memory/knowledge-extractor'
|
|
21
|
+
import {
|
|
22
|
+
ProjectDetector,
|
|
23
|
+
AutoContextLoader,
|
|
24
|
+
DecisionDetector,
|
|
25
|
+
ProactiveRecallEngine,
|
|
26
|
+
type DetectedProject,
|
|
27
|
+
type DetectedDecision,
|
|
28
|
+
type RecallResult
|
|
29
|
+
} from '@/automation'
|
|
30
|
+
|
|
31
|
+
export interface Phase12Config {
|
|
32
|
+
/**
|
|
33
|
+
* Enable automatic context loading on startup
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
autoLoadContext: boolean
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Enable automatic decision detection and saving
|
|
40
|
+
* @default true
|
|
41
|
+
*/
|
|
42
|
+
autoDetectDecisions: boolean
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Enable proactive memory recall
|
|
46
|
+
* @default true
|
|
47
|
+
*/
|
|
48
|
+
enableProactiveRecall: boolean
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Minimum confidence for auto-saving decisions
|
|
52
|
+
* @default 0.7
|
|
53
|
+
*/
|
|
54
|
+
decisionConfidenceThreshold: number
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Enable pattern analysis on startup
|
|
58
|
+
* @default false
|
|
59
|
+
*/
|
|
60
|
+
analyzePatternsonStartup: boolean
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Enable knowledge extraction from messages
|
|
64
|
+
* @default true
|
|
65
|
+
*/
|
|
66
|
+
enableKnowledgeExtraction: boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const DEFAULT_CONFIG: Phase12Config = {
|
|
70
|
+
autoLoadContext: true,
|
|
71
|
+
autoDetectDecisions: true,
|
|
72
|
+
enableProactiveRecall: true,
|
|
73
|
+
decisionConfidenceThreshold: 0.7,
|
|
74
|
+
analyzePatternsonStartup: false,
|
|
75
|
+
enableKnowledgeExtraction: true
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface MessageProcessingResult {
|
|
79
|
+
recalledMemories?: RecallResult
|
|
80
|
+
detectedDecision?: DetectedDecision
|
|
81
|
+
extractedKnowledge?: ExtractedKnowledge[]
|
|
82
|
+
detectedProject?: DetectedProject
|
|
83
|
+
decisionSaved?: boolean
|
|
84
|
+
decisionId?: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface Phase12Stats {
|
|
88
|
+
patterns: {
|
|
89
|
+
total: number
|
|
90
|
+
byType: Record<string, number>
|
|
91
|
+
}
|
|
92
|
+
learning: LearningInsights
|
|
93
|
+
automation: {
|
|
94
|
+
contextLoaded: boolean
|
|
95
|
+
currentProject: string | null
|
|
96
|
+
recallStats: {
|
|
97
|
+
totalRecalls: number
|
|
98
|
+
successfulRecalls: number
|
|
99
|
+
averageRelevance: number
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export class Phase12Manager {
|
|
105
|
+
private logger: Logger
|
|
106
|
+
private config: Phase12Config
|
|
107
|
+
private initialized = false
|
|
108
|
+
|
|
109
|
+
// Advanced Memory
|
|
110
|
+
public readonly patterns: PatternRecognizer
|
|
111
|
+
public readonly learning: LearningSystem
|
|
112
|
+
public readonly knowledge: KnowledgeExtractor
|
|
113
|
+
|
|
114
|
+
// Intelligent Automation
|
|
115
|
+
public readonly projectDetector: ProjectDetector
|
|
116
|
+
public readonly autoContext: AutoContextLoader
|
|
117
|
+
public readonly decisionDetector: DecisionDetector
|
|
118
|
+
public readonly proactiveRecall: ProactiveRecallEngine
|
|
119
|
+
|
|
120
|
+
constructor(
|
|
121
|
+
logger: Logger,
|
|
122
|
+
vault: VaultManager,
|
|
123
|
+
memory: MemoryManager,
|
|
124
|
+
context: ContextManager,
|
|
125
|
+
config: Partial<Phase12Config> = {}
|
|
126
|
+
) {
|
|
127
|
+
this.logger = logger.child({ component: 'phase12-manager' })
|
|
128
|
+
this.config = { ...DEFAULT_CONFIG, ...config }
|
|
129
|
+
|
|
130
|
+
// Initialize advanced memory features
|
|
131
|
+
this.patterns = new PatternRecognizer(logger, memory)
|
|
132
|
+
this.learning = new LearningSystem(logger, memory)
|
|
133
|
+
this.knowledge = new KnowledgeExtractor(logger, memory)
|
|
134
|
+
|
|
135
|
+
// Initialize intelligent automation
|
|
136
|
+
this.projectDetector = new ProjectDetector(logger, vault)
|
|
137
|
+
this.autoContext = new AutoContextLoader(
|
|
138
|
+
logger,
|
|
139
|
+
context,
|
|
140
|
+
this.projectDetector
|
|
141
|
+
)
|
|
142
|
+
this.decisionDetector = new DecisionDetector(logger, memory)
|
|
143
|
+
this.proactiveRecall = new ProactiveRecallEngine(logger, memory)
|
|
144
|
+
|
|
145
|
+
this.logger.info(
|
|
146
|
+
{ config: this.config },
|
|
147
|
+
'Phase 12 Manager created'
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Initialize all Phase 12 features
|
|
153
|
+
*/
|
|
154
|
+
async initialize(): Promise<void> {
|
|
155
|
+
if (this.initialized) {
|
|
156
|
+
this.logger.warn('Phase 12 already initialized')
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this.logger.info('Initializing Phase 12 features...')
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
// Start auto-context loader
|
|
164
|
+
if (this.config.autoLoadContext) {
|
|
165
|
+
await this.autoContext.initialize()
|
|
166
|
+
this.logger.info('Auto-context loader initialized')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Run initial pattern analysis if enabled
|
|
170
|
+
if (this.config.analyzePatternsonStartup) {
|
|
171
|
+
const patterns = await this.patterns.analyzePatterns()
|
|
172
|
+
this.logger.info(
|
|
173
|
+
{ patternCount: patterns.length },
|
|
174
|
+
'Initial pattern analysis complete'
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.initialized = true
|
|
179
|
+
this.logger.info('Phase 12 initialization complete')
|
|
180
|
+
} catch (error) {
|
|
181
|
+
this.logger.error({ error }, 'Phase 12 initialization failed')
|
|
182
|
+
throw error
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Check if Phase 12 is initialized
|
|
188
|
+
*/
|
|
189
|
+
isInitialized(): boolean {
|
|
190
|
+
return this.initialized
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Process incoming message with all Phase 12 features
|
|
195
|
+
*/
|
|
196
|
+
async processMessage(
|
|
197
|
+
message: string,
|
|
198
|
+
project?: string
|
|
199
|
+
): Promise<MessageProcessingResult> {
|
|
200
|
+
const results: MessageProcessingResult = {}
|
|
201
|
+
|
|
202
|
+
// Use detected project if not provided
|
|
203
|
+
const targetProject = project || this.autoContext.getCurrentProject() || undefined
|
|
204
|
+
|
|
205
|
+
// 1. Proactive recall
|
|
206
|
+
if (this.config.enableProactiveRecall) {
|
|
207
|
+
if (this.proactiveRecall.shouldRecall(message)) {
|
|
208
|
+
results.recalledMemories = await this.proactiveRecall.recall(
|
|
209
|
+
message,
|
|
210
|
+
targetProject
|
|
211
|
+
) || undefined
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 2. Decision detection
|
|
216
|
+
if (this.config.autoDetectDecisions && targetProject) {
|
|
217
|
+
const decision = this.decisionDetector.detectDecision(message, targetProject)
|
|
218
|
+
|
|
219
|
+
if (decision && decision.confidence >= this.config.decisionConfidenceThreshold) {
|
|
220
|
+
results.detectedDecision = decision
|
|
221
|
+
|
|
222
|
+
// Auto-save if confidence is high enough
|
|
223
|
+
const decisionId = await this.decisionDetector.autoSaveDecision(
|
|
224
|
+
decision,
|
|
225
|
+
targetProject
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if (decisionId) {
|
|
229
|
+
results.decisionSaved = true
|
|
230
|
+
results.decisionId = decisionId
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// 3. Knowledge extraction
|
|
236
|
+
if (this.config.enableKnowledgeExtraction) {
|
|
237
|
+
const extracted = await this.knowledge.extractFromConversation(
|
|
238
|
+
message,
|
|
239
|
+
targetProject
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if (extracted.knowledge.length > 0) {
|
|
243
|
+
results.extractedKnowledge = extracted.knowledge
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Log processing results
|
|
248
|
+
if (Object.keys(results).length > 0) {
|
|
249
|
+
this.logger.debug(
|
|
250
|
+
{
|
|
251
|
+
hasRecall: !!results.recalledMemories,
|
|
252
|
+
hasDecision: !!results.detectedDecision,
|
|
253
|
+
knowledgeCount: results.extractedKnowledge?.length || 0,
|
|
254
|
+
decisionSaved: results.decisionSaved
|
|
255
|
+
},
|
|
256
|
+
'Message processed with Phase 12 features'
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return results
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Process outgoing response for decisions
|
|
265
|
+
* Call this with Claude's responses to detect and save decisions
|
|
266
|
+
*/
|
|
267
|
+
async processResponse(
|
|
268
|
+
response: string,
|
|
269
|
+
project?: string
|
|
270
|
+
): Promise<MessageProcessingResult> {
|
|
271
|
+
const results: MessageProcessingResult = {}
|
|
272
|
+
const targetProject = project || this.autoContext.getCurrentProject() || undefined
|
|
273
|
+
|
|
274
|
+
// Decision detection on responses (Claude's decisions)
|
|
275
|
+
if (this.config.autoDetectDecisions && targetProject) {
|
|
276
|
+
const decision = this.decisionDetector.detectDecision(response, targetProject)
|
|
277
|
+
|
|
278
|
+
if (decision && decision.confidence >= this.config.decisionConfidenceThreshold) {
|
|
279
|
+
results.detectedDecision = decision
|
|
280
|
+
|
|
281
|
+
const decisionId = await this.decisionDetector.autoSaveDecision(
|
|
282
|
+
decision,
|
|
283
|
+
targetProject
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
if (decisionId) {
|
|
287
|
+
results.decisionSaved = true
|
|
288
|
+
results.decisionId = decisionId
|
|
289
|
+
this.logger.info(
|
|
290
|
+
{ decisionId, project: targetProject },
|
|
291
|
+
'Decision auto-saved from response'
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Knowledge extraction from responses
|
|
298
|
+
if (this.config.enableKnowledgeExtraction) {
|
|
299
|
+
const extracted = await this.knowledge.extractFromConversation(
|
|
300
|
+
response,
|
|
301
|
+
targetProject
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
if (extracted.knowledge.length > 0) {
|
|
305
|
+
results.extractedKnowledge = extracted.knowledge
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return results
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get proactively recalled context for a query
|
|
314
|
+
*/
|
|
315
|
+
async getRecalledContext(query: string, project?: string): Promise<string | null> {
|
|
316
|
+
const result = await this.proactiveRecall.recall(
|
|
317
|
+
query,
|
|
318
|
+
project || this.autoContext.getCurrentProject() || undefined
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
return result?.formattedContext || null
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Get relevant patterns for a query
|
|
326
|
+
*/
|
|
327
|
+
async getRelevantPatterns(query: string): Promise<Pattern[]> {
|
|
328
|
+
return this.patterns.getRelevantPatterns(query)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Record a correction for learning
|
|
333
|
+
*/
|
|
334
|
+
async recordCorrection(
|
|
335
|
+
original: string,
|
|
336
|
+
corrected: string,
|
|
337
|
+
reasoning: string,
|
|
338
|
+
project?: string
|
|
339
|
+
): Promise<string> {
|
|
340
|
+
const targetProject = project || this.autoContext.getCurrentProject() || 'global'
|
|
341
|
+
return this.learning.recordCorrection(original, corrected, reasoning, targetProject)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Get comprehensive Phase 12 statistics
|
|
346
|
+
*/
|
|
347
|
+
getStats(): Phase12Stats {
|
|
348
|
+
const allPatterns = this.patterns.getAllPatterns()
|
|
349
|
+
const patternsByType: Record<string, number> = {}
|
|
350
|
+
|
|
351
|
+
for (const pattern of allPatterns) {
|
|
352
|
+
patternsByType[pattern.type] = (patternsByType[pattern.type] || 0) + 1
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const recallStats = this.proactiveRecall.getStats()
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
patterns: {
|
|
359
|
+
total: allPatterns.length,
|
|
360
|
+
byType: patternsByType
|
|
361
|
+
},
|
|
362
|
+
learning: this.learning.getLearningInsights(),
|
|
363
|
+
automation: {
|
|
364
|
+
contextLoaded: this.autoContext.getCurrentContext() !== undefined,
|
|
365
|
+
currentProject: this.autoContext.getCurrentProject(),
|
|
366
|
+
recallStats: {
|
|
367
|
+
totalRecalls: recallStats.totalRecalls,
|
|
368
|
+
successfulRecalls: recallStats.successfulRecalls,
|
|
369
|
+
averageRelevance: recallStats.averageRelevance
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Get formatted status report
|
|
377
|
+
*/
|
|
378
|
+
getStatusReport(): string {
|
|
379
|
+
const stats = this.getStats()
|
|
380
|
+
const parts: string[] = []
|
|
381
|
+
|
|
382
|
+
parts.push('# Phase 12 Status Report\n')
|
|
383
|
+
|
|
384
|
+
// Current project
|
|
385
|
+
parts.push('## Current Context')
|
|
386
|
+
if (stats.automation.currentProject) {
|
|
387
|
+
parts.push(`- **Project:** ${stats.automation.currentProject}`)
|
|
388
|
+
parts.push(`- **Context Loaded:** Yes`)
|
|
389
|
+
} else {
|
|
390
|
+
parts.push('- **Project:** Not detected (use smart_context to load project context)')
|
|
391
|
+
parts.push('- **Context Loaded:** No')
|
|
392
|
+
}
|
|
393
|
+
parts.push('')
|
|
394
|
+
|
|
395
|
+
// Pattern analysis
|
|
396
|
+
parts.push('## Pattern Analysis')
|
|
397
|
+
parts.push(`- **Total Patterns:** ${stats.patterns.total}`)
|
|
398
|
+
if (stats.patterns.total === 0) {
|
|
399
|
+
parts.push(' *No patterns stored yet. Use `recognize_pattern` to document reusable solutions.*')
|
|
400
|
+
} else {
|
|
401
|
+
for (const [type, count] of Object.entries(stats.patterns.byType)) {
|
|
402
|
+
parts.push(` - ${type}: ${count}`)
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
parts.push('')
|
|
406
|
+
|
|
407
|
+
// Learning system
|
|
408
|
+
parts.push('## Learning System')
|
|
409
|
+
parts.push(`- **Total Corrections:** ${stats.learning.correctionCount}`)
|
|
410
|
+
if (stats.learning.correctionCount === 0) {
|
|
411
|
+
parts.push(' *No corrections recorded yet. Use `record_correction` to document lessons learned.*')
|
|
412
|
+
}
|
|
413
|
+
parts.push(`- **Categories Improved:** ${stats.learning.categoriesImproved.join(', ') || 'None'}`)
|
|
414
|
+
if (stats.learning.preferences.length > 0) {
|
|
415
|
+
parts.push('- **Top Preferences:**')
|
|
416
|
+
for (const pref of stats.learning.preferences.slice(0, 3)) {
|
|
417
|
+
parts.push(` - ${pref.category}: ${Math.round(pref.strength * 100)}% confidence`)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
parts.push('')
|
|
421
|
+
|
|
422
|
+
// Proactive recall
|
|
423
|
+
parts.push('## Proactive Recall')
|
|
424
|
+
parts.push(`- **Total Recalls:** ${stats.automation.recallStats.totalRecalls}`)
|
|
425
|
+
parts.push(`- **Successful:** ${stats.automation.recallStats.successfulRecalls}`)
|
|
426
|
+
const successRate = stats.automation.recallStats.totalRecalls > 0
|
|
427
|
+
? Math.round((stats.automation.recallStats.successfulRecalls / stats.automation.recallStats.totalRecalls) * 100)
|
|
428
|
+
: 0
|
|
429
|
+
parts.push(`- **Success Rate:** ${successRate}%`)
|
|
430
|
+
parts.push(`- **Average Relevance:** ${Math.round(stats.automation.recallStats.averageRelevance * 100)}%`)
|
|
431
|
+
|
|
432
|
+
if (stats.automation.recallStats.totalRecalls === 0) {
|
|
433
|
+
parts.push('\n*Proactive recall activates automatically when you ask questions or request recommendations.*')
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return parts.join('\n')
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Cleanup and reset Phase 12
|
|
441
|
+
*/
|
|
442
|
+
cleanup(): void {
|
|
443
|
+
this.autoContext.clearAllContexts()
|
|
444
|
+
this.patterns.clearPatterns()
|
|
445
|
+
this.learning.clearLearning()
|
|
446
|
+
this.proactiveRecall.resetStats()
|
|
447
|
+
this.initialized = false
|
|
448
|
+
this.logger.info('Phase 12 cleaned up')
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Re-export types
|
|
453
|
+
export type { Pattern } from '@/memory/patterns'
|
|
454
|
+
export type { Correction, Preference, LearningInsights } from '@/memory/learning'
|
|
455
|
+
export type { ExtractedKnowledge, ExtractionResult } from '@/memory/knowledge-extractor'
|
|
456
|
+
export type { DetectedProject, DetectedDecision, RecallResult } from '@/automation'
|
|
@@ -263,6 +263,19 @@ export class ProjectDetector {
|
|
|
263
263
|
return matrix[b.length][a.length]
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Check if a project has any existing memories.
|
|
268
|
+
* Returns true if no memories found (i.e., it's a new project).
|
|
269
|
+
*/
|
|
270
|
+
async isNewProject(projectName: string): Promise<boolean> {
|
|
271
|
+
try {
|
|
272
|
+
const projects = await this.vault.listProjects()
|
|
273
|
+
return !projects.includes(projectName)
|
|
274
|
+
} catch {
|
|
275
|
+
return true
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
266
279
|
/**
|
|
267
280
|
* Get project suggestions based on working directory
|
|
268
281
|
*/
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repo Scanner
|
|
3
|
+
* Phase 22: Scans a repository to extract initial project context.
|
|
4
|
+
* Used on first encounter or via `claude-brain init`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs/promises'
|
|
8
|
+
import path from 'path'
|
|
9
|
+
import { execSync } from 'child_process'
|
|
10
|
+
|
|
11
|
+
export interface ProjectContext {
|
|
12
|
+
name: string
|
|
13
|
+
description: string
|
|
14
|
+
techStack: string[]
|
|
15
|
+
structure: string
|
|
16
|
+
recentActivity: string
|
|
17
|
+
projectInstructions?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Scan a repository and extract project context.
|
|
22
|
+
*/
|
|
23
|
+
export async function scanRepo(repoPath: string): Promise<ProjectContext> {
|
|
24
|
+
const name = path.basename(repoPath)
|
|
25
|
+
|
|
26
|
+
// Detect tech stack and name from manifest files
|
|
27
|
+
const manifest = await detectManifest(repoPath)
|
|
28
|
+
const projectName = manifest?.name || name
|
|
29
|
+
const techStack = manifest?.techStack || []
|
|
30
|
+
|
|
31
|
+
// Read README for description
|
|
32
|
+
const description = await readDescription(repoPath)
|
|
33
|
+
|
|
34
|
+
// Get directory structure
|
|
35
|
+
const structure = await getDirectoryTree(repoPath, 2)
|
|
36
|
+
|
|
37
|
+
// Get recent git activity
|
|
38
|
+
const recentActivity = getRecentGitActivity(repoPath)
|
|
39
|
+
|
|
40
|
+
// Read CLAUDE.md if present
|
|
41
|
+
const projectInstructions = await readClaudeMd(repoPath)
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
name: projectName,
|
|
45
|
+
description,
|
|
46
|
+
techStack,
|
|
47
|
+
structure,
|
|
48
|
+
recentActivity,
|
|
49
|
+
projectInstructions
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface ManifestInfo {
|
|
54
|
+
name: string
|
|
55
|
+
techStack: string[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function detectManifest(repoPath: string): Promise<ManifestInfo | null> {
|
|
59
|
+
// Try package.json (Node.js)
|
|
60
|
+
try {
|
|
61
|
+
const content = await fs.readFile(path.join(repoPath, 'package.json'), 'utf-8')
|
|
62
|
+
const pkg = JSON.parse(content)
|
|
63
|
+
return {
|
|
64
|
+
name: (pkg.name || '').replace(/^@[^/]+\//, ''),
|
|
65
|
+
techStack: extractTechStack(pkg)
|
|
66
|
+
}
|
|
67
|
+
} catch { /* not a Node project */ }
|
|
68
|
+
|
|
69
|
+
// Try pyproject.toml (Python)
|
|
70
|
+
try {
|
|
71
|
+
const content = await fs.readFile(path.join(repoPath, 'pyproject.toml'), 'utf-8')
|
|
72
|
+
const nameMatch = content.match(/name\s*=\s*"([^"]+)"/)
|
|
73
|
+
const deps = content.match(/dependencies\s*=\s*\[([\s\S]*?)\]/)?.[1] || ''
|
|
74
|
+
const techStack = ['python']
|
|
75
|
+
if (deps.includes('django')) techStack.push('django')
|
|
76
|
+
if (deps.includes('fastapi')) techStack.push('fastapi')
|
|
77
|
+
if (deps.includes('flask')) techStack.push('flask')
|
|
78
|
+
if (deps.includes('pytest')) techStack.push('pytest')
|
|
79
|
+
return { name: nameMatch?.[1] || '', techStack }
|
|
80
|
+
} catch { /* not a Python project */ }
|
|
81
|
+
|
|
82
|
+
// Try Cargo.toml (Rust)
|
|
83
|
+
try {
|
|
84
|
+
const content = await fs.readFile(path.join(repoPath, 'Cargo.toml'), 'utf-8')
|
|
85
|
+
const nameMatch = content.match(/name\s*=\s*"([^"]+)"/)
|
|
86
|
+
return { name: nameMatch?.[1] || '', techStack: ['rust'] }
|
|
87
|
+
} catch { /* not a Rust project */ }
|
|
88
|
+
|
|
89
|
+
// Try go.mod (Go)
|
|
90
|
+
try {
|
|
91
|
+
const content = await fs.readFile(path.join(repoPath, 'go.mod'), 'utf-8')
|
|
92
|
+
const moduleMatch = content.match(/module\s+(\S+)/)
|
|
93
|
+
const moduleName = moduleMatch?.[1]?.split('/').pop() || ''
|
|
94
|
+
return { name: moduleName, techStack: ['go'] }
|
|
95
|
+
} catch { /* not a Go project */ }
|
|
96
|
+
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function extractTechStack(pkg: any): string[] {
|
|
101
|
+
const stack: string[] = []
|
|
102
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }
|
|
103
|
+
|
|
104
|
+
// Runtime
|
|
105
|
+
if (allDeps.typescript || allDeps['@types/node']) stack.push('typescript')
|
|
106
|
+
else stack.push('javascript')
|
|
107
|
+
|
|
108
|
+
// Frameworks
|
|
109
|
+
if (allDeps.react || allDeps['react-dom']) stack.push('react')
|
|
110
|
+
if (allDeps.next) stack.push('nextjs')
|
|
111
|
+
if (allDeps.vue) stack.push('vue')
|
|
112
|
+
if (allDeps.svelte) stack.push('svelte')
|
|
113
|
+
if (allDeps.express) stack.push('express')
|
|
114
|
+
if (allDeps.hono) stack.push('hono')
|
|
115
|
+
if (allDeps.fastify) stack.push('fastify')
|
|
116
|
+
|
|
117
|
+
// Tools
|
|
118
|
+
if (allDeps.bun || pkg.devDependencies?.['@types/bun']) stack.push('bun')
|
|
119
|
+
if (allDeps.vitest) stack.push('vitest')
|
|
120
|
+
if (allDeps.jest) stack.push('jest')
|
|
121
|
+
if (allDeps.tailwindcss) stack.push('tailwind')
|
|
122
|
+
if (allDeps.prisma || allDeps['@prisma/client']) stack.push('prisma')
|
|
123
|
+
if (allDeps.drizzle || allDeps['drizzle-orm']) stack.push('drizzle')
|
|
124
|
+
|
|
125
|
+
// AI/ML
|
|
126
|
+
if (allDeps['@modelcontextprotocol/sdk']) stack.push('mcp')
|
|
127
|
+
if (allDeps['@anthropic-ai/sdk']) stack.push('claude-api')
|
|
128
|
+
if (allDeps.openai) stack.push('openai')
|
|
129
|
+
if (allDeps.langchain) stack.push('langchain')
|
|
130
|
+
|
|
131
|
+
return stack
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function readDescription(repoPath: string): Promise<string> {
|
|
135
|
+
for (const readme of ['README.md', 'readme.md', 'Readme.md']) {
|
|
136
|
+
try {
|
|
137
|
+
const content = await fs.readFile(path.join(repoPath, readme), 'utf-8')
|
|
138
|
+
return content.slice(0, 500).trim()
|
|
139
|
+
} catch { /* no readme */ }
|
|
140
|
+
}
|
|
141
|
+
return ''
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function getDirectoryTree(repoPath: string, maxDepth: number): Promise<string> {
|
|
145
|
+
const IGNORED = new Set([
|
|
146
|
+
'node_modules', '.git', 'dist', 'build', '.next', '__pycache__',
|
|
147
|
+
'.venv', 'venv', '.mypy_cache', '.pytest_cache', 'target',
|
|
148
|
+
'.turbo', '.vercel', 'coverage', '.nyc_output'
|
|
149
|
+
])
|
|
150
|
+
|
|
151
|
+
const lines: string[] = []
|
|
152
|
+
|
|
153
|
+
async function walk(dir: string, depth: number, prefix: string) {
|
|
154
|
+
if (depth > maxDepth) return
|
|
155
|
+
|
|
156
|
+
let entries: Awaited<ReturnType<typeof fs.readdir>>
|
|
157
|
+
try {
|
|
158
|
+
entries = await fs.readdir(dir, { withFileTypes: true })
|
|
159
|
+
} catch {
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const filtered = entries
|
|
164
|
+
.filter(e => !e.name.startsWith('.') || e.name === '.github')
|
|
165
|
+
.filter(e => !IGNORED.has(e.name))
|
|
166
|
+
.sort((a, b) => {
|
|
167
|
+
// Directories first
|
|
168
|
+
if (a.isDirectory() && !b.isDirectory()) return -1
|
|
169
|
+
if (!a.isDirectory() && b.isDirectory()) return 1
|
|
170
|
+
return a.name.localeCompare(b.name)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
for (const entry of filtered.slice(0, 30)) { // Cap at 30 per level
|
|
174
|
+
const isDir = entry.isDirectory()
|
|
175
|
+
lines.push(`${prefix}${isDir ? entry.name + '/' : entry.name}`)
|
|
176
|
+
if (isDir) {
|
|
177
|
+
await walk(path.join(dir, entry.name), depth + 1, prefix + ' ')
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
await walk(repoPath, 0, '')
|
|
183
|
+
return lines.join('\n')
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getRecentGitActivity(repoPath: string): string {
|
|
187
|
+
try {
|
|
188
|
+
return execSync('git log --oneline -10', {
|
|
189
|
+
cwd: repoPath,
|
|
190
|
+
encoding: 'utf-8',
|
|
191
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
192
|
+
}).trim()
|
|
193
|
+
} catch {
|
|
194
|
+
return ''
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function readClaudeMd(repoPath: string): Promise<string | undefined> {
|
|
199
|
+
try {
|
|
200
|
+
const content = await fs.readFile(path.join(repoPath, 'CLAUDE.md'), 'utf-8')
|
|
201
|
+
return content.trim()
|
|
202
|
+
} catch {
|
|
203
|
+
return undefined
|
|
204
|
+
}
|
|
205
|
+
}
|