claude-brain 0.25.0 → 0.25.1

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 CHANGED
@@ -1 +1 @@
1
- 0.25.0
1
+ 0.25.1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-brain",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "description": "Local development assistant bridging Obsidian vaults with Claude Code via MCP",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -165,6 +165,7 @@ logLevel: warn
165
165
  const HOOK_FILES = [
166
166
  'brain-hook.ts',
167
167
  'context-hook.ts',
168
+ 'interceptor-hook.ts',
168
169
  'capture.ts',
169
170
  'queue.ts',
170
171
  'types.ts',
@@ -215,7 +216,8 @@ function installHooks() {
215
216
  hasOurHooks(settings.hooks.PostToolUse) &&
216
217
  hasOurHooks(settings.hooks.Stop) &&
217
218
  hasOurHooks(settings.hooks.UserPromptSubmit) &&
218
- hasOurHooks(settings.hooks.SessionStart)) {
219
+ hasOurHooks(settings.hooks.SessionStart) &&
220
+ hasOurHooks(settings.hooks.PreToolUse)) {
219
221
  log('Hooks already installed')
220
222
  return true
221
223
  }
@@ -223,8 +225,10 @@ function installHooks() {
223
225
  // Build hook command
224
226
  const brainScriptPath = join(HOME, 'hooks', 'brain-hook.ts')
225
227
  const contextScriptPath = join(HOME, 'hooks', 'context-hook.ts')
228
+ const interceptorScriptPath = join(HOME, 'hooks', 'interceptor-hook.ts')
229
+ const port = process.env.CLAUDE_BRAIN_PORT || process.env.PORT || '3000'
226
230
  function buildCmd(event, scriptPath) {
227
- return `bun "${scriptPath}" --event ${event} # ${HOOK_MARKER}`
231
+ return `bun "${scriptPath}" --event ${event} --port ${port} # ${HOOK_MARKER}`
228
232
  }
229
233
 
230
234
  if (!settings.hooks) settings.hooks = {}
@@ -265,6 +269,19 @@ function installHooks() {
265
269
  })
266
270
  }
267
271
 
272
+ // PreToolUse — code intelligence interceptor for Glob and Grep
273
+ if (!hasOurHooks(settings.hooks.PreToolUse)) {
274
+ if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = []
275
+ settings.hooks.PreToolUse.push({
276
+ matcher: 'Glob',
277
+ hooks: [{ type: 'command', command: buildCmd('PreToolUse', interceptorScriptPath) }],
278
+ })
279
+ settings.hooks.PreToolUse.push({
280
+ matcher: 'Grep',
281
+ hooks: [{ type: 'command', command: buildCmd('PreToolUse', interceptorScriptPath) }],
282
+ })
283
+ }
284
+
268
285
  // Write atomically
269
286
  if (!existsSync(CLAUDE_DIR)) {
270
287
  mkdirSync(CLAUDE_DIR, { recursive: true })
@@ -109,7 +109,7 @@ async function main(): Promise<void> {
109
109
  }).catch(() => null)
110
110
 
111
111
  // Phase 29: Detect file paths in the user's prompt and fetch linked memories
112
- const projectName = input.cwd ? input.cwd.split('/').filter(Boolean).pop() : ''
112
+ const projectName = input.cwd ? input.cwd.split(/[/\\]/).filter(Boolean).pop() : ''
113
113
  const filePaths = extractFilePathsFromPrompt(input.prompt)
114
114
  const fileMemoryPromises = filePaths.slice(0, 3).map(fp =>
115
115
  fetch(`${baseUrl}/api/memory/for-file?file=${encodeURIComponent(fp)}&project=${encodeURIComponent(projectName || '')}`, {
@@ -135,7 +135,7 @@ async function main(): Promise<void> {
135
135
  if (input.cwd) params.set('cwd', input.cwd)
136
136
 
137
137
  // Extract project name from cwd for code map
138
- const projectName = input.cwd ? input.cwd.split('/').filter(Boolean).pop() : undefined
138
+ const projectName = input.cwd ? input.cwd.split(/[/\\]/).filter(Boolean).pop() : undefined
139
139
 
140
140
  // Fetch brain context AND code map in parallel
141
141
  const [contextRes, codeMapRes] = await Promise.all([
@@ -70,7 +70,7 @@ function resolveProjectName(raw: string): string {
70
70
  let name = raw.trim()
71
71
  // Strip npm scoped prefix: @scope/name → name
72
72
  if (name.startsWith('@') && name.includes('/')) {
73
- name = name.split('/').pop() || name
73
+ name = name.split(/[/\\]/).pop() || name
74
74
  }
75
75
  return name || raw
76
76
  }
@@ -230,7 +230,10 @@ export function isHooksInstalled(): boolean {
230
230
  const hasUserPromptSubmit = Array.isArray(settings.hooks.UserPromptSubmit) &&
231
231
  settings.hooks.UserPromptSubmit.some((entry: any) => isOurHookEntry(entry))
232
232
 
233
- return hasPostToolUse || hasStop || hasUserPromptSubmit
233
+ const hasPreToolUse = Array.isArray(settings.hooks.PreToolUse) &&
234
+ settings.hooks.PreToolUse.some((entry: any) => isOurHookEntry(entry))
235
+
236
+ return hasPostToolUse || hasStop || hasUserPromptSubmit || hasPreToolUse
234
237
  }
235
238
 
236
239
  /** Check if a hook entry belongs to us (by marker in command) */
@@ -133,7 +133,8 @@ function isComplexRegex(pattern: string): boolean {
133
133
 
134
134
  function detectProject(cwd?: string): string | null {
135
135
  if (!cwd) return null
136
- const segments = cwd.split('/').filter(Boolean)
136
+ // Split by both / and \ for cross-platform support (Windows uses backslashes)
137
+ const segments = cwd.split(/[/\\]/).filter(Boolean)
137
138
  return segments[segments.length - 1] || null
138
139
  }
139
140
 
@@ -305,7 +305,7 @@ export class PassiveClassifier {
305
305
  }
306
306
 
307
307
  // Check for Dockerfile without extension
308
- const basename = filePath.split('/').pop()?.toLowerCase() || ''
308
+ const basename = filePath.split(/[/\\]/).pop()?.toLowerCase() || ''
309
309
  if (basename === 'dockerfile' || basename.startsWith('dockerfile.')) {
310
310
  techs.push('docker')
311
311
  }