claude-brain 0.3.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 (200) hide show
  1. package/README.md +157 -0
  2. package/VERSION +1 -0
  3. package/assets/CLAUDE.md +307 -0
  4. package/bunfig.toml +8 -0
  5. package/package.json +74 -0
  6. package/src/automation/auto-context.ts +240 -0
  7. package/src/automation/decision-detector.ts +452 -0
  8. package/src/automation/index.ts +11 -0
  9. package/src/automation/proactive-recall.ts +373 -0
  10. package/src/automation/project-detector.ts +297 -0
  11. package/src/cli/auto-setup.ts +74 -0
  12. package/src/cli/bin.ts +110 -0
  13. package/src/cli/commands/install-mcp.ts +50 -0
  14. package/src/cli/commands/serve.ts +129 -0
  15. package/src/cli/diagnose.ts +4 -0
  16. package/src/cli/health-check.ts +4 -0
  17. package/src/cli/migrate-chroma.ts +106 -0
  18. package/src/cli/setup.ts +4 -0
  19. package/src/config/defaults.ts +47 -0
  20. package/src/config/home.ts +55 -0
  21. package/src/config/index.ts +7 -0
  22. package/src/config/loader.ts +166 -0
  23. package/src/config/migration.ts +76 -0
  24. package/src/config/schema.ts +257 -0
  25. package/src/config/validator.ts +184 -0
  26. package/src/config/watcher.ts +86 -0
  27. package/src/context/assembler.ts +398 -0
  28. package/src/context/cache-manager.ts +101 -0
  29. package/src/context/formatter.ts +84 -0
  30. package/src/context/hierarchy.ts +85 -0
  31. package/src/context/index.ts +83 -0
  32. package/src/context/progress-tracker.ts +174 -0
  33. package/src/context/standards-manager.ts +267 -0
  34. package/src/context/types.ts +252 -0
  35. package/src/context/validator.ts +58 -0
  36. package/src/cross-project/affinity.ts +162 -0
  37. package/src/cross-project/generalizer.ts +283 -0
  38. package/src/cross-project/index.ts +13 -0
  39. package/src/cross-project/transfer.ts +201 -0
  40. package/src/diagnostics/index.ts +123 -0
  41. package/src/health/index.ts +229 -0
  42. package/src/index.ts +7 -0
  43. package/src/knowledge/entity-extractor.ts +416 -0
  44. package/src/knowledge/graph/builder.ts +159 -0
  45. package/src/knowledge/graph/linker.ts +201 -0
  46. package/src/knowledge/graph/memory-graph.ts +359 -0
  47. package/src/knowledge/graph/schema.ts +99 -0
  48. package/src/knowledge/graph/search.ts +168 -0
  49. package/src/knowledge/relationship-extractor.ts +108 -0
  50. package/src/memory/chroma/client.ts +169 -0
  51. package/src/memory/chroma/collection-manager.ts +94 -0
  52. package/src/memory/chroma/config.ts +46 -0
  53. package/src/memory/chroma/embeddings.ts +153 -0
  54. package/src/memory/chroma/index.ts +82 -0
  55. package/src/memory/chroma/migration.ts +270 -0
  56. package/src/memory/chroma/schemas.ts +69 -0
  57. package/src/memory/chroma/search.ts +315 -0
  58. package/src/memory/chroma/store.ts +694 -0
  59. package/src/memory/consolidation/archiver.ts +164 -0
  60. package/src/memory/consolidation/merger.ts +186 -0
  61. package/src/memory/consolidation/scorer.ts +138 -0
  62. package/src/memory/context-builder.ts +236 -0
  63. package/src/memory/database.ts +169 -0
  64. package/src/memory/embedding-utils.ts +156 -0
  65. package/src/memory/embeddings.ts +226 -0
  66. package/src/memory/episodic/detector.ts +108 -0
  67. package/src/memory/episodic/manager.ts +334 -0
  68. package/src/memory/episodic/summarizer.ts +179 -0
  69. package/src/memory/episodic/types.ts +52 -0
  70. package/src/memory/index.ts +395 -0
  71. package/src/memory/knowledge-extractor.ts +455 -0
  72. package/src/memory/learning.ts +378 -0
  73. package/src/memory/patterns.ts +396 -0
  74. package/src/memory/schema.ts +56 -0
  75. package/src/memory/search.ts +309 -0
  76. package/src/memory/store.ts +344 -0
  77. package/src/memory/types.ts +121 -0
  78. package/src/optimization/index.ts +10 -0
  79. package/src/optimization/precompute.ts +202 -0
  80. package/src/optimization/semantic-cache.ts +207 -0
  81. package/src/orchestrator/coordinator.ts +272 -0
  82. package/src/orchestrator/decision-logger.ts +228 -0
  83. package/src/orchestrator/event-emitter.ts +198 -0
  84. package/src/orchestrator/event-queue.ts +184 -0
  85. package/src/orchestrator/handlers/base-handler.ts +70 -0
  86. package/src/orchestrator/handlers/context-handler.ts +73 -0
  87. package/src/orchestrator/handlers/decision-handler.ts +204 -0
  88. package/src/orchestrator/handlers/index.ts +10 -0
  89. package/src/orchestrator/handlers/status-handler.ts +131 -0
  90. package/src/orchestrator/handlers/task-handler.ts +171 -0
  91. package/src/orchestrator/index.ts +275 -0
  92. package/src/orchestrator/task-parser.ts +284 -0
  93. package/src/orchestrator/types.ts +98 -0
  94. package/src/phase12/index.ts +456 -0
  95. package/src/prediction/context-anticipator.ts +198 -0
  96. package/src/prediction/decision-predictor.ts +184 -0
  97. package/src/prediction/index.ts +13 -0
  98. package/src/prediction/recommender.ts +268 -0
  99. package/src/reasoning/chain-retrieval.ts +247 -0
  100. package/src/reasoning/counterfactual.ts +248 -0
  101. package/src/reasoning/index.ts +13 -0
  102. package/src/reasoning/synthesizer.ts +169 -0
  103. package/src/retrieval/bm25/index.ts +300 -0
  104. package/src/retrieval/bm25/tokenizer.ts +184 -0
  105. package/src/retrieval/feedback/adaptive.ts +223 -0
  106. package/src/retrieval/feedback/index.ts +16 -0
  107. package/src/retrieval/feedback/metrics.ts +223 -0
  108. package/src/retrieval/feedback/store.ts +283 -0
  109. package/src/retrieval/fusion/index.ts +194 -0
  110. package/src/retrieval/fusion/rrf.ts +163 -0
  111. package/src/retrieval/index.ts +12 -0
  112. package/src/retrieval/pipeline.ts +375 -0
  113. package/src/retrieval/query/expander.ts +198 -0
  114. package/src/retrieval/query/index.ts +27 -0
  115. package/src/retrieval/query/intent-classifier.ts +236 -0
  116. package/src/retrieval/query/temporal-parser.ts +295 -0
  117. package/src/retrieval/reranker/index.ts +188 -0
  118. package/src/retrieval/reranker/model.ts +95 -0
  119. package/src/retrieval/service.ts +125 -0
  120. package/src/retrieval/types.ts +162 -0
  121. package/src/scripts/health-check.ts +118 -0
  122. package/src/scripts/setup.ts +122 -0
  123. package/src/server/handlers/call-tool.ts +194 -0
  124. package/src/server/handlers/index.ts +9 -0
  125. package/src/server/handlers/list-tools.ts +18 -0
  126. package/src/server/handlers/tools/analyze-decision-evolution.ts +71 -0
  127. package/src/server/handlers/tools/auto-remember.ts +200 -0
  128. package/src/server/handlers/tools/create-project.ts +135 -0
  129. package/src/server/handlers/tools/detect-trends.ts +80 -0
  130. package/src/server/handlers/tools/find-cross-project-patterns.ts +73 -0
  131. package/src/server/handlers/tools/get-activity-log.ts +194 -0
  132. package/src/server/handlers/tools/get-code-standards.ts +124 -0
  133. package/src/server/handlers/tools/get-corrections.ts +154 -0
  134. package/src/server/handlers/tools/get-decision-timeline.ts +86 -0
  135. package/src/server/handlers/tools/get-episode.ts +93 -0
  136. package/src/server/handlers/tools/get-patterns.ts +158 -0
  137. package/src/server/handlers/tools/get-phase12-status.ts +63 -0
  138. package/src/server/handlers/tools/get-project-context.ts +75 -0
  139. package/src/server/handlers/tools/get-recommendations.ts +65 -0
  140. package/src/server/handlers/tools/index.ts +33 -0
  141. package/src/server/handlers/tools/init-project.ts +710 -0
  142. package/src/server/handlers/tools/list-episodes.ts +80 -0
  143. package/src/server/handlers/tools/list-projects.ts +125 -0
  144. package/src/server/handlers/tools/rate-memory.ts +95 -0
  145. package/src/server/handlers/tools/recall-similar.ts +87 -0
  146. package/src/server/handlers/tools/recognize-pattern.ts +126 -0
  147. package/src/server/handlers/tools/record-correction.ts +125 -0
  148. package/src/server/handlers/tools/remember-decision.ts +153 -0
  149. package/src/server/handlers/tools/schemas.ts +241 -0
  150. package/src/server/handlers/tools/search-knowledge-graph.ts +89 -0
  151. package/src/server/handlers/tools/smart-context.ts +124 -0
  152. package/src/server/handlers/tools/update-progress.ts +114 -0
  153. package/src/server/handlers/tools/what-if-analysis.ts +73 -0
  154. package/src/server/http-api.ts +474 -0
  155. package/src/server/index.ts +40 -0
  156. package/src/server/mcp-server.ts +283 -0
  157. package/src/server/providers/index.ts +7 -0
  158. package/src/server/providers/prompts.ts +327 -0
  159. package/src/server/providers/resources.ts +427 -0
  160. package/src/server/services.ts +388 -0
  161. package/src/server/types.ts +39 -0
  162. package/src/server/utils/error-handler.ts +155 -0
  163. package/src/server/utils/index.ts +13 -0
  164. package/src/server/utils/memory-indicator.ts +83 -0
  165. package/src/server/utils/request-context.ts +122 -0
  166. package/src/server/utils/response-formatter.ts +124 -0
  167. package/src/server/utils/validators.ts +210 -0
  168. package/src/setup/index.ts +22 -0
  169. package/src/setup/wizard.ts +321 -0
  170. package/src/temporal/evolution.ts +197 -0
  171. package/src/temporal/index.ts +16 -0
  172. package/src/temporal/query-processor.ts +190 -0
  173. package/src/temporal/timeline.ts +259 -0
  174. package/src/temporal/trends.ts +263 -0
  175. package/src/tools/index.ts +24 -0
  176. package/src/tools/registry.ts +106 -0
  177. package/src/tools/schemas.test.ts +30 -0
  178. package/src/tools/schemas.ts +907 -0
  179. package/src/tools/types.ts +412 -0
  180. package/src/utils/circuit-breaker.ts +130 -0
  181. package/src/utils/cleanup.ts +34 -0
  182. package/src/utils/error-handler.ts +132 -0
  183. package/src/utils/error-messages.ts +60 -0
  184. package/src/utils/fallback.ts +45 -0
  185. package/src/utils/index.ts +54 -0
  186. package/src/utils/logger-utils.ts +80 -0
  187. package/src/utils/logger.ts +88 -0
  188. package/src/utils/phase12-helper.ts +56 -0
  189. package/src/utils/retry.ts +94 -0
  190. package/src/utils/transaction.ts +63 -0
  191. package/src/vault/frontmatter.ts +264 -0
  192. package/src/vault/index.ts +318 -0
  193. package/src/vault/paths.ts +106 -0
  194. package/src/vault/query.ts +422 -0
  195. package/src/vault/reader.ts +264 -0
  196. package/src/vault/templates.ts +186 -0
  197. package/src/vault/types.ts +73 -0
  198. package/src/vault/watcher.ts +277 -0
  199. package/src/vault/writer.ts +393 -0
  200. package/tsconfig.json +30 -0
