claude-brain 0.13.1 → 0.14.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/package.json +1 -1
- package/src/cli/commands/init.ts +24 -0
- package/src/config/defaults.ts +1 -1
- package/src/config/schema.ts +1 -1
- package/src/hooks/git-capture.ts +17 -2
- package/src/hooks/git-hook-installer.ts +11 -1
- package/src/routing/intent-classifier.ts +11 -3
- package/src/routing/router.ts +48 -0
- package/src/server/http-api.ts +22 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.14.0
|
package/package.json
CHANGED
package/src/cli/commands/init.ts
CHANGED
|
@@ -32,6 +32,30 @@ export async function runInit() {
|
|
|
32
32
|
}
|
|
33
33
|
console.log()
|
|
34
34
|
|
|
35
|
+
// Phase 23b: Validate project directory
|
|
36
|
+
const { existsSync: dirExists } = await import('fs')
|
|
37
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || ''
|
|
38
|
+
if (repoPath === homeDir) {
|
|
39
|
+
console.log(warningText(' This is your home directory — not a project root.'))
|
|
40
|
+
console.log(dimText(' Run this inside a project directory, or pass a path: claude-brain init /path/to/project'))
|
|
41
|
+
console.log()
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const hasGit = dirExists(path.join(repoPath, '.git'))
|
|
46
|
+
const hasPackageJson = dirExists(path.join(repoPath, 'package.json'))
|
|
47
|
+
const hasCargoToml = dirExists(path.join(repoPath, 'Cargo.toml'))
|
|
48
|
+
const hasPyproject = dirExists(path.join(repoPath, 'pyproject.toml'))
|
|
49
|
+
const hasGoMod = dirExists(path.join(repoPath, 'go.mod'))
|
|
50
|
+
const isProject = hasGit || hasPackageJson || hasCargoToml || hasPyproject || hasGoMod
|
|
51
|
+
|
|
52
|
+
if (!isProject && !force) {
|
|
53
|
+
console.log(warningText(' No project markers found (no .git, package.json, Cargo.toml, etc.)'))
|
|
54
|
+
console.log(dimText(' Use --force to scan anyway, or run inside a project directory.'))
|
|
55
|
+
console.log()
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
35
59
|
// Scan the repo
|
|
36
60
|
const context = await withSpinner('Scanning repository', () => scanRepo(repoPath))
|
|
37
61
|
|
package/src/config/defaults.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { PartialConfig } from './schema'
|
|
|
3
3
|
/** Default configuration values for Claude Brain */
|
|
4
4
|
export const defaultConfig: PartialConfig = {
|
|
5
5
|
serverName: 'claude-brain',
|
|
6
|
-
serverVersion: '0.
|
|
6
|
+
serverVersion: '0.14.0',
|
|
7
7
|
logLevel: 'info',
|
|
8
8
|
logFilePath: './logs/claude-brain.log',
|
|
9
9
|
dbPath: './data/memory.db',
|
package/src/config/schema.ts
CHANGED
|
@@ -284,7 +284,7 @@ export const ConfigSchema = z.object({
|
|
|
284
284
|
serverName: z.string().default('claude-brain'),
|
|
285
285
|
|
|
286
286
|
/** Server version in semver format */
|
|
287
|
-
serverVersion: z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be semver format').default('0.
|
|
287
|
+
serverVersion: z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be semver format').default('0.14.0'),
|
|
288
288
|
|
|
289
289
|
/** Logging level */
|
|
290
290
|
logLevel: LogLevelSchema.default('info'),
|
package/src/hooks/git-capture.ts
CHANGED
|
@@ -9,13 +9,15 @@ import type { CapturedKnowledge } from './types'
|
|
|
9
9
|
|
|
10
10
|
export async function handleGitCapture(): Promise<void> {
|
|
11
11
|
// Parse positional args: project, branch, message, files, port
|
|
12
|
-
const [, , ,
|
|
12
|
+
const [, , , rawProject, branch, message, filesStr, portStr] = process.argv
|
|
13
13
|
|
|
14
|
-
if (!
|
|
14
|
+
if (!rawProject || !message) {
|
|
15
15
|
process.exit(0)
|
|
16
16
|
return
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// Phase 23b: Resolve project name — strip scoped package prefix, validate
|
|
20
|
+
const project = resolveProjectName(rawProject)
|
|
19
21
|
const port = parseInt(portStr || process.env.CLAUDE_BRAIN_PORT || '3000', 10)
|
|
20
22
|
const files = filesStr ? filesStr.split(',').filter(Boolean) : []
|
|
21
23
|
|
|
@@ -60,6 +62,19 @@ export async function handleGitCapture(): Promise<void> {
|
|
|
60
62
|
process.exit(0)
|
|
61
63
|
}
|
|
62
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
|
+
|
|
63
78
|
/**
|
|
64
79
|
* Detect technologies from file extensions.
|
|
65
80
|
*/
|
|
@@ -25,7 +25,17 @@ function buildHookScript(): string {
|
|
|
25
25
|
' BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)',
|
|
26
26
|
' MESSAGE=$(git log -1 --pretty=%B 2>/dev/null)',
|
|
27
27
|
' FILES=$(git diff-tree --no-commit-id --name-only -r HEAD 2>/dev/null | tr "\\n" "," | sed "s/,$//")',
|
|
28
|
-
'
|
|
28
|
+
' REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)',
|
|
29
|
+
' # Phase 23b: Smart project name detection (package.json > init-context > basename)',
|
|
30
|
+
' if [ -f "$REPO_ROOT/package.json" ]; then',
|
|
31
|
+
' PROJECT=$(grep -m1 \'"name"\' "$REPO_ROOT/package.json" 2>/dev/null | sed \'s/.*"name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/\' | sed \'s/@[^/]*\\///\')',
|
|
32
|
+
' fi',
|
|
33
|
+
' if [ -z "$PROJECT" ] && [ -f "$REPO_ROOT/.claude-brain/init-context.md" ]; then',
|
|
34
|
+
' PROJECT=$(head -1 "$REPO_ROOT/.claude-brain/init-context.md" 2>/dev/null | sed \'s/^Project "\\([^"]*\\)".*/\\1/\')',
|
|
35
|
+
' fi',
|
|
36
|
+
' if [ -z "$PROJECT" ]; then',
|
|
37
|
+
' PROJECT=$(basename "$REPO_ROOT")',
|
|
38
|
+
' fi',
|
|
29
39
|
' PORT=${CLAUDE_BRAIN_PORT:-3000}',
|
|
30
40
|
' claude-brain git-capture "$PROJECT" "$BRANCH" "$MESSAGE" "$FILES" "$PORT" 2>/dev/null',
|
|
31
41
|
') &',
|
|
@@ -88,7 +88,12 @@ const PROGRESS_PHRASES = [
|
|
|
88
88
|
'built', 'created', 'added', 'fixed', 'resolved',
|
|
89
89
|
'shipped', 'deployed', 'merged', 'released',
|
|
90
90
|
"i'm working on", 'currently working', 'making progress',
|
|
91
|
-
'just did', 'just finished'
|
|
91
|
+
'just did', 'just finished',
|
|
92
|
+
// Phase 23b: present-tense statement patterns
|
|
93
|
+
"i'm implementing", "i'm building", "i'm adding",
|
|
94
|
+
"i'm fixing", "i'm refactoring", "i'm setting up",
|
|
95
|
+
"i'm migrating", "i'm updating", "i'm creating",
|
|
96
|
+
'started implementing', 'started building', 'started adding'
|
|
92
97
|
]
|
|
93
98
|
|
|
94
99
|
// Comparison indicators
|
|
@@ -156,10 +161,13 @@ const LIST_PHRASES = [
|
|
|
156
161
|
// Update/correct existing memory
|
|
157
162
|
const UPDATE_PHRASES = [
|
|
158
163
|
'actually,', 'actually ', 'correction:', 'update that',
|
|
159
|
-
'i changed my mind', 'change that to', 'instead of what i said',
|
|
164
|
+
'i changed my mind', 'changed my mind', 'change that to', 'instead of what i said',
|
|
160
165
|
'supersedes', 'override', 'revise that', 'amend that',
|
|
161
166
|
'that should be', 'update the decision', 'modify that',
|
|
162
|
-
'replace that with', 'no wait', 'scratch that, use'
|
|
167
|
+
'replace that with', 'no wait', 'scratch that, use',
|
|
168
|
+
// Phase 23b: additional update triggers
|
|
169
|
+
'switch to', 'instead use', 'use instead', 'no longer using',
|
|
170
|
+
'moved to', 'migrated to', 'replaced with'
|
|
163
171
|
]
|
|
164
172
|
|
|
165
173
|
// Delete/forget memory
|
package/src/routing/router.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
getPhase12Service,
|
|
24
24
|
getKnowledgeGraphService,
|
|
25
25
|
getEpisodeService,
|
|
26
|
+
getSessionTracker,
|
|
26
27
|
isServicesInitialized
|
|
27
28
|
} from '@/server/services'
|
|
28
29
|
import { timed } from '@/utils/timing'
|
|
@@ -926,6 +927,29 @@ export class BrainRouter {
|
|
|
926
927
|
}
|
|
927
928
|
}
|
|
928
929
|
|
|
930
|
+
// Phase 23b Fix 5: Session recall — "what have we discussed" queries session tracker
|
|
931
|
+
if (this.isSessionRecallQuery(message)) {
|
|
932
|
+
try {
|
|
933
|
+
const tracker = getSessionTracker()
|
|
934
|
+
if (tracker) {
|
|
935
|
+
const stats = tracker.getStats()
|
|
936
|
+
if (stats.totalItems > 0) {
|
|
937
|
+
tiers.push({
|
|
938
|
+
label: 'Current Session',
|
|
939
|
+
results: [{
|
|
940
|
+
content: `Active sessions: ${stats.activeSessions}, items tracked: ${stats.totalItems}`,
|
|
941
|
+
score: 0.95,
|
|
942
|
+
source: 'Session Tracker',
|
|
943
|
+
metadata: {}
|
|
944
|
+
}]
|
|
945
|
+
})
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
} catch {
|
|
949
|
+
// Session tracker not available
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
929
953
|
// Register with episode
|
|
930
954
|
this.registerEpisodeMessage(message, effectiveProject, 'question')
|
|
931
955
|
|
|
@@ -1203,6 +1227,30 @@ export class BrainRouter {
|
|
|
1203
1227
|
}
|
|
1204
1228
|
}
|
|
1205
1229
|
|
|
1230
|
+
/**
|
|
1231
|
+
* Phase 23b: Detect session recall queries ("what have we discussed", "this session", etc.)
|
|
1232
|
+
*/
|
|
1233
|
+
private isSessionRecallQuery(message: string): boolean {
|
|
1234
|
+
const lower = message.toLowerCase()
|
|
1235
|
+
const SESSION_RECALL_PHRASES = [
|
|
1236
|
+
'what have we discussed',
|
|
1237
|
+
'what did we discuss',
|
|
1238
|
+
'what have we talked about',
|
|
1239
|
+
'what did we talk about',
|
|
1240
|
+
'this session',
|
|
1241
|
+
'current session',
|
|
1242
|
+
'session so far',
|
|
1243
|
+
'what have we done',
|
|
1244
|
+
'what did we do',
|
|
1245
|
+
'session summary',
|
|
1246
|
+
'summarize this session',
|
|
1247
|
+
'recap this session',
|
|
1248
|
+
'what happened this session',
|
|
1249
|
+
'what have we covered'
|
|
1250
|
+
]
|
|
1251
|
+
return SESSION_RECALL_PHRASES.some(p => lower.includes(p))
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1206
1254
|
/**
|
|
1207
1255
|
* C7: Detect complex multi-part questions
|
|
1208
1256
|
*/
|
package/src/server/http-api.ts
CHANGED
|
@@ -7,6 +7,7 @@ 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 { ResourceProvider } from '@/server/providers/resources'
|
|
10
11
|
import type { MemoryManager } from '@/memory'
|
|
11
12
|
import type { CapturedKnowledge, HookStats } from '@/hooks/types'
|
|
12
13
|
import { SmartDeduplicator } from '@/hooks/deduplicator'
|
|
@@ -81,6 +82,9 @@ export class HttpApiServer {
|
|
|
81
82
|
this.app.post('/api/hooks/ingest', (c) => this.handleHookIngest(c))
|
|
82
83
|
this.app.post('/api/hooks/session-end', (c) => this.handleHookSessionEnd(c))
|
|
83
84
|
this.app.get('/api/hooks/status', () => this.handleHookStatus())
|
|
85
|
+
|
|
86
|
+
// Phase 23b: Expose brain://context/auto via HTTP for testability
|
|
87
|
+
this.app.get('/api/context/auto', () => this.handleContextAuto())
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
private async handleListProjects(): Promise<Response> {
|
|
@@ -458,6 +462,23 @@ export class HttpApiServer {
|
|
|
458
462
|
}
|
|
459
463
|
}
|
|
460
464
|
|
|
465
|
+
// ─── Phase 23b: Context Auto Endpoint ────────────────────
|
|
466
|
+
|
|
467
|
+
private async handleContextAuto(): Promise<Response> {
|
|
468
|
+
try {
|
|
469
|
+
const resourceProvider = new ResourceProvider(this.logger)
|
|
470
|
+
const result = await resourceProvider.readResource('brain://context/auto')
|
|
471
|
+
const text = result?.contents?.[0]?.text || ''
|
|
472
|
+
return Response.json({ success: true, data: { content: text } })
|
|
473
|
+
} catch (error) {
|
|
474
|
+
this.logger.error({ error }, 'Failed to get auto context')
|
|
475
|
+
return Response.json(
|
|
476
|
+
{ success: false, error: 'Failed to get auto context' },
|
|
477
|
+
{ status: 500 }
|
|
478
|
+
)
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
461
482
|
// ─── Phase 17: Hook Endpoints ────────────────────────────
|
|
462
483
|
|
|
463
484
|
private async handleHookIngest(c: any): Promise<Response> {
|
|
@@ -564,7 +585,7 @@ export class HttpApiServer {
|
|
|
564
585
|
...this.hookStats,
|
|
565
586
|
activeSessions: sessionStats?.activeSessions ?? 0,
|
|
566
587
|
sessionItems: sessionStats?.totalItems ?? 0,
|
|
567
|
-
hooksEnabled: this.config.hooks?.enabled ??
|
|
588
|
+
hooksEnabled: this.config.hooks?.enabled ?? true,
|
|
568
589
|
},
|
|
569
590
|
})
|
|
570
591
|
}
|