claude-brain 0.17.10 → 0.17.12

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 (107) hide show
  1. package/VERSION +1 -1
  2. package/package.json +4 -5
  3. package/scripts/postinstall.mjs +43 -16
  4. package/src/automation/decision-detector.ts +2 -2
  5. package/src/automation/proactive-recall.ts +2 -2
  6. package/src/automation/project-detector.ts +10 -10
  7. package/src/automation/repo-scanner.ts +2 -2
  8. package/src/cli/bin.ts +20 -7
  9. package/src/cli/commands/chroma.ts +5 -1
  10. package/src/cli/commands/git-hook.ts +15 -21
  11. package/src/cli/commands/hooks.ts +5 -1
  12. package/src/cli/commands/init.ts +7 -6
  13. package/src/cli/commands/pack.ts +11 -8
  14. package/src/cli/commands/refresh.ts +2 -2
  15. package/src/cli/commands/serve.ts +1 -1
  16. package/src/cli/commands/start.ts +5 -1
  17. package/src/cli/commands/update.ts +2 -2
  18. package/src/cli/migrate-chroma.ts +1 -1
  19. package/src/config/defaults.ts +3 -1
  20. package/src/config/index.ts +0 -2
  21. package/src/config/loader.ts +36 -38
  22. package/src/config/schema.ts +30 -49
  23. package/src/config/validator.ts +4 -6
  24. package/src/context/assembler.ts +20 -18
  25. package/src/context/index.ts +0 -4
  26. package/src/context/progress-tracker.ts +9 -9
  27. package/src/context/standards-manager.ts +23 -23
  28. package/src/context/validator.ts +1 -1
  29. package/src/diagnostics/index.ts +2 -4
  30. package/src/health/index.ts +2 -2
  31. package/src/hooks/passive-classifier.ts +0 -7
  32. package/src/intelligence/cross-project/affinity.ts +4 -7
  33. package/src/intelligence/cross-project/generalizer.ts +1 -1
  34. package/src/intelligence/optimization/semantic-cache.ts +4 -4
  35. package/src/intelligence/prediction/decision-predictor.ts +2 -2
  36. package/src/intelligence/reasoning/counterfactual.ts +1 -1
  37. package/src/intelligence/reasoning/synthesizer.ts +3 -5
  38. package/src/intelligence/temporal/evolution.ts +6 -6
  39. package/src/intelligence/temporal/query-processor.ts +3 -3
  40. package/src/intelligence/temporal/trends.ts +2 -2
  41. package/src/knowledge/entity-extractor.ts +1 -4
  42. package/src/knowledge/graph/builder.ts +3 -3
  43. package/src/knowledge/graph/linker.ts +7 -7
  44. package/src/knowledge/graph/search.ts +1 -3
  45. package/src/memory/chroma/collection-manager.ts +1 -3
  46. package/src/memory/chroma/embeddings.ts +5 -7
  47. package/src/memory/chroma/migration.ts +1 -1
  48. package/src/memory/chroma/search.ts +6 -6
  49. package/src/memory/chroma/store.ts +7 -4
  50. package/src/memory/consolidation/archiver.ts +3 -2
  51. package/src/memory/consolidation/merger.ts +4 -4
  52. package/src/memory/consolidation/scorer.ts +1 -3
  53. package/src/memory/episodic/manager.ts +2 -2
  54. package/src/memory/index.ts +13 -4
  55. package/src/memory/patterns.ts +6 -4
  56. package/src/retrieval/bm25/index.ts +4 -7
  57. package/src/retrieval/feedback/adaptive.ts +2 -4
  58. package/src/retrieval/feedback/metrics.ts +3 -5
  59. package/src/retrieval/fusion/index.ts +1 -1
  60. package/src/retrieval/fusion/rrf.ts +6 -4
  61. package/src/retrieval/pipeline.ts +6 -6
  62. package/src/retrieval/query/intent-classifier.ts +3 -3
  63. package/src/retrieval/query/temporal-parser.ts +7 -7
  64. package/src/retrieval/reranker/index.ts +2 -2
  65. package/src/retrieval/service.ts +1 -1
  66. package/src/routing/intent-classifier.ts +13 -4
  67. package/src/routing/router.ts +32 -19
  68. package/src/routing/search-engine.ts +4 -5
  69. package/src/scripts/health-check.ts +1 -1
  70. package/src/scripts/setup.ts +3 -3
  71. package/src/server/handlers/call-tool.ts +0 -4
  72. package/src/server/handlers/tools/auto-remember.ts +4 -39
  73. package/src/server/handlers/tools/get-code-standards.ts +0 -1
  74. package/src/server/handlers/tools/get-corrections.ts +1 -3
  75. package/src/server/handlers/tools/get-patterns.ts +1 -3
  76. package/src/server/handlers/tools/index.ts +1 -5
  77. package/src/server/handlers/tools/init-project.ts +8 -9
  78. package/src/server/handlers/tools/list-projects.ts +1 -0
  79. package/src/server/handlers/tools/recall-similar.ts +1 -1
  80. package/src/server/handlers/tools/recognize-pattern.ts +7 -1
  81. package/src/server/handlers/tools/record-correction.ts +7 -1
  82. package/src/server/handlers/tools/remember-decision.ts +17 -2
  83. package/src/server/handlers/tools/schemas.ts +0 -90
  84. package/src/server/handlers/tools/smart-context.ts +1 -1
  85. package/src/server/handlers/tools/update-progress.ts +2 -2
  86. package/src/server/http-api.ts +20 -38
  87. package/src/server/mcp-server.ts +2 -1
  88. package/src/server/providers/resources.ts +6 -6
  89. package/src/server/services.ts +67 -2
  90. package/src/setup/wizard.ts +2 -4
  91. package/src/utils/transaction.ts +1 -1
  92. package/src/cli/diagnose.ts +0 -4
  93. package/src/cli/health-check.ts +0 -4
  94. package/src/cli/setup.ts +0 -4
  95. package/src/config/migration.ts +0 -76
  96. package/src/config/watcher.ts +0 -86
  97. package/src/server/handlers/tools/analyze-decision-evolution.ts +0 -151
  98. package/src/server/handlers/tools/detect-trends.ts +0 -144
  99. package/src/server/handlers/tools/find-cross-project-patterns.ts +0 -168
  100. package/src/server/handlers/tools/get-activity-log.ts +0 -194
  101. package/src/server/handlers/tools/get-decision-timeline.ts +0 -172
  102. package/src/server/handlers/tools/get-episode.ts +0 -103
  103. package/src/server/handlers/tools/get-recommendations.ts +0 -145
  104. package/src/server/handlers/tools/list-episodes.ts +0 -90
  105. package/src/server/handlers/tools/rate-memory.ts +0 -101
  106. package/src/server/handlers/tools/search-knowledge-graph.ts +0 -102
  107. package/src/server/handlers/tools/what-if-analysis.ts +0 -135
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.17.10
1
+ 0.17.12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-brain",
3
- "version": "0.17.10",
3
+ "version": "0.17.12",
4
4
  "description": "Local development assistant bridging Obsidian vaults with Claude Code via MCP",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -53,9 +53,8 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/bun": "latest",
