claude-brain 0.30.2 → 0.30.3
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 +241 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +29 -29
- package/package.json +7 -3
- 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 +531 -531
- package/src/automation/decision-detector.ts +452 -452
- 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 +210 -205
- package/src/cli/auto-setup.ts +75 -75
- package/src/cli/auto-start.ts +266 -266
- package/src/cli/bin.ts +264 -264
- package/src/cli/commands/autostart.ts +90 -90
- package/src/cli/commands/chroma.ts +578 -577
- package/src/cli/commands/export-training.ts +70 -70
- package/src/cli/commands/export.ts +130 -130
- package/src/cli/commands/git-hook.ts +183 -183
- package/src/cli/commands/hooks.ts +217 -217
- package/src/cli/commands/init.ts +123 -123
- package/src/cli/commands/install-mcp.ts +122 -111
- package/src/cli/commands/models.ts +979 -979
- package/src/cli/commands/pack.ts +200 -200
- package/src/cli/commands/refresh.ts +344 -339
- package/src/cli/commands/reindex.ts +120 -120
- package/src/cli/commands/serve.ts +466 -463
- package/src/cli/commands/start.ts +44 -44
- package/src/cli/commands/status.ts +220 -203
- package/src/cli/commands/uninstall-mcp.ts +45 -41
- package/src/cli/commands/update.ts +130 -124
- package/src/cli/migrate-chroma.ts +106 -106
- 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/code-intelligence/indexer.ts +352 -352
- package/src/code-intelligence/linker.ts +178 -178
- package/src/code-intelligence/parser.ts +484 -484
- package/src/code-intelligence/query.ts +291 -291
- package/src/code-intelligence/schema.ts +83 -83
- package/src/code-intelligence/types.ts +95 -95
- package/src/config/defaults.ts +52 -52
- package/src/config/home.ts +56 -56
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +192 -192
- package/src/config/schema.ts +446 -415
- package/src/config/validator.ts +182 -182
- package/src/context/assembler.ts +407 -400
- package/src/context/index.ts +79 -79
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +122 -121
- package/src/health/index.ts +233 -232
- package/src/hooks/brain-hook.ts +134 -131
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/claude-code-mastery.md +112 -112
- package/src/hooks/context-hook.ts +260 -245
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +211 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +306 -288
- package/src/hooks/interceptor-hook.ts +204 -201
- package/src/hooks/passive-classifier.ts +397 -397
- package/src/hooks/queue.ts +160 -129
- package/src/hooks/session-tracker.ts +312 -312
- package/src/hooks/types.ts +52 -52
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +7 -7
- package/src/intelligence/hf-downloader.ts +222 -222
- package/src/intelligence/hf-manifest.json +78 -78
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/inference-router.ts +762 -762
- package/src/intelligence/model-manager.ts +263 -245
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +213 -207
- package/src/intelligence/prediction/index.ts +7 -7
- package/src/intelligence/prediction/recommender.ts +276 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +243 -247
- package/src/intelligence/reasoning/index.ts +7 -7
- package/src/intelligence/temporal/evolution.ts +193 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +272 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/intelligence/tokenizer.ts +118 -118
- package/src/knowledge/entity-extractor.ts +447 -443
- 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 +166 -166
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +211 -192
- package/src/memory/chroma/collection-manager.ts +92 -92
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +177 -175
- 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 +319 -315
- package/src/memory/chroma/store.ts +755 -747
- package/src/memory/compression.ts +121 -121
- package/src/memory/consolidation/archiver.ts +162 -165
- package/src/memory/consolidation/merger.ts +182 -186
- package/src/memory/consolidation/scorer.ts +136 -136
- package/src/memory/database.ts +9 -0
- package/src/memory/dual-write.ts +145 -0
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +347 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/fts5-search.ts +692 -633
- package/src/memory/index.ts +943 -1060
- package/src/memory/migrations/add-fts5.ts +118 -108
- package/src/memory/patterns.ts +438 -438
- package/src/memory/pruning.ts +60 -60
- package/src/memory/schema.ts +88 -88
- package/src/memory/store.ts +911 -787
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- 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 -297
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +221 -221
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +221 -221
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +165 -165
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +203 -203
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +252 -252
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +189 -188
- package/src/retrieval/reranker/model.ts +99 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +454 -454
- package/src/routing/handlers/exploration-handler.ts +369 -0
- package/src/routing/handlers/index.ts +19 -0
- package/src/routing/handlers/memory-handler.ts +273 -0
- package/src/routing/handlers/mutation-handler.ts +241 -0
- package/src/routing/handlers/recall-handler.ts +642 -0
- package/src/routing/handlers/shared.ts +515 -0
- package/src/routing/handlers/types.ts +48 -0
- package/src/routing/intent-classifier.ts +552 -552
- package/src/routing/response-filter.ts +399 -391
- package/src/routing/router.ts +245 -2193
- package/src/routing/search-engine.ts +521 -514
- package/src/routing/types.ts +104 -94
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/auto-updater.ts +283 -276
- package/src/server/handlers/call-tool.ts +159 -159
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/auto-remember.ts +165 -165
- package/src/server/handlers/tools/brain.ts +86 -86
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/get-code-standards.ts +123 -123
- package/src/server/handlers/tools/get-corrections.ts +152 -152
- package/src/server/handlers/tools/get-patterns.ts +156 -156
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/index.ts +30 -30
- package/src/server/handlers/tools/init-project.ts +756 -756
- package/src/server/handlers/tools/list-projects.ts +126 -126
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +132 -132
- package/src/server/handlers/tools/record-correction.ts +131 -131
- package/src/server/handlers/tools/remember-decision.ts +168 -168
- package/src/server/handlers/tools/schemas.ts +179 -179
- package/src/server/handlers/tools/search-code.ts +122 -122
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/http-api.ts +215 -1229
- package/src/server/mcp-proxy.ts +85 -84
- package/src/server/mcp-server.ts +285 -284
- package/src/server/middleware/auth.ts +39 -0
- package/src/server/middleware/error-handler.ts +37 -0
- package/src/server/middleware/rate-limit.ts +53 -0
- package/src/server/middleware/validate.ts +42 -0
- package/src/server/pid-manager.ts +137 -136
- package/src/server/providers/resources.ts +581 -581
- package/src/server/routes/code.ts +228 -0
- package/src/server/routes/context.ts +26 -0
- package/src/server/routes/health.ts +19 -0
- package/src/server/routes/helpers.ts +100 -0
- package/src/server/routes/hooks.ts +197 -0
- package/src/server/routes/mcp.ts +47 -0
- package/src/server/routes/memory.ts +397 -0
- package/src/server/routes/models.ts +96 -0
- package/src/server/routes/projects.ts +89 -0
- package/src/server/routes/types.ts +21 -0
- package/src/server/schemas/api-schemas.ts +202 -0
- package/src/server/services.ts +720 -720
- package/src/server/utils/memory-indicator.ts +84 -84
- package/src/server/utils/response-formatter.ts +129 -129
- package/src/server/web-viewer.ts +1145 -1115
- package/src/setup/index.ts +38 -38
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.ts +666 -666
- package/src/tools/types.ts +412 -412
- package/src/training/data-store.ts +320 -298
- package/src/training/retrain-pipeline.ts +399 -394
- package/src/utils/error-handler.ts +136 -136
- package/src/utils/index.ts +58 -58
- package/src/utils/kill-port.ts +55 -53
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/safe-path.ts +43 -0
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/index.ts +4 -3
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +4 -1
- package/src/vault/reader.ts +44 -1
- package/src/vault/watcher.ts +24 -1
- package/src/vault/writer.ts +487 -413
- package/skills/persistent-memory/SKILL.md +0 -148
- package/skills/persistent-memory/references/tool-reference.md +0 -90
|
@@ -1,245 +1,260 @@
|
|
|
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
|
-
* SessionStart also injects the static Claude Code Mastery guidelines
|
|
8
|
-
* (claude-code-mastery.md) to optimize Claude's behavior every session.
|
|
9
|
-
*
|
|
10
|
-
* CRITICAL CONSTRAINTS:
|
|
11
|
-
* - Must complete in <3s (blocks user prompt processing)
|
|
12
|
-
* - No heavy imports — just fetch + JSON parse + fs.readFileSync
|
|
13
|
-
* - All errors silently caught with process.exit(0)
|
|
14
|
-
* - Outputs JSON to stdout: { hookSpecificOutput: { additionalContext: "..." } }
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { readFileSync, existsSync } from 'node:fs'
|
|
18
|
-
import { join, dirname } from 'node:path'
|
|
19
|
-
import { fileURLToPath } from 'node:url'
|
|
20
|
-
|
|
21
|
-
interface HookStdin {
|
|
22
|
-
session_id: string
|
|
23
|
-
hook_event_name: string
|
|
24
|
-
prompt?: string
|
|
25
|
-
cwd?: string
|
|
26
|
-
source?: string
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Cache the mastery doc in memory after first read */
|
|
30
|
-
let masteryCache: string | null = null
|
|
31
|
-
|
|
32
|
-
/** Read the static Claude Code Mastery guidelines from disk (once, then cached) */
|
|
33
|
-
function loadMasteryDoc(): string {
|
|
34
|
-
if (masteryCache !== null) return masteryCache
|
|
35
|
-
|
|
36
|
-
// Resolve relative to this script's location (works both in src/ and installed ~/.claude-brain/hooks/)
|
|
37
|
-
const scriptDir = (import.meta as any).dir ?? dirname(fileURLToPath(import.meta.url))
|
|
38
|
-
const masteryPath = join(scriptDir, 'claude-code-mastery.md')
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
if (existsSync(masteryPath)) {
|
|
42
|
-
masteryCache = readFileSync(masteryPath, 'utf-8').trim()
|
|
43
|
-
} else {
|
|
44
|
-
masteryCache = ''
|
|
45
|
-
}
|
|
46
|
-
} catch {
|
|
47
|
-
masteryCache = ''
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return masteryCache
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function main(): Promise<void> {
|
|
54
|
-
// Parse --event arg
|
|
55
|
-
const eventIdx = process.argv.indexOf('--event')
|
|
56
|
-
const eventName = eventIdx >= 0 ? process.argv[eventIdx + 1] : undefined
|
|
57
|
-
|
|
58
|
-
// Read stdin JSON from Claude Code
|
|
59
|
-
let rawInput: string
|
|
60
|
-
try {
|
|
61
|
-
rawInput = await readStdin()
|
|
62
|
-
} catch {
|
|
63
|
-
process.exit(0)
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (!rawInput.trim()) {
|
|
68
|
-
process.exit(0)
|
|
69
|
-
return
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let input: HookStdin
|
|
73
|
-
try {
|
|
74
|
-
input = JSON.parse(rawInput)
|
|
75
|
-
} catch {
|
|
76
|
-
process.exit(0)
|
|
77
|
-
return
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const event = eventName || input.hook_event_name
|
|
81
|
-
|
|
82
|
-
// Skip if hooks explicitly disabled
|
|
83
|
-
if (process.env.CLAUDE_BRAIN_HOOKS_ENABLED === 'false') {
|
|
84
|
-
process.exit(0)
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// BUG-006: Read port from --port arg (cross-platform). Env var fallback removed — breaks Windows cmd.exe.
|
|
89
|
-
const portIdx = process.argv.indexOf('--port')
|
|
90
|
-
const portArg = portIdx >= 0 ? process.argv[portIdx + 1] : undefined
|
|
91
|
-
const port = parseInt(portArg || '3000', 10)
|
|
92
|
-
const baseUrl = `http://localhost:${port}`
|
|
93
|
-
|
|
94
|
-
let brainContext = ''
|
|
95
|
-
let codeMapText = ''
|
|
96
|
-
let fileMemoryText = ''
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
if (event === 'UserPromptSubmit' && input.prompt) {
|
|
100
|
-
// Query for memories relevant to the user's prompt
|
|
101
|
-
const params = new URLSearchParams({
|
|
102
|
-
query: input.prompt.slice(0, 500), // Limit query length
|
|
103
|
-
limit: '5',
|
|
104
|
-
})
|
|
105
|
-
if (input.cwd) params.set('cwd', input.cwd)
|
|
106
|
-
|
|
107
|
-
const contextPromise = fetch(`${baseUrl}/api/hooks/context-query?${params}`, {
|
|
108
|
-
signal: AbortSignal.timeout(2000),
|
|
109
|
-
}).catch(() =>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
+
* SessionStart also injects the static Claude Code Mastery guidelines
|
|
8
|
+
* (claude-code-mastery.md) to optimize Claude's behavior every session.
|
|
9
|
+
*
|
|
10
|
+
* CRITICAL CONSTRAINTS:
|
|
11
|
+
* - Must complete in <3s (blocks user prompt processing)
|
|
12
|
+
* - No heavy imports — just fetch + JSON parse + fs.readFileSync
|
|
13
|
+
* - All errors silently caught with process.exit(0)
|
|
14
|
+
* - Outputs JSON to stdout: { hookSpecificOutput: { additionalContext: "..." } }
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { readFileSync, existsSync } from 'node:fs'
|
|
18
|
+
import { join, dirname } from 'node:path'
|
|
19
|
+
import { fileURLToPath } from 'node:url'
|
|
20
|
+
|
|
21
|
+
interface HookStdin {
|
|
22
|
+
session_id: string
|
|
23
|
+
hook_event_name: string
|
|
24
|
+
prompt?: string
|
|
25
|
+
cwd?: string
|
|
26
|
+
source?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Cache the mastery doc in memory after first read */
|
|
30
|
+
let masteryCache: string | null = null
|
|
31
|
+
|
|
32
|
+
/** Read the static Claude Code Mastery guidelines from disk (once, then cached) */
|
|
33
|
+
function loadMasteryDoc(): string {
|
|
34
|
+
if (masteryCache !== null) return masteryCache
|
|
35
|
+
|
|
36
|
+
// Resolve relative to this script's location (works both in src/ and installed ~/.claude-brain/hooks/)
|
|
37
|
+
const scriptDir = (import.meta as any).dir ?? dirname(fileURLToPath(import.meta.url))
|
|
38
|
+
const masteryPath = join(scriptDir, 'claude-code-mastery.md')
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
if (existsSync(masteryPath)) {
|
|
42
|
+
masteryCache = readFileSync(masteryPath, 'utf-8').trim()
|
|
43
|
+
} else {
|
|
44
|
+
masteryCache = ''
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
masteryCache = ''
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return masteryCache
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function main(): Promise<void> {
|
|
54
|
+
// Parse --event arg
|
|
55
|
+
const eventIdx = process.argv.indexOf('--event')
|
|
56
|
+
const eventName = eventIdx >= 0 ? process.argv[eventIdx + 1] : undefined
|
|
57
|
+
|
|
58
|
+
// Read stdin JSON from Claude Code
|
|
59
|
+
let rawInput: string
|
|
60
|
+
try {
|
|
61
|
+
rawInput = await readStdin()
|
|
62
|
+
} catch {
|
|
63
|
+
process.exit(0)
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!rawInput.trim()) {
|
|
68
|
+
process.exit(0)
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let input: HookStdin
|
|
73
|
+
try {
|
|
74
|
+
input = JSON.parse(rawInput)
|
|
75
|
+
} catch {
|
|
76
|
+
process.exit(0)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const event = eventName || input.hook_event_name
|
|
81
|
+
|
|
82
|
+
// Skip if hooks explicitly disabled
|
|
83
|
+
if (process.env.CLAUDE_BRAIN_HOOKS_ENABLED === 'false') {
|
|
84
|
+
process.exit(0)
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// BUG-006: Read port from --port arg (cross-platform). Env var fallback removed — breaks Windows cmd.exe.
|
|
89
|
+
const portIdx = process.argv.indexOf('--port')
|
|
90
|
+
const portArg = portIdx >= 0 ? process.argv[portIdx + 1] : undefined
|
|
91
|
+
const port = parseInt(portArg || '3000', 10)
|
|
92
|
+
const baseUrl = `http://localhost:${port}`
|
|
93
|
+
|
|
94
|
+
let brainContext = ''
|
|
95
|
+
let codeMapText = ''
|
|
96
|
+
let fileMemoryText = ''
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
if (event === 'UserPromptSubmit' && input.prompt) {
|
|
100
|
+
// Query for memories relevant to the user's prompt
|
|
101
|
+
const params = new URLSearchParams({
|
|
102
|
+
query: input.prompt.slice(0, 500), // Limit query length
|
|
103
|
+
limit: '5',
|
|
104
|
+
})
|
|
105
|
+
if (input.cwd) params.set('cwd', input.cwd)
|
|
106
|
+
|
|
107
|
+
const contextPromise = fetch(`${baseUrl}/api/hooks/context-query?${params}`, {
|
|
108
|
+
signal: AbortSignal.timeout(2000),
|
|
109
|
+
}).catch((error) => {
|
|
110
|
+
process.stderr.write(`[context-hook] Context query failed: ${error?.message || error}\n`)
|
|
111
|
+
return null
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// Phase 29: Detect file paths in the user's prompt and fetch linked memories
|
|
115
|
+
const projectName = input.cwd ? input.cwd.split(/[/\\]/).filter(Boolean).pop() : ''
|
|
116
|
+
const filePaths = extractFilePathsFromPrompt(input.prompt)
|
|
117
|
+
const fileMemoryPromises = filePaths.slice(0, 3).map(fp =>
|
|
118
|
+
fetch(`${baseUrl}/api/memory/for-file?file=${encodeURIComponent(fp)}&project=${encodeURIComponent(projectName || '')}`, {
|
|
119
|
+
signal: AbortSignal.timeout(1000),
|
|
120
|
+
}).then(r => r.ok ? r.json() : null).catch((error) => {
|
|
121
|
+
process.stderr.write(`[context-hook] File memory fetch failed: ${error?.message || error}\n`)
|
|
122
|
+
return null
|
|
123
|
+
})
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
const [contextRes, ...fileMemories] = await Promise.all([contextPromise, ...fileMemoryPromises])
|
|
127
|
+
|
|
128
|
+
if (contextRes?.ok) {
|
|
129
|
+
const data = await contextRes.json() as { success: boolean; context: string }
|
|
130
|
+
brainContext = data.context || ''
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Phase 29: Aggregate file memory summaries
|
|
134
|
+
const relevant = fileMemories.filter((m: Record<string, unknown>) => (m?.data as Record<string, unknown>)?.summary)
|
|
135
|
+
if (relevant.length > 0) {
|
|
136
|
+
fileMemoryText = relevant.map((m: Record<string, unknown>) => ((m.data as Record<string, unknown>)?.summary as string) || '').join('\n\n')
|
|
137
|
+
}
|
|
138
|
+
} else if (event === 'SessionStart') {
|
|
139
|
+
// Load broader project context on session start
|
|
140
|
+
const params = new URLSearchParams({ type: 'session-start' })
|
|
141
|
+
if (input.cwd) params.set('cwd', input.cwd)
|
|
142
|
+
|
|
143
|
+
// Extract project name from cwd for code map
|
|
144
|
+
const projectName = input.cwd ? input.cwd.split(/[/\\]/).filter(Boolean).pop() : undefined
|
|
145
|
+
|
|
146
|
+
// Fetch brain context AND code map in parallel
|
|
147
|
+
const [contextRes, codeMapRes] = await Promise.all([
|
|
148
|
+
fetch(`${baseUrl}/api/hooks/context-query?${params}`, { signal: AbortSignal.timeout(2000) }).catch((error) => {
|
|
149
|
+
process.stderr.write(`[context-hook] Session-start context query failed: ${error?.message || error}\n`)
|
|
150
|
+
return null
|
|
151
|
+
}),
|
|
152
|
+
projectName
|
|
153
|
+
? fetch(`${baseUrl}/api/code/file-map?project=${encodeURIComponent(projectName)}`, { signal: AbortSignal.timeout(2000) }).catch((error) => {
|
|
154
|
+
process.stderr.write(`[context-hook] Code map fetch failed: ${error?.message || error}\n`)
|
|
155
|
+
return null
|
|
156
|
+
})
|
|
157
|
+
: Promise.resolve(null),
|
|
158
|
+
])
|
|
159
|
+
|
|
160
|
+
// Process brain context (existing logic)
|
|
161
|
+
if (contextRes?.ok) {
|
|
162
|
+
const data = await contextRes.json() as { success: boolean; context: string }
|
|
163
|
+
brainContext = data.context || ''
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Process code map (Phase 28)
|
|
167
|
+
if (codeMapRes?.ok) {
|
|
168
|
+
try {
|
|
169
|
+
const data = await codeMapRes.json() as { success: boolean; data?: { map?: string } }
|
|
170
|
+
if (data.success && data.data?.map) {
|
|
171
|
+
codeMapText = data.data.map
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
// Silently ignore malformed code map response
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} catch {
|
|
179
|
+
// Server unreachable or timeout — continue with static content only for SessionStart
|
|
180
|
+
if (event !== 'SessionStart') {
|
|
181
|
+
process.exit(0)
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Build final injection content
|
|
187
|
+
const parts: string[] = []
|
|
188
|
+
|
|
189
|
+
// SessionStart: prepend static Claude Code Mastery guidelines
|
|
190
|
+
if (event === 'SessionStart') {
|
|
191
|
+
const mastery = loadMasteryDoc()
|
|
192
|
+
if (mastery) {
|
|
193
|
+
parts.push(`[Claude Code Mastery]\n${mastery}`)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Append dynamic brain memories (both events)
|
|
198
|
+
if (brainContext.trim()) {
|
|
199
|
+
parts.push(`[Brain Memory]\n${brainContext}`)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Phase 29: Append file-linked memories (UserPromptSubmit only)
|
|
203
|
+
if (fileMemoryText.trim()) {
|
|
204
|
+
parts.push(`[File Memories]\n${fileMemoryText}`)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Append code map (SessionStart only, Phase 28)
|
|
208
|
+
if (codeMapText.trim()) {
|
|
209
|
+
parts.push(`[Code Map]\nProject file index (use Read directly, skip Glob):\n${codeMapText}`)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Output combined context
|
|
213
|
+
if (parts.length > 0) {
|
|
214
|
+
const output = {
|
|
215
|
+
hookSpecificOutput: {
|
|
216
|
+
hookEventName: event,
|
|
217
|
+
additionalContext: parts.join('\n\n'),
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
process.stdout.write(JSON.stringify(output))
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
process.exit(0)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Phase 29: Extract file paths from user prompt text */
|
|
227
|
+
function extractFilePathsFromPrompt(prompt: string): string[] {
|
|
228
|
+
const pathPattern = /(?:^|\s)((?:src|lib|app|pages|components|utils|hooks|server|api|config|middleware|routes|models|services|types|scripts|tests|test)\/[\w\-./]+\.\w+)/g
|
|
229
|
+
const matches = [...prompt.matchAll(pathPattern)]
|
|
230
|
+
return [...new Set(matches.map(m => m[1]))]
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Read all of stdin as a string */
|
|
234
|
+
function readStdin(): Promise<string> {
|
|
235
|
+
return new Promise((resolve, reject) => {
|
|
236
|
+
const chunks: Buffer[] = []
|
|
237
|
+
const stdin = process.stdin
|
|
238
|
+
|
|
239
|
+
stdin.on('data', (chunk: Buffer) => chunks.push(chunk))
|
|
240
|
+
stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
|
|
241
|
+
stdin.on('error', reject)
|
|
242
|
+
|
|
243
|
+
// Timeout after 2 seconds
|
|
244
|
+
setTimeout(() => {
|
|
245
|
+
stdin.destroy()
|
|
246
|
+
resolve(Buffer.concat(chunks).toString('utf-8'))
|
|
247
|
+
}, 2000)
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Execute when run directly
|
|
252
|
+
const isDirectRun = process.argv[1]?.includes('context-hook')
|
|
253
|
+
if (isDirectRun) {
|
|
254
|
+
main().catch((error) => {
|
|
255
|
+
process.stderr.write(`[context-hook] Fatal error: ${error?.message || error}\n`)
|
|
256
|
+
process.exit(1)
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
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
|
+
}
|