erosolar-cli 2.1.10 → 2.1.12
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/core/preferences.d.ts +2 -0
- package/dist/core/preferences.d.ts.map +1 -1
- package/dist/core/preferences.js +13 -0
- package/dist/core/preferences.js.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.js +16 -5
- package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +10 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +331 -161
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/ui/PromptController.d.ts +13 -0
- package/dist/ui/PromptController.d.ts.map +1 -1
- package/dist/ui/PromptController.js +6 -0
- package/dist/ui/PromptController.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +13 -0
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +113 -12
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/shortcutsHelp.d.ts.map +1 -1
- package/dist/ui/shortcutsHelp.js +1 -0
- package/dist/ui/shortcutsHelp.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { stdin as input, stdout as output, exit } from 'node:process';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
2
3
|
import { exec } from 'node:child_process';
|
|
3
4
|
import { promisify } from 'node:util';
|
|
4
5
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -126,6 +127,7 @@ export class InteractiveShell {
|
|
|
126
127
|
autosaveEnabled;
|
|
127
128
|
autoContinueEnabled;
|
|
128
129
|
verificationEnabled = false;
|
|
130
|
+
criticalApprovalMode = 'auto';
|
|
129
131
|
editGuardMode = 'display-edits';
|
|
130
132
|
pendingPermissionInput = null;
|
|
131
133
|
pendingHistoryLoad = null;
|
|
@@ -188,6 +190,7 @@ export class InteractiveShell {
|
|
|
188
190
|
this.thinkingMode = this.sessionPreferences.thinkingMode;
|
|
189
191
|
this.autosaveEnabled = this.sessionPreferences.autosave;
|
|
190
192
|
this.autoContinueEnabled = this.sessionPreferences.autoContinue;
|
|
193
|
+
this.criticalApprovalMode = this.sessionPreferences.criticalApprovalMode;
|
|
191
194
|
const featureFlags = loadFeatureFlags();
|
|
192
195
|
this.verificationEnabled = featureFlags.verification === true;
|
|
193
196
|
this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
|
|
@@ -235,6 +238,11 @@ export class InteractiveShell {
|
|
|
235
238
|
description: 'Show available and loaded plugins',
|
|
236
239
|
category: 'configuration',
|
|
237
240
|
});
|
|
241
|
+
this.slashCommands.push({
|
|
242
|
+
command: '/approvals',
|
|
243
|
+
description: 'Switch between auto and approval mode for high-impact actions',
|
|
244
|
+
category: 'configuration',
|
|
245
|
+
});
|
|
238
246
|
this.slashCommands.push({
|
|
239
247
|
command: '/offsec',
|
|
240
248
|
description: 'AlphaZero offensive security run (start/status/next)',
|
|
@@ -272,6 +280,7 @@ export class InteractiveShell {
|
|
|
272
280
|
onToggleAutoContinue: () => this.toggleAutoContinueMode(),
|
|
273
281
|
onToggleThinking: () => this.cycleThinkingMode(),
|
|
274
282
|
onClearContext: () => this.handleClearContext(),
|
|
283
|
+
onToggleCriticalApproval: () => this.toggleCriticalApprovalMode('shortcut'),
|
|
275
284
|
});
|
|
276
285
|
// Share renderer with Display so all output flows through the unified queue
|
|
277
286
|
this.renderer = this.terminalInput.getRenderer();
|
|
@@ -280,6 +289,8 @@ export class InteractiveShell {
|
|
|
280
289
|
this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
|
|
281
290
|
this.setupStatusTracking();
|
|
282
291
|
this.refreshContextGauge();
|
|
292
|
+
// Prime renderer state before it first paints
|
|
293
|
+
this.refreshControlBar();
|
|
283
294
|
// Start terminal input (sets up handlers)
|
|
284
295
|
this.terminalInput.start();
|
|
285
296
|
// Allow planning tools (e.g., ProposePlan) to open the interactive approval UI just like Codex CLI
|
|
@@ -287,7 +298,6 @@ export class InteractiveShell {
|
|
|
287
298
|
// Set up command autocomplete with all slash commands
|
|
288
299
|
this.setupCommandAutocomplete();
|
|
289
300
|
// Render chat box immediately using the streaming UI lifecycle
|
|
290
|
-
this.refreshControlBar();
|
|
291
301
|
this.syncRendererInput();
|
|
292
302
|
this.renderer?.render();
|
|
293
303
|
this.rebuildAgent();
|
|
@@ -394,6 +404,38 @@ export class InteractiveShell {
|
|
|
394
404
|
const intro = `${theme.ui.muted('To get started, describe a task or try one of these commands:')}`;
|
|
395
405
|
return `${intro}\n\n${body}`;
|
|
396
406
|
}
|
|
407
|
+
describeSessionLabel() {
|
|
408
|
+
if (this.sessionRestoreConfig.mode === 'autosave')
|
|
409
|
+
return 'resume (autosave)';
|
|
410
|
+
if (this.sessionRestoreConfig.mode === 'session-id')
|
|
411
|
+
return 'resume (id)';
|
|
412
|
+
return 'fresh';
|
|
413
|
+
}
|
|
414
|
+
describeEditGuardMode() {
|
|
415
|
+
switch (this.editGuardMode) {
|
|
416
|
+
case 'display-edits':
|
|
417
|
+
return 'show diffs';
|
|
418
|
+
case 'require-approval':
|
|
419
|
+
return 'ask before write';
|
|
420
|
+
case 'block-writes':
|
|
421
|
+
return 'no writes';
|
|
422
|
+
case 'ask-permission':
|
|
423
|
+
return 'permission gate';
|
|
424
|
+
case 'plan':
|
|
425
|
+
return 'plan then apply';
|
|
426
|
+
default:
|
|
427
|
+
return this.editGuardMode;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
abbreviatePath(pathValue) {
|
|
431
|
+
if (!pathValue)
|
|
432
|
+
return '—';
|
|
433
|
+
const home = homedir();
|
|
434
|
+
if (home && pathValue.startsWith(home)) {
|
|
435
|
+
return pathValue.replace(home, '~');
|
|
436
|
+
}
|
|
437
|
+
return pathValue;
|
|
438
|
+
}
|
|
397
439
|
async checkAndShowUpdates() {
|
|
398
440
|
try {
|
|
399
441
|
const { checkForUpdates, formatUpdateNotification } = await import('../core/updateChecker.js');
|
|
@@ -416,13 +458,7 @@ export class InteractiveShell {
|
|
|
416
458
|
const workspace = this.workingDir;
|
|
417
459
|
const profile = this.profileLabel;
|
|
418
460
|
const thinkingLabel = (this.thinkingMode || 'off').toString();
|
|
419
|
-
const sessionLabel = (
|
|
420
|
-
if (this.sessionRestoreConfig.mode === 'autosave')
|
|
421
|
-
return 'resume (autosave)';
|
|
422
|
-
if (this.sessionRestoreConfig.mode === 'session-id')
|
|
423
|
-
return 'resume (id)';
|
|
424
|
-
return 'fresh';
|
|
425
|
-
})();
|
|
461
|
+
const sessionLabel = this.describeSessionLabel();
|
|
426
462
|
const terminalWidth = typeof process.stdout.columns === 'number' && process.stdout.columns > 0
|
|
427
463
|
? process.stdout.columns
|
|
428
464
|
: 80;
|
|
@@ -466,25 +502,11 @@ export class InteractiveShell {
|
|
|
466
502
|
const rightColored = `${theme.primary(rightLabel)}: ${theme.ui.muted(rightDisplay)}`;
|
|
467
503
|
return padLine(`${leftColored}${gap}${rightColored}`);
|
|
468
504
|
};
|
|
469
|
-
const editGuardLabel = (
|
|
470
|
-
switch (this.editGuardMode) {
|
|
471
|
-
case 'display-edits':
|
|
472
|
-
return 'show diffs';
|
|
473
|
-
case 'require-approval':
|
|
474
|
-
return 'ask before write';
|
|
475
|
-
case 'block-writes':
|
|
476
|
-
return 'no writes';
|
|
477
|
-
case 'ask-permission':
|
|
478
|
-
return 'permission gate';
|
|
479
|
-
case 'plan':
|
|
480
|
-
return 'plan then apply';
|
|
481
|
-
default:
|
|
482
|
-
return this.editGuardMode;
|
|
483
|
-
}
|
|
484
|
-
})();
|
|
505
|
+
const editGuardLabel = this.describeEditGuardMode();
|
|
485
506
|
const statusParts = [
|
|
486
507
|
theme.success('Ready'),
|
|
487
508
|
`${theme.primary('Auto')}: ${this.autoContinueEnabled ? theme.success('on') : theme.ui.muted('off')}`,
|
|
509
|
+
`${theme.primary('Approvals')}: ${this.criticalApprovalMode === 'auto' ? theme.ui.muted('auto') : theme.warning('ask')}`,
|
|
488
510
|
`${theme.primary('Verify')}: ${this.verificationEnabled ? theme.success('on') : theme.ui.muted('off')}`,
|
|
489
511
|
`${theme.primary('Thinking')}: ${theme.info(thinkingLabel)}`,
|
|
490
512
|
`${theme.primary('Autosave')}: ${this.autosaveEnabled ? theme.success('on') : theme.ui.muted('off')}`,
|
|
@@ -644,6 +666,7 @@ export class InteractiveShell {
|
|
|
644
666
|
'/bug',
|
|
645
667
|
'/changes', '/summary',
|
|
646
668
|
'/export',
|
|
669
|
+
'/approvals',
|
|
647
670
|
// Configuration menus
|
|
648
671
|
'/model', '/models',
|
|
649
672
|
'/secrets',
|
|
@@ -716,7 +739,7 @@ export class InteractiveShell {
|
|
|
716
739
|
display.showSystemMessage('✏️ Display edits mode enabled.');
|
|
717
740
|
}
|
|
718
741
|
}
|
|
719
|
-
this.
|
|
742
|
+
this.refreshControlBar();
|
|
720
743
|
}
|
|
721
744
|
toggleVerificationMode() {
|
|
722
745
|
this.setVerificationMode(!this.verificationEnabled, 'shortcut');
|
|
@@ -733,6 +756,23 @@ export class InteractiveShell {
|
|
|
733
756
|
: '⏭️ Verification off. Skipping auto-tests until re-enabled. (Ctrl+Shift+V to toggle; defaults to off unless enabled via /features)';
|
|
734
757
|
display.showSystemMessage(message);
|
|
735
758
|
}
|
|
759
|
+
toggleCriticalApprovalMode(source) {
|
|
760
|
+
const nextMode = this.criticalApprovalMode === 'auto' ? 'approval' : 'auto';
|
|
761
|
+
this.setCriticalApprovalMode(nextMode, source);
|
|
762
|
+
}
|
|
763
|
+
setCriticalApprovalMode(mode, source) {
|
|
764
|
+
const changed = this.criticalApprovalMode !== mode;
|
|
765
|
+
this.criticalApprovalMode = mode;
|
|
766
|
+
saveSessionPreferences({ criticalApprovalMode: mode });
|
|
767
|
+
this.refreshControlBar();
|
|
768
|
+
if (!changed && source === 'shortcut') {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
const message = mode === 'auto'
|
|
772
|
+
? '⚡ High-impact actions will run automatically. (Ctrl+Shift+A to toggle; use /approvals auto to persist)'
|
|
773
|
+
: '🛡️ High-impact actions now require your approval. (Ctrl+Shift+A to toggle; use /approvals ask to persist)';
|
|
774
|
+
display.showSystemMessage(message);
|
|
775
|
+
}
|
|
736
776
|
toggleAutoContinueMode() {
|
|
737
777
|
this.setAutoContinueMode(!this.autoContinueEnabled, 'shortcut');
|
|
738
778
|
}
|
|
@@ -1208,6 +1248,78 @@ export class InteractiveShell {
|
|
|
1208
1248
|
display.showWarning('Invalid input. Enter a step number, command (go/cancel/all/none), or your own solution.');
|
|
1209
1249
|
this.syncRendererInput();
|
|
1210
1250
|
}
|
|
1251
|
+
async runWithCriticalApproval(label, detail, action) {
|
|
1252
|
+
if (this.criticalApprovalMode === 'auto') {
|
|
1253
|
+
await action();
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
if (this.pendingInteraction && this.pendingInteraction.type === 'critical-approval') {
|
|
1257
|
+
display.showWarning('Finish the pending approval first.');
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
await new Promise((resolve) => {
|
|
1261
|
+
this.pendingInteraction = {
|
|
1262
|
+
type: 'critical-approval',
|
|
1263
|
+
label,
|
|
1264
|
+
detail,
|
|
1265
|
+
onApprove: async () => {
|
|
1266
|
+
this.pendingInteraction = null;
|
|
1267
|
+
try {
|
|
1268
|
+
await action();
|
|
1269
|
+
}
|
|
1270
|
+
catch (error) {
|
|
1271
|
+
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
1272
|
+
}
|
|
1273
|
+
resolve();
|
|
1274
|
+
},
|
|
1275
|
+
onCancel: () => {
|
|
1276
|
+
this.pendingInteraction = null;
|
|
1277
|
+
display.showInfo('Action cancelled.');
|
|
1278
|
+
resolve();
|
|
1279
|
+
},
|
|
1280
|
+
};
|
|
1281
|
+
this.showCriticalApprovalPrompt(label, detail);
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
showCriticalApprovalPrompt(label, detail) {
|
|
1285
|
+
const lines = [];
|
|
1286
|
+
lines.push(theme.gradient.primary('⚠️ Approval required'));
|
|
1287
|
+
lines.push('');
|
|
1288
|
+
lines.push(theme.bold(label));
|
|
1289
|
+
if (detail) {
|
|
1290
|
+
lines.push(theme.ui.muted(detail));
|
|
1291
|
+
}
|
|
1292
|
+
lines.push('');
|
|
1293
|
+
lines.push('Type "yes" to proceed or "no" to cancel.');
|
|
1294
|
+
display.showSystemMessage(lines.join('\n'));
|
|
1295
|
+
this.syncRendererInput();
|
|
1296
|
+
}
|
|
1297
|
+
async handleCriticalApprovalInput(input) {
|
|
1298
|
+
const pending = this.pendingInteraction;
|
|
1299
|
+
if (!pending || pending.type !== 'critical-approval') {
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const normalized = input.trim().toLowerCase();
|
|
1303
|
+
if (!normalized) {
|
|
1304
|
+
display.showWarning('Enter "yes" to proceed or "no" to cancel.');
|
|
1305
|
+
this.syncRendererInput();
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
if (normalized === 'yes' || normalized === 'y') {
|
|
1309
|
+
this.pendingInteraction = null;
|
|
1310
|
+
await pending.onApprove();
|
|
1311
|
+
this.syncRendererInput();
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
if (normalized === 'no' || normalized === 'n' || normalized === 'cancel' || normalized === 'c') {
|
|
1315
|
+
this.pendingInteraction = null;
|
|
1316
|
+
pending.onCancel?.();
|
|
1317
|
+
this.syncRendererInput();
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
display.showWarning('Please respond with "yes" to proceed or "no" to cancel.');
|
|
1321
|
+
this.syncRendererInput();
|
|
1322
|
+
}
|
|
1211
1323
|
setupHandlers() {
|
|
1212
1324
|
// Handle terminal resize
|
|
1213
1325
|
output.on('resize', () => {
|
|
@@ -1323,8 +1435,21 @@ export class InteractiveShell {
|
|
|
1323
1435
|
autoContinueEnabled: this.autoContinueEnabled,
|
|
1324
1436
|
verificationHotkey: 'ctrl+shift+v',
|
|
1325
1437
|
autoContinueHotkey: 'ctrl+shift+c',
|
|
1326
|
-
thinkingModeLabel: this.thinkingMode
|
|
1438
|
+
thinkingModeLabel: (this.thinkingMode || 'off').toString(),
|
|
1327
1439
|
thinkingHotkey: 'tab',
|
|
1440
|
+
criticalApprovalMode: this.criticalApprovalMode,
|
|
1441
|
+
criticalApprovalHotkey: 'ctrl+shift+a',
|
|
1442
|
+
});
|
|
1443
|
+
const workspaceDisplay = this.abbreviatePath(this.workingDir);
|
|
1444
|
+
this.terminalInput.setChromeMeta({
|
|
1445
|
+
profile: this.profileLabel,
|
|
1446
|
+
workspace: workspaceDisplay,
|
|
1447
|
+
directory: workspaceDisplay,
|
|
1448
|
+
writes: this.describeEditGuardMode(),
|
|
1449
|
+
sessionLabel: this.describeSessionLabel(),
|
|
1450
|
+
thinkingLabel: (this.thinkingMode || 'off').toString(),
|
|
1451
|
+
autosave: this.autosaveEnabled,
|
|
1452
|
+
version: this.version,
|
|
1328
1453
|
});
|
|
1329
1454
|
this.refreshStatusLine();
|
|
1330
1455
|
this.syncRendererInput();
|
|
@@ -1399,15 +1524,6 @@ export class InteractiveShell {
|
|
|
1399
1524
|
* All three can be shown simultaneously (Erosolar-CLI style).
|
|
1400
1525
|
*/
|
|
1401
1526
|
refreshStatusLine(forceRender = false) {
|
|
1402
|
-
const statusText = this.formatStatusLine(this.statusLineState);
|
|
1403
|
-
// Compose streaming/override/base status into a single line so the prompt
|
|
1404
|
-
// looks the same before and during streaming.
|
|
1405
|
-
this.terminalInput.setStatusLine({
|
|
1406
|
-
streaming: this.streamingStatusLabel,
|
|
1407
|
-
override: this.statusMessageOverride,
|
|
1408
|
-
main: statusText,
|
|
1409
|
-
});
|
|
1410
|
-
// Surface meta header (elapsed + context usage) above the divider
|
|
1411
1527
|
const elapsedSeconds = this.getAiRuntimeSeconds();
|
|
1412
1528
|
const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
|
|
1413
1529
|
const tokensUsed = this.latestTokenUsage.used;
|
|
@@ -1840,6 +1956,9 @@ export class InteractiveShell {
|
|
|
1840
1956
|
case 'plan-approval':
|
|
1841
1957
|
await this.handlePlanApprovalInput(input);
|
|
1842
1958
|
return true;
|
|
1959
|
+
case 'critical-approval':
|
|
1960
|
+
await this.handleCriticalApprovalInput(input);
|
|
1961
|
+
return true;
|
|
1843
1962
|
default:
|
|
1844
1963
|
return false;
|
|
1845
1964
|
}
|
|
@@ -1859,6 +1978,9 @@ export class InteractiveShell {
|
|
|
1859
1978
|
case '/features':
|
|
1860
1979
|
this.showFeaturesMenu(input);
|
|
1861
1980
|
break;
|
|
1981
|
+
case '/approvals':
|
|
1982
|
+
this.handleApprovalsCommand(input);
|
|
1983
|
+
break;
|
|
1862
1984
|
case '/learn':
|
|
1863
1985
|
this.showLearningStatus(input);
|
|
1864
1986
|
break;
|
|
@@ -2042,6 +2164,7 @@ export class InteractiveShell {
|
|
|
2042
2164
|
` ${theme.info('Shift+Tab')} ${theme.ui.muted('Toggle edit mode (auto/ask)')}`,
|
|
2043
2165
|
` ${theme.info('Option+V')} ${theme.ui.muted('Toggle verification')}`,
|
|
2044
2166
|
` ${theme.info('Option+C')} ${theme.ui.muted('Toggle auto-continue')}`,
|
|
2167
|
+
` ${theme.info('Ctrl+Shift+A')} ${theme.ui.muted('Toggle approvals for high-impact actions')}`,
|
|
2045
2168
|
` ${theme.info('Option+T')} ${theme.ui.muted('Cycle thinking mode')}`,
|
|
2046
2169
|
` ${theme.info('Option+E')} ${theme.ui.muted('Toggle edit permission mode')}`,
|
|
2047
2170
|
` ${theme.info('Option+X')} ${theme.ui.muted('Clear/compact context')}`,
|
|
@@ -2606,40 +2729,43 @@ export class InteractiveShell {
|
|
|
2606
2729
|
return;
|
|
2607
2730
|
}
|
|
2608
2731
|
if (subcommand === 'apply') {
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2732
|
+
const runApply = async () => {
|
|
2733
|
+
display.showSystemMessage(theme.gradient.primary('🚀 Running Self-Improvement Cycle...'));
|
|
2734
|
+
display.showSystemMessage('');
|
|
2735
|
+
try {
|
|
2736
|
+
const result = await runSelfImprovementCycle(this.workingDir, {
|
|
2737
|
+
maxChanges: 3,
|
|
2738
|
+
minConfidence: 0.7,
|
|
2739
|
+
runTests: true,
|
|
2740
|
+
autoCommit: true,
|
|
2741
|
+
});
|
|
2742
|
+
const lines = [];
|
|
2743
|
+
lines.push(theme.bold('Results:'));
|
|
2744
|
+
lines.push(result.summary);
|
|
2745
|
+
lines.push('');
|
|
2746
|
+
if (result.applied > 0) {
|
|
2747
|
+
lines.push(theme.success(`✅ Applied ${result.applied} improvements!`));
|
|
2748
|
+
for (const r of result.results.filter(r => r.success)) {
|
|
2749
|
+
lines.push(` - Files: ${r.filesChanged.join(', ')}`);
|
|
2750
|
+
if (r.commitHash) {
|
|
2751
|
+
lines.push(` Commit: ${theme.ui.muted(r.commitHash)}`);
|
|
2752
|
+
lines.push(` Rollback: ${theme.dim(r.rollbackCommand ?? '')}`);
|
|
2753
|
+
}
|
|
2629
2754
|
}
|
|
2630
2755
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2756
|
+
else {
|
|
2757
|
+
lines.push(theme.warning('No improvements were applied.'));
|
|
2758
|
+
for (const r of result.results.filter(r => !r.success)) {
|
|
2759
|
+
lines.push(` ${theme.error('✗')} ${r.error}`);
|
|
2760
|
+
}
|
|
2636
2761
|
}
|
|
2762
|
+
display.showSystemMessage(lines.join('\n'));
|
|
2637
2763
|
}
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2764
|
+
catch (error) {
|
|
2765
|
+
display.showError(`Self-improvement failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2766
|
+
}
|
|
2767
|
+
};
|
|
2768
|
+
await this.runWithCriticalApproval('Apply self-improvement changes', 'Runs automated fixes with tests and commits before continuing.', runApply);
|
|
2643
2769
|
return;
|
|
2644
2770
|
}
|
|
2645
2771
|
if (subcommand === 'dry-run') {
|
|
@@ -2668,7 +2794,7 @@ export class InteractiveShell {
|
|
|
2668
2794
|
return;
|
|
2669
2795
|
}
|
|
2670
2796
|
if (subcommand === 'auto') {
|
|
2671
|
-
|
|
2797
|
+
await this.runWithCriticalApproval('Start autonomous improvement mode', 'Allows the CLI to apply and validate changes until stopped.', () => this.runAutonomousImprovementMode());
|
|
2672
2798
|
return;
|
|
2673
2799
|
}
|
|
2674
2800
|
if (subcommand === 'stop') {
|
|
@@ -2677,13 +2803,15 @@ export class InteractiveShell {
|
|
|
2677
2803
|
return;
|
|
2678
2804
|
}
|
|
2679
2805
|
if (subcommand === 'rollback') {
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2806
|
+
await this.runWithCriticalApproval('Emergency rollback of self-improvement', 'Restores the last checkpoint created by the improvement engine.', () => {
|
|
2807
|
+
const result = emergencyRollback(this.workingDir);
|
|
2808
|
+
if (result.success) {
|
|
2809
|
+
display.showSuccess(result.message);
|
|
2810
|
+
}
|
|
2811
|
+
else {
|
|
2812
|
+
display.showError(result.message);
|
|
2813
|
+
}
|
|
2814
|
+
});
|
|
2687
2815
|
return;
|
|
2688
2816
|
}
|
|
2689
2817
|
if (subcommand === 'status') {
|
|
@@ -2886,64 +3014,67 @@ export class InteractiveShell {
|
|
|
2886
3014
|
return;
|
|
2887
3015
|
}
|
|
2888
3016
|
if (subcommand === 'start') {
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
3017
|
+
const startEvolution = async () => {
|
|
3018
|
+
display.showSystemMessage(theme.gradient.primary('🧬 Starting Self-Evolution Mode'));
|
|
3019
|
+
display.showSystemMessage('');
|
|
3020
|
+
display.showSystemMessage(theme.bold('Safety Features:'));
|
|
3021
|
+
display.showSystemMessage(' • Git checkpoint created before starting');
|
|
3022
|
+
display.showSystemMessage(' • Each change validated with build + tests');
|
|
3023
|
+
display.showSystemMessage(' • Auto-rollback on failures');
|
|
3024
|
+
display.showSystemMessage(' • Auto-relaunch with improved code');
|
|
3025
|
+
display.showSystemMessage(' • Press Ctrl+C to stop gracefully');
|
|
3026
|
+
display.showSystemMessage('');
|
|
3027
|
+
try {
|
|
3028
|
+
const result = await runSelfEvolution(this.workingDir, {
|
|
3029
|
+
maxIterations: 50,
|
|
3030
|
+
minConfidence: 0.8,
|
|
3031
|
+
runTests: true,
|
|
3032
|
+
autoRelaunch: true,
|
|
3033
|
+
}, {
|
|
3034
|
+
onStart: () => {
|
|
3035
|
+
display.showInfo('Evolution started. Creating checkpoint...');
|
|
3036
|
+
},
|
|
3037
|
+
onIteration: (iteration, issues) => {
|
|
3038
|
+
display.showSystemMessage(`\n[Iteration ${iteration}] Found ${issues.length} high-confidence issues`);
|
|
3039
|
+
},
|
|
3040
|
+
onFix: (issue, success) => {
|
|
3041
|
+
if (success) {
|
|
3042
|
+
display.showSuccess(`Fixed: ${issue.description.slice(0, 50)}`);
|
|
3043
|
+
}
|
|
3044
|
+
else {
|
|
3045
|
+
display.showWarning(`Failed: ${issue.description.slice(0, 50)}`);
|
|
3046
|
+
}
|
|
3047
|
+
},
|
|
3048
|
+
onRelaunch: () => {
|
|
3049
|
+
display.showSystemMessage('');
|
|
3050
|
+
display.showSystemMessage(theme.gradient.primary('🔄 Relaunching with improved code...'));
|
|
3051
|
+
},
|
|
3052
|
+
onComplete: (result) => {
|
|
3053
|
+
display.showSystemMessage('');
|
|
3054
|
+
display.showSuccess(`Evolution complete! Fixed ${result.issuesFixed} issues.`);
|
|
3055
|
+
},
|
|
3056
|
+
onError: (error) => {
|
|
3057
|
+
display.showError(`Evolution error: ${error}`);
|
|
3058
|
+
},
|
|
3059
|
+
});
|
|
3060
|
+
const lines = [];
|
|
3061
|
+
lines.push('');
|
|
3062
|
+
lines.push(theme.bold('Evolution Result:'));
|
|
3063
|
+
lines.push(` Success: ${result.success ? theme.success('Yes') : theme.error('No')}`);
|
|
3064
|
+
lines.push(` Iterations: ${result.iteration}`);
|
|
3065
|
+
lines.push(` Issues Found: ${result.issuesFound}`);
|
|
3066
|
+
lines.push(` Issues Fixed: ${result.issuesFixed}`);
|
|
3067
|
+
if (result.error) {
|
|
3068
|
+
lines.push(` Error: ${theme.error(result.error)}`);
|
|
3069
|
+
}
|
|
3070
|
+
lines.push(` Next Action: ${result.nextAction}`);
|
|
3071
|
+
display.showSystemMessage(lines.join('\n'));
|
|
2940
3072
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
}
|
|
3073
|
+
catch (error) {
|
|
3074
|
+
display.showError(`Evolution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
await this.runWithCriticalApproval('Start self-evolution mode', 'Automates repo changes with checkpoints, tests, and relaunch.', startEvolution);
|
|
2947
3078
|
return;
|
|
2948
3079
|
}
|
|
2949
3080
|
if (subcommand === 'stop') {
|
|
@@ -3210,42 +3341,48 @@ export class InteractiveShell {
|
|
|
3210
3341
|
' /offsec runs';
|
|
3211
3342
|
if (sub === 'start') {
|
|
3212
3343
|
const rest = args.slice(1);
|
|
3213
|
-
const
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3344
|
+
const startRun = async () => {
|
|
3345
|
+
const scope = [];
|
|
3346
|
+
const objectiveParts = [];
|
|
3347
|
+
for (let i = 0; i < rest.length; i++) {
|
|
3348
|
+
if (rest[i]?.toLowerCase() === '--scope') {
|
|
3349
|
+
const scopeArg = rest[i + 1];
|
|
3350
|
+
if (scopeArg) {
|
|
3351
|
+
scope.push(...scopeArg.split(',').map((s) => s.trim()).filter(Boolean));
|
|
3352
|
+
}
|
|
3353
|
+
i += 1;
|
|
3354
|
+
continue;
|
|
3220
3355
|
}
|
|
3221
|
-
i
|
|
3222
|
-
continue;
|
|
3356
|
+
objectiveParts.push(rest[i]);
|
|
3223
3357
|
}
|
|
3224
|
-
objectiveParts.
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3358
|
+
const objective = objectiveParts.join(' ').trim();
|
|
3359
|
+
if (!objective) {
|
|
3360
|
+
display.showWarning('Provide an objective. Example: /offsec start gain shell on api.example.com --scope api.example.com');
|
|
3361
|
+
display.showInfo(usage);
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
const run = startOffsecRun(objective, scope);
|
|
3365
|
+
this.offsecRunId = run.id;
|
|
3366
|
+
const next = getOffsecNextActions(run.id, 3);
|
|
3367
|
+
display.showSystemMessage(theme.gradient.primary('🛡️ Offsec AlphaZero run started'));
|
|
3368
|
+
display.showSystemMessage(formatOffsecStatus(run, next));
|
|
3369
|
+
};
|
|
3370
|
+
await this.runWithCriticalApproval('Start offensive security run', 'Begins an automated security workflow and logs results to ~/.erosolar/offsec.', startRun);
|
|
3237
3371
|
return;
|
|
3238
3372
|
}
|
|
3239
3373
|
if (sub === 'resume') {
|
|
3240
|
-
const
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3374
|
+
const resumeRun = async () => {
|
|
3375
|
+
const targetRun = args[1] ?? this.offsecRunId;
|
|
3376
|
+
const run = resumeOffsecRun(targetRun);
|
|
3377
|
+
if (!run) {
|
|
3378
|
+
display.showWarning(`No offsec run found for id ${targetRun ?? '<unset>'}`);
|
|
3379
|
+
return;
|
|
3380
|
+
}
|
|
3381
|
+
this.offsecRunId = run.id;
|
|
3382
|
+
display.showSystemMessage(theme.gradient.primary(`Resumed offsec run ${run.id}`));
|
|
3383
|
+
display.showSystemMessage(formatOffsecStatus(run, getOffsecNextActions(run.id, 3)));
|
|
3384
|
+
};
|
|
3385
|
+
await this.runWithCriticalApproval('Resume offensive security run', 'Continues a previously started offensive security workflow.', resumeRun);
|
|
3249
3386
|
return;
|
|
3250
3387
|
}
|
|
3251
3388
|
if (sub === 'runs') {
|
|
@@ -3636,6 +3773,7 @@ export class InteractiveShell {
|
|
|
3636
3773
|
else {
|
|
3637
3774
|
this.autosaveIfEnabled();
|
|
3638
3775
|
}
|
|
3776
|
+
this.refreshControlBar();
|
|
3639
3777
|
}
|
|
3640
3778
|
clearAutosaveCommand() {
|
|
3641
3779
|
clearAutosaveSnapshot(this.profile);
|
|
@@ -3899,6 +4037,35 @@ export class InteractiveShell {
|
|
|
3899
4037
|
lines.push(theme.ui.muted('Shift+Enter enables multi-line input.'));
|
|
3900
4038
|
display.showSystemMessage(lines.join('\n'));
|
|
3901
4039
|
}
|
|
4040
|
+
handleApprovalsCommand(input) {
|
|
4041
|
+
const mode = input.split(/\s+/)[1]?.toLowerCase();
|
|
4042
|
+
const showStatus = () => {
|
|
4043
|
+
const lines = [];
|
|
4044
|
+
lines.push(theme.bold('High-Impact Action Approvals'));
|
|
4045
|
+
lines.push('');
|
|
4046
|
+
lines.push(`Current mode: ${this.criticalApprovalMode === 'auto' ? theme.ui.muted('auto (no prompt)') : theme.warning('ask before high-impact actions')}`);
|
|
4047
|
+
lines.push('');
|
|
4048
|
+
lines.push(theme.secondary('Switch mode:'));
|
|
4049
|
+
lines.push(' /approvals auto - run critical flows without prompting');
|
|
4050
|
+
lines.push(' /approvals ask - require approval for major actions');
|
|
4051
|
+
lines.push('');
|
|
4052
|
+
lines.push(theme.ui.muted('Keyboard: Ctrl+Shift+A toggles modes.'));
|
|
4053
|
+
display.showSystemMessage(lines.join('\n'));
|
|
4054
|
+
};
|
|
4055
|
+
if (!mode || mode === 'status') {
|
|
4056
|
+
showStatus();
|
|
4057
|
+
return;
|
|
4058
|
+
}
|
|
4059
|
+
if (['auto', 'default', 'off'].includes(mode)) {
|
|
4060
|
+
this.setCriticalApprovalMode('auto', 'command');
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
if (['ask', 'approval', 'require', 'confirm', 'on'].includes(mode)) {
|
|
4064
|
+
this.setCriticalApprovalMode('approval', 'command');
|
|
4065
|
+
return;
|
|
4066
|
+
}
|
|
4067
|
+
display.showWarning('Usage: /approvals [auto|ask|status]');
|
|
4068
|
+
}
|
|
3902
4069
|
handlePermissionsCommand() {
|
|
3903
4070
|
const lines = [];
|
|
3904
4071
|
lines.push(theme.bold('Tool Permissions'));
|
|
@@ -3913,6 +4080,9 @@ export class InteractiveShell {
|
|
|
3913
4080
|
lines.push(' Shift+Tab Cycle through modes');
|
|
3914
4081
|
lines.push(' Option+E Toggle edit permission');
|
|
3915
4082
|
lines.push('');
|
|
4083
|
+
lines.push(theme.secondary('High-impact approvals:'));
|
|
4084
|
+
lines.push(' /approvals ask|auto (Ctrl+Shift+A to toggle)');
|
|
4085
|
+
lines.push('');
|
|
3916
4086
|
lines.push(theme.ui.muted('Use /tools to manage which tool suites are enabled.'));
|
|
3917
4087
|
display.showSystemMessage(lines.join('\n'));
|
|
3918
4088
|
}
|