erosolar-cli 1.7.406 → 1.7.407

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.
@@ -43,6 +43,7 @@ import { enterStreamingMode, exitStreamingMode } from '../ui/globalWriteLock.js'
43
43
  import { setGlobalAIEnhancer } from '../tools/localExplore.js';
44
44
  import { createProvider } from '../providers/providerFactory.js';
45
45
  import { getParallelAgentManager } from '../subagents/parallelAgentManager.js';
46
+ import { formatThinkingContent } from '../ui/textHighlighter.js';
46
47
  const execAsync = promisify(exec);
47
48
  const DROPDOWN_COLORS = [
48
49
  theme.primary,
@@ -147,6 +148,7 @@ export class InteractiveShell {
147
148
  customCommandMap;
148
149
  sessionRestoreConfig;
149
150
  _enabledPlugins;
151
+ autoVerificationInFlight = false;
150
152
  // Auto-test tracking
151
153
  autoTestInFlight = false;
152
154
  // AlphaZero learning tracking
@@ -254,6 +256,11 @@ export class InteractiveShell {
254
256
  description: 'Show available and loaded plugins',
255
257
  category: 'configuration',
256
258
  });
259
+ this.slashCommands.push({
260
+ command: '/contextlog',
261
+ description: 'Show recent context compaction summaries',
262
+ category: 'context',
263
+ });
257
264
  this.slashCommands.push({
258
265
  command: '/offsec',
259
266
  description: 'AlphaZero offensive security run (start/status/next)',
@@ -1455,7 +1462,11 @@ export class InteractiveShell {
1455
1462
  if (this.assistantStreamPhase) {
1456
1463
  this.renderAssistantStreamHeader(this.assistantStreamPhase);
1457
1464
  }
1458
- this.enqueueAssistantStream(content);
1465
+ let output = content;
1466
+ if (this.assistantStreamPhase === 'thought') {
1467
+ output = this.formatThoughtDisplay(content, { streaming: true });
1468
+ }
1469
+ this.enqueueAssistantStream(output);
1459
1470
  }
1460
1471
  setAssistantStreamPhase(phase) {
1461
1472
  if (this.assistantStreamPhase === phase) {
@@ -1534,11 +1545,12 @@ export class InteractiveShell {
1534
1545
  if (!normalized) {
1535
1546
  return;
1536
1547
  }
1548
+ const formatted = type === 'thought' ? this.formatThoughtDisplay(normalized) : normalized;
1537
1549
  if (this.assistantBlocksEnabled && this.assistantBlockRenderer) {
1538
- this.renderAssistantBlock(type, normalized, metadata);
1550
+ this.renderAssistantBlock(type, formatted, metadata);
1539
1551
  return;
1540
1552
  }
1541
- this.renderAssistantFallback(type, normalized, metadata);
1553
+ this.renderAssistantFallback(type, formatted, metadata);
1542
1554
  }
1543
1555
  renderAssistantFallback(type, content, metadata) {
1544
1556
  const header = this.formatAssistantHeader(type, metadata);
@@ -1546,6 +1558,21 @@ export class InteractiveShell {
1546
1558
  const block = `\n${header}\n${compact}\n\n`;
1547
1559
  this.enqueueAssistantStream(block);
1548
1560
  }
1561
+ formatThoughtDisplay(content, options = {}) {
1562
+ const normalized = content.replace(/\r\n/g, '\n');
1563
+ if (options.streaming) {
1564
+ return formatThinkingContent(normalized);
1565
+ }
1566
+ const lines = normalized.split('\n');
1567
+ return lines
1568
+ .map((line, index) => {
1569
+ const prefix = index === 0 ? theme.info(icons.action) : theme.ui.muted(icons.subaction);
1570
+ const trimmed = line.trim();
1571
+ const body = trimmed ? formatThinkingContent(trimmed) : '';
1572
+ return body ? `${prefix} ${body}` : `${prefix}`;
1573
+ })
1574
+ .join('\n');
1575
+ }
1549
1576
  formatAssistantHeader(type, metadata) {
1550
1577
  if (this.assistantBlocksEnabled && this.assistantBlockRenderer) {
1551
1578
  return this.assistantBlockRenderer.formatHeader(type, metadata);
@@ -2245,6 +2272,9 @@ export class InteractiveShell {
2245
2272
  case '/context':
2246
2273
  await this.refreshWorkspaceContextCommand(input);
2247
2274
  break;
2275
+ case '/contextlog':
2276
+ this.showContextSummaryLog(input);
2277
+ break;
2248
2278
  case '/agents':
2249
2279
  this.showAgentsMenu();
2250
2280
  break;
@@ -2543,6 +2573,66 @@ export class InteractiveShell {
2543
2573
  display.showWarning('Workspace snapshot refreshed, but the agent failed to rebuild. Run /doctor for details.');
2544
2574
  }
2545
2575
  }
2576
+ showContextSummaryLog(input) {
2577
+ const agent = this.agent;
2578
+ if (!agent) {
2579
+ display.showWarning('No active agent session. Start one to inspect context summaries.');
2580
+ return;
2581
+ }
2582
+ const contextManager = agent.getContextManager();
2583
+ if (!contextManager?.getSummaryLog) {
2584
+ display.showWarning('Context summary logging is unavailable in this session.');
2585
+ return;
2586
+ }
2587
+ const tokens = input.trim().split(/\s+/).slice(1);
2588
+ let limit = 5;
2589
+ for (const token of tokens) {
2590
+ if (!token)
2591
+ continue;
2592
+ const normalized = token.toLowerCase();
2593
+ if (/^\d+$/.test(token)) {
2594
+ limit = Math.min(Math.max(parseInt(token, 10), 1), 20);
2595
+ }
2596
+ else if (normalized.startsWith('limit=')) {
2597
+ const value = parseInt(normalized.split('=')[1] ?? '', 10);
2598
+ if (!Number.isNaN(value)) {
2599
+ limit = Math.min(Math.max(value, 1), 20);
2600
+ }
2601
+ }
2602
+ }
2603
+ const entries = contextManager.getSummaryLog(limit);
2604
+ if (!entries.length) {
2605
+ display.showInfo('No context summaries captured yet.');
2606
+ return;
2607
+ }
2608
+ const lines = [];
2609
+ lines.push(`${theme.primary('Context summaries')} ${theme.ui.muted(`(latest ${entries.length})`)}`);
2610
+ for (const entry of entries) {
2611
+ const timestamp = new Date(entry.timestamp).toLocaleString();
2612
+ const headerParts = [
2613
+ theme.ui.muted(timestamp),
2614
+ theme.info(entry.method),
2615
+ `removed ${theme.error(String(entry.removed))}`,
2616
+ `kept ${theme.success(String(entry.preserved))}`,
2617
+ ];
2618
+ if (entry.reason) {
2619
+ headerParts.push(theme.ui.muted(entry.reason));
2620
+ }
2621
+ if (entry.model) {
2622
+ headerParts.push(theme.ui.muted(`model=${entry.model}`));
2623
+ }
2624
+ lines.push(headerParts.join(' · '));
2625
+ if (entry.summary) {
2626
+ const trimmed = entry.summary.length > 600 ? `${entry.summary.slice(0, 600)}…` : entry.summary;
2627
+ lines.push(trimmed);
2628
+ }
2629
+ else {
2630
+ lines.push(theme.ui.muted('(no summary text — simple prune)'));
2631
+ }
2632
+ lines.push(''); // spacer
2633
+ }
2634
+ display.showSystemMessage(lines.join('\n'));
2635
+ }
2546
2636
  parseContextOverrideTokens(input) {
2547
2637
  const overrides = {};
2548
2638
  let hasOverride = false;
@@ -5819,7 +5909,25 @@ What's the next action?`;
5819
5909
  }
5820
5910
  if (name === 'bash' || name === 'execute_bash') {
5821
5911
  const command = String(entry.args['command'] ?? '').toLowerCase();
5822
- return command.includes('npm test') || command.includes('yarn test') || command.includes('pnpm test');
5912
+ const patterns = [
5913
+ 'npm test',
5914
+ 'yarn test',
5915
+ 'pnpm test',
5916
+ 'bun test',
5917
+ 'go test',
5918
+ 'cargo test',
5919
+ 'pytest',
5920
+ 'python -m pytest',
5921
+ 'tox',
5922
+ 'mvn test',
5923
+ './mvnw test',
5924
+ 'gradle test',
5925
+ './gradlew test',
5926
+ 'dotnet test',
5927
+ 'make test',
5928
+ 'swift test',
5929
+ ];
5930
+ return patterns.some((pattern) => command.includes(pattern));
5823
5931
  }
5824
5932
  return false;
5825
5933
  }
@@ -5896,10 +6004,31 @@ What's the next action?`;
5896
6004
  }
5897
6005
  if (name === 'bash' || name === 'execute_bash') {
5898
6006
  const command = String(entry.args['command'] ?? '').toLowerCase();
5899
- return (command.includes('npm run build') ||
5900
- command.includes('yarn build') ||
5901
- command.includes('pnpm build') ||
5902
- command.includes('tsc'));
6007
+ const makeBuild = command.startsWith('make') && !command.includes('make test') && !command.includes('make lint');
6008
+ const patterns = [
6009
+ 'npm run build',
6010
+ 'yarn build',
6011
+ 'pnpm build',
6012
+ 'bun run build',
6013
+ 'bun build',
6014
+ 'tsc',
6015
+ 'cargo build',
6016
+ 'go build',
6017
+ 'python -m build',
6018
+ 'mvn -b -dskiptests package',
6019
+ 'mvn package',
6020
+ 'mvn install',
6021
+ './mvnw -b -dskiptests package',
6022
+ './mvnw package',
6023
+ './mvnw install',
6024
+ 'gradle build',
6025
+ 'gradle assemble',
6026
+ './gradlew build',
6027
+ './gradlew assemble',
6028
+ 'dotnet build',
6029
+ 'swift build',
6030
+ ];
6031
+ return makeBuild || patterns.some((pattern) => command.includes(pattern));
5903
6032
  }
5904
6033
  return false;
5905
6034
  }
@@ -6104,6 +6233,7 @@ What's the next action?`;
6104
6233
  // Show notification that context was pruned
6105
6234
  const method = stats['method'];
6106
6235
  const percentage = stats['percentage'];
6236
+ const summarized = stats['summarized'] === true;
6107
6237
  if (method === 'emergency-recovery') {
6108
6238
  display.stream(`\n✅ Context recovery complete. Removed ${removedCount} messages. Context now at ${percentage ?? 'unknown'}%\n`);
6109
6239
  }
@@ -6111,6 +6241,9 @@ What's the next action?`;
6111
6241
  if (typeof percentage === 'number') {
6112
6242
  this.updateContextUsage(percentage);
6113
6243
  }
6244
+ if (summarized) {
6245
+ display.showSystemMessage('Context summary captured. View the latest summaries with /contextlog.');
6246
+ }
6114
6247
  // Ensure prompt remains visible at bottom after context messages
6115
6248
  this.renderPromptArea();
6116
6249
  },
@@ -6248,8 +6381,8 @@ What's the next action?`;
6248
6381
  ].join('\n');
6249
6382
  case 'balanced':
6250
6383
  default:
6251
- // Balanced is the default; avoid injecting extra prompt text to keep context lean.
6252
- return null;
6384
+ // Balanced mode: show thinking for complex reasoning, planning, or multi-step tasks
6385
+ return 'When reasoning through complex tasks, planning approaches, or making decisions, wrap your thought process in <thinking> tags before your response. Keep thinking concise and focused on the decision-making process.';
6253
6386
  }
6254
6387
  }
6255
6388
  buildDisplayMetadata(metadata) {