@stackmemoryai/stackmemory 0.3.17 → 0.3.18
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/dist/cli/commands/skills.js +15 -2
- package/dist/cli/commands/skills.js.map +2 -2
- package/dist/cli/index.js +113 -834
- package/dist/cli/index.js.map +3 -3
- package/dist/core/context/dual-stack-manager.js +1 -1
- package/dist/core/context/dual-stack-manager.js.map +1 -1
- package/dist/core/context/frame-manager.js +3 -0
- package/dist/core/context/frame-manager.js.map +2 -2
- package/dist/integrations/claude-code/subagent-client.js +106 -3
- package/dist/integrations/claude-code/subagent-client.js.map +2 -2
- package/dist/servers/railway/config.js +51 -0
- package/dist/servers/railway/config.js.map +7 -0
- package/dist/servers/railway/index-enhanced.js +156 -0
- package/dist/servers/railway/index-enhanced.js.map +7 -0
- package/dist/servers/railway/minimal.js +48 -3
- package/dist/servers/railway/minimal.js.map +2 -2
- package/dist/servers/railway/storage-test.js +455 -0
- package/dist/servers/railway/storage-test.js.map +7 -0
- package/dist/skills/claude-skills.js +13 -12
- package/dist/skills/claude-skills.js.map +2 -2
- package/dist/skills/recursive-agent-orchestrator.js +27 -18
- package/dist/skills/recursive-agent-orchestrator.js.map +2 -2
- package/dist/skills/unified-rlm-orchestrator.js.map +2 -2
- package/package.json +6 -18
- package/scripts/README-TESTING.md +186 -0
- package/scripts/analyze-cli-security.js +288 -0
- package/scripts/archive/add-phase-tasks-to-linear.js +163 -0
- package/scripts/archive/analyze-linear-duplicates.js +214 -0
- package/scripts/archive/analyze-remaining-duplicates.js +230 -0
- package/scripts/archive/analyze-sta-duplicates.js +292 -0
- package/scripts/archive/analyze-sta-graphql.js +399 -0
- package/scripts/archive/cancel-duplicate-tasks.ts +246 -0
- package/scripts/archive/check-all-duplicates.ts +419 -0
- package/scripts/archive/clean-duplicate-tasks.js +114 -0
- package/scripts/archive/cleanup-duplicate-tasks.ts +286 -0
- package/scripts/archive/create-phase-tasks.js +387 -0
- package/scripts/archive/delete-linear-duplicates.js +182 -0
- package/scripts/archive/delete-remaining-duplicates.js +158 -0
- package/scripts/archive/delete-sta-duplicates.js +201 -0
- package/scripts/archive/delete-sta-oauth.js +201 -0
- package/scripts/archive/export-sta-tasks.js +62 -0
- package/scripts/archive/install-auto-sync.js +266 -0
- package/scripts/archive/install-chromadb-hooks.sh +133 -0
- package/scripts/archive/install-enhanced-clear-hooks.sh +431 -0
- package/scripts/archive/install-post-task-hooks.sh +289 -0
- package/scripts/archive/install-stackmemory-hooks.sh +420 -0
- package/scripts/archive/merge-linear-duplicates-safe.ts +362 -0
- package/scripts/archive/merge-linear-duplicates.ts +180 -0
- package/scripts/archive/remove-sta-tasks.js +70 -0
- package/scripts/archive/setup-background-sync.sh +168 -0
- package/scripts/archive/setup-claude-auto-triggers.sh +181 -0
- package/scripts/archive/setup-claude-autostart.sh +305 -0
- package/scripts/archive/setup-git-hooks.sh +25 -0
- package/scripts/archive/setup-linear-oauth.sh +46 -0
- package/scripts/archive/setup-mcp.sh +113 -0
- package/scripts/archive/setup-railway-deployment.sh +81 -0
- package/scripts/auto-handoff.sh +262 -0
- package/scripts/background-sync-manager.js +416 -0
- package/scripts/benchmark-performance.ts +57 -0
- package/scripts/check-redis.ts +48 -0
- package/scripts/chromadb-auto-loader.sh +128 -0
- package/scripts/chromadb-context-loader.js +479 -0
- package/scripts/claude-chromadb-hook.js +460 -0
- package/scripts/claude-code-wrapper.sh +66 -0
- package/scripts/claude-linear-skill.js +455 -0
- package/scripts/claude-pre-commit.sh +302 -0
- package/scripts/claude-sm-autostart.js +532 -0
- package/scripts/claude-sm-setup.sh +367 -0
- package/scripts/claude-with-chromadb.sh +69 -0
- package/scripts/claude-worktree-manager.sh +323 -0
- package/scripts/claude-worktree-monitor.sh +371 -0
- package/scripts/claude-worktree-setup.sh +327 -0
- package/scripts/clean-linear-backlog.js +273 -0
- package/scripts/cleanup-old-sessions.sh +57 -0
- package/scripts/codex-wrapper.sh +88 -0
- package/scripts/create-sandbox.sh +269 -0
- package/scripts/debug-linear-update.js +174 -0
- package/scripts/delete-linear-tasks.js +167 -0
- package/scripts/deploy.sh +89 -0
- package/scripts/deployment/railway.sh +352 -0
- package/scripts/deployment/test-deployment.js +194 -0
- package/scripts/detect-and-rehydrate.js +162 -0
- package/scripts/detect-and-rehydrate.mjs +165 -0
- package/scripts/development/create-demo-tasks.js +143 -0
- package/scripts/development/debug-frame-test.js +16 -0
- package/scripts/development/demo-auto-sync.js +128 -0
- package/scripts/development/fix-all-imports.js +213 -0
- package/scripts/development/fix-imports.js +229 -0
- package/scripts/development/fix-lint-loop.cjs +103 -0
- package/scripts/development/fix-project-id.ts +161 -0
- package/scripts/development/fix-strict-mode-issues.ts +291 -0
- package/scripts/development/reorganize-structure.sh +228 -0
- package/scripts/development/test-persistence-direct.js +148 -0
- package/scripts/development/test-persistence.js +114 -0
- package/scripts/development/test-tasks.js +93 -0
- package/scripts/development/update-imports.js +212 -0
- package/scripts/fetch-linear-status.js +125 -0
- package/scripts/git-hooks/README.md +310 -0
- package/scripts/git-hooks/branch-context-manager.sh +342 -0
- package/scripts/git-hooks/post-checkout-stackmemory.sh +63 -0
- package/scripts/git-hooks/post-commit-stackmemory.sh +305 -0
- package/scripts/git-hooks/pre-commit-stackmemory.sh +275 -0
- package/scripts/hooks/cleanup-shell.sh +130 -0
- package/scripts/hooks/task-complete.sh +114 -0
- package/scripts/initialize.ts +129 -0
- package/scripts/install-claude-hooks-auto.js +104 -0
- package/scripts/install-claude-hooks.sh +133 -0
- package/scripts/install-global.sh +296 -0
- package/scripts/install.sh +235 -0
- package/scripts/linear-auto-sync.js +262 -0
- package/scripts/linear-auto-sync.sh +161 -0
- package/scripts/linear-sync-daemon.js +150 -0
- package/scripts/linear-task-review.js +237 -0
- package/scripts/list-linear-tasks.ts +178 -0
- package/scripts/mcp-proxy.js +66 -0
- package/scripts/opencode-wrapper.sh +85 -0
- package/scripts/publish-local.js +74 -0
- package/scripts/query-chromadb.ts +201 -0
- package/scripts/railway-env-setup.sh +39 -0
- package/scripts/reconcile-local-tasks.js +170 -0
- package/scripts/recreate-frames-db.js +89 -0
- package/scripts/setup/claude-integration.js +138 -0
- package/scripts/setup/configure-alias.js +125 -0
- package/scripts/setup/configure-codex-alias.js +161 -0
- package/scripts/setup/configure-opencode-alias.js +175 -0
- package/scripts/setup-claude-integration.js +204 -0
- package/scripts/setup-claude-integration.sh +183 -0
- package/scripts/setup.sh +31 -0
- package/scripts/show-linear-summary.ts +172 -0
- package/scripts/stackmemory-auto-handoff.sh +231 -0
- package/scripts/stackmemory-daemon.sh +40 -0
- package/scripts/start-linear-sync-daemon.sh +141 -0
- package/scripts/start-temporal-paradox.sh +214 -0
- package/scripts/status.ts +159 -0
- package/scripts/sync-and-clean-tasks.js +258 -0
- package/scripts/sync-frames-from-railway.js +228 -0
- package/scripts/sync-linear-graphql.js +303 -0
- package/scripts/sync-linear-tasks.js +186 -0
- package/scripts/test-auto-triggers.sh +57 -0
- package/scripts/test-browser-mcp.js +74 -0
- package/scripts/test-chromadb-full.js +115 -0
- package/scripts/test-chromadb-hooks.sh +28 -0
- package/scripts/test-chromadb-sync.ts +245 -0
- package/scripts/test-cli-security.js +293 -0
- package/scripts/test-hooks-persistence.sh +220 -0
- package/scripts/test-installation-scenarios.sh +359 -0
- package/scripts/test-installation.sh +224 -0
- package/scripts/test-mcp.js +163 -0
- package/scripts/test-pre-publish-quick.sh +75 -0
- package/scripts/test-quality-gates.sh +263 -0
- package/scripts/test-railway-db.js +222 -0
- package/scripts/test-redis-storage.ts +490 -0
- package/scripts/test-rlm-basic.sh +122 -0
- package/scripts/test-rlm-comprehensive.sh +260 -0
- package/scripts/test-rlm-e2e.sh +268 -0
- package/scripts/test-rlm-simple.js +90 -0
- package/scripts/test-rlm.js +110 -0
- package/scripts/test-session-handoff.sh +165 -0
- package/scripts/test-shell-integration.sh +275 -0
- package/scripts/testing/ab-test-runner.ts +508 -0
- package/scripts/testing/collect-metrics.ts +457 -0
- package/scripts/testing/quick-effectiveness-demo.js +187 -0
- package/scripts/testing/real-performance-test.js +422 -0
- package/scripts/testing/run-effectiveness-tests.sh +176 -0
- package/scripts/testing/scripts/testing/ab-test-runner.js +363 -0
- package/scripts/testing/scripts/testing/collect-metrics.js +292 -0
- package/scripts/testing/simple-effectiveness-test.js +310 -0
- package/scripts/testing/src/core/context/context-bridge.js +253 -0
- package/scripts/testing/src/core/context/frame-manager.js +746 -0
- package/scripts/testing/src/core/context/shared-context-layer.js +437 -0
- package/scripts/testing/src/core/database/database-adapter.js +54 -0
- package/scripts/testing/src/core/errors/index.js +291 -0
- package/scripts/testing/src/core/errors/recovery.js +268 -0
- package/scripts/testing/src/core/monitoring/logger.js +145 -0
- package/scripts/testing/src/core/retrieval/context-retriever.js +516 -0
- package/scripts/testing/src/core/session/index.js +1 -0
- package/scripts/testing/src/core/session/session-manager.js +323 -0
- package/scripts/testing/src/core/trace/cli-trace-wrapper.js +140 -0
- package/scripts/testing/src/core/trace/db-trace-wrapper.js +251 -0
- package/scripts/testing/src/core/trace/debug-trace.js +398 -0
- package/scripts/testing/src/core/trace/index.js +120 -0
- package/scripts/testing/src/core/trace/linear-api-wrapper.js +204 -0
- package/scripts/update-linear-status.js +268 -0
- package/scripts/update-linear-tasks-fixed.js +284 -0
- package/templates/claude-hooks/hooks.json +5 -0
- package/templates/claude-hooks/on-clear.js +56 -0
- package/templates/claude-hooks/on-startup.js +56 -0
- package/templates/claude-hooks/tool-use-trace.js +67 -0
- package/dist/features/tui/components/analytics-panel.js +0 -157
- package/dist/features/tui/components/analytics-panel.js.map +0 -7
- package/dist/features/tui/components/frame-visualizer.js +0 -377
- package/dist/features/tui/components/frame-visualizer.js.map +0 -7
- package/dist/features/tui/components/pr-tracker.js +0 -135
- package/dist/features/tui/components/pr-tracker.js.map +0 -7
- package/dist/features/tui/components/session-monitor.js +0 -299
- package/dist/features/tui/components/session-monitor.js.map +0 -7
- package/dist/features/tui/components/subagent-fleet.js +0 -395
- package/dist/features/tui/components/subagent-fleet.js.map +0 -7
- package/dist/features/tui/components/task-board.js +0 -1139
- package/dist/features/tui/components/task-board.js.map +0 -7
- package/dist/features/tui/index.js +0 -408
- package/dist/features/tui/index.js.map +0 -7
- package/dist/features/tui/services/data-service.js +0 -641
- package/dist/features/tui/services/data-service.js.map +0 -7
- package/dist/features/tui/services/linear-task-reader.js +0 -102
- package/dist/features/tui/services/linear-task-reader.js.map +0 -7
- package/dist/features/tui/services/websocket-client.js +0 -162
- package/dist/features/tui/services/websocket-client.js.map +0 -7
- package/dist/features/tui/terminal-compat.js +0 -220
- package/dist/features/tui/terminal-compat.js.map +0 -7
- package/dist/features/tui/types.js +0 -1
- package/dist/features/tui/types.js.map +0 -7
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-driven Context Retrieval System
|
|
3
|
+
* Intelligently retrieves relevant context using ParadeDB search capabilities
|
|
4
|
+
*/
|
|
5
|
+
import { logger } from '../monitoring/logger.js';
|
|
6
|
+
export class ContextRetriever {
|
|
7
|
+
constructor(adapter) {
|
|
8
|
+
this.strategies = new Map();
|
|
9
|
+
this.queryCache = new Map();
|
|
10
|
+
this.cacheMaxSize = 100;
|
|
11
|
+
this.cacheExpiryMs = 300000; // 5 minutes
|
|
12
|
+
this.adapter = adapter;
|
|
13
|
+
this.initializeStrategies();
|
|
14
|
+
}
|
|
15
|
+
initializeStrategies() {
|
|
16
|
+
// Keyword-based search for specific terms
|
|
17
|
+
this.strategies.set('keyword', {
|
|
18
|
+
name: 'Keyword Search',
|
|
19
|
+
searchType: 'text',
|
|
20
|
+
boost: {
|
|
21
|
+
name: 2.0,
|
|
22
|
+
digest_text: 1.5,
|
|
23
|
+
inputs: 1.2,
|
|
24
|
+
outputs: 1.2,
|
|
25
|
+
},
|
|
26
|
+
fallbackStrategy: 'semantic',
|
|
27
|
+
});
|
|
28
|
+
// Semantic search using vector embeddings
|
|
29
|
+
this.strategies.set('semantic', {
|
|
30
|
+
name: 'Semantic Search',
|
|
31
|
+
searchType: 'vector',
|
|
32
|
+
fallbackStrategy: 'hybrid',
|
|
33
|
+
});
|
|
34
|
+
// Hybrid approach combining text and vector search
|
|
35
|
+
this.strategies.set('hybrid', {
|
|
36
|
+
name: 'Hybrid Search',
|
|
37
|
+
searchType: 'hybrid',
|
|
38
|
+
weights: { text: 0.6, vector: 0.4 },
|
|
39
|
+
boost: {
|
|
40
|
+
name: 2.0,
|
|
41
|
+
digest_text: 1.5,
|
|
42
|
+
},
|
|
43
|
+
fallbackStrategy: 'keyword',
|
|
44
|
+
});
|
|
45
|
+
// Recent activity search
|
|
46
|
+
this.strategies.set('recent', {
|
|
47
|
+
name: 'Recent Activity',
|
|
48
|
+
searchType: 'text',
|
|
49
|
+
boost: {
|
|
50
|
+
created_at: 3.0,
|
|
51
|
+
closed_at: 2.0,
|
|
52
|
+
},
|
|
53
|
+
fallbackStrategy: 'hybrid',
|
|
54
|
+
});
|
|
55
|
+
// Error and debugging context
|
|
56
|
+
this.strategies.set('debug', {
|
|
57
|
+
name: 'Debug Context',
|
|
58
|
+
searchType: 'hybrid',
|
|
59
|
+
weights: { text: 0.8, vector: 0.2 },
|
|
60
|
+
boost: {
|
|
61
|
+
type: 2.5, // Boost error frames
|
|
62
|
+
digest_text: 2.0,
|
|
63
|
+
outputs: 1.8,
|
|
64
|
+
},
|
|
65
|
+
fallbackStrategy: 'keyword',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async retrieveContext(query) {
|
|
69
|
+
const startTime = Date.now();
|
|
70
|
+
// Handle empty query gracefully
|
|
71
|
+
if (!query.text || query.text.trim().length === 0) {
|
|
72
|
+
logger.debug('Empty query provided, returning empty result');
|
|
73
|
+
return {
|
|
74
|
+
contexts: [],
|
|
75
|
+
totalMatches: 0,
|
|
76
|
+
retrievalTimeMs: Date.now() - startTime,
|
|
77
|
+
strategy: 'empty_query',
|
|
78
|
+
queryAnalysis: {
|
|
79
|
+
intent: 'general',
|
|
80
|
+
concepts: [],
|
|
81
|
+
complexity: 'simple',
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const cacheKey = this.generateCacheKey(query);
|
|
86
|
+
// Check cache first
|
|
87
|
+
const cached = this.getCachedResult(cacheKey);
|
|
88
|
+
if (cached) {
|
|
89
|
+
logger.debug('Context retrieval cache hit');
|
|
90
|
+
return cached;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
logger.info('Starting LLM-driven context retrieval', {
|
|
94
|
+
query: query.text,
|
|
95
|
+
});
|
|
96
|
+
// Analyze query to determine best strategy
|
|
97
|
+
const queryAnalysis = await this.analyzeQuery(query);
|
|
98
|
+
const strategy = this.selectStrategy(queryAnalysis, query);
|
|
99
|
+
logger.debug('Selected retrieval strategy', {
|
|
100
|
+
strategy: strategy.name,
|
|
101
|
+
analysis: queryAnalysis,
|
|
102
|
+
});
|
|
103
|
+
// Execute retrieval with selected strategy
|
|
104
|
+
const contexts = await this.executeRetrieval(query, strategy, queryAnalysis);
|
|
105
|
+
// Post-process and rank results
|
|
106
|
+
const rankedContexts = await this.rankAndFilter(contexts, query, queryAnalysis);
|
|
107
|
+
const result = {
|
|
108
|
+
contexts: rankedContexts,
|
|
109
|
+
totalMatches: contexts.length,
|
|
110
|
+
retrievalTimeMs: Date.now() - startTime,
|
|
111
|
+
strategy: strategy.name,
|
|
112
|
+
queryAnalysis,
|
|
113
|
+
};
|
|
114
|
+
// Cache result
|
|
115
|
+
this.cacheResult(cacheKey, result);
|
|
116
|
+
logger.info('Context retrieval completed', {
|
|
117
|
+
resultsCount: rankedContexts.length,
|
|
118
|
+
timeMs: result.retrievalTimeMs,
|
|
119
|
+
strategy: strategy.name,
|
|
120
|
+
});
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
logger.error('Context retrieval failed:', error);
|
|
125
|
+
// Return fallback empty result
|
|
126
|
+
return {
|
|
127
|
+
contexts: [],
|
|
128
|
+
totalMatches: 0,
|
|
129
|
+
retrievalTimeMs: Date.now() - startTime,
|
|
130
|
+
strategy: 'fallback',
|
|
131
|
+
queryAnalysis: {
|
|
132
|
+
intent: 'unknown',
|
|
133
|
+
concepts: [],
|
|
134
|
+
complexity: 'simple',
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async analyzeQuery(query) {
|
|
140
|
+
const text = query.text.toLowerCase().trim();
|
|
141
|
+
const words = text.split(/\s+/);
|
|
142
|
+
// Determine intent based on keywords
|
|
143
|
+
let intent = 'general';
|
|
144
|
+
if (this.containsKeywords(text, [
|
|
145
|
+
'error',
|
|
146
|
+
'exception',
|
|
147
|
+
'fail',
|
|
148
|
+
'bug',
|
|
149
|
+
'issue',
|
|
150
|
+
'problem',
|
|
151
|
+
'debug',
|
|
152
|
+
])) {
|
|
153
|
+
intent = 'debug';
|
|
154
|
+
}
|
|
155
|
+
else if (this.containsKeywords(text, ['how', 'what', 'why', 'when', 'where'])) {
|
|
156
|
+
intent = 'explanation';
|
|
157
|
+
}
|
|
158
|
+
else if (this.containsKeywords(text, [
|
|
159
|
+
'implement',
|
|
160
|
+
'create',
|
|
161
|
+
'build',
|
|
162
|
+
'add',
|
|
163
|
+
'develop',
|
|
164
|
+
])) {
|
|
165
|
+
intent = 'implementation';
|
|
166
|
+
}
|
|
167
|
+
else if (this.containsKeywords(text, [
|
|
168
|
+
'recent',
|
|
169
|
+
'latest',
|
|
170
|
+
'last',
|
|
171
|
+
'current',
|
|
172
|
+
'happened',
|
|
173
|
+
])) {
|
|
174
|
+
intent = 'recent_activity';
|
|
175
|
+
}
|
|
176
|
+
// Extract concepts (simplified - in production would use NLP)
|
|
177
|
+
const concepts = this.extractConcepts(text);
|
|
178
|
+
// Determine complexity
|
|
179
|
+
let complexity = 'simple';
|
|
180
|
+
if (words.length > 10 || concepts.length > 5) {
|
|
181
|
+
complexity = 'complex';
|
|
182
|
+
}
|
|
183
|
+
else if (words.length > 5 || concepts.length > 2) {
|
|
184
|
+
complexity = 'moderate';
|
|
185
|
+
}
|
|
186
|
+
return { intent, concepts, complexity };
|
|
187
|
+
}
|
|
188
|
+
containsKeywords(text, keywords) {
|
|
189
|
+
return keywords.some((keyword) => text.toLowerCase().includes(keyword.toLowerCase()));
|
|
190
|
+
}
|
|
191
|
+
extractConcepts(text) {
|
|
192
|
+
// Simplified concept extraction - in production would use NLP/embeddings
|
|
193
|
+
const technicalTerms = [
|
|
194
|
+
'database',
|
|
195
|
+
'sql',
|
|
196
|
+
'query',
|
|
197
|
+
'index',
|
|
198
|
+
'migration',
|
|
199
|
+
'adapter',
|
|
200
|
+
'frame',
|
|
201
|
+
'event',
|
|
202
|
+
'anchor',
|
|
203
|
+
'digest',
|
|
204
|
+
'context',
|
|
205
|
+
'search',
|
|
206
|
+
'vector',
|
|
207
|
+
'embedding',
|
|
208
|
+
'similarity',
|
|
209
|
+
'score',
|
|
210
|
+
'rank',
|
|
211
|
+
'performance',
|
|
212
|
+
'optimization',
|
|
213
|
+
'cache',
|
|
214
|
+
'pool',
|
|
215
|
+
'connection',
|
|
216
|
+
'error',
|
|
217
|
+
'exception',
|
|
218
|
+
'debug',
|
|
219
|
+
'trace',
|
|
220
|
+
'log',
|
|
221
|
+
'monitor',
|
|
222
|
+
];
|
|
223
|
+
const concepts = [];
|
|
224
|
+
const words = text.split(/\W+/).map((w) => w.toLowerCase());
|
|
225
|
+
for (const term of technicalTerms) {
|
|
226
|
+
if (words.includes(term)) {
|
|
227
|
+
concepts.push(term);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Add bigrams for common technical phrases
|
|
231
|
+
const bigrams = this.extractBigrams(words);
|
|
232
|
+
const technicalBigrams = [
|
|
233
|
+
'database adapter',
|
|
234
|
+
'query router',
|
|
235
|
+
'connection pool',
|
|
236
|
+
'vector search',
|
|
237
|
+
];
|
|
238
|
+
for (const bigram of bigrams) {
|
|
239
|
+
if (technicalBigrams.includes(bigram)) {
|
|
240
|
+
concepts.push(bigram);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return [...new Set(concepts)]; // Remove duplicates
|
|
244
|
+
}
|
|
245
|
+
extractBigrams(words) {
|
|
246
|
+
const bigrams = [];
|
|
247
|
+
for (let i = 0; i < words.length - 1; i++) {
|
|
248
|
+
bigrams.push(`${words[i]} ${words[i + 1]}`);
|
|
249
|
+
}
|
|
250
|
+
return bigrams;
|
|
251
|
+
}
|
|
252
|
+
selectStrategy(analysis, query) {
|
|
253
|
+
// Override with explicit query type
|
|
254
|
+
if (query.type) {
|
|
255
|
+
return (this.strategies.get(query.type === 'keyword'
|
|
256
|
+
? 'keyword'
|
|
257
|
+
: query.type === 'semantic'
|
|
258
|
+
? 'semantic'
|
|
259
|
+
: 'hybrid') || this.strategies.get('hybrid'));
|
|
260
|
+
}
|
|
261
|
+
// Select based on intent and complexity
|
|
262
|
+
switch (analysis.intent) {
|
|
263
|
+
case 'debug':
|
|
264
|
+
return this.strategies.get('debug');
|
|
265
|
+
case 'recent_activity':
|
|
266
|
+
return this.strategies.get('recent');
|
|
267
|
+
case 'explanation':
|
|
268
|
+
return analysis.complexity === 'simple'
|
|
269
|
+
? this.strategies.get('keyword')
|
|
270
|
+
: this.strategies.get('semantic');
|
|
271
|
+
case 'implementation':
|
|
272
|
+
return this.strategies.get('hybrid');
|
|
273
|
+
default:
|
|
274
|
+
return analysis.complexity === 'complex'
|
|
275
|
+
? this.strategies.get('semantic')
|
|
276
|
+
: this.strategies.get('keyword');
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async executeRetrieval(query, strategy, analysis) {
|
|
280
|
+
const searchOptions = {
|
|
281
|
+
query: query.text,
|
|
282
|
+
searchType: strategy.searchType,
|
|
283
|
+
limit: query.maxResults || 20,
|
|
284
|
+
scoreThreshold: query.scoreThreshold || 0.1,
|
|
285
|
+
boost: strategy.boost,
|
|
286
|
+
};
|
|
287
|
+
// Add field filtering based on query type
|
|
288
|
+
if (query.frameTypes) {
|
|
289
|
+
searchOptions.fields = ['type', 'name', 'digest_text'];
|
|
290
|
+
}
|
|
291
|
+
let rawResults = [];
|
|
292
|
+
try {
|
|
293
|
+
if (strategy.searchType === 'hybrid' && strategy.weights) {
|
|
294
|
+
// Use hybrid search with embeddings (placeholder - would need actual embeddings)
|
|
295
|
+
const embedding = await this.generateEmbedding(query.text);
|
|
296
|
+
rawResults = await this.adapter.searchHybrid(query.text, embedding, strategy.weights);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
// Use text or vector search
|
|
300
|
+
rawResults = await this.adapter.search(searchOptions);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
logger.warn(`Strategy ${strategy.name} failed, trying fallback:`, error);
|
|
305
|
+
if (strategy.fallbackStrategy) {
|
|
306
|
+
const fallbackStrategy = this.strategies.get(strategy.fallbackStrategy);
|
|
307
|
+
if (fallbackStrategy) {
|
|
308
|
+
return this.executeRetrieval(query, fallbackStrategy, analysis);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Return empty results instead of throwing to prevent cascading failures
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
// Convert to RetrievedContext objects
|
|
315
|
+
return rawResults.map((result) => ({
|
|
316
|
+
frame: result,
|
|
317
|
+
score: result.score,
|
|
318
|
+
relevanceReason: this.generateRelevanceReason(result, query, analysis),
|
|
319
|
+
retrievalMethod: strategy.searchType,
|
|
320
|
+
matchedFields: this.identifyMatchedFields(result, query),
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
323
|
+
async generateEmbedding(text) {
|
|
324
|
+
// Placeholder - in production would use actual embedding service
|
|
325
|
+
// For now, return a mock embedding
|
|
326
|
+
const hash = this.simpleHash(text);
|
|
327
|
+
return Array.from({ length: 384 }, (_, i) => ((hash + i) % 100) / 100 - 0.5);
|
|
328
|
+
}
|
|
329
|
+
simpleHash(str) {
|
|
330
|
+
let hash = 0;
|
|
331
|
+
for (let i = 0; i < str.length; i++) {
|
|
332
|
+
const char = str.charCodeAt(i);
|
|
333
|
+
hash = (hash << 5) - hash + char;
|
|
334
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
335
|
+
}
|
|
336
|
+
return Math.abs(hash);
|
|
337
|
+
}
|
|
338
|
+
generateRelevanceReason(frame, query, analysis) {
|
|
339
|
+
const reasons = [];
|
|
340
|
+
// Check for direct matches
|
|
341
|
+
if (frame.name.toLowerCase().includes(query.text.toLowerCase())) {
|
|
342
|
+
reasons.push('Frame name matches query');
|
|
343
|
+
}
|
|
344
|
+
if (frame.digest_text?.toLowerCase().includes(query.text.toLowerCase())) {
|
|
345
|
+
reasons.push('Content contains query terms');
|
|
346
|
+
}
|
|
347
|
+
// Check for concept matches
|
|
348
|
+
for (const concept of analysis.concepts) {
|
|
349
|
+
if (frame.digest_text?.toLowerCase().includes(concept.toLowerCase()) ||
|
|
350
|
+
frame.name.toLowerCase().includes(concept.toLowerCase())) {
|
|
351
|
+
reasons.push(`Related to ${concept}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// Frame type relevance
|
|
355
|
+
if (analysis.intent === 'debug' && frame.type.includes('error')) {
|
|
356
|
+
reasons.push('Error context for debugging');
|
|
357
|
+
}
|
|
358
|
+
return reasons.length > 0
|
|
359
|
+
? reasons.join('; ')
|
|
360
|
+
: 'General semantic similarity';
|
|
361
|
+
}
|
|
362
|
+
identifyMatchedFields(frame, query) {
|
|
363
|
+
const matched = [];
|
|
364
|
+
const queryLower = query.text.toLowerCase();
|
|
365
|
+
if (frame.name.toLowerCase().includes(queryLower)) {
|
|
366
|
+
matched.push('name');
|
|
367
|
+
}
|
|
368
|
+
if (frame.digest_text?.toLowerCase().includes(queryLower)) {
|
|
369
|
+
matched.push('digest_text');
|
|
370
|
+
}
|
|
371
|
+
if (frame.type.toLowerCase().includes(queryLower)) {
|
|
372
|
+
matched.push('type');
|
|
373
|
+
}
|
|
374
|
+
return matched;
|
|
375
|
+
}
|
|
376
|
+
async rankAndFilter(contexts, query, analysis) {
|
|
377
|
+
// Apply additional filtering
|
|
378
|
+
let filtered = contexts;
|
|
379
|
+
// Filter by time range
|
|
380
|
+
if (query.timeRange) {
|
|
381
|
+
filtered = filtered.filter((ctx) => {
|
|
382
|
+
const frameTime = new Date(ctx.frame.created_at);
|
|
383
|
+
const start = query.timeRange?.start;
|
|
384
|
+
const end = query.timeRange?.end;
|
|
385
|
+
return (!start || frameTime >= start) && (!end || frameTime <= end);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
// Filter by frame types
|
|
389
|
+
if (query.frameTypes) {
|
|
390
|
+
filtered = filtered.filter((ctx) => query.frameTypes.includes(ctx.frame.type));
|
|
391
|
+
}
|
|
392
|
+
// Apply score threshold
|
|
393
|
+
if (query.scoreThreshold) {
|
|
394
|
+
filtered = filtered.filter((ctx) => ctx.score >= query.scoreThreshold);
|
|
395
|
+
}
|
|
396
|
+
// Enhanced ranking based on multiple factors
|
|
397
|
+
const ranked = filtered.map((ctx) => ({
|
|
398
|
+
...ctx,
|
|
399
|
+
score: this.calculateEnhancedScore(ctx, query, analysis),
|
|
400
|
+
}));
|
|
401
|
+
// Sort by enhanced score
|
|
402
|
+
ranked.sort((a, b) => b.score - a.score);
|
|
403
|
+
// Limit results
|
|
404
|
+
const maxResults = query.maxResults || 20;
|
|
405
|
+
return ranked.slice(0, maxResults);
|
|
406
|
+
}
|
|
407
|
+
calculateEnhancedScore(context, query, analysis) {
|
|
408
|
+
let score = context.score;
|
|
409
|
+
// Boost recent frames
|
|
410
|
+
const ageHours = (Date.now() - context.frame.created_at) / (1000 * 60 * 60);
|
|
411
|
+
if (ageHours < 24) {
|
|
412
|
+
score *= 1.2; // 20% boost for frames from last 24 hours
|
|
413
|
+
}
|
|
414
|
+
else if (ageHours < 168) {
|
|
415
|
+
// 1 week
|
|
416
|
+
score *= 1.1; // 10% boost for frames from last week
|
|
417
|
+
}
|
|
418
|
+
// Boost based on frame completeness
|
|
419
|
+
if (context.frame.closed_at) {
|
|
420
|
+
score *= 1.1; // Completed frames are more valuable
|
|
421
|
+
}
|
|
422
|
+
// Boost based on intent matching
|
|
423
|
+
if (analysis.intent === 'debug' && context.frame.type.includes('error')) {
|
|
424
|
+
score *= 1.5;
|
|
425
|
+
}
|
|
426
|
+
// Boost based on matched fields
|
|
427
|
+
if (context.matchedFields.includes('name')) {
|
|
428
|
+
score *= 1.3; // Name matches are highly relevant
|
|
429
|
+
}
|
|
430
|
+
if (context.matchedFields.length > 1) {
|
|
431
|
+
score *= 1.1; // Multiple field matches
|
|
432
|
+
}
|
|
433
|
+
// Penalize very old frames for recent queries
|
|
434
|
+
if (analysis.intent === 'recent_activity' && ageHours > 168) {
|
|
435
|
+
score *= 0.5;
|
|
436
|
+
}
|
|
437
|
+
return score;
|
|
438
|
+
}
|
|
439
|
+
generateCacheKey(query) {
|
|
440
|
+
return JSON.stringify({
|
|
441
|
+
text: query.text,
|
|
442
|
+
type: query.type,
|
|
443
|
+
maxResults: query.maxResults,
|
|
444
|
+
frameTypes: query.frameTypes,
|
|
445
|
+
scoreThreshold: query.scoreThreshold,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
getCachedResult(cacheKey) {
|
|
449
|
+
const entry = this.queryCache.get(cacheKey);
|
|
450
|
+
if (!entry)
|
|
451
|
+
return null;
|
|
452
|
+
// Check expiry (simplified - would include timestamp in real implementation)
|
|
453
|
+
return entry;
|
|
454
|
+
}
|
|
455
|
+
cacheResult(cacheKey, result) {
|
|
456
|
+
// Implement LRU eviction if cache is full
|
|
457
|
+
if (this.queryCache.size >= this.cacheMaxSize) {
|
|
458
|
+
const firstKey = this.queryCache.keys().next().value;
|
|
459
|
+
this.queryCache.delete(firstKey);
|
|
460
|
+
}
|
|
461
|
+
this.queryCache.set(cacheKey, result);
|
|
462
|
+
}
|
|
463
|
+
// Utility methods for integration
|
|
464
|
+
async findSimilarFrames(frameId, limit = 10) {
|
|
465
|
+
const frame = await this.adapter.getFrame(frameId);
|
|
466
|
+
if (!frame) {
|
|
467
|
+
throw new Error(`Frame not found: ${frameId}`);
|
|
468
|
+
}
|
|
469
|
+
const query = {
|
|
470
|
+
text: frame.digest_text || frame.name,
|
|
471
|
+
type: 'semantic',
|
|
472
|
+
maxResults: limit,
|
|
473
|
+
scoreThreshold: 0.3,
|
|
474
|
+
};
|
|
475
|
+
const result = await this.retrieveContext(query);
|
|
476
|
+
// Filter out the original frame
|
|
477
|
+
return result.contexts.filter((ctx) => ctx.frame.frame_id !== frameId);
|
|
478
|
+
}
|
|
479
|
+
async findContextForError(errorMessage, stackTrace) {
|
|
480
|
+
const query = {
|
|
481
|
+
text: `${errorMessage} ${stackTrace || ''}`.trim(),
|
|
482
|
+
type: 'hybrid',
|
|
483
|
+
maxResults: 15,
|
|
484
|
+
frameTypes: ['error', 'debug', 'function'],
|
|
485
|
+
scoreThreshold: 0.2,
|
|
486
|
+
};
|
|
487
|
+
const result = await this.retrieveContext(query);
|
|
488
|
+
return result.contexts;
|
|
489
|
+
}
|
|
490
|
+
async getRecentContext(hours = 24, frameTypes) {
|
|
491
|
+
const query = {
|
|
492
|
+
text: 'recent activity context',
|
|
493
|
+
type: 'keyword',
|
|
494
|
+
maxResults: 50,
|
|
495
|
+
timeRange: {
|
|
496
|
+
start: new Date(Date.now() - hours * 60 * 60 * 1000),
|
|
497
|
+
},
|
|
498
|
+
frameTypes,
|
|
499
|
+
scoreThreshold: 0.1,
|
|
500
|
+
};
|
|
501
|
+
const result = await this.retrieveContext(query);
|
|
502
|
+
return result.contexts;
|
|
503
|
+
}
|
|
504
|
+
// Analytics and insights
|
|
505
|
+
getRetrievalStats() {
|
|
506
|
+
return {
|
|
507
|
+
cacheSize: this.queryCache.size,
|
|
508
|
+
strategiesCount: this.strategies.size,
|
|
509
|
+
availableStrategies: Array.from(this.strategies.keys()),
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
clearCache() {
|
|
513
|
+
this.queryCache.clear();
|
|
514
|
+
logger.info('Context retrieval cache cleared');
|
|
515
|
+
}
|
|
516
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SessionManager, sessionManager, FrameQueryMode } from './session-manager.js';
|