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,178 +1,178 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MemoryCodeLinker — Phase 29: Code-Anchored Memory
|
|
3
|
-
*
|
|
4
|
-
* Links stored observations to code files automatically using three strategies:
|
|
5
|
-
* 1. Explicit file paths mentioned in content
|
|
6
|
-
* 2. Recently edited files (from activity_log)
|
|
7
|
-
* 3. Symbol name matching (from code_symbols table)
|
|
8
|
-
*
|
|
9
|
-
* All linkage is fire-and-forget — failures are non-fatal.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { Database } from 'bun:sqlite'
|
|
13
|
-
import type { Logger } from 'pino'
|
|
14
|
-
|
|
15
|
-
/** Regex to extract file paths from memory content */
|
|
16
|
-
const FILE_PATH_REGEX = /(?:^|\s)((?:src|lib|app|pages|components|utils|hooks|server|api|config|middleware|routes|models|services|types|scripts|tests|test)\/[\w\-./]+\.\w+)/g
|
|
17
|
-
|
|
18
|
-
/** Regex to extract camelCase/PascalCase/snake_case identifiers >4 chars */
|
|
19
|
-
const SYMBOL_REGEX = /\b([a-z][a-zA-Z0-9]{3,}|[A-Z][a-zA-Z0-9]{3,}|[a-z]+(?:_[a-z]+){1,})\b/g
|
|
20
|
-
|
|
21
|
-
/** Max symbol candidates to query against code_symbols */
|
|
22
|
-
const MAX_SYMBOL_CANDIDATES = 20
|
|
23
|
-
|
|
24
|
-
export class MemoryCodeLinker {
|
|
25
|
-
constructor(
|
|
26
|
-
private memoryDb: Database,
|
|
27
|
-
private codeDb: Database | null,
|
|
28
|
-
private logger: Logger
|
|
29
|
-
) {
|
|
30
|
-
this.logger = logger.child({ component: 'code-linker' })
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Link an observation to relevant code files.
|
|
35
|
-
* Called after storing an observation — non-blocking, fire-and-forget.
|
|
36
|
-
* Returns the file paths that were linked.
|
|
37
|
-
*/
|
|
38
|
-
async linkObservation(observationId: string, content: string, project: string): Promise<string[]> {
|
|
39
|
-
const filePaths = new Set<string>()
|
|
40
|
-
const symbols = new Set<string>()
|
|
41
|
-
|
|
42
|
-
// Strategy 1: Explicit file paths in content
|
|
43
|
-
try {
|
|
44
|
-
const explicitPaths = this.extractFilePaths(content)
|
|
45
|
-
for (const p of explicitPaths) filePaths.add(p)
|
|
46
|
-
} catch (err) {
|
|
47
|
-
this.logger.debug({ err }, 'Strategy 1 (explicit paths) failed')
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Strategy 2: Recently edited files
|
|
51
|
-
try {
|
|
52
|
-
const recentFiles = this.getRecentlyEditedFiles(project)
|
|
53
|
-
for (const p of recentFiles) filePaths.add(p)
|
|
54
|
-
} catch (err) {
|
|
55
|
-
this.logger.debug({ err }, 'Strategy 2 (recent files) failed')
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Strategy 3: Symbol name matching (only when codeDb available)
|
|
59
|
-
if (this.codeDb) {
|
|
60
|
-
try {
|
|
61
|
-
const symbolMatches = this.matchSymbols(content, project)
|
|
62
|
-
for (const match of symbolMatches) {
|
|
63
|
-
filePaths.add(match.filePath)
|
|
64
|
-
symbols.add(match.symbol)
|
|
65
|
-
}
|
|
66
|
-
} catch (err) {
|
|
67
|
-
this.logger.debug({ err }, 'Strategy 3 (symbol matching) failed')
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (filePaths.size === 0 && symbols.size === 0) {
|
|
72
|
-
return []
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Update the observation with linked paths and symbols
|
|
76
|
-
const pathsArray = [...filePaths]
|
|
77
|
-
const symbolsArray = [...symbols]
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
this.memoryDb.prepare(
|
|
81
|
-
`UPDATE observations SET file_paths = ?, symbols = ?, updated_at = ? WHERE id = ?`
|
|
82
|
-
).run(
|
|
83
|
-
JSON.stringify(pathsArray),
|
|
84
|
-
JSON.stringify(symbolsArray),
|
|
85
|
-
new Date().toISOString(),
|
|
86
|
-
observationId
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
this.logger.debug(
|
|
90
|
-
{ observationId, paths: pathsArray.length, symbols: symbolsArray.length },
|
|
91
|
-
'Observation linked to code'
|
|
92
|
-
)
|
|
93
|
-
} catch (err) {
|
|
94
|
-
this.logger.debug({ err, observationId }, 'Failed to update observation with code links')
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return pathsArray
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Strategy 1: Extract explicit file paths from content.
|
|
102
|
-
*/
|
|
103
|
-
private extractFilePaths(content: string): string[] {
|
|
104
|
-
const paths: string[] = []
|
|
105
|
-
let match: RegExpExecArray | null
|
|
106
|
-
|
|
107
|
-
// Reset regex state
|
|
108
|
-
FILE_PATH_REGEX.lastIndex = 0
|
|
109
|
-
while ((match = FILE_PATH_REGEX.exec(content)) !== null) {
|
|
110
|
-
paths.push(match[1])
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return [...new Set(paths)]
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Strategy 2: Get recently edited files from activity_log.
|
|
118
|
-
* Gracefully degrades if the table doesn't exist or has a different schema.
|
|
119
|
-
*/
|
|
120
|
-
private getRecentlyEditedFiles(project: string): string[] {
|
|
121
|
-
try {
|
|
122
|
-
const rows = this.memoryDb.prepare(`
|
|
123
|
-
SELECT DISTINCT json_extract(metadata, '$.file_path') as file_path
|
|
124
|
-
FROM activity_log
|
|
125
|
-
WHERE project = ?
|
|
126
|
-
AND tool_name IN ('Write', 'Edit')
|
|
127
|
-
AND json_extract(metadata, '$.file_path') IS NOT NULL
|
|
128
|
-
ORDER BY created_at DESC
|
|
129
|
-
LIMIT 5
|
|
130
|
-
`).all(project) as { file_path: string }[]
|
|
131
|
-
|
|
132
|
-
return rows.map(r => r.file_path).filter(Boolean)
|
|
133
|
-
} catch {
|
|
134
|
-
// activity_log table may not exist — graceful degradation
|
|
135
|
-
return []
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Strategy 3: Match potential symbol names from content against code_symbols.
|
|
141
|
-
*/
|
|
142
|
-
private matchSymbols(content: string, project: string): { symbol: string; filePath: string }[] {
|
|
143
|
-
if (!this.codeDb) return []
|
|
144
|
-
|
|
145
|
-
// Extract candidate symbols from content
|
|
146
|
-
const candidates = new Set<string>()
|
|
147
|
-
let match: RegExpExecArray | null
|
|
148
|
-
|
|
149
|
-
SYMBOL_REGEX.lastIndex = 0
|
|
150
|
-
while ((match = SYMBOL_REGEX.exec(content)) !== null) {
|
|
151
|
-
if (candidates.size >= MAX_SYMBOL_CANDIDATES) break
|
|
152
|
-
candidates.add(match[1])
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (candidates.size === 0) return []
|
|
156
|
-
|
|
157
|
-
// Query code_symbols for matches
|
|
158
|
-
const results: { symbol: string; filePath: string }[] = []
|
|
159
|
-
const placeholders = [...candidates].map(() => '?').join(', ')
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
const rows = this.codeDb.prepare(`
|
|
163
|
-
SELECT DISTINCT symbol_name, file_path
|
|
164
|
-
FROM code_symbols
|
|
165
|
-
WHERE project = ? AND symbol_name IN (${placeholders})
|
|
166
|
-
LIMIT 20
|
|
167
|
-
`).all(project, ...candidates) as { symbol_name: string; file_path: string }[]
|
|
168
|
-
|
|
169
|
-
for (const row of rows) {
|
|
170
|
-
results.push({ symbol: row.symbol_name, filePath: row.file_path })
|
|
171
|
-
}
|
|
172
|
-
} catch (err) {
|
|
173
|
-
this.logger.debug({ err }, 'Symbol matching query failed')
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return results
|
|
177
|
-
}
|
|
178
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* MemoryCodeLinker — Phase 29: Code-Anchored Memory
|
|
3
|
+
*
|
|
4
|
+
* Links stored observations to code files automatically using three strategies:
|
|
5
|
+
* 1. Explicit file paths mentioned in content
|
|
6
|
+
* 2. Recently edited files (from activity_log)
|
|
7
|
+
* 3. Symbol name matching (from code_symbols table)
|
|
8
|
+
*
|
|
9
|
+
* All linkage is fire-and-forget — failures are non-fatal.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Database } from 'bun:sqlite'
|
|
13
|
+
import type { Logger } from 'pino'
|
|
14
|
+
|
|
15
|
+
/** Regex to extract file paths from memory content */
|
|
16
|
+
const FILE_PATH_REGEX = /(?:^|\s)((?:src|lib|app|pages|components|utils|hooks|server|api|config|middleware|routes|models|services|types|scripts|tests|test)\/[\w\-./]+\.\w+)/g
|
|
17
|
+
|
|
18
|
+
/** Regex to extract camelCase/PascalCase/snake_case identifiers >4 chars */
|
|
19
|
+
const SYMBOL_REGEX = /\b([a-z][a-zA-Z0-9]{3,}|[A-Z][a-zA-Z0-9]{3,}|[a-z]+(?:_[a-z]+){1,})\b/g
|
|
20
|
+
|
|
21
|
+
/** Max symbol candidates to query against code_symbols */
|
|
22
|
+
const MAX_SYMBOL_CANDIDATES = 20
|
|
23
|
+
|
|
24
|
+
export class MemoryCodeLinker {
|
|
25
|
+
constructor(
|
|
26
|
+
private memoryDb: Database,
|
|
27
|
+
private codeDb: Database | null,
|
|
28
|
+
private logger: Logger
|
|
29
|
+
) {
|
|
30
|
+
this.logger = logger.child({ component: 'code-linker' })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Link an observation to relevant code files.
|
|
35
|
+
* Called after storing an observation — non-blocking, fire-and-forget.
|
|
36
|
+
* Returns the file paths that were linked.
|
|
37
|
+
*/
|
|
38
|
+
async linkObservation(observationId: string, content: string, project: string): Promise<string[]> {
|
|
39
|
+
const filePaths = new Set<string>()
|
|
40
|
+
const symbols = new Set<string>()
|
|
41
|
+
|
|
42
|
+
// Strategy 1: Explicit file paths in content
|
|
43
|
+
try {
|
|
44
|
+
const explicitPaths = this.extractFilePaths(content)
|
|
45
|
+
for (const p of explicitPaths) filePaths.add(p)
|
|
46
|
+
} catch (err) {
|
|
47
|
+
this.logger.debug({ err }, 'Strategy 1 (explicit paths) failed')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Strategy 2: Recently edited files
|
|
51
|
+
try {
|
|
52
|
+
const recentFiles = this.getRecentlyEditedFiles(project)
|
|
53
|
+
for (const p of recentFiles) filePaths.add(p)
|
|
54
|
+
} catch (err) {
|
|
55
|
+
this.logger.debug({ err }, 'Strategy 2 (recent files) failed')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Strategy 3: Symbol name matching (only when codeDb available)
|
|
59
|
+
if (this.codeDb) {
|
|
60
|
+
try {
|
|
61
|
+
const symbolMatches = this.matchSymbols(content, project)
|
|
62
|
+
for (const match of symbolMatches) {
|
|
63
|
+
filePaths.add(match.filePath)
|
|
64
|
+
symbols.add(match.symbol)
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
this.logger.debug({ err }, 'Strategy 3 (symbol matching) failed')
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (filePaths.size === 0 && symbols.size === 0) {
|
|
72
|
+
return []
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Update the observation with linked paths and symbols
|
|
76
|
+
const pathsArray = [...filePaths]
|
|
77
|
+
const symbolsArray = [...symbols]
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
this.memoryDb.prepare(
|
|
81
|
+
`UPDATE observations SET file_paths = ?, symbols = ?, updated_at = ? WHERE id = ?`
|
|
82
|
+
).run(
|
|
83
|
+
JSON.stringify(pathsArray),
|
|
84
|
+
JSON.stringify(symbolsArray),
|
|
85
|
+
new Date().toISOString(),
|
|
86
|
+
observationId
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
this.logger.debug(
|
|
90
|
+
{ observationId, paths: pathsArray.length, symbols: symbolsArray.length },
|
|
91
|
+
'Observation linked to code'
|
|
92
|
+
)
|
|
93
|
+
} catch (err) {
|
|
94
|
+
this.logger.debug({ err, observationId }, 'Failed to update observation with code links')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return pathsArray
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Strategy 1: Extract explicit file paths from content.
|
|
102
|
+
*/
|
|
103
|
+
private extractFilePaths(content: string): string[] {
|
|
104
|
+
const paths: string[] = []
|
|
105
|
+
let match: RegExpExecArray | null
|
|
106
|
+
|
|
107
|
+
// Reset regex state
|
|
108
|
+
FILE_PATH_REGEX.lastIndex = 0
|
|
109
|
+
while ((match = FILE_PATH_REGEX.exec(content)) !== null) {
|
|
110
|
+
paths.push(match[1])
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return [...new Set(paths)]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Strategy 2: Get recently edited files from activity_log.
|
|
118
|
+
* Gracefully degrades if the table doesn't exist or has a different schema.
|
|
119
|
+
*/
|
|
120
|
+
private getRecentlyEditedFiles(project: string): string[] {
|
|
121
|
+
try {
|
|
122
|
+
const rows = this.memoryDb.prepare(`
|
|
123
|
+
SELECT DISTINCT json_extract(metadata, '$.file_path') as file_path
|
|
124
|
+
FROM activity_log
|
|
125
|
+
WHERE project = ?
|
|
126
|
+
AND tool_name IN ('Write', 'Edit')
|
|
127
|
+
AND json_extract(metadata, '$.file_path') IS NOT NULL
|
|
128
|
+
ORDER BY created_at DESC
|
|
129
|
+
LIMIT 5
|
|
130
|
+
`).all(project) as { file_path: string }[]
|
|
131
|
+
|
|
132
|
+
return rows.map(r => r.file_path).filter(Boolean)
|
|
133
|
+
} catch {
|
|
134
|
+
// activity_log table may not exist — graceful degradation
|
|
135
|
+
return []
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Strategy 3: Match potential symbol names from content against code_symbols.
|
|
141
|
+
*/
|
|
142
|
+
private matchSymbols(content: string, project: string): { symbol: string; filePath: string }[] {
|
|
143
|
+
if (!this.codeDb) return []
|
|
144
|
+
|
|
145
|
+
// Extract candidate symbols from content
|
|
146
|
+
const candidates = new Set<string>()
|
|
147
|
+
let match: RegExpExecArray | null
|
|
148
|
+
|
|
149
|
+
SYMBOL_REGEX.lastIndex = 0
|
|
150
|
+
while ((match = SYMBOL_REGEX.exec(content)) !== null) {
|
|
151
|
+
if (candidates.size >= MAX_SYMBOL_CANDIDATES) break
|
|
152
|
+
candidates.add(match[1])
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (candidates.size === 0) return []
|
|
156
|
+
|
|
157
|
+
// Query code_symbols for matches
|
|
158
|
+
const results: { symbol: string; filePath: string }[] = []
|
|
159
|
+
const placeholders = [...candidates].map(() => '?').join(', ')
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const rows = this.codeDb.prepare(`
|
|
163
|
+
SELECT DISTINCT symbol_name, file_path
|
|
164
|
+
FROM code_symbols
|
|
165
|
+
WHERE project = ? AND symbol_name IN (${placeholders})
|
|
166
|
+
LIMIT 20
|
|
167
|
+
`).all(project, ...candidates) as { symbol_name: string; file_path: string }[]
|
|
168
|
+
|
|
169
|
+
for (const row of rows) {
|
|
170
|
+
results.push({ symbol: row.symbol_name, filePath: row.file_path })
|
|
171
|
+
}
|
|
172
|
+
} catch (err) {
|
|
173
|
+
this.logger.debug({ err }, 'Symbol matching query failed')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return results
|
|
177
|
+
}
|
|
178
|
+
}
|