claude-brain 0.30.2 → 0.30.3
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 +241 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +29 -29
- package/package.json +7 -3
- 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 +531 -531
- package/src/automation/decision-detector.ts +452 -452
- 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 +210 -205
- package/src/cli/auto-setup.ts +75 -75
- package/src/cli/auto-start.ts +266 -266
- package/src/cli/bin.ts +264 -264
- package/src/cli/commands/autostart.ts +90 -90
- package/src/cli/commands/chroma.ts +578 -577
- package/src/cli/commands/export-training.ts +70 -70
- package/src/cli/commands/export.ts +130 -130
- package/src/cli/commands/git-hook.ts +183 -183
- package/src/cli/commands/hooks.ts +217 -217
- package/src/cli/commands/init.ts +123 -123
- package/src/cli/commands/install-mcp.ts +122 -111
- package/src/cli/commands/models.ts +979 -979
- package/src/cli/commands/pack.ts +200 -200
- package/src/cli/commands/refresh.ts +344 -339
- package/src/cli/commands/reindex.ts +120 -120
- package/src/cli/commands/serve.ts +466 -463
- package/src/cli/commands/start.ts +44 -44
- package/src/cli/commands/status.ts +220 -203
- package/src/cli/commands/uninstall-mcp.ts +45 -41
- package/src/cli/commands/update.ts +130 -124
- package/src/cli/migrate-chroma.ts +106 -106
- 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/code-intelligence/indexer.ts +352 -352
- package/src/code-intelligence/linker.ts +178 -178
- package/src/code-intelligence/parser.ts +484 -484
- package/src/code-intelligence/query.ts +291 -291
- package/src/code-intelligence/schema.ts +83 -83
- package/src/code-intelligence/types.ts +95 -95
- package/src/config/defaults.ts +52 -52
- package/src/config/home.ts +56 -56
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +192 -192
- package/src/config/schema.ts +446 -415
- package/src/config/validator.ts +182 -182
- package/src/context/assembler.ts +407 -400
- package/src/context/index.ts +79 -79
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +122 -121
- package/src/health/index.ts +233 -232
- package/src/hooks/brain-hook.ts +134 -131
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/claude-code-mastery.md +112 -112
- package/src/hooks/context-hook.ts +260 -245
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +211 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +306 -288
- package/src/hooks/interceptor-hook.ts +204 -201
- package/src/hooks/passive-classifier.ts +397 -397
- package/src/hooks/queue.ts +160 -129
- package/src/hooks/session-tracker.ts +312 -312
- package/src/hooks/types.ts +52 -52
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +7 -7
- package/src/intelligence/hf-downloader.ts +222 -222
- package/src/intelligence/hf-manifest.json +78 -78
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/inference-router.ts +762 -762
- package/src/intelligence/model-manager.ts +263 -245
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +213 -207
- package/src/intelligence/prediction/index.ts +7 -7
- package/src/intelligence/prediction/recommender.ts +276 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +243 -247
- package/src/intelligence/reasoning/index.ts +7 -7
- package/src/intelligence/temporal/evolution.ts +193 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +272 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/intelligence/tokenizer.ts +118 -118
- package/src/knowledge/entity-extractor.ts +447 -443
- 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 +166 -166
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +211 -192
- package/src/memory/chroma/collection-manager.ts +92 -92
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +177 -175
- 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 +319 -315
- package/src/memory/chroma/store.ts +755 -747
- package/src/memory/compression.ts +121 -121
- package/src/memory/consolidation/archiver.ts +162 -165
- package/src/memory/consolidation/merger.ts +182 -186
- package/src/memory/consolidation/scorer.ts +136 -136
- package/src/memory/database.ts +9 -0
- package/src/memory/dual-write.ts +145 -0
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +347 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/fts5-search.ts +692 -633
- package/src/memory/index.ts +943 -1060
- package/src/memory/migrations/add-fts5.ts +118 -108
- package/src/memory/patterns.ts +438 -438
- package/src/memory/pruning.ts +60 -60
- package/src/memory/schema.ts +88 -88
- package/src/memory/store.ts +911 -787
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- 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 -297
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +221 -221
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +221 -221
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +165 -165
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +203 -203
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +252 -252
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +189 -188
- package/src/retrieval/reranker/model.ts +99 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +454 -454
- package/src/routing/handlers/exploration-handler.ts +369 -0
- package/src/routing/handlers/index.ts +19 -0
- package/src/routing/handlers/memory-handler.ts +273 -0
- package/src/routing/handlers/mutation-handler.ts +241 -0
- package/src/routing/handlers/recall-handler.ts +642 -0
- package/src/routing/handlers/shared.ts +515 -0
- package/src/routing/handlers/types.ts +48 -0
- package/src/routing/intent-classifier.ts +552 -552
- package/src/routing/response-filter.ts +399 -391
- package/src/routing/router.ts +245 -2193
- package/src/routing/search-engine.ts +521 -514
- package/src/routing/types.ts +104 -94
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/auto-updater.ts +283 -276
- package/src/server/handlers/call-tool.ts +159 -159
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/auto-remember.ts +165 -165
- package/src/server/handlers/tools/brain.ts +86 -86
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/get-code-standards.ts +123 -123
- package/src/server/handlers/tools/get-corrections.ts +152 -152
- package/src/server/handlers/tools/get-patterns.ts +156 -156
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/index.ts +30 -30
- package/src/server/handlers/tools/init-project.ts +756 -756
- package/src/server/handlers/tools/list-projects.ts +126 -126
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +132 -132
- package/src/server/handlers/tools/record-correction.ts +131 -131
- package/src/server/handlers/tools/remember-decision.ts +168 -168
- package/src/server/handlers/tools/schemas.ts +179 -179
- package/src/server/handlers/tools/search-code.ts +122 -122
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/http-api.ts +215 -1229
- package/src/server/mcp-proxy.ts +85 -84
- package/src/server/mcp-server.ts +285 -284
- package/src/server/middleware/auth.ts +39 -0
- package/src/server/middleware/error-handler.ts +37 -0
- package/src/server/middleware/rate-limit.ts +53 -0
- package/src/server/middleware/validate.ts +42 -0
- package/src/server/pid-manager.ts +137 -136
- package/src/server/providers/resources.ts +581 -581
- package/src/server/routes/code.ts +228 -0
- package/src/server/routes/context.ts +26 -0
- package/src/server/routes/health.ts +19 -0
- package/src/server/routes/helpers.ts +100 -0
- package/src/server/routes/hooks.ts +197 -0
- package/src/server/routes/mcp.ts +47 -0
- package/src/server/routes/memory.ts +397 -0
- package/src/server/routes/models.ts +96 -0
- package/src/server/routes/projects.ts +89 -0
- package/src/server/routes/types.ts +21 -0
- package/src/server/schemas/api-schemas.ts +202 -0
- package/src/server/services.ts +720 -720
- package/src/server/utils/memory-indicator.ts +84 -84
- package/src/server/utils/response-formatter.ts +129 -129
- package/src/server/web-viewer.ts +1145 -1115
- package/src/setup/index.ts +38 -38
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.ts +666 -666
- package/src/tools/types.ts +412 -412
- package/src/training/data-store.ts +320 -298
- package/src/training/retrain-pipeline.ts +399 -394
- package/src/utils/error-handler.ts +136 -136
- package/src/utils/index.ts +58 -58
- package/src/utils/kill-port.ts +55 -53
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/safe-path.ts +43 -0
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/index.ts +4 -3
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +4 -1
- package/src/vault/reader.ts +44 -1
- package/src/vault/watcher.ts +24 -1
- package/src/vault/writer.ts +487 -413
- package/skills/persistent-memory/SKILL.md +0 -148
- package/skills/persistent-memory/references/tool-reference.md +0 -90
package/src/config/loader.ts
CHANGED
|
@@ -1,192 +1,192 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
|
-
import { ConfigSchema, type Config } from './schema'
|
|
4
|
-
import { defaultConfig } from './defaults'
|
|
5
|
-
import { getClaudeBrainHome, resolveHomePath } from './home'
|
|
6
|
-
|
|
7
|
-
/** Parse a .env file without dotenv (avoids stdout pollution from dotenv v17) */
|
|
8
|
-
function loadEnvFile(filePath: string): void {
|
|
9
|
-
if (!existsSync(filePath)) return
|
|
10
|
-
const content = readFileSync(filePath, 'utf-8')
|
|
11
|
-
for (const line of content.split('\n')) {
|
|
12
|
-
const trimmed = line.trim()
|
|
13
|
-
if (!trimmed || trimmed.startsWith('#')) continue
|
|
14
|
-
const eqIdx = trimmed.indexOf('=')
|
|
15
|
-
if (eqIdx === -1) continue
|
|
16
|
-
const key = trimmed.slice(0, eqIdx).trim()
|
|
17
|
-
const val = trimmed.slice(eqIdx + 1).trim()
|
|
18
|
-
// Don't override existing env vars
|
|
19
|
-
if (!(key in process.env)) {
|
|
20
|
-
process.env[key] = val
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const CONFIG_FILE_NAME = '.claudebrainrc.json'
|
|
26
|
-
|
|
27
|
-
/** Load configuration from a JSON file if it exists */
|
|
28
|
-
function loadFromFile(basePath: string): Partial<Config> {
|
|
29
|
-
// Check basePath first, then Claude Brain home
|
|
30
|
-
for (const dir of [basePath, getClaudeBrainHome()]) {
|
|
31
|
-
const configPath = resolve(dir, CONFIG_FILE_NAME)
|
|
32
|
-
if (!existsSync(configPath)) {
|
|
33
|
-
continue
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
const content = readFileSync(configPath, 'utf-8')
|
|
38
|
-
return JSON.parse(content) as Partial<Config>
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.warn(`Warning: Failed to parse config file at ${configPath}:`, error)
|
|
41
|
-
return {}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return {}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Load configuration from environment variables */
|
|
49
|
-
function loadFromEnv(): Partial<Config> {
|
|
50
|
-
// Skip .env loading if SKIP_DOTENV is set (for MCP servers where env is provided externally)
|
|
51
|
-
if (!process.env.SKIP_DOTENV) {
|
|
52
|
-
// Use custom parser instead of dotenv v17 (which prints to stdout and breaks MCP stdio)
|
|
53
|
-
loadEnvFile(resolve(getClaudeBrainHome(), '.env'))
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const config: Partial<Config> = {}
|
|
57
|
-
|
|
58
|
-
if (process.env.VAULT_PATH) {
|
|
59
|
-
config.vaultPath = process.env.VAULT_PATH
|
|
60
|
-
}
|
|
61
|
-
if (process.env.MCP_SERVER_NAME) {
|
|
62
|
-
config.serverName = process.env.MCP_SERVER_NAME
|
|
63
|
-
}
|
|
64
|
-
if (process.env.MCP_SERVER_VERSION) {
|
|
65
|
-
config.serverVersion = process.env.MCP_SERVER_VERSION
|
|
66
|
-
}
|
|
67
|
-
if (process.env.LOG_LEVEL) {
|
|
68
|
-
config.logLevel = process.env.LOG_LEVEL as Config['logLevel']
|
|
69
|
-
}
|
|
70
|
-
if (process.env.LOG_FILE_PATH) {
|
|
71
|
-
config.logFilePath = process.env.LOG_FILE_PATH
|
|
72
|
-
}
|
|
73
|
-
if (process.env.DB_PATH) {
|
|
74
|
-
config.dbPath = process.env.DB_PATH
|
|
75
|
-
}
|
|
76
|
-
if (process.env.PORT) {
|
|
77
|
-
config.port = parseInt(process.env.PORT, 10)
|
|
78
|
-
}
|
|
79
|
-
if (process.env.ENABLE_FILE_WATCH) {
|
|
80
|
-
config.enableFileWatch = process.env.ENABLE_FILE_WATCH === 'true'
|
|
81
|
-
}
|
|
82
|
-
if (process.env.CACHE_SIZE) {
|
|
83
|
-
config.cacheSize = parseInt(process.env.CACHE_SIZE, 10)
|
|
84
|
-
}
|
|
85
|
-
if (process.env.NODE_ENV) {
|
|
86
|
-
config.nodeEnv = process.env.NODE_ENV as Config['nodeEnv']
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Retrieval configuration
|
|
90
|
-
const retrieval = (config as Record<string, any>)
|
|
91
|
-
if (process.env.RETRIEVAL_ENABLED) {
|
|
92
|
-
retrieval.retrieval = retrieval.retrieval || {}
|
|
93
|
-
retrieval.retrieval.enabled = process.env.RETRIEVAL_ENABLED === 'true'
|
|
94
|
-
}
|
|
95
|
-
if (process.env.RETRIEVAL_DENSE_WEIGHT) {
|
|
96
|
-
retrieval.retrieval = retrieval.retrieval || {}
|
|
97
|
-
retrieval.retrieval.dense = retrieval.retrieval.dense || {}
|
|
98
|
-
retrieval.retrieval.dense.weight = parseFloat(process.env.RETRIEVAL_DENSE_WEIGHT)
|
|
99
|
-
}
|
|
100
|
-
if (process.env.RETRIEVAL_SPARSE_ENABLED) {
|
|
101
|
-
retrieval.retrieval = retrieval.retrieval || {}
|
|
102
|
-
retrieval.retrieval.sparse = retrieval.retrieval.sparse || {}
|
|
103
|
-
retrieval.retrieval.sparse.enabled = process.env.RETRIEVAL_SPARSE_ENABLED === 'true'
|
|
104
|
-
}
|
|
105
|
-
if (process.env.RETRIEVAL_SPARSE_WEIGHT) {
|
|
106
|
-
retrieval.retrieval = retrieval.retrieval || {}
|
|
107
|
-
retrieval.retrieval.sparse = retrieval.retrieval.sparse || {}
|
|
108
|
-
retrieval.retrieval.sparse.weight = parseFloat(process.env.RETRIEVAL_SPARSE_WEIGHT)
|
|
109
|
-
}
|
|
110
|
-
if (process.env.RETRIEVAL_FUSION_METHOD) {
|
|
111
|
-
retrieval.retrieval = retrieval.retrieval || {}
|
|
112
|
-
retrieval.retrieval.fusion = retrieval.retrieval.fusion || {}
|
|
113
|
-
retrieval.retrieval.fusion.method = process.env.RETRIEVAL_FUSION_METHOD as 'rrf' | 'linear' | 'max'
|
|
114
|
-
}
|
|
115
|
-
if (process.env.RETRIEVAL_RRF_K) {
|
|
116
|
-
retrieval.retrieval = retrieval.retrieval || {}
|
|
117
|
-
retrieval.retrieval.fusion = retrieval.retrieval.fusion || {}
|
|
118
|
-
retrieval.retrieval.fusion.rrfK = parseInt(process.env.RETRIEVAL_RRF_K, 10)
|
|
119
|
-
}
|
|
120
|
-
if (process.env.RETRIEVAL_RERANKER_ENABLED) {
|
|
121
|
-
retrieval.retrieval = retrieval.retrieval || {}
|
|
122
|
-
retrieval.retrieval.reranker = retrieval.retrieval.reranker || {}
|
|
123
|
-
retrieval.retrieval.reranker.enabled = process.env.RETRIEVAL_RERANKER_ENABLED === 'true'
|
|
124
|
-
}
|
|
125
|
-
if (process.env.RETRIEVAL_RERANKER_MODEL) {
|
|
126
|
-
retrieval.retrieval = retrieval.retrieval || {}
|
|
127
|
-
retrieval.retrieval.reranker = retrieval.retrieval.reranker || {}
|
|
128
|
-
retrieval.retrieval.reranker.model = process.env.RETRIEVAL_RERANKER_MODEL
|
|
129
|
-
}
|
|
130
|
-
if (process.env.RETRIEVAL_FEEDBACK_ENABLED) {
|
|
131
|
-
retrieval.retrieval = retrieval.retrieval || {}
|
|
132
|
-
retrieval.retrieval.feedback = retrieval.retrieval.feedback || {}
|
|
133
|
-
retrieval.retrieval.feedback.enabled = process.env.RETRIEVAL_FEEDBACK_ENABLED === 'true'
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return config
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** Merge multiple partial configs, later values override earlier */
|
|
140
|
-
function mergeConfigs(...configs: Partial<Config>[]): Partial<Config> {
|
|
141
|
-
return configs.reduce<Partial<Config>>((acc, config) => {
|
|
142
|
-
return { ...acc, ...Object.fromEntries(Object.entries(config).filter(([_, v]) => v !== undefined)) }
|
|
143
|
-
}, {})
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** Load and validate configuration from all sources */
|
|
147
|
-
export async function loadConfig(basePath: string = process.cwd()): Promise<Config> {
|
|
148
|
-
const fileConfig = loadFromFile(basePath)
|
|
149
|
-
const envConfig = loadFromEnv()
|
|
150
|
-
|
|
151
|
-
const merged = mergeConfigs(defaultConfig as Partial<Config>, fileConfig, envConfig)
|
|
152
|
-
|
|
153
|
-
const result = ConfigSchema.safeParse(merged)
|
|
154
|
-
|
|
155
|
-
if (!result.success) {
|
|
156
|
-
const errors = result.error.issues
|
|
157
|
-
.map(e => ` - ${e.path.join('.')}: ${e.message}`)
|
|
158
|
-
.join('\n')
|
|
159
|
-
throw new Error(`Configuration validation failed:\n${errors}`)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Resolve relative paths against Claude Brain home
|
|
163
|
-
const data = result.data
|
|
164
|
-
data.logFilePath = resolveHomePath(data.logFilePath)
|
|
165
|
-
data.dbPath = resolveHomePath(data.dbPath)
|
|
166
|
-
|
|
167
|
-
// Always resolve knowledge graph path to absolute
|
|
168
|
-
// Zod .default({}) on nested objects means graph/persistPath may not exist
|
|
169
|
-
if (!data.knowledge) data.knowledge = {} as
|
|
170
|
-
if (!data.knowledge.graph) data.knowledge.graph = {} as
|
|
171
|
-
data.knowledge.graph.persistPath = resolveHomePath(
|
|
172
|
-
data.knowledge.graph.persistPath || './data/knowledge-graph.json'
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
// Phase 30: Auto-detect vaultPath if not configured
|
|
176
|
-
if (!data.vaultPath) {
|
|
177
|
-
const { getHomePaths } = await import('./home')
|
|
178
|
-
data.vaultPath = getHomePaths().vault
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return data
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/** Validate a partial configuration object */
|
|
185
|
-
export function validateConfig(config: unknown): config is Config {
|
|
186
|
-
return ConfigSchema.safeParse(config).success
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** Get the path to the config file */
|
|
190
|
-
export function getConfigFilePath(basePath: string = process.cwd()): string {
|
|
191
|
-
return resolve(basePath, CONFIG_FILE_NAME)
|
|
192
|
-
}
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import { ConfigSchema, type Config } from './schema'
|
|
4
|
+
import { defaultConfig } from './defaults'
|
|
5
|
+
import { getClaudeBrainHome, resolveHomePath } from './home'
|
|
6
|
+
|
|
7
|
+
/** Parse a .env file without dotenv (avoids stdout pollution from dotenv v17) */
|
|
8
|
+
function loadEnvFile(filePath: string): void {
|
|
9
|
+
if (!existsSync(filePath)) return
|
|
10
|
+
const content = readFileSync(filePath, 'utf-8')
|
|
11
|
+
for (const line of content.split('\n')) {
|
|
12
|
+
const trimmed = line.trim()
|
|
13
|
+
if (!trimmed || trimmed.startsWith('#')) continue
|
|
14
|
+
const eqIdx = trimmed.indexOf('=')
|
|
15
|
+
if (eqIdx === -1) continue
|
|
16
|
+
const key = trimmed.slice(0, eqIdx).trim()
|
|
17
|
+
const val = trimmed.slice(eqIdx + 1).trim()
|
|
18
|
+
// Don't override existing env vars
|
|
19
|
+
if (!(key in process.env)) {
|
|
20
|
+
process.env[key] = val
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const CONFIG_FILE_NAME = '.claudebrainrc.json'
|
|
26
|
+
|
|
27
|
+
/** Load configuration from a JSON file if it exists */
|
|
28
|
+
function loadFromFile(basePath: string): Partial<Config> {
|
|
29
|
+
// Check basePath first, then Claude Brain home
|
|
30
|
+
for (const dir of [basePath, getClaudeBrainHome()]) {
|
|
31
|
+
const configPath = resolve(dir, CONFIG_FILE_NAME)
|
|
32
|
+
if (!existsSync(configPath)) {
|
|
33
|
+
continue
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const content = readFileSync(configPath, 'utf-8')
|
|
38
|
+
return JSON.parse(content) as Partial<Config>
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.warn(`Warning: Failed to parse config file at ${configPath}:`, error)
|
|
41
|
+
return {}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Load configuration from environment variables */
|
|
49
|
+
function loadFromEnv(): Partial<Config> {
|
|
50
|
+
// Skip .env loading if SKIP_DOTENV is set (for MCP servers where env is provided externally)
|
|
51
|
+
if (!process.env.SKIP_DOTENV) {
|
|
52
|
+
// Use custom parser instead of dotenv v17 (which prints to stdout and breaks MCP stdio)
|
|
53
|
+
loadEnvFile(resolve(getClaudeBrainHome(), '.env'))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const config: Partial<Config> = {}
|
|
57
|
+
|
|
58
|
+
if (process.env.VAULT_PATH) {
|
|
59
|
+
config.vaultPath = process.env.VAULT_PATH
|
|
60
|
+
}
|
|
61
|
+
if (process.env.MCP_SERVER_NAME) {
|
|
62
|
+
config.serverName = process.env.MCP_SERVER_NAME
|
|
63
|
+
}
|
|
64
|
+
if (process.env.MCP_SERVER_VERSION) {
|
|
65
|
+
config.serverVersion = process.env.MCP_SERVER_VERSION
|
|
66
|
+
}
|
|
67
|
+
if (process.env.LOG_LEVEL) {
|
|
68
|
+
config.logLevel = process.env.LOG_LEVEL as Config['logLevel']
|
|
69
|
+
}
|
|
70
|
+
if (process.env.LOG_FILE_PATH) {
|
|
71
|
+
config.logFilePath = process.env.LOG_FILE_PATH
|
|
72
|
+
}
|
|
73
|
+
if (process.env.DB_PATH) {
|
|
74
|
+
config.dbPath = process.env.DB_PATH
|
|
75
|
+
}
|
|
76
|
+
if (process.env.PORT) {
|
|
77
|
+
config.port = parseInt(process.env.PORT, 10)
|
|
78
|
+
}
|
|
79
|
+
if (process.env.ENABLE_FILE_WATCH) {
|
|
80
|
+
config.enableFileWatch = process.env.ENABLE_FILE_WATCH === 'true'
|
|
81
|
+
}
|
|
82
|
+
if (process.env.CACHE_SIZE) {
|
|
83
|
+
config.cacheSize = parseInt(process.env.CACHE_SIZE, 10)
|
|
84
|
+
}
|
|
85
|
+
if (process.env.NODE_ENV) {
|
|
86
|
+
config.nodeEnv = process.env.NODE_ENV as Config['nodeEnv']
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Retrieval configuration
|
|
90
|
+
const retrieval = (config as Record<string, any>)
|
|
91
|
+
if (process.env.RETRIEVAL_ENABLED) {
|
|
92
|
+
retrieval.retrieval = retrieval.retrieval || {}
|
|
93
|
+
retrieval.retrieval.enabled = process.env.RETRIEVAL_ENABLED === 'true'
|
|
94
|
+
}
|
|
95
|
+
if (process.env.RETRIEVAL_DENSE_WEIGHT) {
|
|
96
|
+
retrieval.retrieval = retrieval.retrieval || {}
|
|
97
|
+
retrieval.retrieval.dense = retrieval.retrieval.dense || {}
|
|
98
|
+
retrieval.retrieval.dense.weight = parseFloat(process.env.RETRIEVAL_DENSE_WEIGHT)
|
|
99
|
+
}
|
|
100
|
+
if (process.env.RETRIEVAL_SPARSE_ENABLED) {
|
|
101
|
+
retrieval.retrieval = retrieval.retrieval || {}
|
|
102
|
+
retrieval.retrieval.sparse = retrieval.retrieval.sparse || {}
|
|
103
|
+
retrieval.retrieval.sparse.enabled = process.env.RETRIEVAL_SPARSE_ENABLED === 'true'
|
|
104
|
+
}
|
|
105
|
+
if (process.env.RETRIEVAL_SPARSE_WEIGHT) {
|
|
106
|
+
retrieval.retrieval = retrieval.retrieval || {}
|
|
107
|
+
retrieval.retrieval.sparse = retrieval.retrieval.sparse || {}
|
|
108
|
+
retrieval.retrieval.sparse.weight = parseFloat(process.env.RETRIEVAL_SPARSE_WEIGHT)
|
|
109
|
+
}
|
|
110
|
+
if (process.env.RETRIEVAL_FUSION_METHOD) {
|
|
111
|
+
retrieval.retrieval = retrieval.retrieval || {}
|
|
112
|
+
retrieval.retrieval.fusion = retrieval.retrieval.fusion || {}
|
|
113
|
+
retrieval.retrieval.fusion.method = process.env.RETRIEVAL_FUSION_METHOD as 'rrf' | 'linear' | 'max'
|
|
114
|
+
}
|
|
115
|
+
if (process.env.RETRIEVAL_RRF_K) {
|
|
116
|
+
retrieval.retrieval = retrieval.retrieval || {}
|
|
117
|
+
retrieval.retrieval.fusion = retrieval.retrieval.fusion || {}
|
|
118
|
+
retrieval.retrieval.fusion.rrfK = parseInt(process.env.RETRIEVAL_RRF_K, 10)
|
|
119
|
+
}
|
|
120
|
+
if (process.env.RETRIEVAL_RERANKER_ENABLED) {
|
|
121
|
+
retrieval.retrieval = retrieval.retrieval || {}
|
|
122
|
+
retrieval.retrieval.reranker = retrieval.retrieval.reranker || {}
|
|
123
|
+
retrieval.retrieval.reranker.enabled = process.env.RETRIEVAL_RERANKER_ENABLED === 'true'
|
|
124
|
+
}
|
|
125
|
+
if (process.env.RETRIEVAL_RERANKER_MODEL) {
|
|
126
|
+
retrieval.retrieval = retrieval.retrieval || {}
|
|
127
|
+
retrieval.retrieval.reranker = retrieval.retrieval.reranker || {}
|
|
128
|
+
retrieval.retrieval.reranker.model = process.env.RETRIEVAL_RERANKER_MODEL
|
|
129
|
+
}
|
|
130
|
+
if (process.env.RETRIEVAL_FEEDBACK_ENABLED) {
|
|
131
|
+
retrieval.retrieval = retrieval.retrieval || {}
|
|
132
|
+
retrieval.retrieval.feedback = retrieval.retrieval.feedback || {}
|
|
133
|
+
retrieval.retrieval.feedback.enabled = process.env.RETRIEVAL_FEEDBACK_ENABLED === 'true'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return config
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Merge multiple partial configs, later values override earlier */
|
|
140
|
+
function mergeConfigs(...configs: Partial<Config>[]): Partial<Config> {
|
|
141
|
+
return configs.reduce<Partial<Config>>((acc, config) => {
|
|
142
|
+
return { ...acc, ...Object.fromEntries(Object.entries(config).filter(([_, v]) => v !== undefined)) }
|
|
143
|
+
}, {})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Load and validate configuration from all sources */
|
|
147
|
+
export async function loadConfig(basePath: string = process.cwd()): Promise<Config> {
|
|
148
|
+
const fileConfig = loadFromFile(basePath)
|
|
149
|
+
const envConfig = loadFromEnv()
|
|
150
|
+
|
|
151
|
+
const merged = mergeConfigs(defaultConfig as Partial<Config>, fileConfig, envConfig)
|
|
152
|
+
|
|
153
|
+
const result = ConfigSchema.safeParse(merged)
|
|
154
|
+
|
|
155
|
+
if (!result.success) {
|
|
156
|
+
const errors = result.error.issues
|
|
157
|
+
.map(e => ` - ${e.path.join('.')}: ${e.message}`)
|
|
158
|
+
.join('\n')
|
|
159
|
+
throw new Error(`Configuration validation failed:\n${errors}`)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Resolve relative paths against Claude Brain home
|
|
163
|
+
const data = result.data
|
|
164
|
+
data.logFilePath = resolveHomePath(data.logFilePath)
|
|
165
|
+
data.dbPath = resolveHomePath(data.dbPath)
|
|
166
|
+
|
|
167
|
+
// Always resolve knowledge graph path to absolute
|
|
168
|
+
// Zod .default({}) on nested objects means graph/persistPath may not exist
|
|
169
|
+
if (!data.knowledge) data.knowledge = {} as Record<string, never>
|
|
170
|
+
if (!data.knowledge.graph) data.knowledge.graph = {} as Record<string, never>
|
|
171
|
+
data.knowledge.graph.persistPath = resolveHomePath(
|
|
172
|
+
data.knowledge.graph.persistPath || './data/knowledge-graph.json'
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
// Phase 30: Auto-detect vaultPath if not configured
|
|
176
|
+
if (!data.vaultPath) {
|
|
177
|
+
const { getHomePaths } = await import('./home')
|
|
178
|
+
data.vaultPath = getHomePaths().vault
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return data
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Validate a partial configuration object */
|
|
185
|
+
export function validateConfig(config: unknown): config is Config {
|
|
186
|
+
return ConfigSchema.safeParse(config).success
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Get the path to the config file */
|
|
190
|
+
export function getConfigFilePath(basePath: string = process.cwd()): string {
|
|
191
|
+
return resolve(basePath, CONFIG_FILE_NAME)
|
|
192
|
+
}
|