minovative-mind-cli 2.13.4 → 2.14.0

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.
Files changed (37) hide show
  1. package/README.md +104 -42
  2. package/dist/commands/chat.d.ts +1 -1
  3. package/dist/commands/chat.js +1 -2
  4. package/dist/services/agent/slashCommands.js +47 -23
  5. package/dist/services/agent/syntaxAgent.js +13 -0
  6. package/dist/services/agent/types.d.ts +0 -4
  7. package/dist/services/agent-tools.d.ts +1 -1
  8. package/dist/services/agent-tools.js +2 -2
  9. package/dist/services/agent.d.ts +1 -7
  10. package/dist/services/agent.js +184 -209
  11. package/dist/services/ai.d.ts +19 -4
  12. package/dist/services/ai.js +97 -12
  13. package/dist/services/contextAgent.js +33 -8
  14. package/dist/services/investigationComplexity.js +1 -1
  15. package/dist/services/mentionEngine.d.ts +385 -0
  16. package/dist/services/mentionEngine.js +1395 -0
  17. package/dist/services/orchestration/investigationAgent.js +4 -1
  18. package/dist/services/orchestration/messageBus.d.ts +27 -4
  19. package/dist/services/orchestration/messageBus.js +206 -22
  20. package/dist/services/orchestration/orchestrator.js +4 -0
  21. package/dist/services/orchestration/scopedTools.js +16 -2
  22. package/dist/services/orchestration/subAgent.js +14 -2
  23. package/dist/services/proxyClient.d.ts +38 -2
  24. package/dist/services/proxyClient.js +42 -24
  25. package/dist/services/swebench/sweBenchRunnerService.js +1 -1
  26. package/dist/utils/config.d.ts +75 -0
  27. package/dist/utils/config.js +93 -0
  28. package/dist/utils/contextPrompts.d.ts +28 -4
  29. package/dist/utils/contextPrompts.js +70 -1
  30. package/dist/utils/historyPrompt.d.ts +166 -7
  31. package/dist/utils/historyPrompt.js +775 -30
  32. package/dist/utils/symbolExtractor.d.ts +111 -8
  33. package/dist/utils/symbolExtractor.js +616 -64
  34. package/dist/utils/systemPrompts.d.ts +3 -4
  35. package/dist/utils/systemPrompts.js +5 -34
  36. package/oclif.manifest.json +2 -2
  37. package/package.json +1 -1
@@ -25,8 +25,8 @@ import { renderTerminalMarkdown } from '../utils/terminal.js';
25
25
  const execAsync = promisify(exec);
26
26
  import { debugLog, isDebugOn } from '../utils/logger.js';
27
27
  import { ensureProjectStorage, ensureIgnored, readCache, writeCache, invalidateCacheForDependents, } from '../utils/projectStorage.js';
28
- import { GEMINI_MODELS, isByokEnabled, TPM_COOLING_DELAYS } from '../utils/config.js';
29
- import { createSharedChatSession, getGeneralChatConfig, getPlanExecutionConfig, compressTextUsingFlashLite, generateChatTitle, getGlobalActiveModel, setGlobalActiveModel, summarizeChatHistory, } from './ai.js';
28
+ import { GEMINI_MODELS, TPM_COOLING_DELAYS } from '../utils/config.js';
29
+ import { createSharedChatSession, getGeneralChatConfig, getPlanExecutionConfig, compressTextUsingFlashLite, generateChatTitle, getGlobalActiveModel, getModelThinkingLevel, summarizeChatHistory, } from './ai.js';
30
30
  import { getAndResetTurnUsage } from './proxyClient.js';
31
31
  import { changeLogger } from './changeLogger.js';
32
32
  import { chatHistoryService } from './chatHistoryService.js';
@@ -35,7 +35,7 @@ import { verifyChangedFiles } from './verificationService.js';
35
35
  import { buildContextInjection } from '../utils/contextPrompts.js';
36
36
  import { registerContextFiles } from '../utils/fileReadGuard.js';
37
37
  import { historyText } from '../utils/historyPrompt.js';
38
- import { loadUserProfile, formatUserProfileForContext, extractAndSaveUserInsights, } from './userProfileService.js';
38
+ import { loadUserProfile, formatUserProfileForContext, extractAndSaveUserInsights } from './userProfileService.js';
39
39
  // Submodule Imports
40
40
  import { AsyncInputHandler } from './agent/inputHandler.js';
41
41
  import { processResponse } from './agent/toolLoop.js';
