@stackmemoryai/stackmemory 0.5.0 → 0.5.1
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/streamlined-cli.js +144 -0
- package/dist/cli/streamlined-cli.js.map +7 -0
- package/dist/core/events/event-bus.js +110 -0
- package/dist/core/events/event-bus.js.map +7 -0
- package/dist/core/plugins/plugin-interface.js +87 -0
- package/dist/core/plugins/plugin-interface.js.map +7 -0
- package/dist/core/storage/simplified-storage.js +328 -0
- package/dist/core/storage/simplified-storage.js.map +7 -0
- package/dist/plugins/linear/index.js +166 -0
- package/dist/plugins/linear/index.js.map +7 -0
- package/dist/plugins/loader.js +57 -0
- package/dist/plugins/loader.js.map +7 -0
- package/dist/plugins/plugin-interface.js +67 -0
- package/dist/plugins/plugin-interface.js.map +7 -0
- package/dist/plugins/ralph/simple-ralph-plugin.js +305 -0
- package/dist/plugins/ralph/simple-ralph-plugin.js.map +7 -0
- package/dist/plugins/ralph/use-cases/code-generator.js +151 -0
- package/dist/plugins/ralph/use-cases/code-generator.js.map +7 -0
- package/dist/plugins/ralph/use-cases/test-generator.js +201 -0
- package/dist/plugins/ralph/use-cases/test-generator.js.map +7 -0
- package/package.json +1 -8
- package/scripts/testing/results/real-performance-results.json +0 -90
- package/scripts/testing/test-tier-migration.js +0 -100
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { SimpleRalphPlugin } from "../simple-ralph-plugin.js";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
class TestGenerator {
|
|
6
|
+
ralphPlugin;
|
|
7
|
+
constructor(ralphPlugin) {
|
|
8
|
+
this.ralphPlugin = ralphPlugin;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Generate tests iteratively until coverage target is met
|
|
12
|
+
*/
|
|
13
|
+
async generateTests(request) {
|
|
14
|
+
const coverageTarget = request.coverageTarget || 80;
|
|
15
|
+
const task = {
|
|
16
|
+
id: `testgen-${Date.now()}`,
|
|
17
|
+
description: `Generate ${request.framework} tests for ${request.targetFile}`,
|
|
18
|
+
acceptanceCriteria: [
|
|
19
|
+
"All tests pass",
|
|
20
|
+
`Test coverage >= ${coverageTarget}%`,
|
|
21
|
+
"No test duplication",
|
|
22
|
+
"Tests are maintainable and clear"
|
|
23
|
+
],
|
|
24
|
+
maxIterations: 7
|
|
25
|
+
};
|
|
26
|
+
if (request.includeEdgeCases) {
|
|
27
|
+
task.acceptanceCriteria.push("Edge cases covered");
|
|
28
|
+
task.acceptanceCriteria.push("Error conditions tested");
|
|
29
|
+
}
|
|
30
|
+
if (request.includeIntegrationTests) {
|
|
31
|
+
task.acceptanceCriteria.push("Integration tests included");
|
|
32
|
+
}
|
|
33
|
+
const result = await this.ralphPlugin.runTask(task);
|
|
34
|
+
const analysis = await this.analyzeGeneratedTests(request.targetFile, request.framework);
|
|
35
|
+
return {
|
|
36
|
+
testFiles: analysis.files,
|
|
37
|
+
coverage: analysis.coverage,
|
|
38
|
+
testCount: analysis.testCount,
|
|
39
|
+
passRate: analysis.passRate,
|
|
40
|
+
iterations: result.iterations
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Analyze generated tests
|
|
45
|
+
*/
|
|
46
|
+
async analyzeGeneratedTests(targetFile, framework) {
|
|
47
|
+
const analysis = {
|
|
48
|
+
files: [],
|
|
49
|
+
coverage: 0,
|
|
50
|
+
testCount: 0,
|
|
51
|
+
passRate: 0
|
|
52
|
+
};
|
|
53
|
+
try {
|
|
54
|
+
const testDir = path.dirname(targetFile);
|
|
55
|
+
const baseName = path.basename(targetFile, path.extname(targetFile));
|
|
56
|
+
const testPatterns = [
|
|
57
|
+
`${baseName}.test.*`,
|
|
58
|
+
`${baseName}.spec.*`,
|
|
59
|
+
`test_${baseName}.*`,
|
|
60
|
+
`${baseName}_test.*`
|
|
61
|
+
];
|
|
62
|
+
for (const pattern of testPatterns) {
|
|
63
|
+
const files = await this.findFiles(testDir, pattern);
|
|
64
|
+
analysis.files.push(...files);
|
|
65
|
+
}
|
|
66
|
+
analysis.coverage = await this.runCoverageAnalysis(framework);
|
|
67
|
+
const testResults = await this.runTests(framework);
|
|
68
|
+
analysis.testCount = testResults.total;
|
|
69
|
+
analysis.passRate = testResults.passed / testResults.total * 100;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error("Error analyzing tests:", error);
|
|
72
|
+
}
|
|
73
|
+
return analysis;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Find files matching pattern
|
|
77
|
+
*/
|
|
78
|
+
async findFiles(dir, pattern) {
|
|
79
|
+
const files = [];
|
|
80
|
+
try {
|
|
81
|
+
const entries = await fs.readdir(dir);
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
if (this.matchesPattern(entry, pattern)) {
|
|
84
|
+
files.push(path.join(dir, entry));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error("Error finding files:", error);
|
|
89
|
+
}
|
|
90
|
+
return files;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Simple pattern matching
|
|
94
|
+
*/
|
|
95
|
+
matchesPattern(filename, pattern) {
|
|
96
|
+
const regex = new RegExp(pattern.replace(/\*/g, ".*"));
|
|
97
|
+
return regex.test(filename);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Run coverage analysis
|
|
101
|
+
*/
|
|
102
|
+
async runCoverageAnalysis(framework) {
|
|
103
|
+
try {
|
|
104
|
+
let command = "";
|
|
105
|
+
switch (framework) {
|
|
106
|
+
case "jest":
|
|
107
|
+
command = "npx jest --coverage --silent";
|
|
108
|
+
break;
|
|
109
|
+
case "vitest":
|
|
110
|
+
command = "npx vitest run --coverage --silent";
|
|
111
|
+
break;
|
|
112
|
+
case "mocha":
|
|
113
|
+
command = "npx nyc mocha";
|
|
114
|
+
break;
|
|
115
|
+
case "pytest":
|
|
116
|
+
command = "pytest --cov --cov-report=json";
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
if (command) {
|
|
120
|
+
const output = execSync(command, { encoding: "utf-8", stdio: "pipe" });
|
|
121
|
+
const match = output.match(/(\d+(?:\.\d+)?)\s*%/);
|
|
122
|
+
if (match) {
|
|
123
|
+
return parseFloat(match[1]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error("Coverage analysis failed:", error);
|
|
128
|
+
}
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Run tests and get results
|
|
133
|
+
*/
|
|
134
|
+
async runTests(framework) {
|
|
135
|
+
try {
|
|
136
|
+
let command = "";
|
|
137
|
+
switch (framework) {
|
|
138
|
+
case "jest":
|
|
139
|
+
command = "npx jest --json";
|
|
140
|
+
break;
|
|
141
|
+
case "vitest":
|
|
142
|
+
command = "npx vitest run --reporter=json";
|
|
143
|
+
break;
|
|
144
|
+
case "mocha":
|
|
145
|
+
command = "npx mocha --reporter json";
|
|
146
|
+
break;
|
|
147
|
+
case "pytest":
|
|
148
|
+
command = "pytest --json-report";
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
if (command) {
|
|
152
|
+
const output = execSync(command, { encoding: "utf-8", stdio: "pipe" });
|
|
153
|
+
try {
|
|
154
|
+
const json = JSON.parse(output);
|
|
155
|
+
return {
|
|
156
|
+
total: json.numTotalTests || json.tests || 0,
|
|
157
|
+
passed: json.numPassedTests || json.passes || 0
|
|
158
|
+
};
|
|
159
|
+
} catch {
|
|
160
|
+
const totalMatch = output.match(/(\d+)\s+tests?/i);
|
|
161
|
+
const passedMatch = output.match(/(\d+)\s+pass/i);
|
|
162
|
+
return {
|
|
163
|
+
total: totalMatch ? parseInt(totalMatch[1]) : 0,
|
|
164
|
+
passed: passedMatch ? parseInt(passedMatch[1]) : 0
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error("Test execution failed:", error);
|
|
170
|
+
}
|
|
171
|
+
return { total: 0, passed: 0 };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function generateTestsForModule() {
|
|
175
|
+
const ralphPlugin = new SimpleRalphPlugin();
|
|
176
|
+
await ralphPlugin.initialize({
|
|
177
|
+
eventBus: {},
|
|
178
|
+
config: { name: "simple-ralph", version: "2.0.0", enabled: true },
|
|
179
|
+
dataDir: ".ralph",
|
|
180
|
+
getRepository: () => null,
|
|
181
|
+
registerRepository: () => {
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
const generator = new TestGenerator(ralphPlugin);
|
|
185
|
+
const result = await generator.generateTests({
|
|
186
|
+
targetFile: "./src/utils/validator.ts",
|
|
187
|
+
framework: "jest",
|
|
188
|
+
coverageTarget: 90,
|
|
189
|
+
includeEdgeCases: true,
|
|
190
|
+
includeIntegrationTests: false,
|
|
191
|
+
mockStrategy: "partial"
|
|
192
|
+
});
|
|
193
|
+
console.log(`Generated ${result.testCount} tests with ${result.coverage}% coverage`);
|
|
194
|
+
console.log(`Pass rate: ${result.passRate}%`);
|
|
195
|
+
console.log(`Completed in ${result.iterations} iterations`);
|
|
196
|
+
}
|
|
197
|
+
export {
|
|
198
|
+
TestGenerator,
|
|
199
|
+
generateTestsForModule
|
|
200
|
+
};
|
|
201
|
+
//# sourceMappingURL=test-generator.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/plugins/ralph/use-cases/test-generator.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Test Generator - Iteratively generate comprehensive test suites\n * Focuses on achieving high coverage and catching edge cases\n */\n\nimport { SimpleRalphPlugin, SimpleTask } from '../simple-ralph-plugin.js';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport { execSync } from 'child_process';\n\nexport interface TestGenRequest {\n targetFile: string;\n framework: 'jest' | 'mocha' | 'vitest' | 'pytest';\n coverageTarget?: number;\n includeEdgeCases?: boolean;\n includeIntegrationTests?: boolean;\n mockStrategy?: 'full' | 'partial' | 'none';\n}\n\nexport interface GeneratedTests {\n testFiles: string[];\n coverage: number;\n testCount: number;\n passRate: number;\n iterations: number;\n}\n\nexport class TestGenerator {\n private ralphPlugin: SimpleRalphPlugin;\n\n constructor(ralphPlugin: SimpleRalphPlugin) {\n this.ralphPlugin = ralphPlugin;\n }\n\n /**\n * Generate tests iteratively until coverage target is met\n */\n async generateTests(request: TestGenRequest): Promise<GeneratedTests> {\n const coverageTarget = request.coverageTarget || 80;\n\n const task: SimpleTask = {\n id: `testgen-${Date.now()}`,\n description: `Generate ${request.framework} tests for ${request.targetFile}`,\n acceptanceCriteria: [\n 'All tests pass',\n `Test coverage >= ${coverageTarget}%`,\n 'No test duplication',\n 'Tests are maintainable and clear'\n ],\n maxIterations: 7\n };\n\n // Add edge case criteria if requested\n if (request.includeEdgeCases) {\n task.acceptanceCriteria.push('Edge cases covered');\n task.acceptanceCriteria.push('Error conditions tested');\n }\n\n if (request.includeIntegrationTests) {\n task.acceptanceCriteria.push('Integration tests included');\n }\n\n // Run iterative test generation\n const result = await this.ralphPlugin.runTask(task);\n\n // Analyze generated tests\n const analysis = await this.analyzeGeneratedTests(request.targetFile, request.framework);\n\n return {\n testFiles: analysis.files,\n coverage: analysis.coverage,\n testCount: analysis.testCount,\n passRate: analysis.passRate,\n iterations: result.iterations\n };\n }\n\n /**\n * Analyze generated tests\n */\n private async analyzeGeneratedTests(\n targetFile: string, \n framework: string\n ): Promise<{\n files: string[];\n coverage: number;\n testCount: number;\n passRate: number;\n }> {\n const analysis = {\n files: [] as string[],\n coverage: 0,\n testCount: 0,\n passRate: 0\n };\n\n try {\n // Find test files\n const testDir = path.dirname(targetFile);\n const baseName = path.basename(targetFile, path.extname(targetFile));\n \n const testPatterns = [\n `${baseName}.test.*`,\n `${baseName}.spec.*`,\n `test_${baseName}.*`,\n `${baseName}_test.*`\n ];\n\n for (const pattern of testPatterns) {\n const files = await this.findFiles(testDir, pattern);\n analysis.files.push(...files);\n }\n\n // Run coverage analysis\n analysis.coverage = await this.runCoverageAnalysis(framework);\n \n // Count tests and check pass rate\n const testResults = await this.runTests(framework);\n analysis.testCount = testResults.total;\n analysis.passRate = testResults.passed / testResults.total * 100;\n\n } catch (error) {\n console.error('Error analyzing tests:', error);\n }\n\n return analysis;\n }\n\n /**\n * Find files matching pattern\n */\n private async findFiles(dir: string, pattern: string): Promise<string[]> {\n const files: string[] = [];\n try {\n const entries = await fs.readdir(dir);\n for (const entry of entries) {\n if (this.matchesPattern(entry, pattern)) {\n files.push(path.join(dir, entry));\n }\n }\n } catch (error) {\n console.error('Error finding files:', error);\n }\n return files;\n }\n\n /**\n * Simple pattern matching\n */\n private matchesPattern(filename: string, pattern: string): boolean {\n const regex = new RegExp(pattern.replace(/\\*/g, '.*'));\n return regex.test(filename);\n }\n\n /**\n * Run coverage analysis\n */\n private async runCoverageAnalysis(framework: string): Promise<number> {\n try {\n let command = '';\n switch (framework) {\n case 'jest':\n command = 'npx jest --coverage --silent';\n break;\n case 'vitest':\n command = 'npx vitest run --coverage --silent';\n break;\n case 'mocha':\n command = 'npx nyc mocha';\n break;\n case 'pytest':\n command = 'pytest --cov --cov-report=json';\n break;\n }\n\n if (command) {\n const output = execSync(command, { encoding: 'utf-8', stdio: 'pipe' });\n \n // Parse coverage from output (simplified)\n const match = output.match(/(\\d+(?:\\.\\d+)?)\\s*%/);\n if (match) {\n return parseFloat(match[1]);\n }\n }\n } catch (error) {\n console.error('Coverage analysis failed:', error);\n }\n \n return 0;\n }\n\n /**\n * Run tests and get results\n */\n private async runTests(framework: string): Promise<{ total: number; passed: number }> {\n try {\n let command = '';\n switch (framework) {\n case 'jest':\n command = 'npx jest --json';\n break;\n case 'vitest':\n command = 'npx vitest run --reporter=json';\n break;\n case 'mocha':\n command = 'npx mocha --reporter json';\n break;\n case 'pytest':\n command = 'pytest --json-report';\n break;\n }\n\n if (command) {\n const output = execSync(command, { encoding: 'utf-8', stdio: 'pipe' });\n \n try {\n const json = JSON.parse(output);\n // Parse based on framework (simplified)\n return {\n total: json.numTotalTests || json.tests || 0,\n passed: json.numPassedTests || json.passes || 0\n };\n } catch {\n // Fallback to regex parsing\n const totalMatch = output.match(/(\\d+)\\s+tests?/i);\n const passedMatch = output.match(/(\\d+)\\s+pass/i);\n \n return {\n total: totalMatch ? parseInt(totalMatch[1]) : 0,\n passed: passedMatch ? parseInt(passedMatch[1]) : 0\n };\n }\n }\n } catch (error) {\n console.error('Test execution failed:', error);\n }\n \n return { total: 0, passed: 0 };\n }\n}\n\n// Example: Generate tests for a TypeScript module\nexport async function generateTestsForModule(): Promise<void> {\n const ralphPlugin = new SimpleRalphPlugin();\n await ralphPlugin.initialize({\n eventBus: {} as any,\n config: { name: 'simple-ralph', version: '2.0.0', enabled: true },\n dataDir: '.ralph',\n getRepository: () => null as any,\n registerRepository: () => {}\n });\n\n const generator = new TestGenerator(ralphPlugin);\n \n const result = await generator.generateTests({\n targetFile: './src/utils/validator.ts',\n framework: 'jest',\n coverageTarget: 90,\n includeEdgeCases: true,\n includeIntegrationTests: false,\n mockStrategy: 'partial'\n });\n\n console.log(`Generated ${result.testCount} tests with ${result.coverage}% coverage`);\n console.log(`Pass rate: ${result.passRate}%`);\n console.log(`Completed in ${result.iterations} iterations`);\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackmemoryai/stackmemory",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Lossless memory runtime for AI coding tools - organizes context as a call stack instead of linear chat logs, with team collaboration and infinite retention",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|
|
@@ -86,11 +86,9 @@
|
|
|
86
86
|
"@linear/sdk": "^68.1.0",
|
|
87
87
|
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
88
88
|
"@stackmemoryai/stackmemory": "^0.3.19",
|
|
89
|
-
"@types/bcryptjs": "^2.4.6",
|
|
90
89
|
"@types/blessed": "^0.1.27",
|
|
91
90
|
"@types/inquirer": "^9.0.9",
|
|
92
91
|
"@types/pg": "^8.16.0",
|
|
93
|
-
"bcryptjs": "^3.0.3",
|
|
94
92
|
"better-sqlite3": "^9.2.2",
|
|
95
93
|
"chalk": "^5.3.0",
|
|
96
94
|
"chromadb": "^3.2.2",
|
|
@@ -105,17 +103,12 @@
|
|
|
105
103
|
"helmet": "^8.1.0",
|
|
106
104
|
"ignore": "^7.0.5",
|
|
107
105
|
"inquirer": "^9.3.8",
|
|
108
|
-
"ioredis": "^5.8.2",
|
|
109
|
-
"jsonwebtoken": "^9.0.3",
|
|
110
|
-
"jwks-rsa": "^3.2.0",
|
|
111
106
|
"msgpackr": "^1.10.1",
|
|
112
107
|
"ngrok": "^5.0.0-beta.2",
|
|
113
108
|
"open": "^11.0.0",
|
|
114
109
|
"ora": "^9.0.0",
|
|
115
110
|
"pg": "^8.17.1",
|
|
116
|
-
"puppeteer": "^24.34.0",
|
|
117
111
|
"rate-limiter-flexible": "^9.0.1",
|
|
118
|
-
"redis": "^5.10.0",
|
|
119
112
|
"shell-escape": "^0.2.0",
|
|
120
113
|
"socket.io": "^4.6.0",
|
|
121
114
|
"socket.io-client": "^4.6.0",
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"timestamp": "2026-01-06T15:16:51.046Z",
|
|
3
|
-
"tests": [
|
|
4
|
-
{
|
|
5
|
-
"test": "status_command",
|
|
6
|
-
"iterations": 3,
|
|
7
|
-
"measurements": [
|
|
8
|
-
393.8040000000001,
|
|
9
|
-
396.434667,
|
|
10
|
-
393.71524999999997
|
|
11
|
-
],
|
|
12
|
-
"average": 394.6513056666667,
|
|
13
|
-
"unit": "ms"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"test": "context_operations",
|
|
17
|
-
"operations": [
|
|
18
|
-
{
|
|
19
|
-
"operation": "version_check",
|
|
20
|
-
"duration": 398.65762500000005,
|
|
21
|
-
"success": true
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"operation": "tasks_list",
|
|
25
|
-
"duration": 518.715792,
|
|
26
|
-
"success": true
|
|
27
|
-
}
|
|
28
|
-
],
|
|
29
|
-
"totalTime": 917.373417,
|
|
30
|
-
"unit": "ms"
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"test": "task_operations",
|
|
34
|
-
"operations": [
|
|
35
|
-
{
|
|
36
|
-
"operation": "add_task",
|
|
37
|
-
"duration": 518.383875,
|
|
38
|
-
"success": true,
|
|
39
|
-
"outputSize": 245
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"operation": "list_tasks",
|
|
43
|
-
"duration": 526.2628340000001,
|
|
44
|
-
"success": true,
|
|
45
|
-
"outputSize": 2751
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"operation": "show_task",
|
|
49
|
-
"duration": 395.6398339999996,
|
|
50
|
-
"success": true,
|
|
51
|
-
"outputSize": 92
|
|
52
|
-
}
|
|
53
|
-
],
|
|
54
|
-
"unit": "ms"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"test": "storage_performance",
|
|
58
|
-
"results": {
|
|
59
|
-
"database": {
|
|
60
|
-
"exists": true,
|
|
61
|
-
"size": 602112,
|
|
62
|
-
"sizeFormatted": "588.00 KB",
|
|
63
|
-
"modified": "2026-01-06T15:16:54.814Z"
|
|
64
|
-
},
|
|
65
|
-
"tasks": {
|
|
66
|
-
"exists": true,
|
|
67
|
-
"size": 219410,
|
|
68
|
-
"sizeFormatted": "214.27 KB",
|
|
69
|
-
"lineCount": 249,
|
|
70
|
-
"modified": "2026-01-06T15:16:54.286Z"
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"test": "baseline_comparison",
|
|
76
|
-
"baseline": {
|
|
77
|
-
"taskListing": {
|
|
78
|
-
"withStackMemory": 501.72233400000005,
|
|
79
|
-
"withoutStackMemory": 5000,
|
|
80
|
-
"unit": "ms"
|
|
81
|
-
},
|
|
82
|
-
"taskCreation": {
|
|
83
|
-
"withStackMemory": 500.09279100000003,
|
|
84
|
-
"withoutStackMemory": 30000,
|
|
85
|
-
"unit": "ms"
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import 'dotenv/config';
|
|
3
|
-
import Database from 'better-sqlite3';
|
|
4
|
-
import { RailwayOptimizedStorage } from './dist/core/storage/railway-optimized-storage.js';
|
|
5
|
-
import { ConfigManager } from './dist/core/config/config-manager.js';
|
|
6
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
7
|
-
|
|
8
|
-
async function testTierMigration() {
|
|
9
|
-
const db = new Database('.stackmemory/context.db');
|
|
10
|
-
const configManager = new ConfigManager();
|
|
11
|
-
|
|
12
|
-
// Create storage with shorter tier durations for testing
|
|
13
|
-
const storage = new RailwayOptimizedStorage(db, configManager, {
|
|
14
|
-
tiers: {
|
|
15
|
-
hotHours: 0.001, // Very short for testing (3.6 seconds)
|
|
16
|
-
warmDays: 0.0001, // Very short for testing (8.64 seconds)
|
|
17
|
-
compressionScore: 0.5
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Create a test trace
|
|
22
|
-
const traceId = `test_${uuidv4()}`;
|
|
23
|
-
const testTrace = {
|
|
24
|
-
id: traceId,
|
|
25
|
-
type: 'test',
|
|
26
|
-
score: 0.7,
|
|
27
|
-
summary: 'Test trace for tier migration',
|
|
28
|
-
metadata: {
|
|
29
|
-
startTime: Date.now() - 1000, // 1 second ago
|
|
30
|
-
endTime: Date.now(),
|
|
31
|
-
filesModified: ['test.js'],
|
|
32
|
-
errorsEncountered: [],
|
|
33
|
-
decisionsRecorded: [],
|
|
34
|
-
causalChain: []
|
|
35
|
-
},
|
|
36
|
-
tools: [
|
|
37
|
-
{ tool: 'test', input: 'test input', output: 'test output' }
|
|
38
|
-
],
|
|
39
|
-
compressed: false
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
console.log('📝 Creating test trace:', testTrace.id);
|
|
43
|
-
|
|
44
|
-
// First, insert the trace into the traces table to satisfy foreign key
|
|
45
|
-
db.prepare(`
|
|
46
|
-
INSERT INTO traces (id, type, score, summary, start_time, end_time,
|
|
47
|
-
files_modified, errors_encountered, decisions_recorded,
|
|
48
|
-
causal_chain, compressed_data, created_at)
|
|
49
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
50
|
-
`).run(
|
|
51
|
-
traceId,
|
|
52
|
-
'test',
|
|
53
|
-
0.7,
|
|
54
|
-
'Test trace for tier migration',
|
|
55
|
-
testTrace.metadata.startTime,
|
|
56
|
-
testTrace.metadata.endTime,
|
|
57
|
-
JSON.stringify(testTrace.metadata.filesModified),
|
|
58
|
-
JSON.stringify(testTrace.metadata.errorsEncountered),
|
|
59
|
-
JSON.stringify(testTrace.metadata.decisionsRecorded),
|
|
60
|
-
testTrace.metadata.causalChain.length,
|
|
61
|
-
JSON.stringify(testTrace),
|
|
62
|
-
Date.now()
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
// Store the trace in storage tiers
|
|
66
|
-
const tier = await storage.storeTrace(testTrace);
|
|
67
|
-
console.log(`✅ Stored in ${tier} tier`);
|
|
68
|
-
|
|
69
|
-
// Check storage location
|
|
70
|
-
const location = db.prepare('SELECT * FROM storage_tiers WHERE trace_id = ?').get(testTrace.id);
|
|
71
|
-
console.log('📍 Initial location:', location);
|
|
72
|
-
|
|
73
|
-
// Wait a moment for the trace to age
|
|
74
|
-
console.log('⏳ Waiting 5 seconds for trace to age...');
|
|
75
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
76
|
-
|
|
77
|
-
// Trigger migration
|
|
78
|
-
console.log('🔄 Triggering migration...');
|
|
79
|
-
const results = await storage.migrateTiers();
|
|
80
|
-
console.log('Migration results:', results);
|
|
81
|
-
|
|
82
|
-
// Check new location
|
|
83
|
-
const newLocation = db.prepare('SELECT * FROM storage_tiers WHERE trace_id = ?').get(testTrace.id);
|
|
84
|
-
console.log('📍 New location:', newLocation);
|
|
85
|
-
|
|
86
|
-
// Try to retrieve the trace
|
|
87
|
-
console.log('🔍 Retrieving trace after migration...');
|
|
88
|
-
const retrieved = await storage.retrieveTrace(testTrace.id);
|
|
89
|
-
console.log('✅ Retrieved:', retrieved ? 'Success' : 'Failed');
|
|
90
|
-
|
|
91
|
-
if (retrieved) {
|
|
92
|
-
console.log(' ID matches:', retrieved.id === testTrace.id);
|
|
93
|
-
console.log(' Summary matches:', retrieved.summary === testTrace.summary);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
db.close();
|
|
97
|
-
console.log('✨ Test complete!');
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
testTierMigration().catch(console.error);
|