erosolar-cli 2.1.49 → 2.1.51
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/capabilities/askUserCapability.d.ts +3 -10
- package/dist/capabilities/askUserCapability.d.ts.map +1 -1
- package/dist/capabilities/askUserCapability.js +29 -33
- package/dist/capabilities/askUserCapability.js.map +1 -1
- package/dist/core/agent.d.ts +1 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +14 -4
- package/dist/core/agent.js.map +1 -1
- package/dist/runtime/agentController.js +2 -2
- package/dist/runtime/agentController.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +7 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +398 -216
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/tools/grepTools.d.ts.map +1 -1
- package/dist/tools/grepTools.js +51 -8
- package/dist/tools/grepTools.js.map +1 -1
- package/dist/tools/interactionTools.d.ts +3 -10
- package/dist/tools/interactionTools.d.ts.map +1 -1
- package/dist/tools/interactionTools.js +32 -32
- package/dist/tools/interactionTools.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +29 -1
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +5 -2
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +43 -29
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/display.d.ts +8 -0
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +43 -0
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/toolDisplay.d.ts.map +1 -1
- package/dist/ui/toolDisplay.js +22 -1
- package/dist/ui/toolDisplay.js.map +1 -1
- package/dist/utils/askUserPrompt.d.ts +21 -0
- package/dist/utils/askUserPrompt.d.ts.map +1 -0
- package/dist/utils/askUserPrompt.js +76 -0
- package/dist/utils/askUserPrompt.js.map +1 -0
- package/package.json +1 -1
|
@@ -2,10 +2,11 @@ import { stdin as input, stdout as output, exit } from 'node:process';
|
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { exec } from 'node:child_process';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
|
-
import { join } from 'node:path';
|
|
5
|
+
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { join, resolve } from 'node:path';
|
|
7
7
|
import { display } from '../ui/display.js';
|
|
8
8
|
import { theme } from '../ui/theme.js';
|
|
9
|
+
import { getTerminalColumns } from '../ui/layout.js';
|
|
9
10
|
import { StreamingResponseFormatter } from '../ui/streamingFormatter.js';
|
|
10
11
|
import { getContextWindowTokens } from '../core/contextWindow.js';
|
|
11
12
|
import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
|
|
@@ -82,6 +83,8 @@ const CONTEXT_CLEANUP_SYSTEM_PROMPT = `Summarize earlier IDE collaboration so th
|
|
|
82
83
|
- Capture decisions, file edits/paths, tool findings, and open questions.
|
|
83
84
|
- Separate finished work from follow-ups; keep it under ~180 words with tight bullets.
|
|
84
85
|
- Respond in plain Markdown only (no tool or shell calls).`;
|
|
86
|
+
const MAX_ATTACHMENT_BYTES = 200 * 1024; // 200KB per attachment
|
|
87
|
+
const MAX_ATTACHMENT_CHARS = 16000; // Guardrail to avoid flooding context
|
|
85
88
|
export class InteractiveShell {
|
|
86
89
|
agent = null;
|
|
87
90
|
profile;
|
|
@@ -175,6 +178,8 @@ export class InteractiveShell {
|
|
|
175
178
|
statusLineState = null;
|
|
176
179
|
statusMessageOverride = null;
|
|
177
180
|
lastPromptBlockNotice = null;
|
|
181
|
+
inlineCommandActive = false;
|
|
182
|
+
inlinePanelScopeActive = false;
|
|
178
183
|
promptRefreshTimer = null;
|
|
179
184
|
launchPaletteShown = false;
|
|
180
185
|
version;
|
|
@@ -242,6 +247,11 @@ export class InteractiveShell {
|
|
|
242
247
|
description: 'Show available and loaded plugins',
|
|
243
248
|
category: 'configuration',
|
|
244
249
|
});
|
|
250
|
+
this.slashCommands.push({
|
|
251
|
+
command: '/attach',
|
|
252
|
+
description: 'Attach a local text file into the conversation (truncated, text-only)',
|
|
253
|
+
category: 'context',
|
|
254
|
+
});
|
|
245
255
|
this.slashCommands.push({
|
|
246
256
|
command: '/approvals',
|
|
247
257
|
description: 'Switch between auto and approval mode for high-impact actions',
|
|
@@ -289,6 +299,17 @@ export class InteractiveShell {
|
|
|
289
299
|
// Share renderer with Display so all output flows through the unified queue
|
|
290
300
|
this.renderer = this.terminalInput.getRenderer();
|
|
291
301
|
display.setRenderer(this.renderer);
|
|
302
|
+
display.setInlinePanelHandler((content) => {
|
|
303
|
+
if (!this.shouldCaptureInlinePanel()) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
const lines = content
|
|
307
|
+
.split('\n')
|
|
308
|
+
.map((line) => line.trimEnd())
|
|
309
|
+
.filter((line) => line.trim().length > 0);
|
|
310
|
+
this.showInlinePanel(lines);
|
|
311
|
+
return true;
|
|
312
|
+
});
|
|
292
313
|
// Initialize Alpha Zero 2 metrics tracking
|
|
293
314
|
this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
|
|
294
315
|
this.setupStatusTracking();
|
|
@@ -388,32 +409,47 @@ export class InteractiveShell {
|
|
|
388
409
|
const stripAnsi = (value) => value.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, '');
|
|
389
410
|
const padCenter = (text, width) => {
|
|
390
411
|
const len = stripAnsi(text).length;
|
|
391
|
-
|
|
392
|
-
|
|
412
|
+
if (len >= width)
|
|
413
|
+
return text;
|
|
414
|
+
const left = Math.floor((width - len) / 2);
|
|
415
|
+
const right = width - len - left;
|
|
393
416
|
return ' '.repeat(left) + text + ' '.repeat(right);
|
|
394
417
|
};
|
|
395
|
-
const
|
|
396
|
-
const
|
|
397
|
-
const
|
|
418
|
+
const targetWidth = 62;
|
|
419
|
+
const terminalWidth = getTerminalColumns();
|
|
420
|
+
const availableWidth = Number.isFinite(terminalWidth) && terminalWidth > 0
|
|
421
|
+
? Math.max(28, terminalWidth - 4)
|
|
422
|
+
: targetWidth;
|
|
423
|
+
const contentWidth = Math.max(28, Math.min(targetWidth, Math.min(availableWidth, 84)));
|
|
424
|
+
const clamp = (value) => {
|
|
425
|
+
const normalized = stripAnsi(value);
|
|
426
|
+
if (normalized.length <= contentWidth)
|
|
427
|
+
return value;
|
|
428
|
+
if (contentWidth <= 1)
|
|
429
|
+
return normalized.slice(0, contentWidth);
|
|
430
|
+
return `${normalized.slice(0, contentWidth - 1)}…`;
|
|
431
|
+
};
|
|
432
|
+
const brand = 'Erosolar CLI';
|
|
433
|
+
const label = ` ${brand} `;
|
|
434
|
+
const labelLength = stripAnsi(label).length;
|
|
435
|
+
const labelFill = Math.max(0, contentWidth - labelLength);
|
|
436
|
+
const labelLeft = Math.floor(labelFill / 2);
|
|
437
|
+
const labelRight = labelFill - labelLeft;
|
|
438
|
+
const top = `╭${'─'.repeat(labelLeft)}${label}${'─'.repeat(labelRight)}╮`;
|
|
439
|
+
const bottom = `╰${'─'.repeat(contentWidth)}╯`;
|
|
398
440
|
const userName = process.env['USER'] || 'there';
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
];
|
|
405
|
-
const framedLogo = logoLines.map((line) => frame(`│${padCenter(line, width)}│`));
|
|
441
|
+
const logo = clamp('⟣╍◎╍⟢');
|
|
442
|
+
const versionLabel = version ? clamp(`${brand} · v${version}`) : brand;
|
|
443
|
+
const modelLine = clamp(model);
|
|
444
|
+
const workspaceLine = clamp(workspace);
|
|
445
|
+
const welcomeLine = clamp(`Welcome back ${userName}!`);
|
|
406
446
|
const lines = [
|
|
407
447
|
frame(top),
|
|
408
|
-
frame(`│${padCenter(
|
|
409
|
-
frame(`│${padCenter(
|
|
410
|
-
frame(`│${padCenter(
|
|
411
|
-
|
|
412
|
-
frame(`│${padCenter(
|
|
413
|
-
frame(`│${padCenter(model, width)}│`),
|
|
414
|
-
frame(`│${padCenter('Erosolar CLI', width)}│`),
|
|
415
|
-
frame(`│${padCenter(workspace, width)}│`),
|
|
416
|
-
frame(`│${padCenter('', width)}│`),
|
|
448
|
+
frame(`│${padCenter(welcomeLine, contentWidth)}│`),
|
|
449
|
+
frame(`│${padCenter(logo, contentWidth)}│`),
|
|
450
|
+
frame(`│${padCenter(modelLine, contentWidth)}│`),
|
|
451
|
+
frame(`│${padCenter(versionLabel, contentWidth)}│`),
|
|
452
|
+
frame(`│${padCenter(workspaceLine, contentWidth)}│`),
|
|
417
453
|
frame(bottom),
|
|
418
454
|
].join('\n');
|
|
419
455
|
if (this.renderer) {
|
|
@@ -985,6 +1021,7 @@ export class InteractiveShell {
|
|
|
985
1021
|
if (normalized === 'cancel') {
|
|
986
1022
|
this.pendingInteraction = null;
|
|
987
1023
|
display.showInfo('Tool selection cancelled.');
|
|
1024
|
+
this.clearInlinePanel();
|
|
988
1025
|
this.syncRendererInput();
|
|
989
1026
|
return;
|
|
990
1027
|
}
|
|
@@ -997,6 +1034,7 @@ export class InteractiveShell {
|
|
|
997
1034
|
if (normalized === 'save') {
|
|
998
1035
|
await this.persistToolSelection(pending);
|
|
999
1036
|
this.pendingInteraction = null;
|
|
1037
|
+
this.clearInlinePanel();
|
|
1000
1038
|
this.syncRendererInput();
|
|
1001
1039
|
return;
|
|
1002
1040
|
}
|
|
@@ -1064,6 +1102,7 @@ export class InteractiveShell {
|
|
|
1064
1102
|
if (trimmed.toLowerCase() === 'cancel') {
|
|
1065
1103
|
this.pendingInteraction = null;
|
|
1066
1104
|
display.showInfo('Agent selection cancelled.');
|
|
1105
|
+
this.clearInlinePanel();
|
|
1067
1106
|
this.syncRendererInput();
|
|
1068
1107
|
return;
|
|
1069
1108
|
}
|
|
@@ -1081,6 +1120,7 @@ export class InteractiveShell {
|
|
|
1081
1120
|
}
|
|
1082
1121
|
await this.persistAgentSelection(option.name);
|
|
1083
1122
|
this.pendingInteraction = null;
|
|
1123
|
+
this.clearInlinePanel();
|
|
1084
1124
|
this.syncRendererInput();
|
|
1085
1125
|
}
|
|
1086
1126
|
async persistAgentSelection(profileName) {
|
|
@@ -1761,13 +1801,16 @@ export class InteractiveShell {
|
|
|
1761
1801
|
this.refreshStatusLine();
|
|
1762
1802
|
}
|
|
1763
1803
|
}
|
|
1764
|
-
handleStreamChunk(chunk) {
|
|
1804
|
+
handleStreamChunk(chunk, type = 'content') {
|
|
1765
1805
|
if (!chunk) {
|
|
1766
1806
|
return;
|
|
1767
1807
|
}
|
|
1808
|
+
const isReasoning = type === 'reasoning';
|
|
1809
|
+
// Keep pinned status updated for all streaming chunks
|
|
1810
|
+
this.updateStreamingStatusFromChunk(chunk);
|
|
1768
1811
|
// Suppress raw streaming output in scrollback; keep only status + final message.
|
|
1769
|
-
|
|
1770
|
-
|
|
1812
|
+
// Reasoning chunks bypass this so the thought process stays visible.
|
|
1813
|
+
if (this.streamingOutputSuppressed && !isReasoning) {
|
|
1771
1814
|
this.captureStreamingThought(chunk);
|
|
1772
1815
|
this.streamingContentSeen = true;
|
|
1773
1816
|
return;
|
|
@@ -1776,8 +1819,10 @@ export class InteractiveShell {
|
|
|
1776
1819
|
this.streamingFormatter = new StreamingResponseFormatter(output.columns ?? undefined);
|
|
1777
1820
|
this.pushUiEvent('streaming', this.streamingFormatter.header());
|
|
1778
1821
|
}
|
|
1779
|
-
this.
|
|
1780
|
-
const formatted =
|
|
1822
|
+
this.streamingFormatter.setMode(isReasoning ? 'reasoning' : 'content');
|
|
1823
|
+
const formatted = isReasoning
|
|
1824
|
+
? this.streamingFormatter.formatReasoningChunk(chunk)
|
|
1825
|
+
: this.streamingFormatter.formatChunk(chunk);
|
|
1781
1826
|
this.captureStreamingThought(chunk);
|
|
1782
1827
|
if (formatted) {
|
|
1783
1828
|
if (formatted.trim().length > 0) {
|
|
@@ -2078,181 +2123,195 @@ export class InteractiveShell {
|
|
|
2078
2123
|
this.syncRendererInput();
|
|
2079
2124
|
return;
|
|
2080
2125
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2126
|
+
this.clearInlinePanel();
|
|
2127
|
+
this.inlinePanelScopeActive = true;
|
|
2128
|
+
this.inlineCommandActive = true;
|
|
2129
|
+
try {
|
|
2130
|
+
switch (command) {
|
|
2131
|
+
case '/help':
|
|
2132
|
+
case '/?':
|
|
2133
|
+
this.showHelp();
|
|
2134
|
+
break;
|
|
2135
|
+
case '/features':
|
|
2136
|
+
this.showFeaturesMenu(input);
|
|
2137
|
+
break;
|
|
2138
|
+
case '/approvals':
|
|
2139
|
+
this.handleApprovalsCommand(input);
|
|
2140
|
+
break;
|
|
2141
|
+
case '/learn':
|
|
2142
|
+
this.showLearningStatus(input);
|
|
2143
|
+
break;
|
|
2144
|
+
case '/improve':
|
|
2145
|
+
void this.handleImprovementCommand(input);
|
|
2146
|
+
break;
|
|
2147
|
+
case '/model':
|
|
2148
|
+
this.showModelMenu();
|
|
2149
|
+
break;
|
|
2150
|
+
case '/exit':
|
|
2151
|
+
case '/quit':
|
|
2152
|
+
case '/q':
|
|
2153
|
+
this.shutdown();
|
|
2154
|
+
break;
|
|
2155
|
+
case '/secrets':
|
|
2156
|
+
this.showSecretsMenu();
|
|
2157
|
+
break;
|
|
2158
|
+
case '/tools':
|
|
2159
|
+
this.showToolsMenu();
|
|
2160
|
+
break;
|
|
2161
|
+
case '/mcp':
|
|
2162
|
+
await this.showMcpStatus();
|
|
2163
|
+
break;
|
|
2164
|
+
case '/doctor':
|
|
2165
|
+
this.runDoctor();
|
|
2166
|
+
break;
|
|
2167
|
+
case '/checks':
|
|
2168
|
+
await this.runRepoChecksCommand();
|
|
2169
|
+
break;
|
|
2170
|
+
case '/attach':
|
|
2171
|
+
await this.handleAttachCommand(input);
|
|
2172
|
+
break;
|
|
2173
|
+
case '/context':
|
|
2174
|
+
await this.refreshWorkspaceContextCommand(input);
|
|
2175
|
+
break;
|
|
2176
|
+
case '/agents':
|
|
2177
|
+
this.showAgentsMenu();
|
|
2178
|
+
break;
|
|
2179
|
+
case '/sessions':
|
|
2180
|
+
await this.handleSessionCommand(input);
|
|
2181
|
+
break;
|
|
2182
|
+
case '/skills':
|
|
2183
|
+
await this.handleSkillsCommand(input);
|
|
2184
|
+
break;
|
|
2185
|
+
case '/thinking':
|
|
2186
|
+
this.handleThinkingCommand(input);
|
|
2187
|
+
break;
|
|
2188
|
+
case '/autocontinue':
|
|
2189
|
+
this.handleAutoContinueCommand(input);
|
|
2190
|
+
break;
|
|
2191
|
+
case '/shortcuts':
|
|
2192
|
+
case '/keys':
|
|
2193
|
+
this.handleShortcutsCommand();
|
|
2194
|
+
break;
|
|
2195
|
+
case '/changes':
|
|
2196
|
+
case '/summary':
|
|
2197
|
+
this.showFileChangeSummary();
|
|
2198
|
+
break;
|
|
2199
|
+
case '/metrics':
|
|
2200
|
+
case '/stats':
|
|
2201
|
+
case '/perf':
|
|
2202
|
+
this.showAlphaZeroMetrics();
|
|
2203
|
+
break;
|
|
2204
|
+
case '/suggestions':
|
|
2205
|
+
this.showImprovementSuggestions();
|
|
2206
|
+
break;
|
|
2207
|
+
case '/plugins':
|
|
2208
|
+
this.showPluginStatus();
|
|
2209
|
+
break;
|
|
2210
|
+
case '/evolve':
|
|
2211
|
+
void this.handleEvolveCommand(input);
|
|
2212
|
+
break;
|
|
2213
|
+
case '/modular':
|
|
2214
|
+
case '/a0':
|
|
2215
|
+
void this.handleModularCommand(input);
|
|
2216
|
+
break;
|
|
2217
|
+
case '/offsec':
|
|
2218
|
+
void this.handleOffsecCommand(input);
|
|
2219
|
+
break;
|
|
2220
|
+
case '/test':
|
|
2221
|
+
case '/tests':
|
|
2222
|
+
void this.handleTestCommand(input);
|
|
2223
|
+
break;
|
|
2224
|
+
case '/provider':
|
|
2225
|
+
await this.handleProviderCommand(input);
|
|
2226
|
+
break;
|
|
2227
|
+
case '/providers':
|
|
2228
|
+
this.showConfiguredProviders();
|
|
2229
|
+
break;
|
|
2230
|
+
case '/local':
|
|
2231
|
+
await this.handleLocalCommand(input);
|
|
2232
|
+
break;
|
|
2233
|
+
case '/discover':
|
|
2234
|
+
await this.discoverModelsCommand();
|
|
2235
|
+
break;
|
|
2236
|
+
// Erosolar-CLI style commands
|
|
2237
|
+
case '/rewind':
|
|
2238
|
+
await this.handleRewindCommand(input);
|
|
2239
|
+
break;
|
|
2240
|
+
case '/memory':
|
|
2241
|
+
this.handleMemoryCommand(input);
|
|
2242
|
+
break;
|
|
2243
|
+
case '/vim':
|
|
2244
|
+
this.handleVimCommand();
|
|
2245
|
+
break;
|
|
2246
|
+
case '/output-style':
|
|
2247
|
+
this.handleOutputStyleCommand(input);
|
|
2248
|
+
break;
|
|
2249
|
+
case '/cost':
|
|
2250
|
+
this.handleCostCommand();
|
|
2251
|
+
break;
|
|
2252
|
+
case '/usage':
|
|
2253
|
+
this.handleUsageCommand(input);
|
|
2254
|
+
break;
|
|
2255
|
+
case '/clear':
|
|
2256
|
+
this.handleClearCommand();
|
|
2257
|
+
break;
|
|
2258
|
+
case '/resume':
|
|
2259
|
+
await this.handleResumeCommand(input);
|
|
2260
|
+
break;
|
|
2261
|
+
case '/export':
|
|
2262
|
+
this.handleExportCommand(input);
|
|
2263
|
+
break;
|
|
2264
|
+
case '/review':
|
|
2265
|
+
await this.handleReviewCommand();
|
|
2266
|
+
break;
|
|
2267
|
+
case '/security-review':
|
|
2268
|
+
await this.handleSecurityReviewCommand();
|
|
2269
|
+
break;
|
|
2270
|
+
case '/new':
|
|
2271
|
+
this.handleClearCommand();
|
|
2272
|
+
break;
|
|
2273
|
+
case '/undo':
|
|
2274
|
+
await this.handleRewindCommand(input);
|
|
2275
|
+
break;
|
|
2276
|
+
case '/diff':
|
|
2277
|
+
await this.handleDiffCommand();
|
|
2278
|
+
break;
|
|
2279
|
+
case '/mention':
|
|
2280
|
+
this.handleMentionCommand();
|
|
2281
|
+
break;
|
|
2282
|
+
case '/status':
|
|
2283
|
+
this.handleStatusCommand();
|
|
2284
|
+
break;
|
|
2285
|
+
case '/bug':
|
|
2286
|
+
this.handleBugCommand();
|
|
2287
|
+
break;
|
|
2288
|
+
case '/terminal-setup':
|
|
2289
|
+
this.handleTerminalSetupCommand();
|
|
2290
|
+
break;
|
|
2291
|
+
case '/permissions':
|
|
2292
|
+
this.handlePermissionsCommand();
|
|
2293
|
+
break;
|
|
2294
|
+
case '/init':
|
|
2295
|
+
this.handleInitCommand(input);
|
|
2296
|
+
break;
|
|
2297
|
+
case '/compact':
|
|
2298
|
+
await this.handleCompactCommand();
|
|
2299
|
+
break;
|
|
2300
|
+
case '/logout':
|
|
2301
|
+
this.handleLogoutCommand();
|
|
2302
|
+
break;
|
|
2303
|
+
default:
|
|
2304
|
+
if (!(await this.tryCustomSlashCommand(command, input))) {
|
|
2305
|
+
display.showWarning(`Unknown command "${command}".`);
|
|
2306
|
+
}
|
|
2307
|
+
break;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
finally {
|
|
2311
|
+
this.inlineCommandActive = false;
|
|
2312
|
+
if (!this.pendingInteraction) {
|
|
2313
|
+
this.inlinePanelScopeActive = false;
|
|
2314
|
+
}
|
|
2256
2315
|
}
|
|
2257
2316
|
this.syncRendererInput();
|
|
2258
2317
|
}
|
|
@@ -2580,6 +2639,104 @@ export class InteractiveShell {
|
|
|
2580
2639
|
: `${theme.info('Thinking off')} (Tab to toggle)`;
|
|
2581
2640
|
display.showSystemMessage(`${headline}\n${theme.ui.muted(descriptions[this.thinkingMode])}`);
|
|
2582
2641
|
}
|
|
2642
|
+
async handleAttachCommand(input) {
|
|
2643
|
+
const paths = input
|
|
2644
|
+
.trim()
|
|
2645
|
+
.split(/\s+/)
|
|
2646
|
+
.slice(1)
|
|
2647
|
+
.filter(Boolean);
|
|
2648
|
+
if (!paths.length) {
|
|
2649
|
+
display.showInfo(`Usage: /attach <file> [...]. Text-only, max ${Math.round(MAX_ATTACHMENT_BYTES / 1024)}KB per file; large files are truncated.`);
|
|
2650
|
+
return;
|
|
2651
|
+
}
|
|
2652
|
+
if (this.isProcessing) {
|
|
2653
|
+
display.showWarning('Wait for the current request to finish before attaching files.');
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
|
+
if (!this.agent && !this.rebuildAgent()) {
|
|
2657
|
+
display.showWarning('Configure a provider via /secrets before attaching files.');
|
|
2658
|
+
return;
|
|
2659
|
+
}
|
|
2660
|
+
const agent = this.agent;
|
|
2661
|
+
if (!agent) {
|
|
2662
|
+
display.showWarning('No active agent is available. Try again in a moment.');
|
|
2663
|
+
return;
|
|
2664
|
+
}
|
|
2665
|
+
const history = agent.getHistory();
|
|
2666
|
+
const attachedSummaries = [];
|
|
2667
|
+
for (const rawPath of paths) {
|
|
2668
|
+
const filePath = resolve(this.workingDir, rawPath);
|
|
2669
|
+
if (!existsSync(filePath)) {
|
|
2670
|
+
display.showWarning(`File not found: ${rawPath}`);
|
|
2671
|
+
continue;
|
|
2672
|
+
}
|
|
2673
|
+
let stats;
|
|
2674
|
+
try {
|
|
2675
|
+
stats = statSync(filePath);
|
|
2676
|
+
}
|
|
2677
|
+
catch (error) {
|
|
2678
|
+
display.showWarning(`Unable to read file: ${rawPath} (${error instanceof Error ? error.message : 'unknown error'})`);
|
|
2679
|
+
continue;
|
|
2680
|
+
}
|
|
2681
|
+
if (!stats.isFile()) {
|
|
2682
|
+
display.showWarning(`Not a file: ${rawPath}`);
|
|
2683
|
+
continue;
|
|
2684
|
+
}
|
|
2685
|
+
if (stats.size > MAX_ATTACHMENT_BYTES) {
|
|
2686
|
+
display.showWarning(`Skipped ${rawPath} (${Math.round(stats.size / 1024)}KB) — limit is ${Math.round(MAX_ATTACHMENT_BYTES / 1024)}KB.`);
|
|
2687
|
+
continue;
|
|
2688
|
+
}
|
|
2689
|
+
let buffer;
|
|
2690
|
+
try {
|
|
2691
|
+
buffer = readFileSync(filePath);
|
|
2692
|
+
}
|
|
2693
|
+
catch (error) {
|
|
2694
|
+
display.showWarning(`Failed to read ${rawPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2695
|
+
continue;
|
|
2696
|
+
}
|
|
2697
|
+
if (this.isBinaryBuffer(buffer)) {
|
|
2698
|
+
display.showWarning(`Skipped ${rawPath} — binary files are not attached.`);
|
|
2699
|
+
continue;
|
|
2700
|
+
}
|
|
2701
|
+
let content = buffer.toString('utf-8');
|
|
2702
|
+
let truncated = false;
|
|
2703
|
+
if (content.length > MAX_ATTACHMENT_CHARS) {
|
|
2704
|
+
content = content.slice(0, MAX_ATTACHMENT_CHARS);
|
|
2705
|
+
truncated = true;
|
|
2706
|
+
}
|
|
2707
|
+
const displayPath = this.abbreviatePath(filePath);
|
|
2708
|
+
const attachmentBlock = [
|
|
2709
|
+
`Attachment: ${displayPath}`,
|
|
2710
|
+
`Size: ${stats.size} bytes${truncated ? ` (truncated to ${MAX_ATTACHMENT_CHARS} chars)` : ''}`,
|
|
2711
|
+
'',
|
|
2712
|
+
'```text',
|
|
2713
|
+
content,
|
|
2714
|
+
'```',
|
|
2715
|
+
].join('\n');
|
|
2716
|
+
history.push({ role: 'user', content: attachmentBlock });
|
|
2717
|
+
const kb = Math.max(1, Math.round(stats.size / 1024));
|
|
2718
|
+
attachedSummaries.push(`${displayPath} (${kb}KB${truncated ? ', truncated' : ''})`);
|
|
2719
|
+
}
|
|
2720
|
+
if (!attachedSummaries.length) {
|
|
2721
|
+
display.showWarning('No attachments were added. Ensure files exist, are under the size limit, and are text.');
|
|
2722
|
+
return;
|
|
2723
|
+
}
|
|
2724
|
+
agent.loadHistory(history);
|
|
2725
|
+
this.cachedHistory = history;
|
|
2726
|
+
this.refreshContextGauge();
|
|
2727
|
+
this.captureHistorySnapshot();
|
|
2728
|
+
this.autosaveIfEnabled();
|
|
2729
|
+
display.showSystemMessage(`Attached ${attachedSummaries.length} file${attachedSummaries.length === 1 ? '' : 's'}: ${attachedSummaries.join(', ')}`);
|
|
2730
|
+
}
|
|
2731
|
+
isBinaryBuffer(buffer) {
|
|
2732
|
+
const maxScan = Math.min(buffer.length, 8192);
|
|
2733
|
+
for (let i = 0; i < maxScan; i++) {
|
|
2734
|
+
if (buffer[i] === 0) {
|
|
2735
|
+
return true;
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
return false;
|
|
2739
|
+
}
|
|
2583
2740
|
handleShortcutsCommand() {
|
|
2584
2741
|
// Display keyboard shortcuts help (Erosolar-CLI style)
|
|
2585
2742
|
display.showSystemMessage(formatShortcutsHelp());
|
|
@@ -4517,6 +4674,16 @@ export class InteractiveShell {
|
|
|
4517
4674
|
const suffix = values.length > maxItems ? ', …' : '';
|
|
4518
4675
|
return `${shown.join(', ')}${suffix}`;
|
|
4519
4676
|
}
|
|
4677
|
+
showInlinePanel(lines) {
|
|
4678
|
+
display.showInlinePanel(lines);
|
|
4679
|
+
}
|
|
4680
|
+
clearInlinePanel() {
|
|
4681
|
+
display.clearInlinePanel();
|
|
4682
|
+
}
|
|
4683
|
+
shouldCaptureInlinePanel() {
|
|
4684
|
+
return (this.inlinePanelScopeActive &&
|
|
4685
|
+
(this.inlineCommandActive || Boolean(this.pendingInteraction)));
|
|
4686
|
+
}
|
|
4520
4687
|
buildSlashCommandList(header) {
|
|
4521
4688
|
const lines = [theme.gradient.primary(header), ''];
|
|
4522
4689
|
for (const command of this.slashCommands) {
|
|
@@ -4527,7 +4694,7 @@ export class InteractiveShell {
|
|
|
4527
4694
|
async showModelMenu() {
|
|
4528
4695
|
// Hold input immediately so numeric selections don't get queued as prompts while we fetch
|
|
4529
4696
|
this.pendingInteraction = { type: 'model-loading' };
|
|
4530
|
-
|
|
4697
|
+
this.showInlinePanel([theme.ui.muted('Fetching latest models from providers...')]);
|
|
4531
4698
|
try {
|
|
4532
4699
|
// Fetch live models from all configured providers
|
|
4533
4700
|
const providerStatuses = await quickCheckProviders();
|
|
@@ -4535,6 +4702,7 @@ export class InteractiveShell {
|
|
|
4535
4702
|
if (!providerOptions.length) {
|
|
4536
4703
|
display.showWarning('No providers are available.');
|
|
4537
4704
|
this.pendingInteraction = null;
|
|
4705
|
+
this.clearInlinePanel();
|
|
4538
4706
|
this.syncRendererInput();
|
|
4539
4707
|
return;
|
|
4540
4708
|
}
|
|
@@ -4550,12 +4718,13 @@ export class InteractiveShell {
|
|
|
4550
4718
|
}),
|
|
4551
4719
|
'Type the number of the provider to continue, or type "cancel".',
|
|
4552
4720
|
];
|
|
4553
|
-
|
|
4721
|
+
this.showInlinePanel(lines);
|
|
4554
4722
|
this.pendingInteraction = { type: 'model-provider', options: providerOptions };
|
|
4555
4723
|
}
|
|
4556
4724
|
catch (error) {
|
|
4557
4725
|
display.showError('Failed to load model list. Try again in a moment.', error);
|
|
4558
4726
|
this.pendingInteraction = null;
|
|
4727
|
+
this.clearInlinePanel();
|
|
4559
4728
|
this.syncRendererInput();
|
|
4560
4729
|
}
|
|
4561
4730
|
}
|
|
@@ -4688,6 +4857,7 @@ export class InteractiveShell {
|
|
|
4688
4857
|
if (!allModels.length) {
|
|
4689
4858
|
display.showWarning(`No models available for ${option.label}.`);
|
|
4690
4859
|
this.pendingInteraction = null;
|
|
4860
|
+
this.clearInlinePanel();
|
|
4691
4861
|
return;
|
|
4692
4862
|
}
|
|
4693
4863
|
const lines = [
|
|
@@ -4703,7 +4873,7 @@ export class InteractiveShell {
|
|
|
4703
4873
|
}),
|
|
4704
4874
|
'Type the number of the model to select it, type "back" to change provider, or type "cancel".',
|
|
4705
4875
|
];
|
|
4706
|
-
|
|
4876
|
+
this.showInlinePanel(lines);
|
|
4707
4877
|
this.pendingInteraction = { type: 'model', provider: option.provider, options: allModels };
|
|
4708
4878
|
}
|
|
4709
4879
|
showSecretsMenu() {
|
|
@@ -4719,7 +4889,7 @@ export class InteractiveShell {
|
|
|
4719
4889
|
}),
|
|
4720
4890
|
'Enter the number to update a key, or type "cancel".',
|
|
4721
4891
|
];
|
|
4722
|
-
|
|
4892
|
+
this.showInlinePanel(lines);
|
|
4723
4893
|
this.pendingInteraction = { type: 'secret-select', options: definitions };
|
|
4724
4894
|
}
|
|
4725
4895
|
showToolsMenu() {
|
|
@@ -4745,7 +4915,7 @@ export class InteractiveShell {
|
|
|
4745
4915
|
'',
|
|
4746
4916
|
'Enter the number to toggle, "save" to persist, "defaults" to restore recommended tools, or "cancel".',
|
|
4747
4917
|
];
|
|
4748
|
-
|
|
4918
|
+
this.showInlinePanel(lines);
|
|
4749
4919
|
}
|
|
4750
4920
|
formatToolOptionLine(option, index, selection) {
|
|
4751
4921
|
const enabled = selection.has(option.id);
|
|
@@ -4857,7 +5027,7 @@ export class InteractiveShell {
|
|
|
4857
5027
|
}
|
|
4858
5028
|
lines.push('');
|
|
4859
5029
|
lines.push(theme.ui.muted('Use /tools to enable/disable individual MCP tools.'));
|
|
4860
|
-
|
|
5030
|
+
this.showInlinePanel(lines);
|
|
4861
5031
|
}
|
|
4862
5032
|
showMcpSetupGuide() {
|
|
4863
5033
|
const lines = [];
|
|
@@ -4933,7 +5103,7 @@ export class InteractiveShell {
|
|
|
4933
5103
|
lines.push(` ${theme.info('description')} - Human-readable description`);
|
|
4934
5104
|
lines.push('');
|
|
4935
5105
|
lines.push(`Run ${theme.primary('/mcp examples')} to see example configurations.`);
|
|
4936
|
-
|
|
5106
|
+
this.showInlinePanel(lines);
|
|
4937
5107
|
}
|
|
4938
5108
|
showMcpExamples() {
|
|
4939
5109
|
const lines = [];
|
|
@@ -5064,7 +5234,7 @@ export class InteractiveShell {
|
|
|
5064
5234
|
lines.push(` ${theme.info('@anthropic/mcp-server-github')} - GitHub API`);
|
|
5065
5235
|
lines.push('');
|
|
5066
5236
|
lines.push(`More servers: ${theme.primary('https://github.com/modelcontextprotocol/servers')}`);
|
|
5067
|
-
|
|
5237
|
+
this.showInlinePanel(lines);
|
|
5068
5238
|
}
|
|
5069
5239
|
showAgentsMenu() {
|
|
5070
5240
|
if (!this.agentMenu) {
|
|
@@ -5077,7 +5247,7 @@ export class InteractiveShell {
|
|
|
5077
5247
|
'',
|
|
5078
5248
|
'Enter the number to save it, or type "cancel".',
|
|
5079
5249
|
];
|
|
5080
|
-
|
|
5250
|
+
this.showInlinePanel(lines);
|
|
5081
5251
|
this.pendingInteraction = { type: 'agent-selection', options: this.agentMenu.options };
|
|
5082
5252
|
}
|
|
5083
5253
|
formatAgentOptionLine(option, index) {
|
|
@@ -5117,6 +5287,7 @@ export class InteractiveShell {
|
|
|
5117
5287
|
if (trimmed.toLowerCase() === 'cancel') {
|
|
5118
5288
|
this.pendingInteraction = null;
|
|
5119
5289
|
display.showInfo('Model selection cancelled.');
|
|
5290
|
+
this.clearInlinePanel();
|
|
5120
5291
|
this.syncRendererInput();
|
|
5121
5292
|
return;
|
|
5122
5293
|
}
|
|
@@ -5154,6 +5325,7 @@ export class InteractiveShell {
|
|
|
5154
5325
|
if (trimmed.toLowerCase() === 'cancel') {
|
|
5155
5326
|
this.pendingInteraction = null;
|
|
5156
5327
|
display.showInfo('Model selection cancelled.');
|
|
5328
|
+
this.clearInlinePanel();
|
|
5157
5329
|
this.syncRendererInput();
|
|
5158
5330
|
return;
|
|
5159
5331
|
}
|
|
@@ -5195,6 +5367,7 @@ export class InteractiveShell {
|
|
|
5195
5367
|
this.persistSessionPreference();
|
|
5196
5368
|
this.resetChatBoxAfterModelSwap();
|
|
5197
5369
|
}
|
|
5370
|
+
this.clearInlinePanel();
|
|
5198
5371
|
}
|
|
5199
5372
|
async handleSecretSelection(input) {
|
|
5200
5373
|
const pending = this.pendingInteraction;
|
|
@@ -5210,6 +5383,7 @@ export class InteractiveShell {
|
|
|
5210
5383
|
if (trimmed.toLowerCase() === 'cancel') {
|
|
5211
5384
|
this.pendingInteraction = null;
|
|
5212
5385
|
display.showInfo('Secret management cancelled.');
|
|
5386
|
+
this.clearInlinePanel();
|
|
5213
5387
|
this.syncRendererInput();
|
|
5214
5388
|
return;
|
|
5215
5389
|
}
|
|
@@ -5225,7 +5399,7 @@ export class InteractiveShell {
|
|
|
5225
5399
|
this.syncRendererInput();
|
|
5226
5400
|
return;
|
|
5227
5401
|
}
|
|
5228
|
-
|
|
5402
|
+
this.showInlinePanel([`Enter a new value for ${secret.label} or type "cancel".`]);
|
|
5229
5403
|
this.pendingInteraction = { type: 'secret-input', secret };
|
|
5230
5404
|
this.syncRendererInput();
|
|
5231
5405
|
}
|
|
@@ -5244,6 +5418,7 @@ export class InteractiveShell {
|
|
|
5244
5418
|
this.pendingInteraction = null;
|
|
5245
5419
|
this.pendingSecretRetry = null;
|
|
5246
5420
|
display.showInfo('Secret unchanged.');
|
|
5421
|
+
this.clearInlinePanel();
|
|
5247
5422
|
this.syncRendererInput();
|
|
5248
5423
|
return;
|
|
5249
5424
|
}
|
|
@@ -5268,6 +5443,7 @@ export class InteractiveShell {
|
|
|
5268
5443
|
this.pendingInteraction = null;
|
|
5269
5444
|
this.pendingSecretRetry = null;
|
|
5270
5445
|
}
|
|
5446
|
+
this.clearInlinePanel();
|
|
5271
5447
|
this.syncRendererInput();
|
|
5272
5448
|
}
|
|
5273
5449
|
async processRequest(request) {
|
|
@@ -5279,6 +5455,8 @@ export class InteractiveShell {
|
|
|
5279
5455
|
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
5280
5456
|
return;
|
|
5281
5457
|
}
|
|
5458
|
+
this.inlinePanelScopeActive = false;
|
|
5459
|
+
this.clearInlinePanel();
|
|
5282
5460
|
const agent = this.agent;
|
|
5283
5461
|
if (!agent) {
|
|
5284
5462
|
return;
|
|
@@ -5418,6 +5596,8 @@ export class InteractiveShell {
|
|
|
5418
5596
|
display.showWarning('Configure an API key via /secrets before sending requests.');
|
|
5419
5597
|
return;
|
|
5420
5598
|
}
|
|
5599
|
+
this.inlinePanelScopeActive = false;
|
|
5600
|
+
this.clearInlinePanel();
|
|
5421
5601
|
const agent = this.agent;
|
|
5422
5602
|
if (!agent) {
|
|
5423
5603
|
return;
|
|
@@ -6209,8 +6389,8 @@ Return ONLY JSON array:
|
|
|
6209
6389
|
autoContinue: this.autoContinueEnabled,
|
|
6210
6390
|
};
|
|
6211
6391
|
this.agent = this.runtimeSession.createAgent(selection, {
|
|
6212
|
-
onStreamChunk: (chunk) => {
|
|
6213
|
-
this.handleStreamChunk(chunk);
|
|
6392
|
+
onStreamChunk: (chunk, type) => {
|
|
6393
|
+
this.handleStreamChunk(chunk, type ?? 'content');
|
|
6214
6394
|
},
|
|
6215
6395
|
onStreamFallback: (info) => this.handleStreamingFallback(info),
|
|
6216
6396
|
onAssistantMessage: (content, metadata) => {
|
|
@@ -6933,7 +7113,9 @@ Return ONLY JSON array:
|
|
|
6933
7113
|
const detailText = info.message?.trim() ?? '';
|
|
6934
7114
|
const detail = detailText ? ` ${detailText}` : '';
|
|
6935
7115
|
const reason = info.reason ? ` (${info.reason.replace(/-/g, ' ')})` : '';
|
|
6936
|
-
const partialNote = info.partialResponse
|
|
7116
|
+
const partialNote = info.partialResponse
|
|
7117
|
+
? ' Partial stream captured before failure; continuing from it without restarting.'
|
|
7118
|
+
: '';
|
|
6937
7119
|
const baseMessage = 'Streaming interrupted, retrying without streaming';
|
|
6938
7120
|
display.showWarning(`${baseMessage}${reason}.${detail}${partialNote}`.trim());
|
|
6939
7121
|
this.finishStreamingFormatter('Stream interrupted - retrying without streaming', { mode: 'update' });
|