@@ -130,7 +130,6 @@ export async function startAgentLoop(workspaceRoot, version) {
130
130
  chat.setSessionInfo(chatSessionState.id, workspaceRoot);
131
131
  const sessionInputHistory = [];
132
132
  let isRawPasteMode = false;
133
- let isPlanMode = false;
134
133
  // Hook process.stdin.emit to intercept fast stream inputs.
135
134
  // When large buffers containing newlines arrive rapidly, we interpret them as a clipboard paste,
136
135
  // sanitizing and collapsing them to prevent premature command line submission.
@@ -165,7 +164,7 @@ export async function startAgentLoop(workspaceRoot, version) {
165
164
  }
166
165
  inputHandler.stop();
167
166
  // Input collection (delegated to a helper function to avoid nested loop warning)
168
- const { userInput: rawInput, canceled } = await collectUserInput(sessionInputHistory, isPlanMode);
167
+ const { userInput: rawInput, canceled } = await collectUserInput(sessionInputHistory, workspaceRoot);
169
168
  if (canceled) {
170
169
  p.outro(pc.green('Goodbye! Happy coding. 🚀'));
171
170
  return;
@@ -180,11 +179,6 @@ export async function startAgentLoop(workspaceRoot, version) {
180
179
  message: 'Command Menu',
181
180
  options: [
182
181
  { value: '/models', label: '/models', hint: 'Change the active AI model' },
183
- {
184
- value: '/plan',
185
- label: '/plan',
186
- hint: `Toggle plan mode (build a plan without executing) ${isPlanMode ? pc.green('(ON)') : pc.red('(OFF)')}`,
187
- },
188
182
  { value: '/paste', label: '/paste', hint: 'Paste large text directly into the CLI (Press Ctrl+D to submit)' },
189
183
  { value: '/clear', label: '/clear', hint: 'Clear chat session history' },
190
184
  {
@@ -244,16 +238,12 @@ export async function startAgentLoop(workspaceRoot, version) {
244
238
  workspaceRoot,
245
239
  version,
246
240
  isRawPasteMode,
247
- isPlanMode,
248
241
  chatSessionState,
249
242
  };
250
243
  const slashResult = await handleSlashCommand(userInput, slashCtx);
251
244
  if (slashResult.isRawPasteMode !== undefined) {
252
245
  isRawPasteMode = slashResult.isRawPasteMode;
253
246
  }
254
- if (slashResult.isPlanModeOverride !== undefined) {
255
- isPlanMode = slashResult.isPlanModeOverride;
256
- }
257
247
  if (slashResult.userInputOverride !== undefined) {
258
248
  userInput = slashResult.userInputOverride;
259
249
  }
@@ -262,32 +252,7 @@ export async function startAgentLoop(workspaceRoot, version) {
262
252
  }
263
253
  }
264
254
  // ─── Main Execution Cycle ────────────────────────────────────────
265
- let currentInput = userInput;
266
- let cachedContextResult = null;
267
- while (true) {
268
- const execResult = await executeSingleTurn(workspaceRoot, currentInput, chat, inputHandler, chatSessionState, isPlanMode, cachedContextResult);
269
- const planModeReturn = execResult?.planModeReturn;
270
- const contextResult = execResult?.contextResult;
271
- if (isPlanMode && planModeReturn) {
272
- if (planModeReturn === 'proceed') {
273
- isPlanMode = false;
274
- currentInput = 'Please execute the plan we just finalized.';
275
- cachedContextResult = contextResult;
276
- p.log.info(pc.cyan('Proceeding with execution...'));
277
- continue;
278
- }
279
- else if (planModeReturn === 'edit') {
280
- p.log.info(pc.cyan('Please provide additional instructions to revise the plan.'));
281
- break;
282
- }
283
- else if (planModeReturn === 'exit') {
284
- isPlanMode = false;
285
- p.log.info(pc.cyan('Exited plan mode. Returning to normal chat.'));
286
- break;
287
- }
288
- }
289
- break;
290
- }
255
+ await executeSingleTurn(workspaceRoot, userInput, chat, inputHandler, chatSessionState);
291
256
  }
292
257
  }
293
258
  /**
@@ -301,11 +266,8 @@ export async function startAgentLoop(workspaceRoot, version) {
301
266
  * @param chat - The active ProxyChatSession instance maintaining message history.
302
267
  * @param inputHandler - The AsyncInputHandler instance for managing keyboard interrupts and queueing.
303
268
  * @param chatSessionState - Object tracking session statistics, token counts, credit usage, and model metrics.
304
- * @param isPlanMode - Flag indicating whether plan mode (read-only architecture planning) is active.
305
- * @param cachedContextResult - Optional pre-gathered context result to bypass duplicate investigation turns.
306
- * @returns An object containing optional plan mode actions or context results, or void.
307
269
  */
308
- export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHandler, chatSessionState, isPlanMode, cachedContextResult) {
270
+ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHandler, chatSessionState) {
309
271
  return runWithAgentId('main', async () => {
310
272
  const { resetTurnAccumulator } = await import('./metrics.js');
311
273
  resetTurnAccumulator();
@@ -324,71 +286,67 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
324
286
  if (ac.signal.aborted)
325
287
  return;
326
288
  // Stage 0: Summarize chat history if length threshold is met
327
- if (!cachedContextResult) {
328
- const isDebug = isDebugOn();
329
- if (!inputHandler.isCurrentlyPrompting() && !isDebug) {
330
- spinner.start('Checking conversation history...');
331
- }
332
- const historySummarized = await summarizeHistoryIfNeeded(chat, ac.signal);
333
- if (historySummarized) {
334
- debugLog('Chat history was summarized and compressed prior to context investigation.');
335
- }
336
- if (!inputHandler.isCurrentlyPrompting() && !isDebug) {
337
- spinner.stop();
338
- }
289
+ const isDebug = isDebugOn();
290
+ if (!inputHandler.isCurrentlyPrompting() && !isDebug) {
291
+ spinner.start('Checking conversation history...');
292
+ }
293
+ const historySummarized = await summarizeHistoryIfNeeded(chat, ac.signal);
294
+ if (historySummarized) {
295
+ debugLog('Chat history was summarized and compressed prior to context investigation.');
296
+ }
297
+ if (!inputHandler.isCurrentlyPrompting() && !isDebug) {
298
+ spinner.stop();
339
299
  }
340
300
  if (ac.signal.aborted)
341
301
  return;
342
302
  // Stage 1: Gather Workspace Context and route intentions
343
303
  spinner.start('🔍 Investigating workspace...');
344
- let gatherRes = { targetAgent: 'EXECUTE', chainedMessages: [], contextResult: cachedContextResult || null };
345
- if (!cachedContextResult) {
346
- const collector = getMetricCollector();
347
- if (collector)
348
- collector.startTimer('contextGather');
349
- const chatHistory = chat.getRecentHistory(3);
350
- const toolLogs = [];
351
- try {
352
- gatherRes = await gatherContext(workspaceRoot, userInput, chatHistory, inputHandler, ac.signal, (msg) => {
353
- if (!inputHandler.isCurrentlyPrompting()) {
354
- spinner.message(`🔍 Investigating workspace... ${pc.dim(msg)}`);
355
- }
356
- }, (toolMsg, label) => {
357
- const formattedLog = `${pc.dim(`[${label}]`)} ${toolMsg}`;
358
- toolLogs.push(formattedLog);
359
- if (!inputHandler.isCurrentlyPrompting()) {
360
- spinner.message(`🔍 Investigating workspace... ${formattedLog}`);
361
- }
362
- });
363
- }
364
- catch (gatherErr) {
365
- spinner.stop();
366
- process.stdout.write('\x1b[2K\r');
367
- if (ac.signal.aborted || gatherErr?.name === 'AbortError' || gatherErr?.message?.includes('abort')) {
368
- return;
304
+ let gatherRes = { targetAgent: 'EXECUTE', chainedMessages: [] };
305
+ const collector = getMetricCollector();
306
+ if (collector)
307
+ collector.startTimer('contextGather');
308
+ const chatHistory = chat.getRecentHistory(3);
309
+ const toolLogs = [];
310
+ try {
311
+ gatherRes = await gatherContext(workspaceRoot, userInput, chatHistory, inputHandler, ac.signal, (msg) => {
312
+ if (!inputHandler.isCurrentlyPrompting()) {
313
+ spinner.message(`🔍 Investigating workspace... ${pc.dim(msg)}`);
369
314
  }
370
- throw gatherErr;
371
- }
372
- if (collector)
373
- collector.stopTimer('contextGather');
374
- if (ac.signal.aborted) {
375
- spinner.stop();
376
- process.stdout.write('\x1b[2K\r');
377
- return;
378
- }
379
- // Print all collected logs at once to prevent flickering, while spinner is stopped
380
- if (toolLogs.length > 0) {
381
- spinner.stop();
382
- toolLogs.forEach((log) => p.log.step(log));
383
- spinner.start(`🔍 Context gathered successfully.`);
384
- }
385
- // Post-investigation cooling-off delay to allow API Tokens-Per-Minute (TPM) sliding window to settle
386
- await new Promise((resolve) => setTimeout(resolve, TPM_COOLING_DELAYS.POST_INVESTIGATION_MS));
387
- if (ac.signal.aborted) {
388
- spinner.stop();
389
- process.stdout.write('\x1b[2K\r');
315
+ }, (toolMsg, label) => {
316
+ const formattedLog = `${pc.dim(`[${label}]`)} ${toolMsg}`;
317
+ toolLogs.push(formattedLog);
318
+ if (!inputHandler.isCurrentlyPrompting()) {
319
+ spinner.message(`🔍 Investigating workspace... ${formattedLog}`);
320
+ }
321
+ });
322
+ }
323
+ catch (gatherErr) {
324
+ spinner.stop();
325
+ process.stdout.write('\x1b[2K\r');
326
+ if (ac.signal.aborted || gatherErr?.name === 'AbortError' || gatherErr?.message?.includes('abort')) {
390
327
  return;
391
328
  }
329
+ throw gatherErr;
330
+ }
331
+ if (collector)
332
+ collector.stopTimer('contextGather');
333
+ if (ac.signal.aborted) {
334
+ spinner.stop();
335
+ process.stdout.write('\x1b[2K\r');
336
+ return;
337
+ }
338
+ // Print all collected logs at once to prevent flickering, while spinner is stopped
339
+ if (toolLogs.length > 0) {
340
+ spinner.stop();
341
+ toolLogs.forEach((log) => p.log.step(log));
342
+ spinner.start(`🔍 Context gathered successfully.`);
343
+ }
344
+ // Post-investigation cooling-off delay to allow API Tokens-Per-Minute (TPM) sliding window to settle
345
+ await new Promise((resolve) => setTimeout(resolve, TPM_COOLING_DELAYS.POST_INVESTIGATION_MS));
346
+ if (ac.signal.aborted) {
347
+ spinner.stop();
348
+ process.stdout.write('\x1b[2K\r');
349
+ return;
392
350
  }
393
351
  let latestUsage = undefined;
394
352
  // Collect any inputs that were queued while the Context Agent was investigating
@@ -427,21 +385,12 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
427
385
  }
428
386
  // Hot-swap the underlying LLM system instruction context depending on intent (conversational vs execution)
429
387
  debugLog(`Intent Router output: original targetAgent = ${gatherRes.targetAgent}, effective = ${effectiveTargetAgent}`);
430
- let config;
431
- if (isPlanMode) {
432
- const { getPlanModeConfig } = await import('./ai.js');
433
- config = getPlanModeConfig();
434
- }
435
- else {
436
- config = effectiveTargetAgent === 'CHAT' ? getGeneralChatConfig() : getPlanExecutionConfig();
437
- }
388
+ const config = effectiveTargetAgent === 'CHAT' ? getGeneralChatConfig() : getPlanExecutionConfig();
438
389
  let dynamicSystemInstruction = config.systemInstruction;
439
390
  // Inject gathered workspace directories, dependency configurations, and matching search patterns
440
391
  if (gatherRes.contextResult) {
441
392
  // Compress each relevant file individually using helper to avoid nested loop warning
442
- if (!cachedContextResult) {
443
- gatherRes.contextResult.relevantFiles = await compressContextFiles(workspaceRoot, gatherRes.contextResult, ac.signal);
444
- }
393
+ gatherRes.contextResult.relevantFiles = await compressContextFiles(workspaceRoot, gatherRes.contextResult, ac.signal);
445
394
  // Assemble the final context injection string
446
395
  const contextInjection = buildContextInjection(gatherRes.contextResult);
447
396
  debugLog(`Final compressed context injection size: ${contextInjection.length} chars`);
@@ -453,6 +402,78 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
453
402
  collector.recordCompressedContextSize(contextInjection.length);
454
403
  dynamicSystemInstruction += '\n\n' + contextInjection;
455
404
  }
405
+ // Resolve and inject explicit @ Context Mentions (files, symbols, git diffs, diagnostics, external workspaces)
406
+ try {
407
+ const { mentionEngine } = await import('./mentionEngine.js');
408
+ const resolvedMentions = await mentionEngine.resolveMentions(userInput, workspaceRoot);
409
+ if (resolvedMentions.hasMentions) {
410
+ const isPrompting = inputHandler.isCurrentlyPrompting();
411
+ if (!isPrompting) {
412
+ spinner.stop();
413
+ }
414
+ for (const item of resolvedMentions.mentions) {
415
+ if (item.resolved) {
416
+ const label = item.statusLabel ? ` ${pc.dim(`(${item.statusLabel})`)}` : '';
417
+ p.log.step(`${pc.green('✔')} Context ${pc.cyan(item.mention.raw)}${label}`);
418
+ }
419
+ else {
420
+ const reason = item.error ? ` ${pc.dim(`(${item.error})`)}` : '';
421
+ p.log.warn(`${pc.red('✖')} Context ${pc.yellow(item.mention.raw)}${reason}`);
422
+ }
423
+ }
424
+ if (!isPrompting) {
425
+ spinner.start('Preparing agent configuration...');
426
+ }
427
+ if (resolvedMentions.formattedContext) {
428
+ debugLog(`Resolved ${resolvedMentions.mentions.length} context mention(s) (${resolvedMentions.totalTokens} tokens estimated)`);
429
+ dynamicSystemInstruction += '\n\n' + resolvedMentions.formattedContext;
430
+ // Pre-register all resolved file mention paths with the read-guard
431
+ const mentionedFiles = resolvedMentions.mentions
432
+ .filter((m) => m.mention.type === 'file' && m.resolved)
433
+ .map((m) => m.mention.target);
434
+ if (mentionedFiles.length > 0) {
435
+ registerContextFiles(mentionedFiles);
436
+ }
437
+ }
438
+ }
439
+ // Also resolve mentions from any chained follow-up instructions received during investigation
440
+ if (gatherRes.chainedMessages.length > 0) {
441
+ const chainedContent = gatherRes.chainedMessages.join('\n');
442
+ const chainedMentions = await mentionEngine.resolveMentions(chainedContent, workspaceRoot);
443
+ if (chainedMentions.hasMentions) {
444
+ const isPrompting = inputHandler.isCurrentlyPrompting();
445
+ if (!isPrompting) {
446
+ spinner.stop();
447
+ }
448
+ for (const item of chainedMentions.mentions) {
449
+ if (item.resolved) {
450
+ const label = item.statusLabel ? ` ${pc.dim(`(${item.statusLabel})`)}` : '';
451
+ p.log.step(`${pc.green('✔')} Chained Context ${pc.cyan(item.mention.raw)}${label}`);
452
+ }
453
+ else {
454
+ const reason = item.error ? ` ${pc.dim(`(${item.error})`)}` : '';
455
+ p.log.warn(`${pc.red('✖')} Chained Context ${pc.yellow(item.mention.raw)}${reason}`);
456
+ }
457
+ }
458
+ if (!isPrompting) {
459
+ spinner.start('Preparing agent configuration...');
460
+ }
461
+ if (chainedMentions.formattedContext) {
462
+ debugLog(`Resolved ${chainedMentions.mentions.length} chained context mention(s) (${chainedMentions.totalTokens} tokens estimated)`);
463
+ dynamicSystemInstruction += '\n\n' + chainedMentions.formattedContext;
464
+ const chainedFiles = chainedMentions.mentions
465
+ .filter((m) => m.mention.type === 'file' && m.resolved)
466
+ .map((m) => m.mention.target);
467
+ if (chainedFiles.length > 0) {
468
+ registerContextFiles(chainedFiles);
469
+ }
470
+ }
471
+ }
472
+ }
473
+ }
474
+ catch (mentionErr) {
475
+ debugLog(`Context mention resolution encountered an error: ${mentionErr?.message || mentionErr}`);
476
+ }
456
477
  // Inject global adaptive user persona and learned preferences
457
478
  try {
458
479
  const userProfile = await loadUserProfile();
@@ -480,10 +501,20 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
480
501
  selectedModel = complexity === 'EASY' ? GEMINI_MODELS.FLASH_LITE : GEMINI_MODELS.FLASH;
481
502
  }
482
503
  chat.setModel(selectedModel);
483
- debugLog(`Auto-routing to model ${selectedModel} based on intent ${effectiveTargetAgent}`);
504
+ const thinkingLevel = getModelThinkingLevel(selectedModel);
505
+ if (typeof chat.setThinkingLevel === 'function') {
506
+ chat.setThinkingLevel(thinkingLevel);
507
+ }
508
+ debugLog(`Auto-routing to model ${selectedModel} (thinking: ${thinkingLevel}) based on intent ${effectiveTargetAgent}`);
484
509
  }
485
510
  else {
486
- chat.setModel(getGlobalActiveModel());
511
+ const activeModel = getGlobalActiveModel();
512
+ chat.setModel(activeModel);
513
+ const thinkingLevel = getModelThinkingLevel(activeModel);
514
+ if (typeof chat.setThinkingLevel === 'function') {
515
+ chat.setThinkingLevel(thinkingLevel);
516
+ }
517
+ debugLog(`Active model set to ${activeModel} (thinking: ${thinkingLevel})`);
487
518
  }
488
519
  if (ac.signal.aborted) {
489
520
  spinner.stop();
@@ -510,7 +541,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
510
541
  gitBranch = stdout.trim();
511
542
  }
512
543
  catch { }
513
- if (!isPlanMode && effectiveTargetAgent === 'EXECUTE' && isSubAgentsEnabled()) {
544
+ if (effectiveTargetAgent === 'EXECUTE' && isSubAgentsEnabled()) {
514
545
  spinner.stop(); // Clear the spinner before delegating
515
546
  inputHandler.clearSpinner(); // Prevent prompt from reviving the old spinner
516
547
  process.stdout.write('\x1b[2K\r');
@@ -650,6 +681,10 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
650
681
  // Send the formulated prompt payload to the generative model
651
682
  let result;
652
683
  try {
684
+ const activeModel = chat.getModel();
685
+ const currentThinking = (typeof chat.getThinkingLevel === 'function' ? chat.getThinkingLevel() : undefined) ||
686
+ getModelThinkingLevel(activeModel);
687
+ debugLog(`Dispatching turn to model ${activeModel} (thinking: ${currentThinking})`);
653
688
  result = await chat.sendMessage(finalInput, undefined, ac.signal);
654
689
  }
655
690
  catch (e) {
@@ -673,7 +708,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
673
708
  const calls = result.response.functionCalls();
674
709
  debugLog(`Initial functionCalls: ${calls && calls.length > 0 ? JSON.stringify(calls) : 'None'}`);
675
710
  // Stage 2: Recursive Tool Loops and Automated Self-Correction (delegated to a helper function to avoid nested loop warning)
676
- const correctionRes = await executeSelfCorrectionLoop(chat, result, workspaceRoot, inputHandler, effectiveTargetAgent, ac.signal, spinner, userInput, isPlanMode);
711
+ const correctionRes = await executeSelfCorrectionLoop(chat, result, workspaceRoot, inputHandler, effectiveTargetAgent, ac.signal, spinner, userInput);
677
712
  const finalText = correctionRes.finalText;
678
713
  inputHandler.stop();
679
714
  // Update latest usage metadata to reflect all completed turns
@@ -685,8 +720,9 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
685
720
  if (modifiedFiles.length > 0) {
686
721
  await invalidateCacheForDependents(workspaceRoot, modifiedFiles);
687
722
  }
723
+ let cleanFinalText = '';
688
724
  if (finalText) {
689
- const cleanFinalText = finalText.replace(/\[TASK_FINISHED\]/g, '').trim();
725
+ cleanFinalText = finalText.replace(/\[TASK_FINISHED\]/g, '').trim();
690
726
  if (cleanFinalText) {
691
727
  console.log(`\n${pc.blue('◆')} ${pc.bold('Minovative Mind')} ${pc.dim(`(${chat.getModel()})`)}\n`);
692
728
  console.log(renderTerminalMarkdown(cleanFinalText));
@@ -824,93 +860,6 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
824
860
  })
825
861
  .catch((e) => debugLog('Failed to auto-save session: ' + e));
826
862
  }
827
- let planModeReturn = undefined;
828
- if (isPlanMode && finalText && finalText !== '[Generation stopped.]') {
829
- inputHandler.stop(); // MUST stop the async input handler before showing an interactive prompt, or it will intercept stdin and freeze/duplicate
830
- // Ensure the terminal has fully scrolled and there is a clean line break
831
- console.log('\n');
832
- // Small delay to allow stdout to flush and terminal to finish scrolling from the markdown output
833
- // This prevents @clack/prompts from miscalculating the cursor position and duplicating the prompt on arrow keys
834
- await new Promise((resolve) => setTimeout(resolve, 200));
835
- while (true) {
836
- const currentModel = getGlobalActiveModel();
837
- const planAction = await p['select']({
838
- message: 'Plan generated. What would you like to do?',
839
- options: [
840
- { value: 'proceed', label: 'Proceed with plan', hint: 'Executes the generated plan immediately' },
841
- { value: 'edit', label: 'Edit plan', hint: 'Provide additional feedback to revise the plan' },
842
- {
843
- value: 'choose_model',
844
- label: 'Choose AI model',
845
- hint: `Change the active AI model (Current: ${currentModel})`,
846
- },
847
- { value: 'exit', label: 'Exit plan mode', hint: 'Return to normal chat without executing' },
848
- ],
849
- });
850
- if (planAction === 'choose_model') {
851
- const byokEnabled = await isByokEnabled();
852
- const options = [
853
- // {
854
- // value: 'claude-opus-5',
855
- // label: 'Claude 5 Opus',
856
- // hint: 'For hardcore & complex problems',
857
- // },
858
- {
859
- value: 'gemini-3.1-pro-preview',
860
- label: 'Gemini 3.1 Pro',
861
- hint: 'For hardcore & complex problems',
862
- },
863
- // {
864
- // value: 'claude-sonnet-5',
865
- // label: 'Claude 5 Sonnet',
866
- // hint: 'Best for raw speed and cost efficiency',
867
- // },
868
- {
869
- value: 'gemini-3.7-flash',
870
- label: 'Gemini 3.7 Flash',
871
- hint: 'Next-gen reasoning, speed & balanced performance',
872
- },
873
- {
874
- value: 'gemini-3.6-flash',
875
- label: 'Gemini 3.6 Flash',
876
- hint: 'Everyday coding — fast and accurate',
877
- },
878
- {
879
- value: 'gemini-3.5-flash-lite',
880
- label: 'Gemini 3.5 Flash-Lite',
881
- hint: 'Best for raw speed and cost efficiency',
882
- },
883
- {
884
- value: 'auto',
885
- label: 'Auto (Flash-Lite / Flash)',
886
- hint: 'Dynamically routes between Gemini 3.5 Flash-Lite and Gemini 3.7 Flash based on prompt complexity',
887
- },
888
- ].filter((o) => !(byokEnabled && o.value.includes('claude')));
889
- const selectedModel = await p['select']({
890
- message: `Select AI Model (Current: ${pc.cyan(currentModel)})`,
891
- initialValue: currentModel,
892
- options: options,
893
- });
894
- if (!p.isCancel(selectedModel)) {
895
- chat.setModel(selectedModel);
896
- setGlobalActiveModel(selectedModel);
897
- p.log.success(`Model successfully switched to ${pc.cyan(selectedModel)}`);
898
- }
899
- continue;
900
- }
901
- if (p.isCancel(planAction) || planAction === 'exit') {
902
- planModeReturn = 'exit';
903
- }
904
- else if (planAction === 'edit') {
905
- planModeReturn = 'edit';
906
- }
907
- else if (planAction === 'proceed') {
908
- planModeReturn = 'proceed';
909
- }
910
- break;
911
- }
912
- }
913
- return { planModeReturn, contextResult: gatherRes.contextResult };
914
863
  }
915
864
  catch (err) {
916
865
  if (ac.signal.aborted || err?.name === 'AbortError' || err?.message?.includes('abort')) {
@@ -931,14 +880,40 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
931
880
  }
932
881
  });
933
882
  }
934
- async function collectUserInput(history, isPlanMode) {
883
+ async function collectUserInput(history, workspaceRoot = process.cwd()) {
935
884
  let lines = [];
936
885
  let isMultiLine = false;
937
886
  while (true) {
938
887
  const userInputRaw = await historyText({
939
- message: isMultiLine ? ' ' : isPlanMode ? pc.cyan('(Plan Mode) ❯') : pc.magenta('❯'),
940
- placeholder: isMultiLine ? '' : 'Use ' + String.fromCharCode(92) + ' for new lines',
888
+ message: isMultiLine ? ' ' : pc.magenta('❯'),
889
+ placeholder: isMultiLine
890
+ ? ''
891
+ : 'Use ' + String.fromCharCode(92) + ' for new lines or @ to mention file paths and more',
941
892
  history,
893
+ autocomplete: {
894
+ workspaceRoot,
895
+ getSuggestions: async (query) => {
896
+ try {
897
+ const { mentionEngine } = await import('./mentionEngine.js');
898
+ const suggestions = await mentionEngine.getSuggestionsForQuery(query, { workspaceRoot });
899
+ return suggestions.map((s) => ({
900
+ value: s.value,
901
+ label: s.label,
902
+ category: s.category,
903
+ description: s.description,
904
+ metadata: {
905
+ filePath: s.filePath,
906
+ symbol: s.label,
907
+ helperLabel: s.helperLabel,
908
+ symbolKind: s.symbolKind,
909
+ },
910
+ }));
911
+ }
912
+ catch {
913
+ return [];
914
+ }
915
+ },
916
+ },
942
917
  });
943
918
  if (p.isCancel(userInputRaw)) {
944
919
  if (isMultiLine) {
@@ -1075,7 +1050,7 @@ export async function summarizeHistoryIfNeeded(chat, abortSignal) {
1075
1050
  }
1076
1051
  return false;
1077
1052
  }
1078
- async function executeSelfCorrectionLoop(chat, initialResult, workspaceRoot, inputHandler, effectiveTargetAgent, signal, spinner, originalUserInput, isPlanMode) {
1053
+ async function executeSelfCorrectionLoop(chat, initialResult, workspaceRoot, inputHandler, effectiveTargetAgent, signal, spinner, originalUserInput) {
1079
1054
  let correctionAttempts = 0;
1080
1055
  const MAX_CORRECTIONS = 5;
1081
1056
  let result = initialResult;
@@ -1141,7 +1116,7 @@ async function executeSelfCorrectionLoop(chat, initialResult, workspaceRoot, inp
1141
1116
  }
1142
1117
  previousChangeCount = currentChanges.length;
1143
1118
  // ─── Tool-Based Intent Verification ─────────────────────────────────
1144
- if (effectiveTargetAgent === 'EXECUTE' && !intentVerified && !isPlanMode) {
1119
+ if (effectiveTargetAgent === 'EXECUTE' && !intentVerified) {
1145
1120
  if (!currentText.includes('[TASK_FINISHED]')) {
1146
1121
  p.log.warn(pc.yellow('AI paused execution without calling finish_task. Prompting to continue or finish...'));
1147
1122
  const finishReminder = `SYSTEM CHECK: You stopped without calling the \`finish_task\` tool.
@@ -1,9 +1,23 @@
1
1
  import { type Content, type FunctionCall } from '@google/generative-ai';
2
+ import { type ThinkingLevel } from '../utils/config.js';
2
3
  export declare function getMultiWorkspaceBlock(): string;
3
4
  export declare function setModelOverride(agent: 'context' | 'execution', overrideStr: string): void;
4
5
  export declare function clearModelOverrides(): void;
5
6
  export declare function setGlobalActiveModel(model: string): void;
6
7
  export declare function getGlobalActiveModel(): string;
8
+ /**
9
+ * Gets the thinking reasoning level configured for a specific model or active model.
10
+ * Falls back to default model thinking levels or 'MEDIUM' if not explicitly configured.
11
+ */
12
+ export declare function getModelThinkingLevel(model?: string): ThinkingLevel;
13
+ /**
14
+ * Sets the user-configured thinking reasoning level for a specific model.
15
+ */
16
+ export declare function setModelThinkingLevel(model: string, level: ThinkingLevel): void;
17
+ /**
18
+ * Resets user-configured thinking levels back to default model presets.
19
+ */
20
+ export declare function resetModelThinkingLevels(): void;
7
21
  export declare class ProxyChatSession {
8
22
  private history;
9
23
  private fullHistory;
@@ -12,6 +26,7 @@ export declare class ProxyChatSession {
12
26
  private tools;
13
27
  private toolConfig?;
14
28
  private generationConfig;
29
+ private configuredThinkingLevel?;
15
30
  private latestUsageMetadata;
16
31
  constructor(modelName: string, systemInstruction: string, tools: any[], generationConfig: any, toolConfig?: any);
17
32
  getLatestUsageMetadata(): any;
@@ -20,6 +35,10 @@ export declare class ProxyChatSession {
20
35
  getToolConfig(): any;
21
36
  setModel(modelName: string): void;
22
37
  getModel(): string;
38
+ setThinkingLevel(level: ThinkingLevel): void;
39
+ getThinkingLevel(): ThinkingLevel | undefined;
40
+ setGenerationConfig(generationConfig: any): void;
41
+ getGenerationConfig(): any;
23
42
  clearHistory(): void;
24
43
  getRawHistory(): Content[];
25
44
  getFullHistory(): Content[];
@@ -95,10 +114,6 @@ export declare function getPlanExecutionConfig(): {
95
114
  functionDeclarations: import("@google/generative-ai").FunctionDeclaration[];
96
115
  }[];
97
116
  };
98
- export declare function getPlanModeConfig(): {
99
- systemInstruction: string;
100
- tools: never[];
101
- };
102
117
  /**
103
118
  * Compresses a large string of text using Gemini Flash.
104
119
  * Used for shrinking context payloads to prevent OOM/choking.