claude-brain 0.5.0 → 0.8.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/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -0
- package/package.json +2 -1
- package/packs/backend/node.json +173 -0
- package/packs/core/javascript.json +176 -0
- package/packs/core/typescript.json +222 -0
- package/packs/frontend/react.json +254 -0
- package/packs/meta/testing.json +172 -0
- package/src/cli/bin.ts +14 -0
- package/src/cli/commands/chroma.ts +53 -17
- package/src/cli/commands/hooks.ts +214 -0
- package/src/cli/commands/pack.ts +197 -0
- package/src/cli/commands/serve.ts +34 -0
- package/src/config/defaults.ts +1 -1
- package/src/config/schema.ts +85 -2
- package/src/hooks/brain-hook.ts +110 -0
- package/src/hooks/capture.ts +161 -0
- package/src/hooks/deduplicator.ts +72 -0
- package/src/hooks/index.ts +19 -0
- package/src/hooks/installer.ts +181 -0
- package/src/hooks/passive-classifier.ts +366 -0
- package/src/hooks/queue.ts +122 -0
- package/src/hooks/session-tracker.ts +199 -0
- package/src/hooks/types.ts +47 -0
- package/src/memory/chroma/client.ts +1 -1
- package/src/memory/chroma/index.ts +1 -1
- package/src/memory/chroma/store.ts +29 -9
- package/src/memory/index.ts +1 -0
- package/src/memory/store.ts +1 -0
- package/src/packs/index.ts +9 -0
- package/src/packs/loader.ts +134 -0
- package/src/packs/manager.ts +204 -0
- package/src/packs/ranker.ts +78 -0
- package/src/packs/types.ts +81 -0
- package/src/routing/entity-extractor.ts +410 -0
- package/src/routing/intent-classifier.ts +229 -0
- package/src/routing/response-filter.ts +221 -0
- package/src/routing/router.ts +671 -0
- package/src/server/handlers/call-tool.ts +7 -0
- package/src/server/handlers/list-tools.ts +22 -5
- package/src/server/handlers/tools/brain.ts +85 -0
- package/src/server/handlers/tools/init-project.ts +47 -0
- package/src/server/handlers/tools/schemas.ts +12 -0
- package/src/server/http-api.ts +188 -0
- package/src/tools/registry.ts +9 -0
- package/src/tools/schemas.ts +33 -1
|
@@ -1,18 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* List Tools Handler
|
|
3
3
|
* Handles the MCP tools/list request
|
|
4
|
+
*
|
|
5
|
+
* In unified tool mode (Phase 16), only the brain() tool is exposed.
|
|
6
|
+
* In legacy mode, all 25 tools + brain are exposed.
|
|
4
7
|
*/
|
|
5
8
|
|
|
6
9
|
import { ToolRegistry } from '@/tools/registry'
|
|
10
|
+
import { getServices, isServicesInitialized } from '@/server/services'
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* Handle tools/list request
|
|
10
|
-
* Returns
|
|
14
|
+
* Returns available tools in MCP format, filtered by config
|
|
11
15
|
*/
|
|
12
16
|
export async function handleListTools() {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
// Check if unified tool mode is enabled
|
|
18
|
+
if (isServicesInitialized()) {
|
|
19
|
+
try {
|
|
20
|
+
const { config } = getServices()
|
|
21
|
+
if (config.unifiedToolMode) {
|
|
22
|
+
const brainTool = ToolRegistry.getToolByName('brain')
|
|
23
|
+
return {
|
|
24
|
+
tools: brainTool ? [brainTool] : []
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// Services not ready, fall through to default
|
|
29
|
+
}
|
|
17
30
|
}
|
|
31
|
+
|
|
32
|
+
// Default: return all tools (legacy + brain)
|
|
33
|
+
const tools = ToolRegistry.getToolsForListResponse()
|
|
34
|
+
return { tools }
|
|
18
35
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brain Tool Handler
|
|
3
|
+
* Phase 16: Unified single tool that replaces all 25 MCP tools
|
|
4
|
+
*
|
|
5
|
+
* Accepts natural language, classifies intent server-side,
|
|
6
|
+
* routes internally, and returns a unified response.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Logger } from 'pino'
|
|
10
|
+
import type { ToolResponse } from '@/tools/types'
|
|
11
|
+
import { ToolValidator } from '@/server/utils/validators'
|
|
12
|
+
import { ResponseFormatter } from '@/server/utils/response-formatter'
|
|
13
|
+
import { withMemoryIndicator } from '@/server/utils/memory-indicator'
|
|
14
|
+
import { ErrorHandler } from '@/server/utils/error-handler'
|
|
15
|
+
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
|
16
|
+
import { BrainSchema } from './schemas'
|
|
17
|
+
import { getBrainRouter } from '@/routing/router'
|
|
18
|
+
|
|
19
|
+
export async function handleBrain(
|
|
20
|
+
args: unknown,
|
|
21
|
+
logger: Logger
|
|
22
|
+
): Promise<ToolResponse> {
|
|
23
|
+
try {
|
|
24
|
+
const input = ToolValidator.validate(args, BrainSchema)
|
|
25
|
+
const { message, project } = input
|
|
26
|
+
|
|
27
|
+
logger.debug(
|
|
28
|
+
{
|
|
29
|
+
messageLength: message.length,
|
|
30
|
+
project,
|
|
31
|
+
messagePreview: message.slice(0, 80)
|
|
32
|
+
},
|
|
33
|
+
'Brain tool called'
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const router = getBrainRouter(logger)
|
|
37
|
+
const response = await router.route({ message, project })
|
|
38
|
+
|
|
39
|
+
logger.info(
|
|
40
|
+
{
|
|
41
|
+
action: response.action,
|
|
42
|
+
summary: response.summary,
|
|
43
|
+
relevantItems: response.relevantItems
|
|
44
|
+
},
|
|
45
|
+
'Brain tool response'
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
// Format response
|
|
49
|
+
if (response.action === 'none' && !response.content) {
|
|
50
|
+
return ResponseFormatter.text(response.summary || 'No action needed.')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const parts: string[] = []
|
|
54
|
+
|
|
55
|
+
if (response.summary) {
|
|
56
|
+
parts.push(`**${response.summary}**`)
|
|
57
|
+
parts.push('')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (response.content) {
|
|
61
|
+
parts.push(response.content)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const content = parts.join('\n')
|
|
65
|
+
|
|
66
|
+
// Add memory indicator if we found relevant items
|
|
67
|
+
if (response.relevantItems > 0 && response.action === 'retrieved') {
|
|
68
|
+
return ResponseFormatter.text(withMemoryIndicator(content, response.relevantItems))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return ResponseFormatter.text(content)
|
|
72
|
+
|
|
73
|
+
} catch (error) {
|
|
74
|
+
ErrorHandler.logError(logger, error, { tool: 'brain', args })
|
|
75
|
+
|
|
76
|
+
if (error instanceof McpError) {
|
|
77
|
+
throw error
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
throw new McpError(
|
|
81
|
+
ErrorCode.InternalError,
|
|
82
|
+
`Brain tool failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -11,8 +11,11 @@ import { ToolValidator } from '@/server/utils/validators'
|
|
|
11
11
|
import { ResponseFormatter } from '@/server/utils/response-formatter'
|
|
12
12
|
import { ErrorHandler } from '@/server/utils/error-handler'
|
|
13
13
|
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
|
14
|
+
import { PackManager, PackLoader, type PackLoadResult } from '@/packs/index'
|
|
14
15
|
import fs from 'fs/promises'
|
|
15
16
|
import path from 'path'
|
|
17
|
+
import { fileURLToPath } from 'node:url'
|
|
18
|
+
import { dirname, resolve } from 'node:path'
|
|
16
19
|
|
|
17
20
|
interface InitProjectInput {
|
|
18
21
|
project_path?: string
|
|
@@ -176,6 +179,36 @@ Has TypeScript: ${analysis.hasTypeScript}
|
|
|
176
179
|
logger.info({ normalizedName }, 'Project saved to memory')
|
|
177
180
|
}
|
|
178
181
|
|
|
182
|
+
// Phase 18: Load knowledge packs based on detected tech stack
|
|
183
|
+
let packResult: PackLoadResult | null = null
|
|
184
|
+
try {
|
|
185
|
+
const initFile = fileURLToPath(import.meta.url)
|
|
186
|
+
const initDir = dirname(initFile)
|
|
187
|
+
const PACKAGE_ROOT = resolve(initDir, '..', '..', '..', '..')
|
|
188
|
+
const dataDir = path.join(process.env.CLAUDE_BRAIN_HOME || path.join(process.env.HOME || '~', '.claude-brain'), 'data')
|
|
189
|
+
|
|
190
|
+
// Default packs config
|
|
191
|
+
const packsConfig = {
|
|
192
|
+
enabled: true,
|
|
193
|
+
packsDir: 'packs',
|
|
194
|
+
alwaysLoadCore: true,
|
|
195
|
+
alwaysLoadMeta: true,
|
|
196
|
+
communityConfidenceMultiplier: 0.8,
|
|
197
|
+
personalBoost: 1.2,
|
|
198
|
+
projectBoost: 1.15
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (packsConfig.enabled) {
|
|
202
|
+
const memory = getMemoryService()
|
|
203
|
+
const packManager = new PackManager(logger, packsConfig, PACKAGE_ROOT, dataDir)
|
|
204
|
+
const packLoader = new PackLoader(logger, memory, packManager, packsConfig)
|
|
205
|
+
packResult = await packLoader.loadPacksForProject(normalizedName, analysis.techStack)
|
|
206
|
+
logger.info({ packResult }, 'Knowledge packs loaded')
|
|
207
|
+
}
|
|
208
|
+
} catch (packError) {
|
|
209
|
+
logger.warn({ error: packError }, 'Pack loading failed (non-blocking)')
|
|
210
|
+
}
|
|
211
|
+
|
|
179
212
|
// Build response
|
|
180
213
|
const parts: string[] = []
|
|
181
214
|
parts.push(`## ${projectExists ? '🔄 Project Updated' : '✅ Project Initialized'}: ${normalizedName}\n`)
|
|
@@ -226,6 +259,20 @@ Has TypeScript: ${analysis.hasTypeScript}
|
|
|
226
259
|
parts.push(`Project context saved to semantic memory for future recall.`)
|
|
227
260
|
}
|
|
228
261
|
|
|
262
|
+
if (packResult && packResult.packsLoaded > 0) {
|
|
263
|
+
parts.push(`\n### Knowledge Packs`)
|
|
264
|
+
parts.push(`Loaded **${packResult.packsLoaded}** pack(s) with **${packResult.entriesLoaded}** entries:`)
|
|
265
|
+
for (const detail of packResult.packDetails) {
|
|
266
|
+
parts.push(`- **${detail.name}** (${detail.entriesLoaded} entries)`)
|
|
267
|
+
}
|
|
268
|
+
if (packResult.skipped.length > 0) {
|
|
269
|
+
parts.push(`\nSkipped: ${packResult.skipped.map(s => `${s.packId} (${s.reason})`).join(', ')}`)
|
|
270
|
+
}
|
|
271
|
+
} else if (packResult && packResult.skipped.length > 0) {
|
|
272
|
+
parts.push(`\n### Knowledge Packs`)
|
|
273
|
+
parts.push(`All packs already loaded (${packResult.skipped.length} skipped).`)
|
|
274
|
+
}
|
|
275
|
+
|
|
229
276
|
parts.push(`\n### Next Steps`)
|
|
230
277
|
parts.push(`1. Use \`get_project_context("${normalizedName}")\` to view full context`)
|
|
231
278
|
parts.push(`2. Use \`remember_decision\` to save architectural decisions`)
|
|
@@ -209,6 +209,15 @@ export const FindCrossProjectPatternsSchema = z.object({
|
|
|
209
209
|
query: z.string().optional()
|
|
210
210
|
})
|
|
211
211
|
|
|
212
|
+
// ============================================================================
|
|
213
|
+
// Phase 16 - Unified Brain Tool
|
|
214
|
+
// ============================================================================
|
|
215
|
+
|
|
216
|
+
export const BrainSchema = z.object({
|
|
217
|
+
message: z.string().min(1, 'message is required'),
|
|
218
|
+
project: z.string().optional()
|
|
219
|
+
})
|
|
220
|
+
|
|
212
221
|
// ============================================================================
|
|
213
222
|
// Type exports for validated inputs
|
|
214
223
|
// ============================================================================
|
|
@@ -239,3 +248,6 @@ export type ValidatedDetectTrends = z.infer<typeof DetectTrendsSchema>
|
|
|
239
248
|
export type ValidatedWhatIfAnalysis = z.infer<typeof WhatIfAnalysisSchema>
|
|
240
249
|
export type ValidatedGetRecommendations = z.infer<typeof GetRecommendationsSchema>
|
|
241
250
|
export type ValidatedFindCrossProjectPatterns = z.infer<typeof FindCrossProjectPatternsSchema>
|
|
251
|
+
|
|
252
|
+
// Phase 16
|
|
253
|
+
export type ValidatedBrain = z.infer<typeof BrainSchema>
|
package/src/server/http-api.ts
CHANGED
|
@@ -7,12 +7,24 @@ import { Hono } from 'hono'
|
|
|
7
7
|
import type { Logger } from 'pino'
|
|
8
8
|
import type { Config } from '@/config'
|
|
9
9
|
import { getMemoryService, getVaultService, isServicesInitialized } from '@/server/services'
|
|
10
|
+
import type { MemoryManager } from '@/memory'
|
|
11
|
+
import type { CapturedKnowledge, HookStats } from '@/hooks/types'
|
|
12
|
+
import { SmartDeduplicator } from '@/hooks/deduplicator'
|
|
13
|
+
import type { HookSessionTracker } from '@/hooks/session-tracker'
|
|
10
14
|
|
|
11
15
|
export class HttpApiServer {
|
|
12
16
|
private app: Hono
|
|
13
17
|
private logger: Logger
|
|
14
18
|
private config: Config
|
|
15
19
|
private server?: any
|
|
20
|
+
private sessionTracker?: HookSessionTracker
|
|
21
|
+
private hookStats: HookStats = {
|
|
22
|
+
totalCaptured: 0,
|
|
23
|
+
totalSkipped: 0,
|
|
24
|
+
totalMerged: 0,
|
|
25
|
+
sessionsTracked: 0,
|
|
26
|
+
queueSize: 0,
|
|
27
|
+
}
|
|
16
28
|
|
|
17
29
|
constructor(config: Config, logger: Logger) {
|
|
18
30
|
this.config = config
|
|
@@ -32,6 +44,11 @@ export class HttpApiServer {
|
|
|
32
44
|
})
|
|
33
45
|
}
|
|
34
46
|
|
|
47
|
+
/** Set the session tracker (called from serve.ts after initialization) */
|
|
48
|
+
setSessionTracker(tracker: HookSessionTracker): void {
|
|
49
|
+
this.sessionTracker = tracker
|
|
50
|
+
}
|
|
51
|
+
|
|
35
52
|
private setupRoutes(): void {
|
|
36
53
|
// Health check
|
|
37
54
|
this.app.get('/api/health', (c) => {
|
|
@@ -59,6 +76,11 @@ export class HttpApiServer {
|
|
|
59
76
|
this.app.post('/api/claude-brain/recognize-pattern', (c) => this.handleRecognizePattern(c))
|
|
60
77
|
this.app.post('/api/claude-brain/record-correction', (c) => this.handleRecordCorrection(c))
|
|
61
78
|
this.app.post('/api/claude-brain/update-progress', (c) => this.handleUpdateProgress(c))
|
|
79
|
+
|
|
80
|
+
// Phase 17: Hook passive learning endpoints
|
|
81
|
+
this.app.post('/api/hooks/ingest', (c) => this.handleHookIngest(c))
|
|
82
|
+
this.app.post('/api/hooks/session-end', (c) => this.handleHookSessionEnd(c))
|
|
83
|
+
this.app.get('/api/hooks/status', () => this.handleHookStatus())
|
|
62
84
|
}
|
|
63
85
|
|
|
64
86
|
private async handleListProjects(): Promise<Response> {
|
|
@@ -436,6 +458,172 @@ export class HttpApiServer {
|
|
|
436
458
|
}
|
|
437
459
|
}
|
|
438
460
|
|
|
461
|
+
// ─── Phase 17: Hook Endpoints ────────────────────────────
|
|
462
|
+
|
|
463
|
+
private async handleHookIngest(c: any): Promise<Response> {
|
|
464
|
+
try {
|
|
465
|
+
const body = await c.req.json()
|
|
466
|
+
const items: CapturedKnowledge[] = body.knowledge || []
|
|
467
|
+
const sessionId: string | undefined = body.sessionId
|
|
468
|
+
|
|
469
|
+
if (items.length === 0) {
|
|
470
|
+
return Response.json({ success: true, data: { stored: 0, skipped: 0, merged: 0 } })
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const memoryService = getMemoryService()
|
|
474
|
+
if (!memoryService || !memoryService.isInitialized()) {
|
|
475
|
+
return Response.json(
|
|
476
|
+
{ success: false, error: 'Memory service not initialized' },
|
|
477
|
+
{ status: 503 }
|
|
478
|
+
)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const deduplicator = new SmartDeduplicator(this.config.hooks?.deduplication)
|
|
482
|
+
let stored = 0, skipped = 0, merged = 0
|
|
483
|
+
|
|
484
|
+
for (const knowledge of items) {
|
|
485
|
+
const action = await deduplicator.beforeStore(knowledge, memoryService)
|
|
486
|
+
|
|
487
|
+
switch (action.action) {
|
|
488
|
+
case 'skip':
|
|
489
|
+
skipped++
|
|
490
|
+
this.hookStats.totalSkipped++
|
|
491
|
+
break
|
|
492
|
+
|
|
493
|
+
case 'merge':
|
|
494
|
+
merged++
|
|
495
|
+
this.hookStats.totalMerged++
|
|
496
|
+
// For merge, we store as update — treat as new store with merged content
|
|
497
|
+
await this.storeKnowledge({ ...knowledge, content: action.mergedContent }, memoryService)
|
|
498
|
+
stored++
|
|
499
|
+
break
|
|
500
|
+
|
|
501
|
+
case 'store_new':
|
|
502
|
+
await this.storeKnowledge(knowledge, memoryService)
|
|
503
|
+
stored++
|
|
504
|
+
this.hookStats.totalCaptured++
|
|
505
|
+
break
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Track in session if sessionId provided
|
|
509
|
+
if (sessionId && this.sessionTracker) {
|
|
510
|
+
await this.sessionTracker.track(sessionId, knowledge)
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
this.hookStats.lastCaptureAt = new Date().toISOString()
|
|
515
|
+
|
|
516
|
+
this.logger.debug(
|
|
517
|
+
{ stored, skipped, merged, sessionId },
|
|
518
|
+
'Hook ingest processed'
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
return Response.json({ success: true, data: { stored, skipped, merged } })
|
|
522
|
+
} catch (error) {
|
|
523
|
+
this.logger.error({ error }, 'Failed to process hook ingest')
|
|
524
|
+
return Response.json(
|
|
525
|
+
{ success: false, error: 'Failed to ingest hook data' },
|
|
526
|
+
{ status: 500 }
|
|
527
|
+
)
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private async handleHookSessionEnd(c: any): Promise<Response> {
|
|
532
|
+
try {
|
|
533
|
+
const body = await c.req.json()
|
|
534
|
+
const sessionId: string | undefined = body.sessionId
|
|
535
|
+
|
|
536
|
+
if (!sessionId) {
|
|
537
|
+
return Response.json(
|
|
538
|
+
{ success: false, error: 'sessionId required' },
|
|
539
|
+
{ status: 400 }
|
|
540
|
+
)
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (this.sessionTracker) {
|
|
544
|
+
await this.sessionTracker.endSession(sessionId)
|
|
545
|
+
this.hookStats.sessionsTracked++
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return Response.json({ success: true, data: { message: 'Session ended' } })
|
|
549
|
+
} catch (error) {
|
|
550
|
+
this.logger.error({ error }, 'Failed to end hook session')
|
|
551
|
+
return Response.json(
|
|
552
|
+
{ success: false, error: 'Failed to end session' },
|
|
553
|
+
{ status: 500 }
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private handleHookStatus(): Response {
|
|
559
|
+
const sessionStats = this.sessionTracker?.getStats()
|
|
560
|
+
|
|
561
|
+
return Response.json({
|
|
562
|
+
success: true,
|
|
563
|
+
data: {
|
|
564
|
+
...this.hookStats,
|
|
565
|
+
activeSessions: sessionStats?.activeSessions ?? 0,
|
|
566
|
+
sessionItems: sessionStats?.totalItems ?? 0,
|
|
567
|
+
hooksEnabled: this.config.hooks?.enabled ?? false,
|
|
568
|
+
},
|
|
569
|
+
})
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/** Route captured knowledge to the appropriate store method */
|
|
573
|
+
private async storeKnowledge(
|
|
574
|
+
knowledge: CapturedKnowledge,
|
|
575
|
+
memoryService: MemoryManager
|
|
576
|
+
): Promise<void> {
|
|
577
|
+
const project = knowledge.project || 'unknown'
|
|
578
|
+
|
|
579
|
+
switch (knowledge.type) {
|
|
580
|
+
case 'decision':
|
|
581
|
+
await memoryService.rememberDecision(
|
|
582
|
+
project,
|
|
583
|
+
`Captured via hook: ${knowledge.metadata.action || 'tool-use'}`,
|
|
584
|
+
knowledge.content,
|
|
585
|
+
`Auto-captured from ${knowledge.source}`,
|
|
586
|
+
{ tags: knowledge.technologies }
|
|
587
|
+
)
|
|
588
|
+
break
|
|
589
|
+
|
|
590
|
+
case 'pattern':
|
|
591
|
+
await memoryService.storePattern({
|
|
592
|
+
project,
|
|
593
|
+
pattern_type: 'solution',
|
|
594
|
+
description: knowledge.content,
|
|
595
|
+
context: `Captured via hook: ${knowledge.metadata.action || 'tool-use'}`,
|
|
596
|
+
confidence: knowledge.confidence,
|
|
597
|
+
})
|
|
598
|
+
break
|
|
599
|
+
|
|
600
|
+
case 'correction':
|
|
601
|
+
await memoryService.storeCorrection({
|
|
602
|
+
project,
|
|
603
|
+
original: knowledge.metadata.command || 'Unknown',
|
|
604
|
+
correction: knowledge.content,
|
|
605
|
+
reasoning: 'Auto-captured from hook passive learning',
|
|
606
|
+
context: `Captured via hook: ${knowledge.metadata.action || 'tool-use'}`,
|
|
607
|
+
confidence: knowledge.confidence,
|
|
608
|
+
})
|
|
609
|
+
break
|
|
610
|
+
|
|
611
|
+
case 'progress': {
|
|
612
|
+
const vaultService = getVaultService()
|
|
613
|
+
if (vaultService) {
|
|
614
|
+
try {
|
|
615
|
+
await vaultService.updateProgress(project, {
|
|
616
|
+
status: `Hook: ${knowledge.content.slice(0, 200)}`,
|
|
617
|
+
})
|
|
618
|
+
} catch {
|
|
619
|
+
// Vault might not have this project
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
break
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
439
627
|
async start(): Promise<void> {
|
|
440
628
|
const port = this.config.port || 3000
|
|
441
629
|
|
package/src/tools/registry.ts
CHANGED
|
@@ -69,6 +69,15 @@ export class ToolRegistry {
|
|
|
69
69
|
return Object.values(TOOLS)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Get only the unified brain tool (Phase 16)
|
|
74
|
+
* @returns Array containing only the brain tool
|
|
75
|
+
*/
|
|
76
|
+
static getUnifiedTools(): ToolDefinition[] {
|
|
77
|
+
const brain = this.getToolByName('brain')
|
|
78
|
+
return brain ? [brain] : []
|
|
79
|
+
}
|
|
80
|
+
|
|
72
81
|
/**
|
|
73
82
|
* Validate that required parameters are present
|
|
74
83
|
* @param toolName - Name of the tool
|
package/src/tools/schemas.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/** Tool definition type */
|
|
12
|
-
interface ToolDefinition {
|
|
12
|
+
export interface ToolDefinition {
|
|
13
13
|
name: string
|
|
14
14
|
description: string
|
|
15
15
|
inputSchema: {
|
|
@@ -895,6 +895,38 @@ export const TOOLS = {
|
|
|
895
895
|
}
|
|
896
896
|
}
|
|
897
897
|
}
|
|
898
|
+
},
|
|
899
|
+
// =========================================================================
|
|
900
|
+
// Phase 16 - Unified Brain Tool
|
|
901
|
+
// =========================================================================
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* BRAIN
|
|
905
|
+
* Unified intelligent tool that replaces all 25 tools
|
|
906
|
+
*
|
|
907
|
+
* Use this when:
|
|
908
|
+
* - You want a single tool for all Claude Brain interactions
|
|
909
|
+
* - Enabled via unifiedToolMode config flag
|
|
910
|
+
*
|
|
911
|
+
* The server classifies intent and routes internally
|
|
912
|
+
*/
|
|
913
|
+
BRAIN: {
|
|
914
|
+
name: 'brain',
|
|
915
|
+
description: 'Your intelligent memory. Tell it what you are doing in natural language. The server classifies intent and routes internally — no need to pick the right tool.',
|
|
916
|
+
inputSchema: {
|
|
917
|
+
type: 'object' as const,
|
|
918
|
+
properties: {
|
|
919
|
+
message: {
|
|
920
|
+
type: 'string',
|
|
921
|
+
description: 'What you are doing, decided, learned, or need. Natural language.'
|
|
922
|
+
},
|
|
923
|
+
project: {
|
|
924
|
+
type: 'string',
|
|
925
|
+
description: 'Project name (optional — auto-detected from message if omitted)'
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
required: ['message']
|
|
929
|
+
}
|
|
898
930
|
}
|
|
899
931
|
} as const satisfies Record<string, ToolDefinition>
|
|
900
932
|
|