claude-brain 0.3.7 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -50,6 +50,18 @@ export async function handleUpdateProgress(
50
50
  completedAt: new Date()
51
51
  })
52
52
 
53
+ // Calculate and update completion percentage
54
+ const progressState = await context.progress.getProgress(project_name)
55
+ const totalTasks = progressState.completedTasks.length + progressState.currentTasks.length
56
+ const completionPercentage = totalTasks > 0
57
+ ? Math.round((progressState.completedTasks.length / totalTasks) * 100)
58
+ : 0
59
+
60
+ await context.progress.updateProgress(project_name, {
61
+ completionPercentage,
62
+ currentPhase: completionPercentage >= 100 ? 'complete' : 'active'
63
+ })
64
+
53
65
  const progressFile = await vault.reader.readMarkdownFile(projectPaths.progress)
54
66
  const updatedContent = updateNextStepsSection(progressFile.content, next_steps)
55
67
 
@@ -23,6 +23,17 @@ export async function handleWhatIfAnalysis(
23
23
  const { change, project_name, max_results } = input
24
24
 
25
25
  const memory = getMemoryService()
26
+
27
+ if (!memory.isChromaDBEnabled()) {
28
+ return ResponseFormatter.text(
29
+ `## What-If: "${change}"\n\n` +
30
+ 'Unable to perform what-if analysis.\n\n' +
31
+ 'Note: ChromaDB is not connected. What-if analysis requires ChromaDB for semantic search across decisions. ' +
32
+ 'Decisions stored via SQLite fallback are available through recall_similar. ' +
33
+ 'To enable what-if analysis, start a ChromaDB server or configure persistent mode.'
34
+ )
35
+ }
36
+
26
37
  const kgService = getKnowledgeGraphService()
27
38
  const graph = kgService?.graph || null
28
39
 
@@ -112,9 +112,9 @@ export async function initializeServices(config: Config, logger: Logger): Promis
112
112
  await phase12.initialize()
113
113
  serviceLogger.info('Phase 12 service initialized')
114
114
 
115
- // Initialize Retrieval Service (Phase 13)
115
+ // Initialize Retrieval Service (Phase 13) — requires ChromaDB
116
116
  let retrieval: RetrievalService | null = null
117
- if (config.retrieval?.feedback?.enabled || config.retrieval?.enabled) {
117
+ if ((config.retrieval?.feedback?.enabled || config.retrieval?.enabled) && memory.isChromaDBEnabled()) {
118
118
  retrieval = new RetrievalService(
119
119
  logger,
120
120
  memory.chroma.collections,
@@ -123,6 +123,8 @@ export async function initializeServices(config: Config, logger: Logger): Promis
123
123
  )
124
124
  await retrieval.initialize()
125
125
  serviceLogger.info('Retrieval service initialized')
126
+ } else if (config.retrieval?.enabled && !memory.isChromaDBEnabled()) {
127
+ serviceLogger.warn('Retrieval service requires ChromaDB, skipping initialization')
126
128
  }
127
129
 
128
130
  // Initialize Knowledge Graph Service (Phase 14)
@@ -146,18 +148,21 @@ export async function initializeServices(config: Config, logger: Logger): Promis
146
148
 
147
149
  knowledgeGraph = { graph, search, builder, linker }
148
150
 
149
- // Migrate existing decisions if graph is empty
150
- if (graph.getNodeCount() === 0) {
151
+ // Migrate existing decisions if graph is empty (requires ChromaDB)
152
+ if (graph.getNodeCount() === 0 && memory.isChromaDBEnabled()) {
151
153
  serviceLogger.info('Empty graph detected, migrating existing decisions...')
152
154
  const migrationResult = await builder.migrateExistingDecisions(memory.chroma.collections)
153
155
  serviceLogger.info(
154
156
  { processed: migrationResult.processed, errors: migrationResult.errors },
155
157
  'Initial graph migration complete'
156
158
  )
159
+ } else if (graph.getNodeCount() === 0) {
160
+ serviceLogger.info('Empty graph detected, but ChromaDB unavailable — graph will populate as new decisions are stored')
157
161
  }
158
162
 
159
163
  // Hook builder into decision storage for real-time graph population
160
- memory.chroma.store.addDecisionStoredListener((input) => {
164
+ // Uses MemoryManager listener so it works for both ChromaDB and SQLite paths
165
+ memory.addDecisionStoredListener((input) => {
161
166
  builder.processDecision(input)
162
167
  })
163
168
 
@@ -170,9 +175,9 @@ export async function initializeServices(config: Config, logger: Logger): Promis
170
175
  }
171
176
  }
172
177
 
173
- // Initialize Episode Manager (Phase 14)
178
+ // Initialize Episode Manager (Phase 14) — requires ChromaDB
174
179
  let episodeManager: EpisodeManager | null = null
175
- if (config.knowledge?.episodic?.enabled !== false) {
180
+ if (config.knowledge?.episodic?.enabled !== false && memory.isChromaDBEnabled()) {
176
181
  try {
177
182
  episodeManager = new EpisodeManager(
178
183
  logger,
@@ -186,10 +191,10 @@ export async function initializeServices(config: Config, logger: Logger): Promis
186
191
  }
187
192
  }
188
193
 
189
- // Initialize Semantic Cache & Precompute (Phase 15)
194
+ // Initialize Semantic Cache & Precompute (Phase 15) — requires ChromaDB
190
195
  let semanticCache: SemanticCache | null = null
191
196
  let precompute: PrecomputeEngine | null = null
192
- if (config.advancedIntelligence?.enabled !== false && config.advancedIntelligence?.cache?.enabled !== false) {
197
+ if (config.advancedIntelligence?.enabled !== false && config.advancedIntelligence?.cache?.enabled !== false && memory.isChromaDBEnabled()) {
193
198
  try {
194
199
  const cacheConfig = config.advancedIntelligence?.cache || {}
195
200
  semanticCache = new SemanticCache(logger, {
@@ -1,6 +1,7 @@
1
1
  import prompts from 'prompts'
2
2
  import fs from 'fs/promises'
3
3
  import { existsSync } from 'fs'
4
+ import { execSync } from 'child_process'
4
5
  import path from 'path'
5
6
  import os from 'os'
6
7
  import { fileURLToPath } from 'url'
@@ -21,6 +22,7 @@ export interface SetupAnswers {
21
22
  vaultPath: string
22
23
  logLevel: string
23
24
  enableFileWatch: boolean
25
+ enableChromaDB: boolean
24
26
  createSampleProject: boolean
25
27
  installClaudeMd: boolean
26
28
  }
@@ -34,7 +36,7 @@ export class SetupWizard {
34
36
 
35
37
  async run(): Promise<SetupAnswers> {
36
38
  // Step 1: Detect Vaults
37
- console.log(stepIndicator(1, 5, 'Detecting Obsidian Vaults'))
39
+ console.log(stepIndicator(1, 6, 'Detecting Obsidian Vaults'))
38
40
  await transition()
39
41
 
40
42
  const suggestedPaths = await withSpinner('Scanning for Obsidian vaults', () =>
@@ -49,7 +51,7 @@ export class SetupWizard {
49
51
  console.log()
50
52
 
51
53
  // Step 2: Vault Configuration
52
- console.log(stepIndicator(2, 5, 'Vault Configuration'))
54
+ console.log(stepIndicator(2, 6, 'Vault Configuration'))
53
55
  await transition()
54
56
 
55
57
  const vaultAnswers = await prompts([
@@ -86,7 +88,7 @@ export class SetupWizard {
86
88
  const finalVaultPath = vaultAnswers.vaultPath || vaultAnswers.vaultPathChoice
87
89
 
88
90
  // Step 3: Logging
89
- console.log(stepIndicator(3, 5, 'Logging Configuration'))
91
+ console.log(stepIndicator(3, 6, 'Logging Configuration'))
90
92
  await transition()
91
93
 
92
94
  const loggingAnswers = await prompts({
@@ -103,7 +105,7 @@ export class SetupWizard {
103
105
  })
104
106
 
105
107
  // Step 4: Features
106
- console.log(stepIndicator(4, 5, 'Feature Selection'))
108
+ console.log(stepIndicator(4, 6, 'Feature Selection'))
107
109
  await transition()
108
110
 
109
111
  const featureAnswers = await prompts([
@@ -127,14 +129,40 @@ export class SetupWizard {
127
129
  }
128
130
  ])
129
131
 
130
- // Step 5: Review
131
- console.log(stepIndicator(5, 5, 'Review Configuration'))
132
+ // Step 5: ChromaDB
133
+ console.log(stepIndicator(5, 6, 'ChromaDB (Vector Database)'))
134
+ await transition()
135
+
136
+ console.log(dimText('ChromaDB enables semantic search, knowledge graph,'))
137
+ console.log(dimText('and advanced intelligence features. Requires Python 3.'))
138
+ console.log()
139
+
140
+ const chromaInstalled = this.isChromaInstalled()
141
+ if (chromaInstalled) {
142
+ console.log(successText('ChromaDB CLI detected'))
143
+ } else {
144
+ console.log(warningText('ChromaDB CLI not found — you can install it later'))
145
+ }
146
+ console.log()
147
+
148
+ const chromaAnswers = await prompts({
149
+ type: 'confirm',
150
+ name: 'enableChromaDB',
151
+ message: chromaInstalled
152
+ ? 'Enable ChromaDB for advanced features?'
153
+ : 'Enable ChromaDB? (install later: pip install chromadb)',
154
+ initial: chromaInstalled
155
+ })
156
+
157
+ // Step 6: Review
158
+ console.log(stepIndicator(6, 6, 'Review Configuration'))
132
159
  await transition()
133
160
 
134
161
  const answers: SetupAnswers = {
135
162
  vaultPath: finalVaultPath,
136
163
  logLevel: loggingAnswers.logLevel ?? 'warn',
137
164
  enableFileWatch: featureAnswers.enableFileWatch ?? true,
165
+ enableChromaDB: chromaAnswers.enableChromaDB ?? false,
138
166
  createSampleProject: featureAnswers.createSampleProject ?? true,
139
167
  installClaudeMd: featureAnswers.installClaudeMd ?? true,
140
168
  }
@@ -143,6 +171,7 @@ export class SetupWizard {
143
171
  { label: 'Vault Path', value: answers.vaultPath, status: 'success' },
144
172
  { label: 'Log Level', value: answers.logLevel, status: 'info' },
145
173
  { label: 'File Watching', value: answers.enableFileWatch ? 'Enabled' : 'Disabled', status: answers.enableFileWatch ? 'success' : 'warning' },
174
+ { label: 'ChromaDB', value: answers.enableChromaDB ? 'Enabled' : 'Disabled', status: answers.enableChromaDB ? 'success' : 'warning' },
146
175
  { label: 'Sample Project', value: answers.createSampleProject ? 'Yes' : 'No', status: 'info' },
147
176
  { label: 'Install CLAUDE.md', value: answers.installClaudeMd ? 'Yes' : 'No', status: 'info' },
148
177
  ]))
@@ -191,11 +220,31 @@ export class SetupWizard {
191
220
  return locations
192
221
  }
193
222
 
223
+ private isChromaInstalled(): boolean {
224
+ try {
225
+ execSync('chroma --version', { stdio: 'pipe', timeout: 5000 })
226
+ return true
227
+ } catch {
228
+ return false
229
+ }
230
+ }
231
+
194
232
  async applyConfiguration(answers: SetupAnswers): Promise<void> {
195
233
  console.log('\n' + heading('Applying configuration...') + '\n')
196
234
 
197
235
  const homePaths = getHomePaths()
198
236
 
237
+ const chromaEnvLines = answers.enableChromaDB
238
+ ? [
239
+ '',
240
+ '# ChromaDB Configuration',
241
+ 'CHROMA_MODE=client-server',
242
+ 'CHROMA_HOST=localhost',
243
+ 'CHROMA_PORT=8000',
244
+ 'CHROMA_EMBEDDING_PROVIDER=transformers',
245
+ ].join('\n')
246
+ : ''
247
+
199
248
  const envContent = `# Claude Brain Configuration
200
249
  VAULT_PATH=${answers.vaultPath}
201
250
  LOG_LEVEL=${answers.logLevel}
@@ -203,6 +252,7 @@ ENABLE_FILE_WATCH=${answers.enableFileWatch}
203
252
  DB_PATH=./data/memory.db
204
253
  LOG_FILE_PATH=./logs/claude-brain.log
205
254
  SERVER_NAME=claude-brain
255
+ ${chromaEnvLines}
206
256
  `
207
257
 
208
258
  await withSpinner('Writing .env configuration', async () => {
@@ -211,6 +261,7 @@ SERVER_NAME=claude-brain
211
261
 
212
262
  await withSpinner('Creating data and log directories', async () => {
213
263
  await fs.mkdir(homePaths.data, { recursive: true })
264
+ await fs.mkdir(homePaths.chroma, { recursive: true })
214
265
  await fs.mkdir(homePaths.logs, { recursive: true })
215
266
  })
216
267
 
@@ -229,16 +280,35 @@ SERVER_NAME=claude-brain
229
280
  }
230
281
  }
231
282
 
232
- console.log()
233
- console.log(box([
283
+ const nextSteps: string[] = [
234
284
  heading('Setup complete!'),
235
285
  '',
236
286
  dimText('Next steps:'),
237
287
  ` ${theme.primary('1.')} ${theme.bold('claude-brain install')}`,
238
288
  ` ${dimText('Register as MCP server')}`,
239
- ` ${theme.primary('2.')} ${theme.bold('claude-brain health')}`,
240
- ` ${dimText('Verify everything works')}`,
241
- ].join('\n'), 'Done'))
289
+ ]
290
+
291
+ if (answers.enableChromaDB) {
292
+ nextSteps.push(
293
+ ` ${theme.primary('2.')} ${theme.bold('claude-brain chroma install')}`,
294
+ ` ${dimText('Install ChromaDB (if not already installed)')}`,
295
+ ` ${theme.primary('3.')} ${theme.bold('claude-brain chroma start')}`,
296
+ ` ${dimText('Start the ChromaDB server')}`,
297
+ ` ${theme.primary('4.')} ${theme.bold('claude-brain health')}`,
298
+ ` ${dimText('Verify everything works')}`,
299
+ )
300
+ } else {
301
+ nextSteps.push(
302
+ ` ${theme.primary('2.')} ${theme.bold('claude-brain health')}`,
303
+ ` ${dimText('Verify everything works')}`,
304
+ '',
305
+ dimText('Optional: Enable ChromaDB later for advanced features:'),
306
+ ` ${dimText('claude-brain chroma install && claude-brain chroma start')}`,
307
+ )
308
+ }
309
+
310
+ console.log()
311
+ console.log(box(nextSteps.join('\n'), 'Done'))
242
312
  console.log()
243
313
  }
244
314