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.
- package/README.md +157 -0
- package/VERSION +1 -0
- package/assets/CLAUDE.md +307 -0
- package/bunfig.toml +8 -0
- package/package.json +74 -0
- package/src/automation/auto-context.ts +240 -0
- package/src/automation/decision-detector.ts +452 -0
- package/src/automation/index.ts +11 -0
- package/src/automation/proactive-recall.ts +373 -0
- package/src/automation/project-detector.ts +297 -0
- package/src/cli/auto-setup.ts +74 -0
- package/src/cli/bin.ts +110 -0
- package/src/cli/commands/install-mcp.ts +50 -0
- package/src/cli/commands/serve.ts +129 -0
- package/src/cli/diagnose.ts +4 -0
- package/src/cli/health-check.ts +4 -0
- package/src/cli/migrate-chroma.ts +106 -0
- package/src/cli/setup.ts +4 -0
- package/src/config/defaults.ts +47 -0
- package/src/config/home.ts +55 -0
- package/src/config/index.ts +7 -0
- package/src/config/loader.ts +166 -0
- package/src/config/migration.ts +76 -0
- package/src/config/schema.ts +257 -0
- package/src/config/validator.ts +184 -0
- package/src/config/watcher.ts +86 -0
- package/src/context/assembler.ts +398 -0
- package/src/context/cache-manager.ts +101 -0
- package/src/context/formatter.ts +84 -0
- package/src/context/hierarchy.ts +85 -0
- package/src/context/index.ts +83 -0
- package/src/context/progress-tracker.ts +174 -0
- package/src/context/standards-manager.ts +267 -0
- package/src/context/types.ts +252 -0
- package/src/context/validator.ts +58 -0
- package/src/cross-project/affinity.ts +162 -0
- package/src/cross-project/generalizer.ts +283 -0
- package/src/cross-project/index.ts +13 -0
- package/src/cross-project/transfer.ts +201 -0
- package/src/diagnostics/index.ts +123 -0
- package/src/health/index.ts +229 -0
- package/src/index.ts +7 -0
- package/src/knowledge/entity-extractor.ts +416 -0
- package/src/knowledge/graph/builder.ts +159 -0
- package/src/knowledge/graph/linker.ts +201 -0
- package/src/knowledge/graph/memory-graph.ts +359 -0
- package/src/knowledge/graph/schema.ts +99 -0
- package/src/knowledge/graph/search.ts +168 -0
- package/src/knowledge/relationship-extractor.ts +108 -0
- package/src/memory/chroma/client.ts +169 -0
- package/src/memory/chroma/collection-manager.ts +94 -0
- package/src/memory/chroma/config.ts +46 -0
- package/src/memory/chroma/embeddings.ts +153 -0
- package/src/memory/chroma/index.ts +82 -0
- package/src/memory/chroma/migration.ts +270 -0
- package/src/memory/chroma/schemas.ts +69 -0
- package/src/memory/chroma/search.ts +315 -0
- package/src/memory/chroma/store.ts +694 -0
- package/src/memory/consolidation/archiver.ts +164 -0
- package/src/memory/consolidation/merger.ts +186 -0
- package/src/memory/consolidation/scorer.ts +138 -0
- package/src/memory/context-builder.ts +236 -0
- package/src/memory/database.ts +169 -0
- package/src/memory/embedding-utils.ts +156 -0
- package/src/memory/embeddings.ts +226 -0
- package/src/memory/episodic/detector.ts +108 -0
- package/src/memory/episodic/manager.ts +334 -0
- package/src/memory/episodic/summarizer.ts +179 -0
- package/src/memory/episodic/types.ts +52 -0
- package/src/memory/index.ts +395 -0
- package/src/memory/knowledge-extractor.ts +455 -0
- package/src/memory/learning.ts +378 -0
- package/src/memory/patterns.ts +396 -0
- package/src/memory/schema.ts +56 -0
- package/src/memory/search.ts +309 -0
- package/src/memory/store.ts +344 -0
- package/src/memory/types.ts +121 -0
- package/src/optimization/index.ts +10 -0
- package/src/optimization/precompute.ts +202 -0
- package/src/optimization/semantic-cache.ts +207 -0
- package/src/orchestrator/coordinator.ts +272 -0
- package/src/orchestrator/decision-logger.ts +228 -0
- package/src/orchestrator/event-emitter.ts +198 -0
- package/src/orchestrator/event-queue.ts +184 -0
- package/src/orchestrator/handlers/base-handler.ts +70 -0
- package/src/orchestrator/handlers/context-handler.ts +73 -0
- package/src/orchestrator/handlers/decision-handler.ts +204 -0
- package/src/orchestrator/handlers/index.ts +10 -0
- package/src/orchestrator/handlers/status-handler.ts +131 -0
- package/src/orchestrator/handlers/task-handler.ts +171 -0
- package/src/orchestrator/index.ts +275 -0
- package/src/orchestrator/task-parser.ts +284 -0
- package/src/orchestrator/types.ts +98 -0
- package/src/phase12/index.ts +456 -0
- package/src/prediction/context-anticipator.ts +198 -0
- package/src/prediction/decision-predictor.ts +184 -0
- package/src/prediction/index.ts +13 -0
- package/src/prediction/recommender.ts +268 -0
- package/src/reasoning/chain-retrieval.ts +247 -0
- package/src/reasoning/counterfactual.ts +248 -0
- package/src/reasoning/index.ts +13 -0
- package/src/reasoning/synthesizer.ts +169 -0
- package/src/retrieval/bm25/index.ts +300 -0
- package/src/retrieval/bm25/tokenizer.ts +184 -0
- package/src/retrieval/feedback/adaptive.ts +223 -0
- package/src/retrieval/feedback/index.ts +16 -0
- package/src/retrieval/feedback/metrics.ts +223 -0
- package/src/retrieval/feedback/store.ts +283 -0
- package/src/retrieval/fusion/index.ts +194 -0
- package/src/retrieval/fusion/rrf.ts +163 -0
- package/src/retrieval/index.ts +12 -0
- package/src/retrieval/pipeline.ts +375 -0
- package/src/retrieval/query/expander.ts +198 -0
- package/src/retrieval/query/index.ts +27 -0
- package/src/retrieval/query/intent-classifier.ts +236 -0
- package/src/retrieval/query/temporal-parser.ts +295 -0
- package/src/retrieval/reranker/index.ts +188 -0
- package/src/retrieval/reranker/model.ts +95 -0
- package/src/retrieval/service.ts +125 -0
- package/src/retrieval/types.ts +162 -0
- package/src/scripts/health-check.ts +118 -0
- package/src/scripts/setup.ts +122 -0
- package/src/server/handlers/call-tool.ts +194 -0
- package/src/server/handlers/index.ts +9 -0
- package/src/server/handlers/list-tools.ts +18 -0
- package/src/server/handlers/tools/analyze-decision-evolution.ts +71 -0
- package/src/server/handlers/tools/auto-remember.ts +200 -0
- package/src/server/handlers/tools/create-project.ts +135 -0
- package/src/server/handlers/tools/detect-trends.ts +80 -0
- package/src/server/handlers/tools/find-cross-project-patterns.ts +73 -0
- package/src/server/handlers/tools/get-activity-log.ts +194 -0
- package/src/server/handlers/tools/get-code-standards.ts +124 -0
- package/src/server/handlers/tools/get-corrections.ts +154 -0
- package/src/server/handlers/tools/get-decision-timeline.ts +86 -0
- package/src/server/handlers/tools/get-episode.ts +93 -0
- package/src/server/handlers/tools/get-patterns.ts +158 -0
- package/src/server/handlers/tools/get-phase12-status.ts +63 -0
- package/src/server/handlers/tools/get-project-context.ts +75 -0
- package/src/server/handlers/tools/get-recommendations.ts +65 -0
- package/src/server/handlers/tools/index.ts +33 -0
- package/src/server/handlers/tools/init-project.ts +710 -0
- package/src/server/handlers/tools/list-episodes.ts +80 -0
- package/src/server/handlers/tools/list-projects.ts +125 -0
- package/src/server/handlers/tools/rate-memory.ts +95 -0
- package/src/server/handlers/tools/recall-similar.ts +87 -0
- package/src/server/handlers/tools/recognize-pattern.ts +126 -0
- package/src/server/handlers/tools/record-correction.ts +125 -0
- package/src/server/handlers/tools/remember-decision.ts +153 -0
- package/src/server/handlers/tools/schemas.ts +241 -0
- package/src/server/handlers/tools/search-knowledge-graph.ts +89 -0
- package/src/server/handlers/tools/smart-context.ts +124 -0
- package/src/server/handlers/tools/update-progress.ts +114 -0
- package/src/server/handlers/tools/what-if-analysis.ts +73 -0
- package/src/server/http-api.ts +474 -0
- package/src/server/index.ts +40 -0
- package/src/server/mcp-server.ts +283 -0
- package/src/server/providers/index.ts +7 -0
- package/src/server/providers/prompts.ts +327 -0
- package/src/server/providers/resources.ts +427 -0
- package/src/server/services.ts +388 -0
- package/src/server/types.ts +39 -0
- package/src/server/utils/error-handler.ts +155 -0
- package/src/server/utils/index.ts +13 -0
- package/src/server/utils/memory-indicator.ts +83 -0
- package/src/server/utils/request-context.ts +122 -0
- package/src/server/utils/response-formatter.ts +124 -0
- package/src/server/utils/validators.ts +210 -0
- package/src/setup/index.ts +22 -0
- package/src/setup/wizard.ts +321 -0
- package/src/temporal/evolution.ts +197 -0
- package/src/temporal/index.ts +16 -0
- package/src/temporal/query-processor.ts +190 -0
- package/src/temporal/timeline.ts +259 -0
- package/src/temporal/trends.ts +263 -0
- package/src/tools/index.ts +24 -0
- package/src/tools/registry.ts +106 -0
- package/src/tools/schemas.test.ts +30 -0
- package/src/tools/schemas.ts +907 -0
- package/src/tools/types.ts +412 -0
- package/src/utils/circuit-breaker.ts +130 -0
- package/src/utils/cleanup.ts +34 -0
- package/src/utils/error-handler.ts +132 -0
- package/src/utils/error-messages.ts +60 -0
- package/src/utils/fallback.ts +45 -0
- package/src/utils/index.ts +54 -0
- package/src/utils/logger-utils.ts +80 -0
- package/src/utils/logger.ts +88 -0
- package/src/utils/phase12-helper.ts +56 -0
- package/src/utils/retry.ts +94 -0
- package/src/utils/transaction.ts +63 -0
- package/src/vault/frontmatter.ts +264 -0
- package/src/vault/index.ts +318 -0
- package/src/vault/paths.ts +106 -0
- package/src/vault/query.ts +422 -0
- package/src/vault/reader.ts +264 -0
- package/src/vault/templates.ts +186 -0
- package/src/vault/types.ts +73 -0
- package/src/vault/watcher.ts +277 -0
- package/src/vault/writer.ts +393 -0
- 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
|
+
}
|