@stackmemoryai/stackmemory 0.3.22 → 0.3.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/ralph.js +294 -0
- package/dist/cli/commands/ralph.js.map +7 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +2 -2
- package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js +586 -0
- package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +7 -0
- package/dist/integrations/ralph/context/context-budget-manager.js +297 -0
- package/dist/integrations/ralph/context/context-budget-manager.js.map +7 -0
- package/dist/integrations/ralph/context/stackmemory-context-loader.js +356 -0
- package/dist/integrations/ralph/context/stackmemory-context-loader.js.map +7 -0
- package/dist/integrations/ralph/index.js +14 -0
- package/dist/integrations/ralph/index.js.map +7 -0
- package/dist/integrations/ralph/learning/pattern-learner.js +397 -0
- package/dist/integrations/ralph/learning/pattern-learner.js.map +7 -0
- package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js +444 -0
- package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js.map +7 -0
- package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js +459 -0
- package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +7 -0
- package/dist/integrations/ralph/performance/performance-optimizer.js +354 -0
- package/dist/integrations/ralph/performance/performance-optimizer.js.map +7 -0
- package/dist/integrations/ralph/ralph-integration-demo.js +178 -0
- package/dist/integrations/ralph/ralph-integration-demo.js.map +7 -0
- package/dist/integrations/ralph/state/state-reconciler.js +400 -0
- package/dist/integrations/ralph/state/state-reconciler.js.map +7 -0
- package/dist/integrations/ralph/swarm/swarm-coordinator.js +487 -0
- package/dist/integrations/ralph/swarm/swarm-coordinator.js.map +7 -0
- package/dist/integrations/ralph/types.js +1 -0
- package/dist/integrations/ralph/types.js.map +7 -0
- package/dist/integrations/ralph/visualization/ralph-debugger.js +581 -0
- package/dist/integrations/ralph/visualization/ralph-debugger.js.map +7 -0
- package/package.json +1 -1
- package/scripts/deploy-ralph-swarm.sh +365 -0
- package/scripts/ralph-integration-test.js +274 -0
- package/scripts/ralph-loop-implementation.js +404 -0
- package/scripts/swarm-monitor.js +509 -0
- package/scripts/test-parallel-swarms.js +443 -0
- package/scripts/testing/ralph-cli-test.js +88 -0
- package/scripts/testing/ralph-integration-validation.js +727 -0
- package/scripts/testing/ralph-swarm-test-scenarios.js +613 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
class RalphLoop {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.baseDir = options.baseDir || '.ralph';
|
|
11
|
+
this.maxIterations = options.maxIterations || 50;
|
|
12
|
+
this.verbose = options.verbose || false;
|
|
13
|
+
|
|
14
|
+
this.paths = {
|
|
15
|
+
task: path.join(this.baseDir, 'task.md'),
|
|
16
|
+
criteria: path.join(this.baseDir, 'completion-criteria.md'),
|
|
17
|
+
iteration: path.join(this.baseDir, 'iteration.txt'),
|
|
18
|
+
feedback: path.join(this.baseDir, 'feedback.txt'),
|
|
19
|
+
state: path.join(this.baseDir, 'state.json'),
|
|
20
|
+
complete: path.join(this.baseDir, 'work-complete.txt'),
|
|
21
|
+
progress: path.join(this.baseDir, 'progress.jsonl'),
|
|
22
|
+
history: path.join(this.baseDir, 'history')
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async initialize(task, criteria) {
|
|
27
|
+
// Create directory structure
|
|
28
|
+
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
29
|
+
fs.mkdirSync(this.paths.history, { recursive: true });
|
|
30
|
+
|
|
31
|
+
// Initialize files
|
|
32
|
+
fs.writeFileSync(this.paths.task, task);
|
|
33
|
+
fs.writeFileSync(this.paths.criteria, criteria);
|
|
34
|
+
fs.writeFileSync(this.paths.iteration, '0');
|
|
35
|
+
fs.writeFileSync(this.paths.feedback, '');
|
|
36
|
+
|
|
37
|
+
// Initial state
|
|
38
|
+
const state = {
|
|
39
|
+
startTime: Date.now(),
|
|
40
|
+
task: task.substring(0, 100),
|
|
41
|
+
status: 'initialized'
|
|
42
|
+
};
|
|
43
|
+
fs.writeFileSync(this.paths.state, JSON.stringify(state, null, 2));
|
|
44
|
+
|
|
45
|
+
this.log('Ralph Loop initialized');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async runWorkerIteration() {
|
|
49
|
+
const iteration = this.getCurrentIteration();
|
|
50
|
+
const task = fs.readFileSync(this.paths.task, 'utf8');
|
|
51
|
+
const feedback = this.getFeedback();
|
|
52
|
+
|
|
53
|
+
this.log(`Worker iteration ${iteration} starting...`);
|
|
54
|
+
|
|
55
|
+
// Step 1: Analyze current state (fresh context)
|
|
56
|
+
const analysis = await this.analyzeCurrentState();
|
|
57
|
+
|
|
58
|
+
// Step 2: Plan based on task and feedback
|
|
59
|
+
const plan = await this.createPlan(task, feedback, analysis);
|
|
60
|
+
|
|
61
|
+
// Step 3: Execute changes
|
|
62
|
+
const changes = await this.executeChanges(plan);
|
|
63
|
+
|
|
64
|
+
// Step 4: Validate changes
|
|
65
|
+
const validation = await this.validateChanges();
|
|
66
|
+
|
|
67
|
+
// Step 5: Save iteration artifacts
|
|
68
|
+
await this.saveIterationArtifacts(iteration, {
|
|
69
|
+
analysis,
|
|
70
|
+
plan,
|
|
71
|
+
changes,
|
|
72
|
+
validation
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Step 6: Commit changes
|
|
76
|
+
this.commitChanges(iteration, plan.summary);
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
iteration,
|
|
80
|
+
changes: changes.length,
|
|
81
|
+
validation
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async runReviewerIteration() {
|
|
86
|
+
const iteration = this.getCurrentIteration();
|
|
87
|
+
const task = fs.readFileSync(this.paths.task, 'utf8');
|
|
88
|
+
const criteria = fs.readFileSync(this.paths.criteria, 'utf8');
|
|
89
|
+
|
|
90
|
+
this.log(`Reviewer iteration ${iteration} evaluating...`);
|
|
91
|
+
|
|
92
|
+
// Fresh evaluation of current state
|
|
93
|
+
const evaluation = await this.evaluateAgainstCriteria(criteria);
|
|
94
|
+
|
|
95
|
+
if (evaluation.complete) {
|
|
96
|
+
fs.writeFileSync(this.paths.complete, 'true');
|
|
97
|
+
this.log('Task completed successfully!');
|
|
98
|
+
return { complete: true };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Generate feedback for next iteration
|
|
102
|
+
const feedback = this.generateFeedback(evaluation);
|
|
103
|
+
fs.writeFileSync(this.paths.feedback, feedback);
|
|
104
|
+
|
|
105
|
+
// Increment iteration
|
|
106
|
+
this.incrementIteration();
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
complete: false,
|
|
110
|
+
feedback: feedback.substring(0, 200)
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async analyzeCurrentState() {
|
|
115
|
+
// Simulate fresh analysis of codebase
|
|
116
|
+
const files = this.scanRelevantFiles();
|
|
117
|
+
const tests = this.getTestStatus();
|
|
118
|
+
const lastCommit = this.getLastCommit();
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
filesCount: files.length,
|
|
122
|
+
testsPass: tests.passing,
|
|
123
|
+
testsFail: tests.failing,
|
|
124
|
+
lastChange: lastCommit
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async createPlan(_task, feedback, _analysis) {
|
|
129
|
+
// In real implementation, this would use an LLM
|
|
130
|
+
// Here we simulate planning based on feedback
|
|
131
|
+
const needsWork = feedback.includes('failing') ||
|
|
132
|
+
feedback.includes('incomplete') ||
|
|
133
|
+
feedback === '';
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
summary: `Iteration work based on: ${feedback.substring(0, 50)}`,
|
|
137
|
+
steps: needsWork ? ['Fix issues', 'Add features', 'Update tests'] : ['Polish'],
|
|
138
|
+
priority: needsWork ? 'high' : 'low'
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async executeChanges(plan) {
|
|
143
|
+
// Simulate making code changes
|
|
144
|
+
const changes = [];
|
|
145
|
+
|
|
146
|
+
for (const step of plan.steps) {
|
|
147
|
+
changes.push({
|
|
148
|
+
step,
|
|
149
|
+
timestamp: Date.now(),
|
|
150
|
+
result: 'simulated'
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return changes;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async validateChanges() {
|
|
158
|
+
// Run tests and checks
|
|
159
|
+
try {
|
|
160
|
+
const testResult = this.runTests();
|
|
161
|
+
const lintResult = this.runLint();
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
testsPass: testResult.passing > 0,
|
|
165
|
+
lintClean: lintResult.clean,
|
|
166
|
+
errors: [...testResult.errors, ...lintResult.errors]
|
|
167
|
+
};
|
|
168
|
+
} catch (error) {
|
|
169
|
+
return {
|
|
170
|
+
testsPass: false,
|
|
171
|
+
lintClean: false,
|
|
172
|
+
errors: [error.message]
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async evaluateAgainstCriteria(criteria) {
|
|
178
|
+
// Parse criteria and check each
|
|
179
|
+
const criteriaLines = criteria.split('\n').filter(l => l.trim().startsWith('-'));
|
|
180
|
+
const results = {};
|
|
181
|
+
|
|
182
|
+
for (const criterion of criteriaLines) {
|
|
183
|
+
const key = criterion.replace('-', '').trim().substring(0, 20);
|
|
184
|
+
// Simulate checking (in reality would analyze code)
|
|
185
|
+
results[key] = Math.random() > 0.3; // 70% chance of meeting each criterion
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const complete = Object.values(results).every(v => v === true);
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
complete,
|
|
192
|
+
criteria: results,
|
|
193
|
+
unmet: Object.entries(results)
|
|
194
|
+
.filter(([_, v]) => !v)
|
|
195
|
+
.map(([k]) => k)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
generateFeedback(evaluation) {
|
|
200
|
+
if (evaluation.unmet.length === 0) {
|
|
201
|
+
return 'All criteria met';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return `Still need to address:\n${evaluation.unmet.map(c => `- ${c}`).join('\n')}`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async saveIterationArtifacts(iteration, artifacts) {
|
|
208
|
+
const iterDir = path.join(this.paths.history, `iteration-${String(iteration).padStart(3, '0')}`);
|
|
209
|
+
fs.mkdirSync(iterDir, { recursive: true });
|
|
210
|
+
|
|
211
|
+
fs.writeFileSync(
|
|
212
|
+
path.join(iterDir, 'artifacts.json'),
|
|
213
|
+
JSON.stringify(artifacts, null, 2)
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// Log progress
|
|
217
|
+
const progress = {
|
|
218
|
+
iteration,
|
|
219
|
+
timestamp: Date.now(),
|
|
220
|
+
changes: artifacts.changes.length,
|
|
221
|
+
validation: artifacts.validation.testsPass,
|
|
222
|
+
errors: artifacts.validation.errors.length
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
fs.appendFileSync(this.paths.progress, JSON.stringify(progress) + '\n');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
commitChanges(iteration, summary) {
|
|
229
|
+
try {
|
|
230
|
+
execSync('git add -A', { stdio: 'pipe' });
|
|
231
|
+
execSync(`git commit -m "Ralph iteration ${iteration}: ${summary}" --allow-empty`, { stdio: 'pipe' });
|
|
232
|
+
this.log(`Committed iteration ${iteration}`);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
this.log(`Commit failed: ${error.message}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
scanRelevantFiles() {
|
|
239
|
+
// Simulate file scanning
|
|
240
|
+
return ['index.js', 'auth.js', 'test.js'];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
getTestStatus() {
|
|
244
|
+
try {
|
|
245
|
+
execSync('npm test', { stdio: 'pipe' });
|
|
246
|
+
return { passing: 5, failing: 0 };
|
|
247
|
+
} catch {
|
|
248
|
+
return { passing: 3, failing: 2 };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
runTests() {
|
|
253
|
+
const status = this.getTestStatus();
|
|
254
|
+
return {
|
|
255
|
+
passing: status.passing,
|
|
256
|
+
failing: status.failing,
|
|
257
|
+
errors: status.failing > 0 ? ['Some tests failed'] : []
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
runLint() {
|
|
262
|
+
try {
|
|
263
|
+
execSync('npm run lint', { stdio: 'pipe' });
|
|
264
|
+
return { clean: true, errors: [] };
|
|
265
|
+
} catch {
|
|
266
|
+
return { clean: false, errors: ['Lint issues found'] };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
getLastCommit() {
|
|
271
|
+
try {
|
|
272
|
+
return execSync('git log -1 --oneline', { encoding: 'utf8' }).trim();
|
|
273
|
+
} catch {
|
|
274
|
+
return 'No commits yet';
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
getCurrentIteration() {
|
|
279
|
+
try {
|
|
280
|
+
return parseInt(fs.readFileSync(this.paths.iteration, 'utf8'));
|
|
281
|
+
} catch {
|
|
282
|
+
return 0;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
incrementIteration() {
|
|
287
|
+
const current = this.getCurrentIteration();
|
|
288
|
+
fs.writeFileSync(this.paths.iteration, String(current + 1));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
getFeedback() {
|
|
292
|
+
try {
|
|
293
|
+
return fs.readFileSync(this.paths.feedback, 'utf8');
|
|
294
|
+
} catch {
|
|
295
|
+
return '';
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
isComplete() {
|
|
300
|
+
return fs.existsSync(this.paths.complete);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
log(message) {
|
|
304
|
+
if (this.verbose) {
|
|
305
|
+
console.log(`[Ralph] ${message}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async run() {
|
|
310
|
+
while (!this.isComplete()) {
|
|
311
|
+
const iteration = this.getCurrentIteration();
|
|
312
|
+
|
|
313
|
+
if (iteration >= this.maxIterations) {
|
|
314
|
+
console.log('Max iterations reached!');
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Worker phase
|
|
319
|
+
await this.runWorkerIteration();
|
|
320
|
+
|
|
321
|
+
// Reviewer phase
|
|
322
|
+
const review = await this.runReviewerIteration();
|
|
323
|
+
|
|
324
|
+
if (review.complete) {
|
|
325
|
+
console.log('Task completed!');
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Brief pause between iterations
|
|
330
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Final summary
|
|
334
|
+
this.printSummary();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
printSummary() {
|
|
338
|
+
const iterations = this.getCurrentIteration();
|
|
339
|
+
const progress = fs.readFileSync(this.paths.progress, 'utf8')
|
|
340
|
+
.split('\n')
|
|
341
|
+
.filter(Boolean)
|
|
342
|
+
.map(line => JSON.parse(line));
|
|
343
|
+
|
|
344
|
+
console.log('\n=== Ralph Loop Summary ===');
|
|
345
|
+
console.log(`Total iterations: ${iterations}`);
|
|
346
|
+
console.log(`Total changes: ${progress.reduce((sum, p) => sum + p.changes, 0)}`);
|
|
347
|
+
console.log(`Final status: ${this.isComplete() ? 'COMPLETE' : 'INCOMPLETE'}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// CLI Interface
|
|
352
|
+
async function main() {
|
|
353
|
+
const args = process.argv.slice(2);
|
|
354
|
+
const command = args[0];
|
|
355
|
+
|
|
356
|
+
if (command === 'init') {
|
|
357
|
+
const task = args[1] || 'Implement a feature';
|
|
358
|
+
const criteria = args[2] || '- Tests pass\n- Code works\n- No errors';
|
|
359
|
+
|
|
360
|
+
const loop = new RalphLoop({ verbose: true });
|
|
361
|
+
await loop.initialize(task, criteria);
|
|
362
|
+
console.log('Ralph Loop initialized. Run with: node ralph-loop-implementation.js run');
|
|
363
|
+
|
|
364
|
+
} else if (command === 'run') {
|
|
365
|
+
const loop = new RalphLoop({ verbose: true });
|
|
366
|
+
|
|
367
|
+
if (!fs.existsSync(loop.paths.task)) {
|
|
368
|
+
console.error('No task found. Initialize first with: node ralph-loop-implementation.js init');
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
await loop.run();
|
|
373
|
+
|
|
374
|
+
} else if (command === 'status') {
|
|
375
|
+
const loop = new RalphLoop();
|
|
376
|
+
const iteration = loop.getCurrentIteration();
|
|
377
|
+
const complete = loop.isComplete();
|
|
378
|
+
const feedback = loop.getFeedback();
|
|
379
|
+
|
|
380
|
+
console.log(`Iteration: ${iteration}`);
|
|
381
|
+
console.log(`Status: ${complete ? 'COMPLETE' : 'IN PROGRESS'}`);
|
|
382
|
+
console.log(`Last feedback: ${feedback.substring(0, 100)}`);
|
|
383
|
+
|
|
384
|
+
} else {
|
|
385
|
+
console.log(`
|
|
386
|
+
Ralph Loop Implementation
|
|
387
|
+
|
|
388
|
+
Usage:
|
|
389
|
+
node ralph-loop-implementation.js init [task] [criteria] - Initialize a new loop
|
|
390
|
+
node ralph-loop-implementation.js run - Run the loop
|
|
391
|
+
node ralph-loop-implementation.js status - Check current status
|
|
392
|
+
|
|
393
|
+
Example:
|
|
394
|
+
node ralph-loop-implementation.js init "Add login feature" "- Tests pass\\n- JWT works\\n- Error handling"
|
|
395
|
+
node ralph-loop-implementation.js run
|
|
396
|
+
`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
401
|
+
main().catch(console.error);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export { RalphLoop };
|