claude-brain 0.27.2 → 0.28.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.
@@ -123,6 +123,50 @@ export async function runStatus() {
123
123
  status: installedHooks.length > 0 ? 'success' : 'warning'
124
124
  })
125
125
 
126
+ // SLM Models
127
+ const modelsDir = join(home, 'models')
128
+ const manifestPath = join(modelsDir, 'manifest.json')
129
+ if (existsSync(manifestPath)) {
130
+ try {
131
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'))
132
+ const tasks = Object.keys(manifest.models || {})
133
+ const available = tasks.filter(t => existsSync(join(modelsDir, manifest.models[t].file)))
134
+ items.push({
135
+ label: 'SLM Models',
136
+ value: `${available.length}/${tasks.length} available (${available.join(', ') || 'none'})`,
137
+ status: available.length > 0 ? 'success' : 'warning'
138
+ })
139
+ } catch {
140
+ items.push({ label: 'SLM Models', value: 'Manifest error', status: 'error' })
141
+ }
142
+ } else {
143
+ items.push({ label: 'SLM Models', value: 'Not installed', status: 'warning' })
144
+ }
145
+
146
+ // Training data
147
+ if (existsSync(dbPath)) {
148
+ try {
149
+ const { Database } = await import('bun:sqlite')
150
+ const db2 = new Database(dbPath, { readonly: true })
151
+ const hasTable = db2.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='training_data'").get()
152
+ if (hasTable) {
153
+ const trainingTotal = (db2.prepare('SELECT COUNT(*) as cnt FROM training_data').get() as any)?.cnt ?? 0
154
+ if (trainingTotal > 0) {
155
+ const taskCounts = db2.prepare('SELECT task, COUNT(*) as cnt FROM training_data GROUP BY task').all() as any[]
156
+ const breakdown = taskCounts.map((r: any) => `${r.cnt} ${r.task}`).join(', ')
157
+ items.push({
158
+ label: 'Training Data',
159
+ value: `${trainingTotal} examples (${breakdown})`,
160
+ status: 'success'
161
+ })
162
+ }
163
+ }
164
+ db2.close()
165
+ } catch {
166
+ // ignore
167
+ }
168
+ }
169
+
126
170
  // Server status
127
171
  const port = process.env.CLAUDE_BRAIN_PORT || '3000'
128
172
  try {
@@ -51,5 +51,6 @@ export function getHomePaths() {
51
51
  vault: join(root, 'vault'),
52
52
  vaultProjects: join(root, 'vault', 'Projects'),
53
53
  vaultGlobal: join(root, 'vault', 'Global'),
54
+ models: join(root, 'models'),
54
55
  }
55
56
  }
@@ -356,6 +356,36 @@ export const ConfigSchema = z.object({
356
356
  autoRestart: z.boolean().default(true),
357
357
  }).default({} as any),
358
358
 
359
+ /** SLM Upgrade: Local model inference configuration */
360
+ slm: z.object({
361
+ /** Master switch for local model inference */
362
+ enabled: z.boolean().default(false),
363
+ /** Directory containing ONNX model files */
364
+ modelsDir: z.string().default('~/.claude-brain/models'),
365
+ /** Minimum model confidence to use model prediction (below → regex fallback) */
366
+ confidenceThreshold: z.number().min(0).max(1).default(0.7),
367
+ /** Per-task mode: 'model' uses model only, 'regex' uses regex only, 'both' runs both and logs comparison */
368
+ tasks: z.object({
369
+ intent: z.enum(['model', 'regex', 'both']).default('regex'),
370
+ entity: z.enum(['model', 'regex', 'both']).default('regex'),
371
+ query: z.enum(['model', 'regex', 'both']).default('regex'),
372
+ knowledge: z.enum(['model', 'regex', 'both']).default('regex'),
373
+ compress: z.enum(['model', 'api', 'both']).default('api'),
374
+ pattern: z.enum(['model', 'regex', 'both']).default('regex'),
375
+ }).default({} as any),
376
+ /** Phase 6B: Automated retraining configuration */
377
+ retrain: z.object({
378
+ /** Minimum new feedback entries before triggering retrain */
379
+ minFeedbackCount: z.number().int().min(1).default(100),
380
+ /** Maximum disagreement rate threshold to trigger retrain */
381
+ maxDisagreementRate: z.number().min(0).max(1).default(0.15),
382
+ /** Python executable path */
383
+ pythonPath: z.string().default('python3'),
384
+ /** Path to SLM training directory */
385
+ trainingDir: z.string().default('~/slm-training'),
386
+ }).default({} as any),
387
+ }).default({} as any),
388
+
359
389
  /** Phase 30: Optional LLM compression for observations */
360
390
  compression: z.object({
361
391
  /** Enable LLM-based compression of long observations */