@stackmemoryai/stackmemory 1.12.0 → 1.14.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.
Files changed (162) hide show
  1. package/LICENSE +131 -64
  2. package/README.md +3 -1
  3. package/bin/claude-sm +16 -1
  4. package/bin/claude-smd +16 -1
  5. package/bin/codex-smd +16 -1
  6. package/bin/gemini-sm +16 -1
  7. package/bin/hermes-sm +21 -0
  8. package/bin/hermes-smd +21 -0
  9. package/bin/opencode-sm +16 -1
  10. package/dist/src/cli/codex-sm.js +51 -11
  11. package/dist/src/cli/commands/brain.js +206 -0
  12. package/dist/src/cli/commands/company-os.js +184 -0
  13. package/dist/src/cli/commands/context.js +5 -0
  14. package/dist/src/cli/commands/operator.js +127 -0
  15. package/dist/src/cli/commands/orchestrate.js +2 -0
  16. package/dist/src/cli/commands/orchestrator.js +3 -2
  17. package/dist/src/cli/commands/patterns.js +254 -0
  18. package/dist/src/cli/commands/portal.js +161 -0
  19. package/dist/src/cli/commands/scaffold.js +92 -0
  20. package/dist/src/cli/commands/setup.js +1 -4
  21. package/dist/src/cli/commands/sync.js +253 -0
  22. package/dist/src/cli/commands/tasks.js +130 -1
  23. package/dist/src/cli/commands/vision.js +221 -0
  24. package/dist/src/cli/hermes-sm.js +224 -0
  25. package/dist/src/cli/index.js +15 -10
  26. package/dist/src/cli/utils/real-cli-bin.js +72 -0
  27. package/dist/src/core/brain/brain-store.js +187 -0
  28. package/dist/src/core/brain/brain-sync.js +193 -0
  29. package/dist/src/core/brain/index.js +78 -0
  30. package/dist/src/core/brain/types.js +10 -0
  31. package/dist/src/core/cache/token-estimator.js +24 -1
  32. package/dist/src/core/config/feature-flags.js +2 -6
  33. package/dist/src/core/context/frame-database.js +44 -0
  34. package/dist/src/core/context/recursive-context-manager.js +1 -1
  35. package/dist/src/core/context/rehydration.js +2 -1
  36. package/dist/src/core/database/sqlite-adapter.js +14 -1
  37. package/dist/src/core/models/model-router.js +33 -1
  38. package/dist/src/core/models/provider-pricing.js +58 -4
  39. package/dist/src/core/patterns/index.js +22 -0
  40. package/dist/src/core/patterns/pattern-applier.js +39 -0
  41. package/dist/src/core/patterns/pattern-observer.js +157 -0
  42. package/dist/src/core/patterns/pattern-store.js +259 -0
  43. package/dist/src/core/patterns/types.js +19 -0
  44. package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
  45. package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
  46. package/dist/src/core/skill-packs/types.js +14 -1
  47. package/dist/src/core/storage/cloud-sync-manager.js +116 -0
  48. package/dist/src/core/storage/cloud-sync.js +574 -0
  49. package/dist/src/core/storage/two-tier-storage.js +5 -1
  50. package/dist/src/core/tasks/master-tasks-template.js +43 -0
  51. package/dist/src/core/tasks/md-task-parser.js +138 -0
  52. package/dist/src/core/vision/index.js +27 -0
  53. package/dist/src/core/vision/signals.js +79 -0
  54. package/dist/src/core/vision/types.js +22 -0
  55. package/dist/src/core/vision/vision-file.js +146 -0
  56. package/dist/src/core/vision/vision-loop.js +220 -0
  57. package/dist/src/core/wiki/wiki-compiler.js +103 -1
  58. package/dist/src/daemon/daemon-config.js +45 -0
  59. package/dist/src/daemon/services/desire-path-service.js +566 -0
  60. package/dist/src/daemon/services/research-stream-service.js +320 -0
  61. package/dist/src/daemon/services/telemetry-service.js +192 -0
  62. package/dist/src/daemon/unified-daemon.js +28 -1
  63. package/dist/src/features/browser/cli-browser-agent.js +417 -0
  64. package/dist/src/features/browser/stagehand-workflows.js +578 -0
  65. package/dist/src/features/operator/adapter-factory.js +62 -0
  66. package/dist/src/features/operator/browser-adapter.js +109 -0
  67. package/dist/src/features/operator/desktop-adapter.js +125 -0
  68. package/dist/src/features/operator/index.js +39 -0
  69. package/dist/src/features/operator/llm-decision.js +137 -0
  70. package/dist/src/features/operator/operator-logger.js +92 -0
  71. package/dist/src/features/operator/overnight-runner.js +327 -0
  72. package/dist/src/features/operator/screen-adapter.js +91 -0
  73. package/dist/src/features/operator/session-manager.js +127 -0
  74. package/dist/src/features/operator/state-machine.js +227 -0
  75. package/dist/src/features/operator/task-queue.js +81 -0
  76. package/dist/src/features/portal/index.js +26 -0
  77. package/dist/src/features/portal/server.js +240 -0
  78. package/dist/src/features/portal/types.js +14 -0
  79. package/dist/src/features/portal/ui.js +195 -0
  80. package/dist/src/features/tasks/task-aware-context.js +2 -1
  81. package/dist/src/features/tui/simple-monitor.js +0 -23
  82. package/dist/src/features/tui/swarm-monitor.js +8 -66
  83. package/dist/src/{integrations/diffmem/index.js → features/web/client/hooks/use-socket.js} +6 -5
  84. package/dist/src/features/web/client/lib/utils.js +12 -0
  85. package/dist/src/features/web/client/stores/session-store.js +12 -0
  86. package/dist/src/features/web/server/gcp-billing.js +76 -0
  87. package/dist/src/features/web/server/index.js +10 -0
  88. package/dist/src/features/web/server/spend-calculator.js +228 -0
  89. package/dist/src/hooks/schemas.js +4 -1
  90. package/dist/src/integrations/anthropic/client.js +3 -2
  91. package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
  92. package/dist/src/integrations/claude-code/subagent-client.js +218 -11
  93. package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
  94. package/dist/src/integrations/linear/webhook-retry.js +196 -0
  95. package/dist/src/integrations/linear/webhook-server.js +18 -22
  96. package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
  97. package/dist/src/integrations/mcp/handlers/index.js +27 -52
  98. package/dist/src/integrations/mcp/server.js +122 -335
  99. package/dist/src/integrations/mcp/tool-alias-registry.js +0 -73
  100. package/dist/src/integrations/mcp/tool-definitions.js +111 -510
  101. package/dist/src/mcp/stackmemory-mcp-server.js +404 -379
  102. package/dist/src/orchestrators/multimodal/determinism.js +2 -1
  103. package/dist/src/orchestrators/multimodal/harness.js +2 -1
  104. package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
  105. package/dist/src/utils/process-cleanup.js +1 -7
  106. package/docs/README.md +42 -0
  107. package/docs/guides/README_INSTALL.md +208 -0
  108. package/package.json +18 -9
  109. package/scripts/claude-code-wrapper.sh +11 -0
  110. package/scripts/claude-sm-setup.sh +12 -1
  111. package/scripts/codex-wrapper.sh +11 -0
  112. package/scripts/git-hooks/branch-context-manager.sh +11 -0
  113. package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
  114. package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
  115. package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
  116. package/scripts/hooks/cleanup-shell.sh +12 -1
  117. package/scripts/hooks/task-complete.sh +12 -1
  118. package/scripts/install-code-execution-hooks.sh +12 -1
  119. package/scripts/install-sweep-hook.sh +12 -0
  120. package/scripts/install.sh +11 -0
  121. package/scripts/opencode-wrapper.sh +11 -0
  122. package/scripts/portal/cloud-init.yaml +69 -0
  123. package/scripts/portal/setup.sh +69 -0
  124. package/scripts/portal/stackmemory-portal.service +34 -0
  125. package/scripts/setup-claude-integration.sh +12 -1
  126. package/scripts/smoke-init-db.sh +23 -0
  127. package/scripts/stackmemory-daemon.sh +11 -0
  128. package/scripts/verify-dist.cjs +11 -4
  129. package/dist/src/cli/commands/ralph.js +0 -1053
  130. package/dist/src/hooks/diffmem-hooks.js +0 -376
  131. package/dist/src/integrations/diffmem/client.js +0 -208
  132. package/dist/src/integrations/diffmem/config.js +0 -14
  133. package/dist/src/integrations/greptile/client.js +0 -101
  134. package/dist/src/integrations/greptile/config.js +0 -14
  135. package/dist/src/integrations/greptile/index.js +0 -11
  136. package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +0 -188
  137. package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
  138. package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
  139. package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
  140. package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
  141. package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
  142. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -354
  143. package/dist/src/integrations/ralph/index.js +0 -17
  144. package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -416
  145. package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
  146. package/dist/src/integrations/ralph/loopmax.js +0 -488
  147. package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
  148. package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
  149. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
  150. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
  151. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
  152. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
  153. package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
  154. package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
  155. package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
  156. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
  157. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1005
  158. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
  159. package/scripts/ralph-loop-implementation.js +0 -404
  160. /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
  161. /package/dist/src/{integrations/greptile → features/operator}/types.js +0 -0
  162. /package/dist/src/{integrations/ralph/types.js → features/web/client/next-env.d.js} +0 -0