@@ -0,0 +1,166 @@
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import { config as loadEnv } from 'dotenv'
4
+ import { ConfigSchema, type Config, type PartialConfig } from './schema'
5
+ import { defaultConfig } from './defaults'
6
+ import { getClaudeBrainHome, resolveHomePath } from './home'
7
+
8
+ const CONFIG_FILE_NAME = '.claudebrainrc.json'
9
+
10
+ /** Load configuration from a JSON file if it exists */
11
+ function loadFromFile(basePath: string): PartialConfig {
12
+ // Check basePath first, then Claude Brain home
13
+ for (const dir of [basePath, getClaudeBrainHome()]) {
14
+ const configPath = resolve(dir, CONFIG_FILE_NAME)
15
+ if (!existsSync(configPath)) {
16
+ continue
17
+ }
18
+
19
+ try {
20
+ const content = readFileSync(configPath, 'utf-8')
21
+ return JSON.parse(content) as PartialConfig
22
+ } catch (error) {
23
+ console.warn(`Warning: Failed to parse config file at ${configPath}:`, error)
24
+ return {}
25
+ }
26
+ }
27
+
28
+ return {}
29
+ }
30
+
31
+ /** Load configuration from environment variables */
32
+ function loadFromEnv(): PartialConfig {
33
+ // Skip .env loading if SKIP_DOTENV is set (for MCP servers where env is provided externally)
34
+ if (!process.env.SKIP_DOTENV) {
35
+ // Load .env from Claude Brain home directory
36
+ loadEnv({ path: resolve(getClaudeBrainHome(), '.env'), debug: false })
37
+ }
38
+
39
+ const config: PartialConfig = {}
40
+
41
+ if (process.env.VAULT_PATH) {
42
+ config.vaultPath = process.env.VAULT_PATH
43
+ }
44
+ if (process.env.MCP_SERVER_NAME) {
45
+ config.serverName = process.env.MCP_SERVER_NAME
46
+ }
47
+ if (process.env.MCP_SERVER_VERSION) {
48
+ config.serverVersion = process.env.MCP_SERVER_VERSION
49
+ }
50
+ if (process.env.LOG_LEVEL) {
51
+ config.logLevel = process.env.LOG_LEVEL as Config['logLevel']
52
+ }
53
+ if (process.env.LOG_FILE_PATH) {
54
+ config.logFilePath = process.env.LOG_FILE_PATH
55
+ }
56
+ if (process.env.DB_PATH) {
57
+ config.dbPath = process.env.DB_PATH
58
+ }
59
+ if (process.env.PORT) {
60
+ config.port = parseInt(process.env.PORT, 10)
61
+ }
62
+ if (process.env.ENABLE_FILE_WATCH) {
63
+ config.enableFileWatch = process.env.ENABLE_FILE_WATCH === 'true'
64
+ }
65
+ if (process.env.CACHE_SIZE) {
66
+ config.cacheSize = parseInt(process.env.CACHE_SIZE, 10)
67
+ }
68
+ if (process.env.NODE_ENV) {
69
+ config.nodeEnv = process.env.NODE_ENV as Config['nodeEnv']
70
+ }
71
+
72
+ // Retrieval configuration
73
+ if (process.env.RETRIEVAL_ENABLED) {
74
+ config.retrieval = config.retrieval || {}
75
+ config.retrieval.enabled = process.env.RETRIEVAL_ENABLED === 'true'
76
+ }
77
+ if (process.env.RETRIEVAL_DENSE_WEIGHT) {
78
+ config.retrieval = config.retrieval || {}
79
+ config.retrieval.dense = config.retrieval.dense || {}
80
+ config.retrieval.dense.weight = parseFloat(process.env.RETRIEVAL_DENSE_WEIGHT)
81
+ }
82
+ if (process.env.RETRIEVAL_SPARSE_ENABLED) {
83
+ config.retrieval = config.retrieval || {}
84
+ config.retrieval.sparse = config.retrieval.sparse || {}
85
+ config.retrieval.sparse.enabled = process.env.RETRIEVAL_SPARSE_ENABLED === 'true'
86
+ }
87
+ if (process.env.RETRIEVAL_SPARSE_WEIGHT) {
88
+ config.retrieval = config.retrieval || {}
89
+ config.retrieval.sparse = config.retrieval.sparse || {}
90
+ config.retrieval.sparse.weight = parseFloat(process.env.RETRIEVAL_SPARSE_WEIGHT)
91
+ }
92
+ if (process.env.RETRIEVAL_FUSION_METHOD) {
93
+ config.retrieval = config.retrieval || {}
94
+ config.retrieval.fusion = config.retrieval.fusion || {}
95
+ config.retrieval.fusion.method = process.env.RETRIEVAL_FUSION_METHOD as 'rrf' | 'linear' | 'max'
96
+ }
97
+ if (process.env.RETRIEVAL_RRF_K) {
98
+ config.retrieval = config.retrieval || {}
99
+ config.retrieval.fusion = config.retrieval.fusion || {}
100
+ config.retrieval.fusion.rrfK = parseInt(process.env.RETRIEVAL_RRF_K, 10)
101
+ }
102
+ if (process.env.RETRIEVAL_RERANKER_ENABLED) {
103
+ config.retrieval = config.retrieval || {}
104
+ config.retrieval.reranker = config.retrieval.reranker || {}
105
+ config.retrieval.reranker.enabled = process.env.RETRIEVAL_RERANKER_ENABLED === 'true'
106
+ }
107
+ if (process.env.RETRIEVAL_RERANKER_MODEL) {
108
+ config.retrieval = config.retrieval || {}
109
+ config.retrieval.reranker = config.retrieval.reranker || {}
110
+ config.retrieval.reranker.model = process.env.RETRIEVAL_RERANKER_MODEL
111
+ }
112
+ if (process.env.RETRIEVAL_FEEDBACK_ENABLED) {
113
+ config.retrieval = config.retrieval || {}
114
+ config.retrieval.feedback = config.retrieval.feedback || {}
115
+ config.retrieval.feedback.enabled = process.env.RETRIEVAL_FEEDBACK_ENABLED === 'true'
116
+ }
117
+
118
+ return config
119
+ }
120
+
121
+ /** Merge multiple partial configs, later values override earlier */
122
+ function mergeConfigs(...configs: PartialConfig[]): PartialConfig {
123
+ return configs.reduce((acc, config) => {
124
+ return { ...acc, ...Object.fromEntries(Object.entries(config).filter(([_, v]) => v !== undefined)) }
125
+ }, {})
126
+ }
127
+
128
+ /** Load and validate configuration from all sources */
129
+ export async function loadConfig(basePath: string = process.cwd()): Promise<Config> {
130
+ const fileConfig = loadFromFile(basePath)
131
+ const envConfig = loadFromEnv()
132
+
133
+ const merged = mergeConfigs(defaultConfig, fileConfig, envConfig)
134
+
135
+ const result = ConfigSchema.safeParse(merged)
136
+
137
+ if (!result.success) {
138
+ const errors = result.error.errors
139
+ .map(e => ` - ${e.path.join('.')}: ${e.message}`)
140
+ .join('\n')
141
+ throw new Error(`Configuration validation failed:\n${errors}`)
142
+ }
143
+
144
+ // Resolve relative paths against Claude Brain home
145
+ const data = result.data
146
+ data.logFilePath = resolveHomePath(data.logFilePath)
147
+ data.dbPath = resolveHomePath(data.dbPath)
148
+ if (data.chroma.path) {
149
+ data.chroma.path = resolveHomePath(data.chroma.path)
150
+ }
151
+ if (data.knowledge?.graph?.persistPath) {
152
+ data.knowledge.graph.persistPath = resolveHomePath(data.knowledge.graph.persistPath)
153
+ }
154
+
155
+ return data
156
+ }
157
+
158
+ /** Validate a partial configuration object */
159
+ export function validateConfig(config: unknown): config is Config {
160
+ return ConfigSchema.safeParse(config).success
161
+ }
162
+
163
+ /** Get the path to the config file */
164
+ export function getConfigFilePath(basePath: string = process.cwd()): string {
165
+ return resolve(basePath, CONFIG_FILE_NAME)
166
+ }
@@ -0,0 +1,76 @@
1
+ import fs from 'fs/promises'
2
+ import type { Logger } from 'pino'
3
+
4
+ export interface ConfigVersion {
5
+ version: string
6
+ config: Record<string, any>
7
+ }
8
+
9
+ export class ConfigMigration {
10
+ private logger: Logger
11
+ private currentVersion = '1.0.0'
12
+
13
+ constructor(logger: Logger) {
14
+ this.logger = logger.child({ component: 'config-migration' })
15
+ }
16
+
17
+ async migrate(config: any): Promise<any> {
18
+ const configVersion = config.version || '0.0.0'
19
+
20
+ if (configVersion === this.currentVersion) {
21
+ return config
22
+ }
23
+
24
+ this.logger.info(
25
+ { from: configVersion, to: this.currentVersion },
26
+ 'Migrating configuration'
27
+ )
28
+
29
+ await this.backup(config)
30
+
31
+ let migrated = { ...config }
32
+
33
+ if (this.compareVersions(configVersion, '1.0.0') < 0) {
34
+ migrated = await this.migrateTo_1_0_0(migrated)
35
+ }
36
+
37
+ migrated.version = this.currentVersion
38
+
39
+ return migrated
40
+ }
41
+
42
+ private async migrateTo_1_0_0(config: any): Promise<any> {
43
+ this.logger.info('Migrating to version 1.0.0')
44
+
45
+ return {
46
+ ...config,
47
+ enableFileWatch: config.enableFileWatch ?? true,
48
+ serverVersion: config.serverVersion || '1.0.0'
49
+ }
50
+ }
51
+
52
+ private async backup(config: any): Promise<void> {
53
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
54
+ const backupPath = `./data/config-backup-${timestamp}.json`
55
+
56
+ await fs.writeFile(
57
+ backupPath,
58
+ JSON.stringify(config, null, 2),
59
+ 'utf-8'
60
+ )
61
+
62
+ this.logger.info({ backupPath }, 'Configuration backed up')
63
+ }
64
+
65
+ private compareVersions(v1: string, v2: string): number {
66
+ const parts1 = v1.split('.').map(Number)
67
+ const parts2 = v2.split('.').map(Number)
68
+
69
+ for (let i = 0; i < 3; i++) {
70
+ if (parts1[i] > parts2[i]) return 1
71
+ if (parts1[i] < parts2[i]) return -1
72
+ }
73
+
74
+ return 0
75
+ }
76
+ }
@@ -0,0 +1,257 @@
1
+ import { z } from 'zod'
2
+
3
+ /** Log level options for the application */
4
+ export const LogLevelSchema = z.enum(['debug', 'info', 'warn', 'error'])
5
+ export type LogLevel = z.infer<typeof LogLevelSchema>
6
+
7
+ /** Retrieval configuration for hybrid search */
8
+ export const RetrievalConfigSchema = z.object({
9
+ /** Enable hybrid retrieval pipeline (opt-in) */
10
+ enabled: z.boolean().default(false),
11
+
12
+ /** Dense (semantic) search configuration */
13
+ dense: z.object({
14
+ /** Weight for dense search in fusion (0-1) */
15
+ weight: z.number().min(0).max(1).default(0.7),
16
+ /** Maximum number of results from dense search */
17
+ limit: z.number().int().min(1).max(100).default(20),
18
+ /** Minimum similarity threshold (0-1) */
19
+ minSimilarity: z.number().min(0).max(1).default(0.3)
20
+ }).default({}),
21
+
22
+ /** Sparse (BM25) search configuration */
23
+ sparse: z.object({
24
+ /** Enable sparse search */
25
+ enabled: z.boolean().default(true),
26
+ /** Weight for sparse search in fusion (0-1) */
27
+ weight: z.number().min(0).max(1).default(0.3),
28
+ /** Maximum number of results from sparse search */
29
+ limit: z.number().int().min(1).max(100).default(20)
30
+ }).default({}),
31
+
32
+ /** Fusion configuration for combining results */
33
+ fusion: z.object({
34
+ /** Fusion method */
35
+ method: z.enum(['rrf', 'linear', 'max']).default('rrf'),
36
+ /** RRF k parameter (default 60 per original paper) */
37
+ rrfK: z.number().int().min(1).default(60)
38
+ }).default({}),
39
+
40
+ /** Cross-encoder reranking configuration */
41
+ reranker: z.object({
42
+ /** Enable cross-encoder reranking */
43
+ enabled: z.boolean().default(false),
44
+ /** Model to use for reranking */
45
+ model: z.string().default('cross-encoder/ms-marco-MiniLM-L-6-v2'),
46
+ /** Number of results to rerank */
47
+ topK: z.number().int().min(1).max(100).default(10)
48
+ }).default({}),
49
+
50
+ /** Query understanding configuration */
51
+ queryUnderstanding: z.object({
52
+ /** Enable query understanding */
53
+ enabled: z.boolean().default(true),
54
+ /** Enable intent classification */
55
+ intentClassification: z.boolean().default(true),
56
+ /** Enable query expansion */
57
+ queryExpansion: z.boolean().default(true),
58
+ /** Enable temporal parsing */
59
+ temporalParsing: z.boolean().default(true)
60
+ }).default({}),
61
+
62
+ /** Feedback collection configuration */
63
+ feedback: z.object({
64
+ /** Enable feedback collection */
65
+ enabled: z.boolean().default(true),
66
+ /** Enable adaptive threshold learning */
67
+ adaptiveThresholds: z.boolean().default(true),
68
+ /** Minimum feedback count before adaptation */
69
+ minFeedbackForAdaptation: z.number().int().min(1).default(10)
70
+ }).default({})
71
+ })
72
+
73
+ export type RetrievalConfig = z.infer<typeof RetrievalConfigSchema>
74
+
75
+ /** Knowledge graph and episodic memory configuration */
76
+ export const KnowledgeConfigSchema = z.object({
77
+ /** Entity extraction settings */
78
+ extraction: z.object({
79
+ /** Minimum confidence for extracted entities */
80
+ minConfidence: z.number().min(0).max(1).default(0.5),
81
+ /** Maximum entities to extract per text */
82
+ maxEntities: z.number().int().min(1).max(200).default(50)
83
+ }).default({}),
84
+
85
+ /** Knowledge graph settings */
86
+ graph: z.object({
87
+ /** Enable knowledge graph auto-population */
88
+ enabled: z.boolean().default(true),
89
+ /** Path for graph persistence */
90
+ persistPath: z.string().default('./data/knowledge-graph.json'),
91
+ /** Auto-save interval in seconds */
92
+ autoSaveInterval: z.number().int().min(10).default(60),
93
+ /** Maximum graph traversal depth */
94
+ maxTraversalDepth: z.number().int().min(1).max(10).default(5),
95
+ /** Alpha for combined scoring (vector vs graph proximity) */
96
+ alpha: z.number().min(0).max(1).default(0.6)
97
+ }).default({}),
98
+
99
+ /** Episodic memory settings */
100
+ episodic: z.object({
101
+ /** Enable episodic memory */
102
+ enabled: z.boolean().default(true),
103
+ /** Time gap in minutes to detect new session */
104
+ sessionGapMinutes: z.number().int().min(1).default(30),
105
+ /** Maximum messages per episode before auto-splitting */
106
+ maxMessagesPerEpisode: z.number().int().min(10).default(200),
107
+ /** Number of top sentences for brief summary */
108
+ briefSummarySize: z.number().int().min(1).max(10).default(2),
109
+ /** Number of top sentences for detailed summary */
110
+ detailedSummarySize: z.number().int().min(1).max(20).default(8)
111
+ }).default({}),
112
+
113
+ /** Memory consolidation settings */
114
+ consolidation: z.object({
115
+ /** Enable memory consolidation */
116
+ enabled: z.boolean().default(true),
117
+ /** Similarity threshold for merge candidates */
118
+ mergeThreshold: z.number().min(0).max(1).default(0.85),
119
+ /** Importance score threshold for archival */
120
+ archiveThreshold: z.number().min(0).max(1).default(0.2),
121
+ /** Importance scoring weights */
122
+ weights: z.object({
123
+ recency: z.number().min(0).max(1).default(0.2),
124
+ frequency: z.number().min(0).max(1).default(0.2),
125
+ references: z.number().min(0).max(1).default(0.2),
126
+ feedback: z.number().min(0).max(1).default(0.2),
127
+ uniqueness: z.number().min(0).max(1).default(0.2)
128
+ }).default({})
129
+ }).default({})
130
+ })
131
+
132
+ export type KnowledgeConfig = z.infer<typeof KnowledgeConfigSchema>
133
+
134
+ /** Phase 15: Advanced Intelligence configuration */
135
+ export const AdvancedIntelligenceConfigSchema = z.object({
136
+ /** Enable advanced intelligence features (Phase 15) */
137
+ enabled: z.boolean().default(true),
138
+
139
+ /** Temporal intelligence settings */
140
+ temporal: z.object({
141
+ /** Enable temporal query processing */
142
+ enabled: z.boolean().default(true),
143
+ /** Default timeline limit */
144
+ defaultTimelineLimit: z.number().int().min(1).max(500).default(50),
145
+ /** Default trend analysis period in days */
146
+ defaultTrendPeriodDays: z.number().int().min(1).max(3650).default(90)
147
+ }).default({}),
148
+
149
+ /** Multi-hop reasoning settings */
150
+ reasoning: z.object({
151
+ /** Enable multi-hop reasoning */
152
+ enabled: z.boolean().default(true),
153
+ /** Maximum retrieval hops */
154
+ maxHops: z.number().int().min(1).max(10).default(3),
155
+ /** Results per hop */
156
+ resultsPerHop: z.number().int().min(1).max(50).default(5)
157
+ }).default({}),
158
+
159
+ /** Predictive intelligence settings */
160
+ prediction: z.object({
161
+ /** Enable predictive features */
162
+ enabled: z.boolean().default(true),
163
+ /** Default recommendation limit */
164
+ defaultRecommendationLimit: z.number().int().min(1).max(50).default(10)
165
+ }).default({}),
166
+
167
+ /** Cross-project intelligence settings */
168
+ crossProject: z.object({
169
+ /** Enable cross-project analysis */
170
+ enabled: z.boolean().default(true),
171
+ /** Minimum projects for pattern detection */
172
+ minProjects: z.number().int().min(2).max(50).default(2)
173
+ }).default({}),
174
+
175
+ /** Semantic caching settings */
176
+ cache: z.object({
177
+ /** Enable semantic caching */
178
+ enabled: z.boolean().default(true),
179
+ /** Maximum cache entries */
180
+ maxSize: z.number().int().min(10).max(10000).default(500),
181
+ /** Cache TTL in milliseconds */
182
+ ttlMs: z.number().int().min(60000).default(30 * 60 * 1000),
183
+ /** Semantic similarity threshold for cache hits */
184
+ similarityThreshold: z.number().min(0.5).max(1).default(0.85)
185
+ }).default({})
186
+ })
187
+
188
+ export type AdvancedIntelligenceConfig = z.infer<typeof AdvancedIntelligenceConfigSchema>
189
+
190
+ /** Main configuration schema for Claude Brain */
191
+ export const ConfigSchema = z.object({
192
+ /** Absolute path to the Obsidian vault */
193
+ vaultPath: z.string().min(1, 'Vault path is required'),
194
+
195
+ /** MCP server identifier */
196
+ serverName: z.string().default('claude-brain'),
197
+
198
+ /** Server version in semver format */
199
+ serverVersion: z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be semver format').default('0.3.0'),
200
+
201
+ /** Logging level */
202
+ logLevel: LogLevelSchema.default('info'),
203
+
204
+ /** Path to log file */
205
+ logFilePath: z.string().default('./logs/claude-brain.log'),
206
+
207
+ /** Path to vector database */
208
+ dbPath: z.string().default('./data/memory.db'),
209
+
210
+ /** Port for future HTTP interface */
211
+ port: z.number().int().min(1).max(65535).default(3000),
212
+
213
+ /** Enable file watching for vault changes */
214
+ enableFileWatch: z.boolean().default(true),
215
+
216
+ /** Cache size in MB for context cache */
217
+ cacheSize: z.number().int().min(1).default(100),
218
+
219
+ /** Node environment */
220
+ nodeEnv: z.enum(['development', 'production', 'test']).default('development'),
221
+
222
+ /** ChromaDB configuration */
223
+ chroma: z.object({
224
+ /** Enable ChromaDB vector database */
225
+ enabled: z.boolean().default(true),
226
+ /** ChromaDB mode */
227
+ mode: z.enum(['persistent', 'client-server', 'ephemeral', 'cloud']).default('persistent'),
228
+ /** Path for persistent storage */
229
+ path: z.string().default('./data/chroma'),
230
+ /** Host for client-server or cloud mode */
231
+ host: z.string().optional(),
232
+ /** Port for client-server mode */
233
+ port: z.number().optional(),
234
+ /** Embedding provider */
235
+ embeddingProvider: z.enum(['default', 'transformers', 'openai']).default('default'),
236
+ /** Chroma Cloud or secured instance authentication token */
237
+ chromaAuthToken: z.string().optional(),
238
+ /** Chroma Cloud tenant name */
239
+ chromaTenant: z.string().optional(),
240
+ /** Chroma Cloud database name */
241
+ chromaDatabase: z.string().optional()
242
+ }).default({}),
243
+
244
+ /** Hybrid retrieval configuration */
245
+ retrieval: RetrievalConfigSchema.default({}),
246
+
247
+ /** Knowledge graph and episodic memory configuration */
248
+ knowledge: KnowledgeConfigSchema.default({}),
249
+
250
+ /** Advanced intelligence configuration (Phase 15) */
251
+ advancedIntelligence: AdvancedIntelligenceConfigSchema.default({})
252
+ })
253
+
254
+ export type Config = z.infer<typeof ConfigSchema>
255
+
256
+ /** Partial config for merging with defaults */
257
+ export type PartialConfig = z.input<typeof ConfigSchema>
@@ -0,0 +1,184 @@
1
+ import fs from 'fs/promises'
2
+ import path from 'path'
3
+ import type { Logger } from 'pino'
4
+
5
+ export interface ValidationResult {
6
+ valid: boolean
7
+ errors: ValidationError[]
8
+ warnings: ValidationWarning[]
9
+ }
10
+
11
+ export interface ValidationError {
12
+ field: string
13
+ message: string
14
+ severity: 'error'
15
+ }
16
+
17
+ export interface ValidationWarning {
18
+ field: string
19
+ message: string
20
+ severity: 'warning'
21
+ }
22
+
23
+ export class ConfigValidator {
24
+ private logger: Logger
25
+
26
+ constructor(logger: Logger) {
27
+ this.logger = logger.child({ component: 'config-validator' })
28
+ }
29
+
30
+ async validate(config: any): Promise<ValidationResult> {
31
+ const errors: ValidationError[] = []
32
+ const warnings: ValidationWarning[] = []
33
+
34
+ await this.validateVaultPath(config.vaultPath, errors, warnings)
35
+ await this.validateDatabasePath(config.dbPath, errors, warnings)
36
+ this.validateLogConfig(config, errors, warnings)
37
+ this.validateServerConfig(config, errors, warnings)
38
+
39
+ const valid = errors.length === 0
40
+
41
+ return { valid, errors, warnings }
42
+ }
43
+
44
+ private async validateVaultPath(
45
+ vaultPath: string,
46
+ errors: ValidationError[],
47
+ warnings: ValidationWarning[]
48
+ ): Promise<void> {
49
+ if (!vaultPath) {
50
+ errors.push({
51
+ field: 'vaultPath',
52
+ message: 'Vault path is required',
53
+ severity: 'error'
54
+ })
55
+ return
56
+ }
57
+
58
+ try {
59
+ const stat = await fs.stat(vaultPath)
60
+
61
+ if (!stat.isDirectory()) {
62
+ errors.push({
63
+ field: 'vaultPath',
64
+ message: 'Vault path must be a directory',
65
+ severity: 'error'
66
+ })
67
+ return
68
+ }
69
+
70
+ const projectsPath = path.join(vaultPath, 'Projects')
71
+ try {
72
+ await fs.access(projectsPath)
73
+ } catch {
74
+ warnings.push({
75
+ field: 'vaultPath',
76
+ message: 'Projects directory not found. It will be created automatically.',
77
+ severity: 'warning'
78
+ })
79
+ }
80
+
81
+ try {
82
+ await fs.access(vaultPath, fs.constants.W_OK)
83
+ } catch {
84
+ errors.push({
85
+ field: 'vaultPath',
86
+ message: 'Vault path is not writable',
87
+ severity: 'error'
88
+ })
89
+ }
90
+
91
+ } catch (error) {
92
+ errors.push({
93
+ field: 'vaultPath',
94
+ message: `Vault path does not exist: ${vaultPath}`,
95
+ severity: 'error'
96
+ })
97
+ }
98
+ }
99
+
100
+ private async validateDatabasePath(
101
+ dbPath: string,
102
+ errors: ValidationError[],
103
+ warnings: ValidationWarning[]
104
+ ): Promise<void> {
105
+ if (!dbPath) {
106
+ errors.push({
107
+ field: 'dbPath',
108
+ message: 'Database path is required',
109
+ severity: 'error'
110
+ })
111
+ return
112
+ }
113
+
114
+ const dbDir = path.dirname(dbPath)
115
+
116
+ try {
117
+ await fs.mkdir(dbDir, { recursive: true })
118
+ } catch (error) {
119
+ errors.push({
120
+ field: 'dbPath',
121
+ message: `Cannot create database directory: ${dbDir}`,
122
+ severity: 'error'
123
+ })
124
+ }
125
+ }
126
+
127
+ private validateLogConfig(
128
+ config: any,
129
+ errors: ValidationError[],
130
+ warnings: ValidationWarning[]
131
+ ): void {
132
+ const validLevels = ['error', 'warn', 'info', 'debug']
133
+
134
+ if (config.logLevel && !validLevels.includes(config.logLevel)) {
135
+ warnings.push({
136
+ field: 'logLevel',
137
+ message: `Invalid log level: ${config.logLevel}. Using 'info' as default.`,
138
+ severity: 'warning'
139
+ })
140
+ }
141
+ }
142
+
143
+ private validateServerConfig(
144
+ config: any,
145
+ errors: ValidationError[],
146
+ warnings: ValidationWarning[]
147
+ ): void {
148
+ if (!config.serverName) {
149
+ errors.push({
150
+ field: 'serverName',
151
+ message: 'Server name is required',
152
+ severity: 'error'
153
+ })
154
+ }
155
+
156
+ if (!config.serverVersion) {
157
+ warnings.push({
158
+ field: 'serverVersion',
159
+ message: 'Server version not specified',
160
+ severity: 'warning'
161
+ })
162
+ }
163
+ }
164
+
165
+ async checkSystemRequirements(): Promise<ValidationResult> {
166
+ const errors: ValidationError[] = []
167
+ const warnings: ValidationWarning[] = []
168
+
169
+ const bunVersion = process.versions.bun
170
+ if (!bunVersion) {
171
+ errors.push({
172
+ field: 'runtime',
173
+ message: 'Bun runtime not detected. Claude Brain requires Bun.',
174
+ severity: 'error'
175
+ })
176
+ }
177
+
178
+ return {
179
+ valid: errors.length === 0,
180
+ errors,
181
+ warnings
182
+ }
183
+ }
184
+ }