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 +1 -1
- package/package.json +1 -1
- package/src/cli/commands/serve.ts +11 -0
- package/src/config/defaults.ts +1 -1
- package/src/config/schema.ts +1 -1
- package/src/routing/router.ts +24 -20
- package/src/routing/search-engine.ts +14 -3
- package/src/server/http-api.ts +10 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.13.1
|
package/package.json
CHANGED
|
@@ -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
|
}
|
package/src/config/defaults.ts
CHANGED
|
@@ -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.
|
|
6
|
+
serverVersion: '0.13.1',
|
|
7
7
|
logLevel: 'info',
|
|
8
8
|
logFilePath: './logs/claude-brain.log',
|
|
9
9
|
dbPath: './data/memory.db',
|
package/src/config/schema.ts
CHANGED
|
@@ -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.
|
|
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'),
|
package/src/routing/router.ts
CHANGED
|
@@ -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,
|
|
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 ||
|
|
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,
|
|
930
|
+
this.registerEpisodeMessage(message, effectiveProject, 'question')
|
|
929
931
|
|
|
930
|
-
return this.responseFilter.synthesize(tiers, message,
|
|
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,
|
|
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,
|
|
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) {
|
package/src/server/http-api.ts
CHANGED
|
@@ -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 {
|