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.
Files changed (66) hide show
  1. package/VERSION +1 -1
  2. package/assets/CLAUDE.md +9 -1
  3. package/package.json +1 -1
  4. package/src/automation/phase12-manager.ts +456 -0
  5. package/src/automation/project-detector.ts +13 -0
  6. package/src/automation/repo-scanner.ts +205 -0
  7. package/src/cli/bin.ts +30 -0
  8. package/src/cli/commands/git-hook.ts +189 -0
  9. package/src/cli/commands/hooks.ts +8 -9
  10. package/src/cli/commands/init.ts +98 -0
  11. package/src/cli/commands/serve.ts +7 -20
  12. package/src/cli/commands/update.ts +3 -3
  13. package/src/config/defaults.ts +4 -1
  14. package/src/config/schema.ts +27 -7
  15. package/src/hooks/brain-hook.ts +8 -6
  16. package/src/hooks/capture.ts +9 -2
  17. package/src/hooks/git-capture.ts +94 -0
  18. package/src/hooks/git-hook-installer.ts +197 -0
  19. package/src/hooks/index.ts +1 -0
  20. package/src/hooks/session-tracker.ts +79 -3
  21. package/src/hooks/types.ts +1 -1
  22. package/src/intelligence/index.ts +24 -0
  23. package/src/knowledge/graph/builder.ts +26 -0
  24. package/src/memory/chroma/store.ts +18 -2
  25. package/src/memory/episodic/manager.ts +17 -0
  26. package/src/memory/index.ts +48 -18
  27. package/src/phase12/index.ts +3 -454
  28. package/src/routing/intent-classifier.ts +107 -9
  29. package/src/routing/response-filter.ts +50 -17
  30. package/src/routing/router.ts +472 -224
  31. package/src/routing/search-engine.ts +464 -0
  32. package/src/routing/types.ts +84 -0
  33. package/src/server/handlers/call-tool.ts +4 -49
  34. package/src/server/handlers/tools/analyze-decision-evolution.ts +1 -1
  35. package/src/server/handlers/tools/detect-trends.ts +1 -1
  36. package/src/server/handlers/tools/find-cross-project-patterns.ts +1 -1
  37. package/src/server/handlers/tools/get-decision-timeline.ts +2 -2
  38. package/src/server/handlers/tools/get-recommendations.ts +1 -1
  39. package/src/server/handlers/tools/index.ts +5 -7
  40. package/src/server/handlers/tools/what-if-analysis.ts +1 -1
  41. package/src/server/providers/resources.ts +195 -0
  42. package/src/server/services.ts +81 -6
  43. package/src/tools/schemas.ts +7 -329
  44. package/src/utils/phase12-helper.ts +2 -2
  45. package/src/utils/timing.ts +47 -0
  46. package/src/vault/writer.ts +22 -2
  47. /package/src/{cross-project → intelligence/cross-project}/affinity.ts +0 -0
  48. /package/src/{cross-project → intelligence/cross-project}/generalizer.ts +0 -0
  49. /package/src/{cross-project → intelligence/cross-project}/index.ts +0 -0
  50. /package/src/{cross-project → intelligence/cross-project}/transfer.ts +0 -0
  51. /package/src/{optimization → intelligence/optimization}/index.ts +0 -0
  52. /package/src/{optimization → intelligence/optimization}/precompute.ts +0 -0
  53. /package/src/{optimization → intelligence/optimization}/semantic-cache.ts +0 -0
  54. /package/src/{prediction → intelligence/prediction}/context-anticipator.ts +0 -0
  55. /package/src/{prediction → intelligence/prediction}/decision-predictor.ts +0 -0
  56. /package/src/{prediction → intelligence/prediction}/index.ts +0 -0
  57. /package/src/{prediction → intelligence/prediction}/recommender.ts +0 -0
  58. /package/src/{reasoning → intelligence/reasoning}/chain-retrieval.ts +0 -0
  59. /package/src/{reasoning → intelligence/reasoning}/counterfactual.ts +0 -0
  60. /package/src/{reasoning → intelligence/reasoning}/index.ts +0 -0
  61. /package/src/{reasoning → intelligence/reasoning}/synthesizer.ts +0 -0
  62. /package/src/{temporal → intelligence/temporal}/evolution.ts +0 -0
  63. /package/src/{temporal → intelligence/temporal}/index.ts +0 -0
  64. /package/src/{temporal → intelligence/temporal}/query-processor.ts +0 -0
  65. /package/src/{temporal → intelligence/temporal}/timeline.ts +0 -0
  66. /package/src/{temporal → intelligence/temporal}/trends.ts +0 -0
