erosolar-cli 2.1.214 → 2.1.216
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/browser/BrowserSessionManager.d.ts.map +1 -1
- package/dist/browser/BrowserSessionManager.js.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/contracts/schemas/agent.schema.json +2 -1
- package/dist/contracts/v1/agent.d.ts +9 -2
- package/dist/contracts/v1/agent.d.ts.map +1 -1
- package/dist/core/agent.d.ts +18 -2
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +112 -29
- package/dist/core/agent.js.map +1 -1
- package/dist/core/agentRulebook.js +1 -1
- package/dist/core/agentRulebook.js.map +1 -1
- package/dist/core/agentSchemaLoader.d.ts.map +1 -1
- package/dist/core/agentSchemaLoader.js +1 -0
- package/dist/core/agentSchemaLoader.js.map +1 -1
- package/dist/core/alphaZeroEngine.d.ts +1 -1
- package/dist/core/alphaZeroEngine.d.ts.map +1 -1
- package/dist/core/alphaZeroEngine.js +1 -1
- package/dist/core/alphaZeroEngine.js.map +1 -1
- package/dist/core/contextManager.d.ts +1 -1
- package/dist/core/contextManager.d.ts.map +1 -1
- package/dist/core/contextManager.js +1 -1
- package/dist/core/contextManager.js.map +1 -1
- package/dist/core/deepBugAnalyzer.d.ts +1 -1
- package/dist/core/deepBugAnalyzer.d.ts.map +1 -1
- package/dist/core/deepBugAnalyzer.js +8 -8
- package/dist/core/deepBugAnalyzer.js.map +1 -1
- package/dist/core/failureRecovery.d.ts +1 -1
- package/dist/core/failureRecovery.d.ts.map +1 -1
- package/dist/core/failureRecovery.js +2 -1
- package/dist/core/failureRecovery.js.map +1 -1
- package/dist/core/hooks.js +2 -2
- package/dist/core/hooks.js.map +1 -1
- package/dist/core/intelligentTestFlows.d.ts.map +1 -1
- package/dist/core/intelligentTestFlows.js +0 -9
- package/dist/core/intelligentTestFlows.js.map +1 -1
- package/dist/core/learningPersistence.js.map +1 -1
- package/dist/core/memorySystem.js.map +1 -1
- package/dist/core/metricsTracker.d.ts +1 -1
- package/dist/core/metricsTracker.d.ts.map +1 -1
- package/dist/core/metricsTracker.js +1 -1
- package/dist/core/metricsTracker.js.map +1 -1
- package/dist/core/modelDiscovery.d.ts.map +1 -1
- package/dist/core/modelDiscovery.js +15 -6
- package/dist/core/modelDiscovery.js.map +1 -1
- package/dist/core/multilinePasteHandler.js +3 -3
- package/dist/core/multilinePasteHandler.js.map +1 -1
- package/dist/core/outputStyles.js +1 -1
- package/dist/core/outputStyles.js.map +1 -1
- package/dist/core/performanceMonitor.d.ts.map +1 -1
- package/dist/core/performanceMonitor.js +5 -1
- package/dist/core/performanceMonitor.js.map +1 -1
- package/dist/core/preferences.d.ts.map +1 -1
- package/dist/core/preferences.js +1 -0
- package/dist/core/preferences.js.map +1 -1
- package/dist/core/schemaValidator.d.ts.map +1 -1
- package/dist/core/schemaValidator.js.map +1 -1
- package/dist/core/selfEvolution.d.ts.map +1 -1
- package/dist/core/selfEvolution.js +1 -20
- package/dist/core/selfEvolution.js.map +1 -1
- package/dist/core/selfImprovement.d.ts +1 -1
- package/dist/core/selfImprovement.d.ts.map +1 -1
- package/dist/core/selfImprovement.js +2 -2
- package/dist/core/selfImprovement.js.map +1 -1
- package/dist/core/toolPreconditions.js +2 -0
- package/dist/core/toolPreconditions.js.map +1 -1
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +1 -0
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/core/validationRunner.d.ts.map +1 -1
- package/dist/core/validationRunner.js +9 -5
- package/dist/core/validationRunner.js.map +1 -1
- package/dist/headless/evalMode.d.ts.map +1 -1
- package/dist/headless/evalMode.js +4 -0
- package/dist/headless/evalMode.js.map +1 -1
- package/dist/intelligence/docGenerator.js +1 -1
- package/dist/intelligence/docGenerator.js.map +1 -1
- package/dist/mcp/stdioClient.d.ts.map +1 -1
- package/dist/mcp/stdioClient.js +1 -0
- package/dist/mcp/stdioClient.js.map +1 -1
- package/dist/providers/anthropicProvider.d.ts.map +1 -1
- package/dist/providers/anthropicProvider.js +2 -10
- package/dist/providers/anthropicProvider.js.map +1 -1
- package/dist/providers/googleProvider.js +1 -0
- package/dist/providers/googleProvider.js.map +1 -1
- package/dist/providers/openaiResponsesProvider.js.map +1 -1
- package/dist/runtime/agentController.d.ts +1 -0
- package/dist/runtime/agentController.d.ts.map +1 -1
- package/dist/runtime/agentController.js +23 -1
- package/dist/runtime/agentController.js.map +1 -1
- package/dist/runtime/agentHost.d.ts.map +1 -1
- package/dist/runtime/agentHost.js +1 -0
- package/dist/runtime/agentHost.js.map +1 -1
- package/dist/shell/autoExecutor.d.ts.map +1 -1
- package/dist/shell/autoExecutor.js +8 -5
- package/dist/shell/autoExecutor.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +27 -1
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +478 -185
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/vimMode.d.ts.map +1 -1
- package/dist/shell/vimMode.js +3 -2
- package/dist/shell/vimMode.js.map +1 -1
- package/dist/skills/skillRepository.d.ts.map +1 -1
- package/dist/skills/skillRepository.js +1 -0
- package/dist/skills/skillRepository.js.map +1 -1
- package/dist/subagents/taskRunner.d.ts.map +1 -1
- package/dist/subagents/taskRunner.js +2 -1
- package/dist/subagents/taskRunner.js.map +1 -1
- package/dist/tools/advancedTestGenerationTools.js +4 -1
- package/dist/tools/advancedTestGenerationTools.js.map +1 -1
- package/dist/tools/backgroundBashTools.js.map +1 -1
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +5 -6
- package/dist/tools/bashTools.js.map +1 -1
- package/dist/tools/buildTools.d.ts.map +1 -1
- package/dist/tools/buildTools.js +6 -4
- package/dist/tools/buildTools.js.map +1 -1
- package/dist/tools/codeQualityTools.d.ts.map +1 -1
- package/dist/tools/codeQualityTools.js +1 -0
- package/dist/tools/codeQualityTools.js.map +1 -1
- package/dist/tools/dependencyTools.d.ts.map +1 -1
- package/dist/tools/dependencyTools.js +1 -0
- package/dist/tools/dependencyTools.js.map +1 -1
- package/dist/tools/devTools.d.ts.map +1 -1
- package/dist/tools/devTools.js +1 -0
- package/dist/tools/devTools.js.map +1 -1
- package/dist/tools/diffUtils.d.ts.map +1 -1
- package/dist/tools/diffUtils.js +0 -2
- package/dist/tools/diffUtils.js.map +1 -1
- package/dist/tools/editTools.js.map +1 -1
- package/dist/tools/enhancedAnalysisTools.d.ts.map +1 -1
- package/dist/tools/enhancedAnalysisTools.js +1 -13
- package/dist/tools/enhancedAnalysisTools.js.map +1 -1
- package/dist/tools/enhancedCodeIntelligenceTools.js.map +1 -1
- package/dist/tools/enhancedDevWorkflowTools.d.ts.map +1 -1
- package/dist/tools/enhancedDevWorkflowTools.js +1 -0
- package/dist/tools/enhancedDevWorkflowTools.js.map +1 -1
- package/dist/tools/fileTools.d.ts.map +1 -1
- package/dist/tools/fileTools.js +3 -0
- package/dist/tools/fileTools.js.map +1 -1
- package/dist/tools/frontendTestingTools.js +1 -1
- package/dist/tools/frontendTestingTools.js.map +1 -1
- package/dist/tools/globTools.js.map +1 -1
- package/dist/tools/grepTools.js.map +1 -1
- package/dist/tools/learnTools.js +0 -4
- package/dist/tools/learnTools.js.map +1 -1
- package/dist/tools/localExplore.d.ts.map +1 -1
- package/dist/tools/localExplore.js +0 -1
- package/dist/tools/localExplore.js.map +1 -1
- package/dist/tools/notebookEditTools.js.map +1 -1
- package/dist/tools/searchTools.js +2 -0
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/testingTools.d.ts.map +1 -1
- package/dist/tools/testingTools.js +8 -4
- package/dist/tools/testingTools.js.map +1 -1
- package/dist/tools/webTools.d.ts.map +1 -1
- package/dist/tools/webTools.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +4 -17
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +2 -1
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +44 -27
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +7 -2
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/errorFormatter.d.ts.map +1 -1
- package/dist/ui/errorFormatter.js +1 -0
- package/dist/ui/errorFormatter.js.map +1 -1
- package/dist/ui/inPlaceUpdater.d.ts +1 -1
- package/dist/ui/inPlaceUpdater.d.ts.map +1 -1
- package/dist/ui/inPlaceUpdater.js +1 -3
- package/dist/ui/inPlaceUpdater.js.map +1 -1
- package/dist/ui/layout.d.ts.map +1 -1
- package/dist/ui/layout.js +1 -0
- package/dist/ui/layout.js.map +1 -1
- package/dist/ui/richText.d.ts.map +1 -1
- package/dist/ui/richText.js +7 -1
- package/dist/ui/richText.js.map +1 -1
- package/dist/ui/toolDisplay.d.ts +1 -1
- package/dist/ui/toolDisplay.d.ts.map +1 -1
- package/dist/ui/toolDisplay.js +11 -5
- package/dist/ui/toolDisplay.js.map +1 -1
- package/dist/utils/askUserPrompt.d.ts.map +1 -1
- package/dist/utils/askUserPrompt.js +1 -0
- package/dist/utils/askUserPrompt.js.map +1 -1
- package/dist/utils/planFormatter.d.ts.map +1 -1
- package/dist/utils/planFormatter.js +1 -0
- package/dist/utils/planFormatter.js.map +1 -1
- package/package.json +1 -1
|
@@ -160,7 +160,11 @@ export class InteractiveShell {
|
|
|
160
160
|
lastUserQuery = '';
|
|
161
161
|
lastAssistantResponse = null;
|
|
162
162
|
responseRendered = false;
|
|
163
|
-
|
|
163
|
+
remainingIssueFixQueued = false;
|
|
164
|
+
lastBuildStatus = null;
|
|
165
|
+
lastTestStatus = null;
|
|
166
|
+
lastRunToolCount = null;
|
|
167
|
+
lastRunDurationSeconds = null;
|
|
164
168
|
lastFailure = null;
|
|
165
169
|
lastAutoTestRun = null;
|
|
166
170
|
// Auto-build tracking
|
|
@@ -183,6 +187,9 @@ export class InteractiveShell {
|
|
|
183
187
|
statusMessageOverride = null;
|
|
184
188
|
inlineCommandActive = false;
|
|
185
189
|
inlinePanelScopeActive = false;
|
|
190
|
+
inlinePanelBase = [];
|
|
191
|
+
inlinePanelNotices = [];
|
|
192
|
+
inlinePanelClearTimer = null;
|
|
186
193
|
promptRefreshTimer = null;
|
|
187
194
|
launchPaletteShown = false;
|
|
188
195
|
version;
|
|
@@ -410,6 +417,7 @@ export class InteractiveShell {
|
|
|
410
417
|
const workspace = this.abbreviatePath(this.workingDir);
|
|
411
418
|
const version = this.version || this.getPackageInfo().version || 'dev';
|
|
412
419
|
const frame = (content) => theme.gradient.primary(content);
|
|
420
|
+
// eslint-disable-next-line no-control-regex
|
|
413
421
|
const stripAnsi = (value) => value.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, '');
|
|
414
422
|
const padCenter = (text, width) => {
|
|
415
423
|
const len = stripAnsi(text).length;
|
|
@@ -942,8 +950,8 @@ export class InteractiveShell {
|
|
|
942
950
|
return;
|
|
943
951
|
}
|
|
944
952
|
const message = enabled
|
|
945
|
-
? '▶️ Auto-continue on.
|
|
946
|
-
: '⏹️ Auto-continue off.
|
|
953
|
+
? '▶️ Auto-continue on. Verification can rerun after fixes automatically. (Ctrl+Shift+G to toggle)'
|
|
954
|
+
: '⏹️ Auto-continue off. Single verification pass only; no automatic retries. (Ctrl+Shift+G to toggle)';
|
|
947
955
|
display.showSystemMessage(message);
|
|
948
956
|
}
|
|
949
957
|
toggleCriticalApprovalMode(source) {
|
|
@@ -1205,15 +1213,14 @@ export class InteractiveShell {
|
|
|
1205
1213
|
}
|
|
1206
1214
|
const trimmed = input.trim();
|
|
1207
1215
|
if (!trimmed) {
|
|
1208
|
-
|
|
1216
|
+
this.appendInlineNotice('Enter a number, "save", "defaults", or "cancel".', 'warning');
|
|
1209
1217
|
this.syncRendererInput();
|
|
1210
1218
|
return;
|
|
1211
1219
|
}
|
|
1212
1220
|
const normalized = trimmed.toLowerCase();
|
|
1213
1221
|
if (normalized === 'cancel') {
|
|
1214
1222
|
this.pendingInteraction = null;
|
|
1215
|
-
|
|
1216
|
-
this.clearInlinePanel();
|
|
1223
|
+
this.showInlineStatus('Tool selection cancelled.', 'info', { autoClearMs: 1600 });
|
|
1217
1224
|
this.syncRendererInput();
|
|
1218
1225
|
return;
|
|
1219
1226
|
}
|
|
@@ -1224,9 +1231,14 @@ export class InteractiveShell {
|
|
|
1224
1231
|
return;
|
|
1225
1232
|
}
|
|
1226
1233
|
if (normalized === 'save') {
|
|
1227
|
-
await this.persistToolSelection(pending);
|
|
1234
|
+
const result = await this.persistToolSelection(pending);
|
|
1228
1235
|
this.pendingInteraction = null;
|
|
1229
|
-
|
|
1236
|
+
if (result) {
|
|
1237
|
+
this.showInlineStatus(result.message, result.tone ?? 'info', { autoClearMs: 1800 });
|
|
1238
|
+
}
|
|
1239
|
+
else {
|
|
1240
|
+
this.clearInlinePanel();
|
|
1241
|
+
}
|
|
1230
1242
|
this.syncRendererInput();
|
|
1231
1243
|
return;
|
|
1232
1244
|
}
|
|
@@ -1234,11 +1246,11 @@ export class InteractiveShell {
|
|
|
1234
1246
|
if (Number.isFinite(choice)) {
|
|
1235
1247
|
const option = pending.options[choice - 1];
|
|
1236
1248
|
if (!option) {
|
|
1237
|
-
|
|
1249
|
+
this.appendInlineNotice('That option is not available.', 'warning');
|
|
1238
1250
|
}
|
|
1239
1251
|
else {
|
|
1240
1252
|
if (option.locked) {
|
|
1241
|
-
|
|
1253
|
+
this.appendInlineNotice('The default tool package is always enabled and cannot be toggled.', 'info');
|
|
1242
1254
|
}
|
|
1243
1255
|
else {
|
|
1244
1256
|
if (pending.selection.has(option.id)) {
|
|
@@ -1253,26 +1265,24 @@ export class InteractiveShell {
|
|
|
1253
1265
|
this.syncRendererInput();
|
|
1254
1266
|
return;
|
|
1255
1267
|
}
|
|
1256
|
-
|
|
1268
|
+
this.appendInlineNotice('Enter a number, "save", "defaults", or "cancel".', 'warning');
|
|
1257
1269
|
this.syncRendererInput();
|
|
1258
1270
|
}
|
|
1259
1271
|
async persistToolSelection(interaction) {
|
|
1260
1272
|
if (setsEqual(interaction.selection, interaction.initialSelection)) {
|
|
1261
|
-
|
|
1262
|
-
return;
|
|
1273
|
+
return { message: 'No changes to save.', tone: 'info' };
|
|
1263
1274
|
}
|
|
1264
1275
|
const defaults = buildEnabledToolSet(null, this.profile);
|
|
1265
1276
|
if (setsEqual(interaction.selection, defaults)) {
|
|
1266
1277
|
clearToolSettings();
|
|
1267
|
-
|
|
1268
|
-
return;
|
|
1278
|
+
return { message: 'Tool settings cleared. Defaults will be used on the next launch.', tone: 'info' };
|
|
1269
1279
|
}
|
|
1270
1280
|
const ordered = interaction.options
|
|
1271
1281
|
.filter((option) => !option.locked)
|
|
1272
1282
|
.map((option) => option.id)
|
|
1273
1283
|
.filter((id) => interaction.selection.has(id));
|
|
1274
1284
|
saveToolSettings({ enabledTools: ordered });
|
|
1275
|
-
|
|
1285
|
+
return { message: 'Tool settings saved. Restart the CLI to apply them.', tone: 'success' };
|
|
1276
1286
|
}
|
|
1277
1287
|
async handleAgentSelectionInput(input) {
|
|
1278
1288
|
const pending = this.pendingInteraction;
|
|
@@ -1281,58 +1291,69 @@ export class InteractiveShell {
|
|
|
1281
1291
|
}
|
|
1282
1292
|
if (!this.agentMenu) {
|
|
1283
1293
|
this.pendingInteraction = null;
|
|
1284
|
-
|
|
1294
|
+
this.showInlineStatus('Agent selection is unavailable in this CLI.', 'warning', { autoClearMs: 1600 });
|
|
1285
1295
|
this.syncRendererInput();
|
|
1286
1296
|
return;
|
|
1287
1297
|
}
|
|
1288
1298
|
const trimmed = input.trim();
|
|
1289
1299
|
if (!trimmed) {
|
|
1290
|
-
|
|
1300
|
+
this.appendInlineNotice('Enter a number or type "cancel".', 'warning');
|
|
1291
1301
|
this.syncRendererInput();
|
|
1292
1302
|
return;
|
|
1293
1303
|
}
|
|
1294
1304
|
if (trimmed.toLowerCase() === 'cancel') {
|
|
1295
1305
|
this.pendingInteraction = null;
|
|
1296
|
-
|
|
1297
|
-
this.clearInlinePanel();
|
|
1306
|
+
this.showInlineStatus('Agent selection cancelled.', 'info', { autoClearMs: 1600 });
|
|
1298
1307
|
this.syncRendererInput();
|
|
1299
1308
|
return;
|
|
1300
1309
|
}
|
|
1301
1310
|
const choice = Number.parseInt(trimmed, 10);
|
|
1302
1311
|
if (!Number.isFinite(choice)) {
|
|
1303
|
-
|
|
1312
|
+
this.appendInlineNotice('Please enter a valid number.', 'warning');
|
|
1304
1313
|
this.syncRendererInput();
|
|
1305
1314
|
return;
|
|
1306
1315
|
}
|
|
1307
1316
|
const option = pending.options[choice - 1];
|
|
1308
1317
|
if (!option) {
|
|
1309
|
-
|
|
1318
|
+
this.appendInlineNotice('That option is not available.', 'warning');
|
|
1310
1319
|
this.syncRendererInput();
|
|
1311
1320
|
return;
|
|
1312
1321
|
}
|
|
1313
|
-
await this.persistAgentSelection(option.name);
|
|
1314
1322
|
this.pendingInteraction = null;
|
|
1315
|
-
this.
|
|
1323
|
+
const result = await this.persistAgentSelection(option.name);
|
|
1324
|
+
if (result) {
|
|
1325
|
+
this.showInlineStatus(result.message, result.tone ?? 'info', { autoClearMs: 2000 });
|
|
1326
|
+
}
|
|
1327
|
+
else {
|
|
1328
|
+
this.clearInlinePanel();
|
|
1329
|
+
}
|
|
1316
1330
|
this.syncRendererInput();
|
|
1317
1331
|
}
|
|
1318
1332
|
async persistAgentSelection(profileName) {
|
|
1319
1333
|
if (!this.agentMenu) {
|
|
1320
|
-
return;
|
|
1334
|
+
return null;
|
|
1321
1335
|
}
|
|
1322
1336
|
const currentDefault = this.agentMenu.persistedProfile ?? this.agentMenu.defaultProfile;
|
|
1323
1337
|
if (profileName === currentDefault) {
|
|
1324
|
-
|
|
1325
|
-
|
|
1338
|
+
return {
|
|
1339
|
+
message: `${this.agentMenuLabel(profileName)} is already configured for the next launch.`,
|
|
1340
|
+
tone: 'info',
|
|
1341
|
+
};
|
|
1326
1342
|
}
|
|
1327
1343
|
if (profileName === this.agentMenu.defaultProfile) {
|
|
1328
1344
|
clearActiveProfilePreference();
|
|
1329
1345
|
this.agentMenu.persistedProfile = null;
|
|
1330
|
-
|
|
1331
|
-
|
|
1346
|
+
return {
|
|
1347
|
+
message: `${this.agentMenuLabel(profileName)} restored as the default agent. Restart the CLI to switch.`,
|
|
1348
|
+
tone: 'success',
|
|
1349
|
+
};
|
|
1332
1350
|
}
|
|
1333
1351
|
saveActiveProfilePreference(profileName);
|
|
1334
1352
|
this.agentMenu.persistedProfile = profileName;
|
|
1335
|
-
|
|
1353
|
+
return {
|
|
1354
|
+
message: `${this.agentMenuLabel(profileName)} will load the next time you start the CLI. Restart to switch now.`,
|
|
1355
|
+
tone: 'success',
|
|
1356
|
+
};
|
|
1336
1357
|
}
|
|
1337
1358
|
/**
|
|
1338
1359
|
* Show plan approval UI with interactive step selection
|
|
@@ -1385,7 +1406,7 @@ export class InteractiveShell {
|
|
|
1385
1406
|
lines.push(` ${theme.primary('cancel')} Cancel planning`);
|
|
1386
1407
|
lines.push(` ${theme.primary('[text]')} Submit your own solution instead`);
|
|
1387
1408
|
lines.push('');
|
|
1388
|
-
|
|
1409
|
+
this.showInlinePanel(lines);
|
|
1389
1410
|
this.syncRendererInput();
|
|
1390
1411
|
}
|
|
1391
1412
|
async handlePlanApprovalInput(input) {
|
|
@@ -1394,7 +1415,7 @@ export class InteractiveShell {
|
|
|
1394
1415
|
return;
|
|
1395
1416
|
const trimmed = input.trim();
|
|
1396
1417
|
if (!trimmed) {
|
|
1397
|
-
|
|
1418
|
+
this.appendInlineNotice('Enter a command or your own solution.', 'warning');
|
|
1398
1419
|
this.syncRendererInput();
|
|
1399
1420
|
return;
|
|
1400
1421
|
}
|
|
@@ -1402,7 +1423,7 @@ export class InteractiveShell {
|
|
|
1402
1423
|
// Cancel
|
|
1403
1424
|
if (lower === 'cancel' || lower === 'c') {
|
|
1404
1425
|
this.pendingInteraction = null;
|
|
1405
|
-
|
|
1426
|
+
this.showInlineStatus('Plan cancelled. You can continue with a different approach.', 'info', { autoClearMs: 1800 });
|
|
1406
1427
|
this.syncRendererInput();
|
|
1407
1428
|
return;
|
|
1408
1429
|
}
|
|
@@ -1422,7 +1443,7 @@ export class InteractiveShell {
|
|
|
1422
1443
|
if (lower === 'go' || lower === 'g' || lower === 'yes' || lower === 'y') {
|
|
1423
1444
|
const selectedSteps = pending.steps.filter(s => pending.selectedSteps.has(s.id));
|
|
1424
1445
|
if (selectedSteps.length === 0) {
|
|
1425
|
-
|
|
1446
|
+
this.appendInlineNotice('No steps selected. Select steps or enter your own solution.', 'warning');
|
|
1426
1447
|
this.syncRendererInput();
|
|
1427
1448
|
return;
|
|
1428
1449
|
}
|
|
@@ -1449,13 +1470,13 @@ export class InteractiveShell {
|
|
|
1449
1470
|
// User submitted their own solution - treat as custom input
|
|
1450
1471
|
if (trimmed.length > 10) {
|
|
1451
1472
|
this.pendingInteraction = null;
|
|
1452
|
-
|
|
1473
|
+
this.showInlineStatus('Using your custom solution...', 'info', { autoClearMs: 1400 });
|
|
1453
1474
|
// Send the custom solution as the implementation request
|
|
1454
1475
|
const customPrompt = `The user has provided their own solution instead of the plan. Please implement this:\n\n${trimmed}`;
|
|
1455
1476
|
await this.enqueuePromptForProcessing(customPrompt, 'programmatic');
|
|
1456
1477
|
return;
|
|
1457
1478
|
}
|
|
1458
|
-
|
|
1479
|
+
this.appendInlineNotice('Invalid input. Enter a step number, command (go/cancel/all/none), or your own solution.', 'warning');
|
|
1459
1480
|
this.syncRendererInput();
|
|
1460
1481
|
}
|
|
1461
1482
|
async runWithCriticalApproval(label, detail, action) {
|
|
@@ -1501,7 +1522,7 @@ export class InteractiveShell {
|
|
|
1501
1522
|
}
|
|
1502
1523
|
lines.push('');
|
|
1503
1524
|
lines.push('Type "yes" to proceed or "no" to cancel.');
|
|
1504
|
-
|
|
1525
|
+
this.showInlinePanel(lines);
|
|
1505
1526
|
this.syncRendererInput();
|
|
1506
1527
|
}
|
|
1507
1528
|
async handleCriticalApprovalInput(input) {
|
|
@@ -1511,23 +1532,25 @@ export class InteractiveShell {
|
|
|
1511
1532
|
}
|
|
1512
1533
|
const normalized = input.trim().toLowerCase();
|
|
1513
1534
|
if (!normalized) {
|
|
1514
|
-
|
|
1535
|
+
this.appendInlineNotice('Enter "yes" to proceed or "no" to cancel.', 'warning');
|
|
1515
1536
|
this.syncRendererInput();
|
|
1516
1537
|
return;
|
|
1517
1538
|
}
|
|
1518
1539
|
if (normalized === 'yes' || normalized === 'y') {
|
|
1519
1540
|
this.pendingInteraction = null;
|
|
1520
1541
|
await pending.onApprove();
|
|
1542
|
+
this.showInlineStatus('Approved.', 'success', { autoClearMs: 1400 });
|
|
1521
1543
|
this.syncRendererInput();
|
|
1522
1544
|
return;
|
|
1523
1545
|
}
|
|
1524
1546
|
if (normalized === 'no' || normalized === 'n' || normalized === 'cancel' || normalized === 'c') {
|
|
1525
1547
|
this.pendingInteraction = null;
|
|
1526
1548
|
pending.onCancel?.();
|
|
1549
|
+
this.showInlineStatus('Action cancelled.', 'info', { autoClearMs: 1400 });
|
|
1527
1550
|
this.syncRendererInput();
|
|
1528
1551
|
return;
|
|
1529
1552
|
}
|
|
1530
|
-
|
|
1553
|
+
this.appendInlineNotice('Please respond with "yes" to proceed or "no" to cancel.', 'warning');
|
|
1531
1554
|
this.syncRendererInput();
|
|
1532
1555
|
}
|
|
1533
1556
|
setupHandlers() {
|
|
@@ -1635,6 +1658,28 @@ export class InteractiveShell {
|
|
|
1635
1658
|
}
|
|
1636
1659
|
describeStatusDetail(detail) {
|
|
1637
1660
|
const parts = detail?.trim() ? [detail.trim()] : [];
|
|
1661
|
+
// Build/test health
|
|
1662
|
+
if (this.lastBuildStatus) {
|
|
1663
|
+
parts.push(this.lastBuildStatus === 'ok' ? 'build ok' : 'build failing');
|
|
1664
|
+
}
|
|
1665
|
+
if (this.lastTestStatus) {
|
|
1666
|
+
parts.push(this.lastTestStatus === 'ok' ? 'tests ok' : 'tests failing');
|
|
1667
|
+
}
|
|
1668
|
+
// Recent run metrics
|
|
1669
|
+
if (this.lastRunDurationSeconds !== null) {
|
|
1670
|
+
const durationLabel = this.formatElapsedTime(this.lastRunDurationSeconds);
|
|
1671
|
+
const toolsLabel = this.lastRunToolCount != null ? `${this.lastRunToolCount} tool${this.lastRunToolCount === 1 ? '' : 's'}` : '';
|
|
1672
|
+
parts.push(`last ${durationLabel}${toolsLabel ? ` · ${toolsLabel}` : ''}`);
|
|
1673
|
+
}
|
|
1674
|
+
// Dirty workspace indicator
|
|
1675
|
+
if (this.hasRecordedWrites()) {
|
|
1676
|
+
parts.push('writes dirty');
|
|
1677
|
+
}
|
|
1678
|
+
// Context headroom warning
|
|
1679
|
+
const ctxPercent = this.computeContextPercent();
|
|
1680
|
+
if (ctxPercent !== null && ctxPercent >= 85) {
|
|
1681
|
+
parts.push(`ctx ${ctxPercent}%`);
|
|
1682
|
+
}
|
|
1638
1683
|
// Show queued prompts in the prompt inbox (Ctrl+Enter during streaming)
|
|
1639
1684
|
const inboxDepth = this.promptInbox.length + (this.promptInFlight ? 1 : 0);
|
|
1640
1685
|
if (inboxDepth > 0) {
|
|
@@ -1700,6 +1745,15 @@ export class InteractiveShell {
|
|
|
1700
1745
|
const provider = this.providerLabel(this.sessionState.provider);
|
|
1701
1746
|
return `${provider} · ${this.sessionState.model}`;
|
|
1702
1747
|
}
|
|
1748
|
+
hasRecordedWrites() {
|
|
1749
|
+
return this._fileChangeTracker.getAllChanges().length > 0;
|
|
1750
|
+
}
|
|
1751
|
+
computeContextPercent() {
|
|
1752
|
+
if (!this.latestTokenUsage.used || !this.latestTokenUsage.limit || this.latestTokenUsage.limit <= 0) {
|
|
1753
|
+
return null;
|
|
1754
|
+
}
|
|
1755
|
+
return Math.min(100, Math.max(0, Math.round((this.latestTokenUsage.used / this.latestTokenUsage.limit) * 100)));
|
|
1756
|
+
}
|
|
1703
1757
|
refreshContextGauge() {
|
|
1704
1758
|
// First try to get context window from provider API (real value)
|
|
1705
1759
|
// Fall back to static model mapping only if provider API is not available
|
|
@@ -1736,7 +1790,7 @@ export class InteractiveShell {
|
|
|
1736
1790
|
// Ignore errors - keep using static mapping
|
|
1737
1791
|
}
|
|
1738
1792
|
}
|
|
1739
|
-
updateContextUsage(percentage,
|
|
1793
|
+
updateContextUsage(percentage, _autoCompactThreshold = CONTEXT_AUTOCOMPACT_PERCENT) {
|
|
1740
1794
|
this.uiAdapter.updateContextUsage(percentage);
|
|
1741
1795
|
this.terminalInput.setContextUsage(percentage);
|
|
1742
1796
|
}
|
|
@@ -1756,7 +1810,7 @@ export class InteractiveShell {
|
|
|
1756
1810
|
profile: this.profileLabel,
|
|
1757
1811
|
workspace: workspaceDisplay,
|
|
1758
1812
|
directory: workspaceDisplay,
|
|
1759
|
-
writes: this.describeEditGuardMode()
|
|
1813
|
+
writes: `${this.describeEditGuardMode()} · ${this.hasRecordedWrites() ? 'dirty' : 'clean'}`,
|
|
1760
1814
|
sessionLabel: this.describeSessionLabel(),
|
|
1761
1815
|
thinkingLabel: (this.thinkingMode || 'off').toString(),
|
|
1762
1816
|
autosave: this.autosaveEnabled,
|
|
@@ -2510,7 +2564,7 @@ export class InteractiveShell {
|
|
|
2510
2564
|
}
|
|
2511
2565
|
switch (this.pendingInteraction.type) {
|
|
2512
2566
|
case 'model-loading':
|
|
2513
|
-
|
|
2567
|
+
this.appendInlineNotice('Still fetching model options. Please wait a moment.', 'info');
|
|
2514
2568
|
this.syncRendererInput();
|
|
2515
2569
|
return true;
|
|
2516
2570
|
case 'model-provider':
|
|
@@ -2544,7 +2598,7 @@ export class InteractiveShell {
|
|
|
2544
2598
|
async processSlashCommand(input) {
|
|
2545
2599
|
const [command] = input.split(/\s+/);
|
|
2546
2600
|
if (!command) {
|
|
2547
|
-
|
|
2601
|
+
this.showSlashWarning('Enter a slash command.');
|
|
2548
2602
|
this.syncRendererInput();
|
|
2549
2603
|
return;
|
|
2550
2604
|
}
|
|
@@ -2720,7 +2774,7 @@ export class InteractiveShell {
|
|
|
2720
2774
|
break;
|
|
2721
2775
|
default:
|
|
2722
2776
|
if (!(await this.tryCustomSlashCommand(command, input))) {
|
|
2723
|
-
|
|
2777
|
+
this.showInlineStatus(`Unknown command "${command}".`, 'warning', { autoClearMs: 1800 });
|
|
2724
2778
|
}
|
|
2725
2779
|
break;
|
|
2726
2780
|
}
|
|
@@ -2740,7 +2794,7 @@ export class InteractiveShell {
|
|
|
2740
2794
|
}
|
|
2741
2795
|
const args = fullInput.slice(command.length).trim();
|
|
2742
2796
|
if (custom.requireInput && !args) {
|
|
2743
|
-
|
|
2797
|
+
this.showSlashWarning(`${command} requires additional input.`);
|
|
2744
2798
|
return true;
|
|
2745
2799
|
}
|
|
2746
2800
|
const prompt = buildCustomCommandPrompt(custom, args, {
|
|
@@ -2750,10 +2804,10 @@ export class InteractiveShell {
|
|
|
2750
2804
|
model: this.sessionState.model,
|
|
2751
2805
|
}).trim();
|
|
2752
2806
|
if (!prompt) {
|
|
2753
|
-
|
|
2807
|
+
this.showSlashWarning(`Custom command ${command} did not produce any text. Check ${custom.source} for errors.`);
|
|
2754
2808
|
return true;
|
|
2755
2809
|
}
|
|
2756
|
-
|
|
2810
|
+
this.showSlashInfo(`Running ${command} from ${custom.source}...`);
|
|
2757
2811
|
await this.processRequest(prompt);
|
|
2758
2812
|
return true;
|
|
2759
2813
|
}
|
|
@@ -2832,7 +2886,7 @@ export class InteractiveShell {
|
|
|
2832
2886
|
}
|
|
2833
2887
|
async runRepoChecksCommand() {
|
|
2834
2888
|
if (this.isProcessing) {
|
|
2835
|
-
|
|
2889
|
+
this.showSlashWarning('Wait for the active response to finish before running checks.');
|
|
2836
2890
|
return;
|
|
2837
2891
|
}
|
|
2838
2892
|
const call = {
|
|
@@ -2840,34 +2894,34 @@ export class InteractiveShell {
|
|
|
2840
2894
|
name: 'run_repo_checks',
|
|
2841
2895
|
arguments: {},
|
|
2842
2896
|
};
|
|
2843
|
-
|
|
2897
|
+
this.showSlashInfo('Running repo checks (npm test/build/lint when available)...');
|
|
2844
2898
|
const output = await this.runtimeSession.toolRuntime.execute(call);
|
|
2845
2899
|
display.showSystemMessage(output);
|
|
2846
2900
|
}
|
|
2847
2901
|
async refreshWorkspaceContextCommand(input) {
|
|
2848
2902
|
if (this.isProcessing) {
|
|
2849
|
-
|
|
2903
|
+
this.showSlashWarning('Wait for the active response to finish before refreshing the snapshot.');
|
|
2850
2904
|
return;
|
|
2851
2905
|
}
|
|
2852
2906
|
const { overrides, error } = this.parseContextOverrideTokens(input);
|
|
2853
2907
|
if (error) {
|
|
2854
|
-
|
|
2908
|
+
this.showSlashWarning(`${error} ${this.describeContextOverrideUsage()}`);
|
|
2855
2909
|
return;
|
|
2856
2910
|
}
|
|
2857
2911
|
if (overrides) {
|
|
2858
2912
|
this.workspaceOptions = { ...this.workspaceOptions, ...overrides };
|
|
2859
2913
|
}
|
|
2860
|
-
|
|
2914
|
+
this.showSlashInfo('Refreshing workspace snapshot...');
|
|
2861
2915
|
const context = buildWorkspaceContext(this.workingDir, this.workspaceOptions);
|
|
2862
2916
|
const profileConfig = this.runtimeSession.refreshWorkspaceContext(context);
|
|
2863
2917
|
const tools = this.runtimeSession.toolRuntime.listProviderTools();
|
|
2864
2918
|
this.baseSystemPrompt = buildInteractiveSystemPrompt(profileConfig.systemPrompt, profileConfig.label, tools);
|
|
2865
2919
|
if (this.rebuildAgent()) {
|
|
2866
|
-
|
|
2920
|
+
this.showSlashInfo(`Workspace snapshot refreshed (${this.describeWorkspaceOptions()}).`);
|
|
2867
2921
|
this.resetChatBoxAfterModelSwap();
|
|
2868
2922
|
}
|
|
2869
2923
|
else {
|
|
2870
|
-
|
|
2924
|
+
this.showSlashWarning('Workspace snapshot refreshed, but the agent failed to rebuild. Run /doctor for details.');
|
|
2871
2925
|
}
|
|
2872
2926
|
}
|
|
2873
2927
|
parseContextOverrideTokens(input) {
|
|
@@ -2942,7 +2996,7 @@ export class InteractiveShell {
|
|
|
2942
2996
|
this.clearAutosaveCommand();
|
|
2943
2997
|
return;
|
|
2944
2998
|
default:
|
|
2945
|
-
|
|
2999
|
+
this.showSlashWarning('Usage: /sessions [list|save <title>|load <id>|delete <id>|new <title>|autosave on|off|clear]');
|
|
2946
3000
|
return;
|
|
2947
3001
|
}
|
|
2948
3002
|
}
|
|
@@ -2979,7 +3033,7 @@ export class InteractiveShell {
|
|
|
2979
3033
|
case 'view': {
|
|
2980
3034
|
const identifier = filtered.shift();
|
|
2981
3035
|
if (!identifier) {
|
|
2982
|
-
|
|
3036
|
+
this.showSlashWarning('Usage: /skills show <skill-id> [sections=metadata,body]');
|
|
2983
3037
|
return;
|
|
2984
3038
|
}
|
|
2985
3039
|
let sectionsArg;
|
|
@@ -3009,13 +3063,13 @@ export class InteractiveShell {
|
|
|
3009
3063
|
break;
|
|
3010
3064
|
}
|
|
3011
3065
|
default:
|
|
3012
|
-
|
|
3066
|
+
this.showSlashWarning('Usage: /skills [list|refresh|show <id> [sections=a,b]]');
|
|
3013
3067
|
break;
|
|
3014
3068
|
}
|
|
3015
3069
|
}
|
|
3016
3070
|
catch (error) {
|
|
3017
3071
|
const message = error instanceof Error ? error.message : String(error);
|
|
3018
|
-
|
|
3072
|
+
this.showInlineStatus(`Skill command failed: ${message}`, 'error');
|
|
3019
3073
|
}
|
|
3020
3074
|
}
|
|
3021
3075
|
async invokeSkillTool(name, args) {
|
|
@@ -3029,16 +3083,15 @@ export class InteractiveShell {
|
|
|
3029
3083
|
handleThinkingCommand(input) {
|
|
3030
3084
|
const value = input.slice('/thinking'.length).trim().toLowerCase();
|
|
3031
3085
|
if (!value) {
|
|
3032
|
-
|
|
3033
|
-
' Press Tab any time to toggle.');
|
|
3086
|
+
this.showSlashInfo(`Thinking mode is currently ${theme.info(this.thinkingMode)}. Usage: /thinking [balanced|extended]. Press Tab any time to toggle.`);
|
|
3034
3087
|
return;
|
|
3035
3088
|
}
|
|
3036
3089
|
if (value !== 'balanced' && value !== 'extended') {
|
|
3037
|
-
|
|
3090
|
+
this.showSlashWarning('Usage: /thinking [balanced|extended]');
|
|
3038
3091
|
return;
|
|
3039
3092
|
}
|
|
3040
3093
|
if (this.isProcessing) {
|
|
3041
|
-
|
|
3094
|
+
this.showSlashWarning('Wait until the current request finishes before changing thinking mode.');
|
|
3042
3095
|
return;
|
|
3043
3096
|
}
|
|
3044
3097
|
this.thinkingMode = value;
|
|
@@ -3063,20 +3116,20 @@ export class InteractiveShell {
|
|
|
3063
3116
|
.slice(1)
|
|
3064
3117
|
.filter(Boolean);
|
|
3065
3118
|
if (!paths.length) {
|
|
3066
|
-
|
|
3119
|
+
this.showSlashInfo(`Usage: /attach <file> [...]. Text-only, max ${Math.round(MAX_ATTACHMENT_BYTES / 1024)}KB per file; large files are truncated.`);
|
|
3067
3120
|
return;
|
|
3068
3121
|
}
|
|
3069
3122
|
if (this.isProcessing) {
|
|
3070
|
-
|
|
3123
|
+
this.showSlashWarning('Wait for the current request to finish before attaching files.');
|
|
3071
3124
|
return;
|
|
3072
3125
|
}
|
|
3073
3126
|
if (!this.agent && !this.rebuildAgent()) {
|
|
3074
|
-
|
|
3127
|
+
this.showSlashWarning('Configure a provider via /secrets before attaching files.');
|
|
3075
3128
|
return;
|
|
3076
3129
|
}
|
|
3077
3130
|
const agent = this.agent;
|
|
3078
3131
|
if (!agent) {
|
|
3079
|
-
|
|
3132
|
+
this.showSlashWarning('No active agent is available. Try again in a moment.');
|
|
3080
3133
|
return;
|
|
3081
3134
|
}
|
|
3082
3135
|
const history = agent.getHistory();
|
|
@@ -3084,7 +3137,7 @@ export class InteractiveShell {
|
|
|
3084
3137
|
for (const rawPath of paths) {
|
|
3085
3138
|
const filePath = resolve(this.workingDir, rawPath);
|
|
3086
3139
|
if (!existsSync(filePath)) {
|
|
3087
|
-
|
|
3140
|
+
this.showSlashWarning(`File not found: ${rawPath}`);
|
|
3088
3141
|
continue;
|
|
3089
3142
|
}
|
|
3090
3143
|
let stats;
|
|
@@ -3092,15 +3145,15 @@ export class InteractiveShell {
|
|
|
3092
3145
|
stats = statSync(filePath);
|
|
3093
3146
|
}
|
|
3094
3147
|
catch (error) {
|
|
3095
|
-
|
|
3148
|
+
this.showSlashWarning(`Unable to read file: ${rawPath} (${error instanceof Error ? error.message : 'unknown error'})`);
|
|
3096
3149
|
continue;
|
|
3097
3150
|
}
|
|
3098
3151
|
if (!stats.isFile()) {
|
|
3099
|
-
|
|
3152
|
+
this.showSlashWarning(`Not a file: ${rawPath}`);
|
|
3100
3153
|
continue;
|
|
3101
3154
|
}
|
|
3102
3155
|
if (stats.size > MAX_ATTACHMENT_BYTES) {
|
|
3103
|
-
|
|
3156
|
+
this.showSlashWarning(`Skipped ${rawPath} (${Math.round(stats.size / 1024)}KB) — limit is ${Math.round(MAX_ATTACHMENT_BYTES / 1024)}KB.`);
|
|
3104
3157
|
continue;
|
|
3105
3158
|
}
|
|
3106
3159
|
let buffer;
|
|
@@ -3108,11 +3161,11 @@ export class InteractiveShell {
|
|
|
3108
3161
|
buffer = readFileSync(filePath);
|
|
3109
3162
|
}
|
|
3110
3163
|
catch (error) {
|
|
3111
|
-
|
|
3164
|
+
this.showSlashWarning(`Failed to read ${rawPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3112
3165
|
continue;
|
|
3113
3166
|
}
|
|
3114
3167
|
if (this.isBinaryBuffer(buffer)) {
|
|
3115
|
-
|
|
3168
|
+
this.showSlashWarning(`Skipped ${rawPath} — binary files are not attached.`);
|
|
3116
3169
|
continue;
|
|
3117
3170
|
}
|
|
3118
3171
|
let content = buffer.toString('utf-8');
|
|
@@ -3135,7 +3188,7 @@ export class InteractiveShell {
|
|
|
3135
3188
|
attachedSummaries.push(`${displayPath} (${kb}KB${truncated ? ', truncated' : ''})`);
|
|
3136
3189
|
}
|
|
3137
3190
|
if (!attachedSummaries.length) {
|
|
3138
|
-
|
|
3191
|
+
this.showSlashWarning('No attachments were added. Ensure files exist, are under the size limit, and are text.');
|
|
3139
3192
|
return;
|
|
3140
3193
|
}
|
|
3141
3194
|
agent.loadHistory(history);
|
|
@@ -3162,7 +3215,7 @@ export class InteractiveShell {
|
|
|
3162
3215
|
const summary = this._fileChangeTracker.getSummary();
|
|
3163
3216
|
const changes = this._fileChangeTracker.getAllChanges();
|
|
3164
3217
|
if (changes.length === 0) {
|
|
3165
|
-
|
|
3218
|
+
this.showSlashInfo('No files modified in this session.');
|
|
3166
3219
|
return;
|
|
3167
3220
|
}
|
|
3168
3221
|
const lines = [];
|
|
@@ -3206,7 +3259,7 @@ export class InteractiveShell {
|
|
|
3206
3259
|
showImprovementSuggestions() {
|
|
3207
3260
|
const suggestions = this.alphaZeroMetrics.getImprovementSuggestions();
|
|
3208
3261
|
if (suggestions.length === 0) {
|
|
3209
|
-
|
|
3262
|
+
this.showSlashInfo('No improvement suggestions at this time. Keep using the shell to generate metrics!');
|
|
3210
3263
|
return;
|
|
3211
3264
|
}
|
|
3212
3265
|
const lines = [
|
|
@@ -3240,8 +3293,8 @@ export class InteractiveShell {
|
|
|
3240
3293
|
const newValue = args[1] === 'on' ? true : args[1] === 'off' ? false : undefined;
|
|
3241
3294
|
const updated = toggleFeatureFlag(matchedKey, newValue);
|
|
3242
3295
|
const status = updated[matchedKey] ? theme.success('enabled') : theme.ui.muted('disabled');
|
|
3243
|
-
|
|
3244
|
-
|
|
3296
|
+
this.showSlashInfo(`Feature "${FEATURE_FLAG_INFO[matchedKey].label}" is now ${status}.`);
|
|
3297
|
+
this.showSlashInfo('Changes will take effect on next launch or after /features refresh.');
|
|
3245
3298
|
return;
|
|
3246
3299
|
}
|
|
3247
3300
|
else if (featureName === 'all') {
|
|
@@ -3252,11 +3305,11 @@ export class InteractiveShell {
|
|
|
3252
3305
|
updated[key] = newValue;
|
|
3253
3306
|
}
|
|
3254
3307
|
saveFeatureFlags(updated);
|
|
3255
|
-
|
|
3308
|
+
this.showSlashInfo(`All features ${newValue ? theme.success('enabled') : theme.ui.muted('disabled')}.`);
|
|
3256
3309
|
return;
|
|
3257
3310
|
}
|
|
3258
3311
|
else {
|
|
3259
|
-
|
|
3312
|
+
this.showSlashWarning(`Unknown feature: ${featureName}`);
|
|
3260
3313
|
}
|
|
3261
3314
|
}
|
|
3262
3315
|
// Show features menu
|
|
@@ -3294,10 +3347,10 @@ export class InteractiveShell {
|
|
|
3294
3347
|
if (subcommand === 'commit') {
|
|
3295
3348
|
const result = commitLearning(args.slice(1).join(' ') || 'Manual learning checkpoint', this.workingDir);
|
|
3296
3349
|
if (result.success) {
|
|
3297
|
-
|
|
3350
|
+
this.showSlashInfo(`Learning committed: ${result.commitHash}`);
|
|
3298
3351
|
}
|
|
3299
3352
|
else {
|
|
3300
|
-
|
|
3353
|
+
this.showSlashWarning(`Could not commit: ${result.error}`);
|
|
3301
3354
|
}
|
|
3302
3355
|
return;
|
|
3303
3356
|
}
|
|
@@ -3892,7 +3945,7 @@ export class InteractiveShell {
|
|
|
3892
3945
|
if (subcommand === 'generate') {
|
|
3893
3946
|
const targetPath = args[1];
|
|
3894
3947
|
if (!targetPath) {
|
|
3895
|
-
|
|
3948
|
+
this.showSlashWarning('Usage: /test generate <file-path>');
|
|
3896
3949
|
return;
|
|
3897
3950
|
}
|
|
3898
3951
|
display.showSystemMessage(theme.gradient.primary(`🧪 Generating Test Flows for ${targetPath}...`));
|
|
@@ -4076,12 +4129,12 @@ export class InteractiveShell {
|
|
|
4076
4129
|
async saveSessionCommand(title) {
|
|
4077
4130
|
const agent = this.agent;
|
|
4078
4131
|
if (!agent) {
|
|
4079
|
-
|
|
4132
|
+
this.showSlashWarning('Start a conversation before saving a session.');
|
|
4080
4133
|
return;
|
|
4081
4134
|
}
|
|
4082
4135
|
const history = agent.getHistory();
|
|
4083
4136
|
if (!history || history.length <= 1) {
|
|
4084
|
-
|
|
4137
|
+
this.showSlashWarning('You need at least one user message before saving a session.');
|
|
4085
4138
|
return;
|
|
4086
4139
|
}
|
|
4087
4140
|
const summary = saveSessionSnapshot({
|
|
@@ -4098,17 +4151,17 @@ export class InteractiveShell {
|
|
|
4098
4151
|
this.updateActiveSession(summary, true);
|
|
4099
4152
|
this.sessionResumeNotice = null;
|
|
4100
4153
|
this.autosaveIfEnabled();
|
|
4101
|
-
|
|
4154
|
+
this.showSlashInfo(`Session saved as "${summary.title}" (id ${this.formatSessionId(summary.id)}).`);
|
|
4102
4155
|
}
|
|
4103
4156
|
async loadSessionCommand(selector) {
|
|
4104
4157
|
const summary = this.resolveSessionBySelector(selector);
|
|
4105
4158
|
if (!summary) {
|
|
4106
|
-
|
|
4159
|
+
this.showSlashWarning('No session matches that selection.');
|
|
4107
4160
|
return;
|
|
4108
4161
|
}
|
|
4109
4162
|
const stored = loadSessionById(summary.id);
|
|
4110
4163
|
if (!stored) {
|
|
4111
|
-
|
|
4164
|
+
this.showSlashWarning('Failed to load that session. It may have been corrupted or deleted.');
|
|
4112
4165
|
return;
|
|
4113
4166
|
}
|
|
4114
4167
|
this.cachedHistory = stored.messages;
|
|
@@ -4117,28 +4170,28 @@ export class InteractiveShell {
|
|
|
4117
4170
|
if (this.agent) {
|
|
4118
4171
|
this.agent.loadHistory(stored.messages);
|
|
4119
4172
|
this.sessionResumeNotice = null;
|
|
4120
|
-
|
|
4173
|
+
this.showSlashInfo(`Loaded session "${summary.title}".`);
|
|
4121
4174
|
this.refreshContextGauge();
|
|
4122
4175
|
this.captureHistorySnapshot();
|
|
4123
4176
|
this.pendingHistoryLoad = null;
|
|
4124
4177
|
}
|
|
4125
4178
|
else {
|
|
4126
4179
|
this.pendingHistoryLoad = stored.messages;
|
|
4127
|
-
|
|
4180
|
+
this.showSlashInfo(`Session "${summary.title}" queued to load once the agent is ready.`);
|
|
4128
4181
|
}
|
|
4129
4182
|
this.autosaveIfEnabled();
|
|
4130
4183
|
}
|
|
4131
4184
|
deleteSessionCommand(selector) {
|
|
4132
4185
|
const summary = this.resolveSessionBySelector(selector);
|
|
4133
4186
|
if (!summary) {
|
|
4134
|
-
|
|
4187
|
+
this.showSlashWarning('No session matches that selection.');
|
|
4135
4188
|
return;
|
|
4136
4189
|
}
|
|
4137
4190
|
if (!deleteSession(summary.id)) {
|
|
4138
|
-
|
|
4191
|
+
this.showSlashWarning('Unable to delete that session.');
|
|
4139
4192
|
return;
|
|
4140
4193
|
}
|
|
4141
|
-
|
|
4194
|
+
this.showSlashInfo(`Deleted session "${summary.title}".`);
|
|
4142
4195
|
if (this.activeSessionId === summary.id) {
|
|
4143
4196
|
this.activeSessionId = null;
|
|
4144
4197
|
this.activeSessionTitle = null;
|
|
@@ -4160,22 +4213,22 @@ export class InteractiveShell {
|
|
|
4160
4213
|
this.sessionResumeNotice = null;
|
|
4161
4214
|
saveSessionPreferences({ lastSessionId: null });
|
|
4162
4215
|
clearAutosaveSnapshot(this.profile);
|
|
4163
|
-
|
|
4216
|
+
this.showSlashInfo('Started a new empty session.');
|
|
4164
4217
|
this.refreshContextGauge();
|
|
4165
4218
|
}
|
|
4166
4219
|
toggleAutosaveCommand(value) {
|
|
4167
4220
|
if (!value) {
|
|
4168
|
-
|
|
4221
|
+
this.showSlashWarning('Usage: /sessions autosave on|off');
|
|
4169
4222
|
return;
|
|
4170
4223
|
}
|
|
4171
4224
|
const normalized = value.toLowerCase();
|
|
4172
4225
|
if (normalized !== 'on' && normalized !== 'off') {
|
|
4173
|
-
|
|
4226
|
+
this.showSlashWarning('Usage: /sessions autosave on|off');
|
|
4174
4227
|
return;
|
|
4175
4228
|
}
|
|
4176
4229
|
this.autosaveEnabled = normalized === 'on';
|
|
4177
4230
|
saveSessionPreferences({ autosave: this.autosaveEnabled });
|
|
4178
|
-
|
|
4231
|
+
this.showSlashInfo(`Autosave ${this.autosaveEnabled ? 'enabled' : 'disabled'}.`);
|
|
4179
4232
|
if (!this.autosaveEnabled) {
|
|
4180
4233
|
clearAutosaveSnapshot(this.profile);
|
|
4181
4234
|
}
|
|
@@ -4186,7 +4239,7 @@ export class InteractiveShell {
|
|
|
4186
4239
|
}
|
|
4187
4240
|
clearAutosaveCommand() {
|
|
4188
4241
|
clearAutosaveSnapshot(this.profile);
|
|
4189
|
-
|
|
4242
|
+
this.showSlashInfo('Cleared autosave history.');
|
|
4190
4243
|
}
|
|
4191
4244
|
// ==================== Erosolar-CLI Style Commands ====================
|
|
4192
4245
|
async handleRewindCommand(_input) {
|
|
@@ -4314,7 +4367,7 @@ export class InteractiveShell {
|
|
|
4314
4367
|
}
|
|
4315
4368
|
showCompactionHistory() {
|
|
4316
4369
|
if (!this.contextCompactionLog.length) {
|
|
4317
|
-
|
|
4370
|
+
this.showSlashInfo('No context compactions recorded yet.');
|
|
4318
4371
|
return;
|
|
4319
4372
|
}
|
|
4320
4373
|
const entries = [...this.contextCompactionLog].slice(-10).reverse();
|
|
@@ -4346,7 +4399,7 @@ export class InteractiveShell {
|
|
|
4346
4399
|
}
|
|
4347
4400
|
display.clear();
|
|
4348
4401
|
clearAutosaveSnapshot(this.profile);
|
|
4349
|
-
|
|
4402
|
+
this.showSlashInfo('Conversation cleared. Starting fresh.');
|
|
4350
4403
|
this.syncRendererInput();
|
|
4351
4404
|
}
|
|
4352
4405
|
async handleDiffCommand() {
|
|
@@ -4354,7 +4407,7 @@ export class InteractiveShell {
|
|
|
4354
4407
|
await execAsync('git rev-parse --is-inside-work-tree', { cwd: this.workingDir });
|
|
4355
4408
|
}
|
|
4356
4409
|
catch {
|
|
4357
|
-
|
|
4410
|
+
this.showSlashWarning('Not a git repository. No diff to show.');
|
|
4358
4411
|
return;
|
|
4359
4412
|
}
|
|
4360
4413
|
const cachedResults = [];
|
|
@@ -4394,7 +4447,7 @@ export class InteractiveShell {
|
|
|
4394
4447
|
// ignore diff errors
|
|
4395
4448
|
}
|
|
4396
4449
|
if (results.length === 0 && cachedResults.length === 0) {
|
|
4397
|
-
|
|
4450
|
+
this.showSlashInfo('No changes detected. Working tree is clean.');
|
|
4398
4451
|
return;
|
|
4399
4452
|
}
|
|
4400
4453
|
display.showSystemMessage([...cachedResults, ...results].join('\n'));
|
|
@@ -4452,11 +4505,11 @@ export class InteractiveShell {
|
|
|
4452
4505
|
// Show session list and auto-select the most recent
|
|
4453
4506
|
const sessions = listSessions(this.profile);
|
|
4454
4507
|
if (sessions.length > 0) {
|
|
4455
|
-
|
|
4508
|
+
this.showSlashInfo('Resuming most recent session...');
|
|
4456
4509
|
await this.loadSessionCommand('1');
|
|
4457
4510
|
}
|
|
4458
4511
|
else {
|
|
4459
|
-
|
|
4512
|
+
this.showSlashWarning('No previous sessions found. Use /sessions to manage sessions.');
|
|
4460
4513
|
}
|
|
4461
4514
|
}
|
|
4462
4515
|
}
|
|
@@ -4475,18 +4528,18 @@ export class InteractiveShell {
|
|
|
4475
4528
|
}
|
|
4476
4529
|
async handleReviewCommand() {
|
|
4477
4530
|
if (this.isProcessing) {
|
|
4478
|
-
|
|
4531
|
+
this.showSlashWarning('Wait for the current operation to finish.');
|
|
4479
4532
|
return;
|
|
4480
4533
|
}
|
|
4481
|
-
|
|
4534
|
+
this.showSlashInfo('Triggering code review of pending changes...');
|
|
4482
4535
|
await this.processRequest('Please review all the code changes we made in this session. Look for bugs, security issues, and improvements.');
|
|
4483
4536
|
}
|
|
4484
4537
|
async handleSecurityReviewCommand() {
|
|
4485
4538
|
if (this.isProcessing) {
|
|
4486
|
-
|
|
4539
|
+
this.showSlashWarning('Wait for the current operation to finish.');
|
|
4487
4540
|
return;
|
|
4488
4541
|
}
|
|
4489
|
-
|
|
4542
|
+
this.showSlashInfo('Running comprehensive security review...');
|
|
4490
4543
|
await this.processRequest('Please perform a comprehensive security review of the codebase. Check for OWASP top 10 vulnerabilities, insecure patterns, and potential attack vectors.');
|
|
4491
4544
|
}
|
|
4492
4545
|
handleBugCommand() {
|
|
@@ -4552,7 +4605,7 @@ export class InteractiveShell {
|
|
|
4552
4605
|
this.setCriticalApprovalMode('approval', 'command');
|
|
4553
4606
|
return;
|
|
4554
4607
|
}
|
|
4555
|
-
|
|
4608
|
+
this.showSlashWarning('Usage: /approvals [auto|ask|status]');
|
|
4556
4609
|
}
|
|
4557
4610
|
handlePermissionsCommand() {
|
|
4558
4611
|
const lines = [];
|
|
@@ -4602,38 +4655,39 @@ export class InteractiveShell {
|
|
|
4602
4655
|
const currentVersion = this.version || this.getPackageInfo().version || '1.7.458';
|
|
4603
4656
|
const updateInfo = await checkForUpdates(currentVersion);
|
|
4604
4657
|
if (!updateInfo) {
|
|
4605
|
-
|
|
4658
|
+
this.showSlashWarning('Unable to check for updates (network issue or timeout).');
|
|
4606
4659
|
return;
|
|
4607
4660
|
}
|
|
4608
4661
|
if (!updateInfo.updateAvailable) {
|
|
4609
|
-
|
|
4662
|
+
this.showSlashSuccess(`You're on the latest version (v${updateInfo.current}).`);
|
|
4610
4663
|
return;
|
|
4611
4664
|
}
|
|
4612
4665
|
// Show update info and perform update immediately (no interactive prompt)
|
|
4613
|
-
|
|
4666
|
+
this.showSlashInfo(`Update available: v${updateInfo.current} → v${updateInfo.latest}`);
|
|
4614
4667
|
await performUpdate(updateInfo, (msg) => this.streamEventBlock(msg));
|
|
4615
4668
|
}
|
|
4616
4669
|
catch (error) {
|
|
4617
|
-
|
|
4670
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
4671
|
+
this.showInlineStatus(`Update check failed: ${detail}`, 'error');
|
|
4618
4672
|
}
|
|
4619
4673
|
return;
|
|
4620
4674
|
}
|
|
4621
4675
|
if (subcommand === 'auto' || subcommand === 'always') {
|
|
4622
4676
|
saveSessionPreferences({ autoUpdate: true });
|
|
4623
|
-
|
|
4677
|
+
this.showSlashSuccess('Auto-update enabled. Updates will install automatically.');
|
|
4624
4678
|
return;
|
|
4625
4679
|
}
|
|
4626
4680
|
if (subcommand === 'skip' || subcommand === 'never' || subcommand === 'off') {
|
|
4627
4681
|
saveSessionPreferences({ autoUpdate: false });
|
|
4628
|
-
|
|
4682
|
+
this.showSlashInfo('Auto-update disabled. Updates will be skipped silently.');
|
|
4629
4683
|
return;
|
|
4630
4684
|
}
|
|
4631
4685
|
if (subcommand === 'ask' || subcommand === 'prompt' || subcommand === 'reset' || subcommand === 'notify') {
|
|
4632
4686
|
saveSessionPreferences({ autoUpdate: null });
|
|
4633
|
-
|
|
4687
|
+
this.showSlashInfo('Update preference reset. You will see a notification when updates are available.');
|
|
4634
4688
|
return;
|
|
4635
4689
|
}
|
|
4636
|
-
|
|
4690
|
+
this.showSlashWarning('Usage: /update [check|auto|skip|notify|status]');
|
|
4637
4691
|
}
|
|
4638
4692
|
handleInitCommand(input) {
|
|
4639
4693
|
const tokens = input.split(/\s+/).slice(1);
|
|
@@ -4641,7 +4695,7 @@ export class InteractiveShell {
|
|
|
4641
4695
|
const target = join(this.workingDir, 'AGENTS.md');
|
|
4642
4696
|
if (confirm) {
|
|
4643
4697
|
if (existsSync(target)) {
|
|
4644
|
-
|
|
4698
|
+
this.showSlashWarning(`AGENTS.md already exists at ${target}.`);
|
|
4645
4699
|
return;
|
|
4646
4700
|
}
|
|
4647
4701
|
const template = [
|
|
@@ -4668,11 +4722,11 @@ export class InteractiveShell {
|
|
|
4668
4722
|
].join('\n');
|
|
4669
4723
|
try {
|
|
4670
4724
|
writeFileSync(target, `${template}\n`, { flag: 'wx' });
|
|
4671
|
-
|
|
4725
|
+
this.showSlashSuccess(`Created ${target}. Update it with project-specific guidance for Codex.`);
|
|
4672
4726
|
}
|
|
4673
4727
|
catch (error) {
|
|
4674
4728
|
const message = error instanceof Error ? error.message : String(error);
|
|
4675
|
-
|
|
4729
|
+
this.showInlineStatus(`Failed to create AGENTS.md: ${message}`, 'error');
|
|
4676
4730
|
}
|
|
4677
4731
|
return;
|
|
4678
4732
|
}
|
|
@@ -4695,10 +4749,10 @@ export class InteractiveShell {
|
|
|
4695
4749
|
}
|
|
4696
4750
|
async handleCompactCommand() {
|
|
4697
4751
|
if (this.isProcessing) {
|
|
4698
|
-
|
|
4752
|
+
this.showSlashWarning('Wait for the current operation to finish.');
|
|
4699
4753
|
return;
|
|
4700
4754
|
}
|
|
4701
|
-
|
|
4755
|
+
this.showSlashInfo('Compacting conversation context...');
|
|
4702
4756
|
await this.performContextCompaction('Manual /compact request');
|
|
4703
4757
|
}
|
|
4704
4758
|
// ==================== End Erosolar-CLI Style Commands ====================
|
|
@@ -4859,9 +4913,136 @@ export class InteractiveShell {
|
|
|
4859
4913
|
return `${shown.join(', ')}${suffix}`;
|
|
4860
4914
|
}
|
|
4861
4915
|
showInlinePanel(lines) {
|
|
4916
|
+
const normalized = (lines ?? [])
|
|
4917
|
+
.map(line => line.replace(/\s+$/g, ''))
|
|
4918
|
+
.filter(line => line.trim().length > 0);
|
|
4919
|
+
// Reset any pending auto-clear timer whenever we render a fresh panel
|
|
4920
|
+
if (this.inlinePanelClearTimer) {
|
|
4921
|
+
clearTimeout(this.inlinePanelClearTimer);
|
|
4922
|
+
this.inlinePanelClearTimer = null;
|
|
4923
|
+
}
|
|
4924
|
+
this.inlinePanelBase = normalized;
|
|
4925
|
+
this.inlinePanelNotices = [];
|
|
4926
|
+
this.flushInlinePanel();
|
|
4927
|
+
}
|
|
4928
|
+
appendInlineNotice(message, tone = 'info') {
|
|
4929
|
+
const normalized = message.trim();
|
|
4930
|
+
if (!normalized)
|
|
4931
|
+
return;
|
|
4932
|
+
this.inlinePanelNotices.push({ text: normalized, tone });
|
|
4933
|
+
// Keep the most recent notices to prevent runaway growth
|
|
4934
|
+
const MAX_NOTICES = 3;
|
|
4935
|
+
if (this.inlinePanelNotices.length > MAX_NOTICES) {
|
|
4936
|
+
this.inlinePanelNotices = this.inlinePanelNotices.slice(-MAX_NOTICES);
|
|
4937
|
+
}
|
|
4938
|
+
this.flushInlinePanel();
|
|
4939
|
+
}
|
|
4940
|
+
showInlineStatus(message, tone = 'info', options) {
|
|
4941
|
+
const normalized = message.trim();
|
|
4942
|
+
if (!normalized) {
|
|
4943
|
+
this.clearInlinePanel();
|
|
4944
|
+
return;
|
|
4945
|
+
}
|
|
4946
|
+
if (this.inlinePanelClearTimer) {
|
|
4947
|
+
clearTimeout(this.inlinePanelClearTimer);
|
|
4948
|
+
this.inlinePanelClearTimer = null;
|
|
4949
|
+
}
|
|
4950
|
+
this.inlinePanelBase = [];
|
|
4951
|
+
this.inlinePanelNotices = [{ text: normalized, tone }];
|
|
4952
|
+
this.flushInlinePanel();
|
|
4953
|
+
if (options?.autoClearMs && options.autoClearMs > 0) {
|
|
4954
|
+
this.inlinePanelClearTimer = setTimeout(() => {
|
|
4955
|
+
this.clearInlinePanel();
|
|
4956
|
+
}, options.autoClearMs);
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
flushInlinePanel() {
|
|
4960
|
+
if (!this.inlinePanelBase.length && !this.inlinePanelNotices.length) {
|
|
4961
|
+
display.clearInlinePanel();
|
|
4962
|
+
return;
|
|
4963
|
+
}
|
|
4964
|
+
const lines = [...this.inlinePanelBase];
|
|
4965
|
+
if (this.inlinePanelNotices.length) {
|
|
4966
|
+
if (lines.length) {
|
|
4967
|
+
lines.push('');
|
|
4968
|
+
}
|
|
4969
|
+
for (const notice of this.inlinePanelNotices) {
|
|
4970
|
+
lines.push(this.formatInlineNotice(notice));
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4862
4973
|
display.showInlinePanel(lines);
|
|
4863
4974
|
}
|
|
4975
|
+
formatInlineNotice(notice) {
|
|
4976
|
+
const prefix = (() => {
|
|
4977
|
+
switch (notice.tone) {
|
|
4978
|
+
case 'success':
|
|
4979
|
+
return theme.success('✓');
|
|
4980
|
+
case 'warning':
|
|
4981
|
+
return theme.warning('!');
|
|
4982
|
+
case 'error':
|
|
4983
|
+
return theme.error('✖');
|
|
4984
|
+
case 'info':
|
|
4985
|
+
default:
|
|
4986
|
+
return theme.info('ℹ');
|
|
4987
|
+
}
|
|
4988
|
+
})();
|
|
4989
|
+
return `${prefix} ${notice.text}`;
|
|
4990
|
+
}
|
|
4991
|
+
showSlashWarning(message) {
|
|
4992
|
+
const normalized = message.trim();
|
|
4993
|
+
if (!normalized)
|
|
4994
|
+
return;
|
|
4995
|
+
if (this.inlineCommandActive || this.pendingInteraction) {
|
|
4996
|
+
if (this.inlinePanelBase.length) {
|
|
4997
|
+
this.appendInlineNotice(normalized, 'warning');
|
|
4998
|
+
}
|
|
4999
|
+
else {
|
|
5000
|
+
this.showInlineStatus(normalized, 'warning', { autoClearMs: 1800 });
|
|
5001
|
+
}
|
|
5002
|
+
this.syncRendererInput();
|
|
5003
|
+
return;
|
|
5004
|
+
}
|
|
5005
|
+
display.showWarning(normalized);
|
|
5006
|
+
}
|
|
5007
|
+
showSlashInfo(message) {
|
|
5008
|
+
const normalized = message.trim();
|
|
5009
|
+
if (!normalized)
|
|
5010
|
+
return;
|
|
5011
|
+
if (this.inlineCommandActive || this.pendingInteraction) {
|
|
5012
|
+
if (this.inlinePanelBase.length) {
|
|
5013
|
+
this.appendInlineNotice(normalized, 'info');
|
|
5014
|
+
}
|
|
5015
|
+
else {
|
|
5016
|
+
this.showInlineStatus(normalized, 'info', { autoClearMs: 1600 });
|
|
5017
|
+
}
|
|
5018
|
+
this.syncRendererInput();
|
|
5019
|
+
return;
|
|
5020
|
+
}
|
|
5021
|
+
display.showInfo(normalized);
|
|
5022
|
+
}
|
|
5023
|
+
showSlashSuccess(message) {
|
|
5024
|
+
const normalized = message.trim();
|
|
5025
|
+
if (!normalized)
|
|
5026
|
+
return;
|
|
5027
|
+
if (this.inlineCommandActive || this.pendingInteraction) {
|
|
5028
|
+
if (this.inlinePanelBase.length) {
|
|
5029
|
+
this.appendInlineNotice(normalized, 'success');
|
|
5030
|
+
}
|
|
5031
|
+
else {
|
|
5032
|
+
this.showInlineStatus(normalized, 'success', { autoClearMs: 1600 });
|
|
5033
|
+
}
|
|
5034
|
+
this.syncRendererInput();
|
|
5035
|
+
return;
|
|
5036
|
+
}
|
|
5037
|
+
display.showSuccess(normalized);
|
|
5038
|
+
}
|
|
4864
5039
|
clearInlinePanel() {
|
|
5040
|
+
if (this.inlinePanelClearTimer) {
|
|
5041
|
+
clearTimeout(this.inlinePanelClearTimer);
|
|
5042
|
+
this.inlinePanelClearTimer = null;
|
|
5043
|
+
}
|
|
5044
|
+
this.inlinePanelBase = [];
|
|
5045
|
+
this.inlinePanelNotices = [];
|
|
4865
5046
|
display.clearInlinePanel();
|
|
4866
5047
|
}
|
|
4867
5048
|
shouldCaptureInlinePanel() {
|
|
@@ -4884,9 +5065,8 @@ export class InteractiveShell {
|
|
|
4884
5065
|
const providerStatuses = await quickCheckProviders();
|
|
4885
5066
|
const providerOptions = this.buildProviderOptionsWithDiscovery(providerStatuses);
|
|
4886
5067
|
if (!providerOptions.length) {
|
|
4887
|
-
display.showWarning('No providers are available.');
|
|
4888
5068
|
this.pendingInteraction = null;
|
|
4889
|
-
this.
|
|
5069
|
+
this.showInlineStatus('No providers are available.', 'warning');
|
|
4890
5070
|
this.syncRendererInput();
|
|
4891
5071
|
return;
|
|
4892
5072
|
}
|
|
@@ -4906,9 +5086,8 @@ export class InteractiveShell {
|
|
|
4906
5086
|
this.pendingInteraction = { type: 'model-provider', options: providerOptions };
|
|
4907
5087
|
}
|
|
4908
5088
|
catch (error) {
|
|
4909
|
-
display.showError('Failed to load model list. Try again in a moment.', error);
|
|
4910
5089
|
this.pendingInteraction = null;
|
|
4911
|
-
this.
|
|
5090
|
+
this.showInlineStatus('Failed to load model list. Try again in a moment.', 'error');
|
|
4912
5091
|
this.syncRendererInput();
|
|
4913
5092
|
}
|
|
4914
5093
|
}
|
|
@@ -5039,9 +5218,8 @@ export class InteractiveShell {
|
|
|
5039
5218
|
}
|
|
5040
5219
|
}
|
|
5041
5220
|
if (!allModels.length) {
|
|
5042
|
-
display.showWarning(`No models available for ${option.label}.`);
|
|
5043
5221
|
this.pendingInteraction = null;
|
|
5044
|
-
this.
|
|
5222
|
+
this.showInlineStatus(`No models available for ${option.label}.`, 'warning');
|
|
5045
5223
|
return;
|
|
5046
5224
|
}
|
|
5047
5225
|
const lines = [
|
|
@@ -5079,7 +5257,7 @@ export class InteractiveShell {
|
|
|
5079
5257
|
showToolsMenu() {
|
|
5080
5258
|
const options = getToolToggleOptions();
|
|
5081
5259
|
if (!options.length) {
|
|
5082
|
-
|
|
5260
|
+
this.showInlineStatus('No configurable tools are available.', 'warning', { autoClearMs: 1600 });
|
|
5083
5261
|
return;
|
|
5084
5262
|
}
|
|
5085
5263
|
const selection = buildEnabledToolSet(loadToolSettings(), this.profile);
|
|
@@ -5422,7 +5600,7 @@ export class InteractiveShell {
|
|
|
5422
5600
|
}
|
|
5423
5601
|
showAgentsMenu() {
|
|
5424
5602
|
if (!this.agentMenu) {
|
|
5425
|
-
|
|
5603
|
+
this.showSlashWarning('Agent selection is not available in this CLI.');
|
|
5426
5604
|
return;
|
|
5427
5605
|
}
|
|
5428
5606
|
const lines = [
|
|
@@ -5464,27 +5642,26 @@ export class InteractiveShell {
|
|
|
5464
5642
|
}
|
|
5465
5643
|
const trimmed = input.trim();
|
|
5466
5644
|
if (!trimmed) {
|
|
5467
|
-
|
|
5645
|
+
this.appendInlineNotice('Enter a number or type cancel.', 'warning');
|
|
5468
5646
|
this.syncRendererInput();
|
|
5469
5647
|
return;
|
|
5470
5648
|
}
|
|
5471
5649
|
if (trimmed.toLowerCase() === 'cancel') {
|
|
5472
5650
|
this.pendingInteraction = null;
|
|
5473
|
-
|
|
5474
|
-
this.clearInlinePanel();
|
|
5651
|
+
this.showInlineStatus('Model selection cancelled.', 'info', { autoClearMs: 1400 });
|
|
5475
5652
|
this.syncRendererInput();
|
|
5476
5653
|
return;
|
|
5477
5654
|
}
|
|
5478
5655
|
const choice = Number.parseInt(trimmed, 10);
|
|
5479
5656
|
const optionCount = pending.options.length;
|
|
5480
5657
|
if (!Number.isFinite(choice) || choice < 1 || choice > optionCount) {
|
|
5481
|
-
|
|
5658
|
+
this.appendInlineNotice(`Enter a number between 1 and ${optionCount}.`, 'warning');
|
|
5482
5659
|
this.syncRendererInput();
|
|
5483
5660
|
return;
|
|
5484
5661
|
}
|
|
5485
5662
|
const option = pending.options[choice - 1];
|
|
5486
5663
|
if (!option) {
|
|
5487
|
-
|
|
5664
|
+
this.appendInlineNotice(`Enter a number between 1 and ${optionCount}.`, 'warning');
|
|
5488
5665
|
this.syncRendererInput();
|
|
5489
5666
|
return;
|
|
5490
5667
|
}
|
|
@@ -5499,7 +5676,7 @@ export class InteractiveShell {
|
|
|
5499
5676
|
const trimmed = input.trim();
|
|
5500
5677
|
const optionCount = pending.options.length;
|
|
5501
5678
|
if (!trimmed) {
|
|
5502
|
-
|
|
5679
|
+
this.appendInlineNotice(`Enter 1-${optionCount}, "back", or "cancel".`, 'warning');
|
|
5503
5680
|
this.syncRendererInput();
|
|
5504
5681
|
return;
|
|
5505
5682
|
}
|
|
@@ -5510,20 +5687,19 @@ export class InteractiveShell {
|
|
|
5510
5687
|
}
|
|
5511
5688
|
if (trimmed.toLowerCase() === 'cancel') {
|
|
5512
5689
|
this.pendingInteraction = null;
|
|
5513
|
-
|
|
5514
|
-
this.clearInlinePanel();
|
|
5690
|
+
this.showInlineStatus('Model selection cancelled.', 'info', { autoClearMs: 1400 });
|
|
5515
5691
|
this.syncRendererInput();
|
|
5516
5692
|
return;
|
|
5517
5693
|
}
|
|
5518
5694
|
const choice = Number.parseInt(trimmed, 10);
|
|
5519
5695
|
if (!Number.isFinite(choice) || choice < 1 || choice > optionCount) {
|
|
5520
|
-
|
|
5696
|
+
this.appendInlineNotice(`Enter 1-${optionCount}, "back", or "cancel".`, 'warning');
|
|
5521
5697
|
this.syncRendererInput();
|
|
5522
5698
|
return;
|
|
5523
5699
|
}
|
|
5524
5700
|
const preset = pending.options[choice - 1];
|
|
5525
5701
|
if (!preset) {
|
|
5526
|
-
|
|
5702
|
+
this.appendInlineNotice(`Enter 1-${optionCount}, "back", or "cancel".`, 'warning');
|
|
5527
5703
|
this.syncRendererInput();
|
|
5528
5704
|
return;
|
|
5529
5705
|
}
|
|
@@ -5547,13 +5723,16 @@ export class InteractiveShell {
|
|
|
5547
5723
|
reasoningEffort: preset.reasoningEffort,
|
|
5548
5724
|
};
|
|
5549
5725
|
this.applyPresetReasoningDefaults();
|
|
5550
|
-
|
|
5551
|
-
|
|
5726
|
+
const rebuilt = this.rebuildAgent();
|
|
5727
|
+
if (rebuilt) {
|
|
5552
5728
|
this.refreshSessionContext();
|
|
5553
5729
|
this.persistSessionPreference();
|
|
5554
5730
|
this.resetChatBoxAfterModelSwap();
|
|
5731
|
+
this.showInlineStatus(`Switched to ${preset.label}.`, 'success', { autoClearMs: 1600 });
|
|
5732
|
+
}
|
|
5733
|
+
else {
|
|
5734
|
+
this.showInlineStatus(`Failed to switch to ${preset.label}. Run /doctor for details.`, 'warning');
|
|
5555
5735
|
}
|
|
5556
|
-
this.clearInlinePanel();
|
|
5557
5736
|
}
|
|
5558
5737
|
async handleSecretSelection(input) {
|
|
5559
5738
|
const pending = this.pendingInteraction;
|
|
@@ -5563,26 +5742,25 @@ export class InteractiveShell {
|
|
|
5563
5742
|
const trimmed = input.trim();
|
|
5564
5743
|
const optionCount = pending.options.length;
|
|
5565
5744
|
if (!trimmed) {
|
|
5566
|
-
|
|
5745
|
+
this.appendInlineNotice(`Enter 1-${optionCount} or "cancel".`, 'warning');
|
|
5567
5746
|
this.syncRendererInput();
|
|
5568
5747
|
return;
|
|
5569
5748
|
}
|
|
5570
5749
|
if (trimmed.toLowerCase() === 'cancel') {
|
|
5571
5750
|
this.pendingInteraction = null;
|
|
5572
|
-
|
|
5573
|
-
this.clearInlinePanel();
|
|
5751
|
+
this.showInlineStatus('Secret management cancelled.', 'info', { autoClearMs: 1600 });
|
|
5574
5752
|
this.syncRendererInput();
|
|
5575
5753
|
return;
|
|
5576
5754
|
}
|
|
5577
5755
|
const choice = Number.parseInt(trimmed, 10);
|
|
5578
5756
|
if (!Number.isFinite(choice) || choice < 1 || choice > optionCount) {
|
|
5579
|
-
|
|
5757
|
+
this.appendInlineNotice(`Enter 1-${optionCount} or "cancel".`, 'warning');
|
|
5580
5758
|
this.syncRendererInput();
|
|
5581
5759
|
return;
|
|
5582
5760
|
}
|
|
5583
5761
|
const secret = pending.options[choice - 1];
|
|
5584
5762
|
if (!secret) {
|
|
5585
|
-
|
|
5763
|
+
this.appendInlineNotice(`Enter 1-${optionCount} or "cancel".`, 'warning');
|
|
5586
5764
|
this.syncRendererInput();
|
|
5587
5765
|
return;
|
|
5588
5766
|
}
|
|
@@ -5601,7 +5779,7 @@ export class InteractiveShell {
|
|
|
5601
5779
|
}
|
|
5602
5780
|
const trimmed = input.trim();
|
|
5603
5781
|
if (!trimmed) {
|
|
5604
|
-
|
|
5782
|
+
this.appendInlineNotice('Enter a value or type cancel.', 'warning');
|
|
5605
5783
|
this.syncRendererInput();
|
|
5606
5784
|
return;
|
|
5607
5785
|
}
|
|
@@ -5609,14 +5787,12 @@ export class InteractiveShell {
|
|
|
5609
5787
|
this.pendingInteraction = null;
|
|
5610
5788
|
this.pendingSecretRetry = null;
|
|
5611
5789
|
this.terminalInput.setSecretMode(false);
|
|
5612
|
-
|
|
5613
|
-
this.clearInlinePanel();
|
|
5790
|
+
this.showInlineStatus('Secret unchanged.', 'info', { autoClearMs: 1600 });
|
|
5614
5791
|
this.syncRendererInput();
|
|
5615
5792
|
return;
|
|
5616
5793
|
}
|
|
5617
5794
|
try {
|
|
5618
5795
|
setSecretValue(pending.secret.id, trimmed);
|
|
5619
|
-
display.showInfo(`${pending.secret.label} updated.`);
|
|
5620
5796
|
this.pendingInteraction = null;
|
|
5621
5797
|
const deferred = this.pendingSecretRetry;
|
|
5622
5798
|
this.pendingSecretRetry = null;
|
|
@@ -5628,15 +5804,15 @@ export class InteractiveShell {
|
|
|
5628
5804
|
if (deferred) {
|
|
5629
5805
|
await deferred();
|
|
5630
5806
|
}
|
|
5807
|
+
this.showInlineStatus(`${pending.secret.label} updated.`, 'success', { autoClearMs: 1600 });
|
|
5631
5808
|
}
|
|
5632
5809
|
catch (error) {
|
|
5633
5810
|
const message = error instanceof Error ? error.message : String(error);
|
|
5634
|
-
|
|
5811
|
+
this.showInlineStatus(message, 'error');
|
|
5635
5812
|
this.pendingInteraction = null;
|
|
5636
5813
|
this.pendingSecretRetry = null;
|
|
5637
5814
|
}
|
|
5638
5815
|
this.terminalInput.setSecretMode(false);
|
|
5639
|
-
this.clearInlinePanel();
|
|
5640
5816
|
this.syncRendererInput();
|
|
5641
5817
|
}
|
|
5642
5818
|
async processRequest(request, options) {
|
|
@@ -5665,7 +5841,7 @@ export class InteractiveShell {
|
|
|
5665
5841
|
}
|
|
5666
5842
|
// Reset per-request render tracking
|
|
5667
5843
|
this.responseRendered = false;
|
|
5668
|
-
this.
|
|
5844
|
+
this.remainingIssueFixQueued = false;
|
|
5669
5845
|
if (this.shouldLogPrompt(request)) {
|
|
5670
5846
|
this.logUserPrompt(request);
|
|
5671
5847
|
}
|
|
@@ -5677,6 +5853,7 @@ export class InteractiveShell {
|
|
|
5677
5853
|
this.syncRendererInput();
|
|
5678
5854
|
this.renderer?.render();
|
|
5679
5855
|
const requestStartTime = Date.now(); // Alpha Zero 2 timing
|
|
5856
|
+
let elapsedMs = null;
|
|
5680
5857
|
// Clear previous parallel agents and start fresh for new request
|
|
5681
5858
|
const parallelManager = getParallelAgentManager();
|
|
5682
5859
|
parallelManager.clear();
|
|
@@ -5718,7 +5895,7 @@ export class InteractiveShell {
|
|
|
5718
5895
|
this.captureHistorySnapshot();
|
|
5719
5896
|
this.autosaveIfEnabled();
|
|
5720
5897
|
// Track metrics with Alpha Zero 2
|
|
5721
|
-
|
|
5898
|
+
elapsedMs = Date.now() - requestStartTime;
|
|
5722
5899
|
this.alphaZeroMetrics.recordMessage(elapsedMs);
|
|
5723
5900
|
if (!responseText?.trim()) {
|
|
5724
5901
|
display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
|
|
@@ -5835,6 +6012,8 @@ export class InteractiveShell {
|
|
|
5835
6012
|
});
|
|
5836
6013
|
this.responseRendered = true;
|
|
5837
6014
|
}
|
|
6015
|
+
this.lastRunDurationSeconds = elapsedMs !== null ? Math.max(0, Math.round(elapsedMs / 1000)) : null;
|
|
6016
|
+
this.lastRunToolCount = this.currentToolCalls.length;
|
|
5838
6017
|
this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
|
|
5839
6018
|
display.stopThinking(false);
|
|
5840
6019
|
this.uiUpdates.setMode('processing');
|
|
@@ -6144,20 +6323,31 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
|
6144
6323
|
if (!this.verificationEnabled) {
|
|
6145
6324
|
return;
|
|
6146
6325
|
}
|
|
6147
|
-
if (!this.autoContinueEnabled) {
|
|
6148
|
-
if (!this.autoContinueNoticeShown) {
|
|
6149
|
-
display.showSystemMessage('⏹️ Auto-continue is off; skipping post-run verification/fixes.');
|
|
6150
|
-
this.autoContinueNoticeShown = true;
|
|
6151
|
-
}
|
|
6152
|
-
return;
|
|
6153
|
-
}
|
|
6154
6326
|
void (async () => {
|
|
6327
|
+
const allowRetry = this.autoContinueEnabled;
|
|
6328
|
+
let attempt = 0;
|
|
6329
|
+
let buildOk = false;
|
|
6330
|
+
let testsOk = false;
|
|
6155
6331
|
try {
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6332
|
+
do {
|
|
6333
|
+
attempt++;
|
|
6334
|
+
buildOk = await this.enforceAutoBuild(trigger);
|
|
6335
|
+
testsOk = buildOk
|
|
6336
|
+
? await this.enforceAutoTests(trigger, assistantResponse, verificationContext)
|
|
6337
|
+
: false;
|
|
6338
|
+
if (allowRetry && !(buildOk && testsOk)) {
|
|
6339
|
+
display.showSystemMessage('🔁 Auto-continue enabled: retrying verification after fixes...');
|
|
6340
|
+
}
|
|
6341
|
+
} while (allowRetry && attempt < 2 && !(buildOk && testsOk));
|
|
6342
|
+
// If verification still shows issues, queue a follow-up fix
|
|
6343
|
+
const issueSignals = [];
|
|
6344
|
+
if (!buildOk)
|
|
6345
|
+
issueSignals.push('Build is failing after verification.');
|
|
6346
|
+
if (!testsOk)
|
|
6347
|
+
issueSignals.push('Tests are still failing after verification.');
|
|
6348
|
+
if (issueSignals.length) {
|
|
6349
|
+
this.maybeQueueRemainingIssueFixes(issueSignals.join('\n'), 'verification');
|
|
6159
6350
|
}
|
|
6160
|
-
await this.enforceAutoTests(trigger, assistantResponse, verificationContext);
|
|
6161
6351
|
}
|
|
6162
6352
|
catch (error) {
|
|
6163
6353
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -6167,22 +6357,22 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
|
6167
6357
|
}
|
|
6168
6358
|
async enforceAutoTests(trigger, assistantResponse, verificationContext) {
|
|
6169
6359
|
if (this.autoTestInFlight) {
|
|
6170
|
-
return;
|
|
6360
|
+
return true;
|
|
6171
6361
|
}
|
|
6172
6362
|
if (!this.verificationEnabled) {
|
|
6173
|
-
return;
|
|
6363
|
+
return true;
|
|
6174
6364
|
}
|
|
6175
6365
|
const latestChange = this.getLatestFileChangeTimestamp();
|
|
6176
6366
|
if (!latestChange) {
|
|
6177
|
-
return;
|
|
6367
|
+
return true;
|
|
6178
6368
|
}
|
|
6179
6369
|
const latestTest = this.getLatestTestTimestamp();
|
|
6180
6370
|
if (latestTest && latestChange <= latestTest) {
|
|
6181
|
-
return;
|
|
6371
|
+
return true;
|
|
6182
6372
|
}
|
|
6183
6373
|
if (!this.lastBuildSucceededAt || this.lastBuildSucceededAt < latestChange) {
|
|
6184
6374
|
display.showSystemMessage('⏭️ Skipping auto-tests because no successful build is recorded for the latest changes.');
|
|
6185
|
-
return;
|
|
6375
|
+
return true;
|
|
6186
6376
|
}
|
|
6187
6377
|
this.autoTestInFlight = true;
|
|
6188
6378
|
const command = 'npm test -- --runInBand';
|
|
@@ -6200,6 +6390,8 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
|
6200
6390
|
const summary = this.buildTestSummary(command, combinedOutput, true);
|
|
6201
6391
|
display.showTestEvent(summary);
|
|
6202
6392
|
this.statusTracker.clearOverride('tests');
|
|
6393
|
+
this.lastTestStatus = 'ok';
|
|
6394
|
+
return true;
|
|
6203
6395
|
}
|
|
6204
6396
|
catch (error) {
|
|
6205
6397
|
combinedOutput = this.formatCommandError(error);
|
|
@@ -6210,6 +6402,8 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
|
6210
6402
|
detail: 'Auto-run npm test failed',
|
|
6211
6403
|
tone: 'danger',
|
|
6212
6404
|
});
|
|
6405
|
+
this.lastTestStatus = 'fail';
|
|
6406
|
+
return false;
|
|
6213
6407
|
}
|
|
6214
6408
|
finally {
|
|
6215
6409
|
try {
|
|
@@ -6256,6 +6450,7 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
|
6256
6450
|
const finishedAt = Date.now();
|
|
6257
6451
|
this.lastAutoBuildRun = finishedAt;
|
|
6258
6452
|
this.lastBuildSucceededAt = finishedAt;
|
|
6453
|
+
this.lastBuildStatus = 'ok';
|
|
6259
6454
|
const outputText = [stdout, stderr].filter(Boolean).join('\n').trim();
|
|
6260
6455
|
display.showSystemMessage('✅ Build succeeded.');
|
|
6261
6456
|
if (outputText && outputText.length < 500) {
|
|
@@ -6269,6 +6464,7 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
|
|
|
6269
6464
|
const finishedAt = Date.now();
|
|
6270
6465
|
this.lastAutoBuildRun = finishedAt;
|
|
6271
6466
|
this.lastBuildSucceededAt = null;
|
|
6467
|
+
this.lastBuildStatus = 'fail';
|
|
6272
6468
|
const errorOutput = this.formatCommandError(error);
|
|
6273
6469
|
display.showWarning('⚠️ Build failed. Feeding errors back to agent...');
|
|
6274
6470
|
if (errorOutput) {
|
|
@@ -6455,6 +6651,94 @@ Return ONLY JSON array:
|
|
|
6455
6651
|
}
|
|
6456
6652
|
display.showTestEvent(results.join('\n'));
|
|
6457
6653
|
}
|
|
6654
|
+
/**
|
|
6655
|
+
* Detect remaining issues in a completion response and queue a follow-up fix if needed.
|
|
6656
|
+
* Runs once per request to avoid infinite loops.
|
|
6657
|
+
*/
|
|
6658
|
+
maybeQueueRemainingIssueFixes(response, source) {
|
|
6659
|
+
if (this.remainingIssueFixQueued) {
|
|
6660
|
+
return;
|
|
6661
|
+
}
|
|
6662
|
+
const issues = this.detectRemainingIssues(response);
|
|
6663
|
+
if (!issues.length) {
|
|
6664
|
+
return;
|
|
6665
|
+
}
|
|
6666
|
+
const uniqueIssues = Array.from(new Set(issues)).slice(0, 6);
|
|
6667
|
+
const fixPrompt = `You indicated the task was complete but there are remaining issues (${source}):
|
|
6668
|
+
|
|
6669
|
+
${uniqueIssues.map(issue => `- ${issue}`).join('\n')}
|
|
6670
|
+
|
|
6671
|
+
Please fix these now. Re-run build/tests as needed. End with TASK_FULLY_COMPLETE when everything truly passes.`;
|
|
6672
|
+
this.remainingIssueFixQueued = true;
|
|
6673
|
+
display.showSystemMessage('🔧 Detected remaining issues after completion; queuing a follow-up fix run.');
|
|
6674
|
+
this.enqueueFollowUpAction({ type: 'continuous', text: fixPrompt });
|
|
6675
|
+
this.refreshQueueIndicators();
|
|
6676
|
+
}
|
|
6677
|
+
/**
|
|
6678
|
+
* Extract probable remaining issues from assistant text.
|
|
6679
|
+
* Looks for explicit "Remaining" sections and common failure phrases.
|
|
6680
|
+
*/
|
|
6681
|
+
detectRemainingIssues(response) {
|
|
6682
|
+
const issues = [];
|
|
6683
|
+
const lines = response.split('\n');
|
|
6684
|
+
// Capture bullet points after a "Remaining" style heading
|
|
6685
|
+
let capturing = false;
|
|
6686
|
+
for (const rawLine of lines) {
|
|
6687
|
+
const line = rawLine.trim();
|
|
6688
|
+
if (!line) {
|
|
6689
|
+
capturing = false;
|
|
6690
|
+
continue;
|
|
6691
|
+
}
|
|
6692
|
+
if (/^(remaining issues?|todos?|pending|open items?|outstanding)/i.test(line)) {
|
|
6693
|
+
capturing = true;
|
|
6694
|
+
continue;
|
|
6695
|
+
}
|
|
6696
|
+
if (capturing && /^[-\d.]+\s+/i.test(line)) {
|
|
6697
|
+
issues.push(line.replace(/^[-\d.]+\s+/, '').trim());
|
|
6698
|
+
continue;
|
|
6699
|
+
}
|
|
6700
|
+
if (capturing && /^[•]/.test(line)) {
|
|
6701
|
+
issues.push(line.replace(/^•\s*/, '').trim());
|
|
6702
|
+
continue;
|
|
6703
|
+
}
|
|
6704
|
+
if (capturing && /^[|]/.test(line)) {
|
|
6705
|
+
// table row bullet capture: | detail |
|
|
6706
|
+
const cell = line.split('|').map(cell => cell.trim()).filter(Boolean);
|
|
6707
|
+
if (cell.length >= 2) {
|
|
6708
|
+
issues.push(cell.slice(0, -1).join(' - '));
|
|
6709
|
+
}
|
|
6710
|
+
continue;
|
|
6711
|
+
}
|
|
6712
|
+
}
|
|
6713
|
+
// Direct pattern checks for common unresolved states
|
|
6714
|
+
const patterns = [
|
|
6715
|
+
{ regex: /\btests?\s+(are\s+)?(still\s+)?failing\b/i, label: 'Tests are failing' },
|
|
6716
|
+
{ regex: /\b(test suites?|tests)\s+failing\b/i, label: 'Tests are failing' },
|
|
6717
|
+
{ regex: /\bbuild (is )?(still )?failing\b/i, label: 'Build is failing' },
|
|
6718
|
+
{ regex: /\bparsing errors?\b/i, label: 'Parsing errors remain' },
|
|
6719
|
+
{ regex: /\bmissing\s+(env|environment)\s+vars?\b/i, label: 'Missing environment variables' },
|
|
6720
|
+
{ regex: /\bneeds?\s+(manual|further)\s+(setup|config|configuration)\b/i, label: 'Manual setup/config required' },
|
|
6721
|
+
{ regex: /\bnot\s+(integrated|deployed|wired)\b/i, label: 'Integration is not complete' },
|
|
6722
|
+
{ regex: /\bremaining\s+(issues?|tasks?|items?)\b/i, label: 'Remaining issues noted' },
|
|
6723
|
+
{ regex: /\bblocked\b/i, label: 'Blocked condition mentioned' },
|
|
6724
|
+
];
|
|
6725
|
+
for (const { regex, label } of patterns) {
|
|
6726
|
+
if (regex.test(response)) {
|
|
6727
|
+
issues.push(label);
|
|
6728
|
+
}
|
|
6729
|
+
}
|
|
6730
|
+
// If the response explicitly lists "remaining" phrases inline
|
|
6731
|
+
const inlineMatches = response.match(/remaining\s+[^\n]{0,80}/gi);
|
|
6732
|
+
if (inlineMatches) {
|
|
6733
|
+
for (const match of inlineMatches) {
|
|
6734
|
+
issues.push(match.trim());
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
return issues
|
|
6738
|
+
.map(issue => issue.trim())
|
|
6739
|
+
.filter(issue => issue.length > 0)
|
|
6740
|
+
.slice(0, 10);
|
|
6741
|
+
}
|
|
6458
6742
|
buildTestSummary(command, output, success) {
|
|
6459
6743
|
const lines = output.split('\n').map(line => line.trim()).filter(Boolean);
|
|
6460
6744
|
const passFailLines = lines.filter(line => /^(PASS|FAIL)\s+/i.test(line)).slice(0, 6);
|
|
@@ -6600,6 +6884,9 @@ Return ONLY JSON array:
|
|
|
6600
6884
|
usage: metadata.usage ?? null,
|
|
6601
6885
|
source: metadata.wasStreamed ? 'stream' : 'final',
|
|
6602
6886
|
});
|
|
6887
|
+
if (finalContent) {
|
|
6888
|
+
this.maybeQueueRemainingIssueFixes(finalContent, 'final-response');
|
|
6889
|
+
}
|
|
6603
6890
|
if (renderedFinal || finalContent) {
|
|
6604
6891
|
this.responseRendered = true;
|
|
6605
6892
|
}
|
|
@@ -6695,6 +6982,12 @@ Return ONLY JSON array:
|
|
|
6695
6982
|
this.renderer?.setActivity('Thinking');
|
|
6696
6983
|
}
|
|
6697
6984
|
},
|
|
6985
|
+
onEditExplanation: (payload) => {
|
|
6986
|
+
const label = payload.files.length ? payload.files.join(', ') : payload.toolName ?? 'edit';
|
|
6987
|
+
const header = label ? `Edit explanation (${label})` : 'Edit explanation';
|
|
6988
|
+
display.showSystemMessage(`${header}:\n${payload.explanation}`);
|
|
6989
|
+
this.ui.controller.recordAssistantThought(payload.explanation);
|
|
6990
|
+
},
|
|
6698
6991
|
onVerificationNeeded: (response, context) => {
|
|
6699
6992
|
this.lastAssistantResponse = response;
|
|
6700
6993
|
void this.runAutoQualityChecks('verification', response, context);
|
|
@@ -6713,7 +7006,7 @@ Return ONLY JSON array:
|
|
|
6713
7006
|
display.showSystemMessage(`⚡ Retry ${attempt}/${maxAttempts}: ${shortError}${error.message.length > 100 ? '...' : ''}`);
|
|
6714
7007
|
this.renderer?.setActivity(`Retrying (${attempt}/${maxAttempts})...`);
|
|
6715
7008
|
},
|
|
6716
|
-
});
|
|
7009
|
+
}, undefined, { explainEdits: true });
|
|
6717
7010
|
// Register global AI enhancer for explore tool - uses active model by default
|
|
6718
7011
|
this.registerExploreAIEnhancer();
|
|
6719
7012
|
// Fetch real context window from provider API (async, updates in background)
|
|
@@ -7496,19 +7789,19 @@ Return ONLY JSON array:
|
|
|
7496
7789
|
const providerOptions = this.buildProviderOptions();
|
|
7497
7790
|
const match = providerOptions.find((opt) => opt.provider.toLowerCase() === targetProvider || opt.label.toLowerCase() === targetProvider);
|
|
7498
7791
|
if (!match) {
|
|
7499
|
-
|
|
7792
|
+
this.showSlashWarning(`Unknown provider "${targetProvider}".`);
|
|
7500
7793
|
const available = providerOptions.map((opt) => opt.provider).join(', ');
|
|
7501
|
-
|
|
7794
|
+
this.showSlashInfo(`Available providers: ${available}`);
|
|
7502
7795
|
return;
|
|
7503
7796
|
}
|
|
7504
7797
|
if (match.provider === this.sessionState.provider) {
|
|
7505
|
-
|
|
7798
|
+
this.showSlashInfo(`Already using ${match.label}.`);
|
|
7506
7799
|
return;
|
|
7507
7800
|
}
|
|
7508
7801
|
// Get default model for the provider
|
|
7509
7802
|
const models = MODEL_PRESETS.filter((preset) => preset.provider === match.provider);
|
|
7510
7803
|
if (!models.length) {
|
|
7511
|
-
|
|
7804
|
+
this.showSlashWarning(`No models available for ${match.label}.`);
|
|
7512
7805
|
return;
|
|
7513
7806
|
}
|
|
7514
7807
|
const defaultModel = models[0];
|
|
@@ -7526,21 +7819,21 @@ Return ONLY JSON array:
|
|
|
7526
7819
|
if (this.rebuildAgent()) {
|
|
7527
7820
|
this.persistSessionPreference();
|
|
7528
7821
|
this.refreshSessionContext();
|
|
7529
|
-
|
|
7822
|
+
this.showSlashSuccess(`Switched from ${this.providerLabel(oldProvider)}/${oldModel} to ${match.label}/${defaultModel.id}`);
|
|
7530
7823
|
this.resetChatBoxAfterModelSwap();
|
|
7531
7824
|
}
|
|
7532
7825
|
else {
|
|
7533
7826
|
// Revert on failure
|
|
7534
7827
|
this.sessionState.provider = oldProvider;
|
|
7535
7828
|
this.sessionState.model = oldModel;
|
|
7536
|
-
|
|
7829
|
+
this.showInlineStatus(`Failed to switch to ${match.label}. Reverted to ${this.providerLabel(oldProvider)}.`, 'error');
|
|
7537
7830
|
}
|
|
7538
7831
|
}
|
|
7539
7832
|
/**
|
|
7540
7833
|
* Discover models from provider APIs.
|
|
7541
7834
|
*/
|
|
7542
7835
|
async discoverModelsCommand() {
|
|
7543
|
-
|
|
7836
|
+
this.showSlashInfo('Discovering models from provider APIs...');
|
|
7544
7837
|
try {
|
|
7545
7838
|
const result = await discoverAllModels();
|
|
7546
7839
|
const lines = [
|
|
@@ -7572,7 +7865,7 @@ Return ONLY JSON array:
|
|
|
7572
7865
|
}
|
|
7573
7866
|
catch (error) {
|
|
7574
7867
|
const message = error instanceof Error ? error.message : String(error);
|
|
7575
|
-
|
|
7868
|
+
this.showInlineStatus(`Discovery failed: ${message}`, 'error');
|
|
7576
7869
|
}
|
|
7577
7870
|
}
|
|
7578
7871
|
/**
|
|
@@ -7581,8 +7874,8 @@ Return ONLY JSON array:
|
|
|
7581
7874
|
showConfiguredProviders() {
|
|
7582
7875
|
const providerOptions = this.buildProviderOptions();
|
|
7583
7876
|
if (!providerOptions.length) {
|
|
7584
|
-
|
|
7585
|
-
|
|
7877
|
+
this.showSlashWarning('No providers are configured.');
|
|
7878
|
+
this.showSlashInfo('Set an API key: OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, etc.');
|
|
7586
7879
|
return;
|
|
7587
7880
|
}
|
|
7588
7881
|
const lines = [
|
|
@@ -7616,8 +7909,8 @@ Return ONLY JSON array:
|
|
|
7616
7909
|
{
|
|
7617
7910
|
const provider = tokens[1]?.toLowerCase();
|
|
7618
7911
|
if (!provider) {
|
|
7619
|
-
|
|
7620
|
-
|
|
7912
|
+
this.showSlashWarning('Usage: /local use <provider>');
|
|
7913
|
+
this.showSlashInfo('Example: /local use ollama');
|
|
7621
7914
|
return;
|
|
7622
7915
|
}
|
|
7623
7916
|
await this.handleProviderCommand(`/provider ${provider}`);
|
|
@@ -7638,7 +7931,7 @@ Return ONLY JSON array:
|
|
|
7638
7931
|
{ id: 'vllm', label: 'vLLM', url: 'http://localhost:8000', envVar: 'VLLM_BASE_URL' },
|
|
7639
7932
|
{ id: 'llamacpp', label: 'llama.cpp', url: 'http://localhost:8080', envVar: 'LLAMACPP_BASE_URL' },
|
|
7640
7933
|
];
|
|
7641
|
-
|
|
7934
|
+
this.showSlashInfo('Scanning for local LLM servers...');
|
|
7642
7935
|
const results = [];
|
|
7643
7936
|
for (const provider of localProviders) {
|
|
7644
7937
|
const baseUrl = process.env[provider.envVar] || provider.url;
|