@stackmemoryai/stackmemory 0.5.51 → 0.5.53

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 (33) hide show
  1. package/dist/cli/commands/handoff.js +215 -59
  2. package/dist/cli/commands/handoff.js.map +2 -2
  3. package/dist/cli/index.js +8 -2
  4. package/dist/cli/index.js.map +2 -2
  5. package/dist/integrations/claude-code/lifecycle-hooks.js +3 -3
  6. package/dist/integrations/claude-code/lifecycle-hooks.js.map +1 -1
  7. package/package.json +1 -1
  8. package/scripts/auto-handoff.sh +1 -1
  9. package/scripts/claude-sm-autostart.js +174 -132
  10. package/scripts/setup-claude-integration.js +14 -10
  11. package/scripts/stackmemory-auto-handoff.sh +3 -3
  12. package/scripts/test-session-handoff.sh +2 -2
  13. package/dist/core/context/compaction-handler.js +0 -330
  14. package/dist/core/context/compaction-handler.js.map +0 -7
  15. package/dist/core/context/context-bridge.js +0 -238
  16. package/dist/core/context/context-bridge.js.map +0 -7
  17. package/scripts/testing/scripts/testing/ab-test-runner.js +0 -363
  18. package/scripts/testing/scripts/testing/collect-metrics.js +0 -292
  19. package/scripts/testing/src/core/context/context-bridge.js +0 -253
  20. package/scripts/testing/src/core/context/frame-manager.js +0 -746
  21. package/scripts/testing/src/core/context/shared-context-layer.js +0 -437
  22. package/scripts/testing/src/core/database/database-adapter.js +0 -54
  23. package/scripts/testing/src/core/errors/index.js +0 -291
  24. package/scripts/testing/src/core/errors/recovery.js +0 -268
  25. package/scripts/testing/src/core/monitoring/logger.js +0 -145
  26. package/scripts/testing/src/core/retrieval/context-retriever.js +0 -516
  27. package/scripts/testing/src/core/session/index.js +0 -1
  28. package/scripts/testing/src/core/session/session-manager.js +0 -323
  29. package/scripts/testing/src/core/trace/cli-trace-wrapper.js +0 -140
  30. package/scripts/testing/src/core/trace/db-trace-wrapper.js +0 -251
  31. package/scripts/testing/src/core/trace/debug-trace.js +0 -398
  32. package/scripts/testing/src/core/trace/index.js +0 -120
  33. package/scripts/testing/src/core/trace/linear-api-wrapper.js +0 -204
