claude-brain 0.13.0 → 0.13.1

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 CHANGED
@@ -1 +1 @@
1
- 0.12.0
1
+ 0.13.1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-brain",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "Local development assistant bridging Obsidian vaults with Claude Code via MCP",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -18,6 +18,17 @@ export async function runServe() {
18
18
  // Auto-initialize home directory on first run
19
19
  ensureHomeDirectory()
20
20
 
21
+ // Auto-install Claude Code hooks (idempotent, non-fatal)
22
+ try {
23
+ const { installHooks } = await import('@/hooks/installer')
24
+ const hookResult = installHooks()
25
+ if (hookResult.message !== 'Hooks already installed') {
26
+ console.error(`[claude-brain] ${hookResult.message}`)
27
+ }
28
+ } catch (error) {
29
+ console.error(`[claude-brain] Hook auto-install skipped: ${error instanceof Error ? error.message : 'unknown error'}`)
30
+ }
31
+
21
32
  if (process.env.NODE_ENV !== 'production') {
22
33
  console.error(BANNER)
23
34
  }
@@ -3,7 +3,7 @@ import type { PartialConfig } from './schema'
3
3
  /** Default configuration values for Claude Brain */
4
4
  export const defaultConfig: PartialConfig = {
5
5
  serverName: 'claude-brain',
6
- serverVersion: '0.13.0',
6
+ serverVersion: '0.13.1',
7
7
  logLevel: 'info',
8
8
  logFilePath: './logs/claude-brain.log',
9
9
  dbPath: './data/memory.db',
@@ -284,7 +284,7 @@ export const ConfigSchema = z.object({
284
284
  serverName: z.string().default('claude-brain'),
285
285
 
286
286
  /** Server version in semver format */
287
- serverVersion: z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be semver format').default('0.13.0'),
287
+ serverVersion: z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be semver format').default('0.13.1'),
288
288
 
289
289
  /** Logging level */
290
290
  logLevel: LogLevelSchema.default('info'),
@@ -251,6 +251,7 @@ export class BrainRouter {
251
251
  return this.servicesNotReady()
252
252
  }
253
253
 
254
+ const effectiveProject = project || DEFAULT_PROJECT
254
255
  const query = entities.topic || message
255
256
  const tiers: TierResults[] = []
256
257
  const hasTemporal = classification?.secondary.includes('exploration') ||
@@ -258,7 +259,7 @@ export class BrainRouter {
258
259
 
259
260
  // Use temporal search if temporal signals detected
260
261
  if (hasTemporal) {
261
- const { results } = await this.searchEngine.temporalSearch(query, { project, limit: 5 })
262
+ const { results } = await this.searchEngine.temporalSearch(query, { project: effectiveProject, limit: 5 })
262
263
  if (results.length > 0) {
263
264
  tiers.push({
264
265
  label: 'Memories',
@@ -273,7 +274,7 @@ export class BrainRouter {
273
274
  } else {
274
275
  // Standard enhanced search
275
276
  const searchResults = await this.searchEngine.enhancedSearch(query, {
276
- project,
277
+ project: effectiveProject,
277
278
  limit: 5,
278
279
  minSimilarity: 0.3
279
280
  })
@@ -291,7 +292,7 @@ export class BrainRouter {
291
292
  }
292
293
 
293
294
  // Also search patterns
294
- const patternResults = await this.searchEngine.searchPatterns(query, { project, limit: 3 })
295
+ const patternResults = await this.searchEngine.searchPatterns(query, { project: effectiveProject, limit: 3 })
295
296
  if (patternResults.length > 0) {
296
297
  tiers.push({
297
298
  label: 'Patterns',
@@ -304,7 +305,7 @@ export class BrainRouter {
304
305
  })
305
306
  }
306
307
 
307
- return this.responseFilter.synthesize(tiers, message, project)
308
+ return this.responseFilter.synthesize(tiers, message, effectiveProject)
308
309
  }
309
310
 
310
311
  /**
@@ -811,6 +812,7 @@ export class BrainRouter {
811
812
  return this.servicesNotReady()
812
813
  }
813
814
 
815
+ const effectiveProject = project || DEFAULT_PROJECT
814
816
  const query = entities.topic || message
815
817
  const tiers: TierResults[] = []
816
818
  const hasTemporal = classification.secondary.includes('exploration') ||
@@ -822,26 +824,26 @@ export class BrainRouter {
822
824
  // Main search — temporal or standard
823
825
  let searchResults: NormalizedResult[]
824
826
  if (hasTemporal) {
825
- const { results } = await this.searchEngine.temporalSearch(query, { project, limit: 5 })
827
+ const { results } = await this.searchEngine.temporalSearch(query, { project: effectiveProject, limit: 5 })
826
828
  searchResults = results
827
829
  } else if (isComplex) {
828
830
  // C7: Try multi-hop chain retrieval
829
- const chainResult = await this.searchEngine.chainSearch(query, { project })
831
+ const chainResult = await this.searchEngine.chainSearch(query, { project: effectiveProject })
830
832
  if (chainResult?.allResults?.length) {
831
833
  searchResults = chainResult.allResults.map((r: any) => ({
832
834
  id: r.id || '',
833
835
  content: r.content || '',
834
836
  score: r.similarity || 0,
835
837
  source: 'decision' as const,
836
- project: r.metadata?.project || project || '',
838
+ project: r.metadata?.project || effectiveProject || '',
837
839
  date: r.metadata?.created_at || '',
838
840
  metadata: r.metadata || {}
839
841
  }))
840
842
  } else {
841
- searchResults = await this.searchEngine.enhancedSearch(query, { project, limit: 5 })
843
+ searchResults = await this.searchEngine.enhancedSearch(query, { project: effectiveProject, limit: 5 })
842
844
  }
843
845
  } else {
844
- searchResults = await this.searchEngine.enhancedSearch(query, { project, limit: 5 })
846
+ searchResults = await this.searchEngine.enhancedSearch(query, { project: effectiveProject, limit: 5 })
845
847
  }
846
848
 
847
849
  if (searchResults.length > 0) {
@@ -858,8 +860,8 @@ export class BrainRouter {
858
860
 
859
861
  // Parallel: patterns + corrections + graph
860
862
  const [patternResults, correctionResults, graphResults] = await Promise.all([
861
- this.searchEngine.searchPatterns(query, { project, limit: 3 }),
862
- this.searchEngine.searchCorrections(query, { project, limit: 3 }),
863
+ this.searchEngine.searchPatterns(query, { project: effectiveProject, limit: 3 }),
864
+ this.searchEngine.searchCorrections(query, { project: effectiveProject, limit: 3 }),
863
865
  // C11: KnowledgeGraph enrichment for all questions
864
866
  this.searchEngine.searchGraph(query, 5)
865
867
  ])
@@ -925,9 +927,9 @@ export class BrainRouter {
925
927
  }
926
928
 
927
929
  // Register with episode
928
- this.registerEpisodeMessage(message, project, 'question')
930
+ this.registerEpisodeMessage(message, effectiveProject, 'question')
929
931
 
930
- return this.responseFilter.synthesize(tiers, message, project)
932
+ return this.responseFilter.synthesize(tiers, message, effectiveProject)
931
933
  }
932
934
 
933
935
  /**
@@ -945,6 +947,7 @@ export class BrainRouter {
945
947
  return this.servicesNotReady()
946
948
  }
947
949
 
950
+ const effectiveProject = project || DEFAULT_PROJECT
948
951
  const query = entities.topic || message
949
952
  const lower = message.toLowerCase()
950
953
  const tiers: TierResults[] = []
@@ -952,7 +955,7 @@ export class BrainRouter {
952
955
  // C6: Timeline queries
953
956
  if (lower.includes('timeline') || lower.includes('chronological') || lower.includes('history of')) {
954
957
  const timeline = await this.searchEngine.buildTimeline({
955
- project,
958
+ project: effectiveProject,
956
959
  topic: query,
957
960
  limit: 20
958
961
  })
@@ -972,7 +975,7 @@ export class BrainRouter {
972
975
 
973
976
  // C6: Evolution queries
974
977
  if (lower.includes('evolution') || lower.includes('how has') || lower.includes('changed') || lower.includes('evolved')) {
975
- const evolution = await this.searchEngine.analyzeEvolution(query, { project })
978
+ const evolution = await this.searchEngine.analyzeEvolution(query, { project: effectiveProject })
976
979
  if (evolution?.timeline?.length) {
977
980
  const parts = []
978
981
  parts.push(`**Stability:** ${evolution.stability || 'unknown'}`)
@@ -995,7 +998,7 @@ export class BrainRouter {
995
998
 
996
999
  // C6: Trend queries
997
1000
  if (lower.includes('trend') || lower.includes('emerging') || lower.includes('declining')) {
998
- const trends = await this.searchEngine.detectTrends({ project })
1001
+ const trends = await this.searchEngine.detectTrends({ project: effectiveProject })
999
1002
  if (trends?.topTrends?.length) {
1000
1003
  tiers.push({
1001
1004
  label: 'Trends',
@@ -1048,7 +1051,7 @@ export class BrainRouter {
1048
1051
 
1049
1052
  // Also do a basic memory search as fallback
1050
1053
  const searchResults = await this.searchEngine.enhancedSearch(query, {
1051
- project,
1054
+ project: effectiveProject,
1052
1055
  limit: 5,
1053
1056
  minSimilarity: 0.2
1054
1057
  })
@@ -1064,7 +1067,7 @@ export class BrainRouter {
1064
1067
  })
1065
1068
  }
1066
1069
 
1067
- return this.responseFilter.synthesize(tiers, message, project, 'analyzed')
1070
+ return this.responseFilter.synthesize(tiers, message, effectiveProject, 'analyzed')
1068
1071
  }
1069
1072
 
1070
1073
  private async handleComparison(
@@ -1076,12 +1079,13 @@ export class BrainRouter {
1076
1079
  return this.servicesNotReady()
1077
1080
  }
1078
1081
 
1082
+ const effectiveProject = project || DEFAULT_PROJECT
1079
1083
  const query = entities.topic || message
1080
1084
  const tiers: TierResults[] = []
1081
1085
 
1082
1086
  // Search for related decisions
1083
1087
  const searchResults = await this.searchEngine.enhancedSearch(query, {
1084
- project,
1088
+ project: effectiveProject,
1085
1089
  limit: 5,
1086
1090
  minSimilarity: 0.2
1087
1091
  })
@@ -1111,7 +1115,7 @@ export class BrainRouter {
1111
1115
  })
1112
1116
  }
1113
1117
 
1114
- return this.responseFilter.synthesize(tiers, message, project, 'analyzed')
1118
+ return this.responseFilter.synthesize(tiers, message, effectiveProject, 'analyzed')
1115
1119
  }
1116
1120
 
1117
1121
  // ===== Helpers =====
@@ -39,6 +39,17 @@ export class SearchEngine {
39
39
  this.logger = logger.child({ component: 'search-engine' })
40
40
  }
41
41
 
42
+ /**
43
+ * Sort results so same-project entries come first (safety net for cross-project noise).
44
+ */
45
+ private prioritizeProject(results: NormalizedResult[], project?: string): NormalizedResult[] {
46
+ if (!project || results.length === 0) return results
47
+ const sameProject = results.filter(r => r.project === project)
48
+ const otherProject = results.filter(r => r.project !== project)
49
+ if (sameProject.length > 0) return [...sameProject, ...otherProject]
50
+ return results
51
+ }
52
+
42
53
  /**
43
54
  * Enhanced search — uses hybrid retrieval pipeline when available,
44
55
  * falls back to plain searchRaw().
@@ -88,7 +99,7 @@ export class SearchEngine {
88
99
  )
89
100
 
90
101
  if (hybridResults.length > 0) {
91
- const normalized = hybridResults.map((r: any) => ({
102
+ const normalized = this.prioritizeProject(hybridResults.map((r: any) => ({
92
103
  id: r.id || '',
93
104
  content: r.content || r.document || '',
94
105
  score: r.score || r.similarity || 0,
@@ -96,7 +107,7 @@ export class SearchEngine {
96
107
  project: r.metadata?.project || options?.project || '',
97
108
  date: r.metadata?.created_at || '',
98
109
  metadata: r.metadata || {}
99
- }))
110
+ })), options?.project)
100
111
 
101
112
  // Cache results
102
113
  if (cache) {
@@ -135,7 +146,7 @@ export class SearchEngine {
135
146
  SEARCH_TIMEOUT,
136
147
  []
137
148
  )
138
- const normalized = normalizeSearchResults(rawResults)
149
+ const normalized = this.prioritizeProject(normalizeSearchResults(rawResults), options?.project)
139
150
 
140
151
  // Cache results
141
152
  if (cache && cacheKey) {
@@ -609,6 +609,16 @@ export class HttpApiServer {
609
609
  break
610
610
 
611
611
  case 'progress': {
612
+ // Store in ChromaDB so it's searchable via brain tool
613
+ await memoryService.rememberDecision(
614
+ project,
615
+ `Captured via hook: ${knowledge.metadata.action || knowledge.metadata.source || 'progress'}`,
616
+ knowledge.content,
617
+ `Auto-captured from ${knowledge.source}`,
618
+ { tags: knowledge.technologies }
619
+ )
620
+
621
+ // Also update vault progress if project exists
612
622
  const vaultService = getVaultService()
613
623
  if (vaultService) {
614
624
  try {