closed-loop-cli 1.0.3 → 1.0.4
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.
Potentially problematic release.
This version of closed-loop-cli might be problematic. Click here for more details.
- package/CLAUDE.md +17 -0
- package/Learnings.md +73 -0
- package/dist/index.js +14 -128
- package/package.json +1 -7
- package/src/index.ts +356 -477
- package/tsconfig.json +16 -0
- package/dist/orchestrator/autogenesis.js +0 -973
- package/dist/orchestrator/dgm-archive.js +0 -223
- package/dist/orchestrator/fitness-evaluator.js +0 -99
- package/dist/orchestrator/mutation-strategies.js +0 -174
- package/dist/orchestrator/prompt-benchmark.js +0 -102
- package/dist/orchestrator/prompt-optimizer.js +0 -169
- package/dist/orchestrator/refactor-scanner.js +0 -222
- package/src/orchestrator/autogenesis.ts +0 -1078
- package/src/orchestrator/dgm-archive.ts +0 -257
- package/src/orchestrator/fitness-evaluator.ts +0 -154
- package/src/orchestrator/mutation-strategies.ts +0 -214
package/src/index.ts
CHANGED
|
@@ -1,477 +1,356 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import * as readline from 'readline';
|
|
5
|
-
import * as dotenv from 'dotenv';
|
|
6
|
-
import * as os from 'os';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
console.log(
|
|
28
|
-
console.log('\x1b[
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
process.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
console.log(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
this.
|
|
156
|
-
this.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
process.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
} catch (err: any) {
|
|
358
|
-
console.error('\n\x1b[31m[Dashboard Failed]:\x1b[0m', err.message);
|
|
359
|
-
process.exit(1);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const isContinuous = args.includes('--continuous') || args.includes('-c');
|
|
364
|
-
if (isContinuous) {
|
|
365
|
-
try {
|
|
366
|
-
let cycles = 0; // infinite
|
|
367
|
-
const cyclesIdx = args.indexOf('--cycles');
|
|
368
|
-
if (cyclesIdx !== -1 && cyclesIdx + 1 < args.length) {
|
|
369
|
-
cycles = parseInt(args[cyclesIdx + 1], 10) || 0;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
let tokenBudget = 500000; // default 500k tokens
|
|
373
|
-
const budgetIdx = args.indexOf('--budget-tokens');
|
|
374
|
-
if (budgetIdx !== -1 && budgetIdx + 1 < args.length) {
|
|
375
|
-
tokenBudget = parseInt(args[budgetIdx + 1], 10) || 500000;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
console.log(`\n\x1b[33m[Running Continuous Self-Evolution Daemon Mode]\x1b[0m`);
|
|
379
|
-
console.log(`- Cycle Limit: ${cycles === 0 ? 'Infinite' : cycles}`);
|
|
380
|
-
console.log(`- Token Budget: ${tokenBudget} tokens\n`);
|
|
381
|
-
|
|
382
|
-
setupLogRedirection();
|
|
383
|
-
startDashboardServer();
|
|
384
|
-
startTelegramBot();
|
|
385
|
-
|
|
386
|
-
const engine = new AutogenesisEngine();
|
|
387
|
-
engine.setEffort(effort);
|
|
388
|
-
engine.codeactMode = codeactMode;
|
|
389
|
-
await engine.runContinuousEvolution(cycles, tokenBudget);
|
|
390
|
-
console.log('\n\x1b[32m[Continuous Self-Evolution Daemon Stopped Cleanly]\x1b[0m');
|
|
391
|
-
process.exit(0);
|
|
392
|
-
} catch (err: any) {
|
|
393
|
-
console.error('\n\x1b[31m[Continuous Self-Evolution Daemon Failed]:\x1b[0m', err.message);
|
|
394
|
-
process.exit(1);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (args[0] === '--refactor') {
|
|
399
|
-
try {
|
|
400
|
-
console.log(`\n\x1b[33m[Running Autonomous Refactoring Mode]\x1b[0m`);
|
|
401
|
-
const engine = new AutogenesisEngine();
|
|
402
|
-
engine.setEffort(effort);
|
|
403
|
-
engine.codeactMode = codeactMode;
|
|
404
|
-
await engine.runAutonomousRefactor();
|
|
405
|
-
console.log('\n\x1b[32m[Autonomous Refactoring Completed]\x1b[0m');
|
|
406
|
-
process.exit(0);
|
|
407
|
-
} catch (err: any) {
|
|
408
|
-
console.error('\n\x1b[31m[Autonomous Refactoring Failed]:\x1b[0m', err.message);
|
|
409
|
-
process.exit(1);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
if (args[0] === '--optimize-prompt') {
|
|
414
|
-
try {
|
|
415
|
-
console.log(`\n\x1b[33m[Running Prompt Optimization Benchmarking Mode]\x1b[0m`);
|
|
416
|
-
const engine = new AutogenesisEngine();
|
|
417
|
-
engine.setEffort(effort);
|
|
418
|
-
engine.codeactMode = codeactMode;
|
|
419
|
-
await engine.runPromptOptimization();
|
|
420
|
-
console.log('\n\x1b[32m[Prompt Optimization Completed]\x1b[0m');
|
|
421
|
-
process.exit(0);
|
|
422
|
-
} catch (err: any) {
|
|
423
|
-
console.error('\n\x1b[31m[Prompt Optimization Failed]:\x1b[0m', err.message);
|
|
424
|
-
process.exit(1);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (args[0] === '--dgm') {
|
|
429
|
-
try {
|
|
430
|
-
// Parse optional --generations N flag
|
|
431
|
-
let maxGenerations = 5;
|
|
432
|
-
const genIdx = args.indexOf('--generations');
|
|
433
|
-
if (genIdx !== -1 && genIdx + 1 < args.length) {
|
|
434
|
-
maxGenerations = parseInt(args[genIdx + 1], 10) || 5;
|
|
435
|
-
args.splice(genIdx, 2);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Task is everything after --dgm flag
|
|
439
|
-
const dgmTask = args.slice(1).join(' ') || 'Improve the codebase using open-ended evolution';
|
|
440
|
-
console.log(`\n\x1b[33m[Running DGM (Darwin Gödel Machine) Evolution Mode]\x1b[0m`);
|
|
441
|
-
console.log(`Task: "${dgmTask}" | Generations: ${maxGenerations}\n`);
|
|
442
|
-
const engine = new AutogenesisEngine();
|
|
443
|
-
engine.setEffort(effort);
|
|
444
|
-
engine.codeactMode = codeactMode;
|
|
445
|
-
await engine.runDGMEvolution(dgmTask, maxGenerations);
|
|
446
|
-
console.log('\n\x1b[32m[DGM Evolution Completed]\x1b[0m');
|
|
447
|
-
process.exit(0);
|
|
448
|
-
} catch (err: any) {
|
|
449
|
-
console.error('\n\x1b[31m[DGM Evolution Failed]:\x1b[0m', err.message);
|
|
450
|
-
process.exit(1);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Direct command execution
|
|
455
|
-
const task = args.join(' ');
|
|
456
|
-
try {
|
|
457
|
-
console.log(`Running direct task: "${task}" (CodeAct=${codeactMode})`);
|
|
458
|
-
const engine = new AutogenesisEngine();
|
|
459
|
-
engine.setEffort(effort);
|
|
460
|
-
engine.codeactMode = codeactMode;
|
|
461
|
-
await engine.runEvolutionStep(task);
|
|
462
|
-
console.log('\n\x1b[32m[Evolution Step Completed]\x1b[0m');
|
|
463
|
-
process.exit(0);
|
|
464
|
-
} catch (err: any) {
|
|
465
|
-
console.error('\n\x1b[31m[Evolution Failed]:\x1b[0m', err.message);
|
|
466
|
-
process.exit(1);
|
|
467
|
-
}
|
|
468
|
-
} else {
|
|
469
|
-
// Start interactive session
|
|
470
|
-
await startInteractiveCLI(effort, codeactMode);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
main().catch(err => {
|
|
475
|
-
console.error('Fatal error:', err);
|
|
476
|
-
process.exit(1);
|
|
477
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as readline from 'readline';
|
|
5
|
+
import * as dotenv from 'dotenv';
|
|
6
|
+
import * as os from 'os';
|
|
7
|
+
import { startDashboardServer } from './dashboard/server';
|
|
8
|
+
import { startTelegramBot } from './orchestrator/telegram-bot';
|
|
9
|
+
import { runTaskAgent } from './orchestrator/task-agent';
|
|
10
|
+
import { Spinner } from './tools/tui-tools';
|
|
11
|
+
|
|
12
|
+
// Load environment variables
|
|
13
|
+
dotenv.config();
|
|
14
|
+
|
|
15
|
+
function printHeader() {
|
|
16
|
+
const banner = `
|
|
17
|
+
\x1b[38;5;99m ___ _ _ _
|
|
18
|
+
/ __\\ | ___ ___ ___ __| | | | ___ ___ _ __
|
|
19
|
+
/ / | |/ _ \\/ __|/ _ \\/ _\` | | |/ _ \\ / _ \\| '_ \\
|
|
20
|
+
/ /___| | (_) \\__ \\ __/ (_| | | | (_) | (_) | |_) |
|
|
21
|
+
\\____/|_|\\___/|___/\\___|\\__,_| |_|\\___/ \\___/| .__/
|
|
22
|
+
|_| \x1b[0m`;
|
|
23
|
+
|
|
24
|
+
console.log(banner);
|
|
25
|
+
console.log('\x1b[37mTips for getting started:\x1b[0m');
|
|
26
|
+
console.log('\x1b[90m1. Ask questions, edit files, or run commands.\x1b[0m');
|
|
27
|
+
console.log('\x1b[90m2. Be specific for the best results.\x1b[0m');
|
|
28
|
+
console.log('\x1b[90m3. Type "exit" or "quit" to close the session.\x1b[0m\n');
|
|
29
|
+
|
|
30
|
+
const endpoint = process.env.ANTHROPIC_BASE_URL || 'default';
|
|
31
|
+
const model = process.env.ANTHROPIC_MODEL || 'mimo-v2.5-pro[1m]';
|
|
32
|
+
const subagent = process.env.CLAUDE_CODE_SUBAGENT_MODEL || 'mimo-v2.5-pro';
|
|
33
|
+
|
|
34
|
+
console.log(`\x1b[90mUsing: Endpoint: \x1b[38;5;86m${endpoint}\x1b[90m | Model: \x1b[38;5;153m${model}\x1b[90m | Subagent: \x1b[38;5;208m${subagent}\x1b[0m\n`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class ClosedLoopTUI {
|
|
38
|
+
private history: { role: 'user' | 'assistant'; content: string }[] = [];
|
|
39
|
+
private input = '';
|
|
40
|
+
private cursorIdx = 0;
|
|
41
|
+
private isThinking = false;
|
|
42
|
+
private effort: 'standard' | 'ultracode';
|
|
43
|
+
|
|
44
|
+
constructor(effort: 'standard' | 'ultracode' = 'standard') {
|
|
45
|
+
this.effort = effort;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
start() {
|
|
49
|
+
process.stdout.write('\x1b[?1049h'); // Enter alternate screen
|
|
50
|
+
process.stdout.write('\x1b[?25h'); // Show cursor
|
|
51
|
+
this.render();
|
|
52
|
+
|
|
53
|
+
readline.emitKeypressEvents(process.stdin);
|
|
54
|
+
if (process.stdin.isTTY) {
|
|
55
|
+
process.stdin.setRawMode(true);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
process.stdin.on('keypress', this.handleKeypress.bind(this));
|
|
59
|
+
process.stdout.on('resize', () => {
|
|
60
|
+
this.render();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
render() {
|
|
65
|
+
const rows = process.stdout.rows || 24;
|
|
66
|
+
const cols = process.stdout.columns || 80;
|
|
67
|
+
|
|
68
|
+
// Clear screen and move cursor to 1,1
|
|
69
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
70
|
+
|
|
71
|
+
// 1. Draw Banner & Info
|
|
72
|
+
const banner = `
|
|
73
|
+
\x1b[38;5;99m ___ _ _ _
|
|
74
|
+
/ __\\ | ___ ___ ___ __| | | | ___ ___ _ __
|
|
75
|
+
/ / | |/ _ \\/ __|/ _ \\/ _\` | | |/ _ \\ / _ \\| '_ \\
|
|
76
|
+
/ /___| | (_) \\__ \\ __/ (_| | | | (_) | (_) | |_) |
|
|
77
|
+
\\____/|_|\\___/|___/\\___|\\__,_| |_|\\___/ \\___/| .__/
|
|
78
|
+
|_| \x1b[0m`;
|
|
79
|
+
console.log(banner);
|
|
80
|
+
|
|
81
|
+
console.log('\x1b[37mTips for getting started:\x1b[0m');
|
|
82
|
+
console.log('\x1b[90m1. Ask questions, edit files, or run commands. 2. Be specific. 3. Type "exit" to quit.\x1b[0m');
|
|
83
|
+
|
|
84
|
+
const endpoint = process.env.ANTHROPIC_BASE_URL || 'default';
|
|
85
|
+
const model = process.env.ANTHROPIC_MODEL || 'mimo-v2.5-pro';
|
|
86
|
+
console.log(`\x1b[90mUsing: Endpoint: \x1b[38;5;86m${endpoint}\x1b[90m | Model: \x1b[38;5;153m${model}\x1b[0m\n`);
|
|
87
|
+
|
|
88
|
+
// 2. Draw History in the middle
|
|
89
|
+
const historyHeight = rows - 14;
|
|
90
|
+
const historyLines: string[] = [];
|
|
91
|
+
|
|
92
|
+
// Format history messages into lines
|
|
93
|
+
for (const msg of this.history) {
|
|
94
|
+
if (msg.role === 'user') {
|
|
95
|
+
historyLines.push(`\x1b[38;5;99mUser ❯\x1b[0m ${msg.content}`);
|
|
96
|
+
} else {
|
|
97
|
+
const lines = msg.content.split('\n');
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
historyLines.push(`\x1b[38;5;86mAgent ❯\x1b[0m ${line}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Slice history to fit historyHeight
|
|
105
|
+
const startIdx = Math.max(0, historyLines.length - historyHeight);
|
|
106
|
+
const visibleHistory = historyLines.slice(startIdx, startIdx + historyHeight);
|
|
107
|
+
|
|
108
|
+
for (let i = 0; i < historyHeight; i++) {
|
|
109
|
+
if (i < visibleHistory.length) {
|
|
110
|
+
console.log(visibleHistory[i]);
|
|
111
|
+
} else {
|
|
112
|
+
console.log(''); // empty line
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 3. Draw Bottom Input Box (3 lines)
|
|
117
|
+
const border = '─'.repeat(cols - 2);
|
|
118
|
+
console.log(`\x1b[38;5;99m╭${border}╮\x1b[0m`);
|
|
119
|
+
|
|
120
|
+
const promptStr = this.isThinking ? ' \x1b[36m⠋ Thinking...\x1b[0m' : ` › ${this.input}`;
|
|
121
|
+
const visiblePromptLen = this.isThinking ? 14 : 3 + this.input.length;
|
|
122
|
+
const padding = ' '.repeat(Math.max(0, cols - 4 - visiblePromptLen));
|
|
123
|
+
console.log(`\x1b[38;5;99m│\x1b[0m${promptStr}${padding}\x1b[38;5;99m│\x1b[0m`);
|
|
124
|
+
|
|
125
|
+
console.log(`\x1b[38;5;99m╰${border}╯\x1b[0m`);
|
|
126
|
+
|
|
127
|
+
// Position cursor
|
|
128
|
+
if (!this.isThinking) {
|
|
129
|
+
const cursorCol = 4 + this.cursorIdx;
|
|
130
|
+
const cursorRow = rows - 1;
|
|
131
|
+
process.stdout.write(`\x1b[${cursorRow};${cursorCol}H`);
|
|
132
|
+
} else {
|
|
133
|
+
process.stdout.write('\x1b[?25l');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async handleKeypress(str: string, key: any) {
|
|
138
|
+
if (this.isThinking) return;
|
|
139
|
+
|
|
140
|
+
if (key.ctrl && key.name === 'c') {
|
|
141
|
+
this.exit();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (key.name === 'return') {
|
|
145
|
+
const cmd = this.input.trim();
|
|
146
|
+
if (!cmd) return;
|
|
147
|
+
|
|
148
|
+
if (cmd.toLowerCase() === 'exit' || cmd.toLowerCase() === 'quit') {
|
|
149
|
+
this.exit();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.history.push({ role: 'user', content: cmd });
|
|
153
|
+
this.input = '';
|
|
154
|
+
this.cursorIdx = 0;
|
|
155
|
+
this.isThinking = true;
|
|
156
|
+
this.render();
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
// Temporarily leave alternate screen buffer for execution
|
|
160
|
+
process.stdout.write('\x1b[?1049l');
|
|
161
|
+
process.stdout.write('\x1b[?25h');
|
|
162
|
+
if (process.stdin.isTTY) {
|
|
163
|
+
process.stdin.setRawMode(false);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
console.log(`\n\x1b[35;1m=================== EXECUTION MODE ===================\x1b[0m`);
|
|
167
|
+
console.log(`\x1b[90mRunning Task:\x1b[0m ${cmd}\n`);
|
|
168
|
+
|
|
169
|
+
const response = await runTaskAgent(cmd, {
|
|
170
|
+
role: 'coder_codeact' as any,
|
|
171
|
+
effort: this.effort,
|
|
172
|
+
history: this.history.slice(0, -1)
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Re-enter TUI and raw mode
|
|
176
|
+
process.stdout.write('\x1b[?1049h');
|
|
177
|
+
if (process.stdin.isTTY) {
|
|
178
|
+
process.stdin.setRawMode(true);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.history.push({ role: 'assistant', content: response.result });
|
|
182
|
+
} catch (err: any) {
|
|
183
|
+
// Re-enter TUI and raw mode
|
|
184
|
+
process.stdout.write('\x1b[?1049h');
|
|
185
|
+
if (process.stdin.isTTY) {
|
|
186
|
+
process.stdin.setRawMode(true);
|
|
187
|
+
}
|
|
188
|
+
this.history.push({ role: 'assistant', content: `Error: ${err.message}` });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this.isThinking = false;
|
|
192
|
+
process.stdout.write('\x1b[?25h');
|
|
193
|
+
this.render();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (key.name === 'backspace') {
|
|
198
|
+
if (this.cursorIdx > 0) {
|
|
199
|
+
this.input = this.input.slice(0, this.cursorIdx - 1) + this.input.slice(this.cursorIdx);
|
|
200
|
+
this.cursorIdx--;
|
|
201
|
+
this.render();
|
|
202
|
+
}
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (key.name === 'left') {
|
|
207
|
+
if (this.cursorIdx > 0) {
|
|
208
|
+
this.cursorIdx--;
|
|
209
|
+
this.render();
|
|
210
|
+
}
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (key.name === 'right') {
|
|
215
|
+
if (this.cursorIdx < this.input.length) {
|
|
216
|
+
this.cursorIdx++;
|
|
217
|
+
this.render();
|
|
218
|
+
}
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Handle normal character inputs
|
|
223
|
+
if (str && str.length === 1 && !key.ctrl && !key.meta) {
|
|
224
|
+
this.input = this.input.slice(0, this.cursorIdx) + str + this.input.slice(this.cursorIdx);
|
|
225
|
+
this.cursorIdx++;
|
|
226
|
+
this.render();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
exit() {
|
|
231
|
+
process.stdout.write('\x1b[?1049l');
|
|
232
|
+
process.stdout.write('\x1b[?25h');
|
|
233
|
+
console.log('Goodbye!');
|
|
234
|
+
process.exit(0);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function startInteractiveCLI(effort: 'standard' | 'ultracode' = 'standard', codeactMode = false) {
|
|
239
|
+
const tui = new ClosedLoopTUI(effort);
|
|
240
|
+
tui.start();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function checkAndPromptAPIKey(): Promise<string> {
|
|
244
|
+
if (process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN) {
|
|
245
|
+
return process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN || '';
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const configPath = path.join(os.homedir(), '.closed-loop.json');
|
|
249
|
+
if (fs.existsSync(configPath)) {
|
|
250
|
+
try {
|
|
251
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
252
|
+
if (config.apiKey) {
|
|
253
|
+
process.env.ANTHROPIC_API_KEY = config.apiKey;
|
|
254
|
+
return config.apiKey;
|
|
255
|
+
}
|
|
256
|
+
} catch (e) {}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
console.log('\n\x1b[33m🔑 Anthropic API key not found in environment or local .env file.\x1b[0m');
|
|
260
|
+
console.log('To run this assistant, please configure your API key below.');
|
|
261
|
+
|
|
262
|
+
const rl = readline.createInterface({
|
|
263
|
+
input: process.stdin,
|
|
264
|
+
output: process.stdout
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const question = (query: string): Promise<string> => {
|
|
268
|
+
return new Promise(resolve => rl.question(query, resolve));
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const key = await question('\x1b[35mEnter your Anthropic API Key:\x1b[0m ');
|
|
272
|
+
rl.close();
|
|
273
|
+
|
|
274
|
+
const trimmedKey = key.trim();
|
|
275
|
+
if (!trimmedKey) {
|
|
276
|
+
console.error('\x1b[31mError: API Key cannot be empty.\x1b[0m');
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
fs.writeFileSync(configPath, JSON.stringify({ apiKey: trimmedKey }, null, 2), 'utf8');
|
|
282
|
+
console.log(`\x1b[32m✔ Saved API key to ${configPath}\x1b[0m\n`);
|
|
283
|
+
} catch (err: any) {
|
|
284
|
+
console.warn(`\x1b[33mWarning: Could not save API key to config: ${err.message}\x1b[0m`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
process.env.ANTHROPIC_API_KEY = trimmedKey;
|
|
288
|
+
return trimmedKey;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function main() {
|
|
292
|
+
// Ensure API Key is configured before running any agent logic
|
|
293
|
+
await checkAndPromptAPIKey();
|
|
294
|
+
|
|
295
|
+
const args = process.argv.slice(2);
|
|
296
|
+
|
|
297
|
+
// Parse codeact option
|
|
298
|
+
let codeactMode = false;
|
|
299
|
+
const codeactIdx = args.findIndex(arg => arg === '--codeact' || arg === '--openhands');
|
|
300
|
+
if (codeactIdx !== -1) {
|
|
301
|
+
codeactMode = true;
|
|
302
|
+
args.splice(codeactIdx, 1);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Parse effort option
|
|
306
|
+
let effort: 'standard' | 'ultracode' = 'standard';
|
|
307
|
+
const effortIdx = args.findIndex(arg => arg === '--effort' || arg === '-e');
|
|
308
|
+
if (effortIdx !== -1 && effortIdx + 1 < args.length) {
|
|
309
|
+
const val = args[effortIdx + 1].toLowerCase();
|
|
310
|
+
if (val === 'ultracode' || val === 'max') {
|
|
311
|
+
effort = 'ultracode';
|
|
312
|
+
}
|
|
313
|
+
// Remove --effort and its value from args
|
|
314
|
+
args.splice(effortIdx, 2);
|
|
315
|
+
} else {
|
|
316
|
+
const envEffort = process.env.CLAUDE_CODE_EFFORT_LEVEL || 'standard';
|
|
317
|
+
if (envEffort === 'max' || envEffort === 'ultracode') {
|
|
318
|
+
effort = 'ultracode';
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (args.length > 0) {
|
|
323
|
+
if (args[0] === '--telegram') {
|
|
324
|
+
try {
|
|
325
|
+
console.log(`\n\x1b[33m[Running Standalone Telegram ChatOps Mode]\x1b[0m`);
|
|
326
|
+
startTelegramBot();
|
|
327
|
+
return;
|
|
328
|
+
} catch (err: any) {
|
|
329
|
+
console.error('\n\x1b[31m[Telegram ChatOps Failed]:\x1b[0m', err.message);
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (args[0] === '--dashboard') {
|
|
335
|
+
try {
|
|
336
|
+
console.log(`\n\x1b[33m[Running Standalone Dashboard Monitor Mode]\x1b[0m`);
|
|
337
|
+
startDashboardServer();
|
|
338
|
+
return;
|
|
339
|
+
} catch (err: any) {
|
|
340
|
+
console.error('\n\x1b[31m[Dashboard Failed]:\x1b[0m', err.message);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
console.log(`Unknown option: ${args[0]}. Running interactive session...`);
|
|
346
|
+
await startInteractiveCLI(effort, codeactMode);
|
|
347
|
+
} else {
|
|
348
|
+
// Start interactive session
|
|
349
|
+
await startInteractiveCLI(effort, codeactMode);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
main().catch(err => {
|
|
354
|
+
console.error('Fatal error:', err);
|
|
355
|
+
process.exit(1);
|
|
356
|
+
});
|