@@ -1,376 +0,0 @@
1
- import { fileURLToPath as __fileURLToPath } from 'url';
2
- import { dirname as __pathDirname } from 'path';
3
- const __filename = __fileURLToPath(import.meta.url);
4
- const __dirname = __pathDirname(__filename);
5
- import { logger } from "../core/monitoring/logger.js";
6
- const DEFAULT_CONFIG = {
7
- enabled: !!process.env.DIFFMEM_ENDPOINT,
8
- endpoint: process.env.DIFFMEM_ENDPOINT || "http://localhost:3100",
9
- autoFetchCategories: ["preference", "expertise", "pattern"],
10
- autoLearnEnabled: true,
11
- learningConfidenceThreshold: 0.7,
12
- maxMemoriesPerSession: 50
13
- };
14
- class DiffMemHooks {
15
- config;
16
- fetchedMemories = [];
17
- learningBuffer = [];
18
- isConnected = false;
19
- frameManager;
20
- sessionStartTime = 0;
21
- constructor(config = {}) {
22
- this.config = { ...DEFAULT_CONFIG, ...config };
23
- }
24
- /**
25
- * Register session hooks with the event emitter
26
- */
27
- register(emitter, frameManager) {
28
- this.frameManager = frameManager;
29
- if (!this.config.enabled) {
30
- logger.debug("DiffMem hooks disabled - skipping registration");
31
- return;
32
- }
33
- emitter.registerHandler("session_start", this.onSessionStart.bind(this));
34
- emitter.registerHandler("session_end", this.onSessionEnd.bind(this));
35
- logger.info("DiffMem hooks registered", {
36
- endpoint: this.config.endpoint,
37
- autoFetchCategories: this.config.autoFetchCategories,
38
- autoLearnEnabled: this.config.autoLearnEnabled
39
- });
40
- }
41
- /**
42
- * Handle session start - fetch user knowledge
43
- */
44
- async onSessionStart(event) {
45
- const sessionEvent = event;
46
- this.sessionStartTime = Date.now();
47
- try {
48
- const status = await this.checkStatus();
49
- this.isConnected = status.connected;
50
- if (!this.isConnected) {
51
- logger.debug("DiffMem not available - skipping memory fetch");
52
- return;
53
- }
54
- const query = {
55
- categories: this.config.autoFetchCategories,
56
- limit: this.config.maxMemoriesPerSession,
57
- minConfidence: this.config.learningConfidenceThreshold
58
- };
59
- this.fetchedMemories = await this.fetchMemories(query);
60
- if (this.frameManager && this.fetchedMemories.length > 0) {
61
- await this.injectAsAnchors(sessionEvent.data.sessionId);
62
- }
63
- logger.info("DiffMem session start completed", {
64
- memoriesFetched: this.fetchedMemories.length,
65
- sessionId: sessionEvent.data.sessionId
66
- });
67
- } catch (error) {
68
- logger.warn("DiffMem session start failed", {
69
- error: error instanceof Error ? error.message : String(error)
70
- });
71
- this.isConnected = false;
72
- }
73
- }
74
- /**
75
- * Handle session end - sync buffered learnings
76
- */
77
- async onSessionEnd(event) {
78
- const sessionEvent = event;
79
- if (!this.config.autoLearnEnabled || this.learningBuffer.length === 0) {
80
- logger.debug("No learnings to sync on session end");
81
- return;
82
- }
83
- if (!this.isConnected) {
84
- const status = await this.checkStatus();
85
- if (!status.connected) {
86
- logger.warn("DiffMem not available - learnings not synced", {
87
- bufferedCount: this.learningBuffer.length
88
- });
89
- return;
90
- }
91
- this.isConnected = true;
92
- }
93
- try {
94
- await this.syncLearnings();
95
- const sessionDuration = Date.now() - this.sessionStartTime;
96
- logger.info("DiffMem session end completed", {
97
- learningSynced: this.learningBuffer.length,
98
- sessionId: sessionEvent.data.sessionId,
99
- sessionDurationMs: sessionDuration
100
- });
101
- this.learningBuffer = [];
102
- } catch (error) {
103
- logger.warn("DiffMem session end sync failed", {
104
- error: error instanceof Error ? error.message : String(error),
105
- bufferedCount: this.learningBuffer.length
106
- });
107
- }
108
- }
109
- /**
110
- * Record a learning during session
111
- */
112
- recordLearning(insight) {
113
- if (!this.config.autoLearnEnabled) {
114
- return;
115
- }
116
- if (insight.confidence < this.config.learningConfidenceThreshold) {
117
- logger.debug("Learning below confidence threshold", {
118
- confidence: insight.confidence,
119
- threshold: this.config.learningConfidenceThreshold
120
- });
121
- return;
122
- }
123
- const learning = {
124
- ...insight,
125
- timestamp: Date.now()
126
- };
127
- this.learningBuffer.push(learning);
128
- logger.debug("Learning recorded", {
129
- category: learning.category,
130
- confidence: learning.confidence,
131
- bufferSize: this.learningBuffer.length
132
- });
133
- }
134
- /**
135
- * Get fetched memories
136
- */
137
- getUserKnowledge() {
138
- return [...this.fetchedMemories];
139
- }
140
- /**
141
- * Format memories for LLM context with token budget
142
- */
143
- formatForContext(maxTokens = 2e3) {
144
- if (this.fetchedMemories.length === 0) {
145
- return "";
146
- }
147
- const sortedMemories = [...this.fetchedMemories].sort((a, b) => {
148
- if (b.confidence !== a.confidence) {
149
- return b.confidence - a.confidence;
150
- }
151
- return b.timestamp - a.timestamp;
152
- });
153
- const sections = /* @__PURE__ */ new Map();
154
- for (const memory of sortedMemories) {
155
- const category = memory.category;
156
- if (!sections.has(category)) {
157
- sections.set(category, []);
158
- }
159
- const categoryMemories = sections.get(category);
160
- if (categoryMemories) {
161
- categoryMemories.push(memory.content);
162
- }
163
- }
164
- const lines = ["## User Knowledge"];
165
- let estimatedTokens = 5;
166
- const categoryLabels = {
167
- preference: "Preferences",
168
- expertise: "Expertise",
169
- project_knowledge: "Project Knowledge",
170
- pattern: "Patterns",
171
- correction: "Corrections"
172
- };
173
- for (const [category, contents] of sections) {
174
- const label = categoryLabels[category] || category;
175
- const categoryHeader = `
176
- ### ${label}`;
177
- const headerTokens = Math.ceil(categoryHeader.length / 4);
178
- if (estimatedTokens + headerTokens > maxTokens) {
179
- break;
180
- }
181
- lines.push(categoryHeader);
182
- estimatedTokens += headerTokens;
183
- for (const content of contents) {
184
- const contentLine = `- ${content}`;
185
- const contentTokens = Math.ceil(contentLine.length / 4);
186
- if (estimatedTokens + contentTokens > maxTokens) {
187
- lines.push("- (additional items truncated for token budget)");
188
- break;
189
- }
190
- lines.push(contentLine);
191
- estimatedTokens += contentTokens;
192
- }
193
- }
194
- return lines.join("\n");
195
- }
196
- /**
197
- * Get current connection status
198
- */
199
- getStatus() {
200
- return {
201
- connected: this.isConnected,
202
- memoriesLoaded: this.fetchedMemories.length,
203
- learningsBuffered: this.learningBuffer.length
204
- };
205
- }
206
- /**
207
- * Update configuration
208
- */
209
- updateConfig(config) {
210
- this.config = { ...this.config, ...config };
211
- logger.debug("DiffMem config updated", { config: this.config });
212
- }
213
- // Private methods
214
- /**
215
- * Check DiffMem service status
216
- */
217
- async checkStatus() {
218
- try {
219
- const controller = new AbortController();
220
- const timeoutId = setTimeout(() => controller.abort(), 3e3);
221
- const response = await fetch(`${this.config.endpoint}/status`, {
222
- method: "GET",
223
- signal: controller.signal
224
- });
225
- clearTimeout(timeoutId);
226
- if (!response.ok) {
227
- return { connected: false, memoryCount: 0, lastSync: null };
228
- }
229
- const status = await response.json();
230
- return {
231
- connected: true,
232
- memoryCount: status.memoryCount || 0,
233
- lastSync: status.lastSync || null,
234
- version: status.version
235
- };
236
- } catch {
237
- return { connected: false, memoryCount: 0, lastSync: null };
238
- }
239
- }
240
- /**
241
- * Fetch memories from DiffMem
242
- */
243
- async fetchMemories(query) {
244
- try {
245
- const controller = new AbortController();
246
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
247
- const response = await fetch(`${this.config.endpoint}/memories/query`, {
248
- method: "POST",
249
- headers: { "Content-Type": "application/json" },
250
- body: JSON.stringify(query),
251
- signal: controller.signal
252
- });
253
- clearTimeout(timeoutId);
254
- if (!response.ok) {
255
- logger.warn("DiffMem query failed", { status: response.status });
256
- return [];
257
- }
258
- const data = await response.json();
259
- return data.memories || [];
260
- } catch (error) {
261
- logger.debug("DiffMem fetch failed", {
262
- error: error instanceof Error ? error.message : String(error)
263
- });
264
- return [];
265
- }
266
- }
267
- /**
268
- * Sync buffered learnings to DiffMem
269
- */
270
- async syncLearnings() {
271
- if (this.learningBuffer.length === 0) {
272
- return;
273
- }
274
- const controller = new AbortController();
275
- const timeoutId = setTimeout(() => controller.abort(), 1e4);
276
- try {
277
- const response = await fetch(`${this.config.endpoint}/memories/learn`, {
278
- method: "POST",
279
- headers: { "Content-Type": "application/json" },
280
- body: JSON.stringify({ insights: this.learningBuffer }),
281
- signal: controller.signal
282
- });
283
- clearTimeout(timeoutId);
284
- if (!response.ok) {
285
- throw new Error(`Sync failed with status ${response.status}`);
286
- }
287
- logger.info("Learnings synced to DiffMem", {
288
- count: this.learningBuffer.length
289
- });
290
- } catch (error) {
291
- clearTimeout(timeoutId);
292
- throw error;
293
- }
294
- }
295
- /**
296
- * Inject fetched memories as frame anchors
297
- */
298
- async injectAsAnchors(sessionId) {
299
- if (!this.frameManager || this.fetchedMemories.length === 0) {
300
- return;
301
- }
302
- try {
303
- const byCategory = /* @__PURE__ */ new Map();
304
- for (const memory of this.fetchedMemories) {
305
- if (!byCategory.has(memory.category)) {
306
- byCategory.set(memory.category, []);
307
- }
308
- const categoryList = byCategory.get(memory.category);
309
- if (categoryList) {
310
- categoryList.push(memory);
311
- }
312
- }
313
- for (const [category, memories] of byCategory) {
314
- const highConfidence = memories.filter((m) => m.confidence >= 0.8);
315
- for (const memory of highConfidence.slice(0, 5)) {
316
- const anchorType = this.categoryToAnchorType(category);
317
- const priority = Math.round(memory.confidence * 10);
318
- this.frameManager.addAnchor(anchorType, memory.content, priority, {
319
- source: "diffmem",
320
- category: memory.category,
321
- memoryId: memory.id,
322
- confidence: memory.confidence,
323
- sessionId
324
- });
325
- }
326
- }
327
- logger.debug("Memories injected as anchors", {
328
- totalMemories: this.fetchedMemories.length,
329
- anchorsCreated: Math.min(
330
- this.fetchedMemories.filter((m) => m.confidence >= 0.8).length,
331
- 25
332
- )
333
- });
334
- } catch (error) {
335
- logger.warn("Failed to inject memories as anchors", {
336
- error: error instanceof Error ? error.message : String(error)
337
- });
338
- }
339
- }
340
- /**
341
- * Map memory category to anchor type
342
- */
343
- categoryToAnchorType(category) {
344
- switch (category) {
345
- case "preference":
346
- return "CONSTRAINT";
347
- case "expertise":
348
- return "FACT";
349
- case "project_knowledge":
350
- return "FACT";
351
- case "pattern":
352
- return "INTERFACE_CONTRACT";
353
- case "correction":
354
- return "DECISION";
355
- default:
356
- return "FACT";
357
- }
358
- }
359
- }
360
- let instance = null;
361
- function getDiffMemHooks(config) {
362
- if (!instance) {
363
- instance = new DiffMemHooks(config);
364
- } else if (config) {
365
- instance.updateConfig(config);
366
- }
367
- return instance;
368
- }
369
- function resetDiffMemHooks() {
370
- instance = null;
371
- }
372
- export {
373
- DiffMemHooks,
374
- getDiffMemHooks,
375
- resetDiffMemHooks
376
- };
@@ -1,208 +0,0 @@
1
- import { fileURLToPath as __fileURLToPath } from 'url';
2
- import { dirname as __pathDirname } from 'path';
3
- const __filename = __fileURLToPath(import.meta.url);
4
- const __dirname = __pathDirname(__filename);
5
- import { DEFAULT_DIFFMEM_CONFIG } from "./config.js";
6
- class DiffMemClientError extends Error {
7
- constructor(message, code, statusCode) {
8
- super(message);
9
- this.code = code;
10
- this.statusCode = statusCode;
11
- this.name = "DiffMemClientError";
12
- }
13
- }
14
- class DiffMemClient {
15
- endpoint;
16
- userId;
17
- timeout;
18
- maxRetries;
19
- constructor(config = {}) {
20
- const mergedConfig = { ...DEFAULT_DIFFMEM_CONFIG, ...config };
21
- this.endpoint = mergedConfig.endpoint.replace(/\/$/, "");
22
- this.userId = mergedConfig.userId;
23
- this.timeout = mergedConfig.timeout;
24
- this.maxRetries = mergedConfig.maxRetries;
25
- }
26
- async request(path, options = {}) {
27
- const controller = new AbortController();
28
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
29
- let lastError;
30
- for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
31
- try {
32
- const response = await fetch(`${this.endpoint}${path}`, {
33
- ...options,
34
- signal: controller.signal,
35
- headers: {
36
- "Content-Type": "application/json",
37
- ...options.headers
38
- }
39
- });
40
- clearTimeout(timeoutId);
41
- if (!response.ok) {
42
- const _errorBody = await response.text().catch(() => "");
43
- throw new DiffMemClientError(
44
- `Request failed: ${response.statusText}`,
45
- "HTTP_ERROR",
46
- response.status
47
- );
48
- }
49
- return await response.json();
50
- } catch (error) {
51
- lastError = error;
52
- if (error instanceof DiffMemClientError) {
53
- throw error;
54
- }
55
- if (error.name === "AbortError") {
56
- throw new DiffMemClientError("Request timeout", "TIMEOUT");
57
- }
58
- if (attempt < this.maxRetries) {
59
- await new Promise(
60
- (resolve) => setTimeout(resolve, Math.pow(2, attempt) * 100)
61
- );
62
- continue;
63
- }
64
- }
65
- }
66
- clearTimeout(timeoutId);
67
- throw new DiffMemClientError(
68
- lastError?.message || "Request failed after retries",
69
- "NETWORK_ERROR"
70
- );
71
- }
72
- /**
73
- * Get user context/memories from DiffMem
74
- * Maps to POST /memory/{user_id}/context
75
- */
76
- async getMemories(query = {}) {
77
- try {
78
- const conversation = query.query ? [{ role: "user", content: query.query }] : [{ role: "user", content: "What do you know about me?" }];
79
- const response = await this.request(`/memory/${this.userId}/context`, {
80
- method: "POST",
81
- body: JSON.stringify({
82
- conversation,
83
- depth: "wide"
84
- })
85
- });
86
- if (response.entities) {
87
- return response.entities.slice(0, query.limit || 10).map((entity) => ({
88
- id: entity.id,
89
- content: entity.content,
90
- category: "project_knowledge",
91
- confidence: entity.score || 0.7,
92
- timestamp: Date.now()
93
- }));
94
- }
95
- return [];
96
- } catch {
97
- return [];
98
- }
99
- }
100
- /**
101
- * Store an insight/learning in DiffMem
102
- * Maps to POST /memory/{user_id}/process-and-commit
103
- */
104
- async storeInsight(insight) {
105
- const response = await this.request(`/memory/${this.userId}/process-and-commit`, {
106
- method: "POST",
107
- body: JSON.stringify({
108
- memory_input: `[${insight.category}] ${insight.content}`,
109
- session_id: `sm-${Date.now()}`,
110
- session_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
111
- })
112
- });
113
- return { id: response.session_id || `insight-${Date.now()}` };
114
- }
115
- /**
116
- * Search memories in DiffMem
117
- * Maps to POST /memory/{user_id}/search
118
- */
119
- async search(query) {
120
- try {
121
- const response = await this.request(`/memory/${this.userId}/search`, {
122
- method: "POST",
123
- body: JSON.stringify({
124
- query: query.query || "",
125
- k: query.limit || 10
126
- })
127
- });
128
- if (response.results) {
129
- return response.results.map((result) => ({
130
- id: result.snippet.id,
131
- content: result.snippet.content,
132
- category: "project_knowledge",
133
- confidence: result.score,
134
- timestamp: Date.now(),
135
- metadata: { filePath: result.snippet.file_path }
136
- }));
137
- }
138
- return [];
139
- } catch {
140
- return [];
141
- }
142
- }
143
- /**
144
- * Get DiffMem server status
145
- * Maps to GET /health
146
- */
147
- async getStatus() {
148
- try {
149
- const health = await this.request("/health", { method: "GET" });
150
- return {
151
- connected: health.status === "healthy",
152
- memoryCount: health.active_contexts || 0,
153
- lastSync: Date.now(),
154
- version: health.version
155
- };
156
- } catch {
157
- return {
158
- connected: false,
159
- memoryCount: 0,
160
- lastSync: null
161
- };
162
- }
163
- }
164
- /**
165
- * Batch sync multiple insights
166
- * Processes each insight individually since DiffMem doesn't have batch API
167
- */
168
- async batchSync(insights) {
169
- if (insights.length === 0) {
170
- return { synced: 0, failed: 0 };
171
- }
172
- let synced = 0;
173
- let failed = 0;
174
- for (const insight of insights) {
175
- try {
176
- await this.storeInsight(insight);
177
- synced++;
178
- } catch {
179
- failed++;
180
- }
181
- }
182
- return { synced, failed };
183
- }
184
- /**
185
- * Onboard a new user in DiffMem
186
- */
187
- async onboardUser(userInfo) {
188
- try {
189
- const response = await this.request(
190
- `/memory/${this.userId}/onboard`,
191
- {
192
- method: "POST",
193
- body: JSON.stringify({
194
- user_info: userInfo,
195
- session_id: `onboard-${Date.now()}`
196
- })
197
- }
198
- );
199
- return { success: response.status === "success" };
200
- } catch {
201
- return { success: false };
202
- }
203
- }
204
- }
205
- export {
206
- DiffMemClient,
207
- DiffMemClientError
208
- };
@@ -1,14 +0,0 @@
1
- import { fileURLToPath as __fileURLToPath } from 'url';
2
- import { dirname as __pathDirname } from 'path';
3
- const __filename = __fileURLToPath(import.meta.url);
4
- const __dirname = __pathDirname(__filename);
5
- const DEFAULT_DIFFMEM_CONFIG = {
6
- endpoint: process.env.DIFFMEM_ENDPOINT || "http://localhost:8000",
7
- userId: process.env.DIFFMEM_USER_ID || "default",
8
- timeout: 5e3,
9
- maxRetries: 3,
10
- enabled: process.env.DIFFMEM_ENABLED === "true" || !!process.env.DIFFMEM_ENDPOINT
11
- };
12
- export {
13
- DEFAULT_DIFFMEM_CONFIG
14
- };
@@ -1,101 +0,0 @@
1
- import { fileURLToPath as __fileURLToPath } from 'url';
2
- import { dirname as __pathDirname } from 'path';
3
- const __filename = __fileURLToPath(import.meta.url);
4
- const __dirname = __pathDirname(__filename);
5
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7
- import { DEFAULT_GREPTILE_CONFIG } from "./config.js";
8
- class GreptileClientError extends Error {
9
- constructor(message, code) {
10
- super(message);
11
- this.code = code;
12
- this.name = "GreptileClientError";
13
- }
14
- }
15
- class GreptileClient {
16
- config;
17
- client = null;
18
- transport = null;
19
- connecting = null;
20
- constructor(config = {}) {
21
- this.config = { ...DEFAULT_GREPTILE_CONFIG, ...config };
22
- if (!this.config.enabled || !this.config.apiKey) {
23
- throw new GreptileClientError(
24
- "Greptile integration disabled (GREPTILE_API_KEY not set)",
25
- "DISABLED"
26
- );
27
- }
28
- }
29
- async ensureConnected() {
30
- if (this.client) return this.client;
31
- if (this.connecting) {
32
- await this.connecting;
33
- return this.client;
34
- }
35
- this.connecting = this.connect();
36
- try {
37
- await this.connecting;
38
- return this.client;
39
- } finally {
40
- this.connecting = null;
41
- }
42
- }
43
- async connect() {
44
- const transport = new StreamableHTTPClientTransport(
45
- new URL(this.config.mcpEndpoint),
46
- {
47
- requestInit: {
48
- headers: {
49
- Authorization: `Bearer ${this.config.apiKey}`
50
- }
51
- },
52
- reconnectionOptions: {
53
- maxRetries: this.config.maxRetries,
54
- initialReconnectionDelay: 1e3,
55
- reconnectionDelayGrowFactor: 1.5,
56
- maxReconnectionDelay: 1e4
57
- }
58
- }
59
- );
60
- const client = new Client(
61
- { name: "stackmemory-greptile", version: "1.0.0" },
62
- { capabilities: {} }
63
- );
64
- transport.onclose = () => {
65
- this.client = null;
66
- this.transport = null;
67
- };
68
- await client.connect(transport);
69
- this.client = client;
70
- this.transport = transport;
71
- }
72
- async callTool(name, args = {}) {
73
- const client = await this.ensureConnected();
74
- const result = await client.callTool({ name, arguments: args });
75
- if (result.content && Array.isArray(result.content)) {
76
- const textParts = result.content.filter(
77
- (c) => c.type === "text" && typeof c.text === "string"
78
- ).map((c) => c.text);
79
- if (textParts.length === 0) return result;
80
- const combined = textParts.join("\n");
81
- try {
82
- return JSON.parse(combined);
83
- } catch {
84
- return combined;
85
- }
86
- }
87
- return result;
88
- }
89
- async disconnect() {
90
- if (this.transport) {
91
- await this.transport.close();
92
- }
93
- this.client = null;
94
- this.transport = null;
95
- this.connecting = null;
96
- }
97
- }
98
- export {
99
- GreptileClient,
100
- GreptileClientError
101
- };
@@ -1,14 +0,0 @@
1
- import { fileURLToPath as __fileURLToPath } from 'url';
2
- import { dirname as __pathDirname } from 'path';
3
- const __filename = __fileURLToPath(import.meta.url);
4
- const __dirname = __pathDirname(__filename);
5
- const DEFAULT_GREPTILE_CONFIG = {
6
- enabled: !!process.env.GREPTILE_API_KEY,
7
- mcpEndpoint: process.env.GREPTILE_MCP_ENDPOINT || "https://api.greptile.com/mcp",
8
- apiKey: process.env.GREPTILE_API_KEY || "",
9
- timeoutMs: 15e3,
10
- maxRetries: 2
11
- };
12
- export {
13
- DEFAULT_GREPTILE_CONFIG
14
- };