@stackmemoryai/stackmemory 0.3.17 → 0.3.19

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.
Files changed (234) hide show
  1. package/dist/cli/claude-sm.js +51 -5
  2. package/dist/cli/claude-sm.js.map +2 -2
  3. package/dist/cli/codex-sm.js +52 -19
  4. package/dist/cli/codex-sm.js.map +2 -2
  5. package/dist/cli/commands/db.js +143 -0
  6. package/dist/cli/commands/db.js.map +7 -0
  7. package/dist/cli/commands/login.js +50 -0
  8. package/dist/cli/commands/login.js.map +7 -0
  9. package/dist/cli/commands/migrate.js +178 -0
  10. package/dist/cli/commands/migrate.js.map +7 -0
  11. package/dist/cli/commands/onboard.js +158 -2
  12. package/dist/cli/commands/onboard.js.map +2 -2
  13. package/dist/cli/commands/skills.js +15 -2
  14. package/dist/cli/commands/skills.js.map +2 -2
  15. package/dist/cli/index.js +118 -834
  16. package/dist/cli/index.js.map +3 -3
  17. package/dist/core/context/dual-stack-manager.js +1 -1
  18. package/dist/core/context/dual-stack-manager.js.map +1 -1
  19. package/dist/core/context/frame-database.js +1 -0
  20. package/dist/core/context/frame-database.js.map +2 -2
  21. package/dist/core/context/frame-manager.js +59 -2
  22. package/dist/core/context/frame-manager.js.map +2 -2
  23. package/dist/core/database/database-adapter.js +6 -1
  24. package/dist/core/database/database-adapter.js.map +2 -2
  25. package/dist/core/database/sqlite-adapter.js +60 -2
  26. package/dist/core/database/sqlite-adapter.js.map +2 -2
  27. package/dist/integrations/claude-code/subagent-client.js +106 -3
  28. package/dist/integrations/claude-code/subagent-client.js.map +2 -2
  29. package/dist/servers/railway/config.js +51 -0
  30. package/dist/servers/railway/config.js.map +7 -0
  31. package/dist/servers/railway/index-enhanced.js +156 -0
  32. package/dist/servers/railway/index-enhanced.js.map +7 -0
  33. package/dist/servers/railway/index.js +843 -82
  34. package/dist/servers/railway/index.js.map +3 -3
  35. package/dist/servers/railway/minimal.js +48 -3
  36. package/dist/servers/railway/minimal.js.map +2 -2
  37. package/dist/servers/railway/storage-test.js +455 -0
  38. package/dist/servers/railway/storage-test.js.map +7 -0
  39. package/dist/skills/claude-skills.js +13 -12
  40. package/dist/skills/claude-skills.js.map +2 -2
  41. package/dist/skills/recursive-agent-orchestrator.js +27 -18
  42. package/dist/skills/recursive-agent-orchestrator.js.map +2 -2
  43. package/dist/skills/unified-rlm-orchestrator.js.map +2 -2
  44. package/package.json +13 -21
  45. package/scripts/README-TESTING.md +186 -0
  46. package/scripts/analyze-cli-security.js +288 -0
  47. package/scripts/archive/add-phase-tasks-to-linear.js +163 -0
  48. package/scripts/archive/analyze-linear-duplicates.js +214 -0
  49. package/scripts/archive/analyze-remaining-duplicates.js +230 -0
  50. package/scripts/archive/analyze-sta-duplicates.js +292 -0
  51. package/scripts/archive/analyze-sta-graphql.js +399 -0
  52. package/scripts/archive/cancel-duplicate-tasks.ts +246 -0
  53. package/scripts/archive/check-all-duplicates.ts +419 -0
  54. package/scripts/archive/clean-duplicate-tasks.js +114 -0
  55. package/scripts/archive/cleanup-duplicate-tasks.ts +286 -0
  56. package/scripts/archive/create-phase-tasks.js +387 -0
  57. package/scripts/archive/delete-linear-duplicates.js +182 -0
  58. package/scripts/archive/delete-remaining-duplicates.js +158 -0
  59. package/scripts/archive/delete-sta-duplicates.js +201 -0
  60. package/scripts/archive/delete-sta-oauth.js +201 -0
  61. package/scripts/archive/export-sta-tasks.js +62 -0
  62. package/scripts/archive/install-auto-sync.js +266 -0
  63. package/scripts/archive/install-chromadb-hooks.sh +133 -0
  64. package/scripts/archive/install-enhanced-clear-hooks.sh +431 -0
  65. package/scripts/archive/install-post-task-hooks.sh +289 -0
  66. package/scripts/archive/install-stackmemory-hooks.sh +420 -0
  67. package/scripts/archive/merge-linear-duplicates-safe.ts +362 -0
  68. package/scripts/archive/merge-linear-duplicates.ts +180 -0
  69. package/scripts/archive/remove-sta-tasks.js +70 -0
  70. package/scripts/archive/setup-background-sync.sh +168 -0
  71. package/scripts/archive/setup-claude-auto-triggers.sh +181 -0
  72. package/scripts/archive/setup-claude-autostart.sh +305 -0
  73. package/scripts/archive/setup-git-hooks.sh +25 -0
  74. package/scripts/archive/setup-linear-oauth.sh +46 -0
  75. package/scripts/archive/setup-mcp.sh +113 -0
  76. package/scripts/archive/setup-railway-deployment.sh +81 -0
  77. package/scripts/auto-handoff.sh +262 -0
  78. package/scripts/background-sync-manager.js +416 -0
  79. package/scripts/benchmark-performance.ts +57 -0
  80. package/scripts/check-redis.ts +48 -0
  81. package/scripts/chromadb-auto-loader.sh +128 -0
  82. package/scripts/chromadb-context-loader.js +479 -0
  83. package/scripts/claude-chromadb-hook.js +460 -0
  84. package/scripts/claude-code-wrapper.sh +66 -0
  85. package/scripts/claude-linear-skill.js +455 -0
  86. package/scripts/claude-pre-commit.sh +302 -0
  87. package/scripts/claude-sm-autostart.js +532 -0
  88. package/scripts/claude-sm-setup.sh +367 -0
  89. package/scripts/claude-with-chromadb.sh +69 -0
  90. package/scripts/claude-worktree-manager.sh +323 -0
  91. package/scripts/claude-worktree-monitor.sh +371 -0
  92. package/scripts/claude-worktree-setup.sh +327 -0
  93. package/scripts/clean-linear-backlog.js +273 -0
  94. package/scripts/cleanup-old-sessions.sh +57 -0
  95. package/scripts/codex-wrapper.sh +88 -0
  96. package/scripts/create-sandbox.sh +269 -0
  97. package/scripts/debug-linear-update.js +174 -0
  98. package/scripts/delete-linear-tasks.js +167 -0
  99. package/scripts/deploy.sh +89 -0
  100. package/scripts/deployment/railway.sh +352 -0
  101. package/scripts/deployment/test-deployment.js +194 -0
  102. package/scripts/detect-and-rehydrate.js +162 -0
  103. package/scripts/detect-and-rehydrate.mjs +165 -0
  104. package/scripts/development/create-demo-tasks.js +143 -0
  105. package/scripts/development/debug-frame-test.js +16 -0
  106. package/scripts/development/demo-auto-sync.js +128 -0
  107. package/scripts/development/fix-all-imports.js +213 -0
  108. package/scripts/development/fix-imports.js +229 -0
  109. package/scripts/development/fix-lint-loop.cjs +103 -0
  110. package/scripts/development/fix-project-id.ts +161 -0
  111. package/scripts/development/fix-strict-mode-issues.ts +291 -0
  112. package/scripts/development/reorganize-structure.sh +228 -0
  113. package/scripts/development/test-persistence-direct.js +148 -0
  114. package/scripts/development/test-persistence.js +114 -0
  115. package/scripts/development/test-tasks.js +93 -0
  116. package/scripts/development/update-imports.js +212 -0
  117. package/scripts/fetch-linear-status.js +125 -0
  118. package/scripts/git-hooks/README.md +310 -0
  119. package/scripts/git-hooks/branch-context-manager.sh +342 -0
  120. package/scripts/git-hooks/post-checkout-stackmemory.sh +63 -0
  121. package/scripts/git-hooks/post-commit-stackmemory.sh +305 -0
  122. package/scripts/git-hooks/pre-commit-stackmemory.sh +275 -0
  123. package/scripts/hooks/cleanup-shell.sh +130 -0
  124. package/scripts/hooks/task-complete.sh +114 -0
  125. package/scripts/initialize.ts +129 -0
  126. package/scripts/install-claude-hooks-auto.js +104 -0
  127. package/scripts/install-claude-hooks.sh +133 -0
  128. package/scripts/install-global.sh +296 -0
  129. package/scripts/install.sh +235 -0
  130. package/scripts/linear-auto-sync.js +262 -0
  131. package/scripts/linear-auto-sync.sh +161 -0
  132. package/scripts/linear-sync-daemon.js +150 -0
  133. package/scripts/linear-task-review.js +237 -0
  134. package/scripts/list-linear-tasks.ts +178 -0
  135. package/scripts/mcp-proxy.js +66 -0
  136. package/scripts/opencode-wrapper.sh +85 -0
  137. package/scripts/publish-local.js +74 -0
  138. package/scripts/query-chromadb.ts +201 -0
  139. package/scripts/railway-env-setup.sh +39 -0
  140. package/scripts/reconcile-local-tasks.js +170 -0
  141. package/scripts/recreate-frames-db.js +89 -0
  142. package/scripts/setup/claude-integration.js +138 -0
  143. package/scripts/setup/configure-alias.js +125 -0
  144. package/scripts/setup/configure-codex-alias.js +161 -0
  145. package/scripts/setup/configure-opencode-alias.js +175 -0
  146. package/scripts/setup-claude-integration.js +204 -0
  147. package/scripts/setup-claude-integration.sh +183 -0
  148. package/scripts/setup-railway-deployment.sh +37 -0
  149. package/scripts/setup.sh +31 -0
  150. package/scripts/show-linear-summary.ts +172 -0
  151. package/scripts/stackmemory-auto-handoff.sh +231 -0
  152. package/scripts/stackmemory-daemon.sh +40 -0
  153. package/scripts/start-linear-sync-daemon.sh +141 -0
  154. package/scripts/start-temporal-paradox.sh +214 -0
  155. package/scripts/status.ts +159 -0
  156. package/scripts/sync-and-clean-tasks.js +258 -0
  157. package/scripts/sync-frames-from-railway.js +228 -0
  158. package/scripts/sync-linear-graphql.js +303 -0
  159. package/scripts/sync-linear-tasks.js +186 -0
  160. package/scripts/test-auto-triggers.sh +57 -0
  161. package/scripts/test-browser-mcp.js +74 -0
  162. package/scripts/test-chromadb-full.js +115 -0
  163. package/scripts/test-chromadb-hooks.sh +28 -0
  164. package/scripts/test-chromadb-sync.ts +245 -0
  165. package/scripts/test-cli-security.js +293 -0
  166. package/scripts/test-hooks-persistence.sh +220 -0
  167. package/scripts/test-installation-scenarios.sh +359 -0
  168. package/scripts/test-installation.sh +224 -0
  169. package/scripts/test-mcp.js +163 -0
  170. package/scripts/test-pre-publish-quick.sh +75 -0
  171. package/scripts/test-quality-gates.sh +263 -0
  172. package/scripts/test-railway-db.js +222 -0
  173. package/scripts/test-redis-storage.ts +490 -0
  174. package/scripts/test-rlm-basic.sh +122 -0
  175. package/scripts/test-rlm-comprehensive.sh +260 -0
  176. package/scripts/test-rlm-e2e.sh +268 -0
  177. package/scripts/test-rlm-simple.js +90 -0
  178. package/scripts/test-rlm.js +110 -0
  179. package/scripts/test-session-handoff.sh +165 -0
  180. package/scripts/test-shell-integration.sh +275 -0
  181. package/scripts/testing/ab-test-runner.ts +508 -0
  182. package/scripts/testing/collect-metrics.ts +457 -0
  183. package/scripts/testing/quick-effectiveness-demo.js +187 -0
  184. package/scripts/testing/real-performance-test.js +422 -0
  185. package/scripts/testing/run-effectiveness-tests.sh +176 -0
  186. package/scripts/testing/scripts/testing/ab-test-runner.js +363 -0
  187. package/scripts/testing/scripts/testing/collect-metrics.js +292 -0
  188. package/scripts/testing/simple-effectiveness-test.js +310 -0
  189. package/scripts/testing/src/core/context/context-bridge.js +253 -0
  190. package/scripts/testing/src/core/context/frame-manager.js +746 -0
  191. package/scripts/testing/src/core/context/shared-context-layer.js +437 -0
  192. package/scripts/testing/src/core/database/database-adapter.js +54 -0
  193. package/scripts/testing/src/core/errors/index.js +291 -0
  194. package/scripts/testing/src/core/errors/recovery.js +268 -0
  195. package/scripts/testing/src/core/monitoring/logger.js +145 -0
  196. package/scripts/testing/src/core/retrieval/context-retriever.js +516 -0
  197. package/scripts/testing/src/core/session/index.js +1 -0
  198. package/scripts/testing/src/core/session/session-manager.js +323 -0
  199. package/scripts/testing/src/core/trace/cli-trace-wrapper.js +140 -0
  200. package/scripts/testing/src/core/trace/db-trace-wrapper.js +251 -0
  201. package/scripts/testing/src/core/trace/debug-trace.js +398 -0
  202. package/scripts/testing/src/core/trace/index.js +120 -0
  203. package/scripts/testing/src/core/trace/linear-api-wrapper.js +204 -0
  204. package/scripts/update-linear-status.js +268 -0
  205. package/scripts/update-linear-tasks-fixed.js +284 -0
  206. package/scripts/verify-railway-schema.ts +35 -0
  207. package/templates/claude-hooks/hooks.json +5 -0
  208. package/templates/claude-hooks/on-clear.js +56 -0
  209. package/templates/claude-hooks/on-startup.js +56 -0
  210. package/templates/claude-hooks/tool-use-trace.js +67 -0
  211. package/dist/features/tui/components/analytics-panel.js +0 -157
  212. package/dist/features/tui/components/analytics-panel.js.map +0 -7
  213. package/dist/features/tui/components/frame-visualizer.js +0 -377
  214. package/dist/features/tui/components/frame-visualizer.js.map +0 -7
  215. package/dist/features/tui/components/pr-tracker.js +0 -135
  216. package/dist/features/tui/components/pr-tracker.js.map +0 -7
  217. package/dist/features/tui/components/session-monitor.js +0 -299
  218. package/dist/features/tui/components/session-monitor.js.map +0 -7
  219. package/dist/features/tui/components/subagent-fleet.js +0 -395
  220. package/dist/features/tui/components/subagent-fleet.js.map +0 -7
  221. package/dist/features/tui/components/task-board.js +0 -1139
  222. package/dist/features/tui/components/task-board.js.map +0 -7
  223. package/dist/features/tui/index.js +0 -408
  224. package/dist/features/tui/index.js.map +0 -7
  225. package/dist/features/tui/services/data-service.js +0 -641
  226. package/dist/features/tui/services/data-service.js.map +0 -7
  227. package/dist/features/tui/services/linear-task-reader.js +0 -102
  228. package/dist/features/tui/services/linear-task-reader.js.map +0 -7
  229. package/dist/features/tui/services/websocket-client.js +0 -162
  230. package/dist/features/tui/services/websocket-client.js.map +0 -7
  231. package/dist/features/tui/terminal-compat.js +0 -220
  232. package/dist/features/tui/terminal-compat.js.map +0 -7
  233. package/dist/features/tui/types.js +0 -1
  234. 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';