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,295 +1,295 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Temporal Parser
|
|
3
|
-
* Parses temporal expressions in queries using compromise.js
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { TemporalParse } from '../types'
|
|
7
|
-
|
|
8
|
-
// Dynamic import for compromise to avoid bundling issues
|
|
9
|
-
let nlp:
|
|
10
|
-
|
|
11
|
-
async function loadNlp(): Promise<
|
|
12
|
-
if (!nlp) {
|
|
13
|
-
nlp = (await import('compromise')).default
|
|
14
|
-
}
|
|
15
|
-
return nlp
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Relative time patterns */
|
|
19
|
-
const RELATIVE_PATTERNS: Array<{
|
|
20
|
-
pattern: RegExp
|
|
21
|
-
parse: (match: RegExpMatchArray) => { start?: Date; end?: Date }
|
|
22
|
-
}> = [
|
|
23
|
-
// "last N days/weeks/months/years"
|
|
24
|
-
{
|
|
25
|
-
pattern: /(?:in\s+the\s+)?last\s+(\d+)\s+(day|week|month|year)s?/i,
|
|
26
|
-
parse: (match) => {
|
|
27
|
-
const amount = parseInt(match[1]!, 10)
|
|
28
|
-
const unit = match[2]!.toLowerCase()
|
|
29
|
-
const now = new Date()
|
|
30
|
-
const start = new Date(now)
|
|
31
|
-
|
|
32
|
-
switch (unit) {
|
|
33
|
-
case 'day':
|
|
34
|
-
start.setDate(start.getDate() - amount)
|
|
35
|
-
break
|
|
36
|
-
case 'week':
|
|
37
|
-
start.setDate(start.getDate() - amount * 7)
|
|
38
|
-
break
|
|
39
|
-
case 'month':
|
|
40
|
-
start.setMonth(start.getMonth() - amount)
|
|
41
|
-
break
|
|
42
|
-
case 'year':
|
|
43
|
-
start.setFullYear(start.getFullYear() - amount)
|
|
44
|
-
break
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return { start, end: now }
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
// "N days/weeks/months ago"
|
|
51
|
-
{
|
|
52
|
-
pattern: /(\d+)\s+(day|week|month|year)s?\s+ago/i,
|
|
53
|
-
parse: (match) => {
|
|
54
|
-
const amount = parseInt(match[1]!, 10)
|
|
55
|
-
const unit = match[2]!.toLowerCase()
|
|
56
|
-
const target = new Date()
|
|
57
|
-
|
|
58
|
-
switch (unit) {
|
|
59
|
-
case 'day':
|
|
60
|
-
target.setDate(target.getDate() - amount)
|
|
61
|
-
break
|
|
62
|
-
case 'week':
|
|
63
|
-
target.setDate(target.getDate() - amount * 7)
|
|
64
|
-
break
|
|
65
|
-
case 'month':
|
|
66
|
-
target.setMonth(target.getMonth() - amount)
|
|
67
|
-
break
|
|
68
|
-
case 'year':
|
|
69
|
-
target.setFullYear(target.getFullYear() - amount)
|
|
70
|
-
break
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// For "X ago", use a range around that date (±1 day)
|
|
74
|
-
const start = new Date(target)
|
|
75
|
-
start.setDate(start.getDate() - 1)
|
|
76
|
-
const end = new Date(target)
|
|
77
|
-
end.setDate(end.getDate() + 1)
|
|
78
|
-
|
|
79
|
-
return { start, end }
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
// "this week/month/year"
|
|
83
|
-
{
|
|
84
|
-
pattern: /this\s+(week|month|year)/i,
|
|
85
|
-
parse: (match) => {
|
|
86
|
-
const unit = match[1]!.toLowerCase()
|
|
87
|
-
const now = new Date()
|
|
88
|
-
const start = new Date(now)
|
|
89
|
-
|
|
90
|
-
switch (unit) {
|
|
91
|
-
case 'week':
|
|
92
|
-
const day = start.getDay()
|
|
93
|
-
start.setDate(start.getDate() - day) // Start of week (Sunday)
|
|
94
|
-
break
|
|
95
|
-
case 'month':
|
|
96
|
-
start.setDate(1) // Start of month
|
|
97
|
-
break
|
|
98
|
-
case 'year':
|
|
99
|
-
start.setMonth(0, 1) // Start of year
|
|
100
|
-
break
|
|
101
|
-
}
|
|
102
|
-
start.setHours(0, 0, 0, 0)
|
|
103
|
-
|
|
104
|
-
return { start, end: now }
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
// "yesterday"
|
|
108
|
-
{
|
|
109
|
-
pattern: /yesterday/i,
|
|
110
|
-
parse: () => {
|
|
111
|
-
const yesterday = new Date()
|
|
112
|
-
yesterday.setDate(yesterday.getDate() - 1)
|
|
113
|
-
yesterday.setHours(0, 0, 0, 0)
|
|
114
|
-
|
|
115
|
-
const endOfYesterday = new Date(yesterday)
|
|
116
|
-
endOfYesterday.setHours(23, 59, 59, 999)
|
|
117
|
-
|
|
118
|
-
return { start: yesterday, end: endOfYesterday }
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
// "today"
|
|
122
|
-
{
|
|
123
|
-
pattern: /today/i,
|
|
124
|
-
parse: () => {
|
|
125
|
-
const today = new Date()
|
|
126
|
-
today.setHours(0, 0, 0, 0)
|
|
127
|
-
|
|
128
|
-
return { start: today, end: new Date() }
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
// "recent" / "recently" / "lately"
|
|
132
|
-
{
|
|
133
|
-
pattern: /recent(?:ly)?|lately/i,
|
|
134
|
-
parse: () => {
|
|
135
|
-
const now = new Date()
|
|
136
|
-
const start = new Date(now)
|
|
137
|
-
start.setDate(start.getDate() - 7) // Default "recent" to 7 days
|
|
138
|
-
|
|
139
|
-
return { start, end: now }
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
// "past week/month"
|
|
143
|
-
{
|
|
144
|
-
pattern: /past\s+(week|month|year)/i,
|
|
145
|
-
parse: (match) => {
|
|
146
|
-
const unit = match[1]!.toLowerCase()
|
|
147
|
-
const now = new Date()
|
|
148
|
-
const start = new Date(now)
|
|
149
|
-
|
|
150
|
-
switch (unit) {
|
|
151
|
-
case 'week':
|
|
152
|
-
start.setDate(start.getDate() - 7)
|
|
153
|
-
break
|
|
154
|
-
case 'month':
|
|
155
|
-
start.setMonth(start.getMonth() - 1)
|
|
156
|
-
break
|
|
157
|
-
case 'year':
|
|
158
|
-
start.setFullYear(start.getFullYear() - 1)
|
|
159
|
-
break
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return { start, end: now }
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
// "since January" / "since 2024"
|
|
166
|
-
{
|
|
167
|
-
pattern: /since\s+(january|february|march|april|may|june|july|august|september|october|november|december|\d{4})/i,
|
|
168
|
-
parse: (match) => {
|
|
169
|
-
const value = match[1]!.toLowerCase()
|
|
170
|
-
const start = new Date()
|
|
171
|
-
|
|
172
|
-
// Check if it's a year
|
|
173
|
-
if (/^\d{4}$/.test(value)) {
|
|
174
|
-
start.setFullYear(parseInt(value, 10), 0, 1)
|
|
175
|
-
} else {
|
|
176
|
-
// It's a month
|
|
177
|
-
const months: Record<string, number> = {
|
|
178
|
-
january: 0, february: 1, march: 2, april: 3,
|
|
179
|
-
may: 4, june: 5, july: 6, august: 7,
|
|
180
|
-
september: 8, october: 9, november: 10, december: 11
|
|
181
|
-
}
|
|
182
|
-
const monthIndex = months[value]
|
|
183
|
-
if (monthIndex !== undefined) {
|
|
184
|
-
start.setMonth(monthIndex, 1)
|
|
185
|
-
// If the month is in the future, assume last year
|
|
186
|
-
if (start > new Date()) {
|
|
187
|
-
start.setFullYear(start.getFullYear() - 1)
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
start.setHours(0, 0, 0, 0)
|
|
192
|
-
|
|
193
|
-
return { start, end: new Date() }
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
]
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Parse temporal expressions from a query
|
|
200
|
-
*/
|
|
201
|
-
export function parseTemporal(query: string): TemporalParse {
|
|
202
|
-
// Check each pattern
|
|
203
|
-
for (const { pattern, parse } of RELATIVE_PATTERNS) {
|
|
204
|
-
const match = query.match(pattern)
|
|
205
|
-
if (match) {
|
|
206
|
-
const { start, end } = parse(match)
|
|
207
|
-
|
|
208
|
-
// Remove temporal expression from query
|
|
209
|
-
const cleanedQuery = query.replace(pattern, '').replace(/\s+/g, ' ').trim()
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
hasTemporal: true,
|
|
213
|
-
start,
|
|
214
|
-
end,
|
|
215
|
-
expression: match[0],
|
|
216
|
-
cleanedQuery
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return {
|
|
222
|
-
hasTemporal: false,
|
|
223
|
-
cleanedQuery: query
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Parse temporal using compromise.js for more complex expressions
|
|
229
|
-
* Falls back to regex patterns if compromise fails
|
|
230
|
-
*/
|
|
231
|
-
export async function parseTemporalAdvanced(query: string): Promise<TemporalParse> {
|
|
232
|
-
// First try regex patterns
|
|
233
|
-
const regexResult = parseTemporal(query)
|
|
234
|
-
if (regexResult.hasTemporal) {
|
|
235
|
-
return regexResult
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Try compromise.js for more complex parsing
|
|
239
|
-
try {
|
|
240
|
-
const nlpInstance = await loadNlp()
|
|
241
|
-
const doc = nlpInstance(query)
|
|
242
|
-
const dates = doc.dates()
|
|
243
|
-
|
|
244
|
-
if (dates.length > 0) {
|
|
245
|
-
const dateText = dates.text()
|
|
246
|
-
const parsed = dates.get()[0]
|
|
247
|
-
|
|
248
|
-
if (parsed) {
|
|
249
|
-
const cleanedQuery = query.replace(dateText, '').replace(/\s+/g, ' ').trim()
|
|
250
|
-
|
|
251
|
-
return {
|
|
252
|
-
hasTemporal: true,
|
|
253
|
-
start: parsed.start ? new Date(parsed.start) : undefined,
|
|
254
|
-
end: parsed.end ? new Date(parsed.end) : new Date(),
|
|
255
|
-
expression: dateText,
|
|
256
|
-
cleanedQuery
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
} catch (error) {
|
|
261
|
-
// Compromise not available or parsing failed, use regex result
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return regexResult
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Format a date range for ChromaDB where clause
|
|
269
|
-
*/
|
|
270
|
-
export function formatDateRangeForQuery(
|
|
271
|
-
temporal: TemporalParse
|
|
272
|
-
): { start?: string; end?: string } | undefined {
|
|
273
|
-
if (!temporal.hasTemporal) {
|
|
274
|
-
return undefined
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return {
|
|
278
|
-
start: temporal.start?.toISOString(),
|
|
279
|
-
end: temporal.end?.toISOString()
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
export class TemporalParser {
|
|
284
|
-
parse(query: string): TemporalParse {
|
|
285
|
-
return parseTemporal(query)
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
async parseAdvanced(query: string): Promise<TemporalParse> {
|
|
289
|
-
return parseTemporalAdvanced(query)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
formatForQuery(temporal: TemporalParse) {
|
|
293
|
-
return formatDateRangeForQuery(temporal)
|
|
294
|
-
}
|
|
295
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Temporal Parser
|
|
3
|
+
* Parses temporal expressions in queries using compromise.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TemporalParse } from '../types'
|
|
7
|
+
|
|
8
|
+
// Dynamic import for compromise to avoid bundling issues
|
|
9
|
+
let nlp: unknown
|
|
10
|
+
|
|
11
|
+
async function loadNlp(): Promise<unknown> {
|
|
12
|
+
if (!nlp) {
|
|
13
|
+
nlp = (await import('compromise')).default
|
|
14
|
+
}
|
|
15
|
+
return nlp
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Relative time patterns */
|
|
19
|
+
const RELATIVE_PATTERNS: Array<{
|
|
20
|
+
pattern: RegExp
|
|
21
|
+
parse: (match: RegExpMatchArray) => { start?: Date; end?: Date }
|
|
22
|
+
}> = [
|
|
23
|
+
// "last N days/weeks/months/years"
|
|
24
|
+
{
|
|
25
|
+
pattern: /(?:in\s+the\s+)?last\s+(\d+)\s+(day|week|month|year)s?/i,
|
|
26
|
+
parse: (match) => {
|
|
27
|
+
const amount = parseInt(match[1]!, 10)
|
|
28
|
+
const unit = match[2]!.toLowerCase()
|
|
29
|
+
const now = new Date()
|
|
30
|
+
const start = new Date(now)
|
|
31
|
+
|
|
32
|
+
switch (unit) {
|
|
33
|
+
case 'day':
|
|
34
|
+
start.setDate(start.getDate() - amount)
|
|
35
|
+
break
|
|
36
|
+
case 'week':
|
|
37
|
+
start.setDate(start.getDate() - amount * 7)
|
|
38
|
+
break
|
|
39
|
+
case 'month':
|
|
40
|
+
start.setMonth(start.getMonth() - amount)
|
|
41
|
+
break
|
|
42
|
+
case 'year':
|
|
43
|
+
start.setFullYear(start.getFullYear() - amount)
|
|
44
|
+
break
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { start, end: now }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
// "N days/weeks/months ago"
|
|
51
|
+
{
|
|
52
|
+
pattern: /(\d+)\s+(day|week|month|year)s?\s+ago/i,
|
|
53
|
+
parse: (match) => {
|
|
54
|
+
const amount = parseInt(match[1]!, 10)
|
|
55
|
+
const unit = match[2]!.toLowerCase()
|
|
56
|
+
const target = new Date()
|
|
57
|
+
|
|
58
|
+
switch (unit) {
|
|
59
|
+
case 'day':
|
|
60
|
+
target.setDate(target.getDate() - amount)
|
|
61
|
+
break
|
|
62
|
+
case 'week':
|
|
63
|
+
target.setDate(target.getDate() - amount * 7)
|
|
64
|
+
break
|
|
65
|
+
case 'month':
|
|
66
|
+
target.setMonth(target.getMonth() - amount)
|
|
67
|
+
break
|
|
68
|
+
case 'year':
|
|
69
|
+
target.setFullYear(target.getFullYear() - amount)
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// For "X ago", use a range around that date (±1 day)
|
|
74
|
+
const start = new Date(target)
|
|
75
|
+
start.setDate(start.getDate() - 1)
|
|
76
|
+
const end = new Date(target)
|
|
77
|
+
end.setDate(end.getDate() + 1)
|
|
78
|
+
|
|
79
|
+
return { start, end }
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
// "this week/month/year"
|
|
83
|
+
{
|
|
84
|
+
pattern: /this\s+(week|month|year)/i,
|
|
85
|
+
parse: (match) => {
|
|
86
|
+
const unit = match[1]!.toLowerCase()
|
|
87
|
+
const now = new Date()
|
|
88
|
+
const start = new Date(now)
|
|
89
|
+
|
|
90
|
+
switch (unit) {
|
|
91
|
+
case 'week':
|
|
92
|
+
const day = start.getDay()
|
|
93
|
+
start.setDate(start.getDate() - day) // Start of week (Sunday)
|
|
94
|
+
break
|
|
95
|
+
case 'month':
|
|
96
|
+
start.setDate(1) // Start of month
|
|
97
|
+
break
|
|
98
|
+
case 'year':
|
|
99
|
+
start.setMonth(0, 1) // Start of year
|
|
100
|
+
break
|
|
101
|
+
}
|
|
102
|
+
start.setHours(0, 0, 0, 0)
|
|
103
|
+
|
|
104
|
+
return { start, end: now }
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
// "yesterday"
|
|
108
|
+
{
|
|
109
|
+
pattern: /yesterday/i,
|
|
110
|
+
parse: () => {
|
|
111
|
+
const yesterday = new Date()
|
|
112
|
+
yesterday.setDate(yesterday.getDate() - 1)
|
|
113
|
+
yesterday.setHours(0, 0, 0, 0)
|
|
114
|
+
|
|
115
|
+
const endOfYesterday = new Date(yesterday)
|
|
116
|
+
endOfYesterday.setHours(23, 59, 59, 999)
|
|
117
|
+
|
|
118
|
+
return { start: yesterday, end: endOfYesterday }
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
// "today"
|
|
122
|
+
{
|
|
123
|
+
pattern: /today/i,
|
|
124
|
+
parse: () => {
|
|
125
|
+
const today = new Date()
|
|
126
|
+
today.setHours(0, 0, 0, 0)
|
|
127
|
+
|
|
128
|
+
return { start: today, end: new Date() }
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
// "recent" / "recently" / "lately"
|
|
132
|
+
{
|
|
133
|
+
pattern: /recent(?:ly)?|lately/i,
|
|
134
|
+
parse: () => {
|
|
135
|
+
const now = new Date()
|
|
136
|
+
const start = new Date(now)
|
|
137
|
+
start.setDate(start.getDate() - 7) // Default "recent" to 7 days
|
|
138
|
+
|
|
139
|
+
return { start, end: now }
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
// "past week/month"
|
|
143
|
+
{
|
|
144
|
+
pattern: /past\s+(week|month|year)/i,
|
|
145
|
+
parse: (match) => {
|
|
146
|
+
const unit = match[1]!.toLowerCase()
|
|
147
|
+
const now = new Date()
|
|
148
|
+
const start = new Date(now)
|
|
149
|
+
|
|
150
|
+
switch (unit) {
|
|
151
|
+
case 'week':
|
|
152
|
+
start.setDate(start.getDate() - 7)
|
|
153
|
+
break
|
|
154
|
+
case 'month':
|
|
155
|
+
start.setMonth(start.getMonth() - 1)
|
|
156
|
+
break
|
|
157
|
+
case 'year':
|
|
158
|
+
start.setFullYear(start.getFullYear() - 1)
|
|
159
|
+
break
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return { start, end: now }
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
// "since January" / "since 2024"
|
|
166
|
+
{
|
|
167
|
+
pattern: /since\s+(january|february|march|april|may|june|july|august|september|october|november|december|\d{4})/i,
|
|
168
|
+
parse: (match) => {
|
|
169
|
+
const value = match[1]!.toLowerCase()
|
|
170
|
+
const start = new Date()
|
|
171
|
+
|
|
172
|
+
// Check if it's a year
|
|
173
|
+
if (/^\d{4}$/.test(value)) {
|
|
174
|
+
start.setFullYear(parseInt(value, 10), 0, 1)
|
|
175
|
+
} else {
|
|
176
|
+
// It's a month
|
|
177
|
+
const months: Record<string, number> = {
|
|
178
|
+
january: 0, february: 1, march: 2, april: 3,
|
|
179
|
+
may: 4, june: 5, july: 6, august: 7,
|
|
180
|
+
september: 8, october: 9, november: 10, december: 11
|
|
181
|
+
}
|
|
182
|
+
const monthIndex = months[value]
|
|
183
|
+
if (monthIndex !== undefined) {
|
|
184
|
+
start.setMonth(monthIndex, 1)
|
|
185
|
+
// If the month is in the future, assume last year
|
|
186
|
+
if (start > new Date()) {
|
|
187
|
+
start.setFullYear(start.getFullYear() - 1)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
start.setHours(0, 0, 0, 0)
|
|
192
|
+
|
|
193
|
+
return { start, end: new Date() }
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Parse temporal expressions from a query
|
|
200
|
+
*/
|
|
201
|
+
export function parseTemporal(query: string): TemporalParse {
|
|
202
|
+
// Check each pattern
|
|
203
|
+
for (const { pattern, parse } of RELATIVE_PATTERNS) {
|
|
204
|
+
const match = query.match(pattern)
|
|
205
|
+
if (match) {
|
|
206
|
+
const { start, end } = parse(match)
|
|
207
|
+
|
|
208
|
+
// Remove temporal expression from query
|
|
209
|
+
const cleanedQuery = query.replace(pattern, '').replace(/\s+/g, ' ').trim()
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
hasTemporal: true,
|
|
213
|
+
start,
|
|
214
|
+
end,
|
|
215
|
+
expression: match[0],
|
|
216
|
+
cleanedQuery
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
hasTemporal: false,
|
|
223
|
+
cleanedQuery: query
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Parse temporal using compromise.js for more complex expressions
|
|
229
|
+
* Falls back to regex patterns if compromise fails
|
|
230
|
+
*/
|
|
231
|
+
export async function parseTemporalAdvanced(query: string): Promise<TemporalParse> {
|
|
232
|
+
// First try regex patterns
|
|
233
|
+
const regexResult = parseTemporal(query)
|
|
234
|
+
if (regexResult.hasTemporal) {
|
|
235
|
+
return regexResult
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Try compromise.js for more complex parsing
|
|
239
|
+
try {
|
|
240
|
+
const nlpInstance = await loadNlp() as (text: string) => { dates(): { length: number; text(): string; get(): Array<{ start?: string; end?: string }> } }
|
|
241
|
+
const doc = nlpInstance(query)
|
|
242
|
+
const dates = doc.dates()
|
|
243
|
+
|
|
244
|
+
if (dates.length > 0) {
|
|
245
|
+
const dateText = dates.text()
|
|
246
|
+
const parsed = dates.get()[0]
|
|
247
|
+
|
|
248
|
+
if (parsed) {
|
|
249
|
+
const cleanedQuery = query.replace(dateText, '').replace(/\s+/g, ' ').trim()
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
hasTemporal: true,
|
|
253
|
+
start: parsed.start ? new Date(parsed.start) : undefined,
|
|
254
|
+
end: parsed.end ? new Date(parsed.end) : new Date(),
|
|
255
|
+
expression: dateText,
|
|
256
|
+
cleanedQuery
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} catch (error) {
|
|
261
|
+
// Compromise not available or parsing failed, use regex result
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return regexResult
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Format a date range for ChromaDB where clause
|
|
269
|
+
*/
|
|
270
|
+
export function formatDateRangeForQuery(
|
|
271
|
+
temporal: TemporalParse
|
|
272
|
+
): { start?: string; end?: string } | undefined {
|
|
273
|
+
if (!temporal.hasTemporal) {
|
|
274
|
+
return undefined
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
start: temporal.start?.toISOString(),
|
|
279
|
+
end: temporal.end?.toISOString()
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export class TemporalParser {
|
|
284
|
+
parse(query: string): TemporalParse {
|
|
285
|
+
return parseTemporal(query)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async parseAdvanced(query: string): Promise<TemporalParse> {
|
|
289
|
+
return parseTemporalAdvanced(query)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
formatForQuery(temporal: TemporalParse) {
|
|
293
|
+
return formatDateRangeForQuery(temporal)
|
|
294
|
+
}
|
|
295
|
+
}
|