claude-brain 0.15.2 → 0.17.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 +191 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +29 -11
- package/bunfig.toml +8 -8
- package/package.json +82 -82
- package/packs/backend/node.json +173 -173
- package/packs/core/javascript.json +176 -176
- package/packs/core/typescript.json +222 -222
- package/packs/frontend/react.json +254 -254
- package/packs/meta/testing.json +172 -172
- package/scripts/postinstall.mjs +341 -341
- package/src/automation/auto-context.ts +240 -240
- package/src/automation/decision-detector.ts +452 -452
- package/src/automation/index.ts +11 -11
- package/src/automation/phase12-manager.ts +456 -456
- package/src/automation/proactive-recall.ts +373 -373
- package/src/automation/project-detector.ts +310 -310
- package/src/automation/repo-scanner.ts +205 -205
- package/src/cli/auto-setup.ts +82 -82
- package/src/cli/bin.ts +209 -202
- package/src/cli/commands/chroma.ts +573 -573
- package/src/cli/commands/git-hook.ts +189 -189
- package/src/cli/commands/hooks.ts +213 -213
- package/src/cli/commands/init.ts +122 -122
- package/src/cli/commands/install-mcp.ts +92 -92
- package/src/cli/commands/pack.ts +197 -197
- package/src/cli/commands/refresh.ts +323 -0
- package/src/cli/commands/serve.ts +167 -173
- package/src/cli/commands/start.ts +42 -42
- package/src/cli/commands/uninstall-mcp.ts +41 -41
- package/src/cli/commands/update.ts +124 -121
- package/src/cli/diagnose.ts +4 -4
- package/src/cli/health-check.ts +4 -4
- package/src/cli/migrate-chroma.ts +106 -106
- package/src/cli/setup.ts +4 -4
- package/src/cli/ui/animations.ts +80 -80
- package/src/cli/ui/components.ts +82 -82
- package/src/cli/ui/index.ts +4 -4
- package/src/cli/ui/logo.ts +36 -36
- package/src/cli/ui/theme.ts +55 -55
- package/src/config/defaults.ts +50 -50
- package/src/config/home.ts +55 -55
- package/src/config/index.ts +7 -7
- package/src/config/loader.ts +166 -166
- package/src/config/migration.ts +76 -76
- package/src/config/schema.ts +360 -360
- package/src/config/validator.ts +184 -184
- package/src/config/watcher.ts +86 -86
- package/src/context/assembler.ts +398 -398
- package/src/context/cache-manager.ts +101 -101
- package/src/context/formatter.ts +84 -84
- package/src/context/hierarchy.ts +85 -85
- package/src/context/index.ts +83 -83
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/types.ts +252 -252
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +123 -123
- package/src/health/index.ts +229 -229
- package/src/hooks/brain-hook.ts +128 -112
- package/src/hooks/capture.ts +168 -205
- package/src/hooks/context-hook.ts +137 -0
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +207 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +244 -194
- package/src/hooks/passive-classifier.ts +404 -723
- package/src/hooks/queue.ts +129 -129
- package/src/hooks/session-tracker.ts +312 -275
- package/src/hooks/types.ts +52 -47
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/affinity.ts +162 -162
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +13 -13
- package/src/intelligence/cross-project/transfer.ts +201 -201
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +207 -207
- package/src/intelligence/prediction/context-anticipator.ts +198 -198
- package/src/intelligence/prediction/decision-predictor.ts +184 -184
- package/src/intelligence/prediction/index.ts +13 -13
- package/src/intelligence/prediction/recommender.ts +268 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +247 -247
- package/src/intelligence/reasoning/counterfactual.ts +248 -248
- package/src/intelligence/reasoning/index.ts +13 -13
- package/src/intelligence/reasoning/synthesizer.ts +169 -169
- package/src/intelligence/temporal/evolution.ts +197 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +259 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/knowledge/entity-extractor.ts +416 -416
- package/src/knowledge/graph/builder.ts +185 -185
- package/src/knowledge/graph/linker.ts +201 -201
- package/src/knowledge/graph/memory-graph.ts +359 -359
- package/src/knowledge/graph/schema.ts +99 -99
- package/src/knowledge/graph/search.ts +168 -168
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +174 -174
- package/src/memory/chroma/collection-manager.ts +94 -94
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +155 -155
- package/src/memory/chroma/index.ts +82 -82
- package/src/memory/chroma/migration.ts +270 -270
- package/src/memory/chroma/schemas.ts +69 -69
- package/src/memory/chroma/search.ts +315 -315
- package/src/memory/chroma/store.ts +741 -741
- package/src/memory/consolidation/archiver.ts +164 -164
- package/src/memory/consolidation/merger.ts +186 -186
- package/src/memory/consolidation/scorer.ts +138 -138
- package/src/memory/context-builder.ts +236 -236
- package/src/memory/database.ts +169 -169
- package/src/memory/embedding-utils.ts +156 -156
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +351 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/index.ts +582 -582
- package/src/memory/knowledge-extractor.ts +455 -455
- package/src/memory/learning.ts +378 -378
- package/src/memory/patterns.ts +396 -396
- package/src/memory/schema.ts +88 -88
- package/src/memory/search.ts +309 -309
- package/src/memory/store.ts +787 -787
- package/src/memory/types.ts +121 -121
- package/src/orchestrator/coordinator.ts +272 -272
- package/src/orchestrator/decision-logger.ts +228 -228
- package/src/orchestrator/event-emitter.ts +198 -198
- package/src/orchestrator/event-queue.ts +184 -184
- package/src/orchestrator/handlers/base-handler.ts +70 -70
- package/src/orchestrator/handlers/context-handler.ts +73 -73
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- package/src/orchestrator/handlers/index.ts +10 -10
- package/src/orchestrator/handlers/status-handler.ts +131 -131
- package/src/orchestrator/handlers/task-handler.ts +171 -171
- package/src/orchestrator/index.ts +275 -275
- package/src/orchestrator/task-parser.ts +284 -284
- package/src/orchestrator/types.ts +98 -98
- package/src/packs/index.ts +9 -9
- package/src/packs/loader.ts +134 -134
- package/src/packs/manager.ts +204 -204
- package/src/packs/ranker.ts +78 -78
- package/src/packs/types.ts +81 -81
- package/src/phase12/index.ts +5 -5
- package/src/retrieval/bm25/index.ts +300 -300
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +223 -223
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +223 -223
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +163 -163
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +198 -198
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +236 -236
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +188 -188
- package/src/retrieval/reranker/model.ts +95 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +428 -428
- package/src/routing/intent-classifier.ts +450 -436
- package/src/routing/response-filter.ts +261 -258
- package/src/routing/router.ts +1441 -1322
- package/src/routing/search-engine.ts +515 -475
- package/src/routing/types.ts +94 -94
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/handlers/call-tool.ts +156 -156
- package/src/server/handlers/index.ts +9 -9
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/analyze-decision-evolution.ts +151 -151
- package/src/server/handlers/tools/auto-remember.ts +200 -200
- package/src/server/handlers/tools/brain.ts +85 -85
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/detect-trends.ts +144 -144
- package/src/server/handlers/tools/find-cross-project-patterns.ts +168 -168
- package/src/server/handlers/tools/get-activity-log.ts +194 -194
- package/src/server/handlers/tools/get-code-standards.ts +124 -124
- package/src/server/handlers/tools/get-corrections.ts +154 -154
- package/src/server/handlers/tools/get-decision-timeline.ts +172 -172
- package/src/server/handlers/tools/get-episode.ts +103 -103
- package/src/server/handlers/tools/get-patterns.ts +158 -158
- package/src/server/handlers/tools/get-phase12-status.ts +63 -63
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/get-recommendations.ts +145 -145
- package/src/server/handlers/tools/index.ts +31 -31
- package/src/server/handlers/tools/init-project.ts +757 -757
- package/src/server/handlers/tools/list-episodes.ts +90 -90
- package/src/server/handlers/tools/list-projects.ts +125 -125
- package/src/server/handlers/tools/rate-memory.ts +101 -101
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +126 -126
- package/src/server/handlers/tools/record-correction.ts +125 -125
- package/src/server/handlers/tools/remember-decision.ts +153 -153
- package/src/server/handlers/tools/schemas.ts +253 -253
- package/src/server/handlers/tools/search-knowledge-graph.ts +102 -102
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/handlers/tools/what-if-analysis.ts +135 -135
- package/src/server/http-api.ts +761 -693
- package/src/server/index.ts +40 -40
- package/src/server/mcp-server.ts +283 -283
- package/src/server/providers/index.ts +7 -7
- package/src/server/providers/prompts.ts +327 -327
- package/src/server/providers/resources.ts +622 -622
- package/src/server/services.ts +468 -468
- package/src/server/types.ts +39 -39
- package/src/server/utils/error-handler.ts +155 -155
- package/src/server/utils/index.ts +13 -13
- package/src/server/utils/memory-indicator.ts +83 -83
- package/src/server/utils/request-context.ts +122 -122
- package/src/server/utils/response-formatter.ts +129 -129
- package/src/server/utils/validators.ts +210 -210
- package/src/setup/index.ts +48 -48
- package/src/setup/wizard.ts +461 -461
- package/src/tools/index.ts +24 -24
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.test.ts +30 -30
- package/src/tools/schemas.ts +617 -617
- package/src/tools/types.ts +412 -412
- package/src/utils/circuit-breaker.ts +130 -130
- package/src/utils/cleanup.ts +34 -34
- package/src/utils/error-handler.ts +132 -132
- package/src/utils/error-messages.ts +60 -60
- package/src/utils/fallback.ts +45 -45
- package/src/utils/index.ts +54 -54
- package/src/utils/logger-utils.ts +80 -80
- package/src/utils/logger.ts +88 -88
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/retry.ts +94 -94
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/frontmatter.ts +264 -264
- package/src/vault/index.ts +318 -318
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +422 -422
- package/src/vault/reader.ts +264 -264
- package/src/vault/templates.ts +186 -186
- package/src/vault/types.ts +73 -73
- package/src/vault/watcher.ts +277 -277
- package/src/vault/writer.ts +413 -413
- package/tsconfig.json +30 -30
- package/src/cli/auto-update.ts +0 -157
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Phase 26: Context Injection Hook
|
|
4
|
+
* Runs on UserPromptSubmit and SessionStart to inject relevant memories
|
|
5
|
+
* into Claude's context via additionalContext.
|
|
6
|
+
*
|
|
7
|
+
* CRITICAL CONSTRAINTS:
|
|
8
|
+
* - Must complete in <3s (blocks user prompt processing)
|
|
9
|
+
* - No heavy imports — just fetch + JSON parse
|
|
10
|
+
* - All errors silently caught with process.exit(0)
|
|
11
|
+
* - Outputs JSON to stdout: { hookSpecificOutput: { additionalContext: "..." } }
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface HookStdin {
|
|
15
|
+
session_id: string
|
|
16
|
+
hook_event_name: string
|
|
17
|
+
prompt?: string
|
|
18
|
+
cwd?: string
|
|
19
|
+
source?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function main(): Promise<void> {
|
|
23
|
+
// Parse --event arg
|
|
24
|
+
const eventIdx = process.argv.indexOf('--event')
|
|
25
|
+
const eventName = eventIdx >= 0 ? process.argv[eventIdx + 1] : undefined
|
|
26
|
+
|
|
27
|
+
// Read stdin JSON from Claude Code
|
|
28
|
+
let rawInput: string
|
|
29
|
+
try {
|
|
30
|
+
rawInput = await readStdin()
|
|
31
|
+
} catch {
|
|
32
|
+
process.exit(0)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!rawInput.trim()) {
|
|
37
|
+
process.exit(0)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let input: HookStdin
|
|
42
|
+
try {
|
|
43
|
+
input = JSON.parse(rawInput)
|
|
44
|
+
} catch {
|
|
45
|
+
process.exit(0)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const event = eventName || input.hook_event_name
|
|
50
|
+
|
|
51
|
+
// Skip if hooks explicitly disabled
|
|
52
|
+
if (process.env.CLAUDE_BRAIN_HOOKS_ENABLED === 'false') {
|
|
53
|
+
process.exit(0)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const port = parseInt(process.env.CLAUDE_BRAIN_PORT || '3000', 10)
|
|
58
|
+
const baseUrl = `http://localhost:${port}`
|
|
59
|
+
|
|
60
|
+
let context = ''
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
if (event === 'UserPromptSubmit' && input.prompt) {
|
|
64
|
+
// Query for memories relevant to the user's prompt
|
|
65
|
+
const params = new URLSearchParams({
|
|
66
|
+
query: input.prompt.slice(0, 500), // Limit query length
|
|
67
|
+
limit: '5',
|
|
68
|
+
})
|
|
69
|
+
if (input.cwd) params.set('cwd', input.cwd)
|
|
70
|
+
|
|
71
|
+
const res = await fetch(`${baseUrl}/api/hooks/context-query?${params}`, {
|
|
72
|
+
signal: AbortSignal.timeout(2000),
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
if (res.ok) {
|
|
76
|
+
const data = await res.json() as { success: boolean; context: string }
|
|
77
|
+
context = data.context || ''
|
|
78
|
+
}
|
|
79
|
+
} else if (event === 'SessionStart') {
|
|
80
|
+
// Load broader project context on session start
|
|
81
|
+
const params = new URLSearchParams({ type: 'session-start' })
|
|
82
|
+
if (input.cwd) params.set('cwd', input.cwd)
|
|
83
|
+
|
|
84
|
+
const res = await fetch(`${baseUrl}/api/hooks/context-query?${params}`, {
|
|
85
|
+
signal: AbortSignal.timeout(2000),
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
if (res.ok) {
|
|
89
|
+
const data = await res.json() as { success: boolean; context: string }
|
|
90
|
+
context = data.context || ''
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
// Server unreachable or timeout — exit silently
|
|
95
|
+
process.exit(0)
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Only output if we have context to inject
|
|
100
|
+
if (context.trim()) {
|
|
101
|
+
const output = {
|
|
102
|
+
hookSpecificOutput: {
|
|
103
|
+
hookEventName: event,
|
|
104
|
+
additionalContext: `[Brain Memory]\n${context}`,
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
process.stdout.write(JSON.stringify(output))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
process.exit(0)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Read all of stdin as a string */
|
|
114
|
+
function readStdin(): Promise<string> {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
const chunks: Buffer[] = []
|
|
117
|
+
const stdin = process.stdin
|
|
118
|
+
|
|
119
|
+
stdin.on('data', (chunk: Buffer) => chunks.push(chunk))
|
|
120
|
+
stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
|
|
121
|
+
stdin.on('error', reject)
|
|
122
|
+
|
|
123
|
+
// Timeout after 2 seconds
|
|
124
|
+
setTimeout(() => {
|
|
125
|
+
stdin.destroy()
|
|
126
|
+
resolve(Buffer.concat(chunks).toString('utf-8'))
|
|
127
|
+
}, 2000)
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Execute when run directly
|
|
132
|
+
const isDirectRun = process.argv[1]?.includes('context-hook')
|
|
133
|
+
if (isDirectRun) {
|
|
134
|
+
main().catch(() => process.exit(0))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export { main }
|
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 17: Smart Deduplicator
|
|
3
|
-
* Three-tier dedup before storage:
|
|
4
|
-
* >0.95 similarity → skip (exact duplicate)
|
|
5
|
-
* 0.85–0.95 similarity → merge (update existing)
|
|
6
|
-
* <0.85 similarity → store_new
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { MemoryManager } from '@/memory'
|
|
10
|
-
import type { CapturedKnowledge, StoreAction } from './types'
|
|
11
|
-
import type { HooksConfig } from '@/config/schema'
|
|
12
|
-
|
|
13
|
-
export class SmartDeduplicator {
|
|
14
|
-
private skipThreshold: number
|
|
15
|
-
private mergeThreshold: number
|
|
16
|
-
|
|
17
|
-
constructor(config?: HooksConfig['deduplication']) {
|
|
18
|
-
this.skipThreshold = config?.skipThreshold ?? 0.95
|
|
19
|
-
this.mergeThreshold = config?.mergeThreshold ?? 0.85
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Check captured knowledge against existing memory
|
|
24
|
-
* and decide: store_new, merge, or skip
|
|
25
|
-
*/
|
|
26
|
-
async beforeStore(
|
|
27
|
-
knowledge: CapturedKnowledge,
|
|
28
|
-
memoryManager: MemoryManager
|
|
29
|
-
): Promise<StoreAction> {
|
|
30
|
-
try {
|
|
31
|
-
const results = await memoryManager.searchRaw(knowledge.content, {
|
|
32
|
-
project: knowledge.project,
|
|
33
|
-
limit: 3,
|
|
34
|
-
minSimilarity: this.mergeThreshold,
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
if (!results || results.length === 0) {
|
|
38
|
-
return { action: 'store_new' }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const topResult = results[0]
|
|
42
|
-
const similarity = topResult.similarity ?? 0
|
|
43
|
-
|
|
44
|
-
if (similarity >= this.skipThreshold) {
|
|
45
|
-
return {
|
|
46
|
-
action: 'skip',
|
|
47
|
-
reason: `Duplicate (similarity: ${similarity.toFixed(3)})`,
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (similarity >= this.mergeThreshold) {
|
|
52
|
-
const existingId = topResult.memory?.id || topResult.id
|
|
53
|
-
const existingContent = topResult.memory?.content ||
|
|
54
|
-
topResult.decision?.decision ||
|
|
55
|
-
''
|
|
56
|
-
const datestamp = new Date().toISOString().split('T')[0]
|
|
57
|
-
const mergedContent = `${existingContent}\n[Updated ${datestamp}]: ${knowledge.content}`
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
action: 'merge',
|
|
61
|
-
existingId,
|
|
62
|
-
mergedContent,
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return { action: 'store_new' }
|
|
67
|
-
} catch {
|
|
68
|
-
// If search fails, store as new to avoid data loss
|
|
69
|
-
return { action: 'store_new' }
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Phase 17: Smart Deduplicator
|
|
3
|
+
* Three-tier dedup before storage:
|
|
4
|
+
* >0.95 similarity → skip (exact duplicate)
|
|
5
|
+
* 0.85–0.95 similarity → merge (update existing)
|
|
6
|
+
* <0.85 similarity → store_new
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { MemoryManager } from '@/memory'
|
|
10
|
+
import type { CapturedKnowledge, StoreAction } from './types'
|
|
11
|
+
import type { HooksConfig } from '@/config/schema'
|
|
12
|
+
|
|
13
|
+
export class SmartDeduplicator {
|
|
14
|
+
private skipThreshold: number
|
|
15
|
+
private mergeThreshold: number
|
|
16
|
+
|
|
17
|
+
constructor(config?: HooksConfig['deduplication']) {
|
|
18
|
+
this.skipThreshold = config?.skipThreshold ?? 0.95
|
|
19
|
+
this.mergeThreshold = config?.mergeThreshold ?? 0.85
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check captured knowledge against existing memory
|
|
24
|
+
* and decide: store_new, merge, or skip
|
|
25
|
+
*/
|
|
26
|
+
async beforeStore(
|
|
27
|
+
knowledge: CapturedKnowledge,
|
|
28
|
+
memoryManager: MemoryManager
|
|
29
|
+
): Promise<StoreAction> {
|
|
30
|
+
try {
|
|
31
|
+
const results = await memoryManager.searchRaw(knowledge.content, {
|
|
32
|
+
project: knowledge.project,
|
|
33
|
+
limit: 3,
|
|
34
|
+
minSimilarity: this.mergeThreshold,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
if (!results || results.length === 0) {
|
|
38
|
+
return { action: 'store_new' }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const topResult = results[0]
|
|
42
|
+
const similarity = topResult.similarity ?? 0
|
|
43
|
+
|
|
44
|
+
if (similarity >= this.skipThreshold) {
|
|
45
|
+
return {
|
|
46
|
+
action: 'skip',
|
|
47
|
+
reason: `Duplicate (similarity: ${similarity.toFixed(3)})`,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (similarity >= this.mergeThreshold) {
|
|
52
|
+
const existingId = topResult.memory?.id || topResult.id
|
|
53
|
+
const existingContent = topResult.memory?.content ||
|
|
54
|
+
topResult.decision?.decision ||
|
|
55
|
+
''
|
|
56
|
+
const datestamp = new Date().toISOString().split('T')[0]
|
|
57
|
+
const mergedContent = `${existingContent}\n[Updated ${datestamp}]: ${knowledge.content}`
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
action: 'merge',
|
|
61
|
+
existingId,
|
|
62
|
+
mergedContent,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { action: 'store_new' }
|
|
67
|
+
} catch {
|
|
68
|
+
// If search fails, store as new to avoid data loss
|
|
69
|
+
return { action: 'store_new' }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/hooks/git-capture.ts
CHANGED
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 21: Git Commit Capture
|
|
3
|
-
* Receives commit data from the post-commit hook and sends it to brain.
|
|
4
|
-
* Called as: claude-brain git-capture <project> <branch> <message> <files> <port>
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { appendToQueue } from './queue'
|
|
8
|
-
import type { CapturedKnowledge } from './types'
|
|
9
|
-
|
|
10
|
-
export async function handleGitCapture(): Promise<void> {
|
|
11
|
-
// Parse positional args: project, branch, message, files, port
|
|
12
|
-
const [, , , rawProject, branch, message, filesStr, portStr] = process.argv
|
|
13
|
-
|
|
14
|
-
if (!rawProject || !message) {
|
|
15
|
-
process.exit(0)
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Phase 23b: Resolve project name — strip scoped package prefix, validate
|
|
20
|
-
const project = resolveProjectName(rawProject)
|
|
21
|
-
const port = parseInt(portStr || process.env.CLAUDE_BRAIN_PORT || '3000', 10)
|
|
22
|
-
const files = filesStr ? filesStr.split(',').filter(Boolean) : []
|
|
23
|
-
|
|
24
|
-
// Build captured knowledge
|
|
25
|
-
const knowledge: CapturedKnowledge = {
|
|
26
|
-
type: 'progress',
|
|
27
|
-
confidence: 0.9,
|
|
28
|
-
content: `Git commit on ${branch || 'unknown'}: ${message.trim()}${files.length > 0 ? ` (files: ${files.join(', ')})` : ''}`,
|
|
29
|
-
project: project || undefined,
|
|
30
|
-
technologies: detectTechnologies(files),
|
|
31
|
-
metadata: {
|
|
32
|
-
source: 'git-hook',
|
|
33
|
-
branch: branch || 'unknown',
|
|
34
|
-
commit_message: message.trim(),
|
|
35
|
-
files,
|
|
36
|
-
file_count: files.length,
|
|
37
|
-
},
|
|
38
|
-
source: 'hook-passive',
|
|
39
|
-
timestamp: new Date().toISOString(),
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// POST to HTTP API server
|
|
43
|
-
try {
|
|
44
|
-
const res = await fetch(`http://localhost:${port}/api/hooks/ingest`, {
|
|
45
|
-
method: 'POST',
|
|
46
|
-
headers: { 'Content-Type': 'application/json' },
|
|
47
|
-
body: JSON.stringify({
|
|
48
|
-
knowledge: [knowledge],
|
|
49
|
-
sessionId: `git-${project}-${Date.now()}`,
|
|
50
|
-
}),
|
|
51
|
-
signal: AbortSignal.timeout(3000),
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
if (!res.ok) {
|
|
55
|
-
appendToQueue([knowledge])
|
|
56
|
-
}
|
|
57
|
-
} catch {
|
|
58
|
-
// Server unreachable — queue for later
|
|
59
|
-
appendToQueue([knowledge])
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
process.exit(0)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Phase 23b: Resolve project name from raw input.
|
|
67
|
-
* Strips npm scoped prefixes (@scope/name → name) and normalizes.
|
|
68
|
-
*/
|
|
69
|
-
function resolveProjectName(raw: string): string {
|
|
70
|
-
let name = raw.trim()
|
|
71
|
-
// Strip npm scoped prefix: @scope/name → name
|
|
72
|
-
if (name.startsWith('@') && name.includes('/')) {
|
|
73
|
-
name = name.split('/').pop() || name
|
|
74
|
-
}
|
|
75
|
-
return name || raw
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Detect technologies from file extensions.
|
|
80
|
-
*/
|
|
81
|
-
function detectTechnologies(files: string[]): string[] {
|
|
82
|
-
const techMap: Record<string, string> = {
|
|
83
|
-
'.ts': 'typescript',
|
|
84
|
-
'.tsx': 'react',
|
|
85
|
-
'.js': 'javascript',
|
|
86
|
-
'.jsx': 'react',
|
|
87
|
-
'.py': 'python',
|
|
88
|
-
'.rs': 'rust',
|
|
89
|
-
'.go': 'go',
|
|
90
|
-
'.rb': 'ruby',
|
|
91
|
-
'.java': 'java',
|
|
92
|
-
'.swift': 'swift',
|
|
93
|
-
'.kt': 'kotlin',
|
|
94
|
-
'.vue': 'vue',
|
|
95
|
-
'.svelte': 'svelte',
|
|
96
|
-
'.css': 'css',
|
|
97
|
-
'.scss': 'scss',
|
|
98
|
-
'.sql': 'sql',
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const techs = new Set<string>()
|
|
102
|
-
for (const file of files) {
|
|
103
|
-
const ext = '.' + file.split('.').pop()
|
|
104
|
-
if (techMap[ext]) {
|
|
105
|
-
techs.add(techMap[ext])
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return [...techs]
|
|
109
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Phase 21: Git Commit Capture
|
|
3
|
+
* Receives commit data from the post-commit hook and sends it to brain.
|
|
4
|
+
* Called as: claude-brain git-capture <project> <branch> <message> <files> <port>
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { appendToQueue } from './queue'
|
|
8
|
+
import type { CapturedKnowledge } from './types'
|
|
9
|
+
|
|
10
|
+
export async function handleGitCapture(): Promise<void> {
|
|
11
|
+
// Parse positional args: project, branch, message, files, port
|
|
12
|
+
const [, , , rawProject, branch, message, filesStr, portStr] = process.argv
|
|
13
|
+
|
|
14
|
+
if (!rawProject || !message) {
|
|
15
|
+
process.exit(0)
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Phase 23b: Resolve project name — strip scoped package prefix, validate
|
|
20
|
+
const project = resolveProjectName(rawProject)
|
|
21
|
+
const port = parseInt(portStr || process.env.CLAUDE_BRAIN_PORT || '3000', 10)
|
|
22
|
+
const files = filesStr ? filesStr.split(',').filter(Boolean) : []
|
|
23
|
+
|
|
24
|
+
// Build captured knowledge
|
|
25
|
+
const knowledge: CapturedKnowledge = {
|
|
26
|
+
type: 'progress',
|
|
27
|
+
confidence: 0.9,
|
|
28
|
+
content: `Git commit on ${branch || 'unknown'}: ${message.trim()}${files.length > 0 ? ` (files: ${files.join(', ')})` : ''}`,
|
|
29
|
+
project: project || undefined,
|
|
30
|
+
technologies: detectTechnologies(files),
|
|
31
|
+
metadata: {
|
|
32
|
+
source: 'git-hook',
|
|
33
|
+
branch: branch || 'unknown',
|
|
34
|
+
commit_message: message.trim(),
|
|
35
|
+
files,
|
|
36
|
+
file_count: files.length,
|
|
37
|
+
},
|
|
38
|
+
source: 'hook-passive',
|
|
39
|
+
timestamp: new Date().toISOString(),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// POST to HTTP API server
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch(`http://localhost:${port}/api/hooks/ingest`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: { 'Content-Type': 'application/json' },
|
|
47
|
+
body: JSON.stringify({
|
|
48
|
+
knowledge: [knowledge],
|
|
49
|
+
sessionId: `git-${project}-${Date.now()}`,
|
|
50
|
+
}),
|
|
51
|
+
signal: AbortSignal.timeout(3000),
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
appendToQueue([knowledge])
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
// Server unreachable — queue for later
|
|
59
|
+
appendToQueue([knowledge])
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
process.exit(0)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Phase 23b: Resolve project name from raw input.
|
|
67
|
+
* Strips npm scoped prefixes (@scope/name → name) and normalizes.
|
|
68
|
+
*/
|
|
69
|
+
function resolveProjectName(raw: string): string {
|
|
70
|
+
let name = raw.trim()
|
|
71
|
+
// Strip npm scoped prefix: @scope/name → name
|
|
72
|
+
if (name.startsWith('@') && name.includes('/')) {
|
|
73
|
+
name = name.split('/').pop() || name
|
|
74
|
+
}
|
|
75
|
+
return name || raw
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Detect technologies from file extensions.
|
|
80
|
+
*/
|
|
81
|
+
function detectTechnologies(files: string[]): string[] {
|
|
82
|
+
const techMap: Record<string, string> = {
|
|
83
|
+
'.ts': 'typescript',
|
|
84
|
+
'.tsx': 'react',
|
|
85
|
+
'.js': 'javascript',
|
|
86
|
+
'.jsx': 'react',
|
|
87
|
+
'.py': 'python',
|
|
88
|
+
'.rs': 'rust',
|
|
89
|
+
'.go': 'go',
|
|
90
|
+
'.rb': 'ruby',
|
|
91
|
+
'.java': 'java',
|
|
92
|
+
'.swift': 'swift',
|
|
93
|
+
'.kt': 'kotlin',
|
|
94
|
+
'.vue': 'vue',
|
|
95
|
+
'.svelte': 'svelte',
|
|
96
|
+
'.css': 'css',
|
|
97
|
+
'.scss': 'scss',
|
|
98
|
+
'.sql': 'sql',
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const techs = new Set<string>()
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
const ext = '.' + file.split('.').pop()
|
|
104
|
+
if (techMap[ext]) {
|
|
105
|
+
techs.add(techMap[ext])
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return [...techs]
|
|
109
|
+
}
|