@@ -9,6 +9,8 @@ import {
9
9
  getVaultService,
10
10
  getContextService,
11
11
  getMemoryService,
12
+ getEpisodeService,
13
+ getSessionTracker,
12
14
  isServicesInitialized
13
15
  } from '@/server/services'
14
16
  import { isPhase12Available, getPhase12Instance } from '@/utils/phase12-helper'
@@ -42,6 +44,12 @@ export class ResourceProvider {
42
44
  const detectedProject = await this.detectCurrentProject()
43
45
 
44
46
  const resources: ResourceItem[] = [
47
+ {
48
+ uri: 'brain://context/auto',
49
+ name: 'Auto Context',
50
+ description: 'Session context auto-loaded by brain',
51
+ mimeType: 'text/markdown'
52
+ },
45
53
  {
46
54
  uri: 'project://list',
47
55
  name: 'All Projects',
@@ -88,6 +96,10 @@ export class ResourceProvider {
88
96
  async readResource(uri: string) {
89
97
  this.logger.info({ uri }, 'Reading resource')
90
98
 
99
+ if (uri.startsWith('brain://')) {
100
+ return await this.getBrainResource(uri)
101
+ }
102
+
91
103
  if (uri === 'project://list') {
92
104
  return await this.getProjectListResource()
93
105
  }
@@ -372,6 +384,189 @@ export class ResourceProvider {
372
384
  }
373
385
  }
374
386
 
387
+ /**
388
+ * Handle brain:// resource URIs
389
+ */
390
+ private async getBrainResource(uri: string) {
391
+ if (uri === 'brain://context/auto') {
392
+ const content = await this.getAutoContextContent()
393
+ return {
394
+ contents: [
395
+ {
396
+ uri,
397
+ mimeType: 'text/markdown',
398
+ text: content
399
+ }
400
+ ]
401
+ }
402
+ }
403
+
404
+ throw new Error(`Unknown brain resource URI: ${uri}`)
405
+ }
406
+
407
+ /**
408
+ * Phase 21: Build aggregated auto-context payload
409
+ * Combines last session summary, recent decisions, corrections,
410
+ * cross-project knowledge, and current session info.
411
+ */
412
+ private async getAutoContextContent(): Promise<string> {
413
+ if (!isServicesInitialized()) {
414
+ return '# Auto Context\n\nServices not initialized.'
415
+ }
416
+
417
+ const sections: string[] = ['# Auto Context\n']
418
+ const detectedProject = await this.detectCurrentProject()
419
+ const projectName = detectedProject?.name
420
+
421
+ // 1. Last session summary
422
+ try {
423
+ const episodeService = getEpisodeService()
424
+ if (episodeService) {
425
+ const recentEpisodes = await episodeService.getRecentEpisodes(projectName, 1)
426
+ if (recentEpisodes.length > 0 && recentEpisodes[0].summary?.brief) {
427
+ const ep = recentEpisodes[0]
428
+ sections.push('## Last Session\n')
429
+ sections.push(ep.summary!.brief)
430
+ if (ep.summary!.key_topics.length > 0) {
431
+ sections.push(`\n**Topics:** ${ep.summary!.key_topics.join(', ')}`)
432
+ }
433
+ sections.push('')
434
+ }
435
+ }
436
+ } catch (err) {
437
+ this.logger.debug({ err }, 'Failed to get last session for auto-context')
438
+ }
439
+
440
+ // 2. Current session info
441
+ try {
442
+ const tracker = getSessionTracker()
443
+ if (tracker) {
444
+ const stats = tracker.getStats()
445
+ if (stats.activeSessions > 0) {
446
+ sections.push('## Current Session\n')
447
+ sections.push(`Active sessions: ${stats.activeSessions}, items tracked: ${stats.totalItems}`)
448
+ sections.push('')
449
+ }
450
+ }
451
+ } catch (err) {
452
+ this.logger.debug({ err }, 'Failed to get current session for auto-context')
453
+ }
454
+
455
+ // 3. Recent decisions
456
+ try {
457
+ const memory = getMemoryService()
458
+ const searchProject = projectName || 'general'
459
+ const decisions = await memory.searchRaw(searchProject, {
460
+ project: searchProject,
461
+ limit: 5,
462
+ minSimilarity: 0.1
463
+ })
464
+
465
+ if (decisions.length > 0) {
466
+ sections.push('## Recent Decisions\n')
467
+ for (const d of decisions) {
468
+ const title = d.decision || d.content || 'Untitled'
469
+ const truncated = title.length > 120 ? title.slice(0, 120) + '...' : title
470
+ sections.push(`- ${truncated}`)
471
+ }
472
+ sections.push('')
473
+ }
474
+ } catch (err) {
475
+ this.logger.debug({ err }, 'Failed to get decisions for auto-context')
476
+ }
477
+
478
+ // 4. Corrections (watch out for)
479
+ try {
480
+ const memory = getMemoryService()
481
+ if (typeof memory.getCorrections === 'function') {
482
+ const corrections = await memory.getCorrections(projectName || 'general', { limit: 3 })
483
+ if (corrections && corrections.length > 0) {
484
+ sections.push('## Watch Out For\n')
485
+ for (const c of corrections) {
486
+ const text = typeof c === 'string' ? c : (c.content || c.correction || JSON.stringify(c))
487
+ const truncated = text.length > 120 ? text.slice(0, 120) + '...' : text
488
+ sections.push(`- ${truncated}`)
489
+ }
490
+ sections.push('')
491
+ }
492
+ }
493
+ } catch (err) {
494
+ this.logger.debug({ err }, 'Failed to get corrections for auto-context')
495
+ }
496
+
497
+ // 5. Cross-project knowledge
498
+ if (projectName) {
499
+ try {
500
+ const vault = getVaultService()
501
+ const allProjects = await vault.reader.getProjectDirectories(vault.paths.projects)
502
+ const otherProjects = allProjects.filter(p => p !== projectName)
503
+
504
+ if (otherProjects.length > 0) {
505
+ const memory = getMemoryService()
506
+ if (memory.isChromaDBEnabled()) {
507
+ const { KnowledgeTransfer } = await import('@/intelligence/cross-project/transfer')
508
+ const transfer = new KnowledgeTransfer(
509
+ this.logger,
510
+ memory.chroma.collections,
511
+ memory.chroma.embeddings
512
+ )
513
+
514
+ const transferItems: string[] = []
515
+ // Check up to 3 other projects for transferable knowledge
516
+ for (const other of otherProjects.slice(0, 3)) {
517
+ try {
518
+ const result = await transfer.findTransferable(other, projectName, { limit: 2, minRelevance: 0.5 })
519
+ for (const item of result.items) {
520
+ const truncated = item.content.length > 100 ? item.content.slice(0, 100) + '...' : item.content
521
+ transferItems.push(`- [${item.type} from ${other}] ${truncated}`)
522
+ }
523
+ } catch {
524
+ // Skip failed transfers
525
+ }
526
+ }
527
+
528
+ if (transferItems.length > 0) {
529
+ sections.push('## From Other Projects\n')
530
+ sections.push(...transferItems)
531
+ sections.push('')
532
+ }
533
+ }
534
+ }
535
+ } catch (err) {
536
+ this.logger.debug({ err }, 'Failed to get cross-project knowledge for auto-context')
537
+ }
538
+ }
539
+
540
+ // If no sections beyond the header, try auto-scan for first-session value
541
+ if (sections.length <= 1) {
542
+ try {
543
+ const cwd = process.cwd()
544
+ const { scanRepo } = await import('@/automation/repo-scanner')
545
+ const repoContext = await scanRepo(cwd)
546
+ if (repoContext.name) {
547
+ sections.push('## Project Detected\n')
548
+ sections.push(`**Name:** ${repoContext.name}`)
549
+ if (repoContext.techStack.length > 0) {
550
+ sections.push(`**Tech stack:** ${repoContext.techStack.join(', ')}`)
551
+ }
552
+ if (repoContext.description) {
553
+ const desc = repoContext.description.split('\n')[0]?.slice(0, 200) || ''
554
+ sections.push(`**Description:** ${desc}`)
555
+ }
556
+ sections.push('')
557
+ }
558
+ } catch {
559
+ // Scan failed, continue with empty context
560
+ }
561
+ }
562
+
563
+ if (sections.length <= 1) {
564
+ return '# Auto Context\n\nNo context available yet. Use brain to store decisions, patterns, and corrections.'
565
+ }
566
+
567
+ return sections.join('\n')
568
+ }
569
+
375
570
  /**
376
571
  * Detect current project from working directory
377
572
  */
@@ -9,16 +9,18 @@ import * as path from 'path'
9
9
  import { MemoryManager } from '@/memory'
10
10
  import { VaultManager } from '@/vault'
11
11
  import { ContextManager } from '@/context'
12
- import { Phase12Manager } from '@/phase12'
12
+ import { Phase12Manager } from '@/automation/phase12-manager'
13
13
  import { RetrievalService } from '@/retrieval/service'
14
+ import { RetrievalPipeline } from '@/retrieval/pipeline'
14
15
  import { InMemoryKnowledgeGraph } from '@/knowledge/graph/memory-graph'
15
16
  import { GraphSearchEngine } from '@/knowledge/graph/search'
16
17
  import { KnowledgeGraphBuilder } from '@/knowledge/graph/builder'
17
18
  import { CrossReferenceLinker } from '@/knowledge/graph/linker'
18
19
  import { EpisodeManager } from '@/memory/episodic/manager'
20
+ import { HookSessionTracker } from '@/hooks/session-tracker'
19
21
  import type { Config } from '@/config'
20
- import { SemanticCache } from '@/optimization/semantic-cache'
21
- import { PrecomputeEngine } from '@/optimization/precompute'
22
+ import { SemanticCache } from '@/intelligence/optimization/semantic-cache'
23
+ import { PrecomputeEngine } from '@/intelligence/optimization/precompute'
22
24
 
23
25
  export interface KnowledgeGraphServiceContainer {
24
26
  graph: InMemoryKnowledgeGraph
@@ -36,8 +38,10 @@ export interface Services {
36
38
  context: ContextManager
37
39
  phase12: Phase12Manager
38
40
  retrieval: RetrievalService | null
41
+ retrievalPipeline: RetrievalPipeline | null
39
42
  knowledgeGraph: KnowledgeGraphServiceContainer | null
40
43
  episodeManager: EpisodeManager | null
44
+ sessionTracker: HookSessionTracker | null
41
45
  semanticCache: SemanticCache | null
42
46
  precompute: PrecomputeEngine | null
43
47
  logger: Logger
@@ -127,6 +131,23 @@ export async function initializeServices(config: Config, logger: Logger): Promis
127
131
  serviceLogger.warn('Retrieval service requires ChromaDB, skipping initialization')
128
132
  }
129
133
 
134
+ // Initialize Retrieval Pipeline (Phase 19) — hybrid search with BM25 + semantic + fusion
135
+ let retrievalPipeline: RetrievalPipeline | null = null
136
+ if (config.retrieval?.enabled && memory.isChromaDBEnabled()) {
137
+ try {
138
+ retrievalPipeline = new RetrievalPipeline(
139
+ logger,
140
+ memory.chroma.collections,
141
+ memory.chroma.embeddings,
142
+ config.retrieval
143
+ )
144
+ await retrievalPipeline.initialize()
145
+ serviceLogger.info('Retrieval pipeline initialized (hybrid search)')
146
+ } catch (error) {
147
+ serviceLogger.warn({ error }, 'Failed to initialize retrieval pipeline, continuing without hybrid search')
148
+ }
149
+ }
150
+
130
151
  // Initialize Knowledge Graph Service (Phase 14)
131
152
  let knowledgeGraph: KnowledgeGraphServiceContainer | null = null
132
153
  if (config.knowledge?.graph?.enabled !== false) {
@@ -166,6 +187,21 @@ export async function initializeServices(config: Config, logger: Logger): Promis
166
187
  builder.processDecision(input)
167
188
  })
168
189
 
190
+ // Hook builder into decision deletion for graph synchronization
191
+ memory.addDecisionDeletedListener((id) => {
192
+ builder.removeDecision(id)
193
+
194
+ // Phase 20: Also unlink from active episodes
195
+ if (episodeManager) {
196
+ episodeManager.unlinkDecision(id)
197
+ }
198
+
199
+ // Phase 20: Invalidate semantic cache for deleted decisions
200
+ if (semanticCache) {
201
+ semanticCache.clear()
202
+ }
203
+ })
204
+
169
205
  serviceLogger.info(
170
206
  { nodes: graph.getNodeCount(), edges: graph.getEdgeCount() },
171
207
  'Knowledge graph service initialized'
@@ -191,6 +227,17 @@ export async function initializeServices(config: Config, logger: Logger): Promis
191
227
  }
192
228
  }
193
229
 
230
+ // Initialize Session Tracker (Phase 21) — promoted from serve.ts
231
+ let sessionTracker: HookSessionTracker | null = null
232
+ if (config.hooks?.enabled !== false) {
233
+ try {
234
+ sessionTracker = new HookSessionTracker(logger, episodeManager, config.hooks?.sessions, memory)
235
+ serviceLogger.info('Session tracker initialized')
236
+ } catch (error) {
237
+ serviceLogger.warn({ error }, 'Failed to initialize session tracker, continuing without it')
238
+ }
239
+ }
240
+
194
241
  // Initialize Semantic Cache & Precompute (Phase 15) — requires ChromaDB
195
242
  let semanticCache: SemanticCache | null = null
196
243
  let precompute: PrecomputeEngine | null = null
@@ -198,9 +245,9 @@ export async function initializeServices(config: Config, logger: Logger): Promis
198
245
  try {
199
246
  const cacheConfig = config.advancedIntelligence?.cache || {}
200
247
  semanticCache = new SemanticCache(logger, {
201
- maxSize: cacheConfig.maxSize || 500,
202
- ttlMs: cacheConfig.ttlMs || 30 * 60 * 1000,
203
- similarityThreshold: cacheConfig.similarityThreshold || 0.85,
248
+ maxSize: cacheConfig.maxSize || 1000,
249
+ ttlMs: cacheConfig.ttlMs || 60 * 60 * 1000,
250
+ similarityThreshold: cacheConfig.similarityThreshold || 0.80,
204
251
  embeddings: memory.chroma.embeddings
205
252
  })
206
253
 
@@ -221,8 +268,10 @@ export async function initializeServices(config: Config, logger: Logger): Promis
221
268
  context,
222
269
  phase12,
223
270
  retrieval,
271
+ retrievalPipeline,
224
272
  knowledgeGraph,
225
273
  episodeManager,
274
+ sessionTracker,
226
275
  semanticCache,
227
276
  precompute,
228
277
  logger,
@@ -283,6 +332,14 @@ export function getRetrievalService(): RetrievalService | null {
283
332
  return getServices().retrieval
284
333
  }
285
334
 
335
+ /**
336
+ * Get Retrieval Pipeline (Phase 19)
337
+ * Returns null if hybrid search is not enabled
338
+ */
339
+ export function getRetrievalPipeline(): RetrievalPipeline | null {
340
+ return getServices().retrievalPipeline
341
+ }
342
+
286
343
  /**
287
344
  * Get Knowledge Graph service
288
345
  * Returns null if knowledge graph is not enabled
@@ -299,6 +356,14 @@ export function getEpisodeService(): EpisodeManager | null {
299
356
  return getServices().episodeManager
300
357
  }
301
358
 
359
+ /**
360
+ * Get Session Tracker service
361
+ * Returns null if hooks are not enabled
362
+ */
363
+ export function getSessionTracker(): HookSessionTracker | null {
364
+ return getServices().sessionTracker
365
+ }
366
+
302
367
  /**
303
368
  * Get Semantic Cache service
304
369
  * Returns null if caching is not enabled
@@ -352,6 +417,16 @@ export async function shutdownServices(): Promise<void> {
352
417
  const serviceLogger = services.logger.child({ component: 'services' })
353
418
  serviceLogger.info('Shutting down services...')
354
419
 
420
+ // End all active sessions
421
+ if (services.sessionTracker) {
422
+ try {
423
+ await services.sessionTracker.endAllSessions()
424
+ serviceLogger.info('Session tracker shut down')
425
+ } catch (error) {
426
+ serviceLogger.error({ error }, 'Failed to end sessions on shutdown')
427
+ }
428
+ }
429
+
355
430
  // Save and stop knowledge graph
356
431
  if (services.knowledgeGraph) {
357
432
  services.knowledgeGraph.graph.stopAutoSave()