@@ -1,292 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Database } from '../../src/core/database/database.js';
3
- import { FrameManager } from '../../src/core/frame/frame-manager.js';
4
- import { SessionManager } from '../../src/core/context/session-manager.js';
5
- import { ContextRetriever } from '../../src/core/retrieval/context-retriever.js';
6
- import { performance } from 'perf_hooks';
7
- import * as fs from 'fs/promises';
8
- import * as path from 'path';
9
- export class MetricsCollector {
10
- constructor() {
11
- this.metrics = new Map();
12
- this.db = Database.getInstance();
13
- this.frameManager = FrameManager.getInstance();
14
- this.sessionManager = SessionManager.getInstance();
15
- this.retriever = new ContextRetriever();
16
- }
17
- async initialize() {
18
- await this.db.initialize();
19
- }
20
- async startSession(variant) {
21
- const sessionId = `test-${variant}-${Date.now()}`;
22
- if (variant === 'with_stackmemory') {
23
- await this.sessionManager.createSession(sessionId);
24
- }
25
- this.metrics.set(sessionId, {
26
- sessionId,
27
- variant,
28
- startTime: new Date(),
29
- contextReestablishmentTime: 0,
30
- toolCalls: 0,
31
- framesCreated: 0,
32
- framesClosedProperly: 0,
33
- decisionsAnchored: 0,
34
- errorsEncountered: 0,
35
- completionTime: 0,
36
- reworkInstances: 0,
37
- contextRetrievals: 0,
38
- contextRelevanceScores: [],
39
- memoryUsage: process.memoryUsage().heapUsed,
40
- tokenUsage: 0
41
- });
42
- return sessionId;
43
- }
44
- async measureContextReestablishment(sessionId) {
45
- const start = performance.now();
46
- const metrics = this.metrics.get(sessionId);
47
- if (!metrics)
48
- throw new Error(`Session ${sessionId} not found`);
49
- if (metrics.variant === 'with_stackmemory') {
50
- // Measure time to retrieve context
51
- const context = await this.retriever.getRelevantContext('continue previous work', 10000);
52
- const duration = performance.now() - start;
53
- metrics.contextReestablishmentTime = duration;
54
- metrics.contextRetrievals++;
55
- metrics.tokenUsage += context.totalTokens || 0;
56
- return duration;
57
- }
58
- else {
59
- // Simulate manual context reestablishment
60
- const simulatedTime = 300000; // 5 minutes
61
- metrics.contextReestablishmentTime = simulatedTime;
62
- return simulatedTime;
63
- }
64
- }
65
- trackToolCall(sessionId, toolName) {
66
- const metrics = this.metrics.get(sessionId);
67
- if (metrics) {
68
- metrics.toolCalls++;
69
- }
70
- }
71
- trackFrameCreation(sessionId, frameId) {
72
- const metrics = this.metrics.get(sessionId);
73
- if (metrics) {
74
- metrics.framesCreated++;
75
- }
76
- }
77
- trackFrameClosure(sessionId, frameId, properClosure) {
78
- const metrics = this.metrics.get(sessionId);
79
- if (metrics && properClosure) {
80
- metrics.framesClosedProperly++;
81
- }
82
- }
83
- trackDecision(sessionId, decision) {
84
- const metrics = this.metrics.get(sessionId);
85
- if (metrics) {
86
- metrics.decisionsAnchored++;
87
- }
88
- }
89
- trackError(sessionId, error) {
90
- const metrics = this.metrics.get(sessionId);
91
- if (metrics) {
92
- metrics.errorsEncountered++;
93
- }
94
- }
95
- trackRework(sessionId) {
96
- const metrics = this.metrics.get(sessionId);
97
- if (metrics) {
98
- metrics.reworkInstances++;
99
- }
100
- }
101
- async scoreContextRelevance(sessionId, query, retrievedContext) {
102
- // In real implementation, this would use LLM to score relevance
103
- const score = Math.random() * 0.3 + 0.7; // Mock: 0.7-1.0 range
104
- const metrics = this.metrics.get(sessionId);
105
- if (metrics) {
106
- metrics.contextRelevanceScores.push(score);
107
- }
108
- return score;
109
- }
110
- async endSession(sessionId) {
111
- const metrics = this.metrics.get(sessionId);
112
- if (!metrics)
113
- throw new Error(`Session ${sessionId} not found`);
114
- metrics.endTime = new Date();
115
- metrics.completionTime = metrics.endTime.getTime() - metrics.startTime.getTime();
116
- metrics.memoryUsage = process.memoryUsage().heapUsed - metrics.memoryUsage;
117
- return metrics;
118
- }
119
- async collectSessionMetrics(sessionId) {
120
- const metrics = this.metrics.get(sessionId);
121
- if (!metrics)
122
- throw new Error(`Session ${sessionId} not found`);
123
- // Collect additional metrics from database
124
- if (metrics.variant === 'with_stackmemory') {
125
- const frames = await this.db.query('SELECT * FROM frames WHERE session_id = ?', [sessionId]);
126
- const events = await this.db.query('SELECT * FROM events WHERE session_id = ?', [sessionId]);
127
- const anchors = await this.db.query('SELECT * FROM anchors WHERE session_id = ?', [sessionId]);
128
- metrics.framesCreated = frames.length;
129
- metrics.framesClosedProperly = frames.filter((f) => f.state === 'closed').length;
130
- metrics.decisionsAnchored = anchors.length;
131
- metrics.toolCalls = events.filter((e) => e.type === 'tool_call').length;
132
- metrics.errorsEncountered = events.filter((e) => e.type === 'error').length;
133
- }
134
- return metrics;
135
- }
136
- async compareVariants(withStackMemory, withoutStackMemory) {
137
- // Calculate improvements
138
- const avgWith = this.calculateAverages(withStackMemory);
139
- const avgWithout = this.calculateAverages(withoutStackMemory);
140
- const contextSpeedImprovement = ((avgWithout.contextReestablishmentTime - avgWith.contextReestablishmentTime) /
141
- avgWithout.contextReestablishmentTime) * 100;
142
- const taskCompletionImprovement = ((avgWithout.completionTime - avgWith.completionTime) /
143
- avgWithout.completionTime) * 100;
144
- const errorRecoveryImprovement = ((avgWithout.errorsEncountered - avgWith.errorsEncountered) /
145
- Math.max(avgWithout.errorsEncountered, 1)) * 100;
146
- const consistencyImprovement = ((avgWith.decisionsAnchored - avgWithout.decisionsAnchored) /
147
- Math.max(avgWithout.decisionsAnchored, 1)) * 100;
148
- // Calculate statistical significance (simplified)
149
- const pValue = this.calculatePValue(withStackMemory, withoutStackMemory);
150
- const confidence = (1 - pValue) * 100;
151
- return {
152
- improvement: {
153
- contextSpeed: contextSpeedImprovement,
154
- taskCompletion: taskCompletionImprovement,
155
- errorRecovery: errorRecoveryImprovement,
156
- consistency: consistencyImprovement
157
- },
158
- statistics: {
159
- sampleSize: withStackMemory.length + withoutStackMemory.length,
160
- confidence,
161
- pValue
162
- },
163
- recommendations: this.generateRecommendations({
164
- contextSpeedImprovement,
165
- taskCompletionImprovement,
166
- errorRecoveryImprovement,
167
- consistencyImprovement
168
- })
169
- };
170
- }
171
- calculateAverages(metrics) {
172
- const sum = metrics.reduce((acc, m) => ({
173
- contextReestablishmentTime: acc.contextReestablishmentTime + m.contextReestablishmentTime,
174
- completionTime: acc.completionTime + m.completionTime,
175
- errorsEncountered: acc.errorsEncountered + m.errorsEncountered,
176
- decisionsAnchored: acc.decisionsAnchored + m.decisionsAnchored,
177
- reworkInstances: acc.reworkInstances + m.reworkInstances
178
- }), {
179
- contextReestablishmentTime: 0,
180
- completionTime: 0,
181
- errorsEncountered: 0,
182
- decisionsAnchored: 0,
183
- reworkInstances: 0
184
- });
185
- return {
186
- contextReestablishmentTime: sum.contextReestablishmentTime / metrics.length,
187
- completionTime: sum.completionTime / metrics.length,
188
- errorsEncountered: sum.errorsEncountered / metrics.length,
189
- decisionsAnchored: sum.decisionsAnchored / metrics.length,
190
- reworkInstances: sum.reworkInstances / metrics.length
191
- };
192
- }
193
- calculatePValue(group1, group2) {
194
- // Simplified t-test calculation
195
- // In production, use proper statistical library
196
- return 0.02; // Mock significant result
197
- }
198
- generateRecommendations(improvements) {
199
- const recommendations = [];
200
- if (improvements.contextSpeedImprovement > 50) {
201
- recommendations.push('StackMemory significantly reduces context reestablishment time');
202
- }
203
- if (improvements.taskCompletionImprovement > 30) {
204
- recommendations.push('Tasks complete faster with StackMemory enabled');
205
- }
206
- if (improvements.errorRecoveryImprovement > 20) {
207
- recommendations.push('Error recovery is more efficient with saved context');
208
- }
209
- if (improvements.consistencyImprovement > 40) {
210
- recommendations.push('Decision consistency greatly improved with anchored context');
211
- }
212
- return recommendations;
213
- }
214
- async saveMetrics(sessionId, outputDir = './test-results') {
215
- const metrics = this.metrics.get(sessionId);
216
- if (!metrics)
217
- return;
218
- await fs.mkdir(outputDir, { recursive: true });
219
- const filename = path.join(outputDir, `${sessionId}.json`);
220
- await fs.writeFile(filename, JSON.stringify(metrics, null, 2));
221
- }
222
- async generateReport(outputPath = './test-results/report.md') {
223
- const withStackMemory = Array.from(this.metrics.values())
224
- .filter(m => m.variant === 'with_stackmemory');
225
- const withoutStackMemory = Array.from(this.metrics.values())
226
- .filter(m => m.variant === 'without_stackmemory');
227
- const comparison = await this.compareVariants(withStackMemory, withoutStackMemory);
228
- const report = `# StackMemory Effectiveness Report
229
-
230
- ## Executive Summary
231
- - Sample Size: ${comparison.statistics.sampleSize} sessions
232
- - Statistical Confidence: ${comparison.statistics.confidence.toFixed(1)}%
233
- - P-Value: ${comparison.statistics.pValue}
234
-
235
- ## Performance Improvements
236
- - Context Reestablishment: ${comparison.improvement.contextSpeed.toFixed(1)}% faster
237
- - Task Completion: ${comparison.improvement.taskCompletion.toFixed(1)}% faster
238
- - Error Recovery: ${comparison.improvement.errorRecovery.toFixed(1)}% better
239
- - Decision Consistency: ${comparison.improvement.consistency.toFixed(1)}% improved
240
-
241
- ## Recommendations
242
- ${comparison.recommendations.map(r => `- ${r}`).join('\n')}
243
-
244
- ## Detailed Metrics
245
-
246
- ### With StackMemory
247
- ${this.formatMetricsTable(withStackMemory)}
248
-
249
- ### Without StackMemory
250
- ${this.formatMetricsTable(withoutStackMemory)}
251
-
252
- Generated: ${new Date().toISOString()}
253
- `;
254
- await fs.writeFile(outputPath, report);
255
- console.log(`Report generated: ${outputPath}`);
256
- }
257
- formatMetricsTable(metrics) {
258
- if (metrics.length === 0)
259
- return 'No data available';
260
- const avg = this.calculateAverages(metrics);
261
- return `
262
- | Metric | Average |
263
- |--------|---------|
264
- | Context Reestablishment | ${(avg.contextReestablishmentTime / 1000).toFixed(2)}s |
265
- | Task Completion | ${(avg.completionTime / 1000 / 60).toFixed(2)} min |
266
- | Errors Encountered | ${avg.errorsEncountered.toFixed(1)} |
267
- | Decisions Anchored | ${avg.decisionsAnchored.toFixed(1)} |
268
- | Rework Instances | ${avg.reworkInstances.toFixed(1)} |
269
- `;
270
- }
271
- }
272
- // CLI interface
273
- if (import.meta.url === `file://${process.argv[1]}`) {
274
- const collector = new MetricsCollector();
275
- const command = process.argv[2];
276
- async function main() {
277
- await collector.initialize();
278
- switch (command) {
279
- case 'start':
280
- const variant = process.argv[3];
281
- const sessionId = await collector.startSession(variant);
282
- console.log(`Session started: ${sessionId}`);
283
- break;
284
- case 'report':
285
- await collector.generateReport();
286
- break;
287
- default:
288
- console.log('Usage: collect-metrics.ts [start|report] [with_stackmemory|without_stackmemory]');
289
- }
290
- }
291
- main().catch(console.error);
292
- }
@@ -1,253 +0,0 @@
1
- /**
2
- * Context Bridge - Automatic synchronization between sessions and shared context
3
- *
4
- * This bridge automatically:
5
- * - Syncs important frames to shared context
6
- * - Loads relevant context on session start
7
- * - Maintains consistency across sessions
8
- */
9
- import { sharedContextLayer } from './shared-context-layer.js';
10
- import { sessionManager } from '../session/session-manager.js';
11
- import { logger } from '../monitoring/logger.js';
12
- export class ContextBridge {
13
- constructor() {
14
- this.frameManager = null;
15
- this.syncTimer = null;
16
- this.lastSyncTime = 0;
17
- this.options = {
18
- autoSync: true,
19
- syncInterval: 60000, // 1 minute
20
- minFrameScore: 0.5, // Include frames with score above 0.5
21
- importantTags: ['decision', 'error', 'milestone', 'learning'],
22
- };
23
- }
24
- static getInstance() {
25
- if (!ContextBridge.instance) {
26
- ContextBridge.instance = new ContextBridge();
27
- }
28
- return ContextBridge.instance;
29
- }
30
- /**
31
- * Initialize the bridge with a frame manager
32
- */
33
- async initialize(frameManager, options) {
34
- this.frameManager = frameManager;
35
- this.options = { ...this.options, ...options };
36
- // Load shared context on initialization
37
- await this.loadSharedContext();
38
- // Start auto-sync if enabled
39
- if (this.options.autoSync) {
40
- this.startAutoSync();
41
- }
42
- logger.info('Context bridge initialized', {
43
- autoSync: this.options.autoSync,
44
- syncInterval: this.options.syncInterval,
45
- });
46
- }
47
- /**
48
- * Load relevant shared context into current session
49
- */
50
- async loadSharedContext() {
51
- try {
52
- const session = sessionManager.getCurrentSession();
53
- if (!session)
54
- return;
55
- // Get context discovery
56
- const discovery = await sharedContextLayer.autoDiscoverContext();
57
- if (!discovery.hasSharedContext) {
58
- logger.info('No shared context available to load');
59
- return;
60
- }
61
- // Load recent patterns as metadata
62
- if (discovery.recentPatterns.length > 0) {
63
- logger.info('Loaded recent patterns from shared context', {
64
- patternCount: discovery.recentPatterns.length,
65
- });
66
- }
67
- // Load last decisions for reference
68
- if (discovery.lastDecisions.length > 0) {
69
- logger.info('Loaded recent decisions from shared context', {
70
- decisionCount: discovery.lastDecisions.length,
71
- });
72
- }
73
- // Store suggested frames in metadata for quick reference
74
- if (discovery.suggestedFrames.length > 0) {
75
- const metadata = {
76
- suggestedFrames: discovery.suggestedFrames,
77
- loadedAt: Date.now(),
78
- };
79
- // Store in frame manager's context
80
- if (this.frameManager) {
81
- await this.frameManager.addContext('shared-context-suggestions', metadata);
82
- }
83
- logger.info('Loaded suggested frames from shared context', {
84
- frameCount: discovery.suggestedFrames.length,
85
- });
86
- }
87
- }
88
- catch (error) {
89
- logger.error('Failed to load shared context', error);
90
- }
91
- }
92
- /**
93
- * Sync current session's important frames to shared context
94
- */
95
- async syncToSharedContext() {
96
- try {
97
- if (!this.frameManager)
98
- return;
99
- const session = sessionManager.getCurrentSession();
100
- if (!session)
101
- return;
102
- // Get all active frames (filter out any nulls from missing frames)
103
- const activeFrames = this.frameManager.getActiveFramePath().filter(Boolean);
104
- // Get recent closed frames (last 100)
105
- const recentFrames = await this.frameManager.getRecentFrames(100);
106
- // Combine and filter important frames
107
- const allFrames = [...activeFrames, ...recentFrames].filter(Boolean);
108
- const importantFrames = this.filterImportantFrames(allFrames);
109
- if (importantFrames.length === 0) {
110
- logger.debug('No important frames to sync');
111
- return;
112
- }
113
- // Add to shared context
114
- await sharedContextLayer.addToSharedContext(importantFrames, {
115
- minScore: this.options.minFrameScore,
116
- tags: this.options.importantTags,
117
- });
118
- this.lastSyncTime = Date.now();
119
- logger.info('Synced frames to shared context', {
120
- frameCount: importantFrames.length,
121
- sessionId: session.sessionId,
122
- });
123
- }
124
- catch (error) {
125
- logger.error('Failed to sync to shared context', error);
126
- }
127
- }
128
- /**
129
- * Query shared context for relevant frames
130
- */
131
- async querySharedFrames(query) {
132
- try {
133
- const results = await sharedContextLayer.querySharedContext({
134
- ...query,
135
- minScore: this.options.minFrameScore,
136
- });
137
- logger.info('Queried shared context', {
138
- query,
139
- resultCount: results.length,
140
- });
141
- return results;
142
- }
143
- catch (error) {
144
- logger.error('Failed to query shared context', error);
145
- return [];
146
- }
147
- }
148
- /**
149
- * Add a decision to shared context
150
- */
151
- async addDecision(decision, reasoning) {
152
- try {
153
- await sharedContextLayer.addDecision({
154
- decision,
155
- reasoning,
156
- outcome: 'pending',
157
- });
158
- logger.info('Added decision to shared context', { decision });
159
- }
160
- catch (error) {
161
- logger.error('Failed to add decision', error);
162
- }
163
- }
164
- /**
165
- * Start automatic synchronization
166
- */
167
- startAutoSync() {
168
- if (this.syncTimer) {
169
- clearInterval(this.syncTimer);
170
- }
171
- this.syncTimer = setInterval(() => {
172
- this.syncToSharedContext().catch((error) => {
173
- logger.error('Auto-sync failed', error);
174
- });
175
- }, this.options.syncInterval);
176
- // Also sync on important events
177
- this.setupEventListeners();
178
- }
179
- /**
180
- * Stop automatic synchronization
181
- */
182
- stopAutoSync() {
183
- if (this.syncTimer) {
184
- clearInterval(this.syncTimer);
185
- this.syncTimer = null;
186
- }
187
- }
188
- /**
189
- * Filter frames that are important enough to share
190
- */
191
- filterImportantFrames(frames) {
192
- return frames.filter((frame) => {
193
- // Check if frame has important tags
194
- const hasImportantTag = this.options.importantTags.some((tag) => frame.metadata?.tags?.includes(tag));
195
- // Check frame type importance
196
- const isImportantType = [
197
- 'task',
198
- 'milestone',
199
- 'error',
200
- 'resolution',
201
- 'decision',
202
- ].includes(frame.type);
203
- // Check metadata importance flag
204
- const markedImportant = frame.metadata?.importance === 'high';
205
- return hasImportantTag || isImportantType || markedImportant;
206
- });
207
- }
208
- /**
209
- * Setup event listeners for automatic syncing
210
- */
211
- setupEventListeners() {
212
- if (!this.frameManager)
213
- return;
214
- // Sync when a frame is closed
215
- const originalClose = this.frameManager.closeFrame.bind(this.frameManager);
216
- this.frameManager.closeFrame = async (frameId, metadata) => {
217
- const result = await originalClose(frameId, metadata);
218
- // Sync if it was an important frame
219
- const frame = await this.frameManager.getFrame(frameId);
220
- if (frame && this.filterImportantFrames([frame]).length > 0) {
221
- await this.syncToSharedContext();
222
- }
223
- return result;
224
- };
225
- // Sync when a milestone is reached
226
- const originalMilestone = this.frameManager.createFrame.bind(this.frameManager);
227
- this.frameManager.createFrame = async (params) => {
228
- const result = await originalMilestone(params);
229
- if (params.type === 'milestone') {
230
- await this.syncToSharedContext();
231
- }
232
- return result;
233
- };
234
- }
235
- /**
236
- * Get sync statistics
237
- */
238
- getSyncStats() {
239
- return {
240
- lastSyncTime: this.lastSyncTime,
241
- autoSyncEnabled: this.options.autoSync,
242
- syncInterval: this.options.syncInterval,
243
- };
244
- }
245
- /**
246
- * Manual trigger for immediate sync
247
- */
248
- async forceSyncNow() {
249
- logger.info('Force sync triggered');
250
- await this.syncToSharedContext();
251
- }
252
- }
253
- export const contextBridge = ContextBridge.getInstance();