@stackmemoryai/stackmemory 0.3.21 → 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/linear-unified.js +2 -3
- package/dist/cli/commands/linear-unified.js.map +2 -2
- package/dist/cli/commands/ralph.js +294 -0
- package/dist/cli/commands/ralph.js.map +7 -0
- package/dist/cli/commands/tasks.js +1 -1
- package/dist/cli/commands/tasks.js.map +2 -2
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +2 -2
- package/dist/integrations/mcp/handlers/code-execution-handlers.js +262 -0
- package/dist/integrations/mcp/handlers/code-execution-handlers.js.map +7 -0
- package/dist/integrations/mcp/tool-definitions-code.js +121 -0
- package/dist/integrations/mcp/tool-definitions-code.js.map +7 -0
- 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/dist/servers/railway/index.js +98 -92
- package/dist/servers/railway/index.js.map +3 -3
- package/package.json +1 -2
- package/scripts/claude-sm-autostart.js +1 -1
- package/scripts/clean-linear-backlog.js +2 -2
- package/scripts/debug-linear-update.js +1 -1
- package/scripts/debug-railway-build.js +87 -0
- package/scripts/delete-linear-tasks.js +2 -2
- package/scripts/deploy-ralph-swarm.sh +365 -0
- package/scripts/install-code-execution-hooks.sh +96 -0
- package/scripts/linear-task-review.js +1 -1
- 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/sync-and-clean-tasks.js +1 -1
- package/scripts/sync-linear-graphql.js +3 -3
- package/scripts/sync-linear-tasks.js +1 -1
- package/scripts/test-code-execution.js +143 -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
- package/scripts/update-linear-tasks-fixed.js +1 -1
- package/scripts/validate-railway-deployment.js +137 -0
- package/templates/claude-hooks/hook-config.json +59 -0
- package/templates/claude-hooks/pre-tool-use +189 -0
- package/dist/servers/railway/minimal.js +0 -91
- package/dist/servers/railway/minimal.js.map +0 -7
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ralph-StackMemory Integration Test Script
|
|
5
|
+
* Simple CLI to test the integration functionality
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { RalphIntegrationDemo } from '../dist/integrations/ralph/ralph-integration-demo.js';
|
|
9
|
+
import { performance } from 'perf_hooks';
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const command = args[0] || 'demo';
|
|
14
|
+
|
|
15
|
+
console.log('\n🧪 Ralph-StackMemory Integration Test');
|
|
16
|
+
console.log('=====================================\n');
|
|
17
|
+
|
|
18
|
+
const startTime = performance.now();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
switch (command) {
|
|
22
|
+
case 'demo':
|
|
23
|
+
await runDemo();
|
|
24
|
+
break;
|
|
25
|
+
case 'quick':
|
|
26
|
+
await runQuickTest();
|
|
27
|
+
break;
|
|
28
|
+
case 'validate':
|
|
29
|
+
await runValidation();
|
|
30
|
+
break;
|
|
31
|
+
default:
|
|
32
|
+
showUsage();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const endTime = performance.now();
|
|
37
|
+
const duration = Math.round(endTime - startTime);
|
|
38
|
+
|
|
39
|
+
console.log(`\n✅ Test completed successfully in ${duration}ms`);
|
|
40
|
+
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error(`\n❌ Test failed: ${error.message}`);
|
|
43
|
+
|
|
44
|
+
if (process.env.DEBUG) {
|
|
45
|
+
console.error('\nDebug info:');
|
|
46
|
+
console.error(error.stack);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function runDemo() {
|
|
54
|
+
console.log('🎬 Running full integration demo...\n');
|
|
55
|
+
|
|
56
|
+
const demo = new RalphIntegrationDemo();
|
|
57
|
+
await demo.run();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function runQuickTest() {
|
|
61
|
+
console.log('⚡ Running quick validation test...\n');
|
|
62
|
+
|
|
63
|
+
// Import the core classes to test they load correctly
|
|
64
|
+
try {
|
|
65
|
+
await import('../dist/integrations/ralph/index.js');
|
|
66
|
+
console.log('✓ Core bridge class loaded');
|
|
67
|
+
|
|
68
|
+
const { ContextBudgetManager } = await import('../dist/integrations/ralph/index.js');
|
|
69
|
+
console.log('✓ Context budget manager loaded');
|
|
70
|
+
|
|
71
|
+
const { StateReconciler } = await import('../dist/integrations/ralph/index.js');
|
|
72
|
+
console.log('✓ State reconciler loaded');
|
|
73
|
+
|
|
74
|
+
await import('../dist/integrations/ralph/index.js');
|
|
75
|
+
console.log('✓ Iteration lifecycle loaded');
|
|
76
|
+
|
|
77
|
+
await import('../dist/integrations/ralph/index.js');
|
|
78
|
+
console.log('✓ Performance optimizer loaded');
|
|
79
|
+
|
|
80
|
+
// Test basic instantiation
|
|
81
|
+
const budgetManager = new ContextBudgetManager();
|
|
82
|
+
const tokenEstimate = budgetManager.estimateTokens('Hello, world!');
|
|
83
|
+
console.log(`✓ Token estimation working: ${tokenEstimate} tokens`);
|
|
84
|
+
|
|
85
|
+
new StateReconciler();
|
|
86
|
+
console.log('✓ State reconciler instantiated');
|
|
87
|
+
|
|
88
|
+
console.log('\n🎯 All core components validated successfully!');
|
|
89
|
+
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw new Error(`Component loading failed: ${error.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function runValidation() {
|
|
96
|
+
console.log('🔍 Running detailed validation...\n');
|
|
97
|
+
|
|
98
|
+
// Test context budget manager
|
|
99
|
+
await testContextBudgetManager();
|
|
100
|
+
|
|
101
|
+
// Test state reconciler
|
|
102
|
+
await testStateReconciler();
|
|
103
|
+
|
|
104
|
+
// Test performance optimizer
|
|
105
|
+
await testPerformanceOptimizer();
|
|
106
|
+
|
|
107
|
+
console.log('\n✨ All validation tests passed!');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function testContextBudgetManager() {
|
|
111
|
+
console.log('📊 Testing Context Budget Manager...');
|
|
112
|
+
|
|
113
|
+
const { ContextBudgetManager } = await import('../dist/integrations/ralph/index.js');
|
|
114
|
+
|
|
115
|
+
const manager = new ContextBudgetManager({
|
|
116
|
+
maxTokens: 100,
|
|
117
|
+
compressionEnabled: true
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Test token estimation
|
|
121
|
+
const shortText = 'Short text';
|
|
122
|
+
const longText = 'This is a much longer piece of text that should have more tokens estimated for it than the shorter version above.';
|
|
123
|
+
|
|
124
|
+
const shortTokens = manager.estimateTokens(shortText);
|
|
125
|
+
const longTokens = manager.estimateTokens(longText);
|
|
126
|
+
|
|
127
|
+
if (longTokens <= shortTokens) {
|
|
128
|
+
throw new Error('Token estimation not working correctly');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Test empty text
|
|
132
|
+
const emptyTokens = manager.estimateTokens('');
|
|
133
|
+
if (emptyTokens !== 0) {
|
|
134
|
+
throw new Error('Empty text should have 0 tokens');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log(' ✓ Token estimation working correctly');
|
|
138
|
+
|
|
139
|
+
// Test usage tracking
|
|
140
|
+
const usage = manager.getUsage();
|
|
141
|
+
if (typeof usage.used !== 'number' || typeof usage.available !== 'number') {
|
|
142
|
+
throw new Error('Usage tracking not working');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(' ✓ Usage tracking functional');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function testStateReconciler() {
|
|
149
|
+
console.log('🔄 Testing State Reconciler...');
|
|
150
|
+
|
|
151
|
+
const { StateReconciler } = await import('../dist/integrations/ralph/index.js');
|
|
152
|
+
|
|
153
|
+
const reconciler = new StateReconciler({
|
|
154
|
+
precedence: ['git', 'files', 'memory'],
|
|
155
|
+
conflictResolution: 'automatic'
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Test conflict detection
|
|
159
|
+
const sources = [
|
|
160
|
+
{
|
|
161
|
+
type: 'git',
|
|
162
|
+
state: { iteration: 5, status: 'running' },
|
|
163
|
+
timestamp: Date.now(),
|
|
164
|
+
confidence: 0.9
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'files',
|
|
168
|
+
state: { iteration: 5, status: 'running' },
|
|
169
|
+
timestamp: Date.now(),
|
|
170
|
+
confidence: 0.95
|
|
171
|
+
}
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const conflicts = reconciler.detectConflicts(sources);
|
|
175
|
+
if (conflicts.length !== 0) {
|
|
176
|
+
throw new Error('Should not detect conflicts in matching sources');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log(' ✓ Conflict detection working');
|
|
180
|
+
|
|
181
|
+
// Test state sources
|
|
182
|
+
const gitState = await reconciler.getGitState();
|
|
183
|
+
const fileState = await reconciler.getFileState();
|
|
184
|
+
const memoryState = await reconciler.getMemoryState('test-loop');
|
|
185
|
+
|
|
186
|
+
if (gitState.type !== 'git' || fileState.type !== 'files' || memoryState.type !== 'memory') {
|
|
187
|
+
throw new Error('State source types incorrect');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log(' ✓ State source loading functional');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function testPerformanceOptimizer() {
|
|
194
|
+
console.log('⚡ Testing Performance Optimizer...');
|
|
195
|
+
|
|
196
|
+
const { PerformanceOptimizer } = await import('../dist/integrations/ralph/index.js');
|
|
197
|
+
|
|
198
|
+
const optimizer = new PerformanceOptimizer({
|
|
199
|
+
asyncSaves: true,
|
|
200
|
+
compressionLevel: 1,
|
|
201
|
+
cacheEnabled: true
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Test metrics
|
|
205
|
+
const metrics = optimizer.getMetrics();
|
|
206
|
+
const requiredMetrics = ['iterationTime', 'contextLoadTime', 'stateSaveTime', 'memoryUsage', 'tokenCount', 'cacheHitRate'];
|
|
207
|
+
|
|
208
|
+
for (const metric of requiredMetrics) {
|
|
209
|
+
if (typeof metrics[metric] !== 'number') {
|
|
210
|
+
throw new Error(`Missing or invalid metric: ${metric}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log(' ✓ Performance metrics available');
|
|
215
|
+
|
|
216
|
+
// Test compression
|
|
217
|
+
const testData = { message: 'Hello, world!', array: [1, 2, 3, 4, 5] };
|
|
218
|
+
const compressed = await optimizer.compressData(testData);
|
|
219
|
+
|
|
220
|
+
if (compressed.compressed !== true) {
|
|
221
|
+
throw new Error('Data compression not working');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const decompressed = await optimizer.decompressData(compressed);
|
|
225
|
+
if (JSON.stringify(decompressed) !== JSON.stringify(testData)) {
|
|
226
|
+
throw new Error('Data decompression failed');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
console.log(' ✓ Compression/decompression working');
|
|
230
|
+
|
|
231
|
+
// Cleanup
|
|
232
|
+
optimizer.cleanup();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function showUsage() {
|
|
236
|
+
console.log(`
|
|
237
|
+
Usage: node ralph-integration-test.js [command]
|
|
238
|
+
|
|
239
|
+
Commands:
|
|
240
|
+
demo Run the full integration demonstration (default)
|
|
241
|
+
quick Quick validation of core components
|
|
242
|
+
validate Detailed validation of all functionality
|
|
243
|
+
|
|
244
|
+
Examples:
|
|
245
|
+
node ralph-integration-test.js
|
|
246
|
+
node ralph-integration-test.js quick
|
|
247
|
+
DEBUG=1 node ralph-integration-test.js validate
|
|
248
|
+
|
|
249
|
+
Environment Variables:
|
|
250
|
+
DEBUG=1 Show detailed error information
|
|
251
|
+
`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Handle unhandled promise rejections
|
|
255
|
+
process.on('unhandledRejection', (reason, _promise) => {
|
|
256
|
+
console.error('\n💥 Unhandled Promise Rejection:');
|
|
257
|
+
console.error(reason);
|
|
258
|
+
process.exit(1);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Handle uncaught exceptions
|
|
262
|
+
process.on('uncaughtException', (error) => {
|
|
263
|
+
console.error('\n💥 Uncaught Exception:');
|
|
264
|
+
console.error(error.message);
|
|
265
|
+
if (process.env.DEBUG) {
|
|
266
|
+
console.error(error.stack);
|
|
267
|
+
}
|
|
268
|
+
process.exit(1);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Run if called directly
|
|
272
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
273
|
+
main().catch(console.error);
|
|
274
|
+
}
|
|
@@ -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 };
|