erosolar-cli 2.1.11 → 2.1.13
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/customCommands.d.ts.map +1 -1
- package/dist/core/customCommands.js +3 -1
- package/dist/core/customCommands.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 +5 -2
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +291 -238
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/ui/PromptController.d.ts +1 -0
- package/dist/ui/PromptController.d.ts.map +1 -1
- package/dist/ui/PromptController.js +2 -2
- package/dist/ui/PromptController.js.map +1 -1
- package/dist/ui/ShellUIAdapter.js +1 -1
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +6 -0
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +31 -54
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +2 -1
- package/dist/ui/display.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';
|
|
@@ -237,6 +238,11 @@ export class InteractiveShell {
|
|
|
237
238
|
description: 'Show available and loaded plugins',
|
|
238
239
|
category: 'configuration',
|
|
239
240
|
});
|
|
241
|
+
this.slashCommands.push({
|
|
242
|
+
command: '/approvals',
|
|
243
|
+
description: 'Switch between auto and approval mode for high-impact actions',
|
|
244
|
+
category: 'configuration',
|
|
245
|
+
});
|
|
240
246
|
this.slashCommands.push({
|
|
241
247
|
command: '/offsec',
|
|
242
248
|
description: 'AlphaZero offensive security run (start/status/next)',
|
|
@@ -283,6 +289,8 @@ export class InteractiveShell {
|
|
|
283
289
|
this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
|
|
284
290
|
this.setupStatusTracking();
|
|
285
291
|
this.refreshContextGauge();
|
|
292
|
+
// Prime renderer state before it first paints
|
|
293
|
+
this.refreshControlBar();
|
|
286
294
|
// Start terminal input (sets up handlers)
|
|
287
295
|
this.terminalInput.start();
|
|
288
296
|
// Allow planning tools (e.g., ProposePlan) to open the interactive approval UI just like Codex CLI
|
|
@@ -290,7 +298,6 @@ export class InteractiveShell {
|
|
|
290
298
|
// Set up command autocomplete with all slash commands
|
|
291
299
|
this.setupCommandAutocomplete();
|
|
292
300
|
// Render chat box immediately using the streaming UI lifecycle
|
|
293
|
-
this.refreshControlBar();
|
|
294
301
|
this.syncRendererInput();
|
|
295
302
|
this.renderer?.render();
|
|
296
303
|
this.rebuildAgent();
|
|
@@ -371,31 +378,29 @@ export class InteractiveShell {
|
|
|
371
378
|
display.showInfo(this.sessionResumeNotice);
|
|
372
379
|
this.sessionResumeNotice = null;
|
|
373
380
|
}
|
|
374
|
-
async showWelcomeBanner(
|
|
375
|
-
if (this.welcomeShown
|
|
381
|
+
async showWelcomeBanner() {
|
|
382
|
+
if (this.welcomeShown)
|
|
376
383
|
return;
|
|
377
|
-
}
|
|
378
|
-
const banner = this.buildClaudeStyleBanner();
|
|
379
|
-
this.streamEventBlock(banner);
|
|
380
|
-
this.streamEventBlock(this.buildQuickstartPanel());
|
|
381
384
|
this.welcomeShown = true;
|
|
385
|
+
if (process.env['EROSOLAR_SHOW_WELCOME']) {
|
|
386
|
+
const pkg = this.getPackageInfo();
|
|
387
|
+
const version = pkg.version || 'unknown';
|
|
388
|
+
const model = `${this.providerLabel(this.sessionState.provider)} · ${this.sessionState.model}`;
|
|
389
|
+
const workspace = this.workingDir.replace(`${process.env['HOME'] ?? ''}/`, '~/');
|
|
390
|
+
const lines = [];
|
|
391
|
+
lines.push(theme.gradient.primary('╭─ Erosolar CLI ───────────────────────────────────────────────╮'));
|
|
392
|
+
lines.push(theme.gradient.primary('│ │'));
|
|
393
|
+
lines.push(theme.gradient.primary(`│ ${theme.bold('Welcome!').padEnd(60)}│`));
|
|
394
|
+
lines.push(theme.gradient.primary('│ │'));
|
|
395
|
+
lines.push(theme.gradient.primary(`│ ${theme.ui.muted('Model:').padEnd(10)}${theme.info(model).padEnd(50)}│`));
|
|
396
|
+
lines.push(theme.gradient.primary(`│ ${theme.ui.muted('Workspace:').padEnd(10)}${theme.ui.muted(workspace).padEnd(50)}│`));
|
|
397
|
+
lines.push(theme.gradient.primary(`│ ${theme.ui.muted('Version:').padEnd(10)}${theme.info(`v${version}`).padEnd(50)}│`));
|
|
398
|
+
lines.push(theme.gradient.primary('│ │'));
|
|
399
|
+
lines.push(theme.gradient.primary('╰──────────────────────────────────────────────────────────────╯'));
|
|
400
|
+
display.showSystemMessage(lines.join('\n'));
|
|
401
|
+
}
|
|
382
402
|
// Check for updates asynchronously (non-blocking)
|
|
383
403
|
void this.checkAndShowUpdates();
|
|
384
|
-
this.requestPromptRefresh(true);
|
|
385
|
-
}
|
|
386
|
-
buildQuickstartPanel() {
|
|
387
|
-
const commands = [
|
|
388
|
-
{ cmd: '/init', desc: 'Create AGENTS.md with working instructions' },
|
|
389
|
-
{ cmd: '/status', desc: 'Show current session configuration' },
|
|
390
|
-
{ cmd: '/model', desc: 'Choose model and reasoning effort' },
|
|
391
|
-
{ cmd: '/approvals', desc: 'Adjust what Erosolar can do without prompts' },
|
|
392
|
-
{ cmd: '/review', desc: 'Review local changes and find issues' },
|
|
393
|
-
];
|
|
394
|
-
const body = commands
|
|
395
|
-
.map(entry => `${theme.primary(entry.cmd.padEnd(9))}${theme.ui.muted('·')} ${entry.desc}`)
|
|
396
|
-
.join('\n');
|
|
397
|
-
const intro = `${theme.ui.muted('To get started, describe a task or try one of these commands:')}`;
|
|
398
|
-
return `${intro}\n\n${body}`;
|
|
399
404
|
}
|
|
400
405
|
describeSessionLabel() {
|
|
401
406
|
if (this.sessionRestoreConfig.mode === 'autosave')
|
|
@@ -420,6 +425,15 @@ export class InteractiveShell {
|
|
|
420
425
|
return this.editGuardMode;
|
|
421
426
|
}
|
|
422
427
|
}
|
|
428
|
+
abbreviatePath(pathValue) {
|
|
429
|
+
if (!pathValue)
|
|
430
|
+
return '—';
|
|
431
|
+
const home = homedir();
|
|
432
|
+
if (home && pathValue.startsWith(home)) {
|
|
433
|
+
return pathValue.replace(home, '~');
|
|
434
|
+
}
|
|
435
|
+
return pathValue;
|
|
436
|
+
}
|
|
423
437
|
async checkAndShowUpdates() {
|
|
424
438
|
try {
|
|
425
439
|
const { checkForUpdates, formatUpdateNotification } = await import('../core/updateChecker.js');
|
|
@@ -435,86 +449,6 @@ export class InteractiveShell {
|
|
|
435
449
|
// Silently fail - don't interrupt user experience
|
|
436
450
|
}
|
|
437
451
|
}
|
|
438
|
-
buildClaudeStyleBanner() {
|
|
439
|
-
const pkg = this.getPackageInfo();
|
|
440
|
-
const version = pkg.version || '2.0.0';
|
|
441
|
-
const model = `${this.providerLabel(this.sessionState.provider)} · ${this.sessionState.model}`;
|
|
442
|
-
const workspace = this.workingDir;
|
|
443
|
-
const profile = this.profileLabel;
|
|
444
|
-
const thinkingLabel = (this.thinkingMode || 'off').toString();
|
|
445
|
-
const sessionLabel = this.describeSessionLabel();
|
|
446
|
-
const terminalWidth = typeof process.stdout.columns === 'number' && process.stdout.columns > 0
|
|
447
|
-
? process.stdout.columns
|
|
448
|
-
: 80;
|
|
449
|
-
const maxContentWidth = Math.max(48, terminalWidth - 4);
|
|
450
|
-
const innerWidth = Math.max(48, Math.min(maxContentWidth, 90));
|
|
451
|
-
const gap = ' ';
|
|
452
|
-
const stripAnsi = (value) => value.replace(/\u001B\[[0-9;]*m/g, '');
|
|
453
|
-
const visibleLength = (value) => stripAnsi(value).length;
|
|
454
|
-
const padLine = (value) => {
|
|
455
|
-
const len = visibleLength(value);
|
|
456
|
-
if (len < innerWidth) {
|
|
457
|
-
return value + ' '.repeat(innerWidth - len);
|
|
458
|
-
}
|
|
459
|
-
return value;
|
|
460
|
-
};
|
|
461
|
-
const truncateEnd = (value, limit) => {
|
|
462
|
-
if (limit <= 0)
|
|
463
|
-
return '';
|
|
464
|
-
if (value.length <= limit)
|
|
465
|
-
return value;
|
|
466
|
-
return `${value.slice(0, Math.max(0, limit - 1))}…`;
|
|
467
|
-
};
|
|
468
|
-
const truncatePath = (value, limit) => {
|
|
469
|
-
if (limit <= 0)
|
|
470
|
-
return '';
|
|
471
|
-
if (value.length <= limit)
|
|
472
|
-
return value;
|
|
473
|
-
return `…${value.slice(-Math.max(1, limit - 1))}`;
|
|
474
|
-
};
|
|
475
|
-
const formatDual = (leftLabel, leftValue, rightLabel, rightValue, rightValueIsPath = false) => {
|
|
476
|
-
const available = innerWidth - visibleLength(gap);
|
|
477
|
-
const leftBudget = Math.min(Math.max(18, Math.floor(available * 0.52)), available - 12);
|
|
478
|
-
const rightBudget = available - leftBudget;
|
|
479
|
-
const leftLimit = Math.max(6, leftBudget - (leftLabel.length + 2));
|
|
480
|
-
const rightLimit = Math.max(6, rightBudget - (rightLabel.length + 2));
|
|
481
|
-
const leftDisplay = truncateEnd(leftValue, leftLimit);
|
|
482
|
-
const rightDisplay = rightValueIsPath
|
|
483
|
-
? truncatePath(rightValue, rightLimit)
|
|
484
|
-
: truncateEnd(rightValue, rightLimit);
|
|
485
|
-
const leftColored = `${theme.primary(leftLabel)}: ${theme.info(leftDisplay)}`;
|
|
486
|
-
const rightColored = `${theme.primary(rightLabel)}: ${theme.ui.muted(rightDisplay)}`;
|
|
487
|
-
return padLine(`${leftColored}${gap}${rightColored}`);
|
|
488
|
-
};
|
|
489
|
-
const editGuardLabel = this.describeEditGuardMode();
|
|
490
|
-
const statusParts = [
|
|
491
|
-
theme.success('Ready'),
|
|
492
|
-
`${theme.primary('Auto')}: ${this.autoContinueEnabled ? theme.success('on') : theme.ui.muted('off')}`,
|
|
493
|
-
`${theme.primary('Verify')}: ${this.verificationEnabled ? theme.success('on') : theme.ui.muted('off')}`,
|
|
494
|
-
`${theme.primary('Thinking')}: ${theme.info(thinkingLabel)}`,
|
|
495
|
-
`${theme.primary('Autosave')}: ${this.autosaveEnabled ? theme.success('on') : theme.ui.muted('off')}`,
|
|
496
|
-
];
|
|
497
|
-
let statusLine = statusParts.join(theme.ui.muted(' • '));
|
|
498
|
-
while (visibleLength(statusLine) > innerWidth && statusParts.length > 2) {
|
|
499
|
-
statusParts.pop();
|
|
500
|
-
statusLine = statusParts.join(theme.ui.muted(' • '));
|
|
501
|
-
}
|
|
502
|
-
const statusContent = padLine(statusLine);
|
|
503
|
-
const title = `${theme.primary('Erosolar CLI')} ${theme.ui.muted('·')} ${theme.info(`v${version}`)}`;
|
|
504
|
-
const dashCount = Math.max(0, innerWidth - visibleLength(title) - 1);
|
|
505
|
-
const top = theme.ui.muted(`╭─ ${title} ${'─'.repeat(dashCount)}╮`);
|
|
506
|
-
const bottom = theme.ui.muted(`╰${'─'.repeat(innerWidth + 2)}╯`);
|
|
507
|
-
const frameLine = (content) => `${theme.ui.muted('│')} ${content} ${theme.ui.muted('│')}`;
|
|
508
|
-
const lines = [
|
|
509
|
-
top,
|
|
510
|
-
frameLine(formatDual('Profile', profile, 'Workspace', workspace, true)),
|
|
511
|
-
frameLine(formatDual('Model', model, 'Writes', editGuardLabel)),
|
|
512
|
-
frameLine(formatDual('Session', sessionLabel, 'Thinking', thinkingLabel)),
|
|
513
|
-
frameLine(statusContent),
|
|
514
|
-
bottom,
|
|
515
|
-
];
|
|
516
|
-
return lines.join('\n');
|
|
517
|
-
}
|
|
518
452
|
getPackageInfo() {
|
|
519
453
|
try {
|
|
520
454
|
const pkgPath = join(process.cwd(), 'package.json');
|
|
@@ -649,6 +583,7 @@ export class InteractiveShell {
|
|
|
649
583
|
'/bug',
|
|
650
584
|
'/changes', '/summary',
|
|
651
585
|
'/export',
|
|
586
|
+
'/approvals',
|
|
652
587
|
// Configuration menus
|
|
653
588
|
'/model', '/models',
|
|
654
589
|
'/secrets',
|
|
@@ -1230,6 +1165,78 @@ export class InteractiveShell {
|
|
|
1230
1165
|
display.showWarning('Invalid input. Enter a step number, command (go/cancel/all/none), or your own solution.');
|
|
1231
1166
|
this.syncRendererInput();
|
|
1232
1167
|
}
|
|
1168
|
+
async runWithCriticalApproval(label, detail, action) {
|
|
1169
|
+
if (this.criticalApprovalMode === 'auto') {
|
|
1170
|
+
await action();
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
if (this.pendingInteraction && this.pendingInteraction.type === 'critical-approval') {
|
|
1174
|
+
display.showWarning('Finish the pending approval first.');
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
await new Promise((resolve) => {
|
|
1178
|
+
this.pendingInteraction = {
|
|
1179
|
+
type: 'critical-approval',
|
|
1180
|
+
label,
|
|
1181
|
+
detail,
|
|
1182
|
+
onApprove: async () => {
|
|
1183
|
+
this.pendingInteraction = null;
|
|
1184
|
+
try {
|
|
1185
|
+
await action();
|
|
1186
|
+
}
|
|
1187
|
+
catch (error) {
|
|
1188
|
+
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
1189
|
+
}
|
|
1190
|
+
resolve();
|
|
1191
|
+
},
|
|
1192
|
+
onCancel: () => {
|
|
1193
|
+
this.pendingInteraction = null;
|
|
1194
|
+
display.showInfo('Action cancelled.');
|
|
1195
|
+
resolve();
|
|
1196
|
+
},
|
|
1197
|
+
};
|
|
1198
|
+
this.showCriticalApprovalPrompt(label, detail);
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
showCriticalApprovalPrompt(label, detail) {
|
|
1202
|
+
const lines = [];
|
|
1203
|
+
lines.push(theme.gradient.primary('⚠️ Approval required'));
|
|
1204
|
+
lines.push('');
|
|
1205
|
+
lines.push(theme.bold(label));
|
|
1206
|
+
if (detail) {
|
|
1207
|
+
lines.push(theme.ui.muted(detail));
|
|
1208
|
+
}
|
|
1209
|
+
lines.push('');
|
|
1210
|
+
lines.push('Type "yes" to proceed or "no" to cancel.');
|
|
1211
|
+
display.showSystemMessage(lines.join('\n'));
|
|
1212
|
+
this.syncRendererInput();
|
|
1213
|
+
}
|
|
1214
|
+
async handleCriticalApprovalInput(input) {
|
|
1215
|
+
const pending = this.pendingInteraction;
|
|
1216
|
+
if (!pending || pending.type !== 'critical-approval') {
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
const normalized = input.trim().toLowerCase();
|
|
1220
|
+
if (!normalized) {
|
|
1221
|
+
display.showWarning('Enter "yes" to proceed or "no" to cancel.');
|
|
1222
|
+
this.syncRendererInput();
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
if (normalized === 'yes' || normalized === 'y') {
|
|
1226
|
+
this.pendingInteraction = null;
|
|
1227
|
+
await pending.onApprove();
|
|
1228
|
+
this.syncRendererInput();
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
if (normalized === 'no' || normalized === 'n' || normalized === 'cancel' || normalized === 'c') {
|
|
1232
|
+
this.pendingInteraction = null;
|
|
1233
|
+
pending.onCancel?.();
|
|
1234
|
+
this.syncRendererInput();
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
display.showWarning('Please respond with "yes" to proceed or "no" to cancel.');
|
|
1238
|
+
this.syncRendererInput();
|
|
1239
|
+
}
|
|
1233
1240
|
setupHandlers() {
|
|
1234
1241
|
// Handle terminal resize
|
|
1235
1242
|
output.on('resize', () => {
|
|
@@ -1350,9 +1357,11 @@ export class InteractiveShell {
|
|
|
1350
1357
|
criticalApprovalMode: this.criticalApprovalMode,
|
|
1351
1358
|
criticalApprovalHotkey: 'ctrl+shift+a',
|
|
1352
1359
|
});
|
|
1360
|
+
const workspaceDisplay = this.abbreviatePath(this.workingDir);
|
|
1353
1361
|
this.terminalInput.setChromeMeta({
|
|
1354
1362
|
profile: this.profileLabel,
|
|
1355
|
-
workspace:
|
|
1363
|
+
workspace: workspaceDisplay,
|
|
1364
|
+
directory: workspaceDisplay,
|
|
1356
1365
|
writes: this.describeEditGuardMode(),
|
|
1357
1366
|
sessionLabel: this.describeSessionLabel(),
|
|
1358
1367
|
thinkingLabel: (this.thinkingMode || 'off').toString(),
|
|
@@ -1432,15 +1441,6 @@ export class InteractiveShell {
|
|
|
1432
1441
|
* All three can be shown simultaneously (Erosolar-CLI style).
|
|
1433
1442
|
*/
|
|
1434
1443
|
refreshStatusLine(forceRender = false) {
|
|
1435
|
-
const statusText = this.formatStatusLine(this.statusLineState);
|
|
1436
|
-
// Compose streaming/override/base status into a single line so the prompt
|
|
1437
|
-
// looks the same before and during streaming.
|
|
1438
|
-
this.terminalInput.setStatusLine({
|
|
1439
|
-
streaming: this.streamingStatusLabel,
|
|
1440
|
-
override: this.statusMessageOverride,
|
|
1441
|
-
main: statusText,
|
|
1442
|
-
});
|
|
1443
|
-
// Surface meta header (elapsed + context usage) above the divider
|
|
1444
1444
|
const elapsedSeconds = this.getAiRuntimeSeconds();
|
|
1445
1445
|
const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
|
|
1446
1446
|
const tokensUsed = this.latestTokenUsage.used;
|
|
@@ -1873,6 +1873,9 @@ export class InteractiveShell {
|
|
|
1873
1873
|
case 'plan-approval':
|
|
1874
1874
|
await this.handlePlanApprovalInput(input);
|
|
1875
1875
|
return true;
|
|
1876
|
+
case 'critical-approval':
|
|
1877
|
+
await this.handleCriticalApprovalInput(input);
|
|
1878
|
+
return true;
|
|
1876
1879
|
default:
|
|
1877
1880
|
return false;
|
|
1878
1881
|
}
|
|
@@ -1892,6 +1895,9 @@ export class InteractiveShell {
|
|
|
1892
1895
|
case '/features':
|
|
1893
1896
|
this.showFeaturesMenu(input);
|
|
1894
1897
|
break;
|
|
1898
|
+
case '/approvals':
|
|
1899
|
+
this.handleApprovalsCommand(input);
|
|
1900
|
+
break;
|
|
1895
1901
|
case '/learn':
|
|
1896
1902
|
this.showLearningStatus(input);
|
|
1897
1903
|
break;
|
|
@@ -2075,6 +2081,7 @@ export class InteractiveShell {
|
|
|
2075
2081
|
` ${theme.info('Shift+Tab')} ${theme.ui.muted('Toggle edit mode (auto/ask)')}`,
|
|
2076
2082
|
` ${theme.info('Option+V')} ${theme.ui.muted('Toggle verification')}`,
|
|
2077
2083
|
` ${theme.info('Option+C')} ${theme.ui.muted('Toggle auto-continue')}`,
|
|
2084
|
+
` ${theme.info('Ctrl+Shift+A')} ${theme.ui.muted('Toggle approvals for high-impact actions')}`,
|
|
2078
2085
|
` ${theme.info('Option+T')} ${theme.ui.muted('Cycle thinking mode')}`,
|
|
2079
2086
|
` ${theme.info('Option+E')} ${theme.ui.muted('Toggle edit permission mode')}`,
|
|
2080
2087
|
` ${theme.info('Option+X')} ${theme.ui.muted('Clear/compact context')}`,
|
|
@@ -2639,40 +2646,43 @@ export class InteractiveShell {
|
|
|
2639
2646
|
return;
|
|
2640
2647
|
}
|
|
2641
2648
|
if (subcommand === 'apply') {
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2649
|
+
const runApply = async () => {
|
|
2650
|
+
display.showSystemMessage(theme.gradient.primary('🚀 Running Self-Improvement Cycle...'));
|
|
2651
|
+
display.showSystemMessage('');
|
|
2652
|
+
try {
|
|
2653
|
+
const result = await runSelfImprovementCycle(this.workingDir, {
|
|
2654
|
+
maxChanges: 3,
|
|
2655
|
+
minConfidence: 0.7,
|
|
2656
|
+
runTests: true,
|
|
2657
|
+
autoCommit: true,
|
|
2658
|
+
});
|
|
2659
|
+
const lines = [];
|
|
2660
|
+
lines.push(theme.bold('Results:'));
|
|
2661
|
+
lines.push(result.summary);
|
|
2662
|
+
lines.push('');
|
|
2663
|
+
if (result.applied > 0) {
|
|
2664
|
+
lines.push(theme.success(`✅ Applied ${result.applied} improvements!`));
|
|
2665
|
+
for (const r of result.results.filter(r => r.success)) {
|
|
2666
|
+
lines.push(` - Files: ${r.filesChanged.join(', ')}`);
|
|
2667
|
+
if (r.commitHash) {
|
|
2668
|
+
lines.push(` Commit: ${theme.ui.muted(r.commitHash)}`);
|
|
2669
|
+
lines.push(` Rollback: ${theme.dim(r.rollbackCommand ?? '')}`);
|
|
2670
|
+
}
|
|
2662
2671
|
}
|
|
2663
2672
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2673
|
+
else {
|
|
2674
|
+
lines.push(theme.warning('No improvements were applied.'));
|
|
2675
|
+
for (const r of result.results.filter(r => !r.success)) {
|
|
2676
|
+
lines.push(` ${theme.error('✗')} ${r.error}`);
|
|
2677
|
+
}
|
|
2669
2678
|
}
|
|
2679
|
+
display.showSystemMessage(lines.join('\n'));
|
|
2670
2680
|
}
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2681
|
+
catch (error) {
|
|
2682
|
+
display.showError(`Self-improvement failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2683
|
+
}
|
|
2684
|
+
};
|
|
2685
|
+
await this.runWithCriticalApproval('Apply self-improvement changes', 'Runs automated fixes with tests and commits before continuing.', runApply);
|
|
2676
2686
|
return;
|
|
2677
2687
|
}
|
|
2678
2688
|
if (subcommand === 'dry-run') {
|
|
@@ -2701,7 +2711,7 @@ export class InteractiveShell {
|
|
|
2701
2711
|
return;
|
|
2702
2712
|
}
|
|
2703
2713
|
if (subcommand === 'auto') {
|
|
2704
|
-
|
|
2714
|
+
await this.runWithCriticalApproval('Start autonomous improvement mode', 'Allows the CLI to apply and validate changes until stopped.', () => this.runAutonomousImprovementMode());
|
|
2705
2715
|
return;
|
|
2706
2716
|
}
|
|
2707
2717
|
if (subcommand === 'stop') {
|
|
@@ -2710,13 +2720,15 @@ export class InteractiveShell {
|
|
|
2710
2720
|
return;
|
|
2711
2721
|
}
|
|
2712
2722
|
if (subcommand === 'rollback') {
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2723
|
+
await this.runWithCriticalApproval('Emergency rollback of self-improvement', 'Restores the last checkpoint created by the improvement engine.', () => {
|
|
2724
|
+
const result = emergencyRollback(this.workingDir);
|
|
2725
|
+
if (result.success) {
|
|
2726
|
+
display.showSuccess(result.message);
|
|
2727
|
+
}
|
|
2728
|
+
else {
|
|
2729
|
+
display.showError(result.message);
|
|
2730
|
+
}
|
|
2731
|
+
});
|
|
2720
2732
|
return;
|
|
2721
2733
|
}
|
|
2722
2734
|
if (subcommand === 'status') {
|
|
@@ -2919,64 +2931,67 @@ export class InteractiveShell {
|
|
|
2919
2931
|
return;
|
|
2920
2932
|
}
|
|
2921
2933
|
if (subcommand === 'start') {
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2934
|
+
const startEvolution = async () => {
|
|
2935
|
+
display.showSystemMessage(theme.gradient.primary('🧬 Starting Self-Evolution Mode'));
|
|
2936
|
+
display.showSystemMessage('');
|
|
2937
|
+
display.showSystemMessage(theme.bold('Safety Features:'));
|
|
2938
|
+
display.showSystemMessage(' • Git checkpoint created before starting');
|
|
2939
|
+
display.showSystemMessage(' • Each change validated with build + tests');
|
|
2940
|
+
display.showSystemMessage(' • Auto-rollback on failures');
|
|
2941
|
+
display.showSystemMessage(' • Auto-relaunch with improved code');
|
|
2942
|
+
display.showSystemMessage(' • Press Ctrl+C to stop gracefully');
|
|
2943
|
+
display.showSystemMessage('');
|
|
2944
|
+
try {
|
|
2945
|
+
const result = await runSelfEvolution(this.workingDir, {
|
|
2946
|
+
maxIterations: 50,
|
|
2947
|
+
minConfidence: 0.8,
|
|
2948
|
+
runTests: true,
|
|
2949
|
+
autoRelaunch: true,
|
|
2950
|
+
}, {
|
|
2951
|
+
onStart: () => {
|
|
2952
|
+
display.showInfo('Evolution started. Creating checkpoint...');
|
|
2953
|
+
},
|
|
2954
|
+
onIteration: (iteration, issues) => {
|
|
2955
|
+
display.showSystemMessage(`\n[Iteration ${iteration}] Found ${issues.length} high-confidence issues`);
|
|
2956
|
+
},
|
|
2957
|
+
onFix: (issue, success) => {
|
|
2958
|
+
if (success) {
|
|
2959
|
+
display.showSuccess(`Fixed: ${issue.description.slice(0, 50)}`);
|
|
2960
|
+
}
|
|
2961
|
+
else {
|
|
2962
|
+
display.showWarning(`Failed: ${issue.description.slice(0, 50)}`);
|
|
2963
|
+
}
|
|
2964
|
+
},
|
|
2965
|
+
onRelaunch: () => {
|
|
2966
|
+
display.showSystemMessage('');
|
|
2967
|
+
display.showSystemMessage(theme.gradient.primary('🔄 Relaunching with improved code...'));
|
|
2968
|
+
},
|
|
2969
|
+
onComplete: (result) => {
|
|
2970
|
+
display.showSystemMessage('');
|
|
2971
|
+
display.showSuccess(`Evolution complete! Fixed ${result.issuesFixed} issues.`);
|
|
2972
|
+
},
|
|
2973
|
+
onError: (error) => {
|
|
2974
|
+
display.showError(`Evolution error: ${error}`);
|
|
2975
|
+
},
|
|
2976
|
+
});
|
|
2977
|
+
const lines = [];
|
|
2978
|
+
lines.push('');
|
|
2979
|
+
lines.push(theme.bold('Evolution Result:'));
|
|
2980
|
+
lines.push(` Success: ${result.success ? theme.success('Yes') : theme.error('No')}`);
|
|
2981
|
+
lines.push(` Iterations: ${result.iteration}`);
|
|
2982
|
+
lines.push(` Issues Found: ${result.issuesFound}`);
|
|
2983
|
+
lines.push(` Issues Fixed: ${result.issuesFixed}`);
|
|
2984
|
+
if (result.error) {
|
|
2985
|
+
lines.push(` Error: ${theme.error(result.error)}`);
|
|
2986
|
+
}
|
|
2987
|
+
lines.push(` Next Action: ${result.nextAction}`);
|
|
2988
|
+
display.showSystemMessage(lines.join('\n'));
|
|
2973
2989
|
}
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
}
|
|
2990
|
+
catch (error) {
|
|
2991
|
+
display.showError(`Evolution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
2994
|
+
await this.runWithCriticalApproval('Start self-evolution mode', 'Automates repo changes with checkpoints, tests, and relaunch.', startEvolution);
|
|
2980
2995
|
return;
|
|
2981
2996
|
}
|
|
2982
2997
|
if (subcommand === 'stop') {
|
|
@@ -3243,42 +3258,48 @@ export class InteractiveShell {
|
|
|
3243
3258
|
' /offsec runs';
|
|
3244
3259
|
if (sub === 'start') {
|
|
3245
3260
|
const rest = args.slice(1);
|
|
3246
|
-
const
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3261
|
+
const startRun = async () => {
|
|
3262
|
+
const scope = [];
|
|
3263
|
+
const objectiveParts = [];
|
|
3264
|
+
for (let i = 0; i < rest.length; i++) {
|
|
3265
|
+
if (rest[i]?.toLowerCase() === '--scope') {
|
|
3266
|
+
const scopeArg = rest[i + 1];
|
|
3267
|
+
if (scopeArg) {
|
|
3268
|
+
scope.push(...scopeArg.split(',').map((s) => s.trim()).filter(Boolean));
|
|
3269
|
+
}
|
|
3270
|
+
i += 1;
|
|
3271
|
+
continue;
|
|
3253
3272
|
}
|
|
3254
|
-
i
|
|
3255
|
-
continue;
|
|
3273
|
+
objectiveParts.push(rest[i]);
|
|
3256
3274
|
}
|
|
3257
|
-
objectiveParts.
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3275
|
+
const objective = objectiveParts.join(' ').trim();
|
|
3276
|
+
if (!objective) {
|
|
3277
|
+
display.showWarning('Provide an objective. Example: /offsec start gain shell on api.example.com --scope api.example.com');
|
|
3278
|
+
display.showInfo(usage);
|
|
3279
|
+
return;
|
|
3280
|
+
}
|
|
3281
|
+
const run = startOffsecRun(objective, scope);
|
|
3282
|
+
this.offsecRunId = run.id;
|
|
3283
|
+
const next = getOffsecNextActions(run.id, 3);
|
|
3284
|
+
display.showSystemMessage(theme.gradient.primary('🛡️ Offsec AlphaZero run started'));
|
|
3285
|
+
display.showSystemMessage(formatOffsecStatus(run, next));
|
|
3286
|
+
};
|
|
3287
|
+
await this.runWithCriticalApproval('Start offensive security run', 'Begins an automated security workflow and logs results to ~/.erosolar/offsec.', startRun);
|
|
3270
3288
|
return;
|
|
3271
3289
|
}
|
|
3272
3290
|
if (sub === 'resume') {
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3291
|
+
const resumeRun = async () => {
|
|
3292
|
+
const targetRun = args[1] ?? this.offsecRunId;
|
|
3293
|
+
const run = resumeOffsecRun(targetRun);
|
|
3294
|
+
if (!run) {
|
|
3295
|
+
display.showWarning(`No offsec run found for id ${targetRun ?? '<unset>'}`);
|
|
3296
|
+
return;
|
|
3297
|
+
}
|
|
3298
|
+
this.offsecRunId = run.id;
|
|
3299
|
+
display.showSystemMessage(theme.gradient.primary(`Resumed offsec run ${run.id}`));
|
|
3300
|
+
display.showSystemMessage(formatOffsecStatus(run, getOffsecNextActions(run.id, 3)));
|
|
3301
|
+
};
|
|
3302
|
+
await this.runWithCriticalApproval('Resume offensive security run', 'Continues a previously started offensive security workflow.', resumeRun);
|
|
3282
3303
|
return;
|
|
3283
3304
|
}
|
|
3284
3305
|
if (sub === 'runs') {
|
|
@@ -3933,6 +3954,35 @@ export class InteractiveShell {
|
|
|
3933
3954
|
lines.push(theme.ui.muted('Shift+Enter enables multi-line input.'));
|
|
3934
3955
|
display.showSystemMessage(lines.join('\n'));
|
|
3935
3956
|
}
|
|
3957
|
+
handleApprovalsCommand(input) {
|
|
3958
|
+
const mode = input.split(/\s+/)[1]?.toLowerCase();
|
|
3959
|
+
const showStatus = () => {
|
|
3960
|
+
const lines = [];
|
|
3961
|
+
lines.push(theme.bold('High-Impact Action Approvals'));
|
|
3962
|
+
lines.push('');
|
|
3963
|
+
lines.push(`Current mode: ${this.criticalApprovalMode === 'auto' ? theme.ui.muted('auto (no prompt)') : theme.warning('ask before high-impact actions')}`);
|
|
3964
|
+
lines.push('');
|
|
3965
|
+
lines.push(theme.secondary('Switch mode:'));
|
|
3966
|
+
lines.push(' /approvals auto - run critical flows without prompting');
|
|
3967
|
+
lines.push(' /approvals ask - require approval for major actions');
|
|
3968
|
+
lines.push('');
|
|
3969
|
+
lines.push(theme.ui.muted('Keyboard: Ctrl+Shift+A toggles modes.'));
|
|
3970
|
+
display.showSystemMessage(lines.join('\n'));
|
|
3971
|
+
};
|
|
3972
|
+
if (!mode || mode === 'status') {
|
|
3973
|
+
showStatus();
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3976
|
+
if (['auto', 'default', 'off'].includes(mode)) {
|
|
3977
|
+
this.setCriticalApprovalMode('auto', 'command');
|
|
3978
|
+
return;
|
|
3979
|
+
}
|
|
3980
|
+
if (['ask', 'approval', 'require', 'confirm', 'on'].includes(mode)) {
|
|
3981
|
+
this.setCriticalApprovalMode('approval', 'command');
|
|
3982
|
+
return;
|
|
3983
|
+
}
|
|
3984
|
+
display.showWarning('Usage: /approvals [auto|ask|status]');
|
|
3985
|
+
}
|
|
3936
3986
|
handlePermissionsCommand() {
|
|
3937
3987
|
const lines = [];
|
|
3938
3988
|
lines.push(theme.bold('Tool Permissions'));
|
|
@@ -3947,6 +3997,9 @@ export class InteractiveShell {
|
|
|
3947
3997
|
lines.push(' Shift+Tab Cycle through modes');
|
|
3948
3998
|
lines.push(' Option+E Toggle edit permission');
|
|
3949
3999
|
lines.push('');
|
|
4000
|
+
lines.push(theme.secondary('High-impact approvals:'));
|
|
4001
|
+
lines.push(' /approvals ask|auto (Ctrl+Shift+A to toggle)');
|
|
4002
|
+
lines.push('');
|
|
3950
4003
|
lines.push(theme.ui.muted('Use /tools to manage which tool suites are enabled.'));
|
|
3951
4004
|
display.showSystemMessage(lines.join('\n'));
|
|
3952
4005
|
}
|