claude-brain 0.3.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/README.md +157 -0
- package/VERSION +1 -0
- package/assets/CLAUDE.md +307 -0
- package/bunfig.toml +8 -0
- package/package.json +74 -0
- package/src/automation/auto-context.ts +240 -0
- package/src/automation/decision-detector.ts +452 -0
- package/src/automation/index.ts +11 -0
- package/src/automation/proactive-recall.ts +373 -0
- package/src/automation/project-detector.ts +297 -0
- package/src/cli/auto-setup.ts +74 -0
- package/src/cli/bin.ts +110 -0
- package/src/cli/commands/install-mcp.ts +50 -0
- package/src/cli/commands/serve.ts +129 -0
- package/src/cli/diagnose.ts +4 -0
- package/src/cli/health-check.ts +4 -0
- package/src/cli/migrate-chroma.ts +106 -0
- package/src/cli/setup.ts +4 -0
- package/src/config/defaults.ts +47 -0
- package/src/config/home.ts +55 -0
- package/src/config/index.ts +7 -0
- package/src/config/loader.ts +166 -0
- package/src/config/migration.ts +76 -0
- package/src/config/schema.ts +257 -0
- package/src/config/validator.ts +184 -0
- package/src/config/watcher.ts +86 -0
- package/src/context/assembler.ts +398 -0
- package/src/context/cache-manager.ts +101 -0
- package/src/context/formatter.ts +84 -0
- package/src/context/hierarchy.ts +85 -0
- package/src/context/index.ts +83 -0
- package/src/context/progress-tracker.ts +174 -0
- package/src/context/standards-manager.ts +267 -0
- package/src/context/types.ts +252 -0
- package/src/context/validator.ts +58 -0
- package/src/cross-project/affinity.ts +162 -0
- package/src/cross-project/generalizer.ts +283 -0
- package/src/cross-project/index.ts +13 -0
- package/src/cross-project/transfer.ts +201 -0
- package/src/diagnostics/index.ts +123 -0
- package/src/health/index.ts +229 -0
- package/src/index.ts +7 -0
- package/src/knowledge/entity-extractor.ts +416 -0
- package/src/knowledge/graph/builder.ts +159 -0
- package/src/knowledge/graph/linker.ts +201 -0
- package/src/knowledge/graph/memory-graph.ts +359 -0
- package/src/knowledge/graph/schema.ts +99 -0
- package/src/knowledge/graph/search.ts +168 -0
- package/src/knowledge/relationship-extractor.ts +108 -0
- package/src/memory/chroma/client.ts +169 -0
- package/src/memory/chroma/collection-manager.ts +94 -0
- package/src/memory/chroma/config.ts +46 -0
- package/src/memory/chroma/embeddings.ts +153 -0
- package/src/memory/chroma/index.ts +82 -0
- package/src/memory/chroma/migration.ts +270 -0
- package/src/memory/chroma/schemas.ts +69 -0
- package/src/memory/chroma/search.ts +315 -0
- package/src/memory/chroma/store.ts +694 -0
- package/src/memory/consolidation/archiver.ts +164 -0
- package/src/memory/consolidation/merger.ts +186 -0
- package/src/memory/consolidation/scorer.ts +138 -0
- package/src/memory/context-builder.ts +236 -0
- package/src/memory/database.ts +169 -0
- package/src/memory/embedding-utils.ts +156 -0
- package/src/memory/embeddings.ts +226 -0
- package/src/memory/episodic/detector.ts +108 -0
- package/src/memory/episodic/manager.ts +334 -0
- package/src/memory/episodic/summarizer.ts +179 -0
- package/src/memory/episodic/types.ts +52 -0
- package/src/memory/index.ts +395 -0
- package/src/memory/knowledge-extractor.ts +455 -0
- package/src/memory/learning.ts +378 -0
- package/src/memory/patterns.ts +396 -0
- package/src/memory/schema.ts +56 -0
- package/src/memory/search.ts +309 -0
- package/src/memory/store.ts +344 -0
- package/src/memory/types.ts +121 -0
- package/src/optimization/index.ts +10 -0
- package/src/optimization/precompute.ts +202 -0
- package/src/optimization/semantic-cache.ts +207 -0
- package/src/orchestrator/coordinator.ts +272 -0
- package/src/orchestrator/decision-logger.ts +228 -0
- package/src/orchestrator/event-emitter.ts +198 -0
- package/src/orchestrator/event-queue.ts +184 -0
- package/src/orchestrator/handlers/base-handler.ts +70 -0
- package/src/orchestrator/handlers/context-handler.ts +73 -0
- package/src/orchestrator/handlers/decision-handler.ts +204 -0
- package/src/orchestrator/handlers/index.ts +10 -0
- package/src/orchestrator/handlers/status-handler.ts +131 -0
- package/src/orchestrator/handlers/task-handler.ts +171 -0
- package/src/orchestrator/index.ts +275 -0
- package/src/orchestrator/task-parser.ts +284 -0
- package/src/orchestrator/types.ts +98 -0
- package/src/phase12/index.ts +456 -0
- package/src/prediction/context-anticipator.ts +198 -0
- package/src/prediction/decision-predictor.ts +184 -0
- package/src/prediction/index.ts +13 -0
- package/src/prediction/recommender.ts +268 -0
- package/src/reasoning/chain-retrieval.ts +247 -0
- package/src/reasoning/counterfactual.ts +248 -0
- package/src/reasoning/index.ts +13 -0
- package/src/reasoning/synthesizer.ts +169 -0
- package/src/retrieval/bm25/index.ts +300 -0
- package/src/retrieval/bm25/tokenizer.ts +184 -0
- package/src/retrieval/feedback/adaptive.ts +223 -0
- package/src/retrieval/feedback/index.ts +16 -0
- package/src/retrieval/feedback/metrics.ts +223 -0
- package/src/retrieval/feedback/store.ts +283 -0
- package/src/retrieval/fusion/index.ts +194 -0
- package/src/retrieval/fusion/rrf.ts +163 -0
- package/src/retrieval/index.ts +12 -0
- package/src/retrieval/pipeline.ts +375 -0
- package/src/retrieval/query/expander.ts +198 -0
- package/src/retrieval/query/index.ts +27 -0
- package/src/retrieval/query/intent-classifier.ts +236 -0
- package/src/retrieval/query/temporal-parser.ts +295 -0
- package/src/retrieval/reranker/index.ts +188 -0
- package/src/retrieval/reranker/model.ts +95 -0
- package/src/retrieval/service.ts +125 -0
- package/src/retrieval/types.ts +162 -0
- package/src/scripts/health-check.ts +118 -0
- package/src/scripts/setup.ts +122 -0
- package/src/server/handlers/call-tool.ts +194 -0
- package/src/server/handlers/index.ts +9 -0
- package/src/server/handlers/list-tools.ts +18 -0
- package/src/server/handlers/tools/analyze-decision-evolution.ts +71 -0
- package/src/server/handlers/tools/auto-remember.ts +200 -0
- package/src/server/handlers/tools/create-project.ts +135 -0
- package/src/server/handlers/tools/detect-trends.ts +80 -0
- package/src/server/handlers/tools/find-cross-project-patterns.ts +73 -0
- package/src/server/handlers/tools/get-activity-log.ts +194 -0
- package/src/server/handlers/tools/get-code-standards.ts +124 -0
- package/src/server/handlers/tools/get-corrections.ts +154 -0
- package/src/server/handlers/tools/get-decision-timeline.ts +86 -0
- package/src/server/handlers/tools/get-episode.ts +93 -0
- package/src/server/handlers/tools/get-patterns.ts +158 -0
- package/src/server/handlers/tools/get-phase12-status.ts +63 -0
- package/src/server/handlers/tools/get-project-context.ts +75 -0
- package/src/server/handlers/tools/get-recommendations.ts +65 -0
- package/src/server/handlers/tools/index.ts +33 -0
- package/src/server/handlers/tools/init-project.ts +710 -0
- package/src/server/handlers/tools/list-episodes.ts +80 -0
- package/src/server/handlers/tools/list-projects.ts +125 -0
- package/src/server/handlers/tools/rate-memory.ts +95 -0
- package/src/server/handlers/tools/recall-similar.ts +87 -0
- package/src/server/handlers/tools/recognize-pattern.ts +126 -0
- package/src/server/handlers/tools/record-correction.ts +125 -0
- package/src/server/handlers/tools/remember-decision.ts +153 -0
- package/src/server/handlers/tools/schemas.ts +241 -0
- package/src/server/handlers/tools/search-knowledge-graph.ts +89 -0
- package/src/server/handlers/tools/smart-context.ts +124 -0
- package/src/server/handlers/tools/update-progress.ts +114 -0
- package/src/server/handlers/tools/what-if-analysis.ts +73 -0
- package/src/server/http-api.ts +474 -0
- package/src/server/index.ts +40 -0
- package/src/server/mcp-server.ts +283 -0
- package/src/server/providers/index.ts +7 -0
- package/src/server/providers/prompts.ts +327 -0
- package/src/server/providers/resources.ts +427 -0
- package/src/server/services.ts +388 -0
- package/src/server/types.ts +39 -0
- package/src/server/utils/error-handler.ts +155 -0
- package/src/server/utils/index.ts +13 -0
- package/src/server/utils/memory-indicator.ts +83 -0
- package/src/server/utils/request-context.ts +122 -0
- package/src/server/utils/response-formatter.ts +124 -0
- package/src/server/utils/validators.ts +210 -0
- package/src/setup/index.ts +22 -0
- package/src/setup/wizard.ts +321 -0
- package/src/temporal/evolution.ts +197 -0
- package/src/temporal/index.ts +16 -0
- package/src/temporal/query-processor.ts +190 -0
- package/src/temporal/timeline.ts +259 -0
- package/src/temporal/trends.ts +263 -0
- package/src/tools/index.ts +24 -0
- package/src/tools/registry.ts +106 -0
- package/src/tools/schemas.test.ts +30 -0
- package/src/tools/schemas.ts +907 -0
- package/src/tools/types.ts +412 -0
- package/src/utils/circuit-breaker.ts +130 -0
- package/src/utils/cleanup.ts +34 -0
- package/src/utils/error-handler.ts +132 -0
- package/src/utils/error-messages.ts +60 -0
- package/src/utils/fallback.ts +45 -0
- package/src/utils/index.ts +54 -0
- package/src/utils/logger-utils.ts +80 -0
- package/src/utils/logger.ts +88 -0
- package/src/utils/phase12-helper.ts +56 -0
- package/src/utils/retry.ts +94 -0
- package/src/utils/transaction.ts +63 -0
- package/src/vault/frontmatter.ts +264 -0
- package/src/vault/index.ts +318 -0
- package/src/vault/paths.ts +106 -0
- package/src/vault/query.ts +422 -0
- package/src/vault/reader.ts +264 -0
- package/src/vault/templates.ts +186 -0
- package/src/vault/types.ts +73 -0
- package/src/vault/watcher.ts +277 -0
- package/src/vault/writer.ts +393 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporal Query Processor
|
|
3
|
+
* Parses temporal expressions from natural language using chrono-node
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as chrono from 'chrono-node'
|
|
7
|
+
import type { Logger } from 'pino'
|
|
8
|
+
|
|
9
|
+
export interface TemporalQuery {
|
|
10
|
+
/** Original query text */
|
|
11
|
+
original: string
|
|
12
|
+
/** Cleaned query with temporal expressions removed */
|
|
13
|
+
cleaned: string
|
|
14
|
+
/** Parsed start date (if any) */
|
|
15
|
+
startDate?: string
|
|
16
|
+
/** Parsed end date (if any) */
|
|
17
|
+
endDate?: string
|
|
18
|
+
/** Temporal intent detected */
|
|
19
|
+
intent: 'range' | 'before' | 'after' | 'point' | 'none'
|
|
20
|
+
/** Relative time expressions found */
|
|
21
|
+
expressions: string[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class TemporalQueryProcessor {
|
|
25
|
+
private logger: Logger
|
|
26
|
+
|
|
27
|
+
constructor(logger: Logger) {
|
|
28
|
+
this.logger = logger.child({ component: 'temporal-query-processor' })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse temporal expressions from a natural language query
|
|
33
|
+
*/
|
|
34
|
+
parse(query: string): TemporalQuery {
|
|
35
|
+
const result: TemporalQuery = {
|
|
36
|
+
original: query,
|
|
37
|
+
cleaned: query,
|
|
38
|
+
intent: 'none',
|
|
39
|
+
expressions: []
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
// Parse with chrono-node
|
|
44
|
+
const parsed = chrono.parse(query, new Date(), { forwardDate: false })
|
|
45
|
+
|
|
46
|
+
if (parsed.length === 0) {
|
|
47
|
+
// Try manual temporal patterns
|
|
48
|
+
return this.parseManualPatterns(query, result)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const ref = parsed[0]
|
|
52
|
+
result.expressions.push(ref.text)
|
|
53
|
+
|
|
54
|
+
// Remove temporal expression from query for cleaner semantic search
|
|
55
|
+
result.cleaned = query.replace(ref.text, '').replace(/\s+/g, ' ').trim()
|
|
56
|
+
|
|
57
|
+
if (ref.start && ref.end) {
|
|
58
|
+
// Date range
|
|
59
|
+
result.startDate = ref.start.date().toISOString()
|
|
60
|
+
result.endDate = ref.end.date().toISOString()
|
|
61
|
+
result.intent = 'range'
|
|
62
|
+
} else if (ref.start) {
|
|
63
|
+
const startDate = ref.start.date()
|
|
64
|
+
|
|
65
|
+
// Check context for before/after
|
|
66
|
+
const beforeIndex = query.toLowerCase().indexOf('before')
|
|
67
|
+
const afterIndex = query.toLowerCase().indexOf('after')
|
|
68
|
+
const sinceIndex = query.toLowerCase().indexOf('since')
|
|
69
|
+
const untilIndex = query.toLowerCase().indexOf('until')
|
|
70
|
+
|
|
71
|
+
if (beforeIndex >= 0 && beforeIndex < query.indexOf(ref.text)) {
|
|
72
|
+
result.endDate = startDate.toISOString()
|
|
73
|
+
result.intent = 'before'
|
|
74
|
+
} else if (
|
|
75
|
+
(afterIndex >= 0 && afterIndex < query.indexOf(ref.text)) ||
|
|
76
|
+
(sinceIndex >= 0 && sinceIndex < query.indexOf(ref.text))
|
|
77
|
+
) {
|
|
78
|
+
result.startDate = startDate.toISOString()
|
|
79
|
+
result.intent = 'after'
|
|
80
|
+
} else if (untilIndex >= 0 && untilIndex < query.indexOf(ref.text)) {
|
|
81
|
+
result.endDate = startDate.toISOString()
|
|
82
|
+
result.intent = 'before'
|
|
83
|
+
} else {
|
|
84
|
+
// Default: treat as a point in time, create a window around it
|
|
85
|
+
const dayBefore = new Date(startDate)
|
|
86
|
+
dayBefore.setDate(dayBefore.getDate() - 1)
|
|
87
|
+
const dayAfter = new Date(startDate)
|
|
88
|
+
dayAfter.setDate(dayAfter.getDate() + 1)
|
|
89
|
+
|
|
90
|
+
result.startDate = dayBefore.toISOString()
|
|
91
|
+
result.endDate = dayAfter.toISOString()
|
|
92
|
+
result.intent = 'point'
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Handle multiple temporal expressions
|
|
97
|
+
for (let i = 1; i < parsed.length; i++) {
|
|
98
|
+
result.expressions.push(parsed[i].text)
|
|
99
|
+
result.cleaned = result.cleaned.replace(parsed[i].text, '').replace(/\s+/g, ' ').trim()
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
this.logger.debug({ error, query }, 'Failed to parse temporal expression')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return result
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private parseManualPatterns(query: string, result: TemporalQuery): TemporalQuery {
|
|
109
|
+
const lowerQuery = query.toLowerCase()
|
|
110
|
+
const now = new Date()
|
|
111
|
+
|
|
112
|
+
// "last week", "last month", "last year"
|
|
113
|
+
const lastMatch = lowerQuery.match(/\b(?:in the )?last\s+(week|month|year|quarter|(\d+)\s*(days?|weeks?|months?))\b/)
|
|
114
|
+
if (lastMatch) {
|
|
115
|
+
result.expressions.push(lastMatch[0])
|
|
116
|
+
result.cleaned = query.replace(new RegExp(lastMatch[0], 'i'), '').replace(/\s+/g, ' ').trim()
|
|
117
|
+
result.endDate = now.toISOString()
|
|
118
|
+
|
|
119
|
+
const start = new Date(now)
|
|
120
|
+
if (lastMatch[1] === 'week') {
|
|
121
|
+
start.setDate(start.getDate() - 7)
|
|
122
|
+
} else if (lastMatch[1] === 'month') {
|
|
123
|
+
start.setMonth(start.getMonth() - 1)
|
|
124
|
+
} else if (lastMatch[1] === 'year') {
|
|
125
|
+
start.setFullYear(start.getFullYear() - 1)
|
|
126
|
+
} else if (lastMatch[1] === 'quarter') {
|
|
127
|
+
start.setMonth(start.getMonth() - 3)
|
|
128
|
+
} else if (lastMatch[2] && lastMatch[3]) {
|
|
129
|
+
const n = parseInt(lastMatch[2])
|
|
130
|
+
const unit = lastMatch[3].replace(/s$/, '')
|
|
131
|
+
if (unit === 'day') start.setDate(start.getDate() - n)
|
|
132
|
+
else if (unit === 'week') start.setDate(start.getDate() - n * 7)
|
|
133
|
+
else if (unit === 'month') start.setMonth(start.getMonth() - n)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
result.startDate = start.toISOString()
|
|
137
|
+
result.intent = 'range'
|
|
138
|
+
return result
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// "recently", "lately"
|
|
142
|
+
if (/\b(recently|lately)\b/.test(lowerQuery)) {
|
|
143
|
+
const match = lowerQuery.match(/\b(recently|lately)\b/)!
|
|
144
|
+
result.expressions.push(match[0])
|
|
145
|
+
result.cleaned = query.replace(new RegExp(match[0], 'i'), '').replace(/\s+/g, ' ').trim()
|
|
146
|
+
const start = new Date(now)
|
|
147
|
+
start.setDate(start.getDate() - 14) // 2 weeks for "recently"
|
|
148
|
+
result.startDate = start.toISOString()
|
|
149
|
+
result.endDate = now.toISOString()
|
|
150
|
+
result.intent = 'range'
|
|
151
|
+
return result
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// "this week", "this month", "this year"
|
|
155
|
+
const thisMatch = lowerQuery.match(/\bthis\s+(week|month|year)\b/)
|
|
156
|
+
if (thisMatch) {
|
|
157
|
+
result.expressions.push(thisMatch[0])
|
|
158
|
+
result.cleaned = query.replace(new RegExp(thisMatch[0], 'i'), '').replace(/\s+/g, ' ').trim()
|
|
159
|
+
result.endDate = now.toISOString()
|
|
160
|
+
|
|
161
|
+
const start = new Date(now)
|
|
162
|
+
if (thisMatch[1] === 'week') {
|
|
163
|
+
start.setDate(start.getDate() - start.getDay())
|
|
164
|
+
start.setHours(0, 0, 0, 0)
|
|
165
|
+
} else if (thisMatch[1] === 'month') {
|
|
166
|
+
start.setDate(1)
|
|
167
|
+
start.setHours(0, 0, 0, 0)
|
|
168
|
+
} else if (thisMatch[1] === 'year') {
|
|
169
|
+
start.setMonth(0, 1)
|
|
170
|
+
start.setHours(0, 0, 0, 0)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
result.startDate = start.toISOString()
|
|
174
|
+
result.intent = 'range'
|
|
175
|
+
return result
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// "ever", "all time", "historically"
|
|
179
|
+
if (/\b(ever|all\s+time|historically|over\s+time)\b/.test(lowerQuery)) {
|
|
180
|
+
const match = lowerQuery.match(/\b(ever|all\s+time|historically|over\s+time)\b/)!
|
|
181
|
+
result.expressions.push(match[0])
|
|
182
|
+
result.cleaned = query.replace(new RegExp(match[0], 'i'), '').replace(/\s+/g, ' ').trim()
|
|
183
|
+
result.intent = 'range'
|
|
184
|
+
// No date constraints = all time
|
|
185
|
+
return result
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return result
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline Construction
|
|
3
|
+
* Builds chronological timelines from ChromaDB memories
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
import type { CollectionManager } from '@/memory/chroma/collection-manager'
|
|
8
|
+
import type { EmbeddingProvider } from '@/memory/chroma/embeddings'
|
|
9
|
+
|
|
10
|
+
export interface TimelineEntry {
|
|
11
|
+
id: string
|
|
12
|
+
date: string
|
|
13
|
+
type: 'decision' | 'pattern' | 'correction'
|
|
14
|
+
project: string
|
|
15
|
+
content: string
|
|
16
|
+
metadata: Record<string, any>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Timeline {
|
|
20
|
+
entries: TimelineEntry[]
|
|
21
|
+
project?: string
|
|
22
|
+
startDate?: string
|
|
23
|
+
endDate?: string
|
|
24
|
+
totalCount: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class TimelineBuilder {
|
|
28
|
+
private logger: Logger
|
|
29
|
+
private collections: CollectionManager
|
|
30
|
+
private embeddings?: EmbeddingProvider
|
|
31
|
+
|
|
32
|
+
constructor(logger: Logger, collections: CollectionManager, embeddings?: EmbeddingProvider) {
|
|
33
|
+
this.logger = logger.child({ component: 'timeline-builder' })
|
|
34
|
+
this.collections = collections
|
|
35
|
+
this.embeddings = embeddings
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build a timeline of decisions for a project or topic
|
|
40
|
+
*/
|
|
41
|
+
async buildTimeline(options: {
|
|
42
|
+
project?: string
|
|
43
|
+
topic?: string
|
|
44
|
+
startDate?: string
|
|
45
|
+
endDate?: string
|
|
46
|
+
limit?: number
|
|
47
|
+
types?: Array<'decision' | 'pattern' | 'correction'>
|
|
48
|
+
} = {}): Promise<Timeline> {
|
|
49
|
+
const {
|
|
50
|
+
project,
|
|
51
|
+
topic,
|
|
52
|
+
startDate,
|
|
53
|
+
endDate,
|
|
54
|
+
limit = 50,
|
|
55
|
+
types = ['decision', 'pattern', 'correction']
|
|
56
|
+
} = options
|
|
57
|
+
|
|
58
|
+
const entries: TimelineEntry[] = []
|
|
59
|
+
|
|
60
|
+
// Fetch from each collection type
|
|
61
|
+
if (types.includes('decision')) {
|
|
62
|
+
const decisions = await this.fetchDecisions(project, topic, startDate, endDate, limit)
|
|
63
|
+
entries.push(...decisions)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (types.includes('pattern')) {
|
|
67
|
+
const patterns = await this.fetchPatterns(project, topic, startDate, endDate, limit)
|
|
68
|
+
entries.push(...patterns)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (types.includes('correction')) {
|
|
72
|
+
const corrections = await this.fetchCorrections(project, topic, startDate, endDate, limit)
|
|
73
|
+
entries.push(...corrections)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Sort chronologically
|
|
77
|
+
entries.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
|
|
78
|
+
|
|
79
|
+
// Apply limit after merge
|
|
80
|
+
const limited = entries.slice(0, limit)
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
entries: limited,
|
|
84
|
+
project,
|
|
85
|
+
startDate: limited[0]?.date,
|
|
86
|
+
endDate: limited[limited.length - 1]?.date,
|
|
87
|
+
totalCount: limited.length
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private async fetchDecisions(
|
|
92
|
+
project?: string,
|
|
93
|
+
topic?: string,
|
|
94
|
+
startDate?: string,
|
|
95
|
+
endDate?: string,
|
|
96
|
+
limit: number = 50
|
|
97
|
+
): Promise<TimelineEntry[]> {
|
|
98
|
+
try {
|
|
99
|
+
const collection = await this.collections.getDecisions()
|
|
100
|
+
|
|
101
|
+
if (topic && this.embeddings) {
|
|
102
|
+
// Semantic search by topic
|
|
103
|
+
const embedding = await this.embeddings.generate(topic)
|
|
104
|
+
const where = this.buildWhere(project, startDate, endDate)
|
|
105
|
+
const results = await collection.query({
|
|
106
|
+
queryEmbeddings: [embedding],
|
|
107
|
+
nResults: limit,
|
|
108
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
109
|
+
include: ['documents', 'metadatas', 'distances']
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return this.processQueryResults(results, 'decision')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Direct fetch
|
|
116
|
+
const where = this.buildWhere(project, startDate, endDate)
|
|
117
|
+
const results = await collection.get({
|
|
118
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
119
|
+
include: ['documents', 'metadatas'],
|
|
120
|
+
limit
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
return results.ids.map((id, i) => ({
|
|
124
|
+
id,
|
|
125
|
+
date: (results.metadatas![i] as any)?.created_at || new Date().toISOString(),
|
|
126
|
+
type: 'decision' as const,
|
|
127
|
+
project: (results.metadatas![i] as any)?.project || '',
|
|
128
|
+
content: results.documents![i] as string,
|
|
129
|
+
metadata: results.metadatas![i] as Record<string, any>
|
|
130
|
+
}))
|
|
131
|
+
} catch (error) {
|
|
132
|
+
this.logger.warn({ error }, 'Failed to fetch decisions for timeline')
|
|
133
|
+
return []
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private async fetchPatterns(
|
|
138
|
+
project?: string,
|
|
139
|
+
topic?: string,
|
|
140
|
+
startDate?: string,
|
|
141
|
+
endDate?: string,
|
|
142
|
+
limit: number = 50
|
|
143
|
+
): Promise<TimelineEntry[]> {
|
|
144
|
+
try {
|
|
145
|
+
const collection = await this.collections.getPatterns()
|
|
146
|
+
|
|
147
|
+
if (topic && this.embeddings) {
|
|
148
|
+
const embedding = await this.embeddings.generate(topic)
|
|
149
|
+
const where = this.buildWhere(project, startDate, endDate)
|
|
150
|
+
const results = await collection.query({
|
|
151
|
+
queryEmbeddings: [embedding],
|
|
152
|
+
nResults: limit,
|
|
153
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
154
|
+
include: ['documents', 'metadatas', 'distances']
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
return this.processQueryResults(results, 'pattern')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const where = this.buildWhere(project, startDate, endDate)
|
|
161
|
+
const results = await collection.get({
|
|
162
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
163
|
+
include: ['documents', 'metadatas'],
|
|
164
|
+
limit
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
return results.ids.map((id, i) => ({
|
|
168
|
+
id,
|
|
169
|
+
date: (results.metadatas![i] as any)?.created_at || new Date().toISOString(),
|
|
170
|
+
type: 'pattern' as const,
|
|
171
|
+
project: (results.metadatas![i] as any)?.project || '',
|
|
172
|
+
content: results.documents![i] as string,
|
|
173
|
+
metadata: results.metadatas![i] as Record<string, any>
|
|
174
|
+
}))
|
|
175
|
+
} catch (error) {
|
|
176
|
+
this.logger.warn({ error }, 'Failed to fetch patterns for timeline')
|
|
177
|
+
return []
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private async fetchCorrections(
|
|
182
|
+
project?: string,
|
|
183
|
+
topic?: string,
|
|
184
|
+
startDate?: string,
|
|
185
|
+
endDate?: string,
|
|
186
|
+
limit: number = 50
|
|
187
|
+
): Promise<TimelineEntry[]> {
|
|
188
|
+
try {
|
|
189
|
+
const collection = await this.collections.getCorrections()
|
|
190
|
+
|
|
191
|
+
if (topic && this.embeddings) {
|
|
192
|
+
const embedding = await this.embeddings.generate(topic)
|
|
193
|
+
const where = this.buildWhere(project, startDate, endDate)
|
|
194
|
+
const results = await collection.query({
|
|
195
|
+
queryEmbeddings: [embedding],
|
|
196
|
+
nResults: limit,
|
|
197
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
198
|
+
include: ['documents', 'metadatas', 'distances']
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
return this.processQueryResults(results, 'correction')
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const where = this.buildWhere(project, startDate, endDate)
|
|
205
|
+
const results = await collection.get({
|
|
206
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
207
|
+
include: ['documents', 'metadatas'],
|
|
208
|
+
limit
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
return results.ids.map((id, i) => ({
|
|
212
|
+
id,
|
|
213
|
+
date: (results.metadatas![i] as any)?.created_at || new Date().toISOString(),
|
|
214
|
+
type: 'correction' as const,
|
|
215
|
+
project: (results.metadatas![i] as any)?.project || '',
|
|
216
|
+
content: results.documents![i] as string,
|
|
217
|
+
metadata: results.metadatas![i] as Record<string, any>
|
|
218
|
+
}))
|
|
219
|
+
} catch (error) {
|
|
220
|
+
this.logger.warn({ error }, 'Failed to fetch corrections for timeline')
|
|
221
|
+
return []
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private buildWhere(project?: string, startDate?: string, endDate?: string): Record<string, any> {
|
|
226
|
+
const conditions: any[] = []
|
|
227
|
+
|
|
228
|
+
if (project) {
|
|
229
|
+
conditions.push({ project: { $eq: project } })
|
|
230
|
+
}
|
|
231
|
+
if (startDate) {
|
|
232
|
+
conditions.push({ created_at: { $gte: startDate } })
|
|
233
|
+
}
|
|
234
|
+
if (endDate) {
|
|
235
|
+
conditions.push({ created_at: { $lte: endDate } })
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (conditions.length === 0) return {}
|
|
239
|
+
if (conditions.length === 1) return conditions[0]
|
|
240
|
+
return { $and: conditions }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private processQueryResults(results: any, type: 'decision' | 'pattern' | 'correction'): TimelineEntry[] {
|
|
244
|
+
if (!results.ids || !results.ids[0]) return []
|
|
245
|
+
|
|
246
|
+
const ids = results.ids[0] || []
|
|
247
|
+
const documents = results.documents?.[0] || []
|
|
248
|
+
const metadatas = results.metadatas?.[0] || []
|
|
249
|
+
|
|
250
|
+
return ids.map((id: string, i: number) => ({
|
|
251
|
+
id,
|
|
252
|
+
date: metadatas[i]?.created_at || new Date().toISOString(),
|
|
253
|
+
type,
|
|
254
|
+
project: metadatas[i]?.project || '',
|
|
255
|
+
content: documents[i] || '',
|
|
256
|
+
metadata: metadatas[i] || {}
|
|
257
|
+
}))
|
|
258
|
+
}
|
|
259
|
+
}
|