56
- "@types/node": "^20",
57
- "@types/prompts": "2.4.9",
58
- "daisyui": "^5.5.14"
56
+ "@types/node": "25.2.2",
57
+ "@types/prompts": "2.4.9"
59
58
  },
60
59
  "peerDependencies": {
61
60
  "typescript": "^5"
@@ -66,8 +65,8 @@
66
65
  "@xenova/transformers": "2.17.2",
67
66
  "chalk": "5.6.2",
68
67
  "chromadb": "3.2.2",
69
- "chromadb-default-embed": "2.14.0",
70
68
  "chrono-node": "2.9.0",
69
+ "citty": "0.2.0",
71
70
  "compromise": "^14.10.0",
72
71
  "dotenv": "^17.2.3",
73
72
  "gray-matter": "^4.0.3",
@@ -260,32 +260,59 @@ function installHooks() {
260
260
  )
261
261
  }
262
262
 
263
- if (settings.hooks && (hasOurHooks(settings.hooks.PostToolUse) || hasOurHooks(settings.hooks.Stop))) {
263
+ if (settings.hooks &&
264
+ hasOurHooks(settings.hooks.PostToolUse) &&
265
+ hasOurHooks(settings.hooks.Stop) &&
266
+ hasOurHooks(settings.hooks.UserPromptSubmit) &&
267
+ hasOurHooks(settings.hooks.SessionStart)) {
264
268
  log('Hooks already installed')
265
269
  return true
266
270
  }
267
271
 
268
272
  // Build hook command
269
- const scriptPath = join(HOME, 'hooks', 'brain-hook.ts')
270
- function buildCmd(event) {
273
+ const brainScriptPath = join(HOME, 'hooks', 'brain-hook.ts')
274
+ const contextScriptPath = join(HOME, 'hooks', 'context-hook.ts')
275
+ function buildCmd(event, scriptPath) {
271
276
  return `bun "${scriptPath}" --event ${event} # ${HOOK_MARKER}`
272
277
  }
273
278
 
274
279
  if (!settings.hooks) settings.hooks = {}
275
280
 
276
- // PostToolUse
277
- if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = []
278
- settings.hooks.PostToolUse.push({
279
- matcher: '',
280
- hooks: [{ type: 'command', command: buildCmd('PostToolUse') }],
281
- })
282
-
283
- // Stop
284
- if (!settings.hooks.Stop) settings.hooks.Stop = []
285
- settings.hooks.Stop.push({
286
- matcher: '',
287
- hooks: [{ type: 'command', command: buildCmd('Stop') }],
288
- })
281
+ // PostToolUse — captures tool events
282
+ if (!hasOurHooks(settings.hooks.PostToolUse)) {
283
+ if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = []
284
+ settings.hooks.PostToolUse.push({
285
+ matcher: '',
286
+ hooks: [{ type: 'command', command: buildCmd('PostToolUse', brainScriptPath) }],
287
+ })
288
+ }
289
+
290
+ // Stop — triggers session-end summary
291
+ if (!hasOurHooks(settings.hooks.Stop)) {
292
+ if (!settings.hooks.Stop) settings.hooks.Stop = []
293
+ settings.hooks.Stop.push({
294
+ matcher: '',
295
+ hooks: [{ type: 'command', command: buildCmd('Stop', brainScriptPath) }],
296
+ })
297
+ }
298
+
299
+ // UserPromptSubmit — injects relevant memories into every prompt
300
+ if (!hasOurHooks(settings.hooks.UserPromptSubmit)) {
301
+ if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = []
302
+ settings.hooks.UserPromptSubmit.push({
303
+ matcher: '',
304
+ hooks: [{ type: 'command', command: buildCmd('UserPromptSubmit', contextScriptPath) }],
305
+ })
306
+ }
307
+
308
+ // SessionStart — injects project context on session start/resume
309
+ if (!hasOurHooks(settings.hooks.SessionStart)) {
310
+ if (!settings.hooks.SessionStart) settings.hooks.SessionStart = []
311
+ settings.hooks.SessionStart.push({
312
+ matcher: 'startup,resume,compact',
313
+ hooks: [{ type: 'command', command: buildCmd('SessionStart', contextScriptPath) }],
314
+ })
315
+ }
289
316
 
290
317
  // Write atomically
291
318
  if (!existsSync(CLAUDE_DIR)) {
@@ -81,7 +81,7 @@ export class DecisionDetector {
81
81
  /**
82
82
  * Detect decision from text
83
83
  */
84
- detectDecision(text: string, project?: string): DetectedDecision | null {
84
+ detectDecision(text: string, _project?: string): DetectedDecision | null {
85
85
  const textLower = text.toLowerCase()
86
86
 
87
87
  // Check for decision phrases
@@ -133,7 +133,7 @@ export class DecisionDetector {
133
133
 
134
134
  // Find sentence with decision phrase
135
135
  for (let i = 0; i < sentences.length; i++) {
136
- const sentence = sentences[i].toLowerCase()
136
+ const sentence = (sentences[i] ?? "").toLowerCase()
137
137
 
138
138
  if (this.DECISION_PHRASES.some(p => sentence.includes(p))) {
139
139
  // Context is likely in previous sentences
@@ -148,7 +148,7 @@ export class ProactiveRecallEngine {
148
148
  // Also extract potential compound terms
149
149
  const compounds: string[] = []
150
150
  for (let i = 0; i < words.length - 1; i++) {
151
- if (words[i].length > 3 && words[i + 1].length > 3) {
151
+ if (words[i]!.length > 3 && words[i + 1]!.length > 3) {
152
152
  compounds.push(`${words[i]} ${words[i + 1]}`)
153
153
  }
154
154
  }
@@ -256,7 +256,7 @@ export class ProactiveRecallEngine {
256
256
  /**
257
257
  * Calculate relevance score
258
258
  */
259
- private calculateRelevance(query: string, memories: MemorySearchResult[]): number {
259
+ private calculateRelevance(_query: string, memories: MemorySearchResult[]): number {
260
260
  if (memories.length === 0) return 0
261
261
 
262
262
  // Average similarity score
@@ -112,7 +112,7 @@ export class ProjectDetector {
112
112
 
113
113
  const nameMatch = pyprojectContent.match(/name\s*=\s*"([^"]+)"/)
114
114
  if (nameMatch) {
115
- return nameMatch[1]
115
+ return nameMatch[1] ?? null
116
116
  }
117
117
  } catch {
118
118
  // No pyproject.toml
@@ -125,7 +125,7 @@ export class ProjectDetector {
125
125
 
126
126
  const nameMatch = cargoContent.match(/name\s*=\s*"([^"]+)"/)
127
127
  if (nameMatch) {
128
- return nameMatch[1]
128
+ return nameMatch[1] ?? null
129
129
  }
130
130
  } catch {
131
131
  // No Cargo.toml
@@ -139,7 +139,7 @@ export class ProjectDetector {
139
139
  // Extract repo name from URL
140
140
  const urlMatch = gitConfig.match(/url\s*=\s*.+\/([^/\n]+?)(?:\.git)?[\s\n]/)
141
141
  if (urlMatch) {
142
- return urlMatch[1].trim()
142
+ return urlMatch[1]?.trim() ?? null
143
143
  }
144
144
  } catch {
145
145
  // No .git/config
@@ -243,24 +243,24 @@ export class ProjectDetector {
243
243
  }
244
244
 
245
245
  for (let j = 0; j <= a.length; j++) {
246
- matrix[0][j] = j
246
+ matrix[0]![j] = j
247
247
  }
248
248
 
249
249
  for (let i = 1; i <= b.length; i++) {
250
250
  for (let j = 1; j <= a.length; j++) {
251
251
  if (b.charAt(i - 1) === a.charAt(j - 1)) {
252
- matrix[i][j] = matrix[i - 1][j - 1]
252
+ matrix[i]![j] = matrix[i - 1]![j - 1]!
253
253
  } else {
254
- matrix[i][j] = Math.min(
255
- matrix[i - 1][j - 1] + 1,
256
- matrix[i][j - 1] + 1,
257
- matrix[i - 1][j] + 1
254
+ matrix[i]![j] = Math.min(
255
+ matrix[i - 1]![j - 1]! + 1,
256
+ matrix[i]![j - 1]! + 1,
257
+ matrix[i - 1]![j]! + 1
258
258
  )
259
259
  }
260
260
  }
261
261
  }
262
262
 
263
- return matrix[b.length][a.length]
263
+ return matrix[b.length]![a.length]!
264
264
  }
265
265
 
266
266
  /**
@@ -153,9 +153,9 @@ async function getDirectoryTree(repoPath: string, maxDepth: number): Promise<str
153
153
  async function walk(dir: string, depth: number, prefix: string) {
154
154
  if (depth > maxDepth) return
155
155
 
156
- let entries: Awaited<ReturnType<typeof fs.readdir>>
156
+ let entries: import("fs").Dirent[]
157
157
  try {
158
- entries = await fs.readdir(dir, { withFileTypes: true })
158
+ entries = await fs.readdir(dir, { withFileTypes: true, encoding: 'utf-8' })
159
159
  } catch {
160
160
  return
161
161
  }
package/src/cli/bin.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  import { readFileSync } from 'node:fs'
4
4
  import { resolve, dirname } from 'node:path'
5
5
  import { fileURLToPath } from 'node:url'
6
+ import { parseArgs } from 'citty'
6
7
  import { renderLogo, theme, dimText, box, errorText } from '@/cli/ui/index.js'
7
8
 
8
9
  const __filename = fileURLToPath(import.meta.url)
@@ -77,7 +78,23 @@ function printHelp() {
77
78
  }
78
79
 
79
80
  async function main() {
80
- const command = process.argv[2] || 'serve'
81
+ const args = parseArgs(process.argv.slice(2), {
82
+ command: { type: 'positional', required: false, description: 'Command to run' },
83
+ version: { type: 'boolean', alias: ['v'], description: 'Show version' },
84
+ help: { type: 'boolean', alias: ['h'], description: 'Show help' },
85
+ })
86
+
87
+ if (args.version) {
88
+ console.log(getVersion())
89
+ return
90
+ }
91
+
92
+ if (args.help) {
93
+ printHelp()
94
+ return
95
+ }
96
+
97
+ const command: string = args.command ?? 'serve'
81
98
 
82
99
  switch (command) {
83
100
  case 'start': {
@@ -179,16 +196,12 @@ async function main() {
179
196
  break
180
197
  }
181
198
 
182
- case 'version':
183
- case '-v':
184
- case '--version': {
199
+ case 'version': {
185
200
  console.log(getVersion())
186
201
  break
187
202
  }
188
203
 
189
- case 'help':
190
- case '-h':
191
- case '--help': {
204
+ case 'help': {
192
205
  printHelp()
193
206
  break
194
207
  }
@@ -6,6 +6,7 @@
6
6
  import { spawn, execSync } from 'node:child_process'
7
7
  import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs'
8
8
  import { join } from 'node:path'
9
+ import { parseArgs } from 'citty'
9
10
  import { getHomePaths } from '@/config/home'
10
11
  import {
11
12
  theme, heading, successText, errorText, warningText, dimText,
@@ -544,7 +545,10 @@ export async function ensureChromaRunning(options?: { silent?: boolean }): Promi
544
545
  // ── Entry Point ───────────────────────────────────────────
545
546
 
546
547
  export async function runChroma(): Promise<void> {
547
- const subcommand = process.argv[3] || 'help'
548
+ const args = parseArgs(process.argv.slice(3), {
549
+ subcommand: { type: 'positional', required: false, description: 'start|stop|status|install|help' },
550
+ })
551
+ const subcommand: string = args.subcommand ?? 'help'
548
552
 
549
553
  switch (subcommand) {
550
554
  case 'start':
@@ -3,6 +3,7 @@
3
3
  * Manages git post-commit hook installation (install/uninstall/status)
4
4
  */
5
5
 
6
+ import { parseArgs } from 'citty'
6
7
  import {
7
8
  renderLogo, theme, heading, successText, warningText, errorText, dimText,
8
9
  box, summaryPanel, withSpinner,
@@ -12,17 +13,22 @@ import {
12
13
  } from '@/hooks/git-hook-installer'
13
14
 
14
15
  export async function runGitHook() {
15
- const subcommand = process.argv[3] || 'status'
16
+ const args = parseArgs(process.argv.slice(3), {
17
+ subcommand: { type: 'positional', required: false, description: 'install|uninstall|status' },
18
+ path: { type: 'positional', required: false, description: 'Repository path' },
19
+ all: { type: 'string', required: false, description: 'Install in all repos under directory' },
20
+ })
21
+ const subcommand: string = args.subcommand ?? 'status'
16
22
 
17
23
  switch (subcommand) {
18
24
  case 'install':
19
- await handleInstall()
25
+ await handleInstall(args.all, args.path)
20
26
  break
21
27
  case 'uninstall':
22
- await handleUninstall()
28
+ await handleUninstall(args.path)
23
29
  break
24
30
  case 'status':
25
- handleStatus()
31
+ handleStatus(args.path)
26
32
  break
27
33
  default:
28
34
  console.log()
@@ -32,21 +38,15 @@ export async function runGitHook() {
32
38
  }
33
39
  }
34
40
 
35
- async function handleInstall() {
41
+ async function handleInstall(allDir?: string, repoPath?: string) {
36
42
  console.log()
37
43
  console.log(renderLogo())
38
44
  console.log()
39
45
  console.log(heading('Install Git Hook'))
40
46
  console.log()
41
47
 
42
- // Check for --all <dir> flag
43
- const allIdx = process.argv.indexOf('--all')
44
- if (allIdx >= 0) {
45
- const dir = process.argv[allIdx + 1]
46
- if (!dir) {
47
- console.log(errorText('--all requires a directory path'))
48
- process.exit(1)
49
- }
48
+ if (allDir) {
49
+ const dir = allDir
50
50
 
51
51
  let results: { installed: number; skipped: number; errors: number } | undefined
52
52
  await withSpinner(`Installing git hooks in all repos under ${dir}`, async () => {
@@ -65,9 +65,6 @@ async function handleInstall() {
65
65
  return
66
66
  }
67
67
 
68
- // Single repo install (current directory or specified path)
69
- const repoPath = process.argv[4] || undefined
70
-
71
68
  try {
72
69
  let result: { installed: boolean; message: string; hookPath: string } | undefined
73
70
  await withSpinner('Installing post-commit hook', async () => {
@@ -99,15 +96,13 @@ async function handleInstall() {
99
96
  console.log()
100
97
  }
101
98
 
102
- async function handleUninstall() {
99
+ async function handleUninstall(repoPath?: string) {
103
100
  console.log()
104
101
  console.log(renderLogo())
105
102
  console.log()
106
103
  console.log(heading('Uninstall Git Hook'))
107
104
  console.log()
108
105
 
109
- const repoPath = process.argv[4] || undefined
110
-
111
106
  try {
112
107
  let result: { uninstalled: boolean; message: string } | undefined
113
108
  await withSpinner('Removing post-commit hook', async () => {
@@ -129,14 +124,13 @@ async function handleUninstall() {
129
124
  console.log()
130
125
  }
131
126
 
132
- function handleStatus() {
127
+ function handleStatus(repoPath?: string) {
133
128
  console.log()
134
129
  console.log(renderLogo())
135
130
  console.log()
136
131
  console.log(heading('Git Hook Status'))
137
132
  console.log()
138
133
 
139
- const repoPath = process.argv[4] || undefined
140
134
  const installed = isGitHookInstalled(repoPath)
141
135
 
142
136
  const items = [
@@ -3,6 +3,7 @@
3
3
  * Manages passive learning hooks (install/uninstall/status/enable/disable)
4
4
  */
5
5
 
6
+ import { parseArgs } from 'citty'
6
7
  import {
7
8
  renderLogo, theme, heading, successText, warningText, errorText, dimText,
8
9
  box, summaryPanel, withSpinner,
@@ -11,7 +12,10 @@ import { installHooks, uninstallHooks, isHooksInstalled, getHookScriptPath } fro
11
12
  import { readQueue } from '@/hooks/queue'
12
13
 
13
14
  export async function runHooks() {
14
- const subcommand = process.argv[3] || 'status'
15
+ const args = parseArgs(process.argv.slice(3), {
16
+ subcommand: { type: 'positional', required: false, description: 'install|uninstall|status|enable|disable' },
17
+ })
18
+ const subcommand: string = args.subcommand ?? 'status'
15
19
 
16
20
  switch (subcommand) {
17
21
  case 'install':
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import path from 'path'
9
+ import { parseArgs } from 'citty'
9
10
  import {
10
11
  renderLogo, theme, heading, successText, warningText, dimText,
11
12
  box, withSpinner,
@@ -19,12 +20,12 @@ export async function runInit() {
19
20
  console.log(heading('Initialize Project'))
20
21
  console.log()
21
22
 
22
- // Parse arguments
23
- const args = process.argv.slice(3)
24
- const force = args.includes('--force')
25
- const repoPath = path.resolve(
26
- args.find(a => !a.startsWith('--')) || process.cwd()
27
- )
23
+ const args = parseArgs(process.argv.slice(3), {
24
+ path: { type: 'positional', required: false, description: 'Path to project directory' },
25
+ force: { type: 'boolean', default: false, description: 'Force re-scan even without project markers' },
26
+ })
27
+ const force = args.force
28
+ const repoPath = path.resolve(args.path || process.cwd())
28
29
 
29
30
  console.log(` ${theme.bold('Path:')} ${dimText(repoPath)}`)
30
31
  if (force) {
@@ -3,11 +3,11 @@
3
3
  * Manages knowledge packs (list/status/reload)
4
4
  */
5
5
 
6
+ import { parseArgs } from 'citty'
6
7
  import {
7
8
  renderLogo, theme, heading, successText, warningText, errorText, dimText,
8
9
  box, summaryPanel, withSpinner,
9
10
  } from '@/cli/ui/index.js'
10
- import { readFileSync } from 'node:fs'
11
11
  import { resolve, dirname, join } from 'node:path'
12
12
  import { fileURLToPath } from 'node:url'
13
13
  import { PackManager } from '@/packs/manager'
@@ -49,17 +49,22 @@ function createPackManager(): PackManager {
49
49
  }
50
50
 
51
51
  export async function runPack() {
52
- const subcommand = process.argv[3] || 'list'
52
+ const args = parseArgs(process.argv.slice(3), {
53
+ subcommand: { type: 'positional', required: false, description: 'list|status|reload' },
54
+ project: { type: 'positional', required: false, description: 'Project name' },
55
+ })
56
+
57
+ const subcommand: string = args.subcommand ?? 'list'
53
58
 
54
59
  switch (subcommand) {
55
60
  case 'list':
56
61
  await handleList()
57
62
  break
58
63
  case 'status':
59
- await handleStatus()
64
+ await handleStatus(args.project)
60
65
  break
61
66
  case 'reload':
62
- await handleReload()
67
+ await handleReload(args.project)
63
68
  break
64
69
  default:
65
70
  console.log()
@@ -102,14 +107,13 @@ async function handleList() {
102
107
  console.log()
103
108
  }
104
109
 
105
- async function handleStatus() {
110
+ async function handleStatus(project?: string) {
106
111
  console.log()
107
112
  console.log(renderLogo())
108
113
  console.log()
109
114
  console.log(heading('Pack Status'))
110
115
  console.log()
111
116
 
112
- const project = process.argv[4]
113
117
  if (!project) {
114
118
  console.log(warningText(' Usage: claude-brain pack status <project-name>'))
115
119
  console.log(dimText(' Example: claude-brain pack status my-app'))
@@ -139,14 +143,13 @@ async function handleStatus() {
139
143
  console.log()
140
144
  }
141
145
 
142
- async function handleReload() {
146
+ async function handleReload(project?: string) {
143
147
  console.log()
144
148
  console.log(renderLogo())
145
149
  console.log()
146
150
  console.log(heading('Reload Knowledge Packs'))
147
151
  console.log()
148
152
 
149
- const project = process.argv[4]
150
153
  if (!project) {
151
154
  console.log(warningText(' Usage: claude-brain pack reload <project-name>'))
152
155
  console.log(dimText(' This clears the manifest so packs are re-loaded on next init_project'))
@@ -176,8 +176,8 @@ export async function runRefresh() {
176
176
  killByPattern('chroma run')
177
177
  }
178
178
 
179
- // Kill by port (HTTP API 3333, ChromaDB 8000)
180
- killByPort(3333)
179
+ // Kill by port (HTTP API 3000, ChromaDB 8000)
180
+ killByPort(3000)
181
181
  killByPort(8000)
182
182
 
183
183
  // Clean up stale PID file
@@ -38,7 +38,7 @@ export async function runServe() {
38
38
  const logger = createLogger(config.logLevel, config.logFilePath)
39
39
  const mainLogger = createComponentLogger(logger, 'main')
40
40
 
41
- const errorHandler = new GlobalErrorHandler(logger)
41
+ new GlobalErrorHandler(logger)
42
42
  const cleanup = new CleanupManager(logger)
43
43
 
44
44
  mainLogger.info({
@@ -7,13 +7,17 @@
7
7
  * claude-brain start --chroma-only Start only ChromaDB server
8
8
  */
9
9
 
10
+ import { parseArgs } from 'citty'
10
11
  import {
11
12
  heading, successText, warningText, dimText,
12
13
  } from '@/cli/ui/index.js'
13
14
  import { ensureChromaRunning } from '@/cli/commands/chroma'
14
15
 
15
16
  export async function runStart(): Promise<void> {
16
- const chromaOnly = process.argv.includes('--chroma-only')
17
+ const args = parseArgs(process.argv.slice(3), {
18
+ 'chroma-only': { type: 'boolean', default: false, description: 'Start only ChromaDB server' },
19
+ })
20
+ const chromaOnly = args['chroma-only']
17
21
 
18
22
  if (chromaOnly) {
19
23
  console.log()
@@ -44,7 +44,7 @@ export async function runUpdate() {
44
44
  const currentVersion = getInstalledVersion()
45
45
  console.log(` ${theme.bold('Installed:')} ${dimText('v' + currentVersion)}`)
46
46
 
47
- const latestVersion = await withSpinner('Checking for updates', () => getLatestVersion())
47
+ const latestVersion = await withSpinner('Checking for updates', async () => getLatestVersion())
48
48
 
49
49
  if (latestVersion) {
50
50
  console.log(` ${theme.bold('Latest:')} ${dimText('v' + latestVersion)}`)
@@ -54,7 +54,7 @@ export async function runUpdate() {
54
54
  // Step 1: Update package if newer version available
55
55
  if (latestVersion && latestVersion !== currentVersion) {
56
56
  try {
57
- await withSpinner(`Updating claude-brain to v${latestVersion}`, () => {
57
+ await withSpinner(`Updating claude-brain to v${latestVersion}`, async () => {
58
58
  execSync('bun update -g claude-brain', {
59
59
  encoding: 'utf-8',
60
60
  stdio: ['pipe', 'pipe', 'pipe'],
@@ -16,7 +16,7 @@ for (const arg of args) {
16
16
  if (arg === '--validate-after') options.validateAfter = true
17
17
  if (arg === '--verbose') options.verbose = true
18
18
  const batchMatch = arg.match(/--batch-size=(\d+)/)
19
- if (batchMatch) options.batchSize = parseInt(batchMatch[1])
19
+ if (batchMatch) options.batchSize = parseInt(batchMatch[1]!)
20
20
  }
21
21
 
22
22
  // Create dedicated migration logger
@@ -1,9 +1,11 @@
1
+ import pkg from '../../package.json'
1
2
  import type { PartialConfig } from './schema'
2
3
 
3
4
  /** Default configuration values for Claude Brain */
4
5
  export const defaultConfig: PartialConfig = {
6
+ vaultPath: '',
5
7
  serverName: 'claude-brain',
6
- serverVersion: '0.17.10',
8
+ serverVersion: pkg.version,
7
9
  logLevel: 'info',
8
10
  logFilePath: './logs/claude-brain.log',
9
11
  dbPath: './data/memory.db',
@@ -1,7 +1,5 @@
1
1
  export { ConfigSchema, LogLevelSchema, type Config, type LogLevel, type PartialConfig } from './schema'
2
2
  export { defaultConfig } from './defaults'
3
3
  export { loadConfig, validateConfig, getConfigFilePath } from './loader'
4
- export { ConfigWatcher, createConfigWatcher, type ConfigWatcherEvents } from './watcher'
5
4
  export { ConfigValidator, type ValidationResult, type ValidationError, type ValidationWarning } from './validator'
6
- export { ConfigMigration } from './migration'
7
5
  export { getClaudeBrainHome, resolveHomePath, isHomeInitialized, getHomePaths } from './home'