minovative-mind-cli 1.1.5 → 1.2.1

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Minovative Mind CLI (Claude AI models coming soon)
1
+ # Minovative Mind CLI (w/ Gemini 3.5 Flash & Gemini 3.1 Pro)
2
2
 
3
- ## Fast & Handy AI agent without the bloat. Avaliable in virtually all terminals.
3
+ ## Fast & Handy AI agent without the bloat. Avaliable in virtually all terminals. [(Click Demo)](https://youtu.be/KjE5nbKEf3w?si=mcjb5_2QLf4vofRC)
4
4
 
5
5
  An automated AI agent powered by Vertex AI that helps you write software right inside your terminal.
6
6
 
@@ -8,20 +8,20 @@ An automated AI agent powered by Vertex AI that helps you write software right i
8
8
  [![Version](https://img.shields.io/npm/v/minovative-mind-cli.svg)](https://npmjs.org/package/minovative-mind-cli)
9
9
  [![Downloads/week](https://img.shields.io/npm/dw/minovative-mind-cli.svg)](https://npmjs.org/package/minovative-mind-cli)
10
10
 
11
- Official Website: [https://www.minovativemind.dev/](https://www.minovativemind.dev/)
12
- Latest Updates: [https://www.minovativemind.dev/updates](https://www.minovativemind.dev/updates)
11
+ - Official Website: [https://www.minovativemind.dev/](https://www.minovativemind.dev/)
12
+ - Latest Updates: [https://www.minovativemind.dev/updates](https://www.minovativemind.dev/updates)
13
13
 
14
14
  ## Supported Models
15
15
 
16
16
  The CLI supports the following advanced reasoning and coding models via Vertex AI Model Garden. You can hot-swap between these models at any time during a chat session by typing `/models` in the CLI:
17
17
 
18
- - **Gemini 3.5 Flash** (Default): Lightning fast, top-tier performance for everyday coding tasks.
19
18
  - **Gemini 3.1 Pro**: Best for highly complex architectural reasoning and large context windows.
20
- - **Gemini 2.5 Pro / Flash**: Legacy high-performance models still fully supported.
19
+ - **Gemini 3.5 Flash** (Default): Lightning fast, top-tier performance for everyday coding tasks.
20
+ - **Gemini 3.1 Flash Lite**: Lightning fast, cost-effective performance for everyday coding tasks.
21
21
 
22
- ## TOC
22
+ ## Table of Contents
23
23
 
24
- - [Minovative Mind CLI](#minovative-mind-cli)
24
+ - [Quick Start](#quick-start)
25
25
  - [Commands](#commands)
26
26
 
27
27
  <!-- tocstop -->
@@ -62,6 +62,8 @@ Getting started with Minovative Mind CLI is easy!
62
62
  npm update -g minovative-mind-cli
63
63
  ```
64
64
 
65
+ ---
66
+
65
67
  # Commands
66
68
 
67
69
  <!-- commands -->
@@ -81,10 +83,24 @@ USAGE
81
83
  DESCRIPTION
82
84
  Start an interactive AI coding agent session powered by Vertex AI.
83
85
 
86
+ Inside the chat session, you can use the following commands in the slash menu:
87
+ /models - Select the active model
88
+ /paste - Enter multi-line paste mode for long snippets
89
+ /clear - Clear conversation history
90
+ /debug - Debug tests or command execution in a sandbox loop
91
+ /auto-approve - Toggle automatic approval of tool/command runs
92
+ /revert - Revert the last file modification made by the agent
93
+ /commit - Commit current workspace changes to Git
94
+
95
+ Chat Controls:
96
+ - Multi-line Input: End a line with \ to continue on the next line
97
+ - Stop/Abort: Type "stop" to immediately interrupt agent generation
98
+ - Exit Session: Type "exit" or "quit" to end the agent session
99
+
84
100
  EXAMPLES
85
- $ minovative-mind-cli
101
+ $ minovative-mind-cli chat
86
102
 
87
- $ minovative-mind-cli --help
103
+ $ minovative-mind-cli chat --help
88
104
  ```
89
105
 
90
106
  ## `minovative-mind-cli help [COMMAND]`
@@ -1,7 +1,27 @@
1
1
  import 'dotenv/config';
2
2
  import { Command } from '@oclif/core';
3
+ /**
4
+ * @class DefaultCommand
5
+ * @extends Command
6
+ * @description The entry point for the "chat" CLI command. Manages user authentication,
7
+ * environment initialization, and initializes the interactive agent loop session.
8
+ */
3
9
  export default class DefaultCommand extends Command {
10
+ /**
11
+ * The description displayed in the CLI help output.
12
+ * Details the available slash commands, chat controls, and functionality.
13
+ */
4
14
  static description: string;
15
+ /**
16
+ * Example usages for the chat command shown in help output.
17
+ */
5
18
  static examples: string[];
19
+ /**
20
+ * The main execution logic for the chat command.
21
+ * - Initializes update notifications.
22
+ * - Validates user authentication via Firebase/GitHub.
23
+ * - Sets up the workspace environment.
24
+ * - Launches the main agent loop.
25
+ */
6
26
  run(): Promise<void>;
7
27
  }
@@ -7,9 +7,46 @@ import pc from 'picocolors';
7
7
  import { startAgentLoop } from '../services/agent.js';
8
8
  import { getAuthorizedIdToken, login } from '../services/auth.js';
9
9
  import { updateWorkspaceStatus } from '../services/workspace.js';
10
+ /**
11
+ * @class DefaultCommand
12
+ * @extends Command
13
+ * @description The entry point for the "chat" CLI command. Manages user authentication,
14
+ * environment initialization, and initializes the interactive agent loop session.
15
+ */
10
16
  export default class DefaultCommand extends Command {
11
- static description = 'Start an interactive AI coding agent session powered by Vertex AI.';
12
- static examples = ['<%= config.bin %>', '<%= config.bin %> --help'];
17
+ /**
18
+ * The description displayed in the CLI help output.
19
+ * Details the available slash commands, chat controls, and functionality.
20
+ */
21
+ static description = `Start an interactive AI coding agent session powered by Vertex AI.
22
+
23
+ Inside the chat session, you can use the following commands in the slash menu:
24
+ /models - Select the active model
25
+ /paste - Enter multi-line paste mode for long snippets
26
+ /clear - Clear conversation history
27
+ /debug - Debug tests or command execution in a sandbox loop
28
+ /auto-approve - Toggle automatic approval of tool/command runs
29
+ /revert - Revert the last file modification made by the agent
30
+ /commit - Commit current workspace changes to Git
31
+
32
+ Chat Controls:
33
+ - Multi-line Input: End a line with \\ to continue on the next line
34
+ - Stop/Abort: Type "stop" to immediately interrupt agent generation
35
+ - Exit Session: Type "exit" or "quit" to end the agent session`;
36
+ /**
37
+ * Example usages for the chat command shown in help output.
38
+ */
39
+ static examples = [
40
+ '<%= config.bin %> chat',
41
+ '<%= config.bin %> chat --help',
42
+ ];
43
+ /**
44
+ * The main execution logic for the chat command.
45
+ * - Initializes update notifications.
46
+ * - Validates user authentication via Firebase/GitHub.
47
+ * - Sets up the workspace environment.
48
+ * - Launches the main agent loop.
49
+ */
13
50
  async run() {
14
51
  const workspaceRoot = process.cwd();
15
52
  console.clear();
package/dist/help.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { Help } from '@oclif/core';
2
+ export default class CustomHelp extends Help {
3
+ formatRoot(): string;
4
+ }
package/dist/help.js ADDED
@@ -0,0 +1,15 @@
1
+ import { Help } from '@oclif/core';
2
+ export default class CustomHelp extends Help {
3
+ formatRoot() {
4
+ const original = super.formatRoot();
5
+ const lines = original.split('\n');
6
+ const usageIndex = lines.findIndex((line) => line.includes('USAGE'));
7
+ if (usageIndex !== -1 && lines[usageIndex + 1]) {
8
+ const firstUsageLine = lines[usageIndex + 1];
9
+ const secondUsageLine = firstUsageLine.replace('[COMMAND]', 'help [COMMAND]');
10
+ lines.splice(usageIndex + 2, 0, secondUsageLine);
11
+ return lines.join('\n');
12
+ }
13
+ return original;
14
+ }
15
+ }
@@ -25,7 +25,7 @@ export declare function modifyFile(workspaceRoot: string, filePath: string, edit
25
25
  }>): Promise<ToolResult>;
26
26
  export declare function listDirectory(workspaceRoot: string, dirPath: string, maxDepth?: number): Promise<ToolResult>;
27
27
  export declare function runCommand(workspaceRoot: string, command: string, abortSignal?: AbortSignal): Promise<ToolResult>;
28
- export declare function grepSearch(workspaceRoot: string, pattern: string, fileGlob?: string): Promise<ToolResult>;
28
+ export declare function grepSearch(workspaceRoot: string, pattern: string, fileGlob?: string, abortSignal?: AbortSignal): Promise<ToolResult>;
29
29
  export declare function traceDependencies(workspaceRoot: string, filePath: string, direction?: string, maxDepth?: number): Promise<ToolResult>;
30
30
  export declare function findRecentChanges(workspaceRoot: string, dirPath?: string, minutes?: number, maxDepth?: number): Promise<ToolResult>;
31
31
  export declare function runDebugScript(workspaceRoot: string, language: string, code: string, abortSignal?: AbortSignal): Promise<ToolResult>;
@@ -541,7 +541,7 @@ export async function runCommand(workspaceRoot, command, abortSignal) {
541
541
  return { output: '', error: `Command failed: ${truncatedMsg}` };
542
542
  }
543
543
  }
544
- export async function grepSearch(workspaceRoot, pattern, fileGlob) {
544
+ export async function grepSearch(workspaceRoot, pattern, fileGlob, abortSignal) {
545
545
  try {
546
546
  const { ignoredDirs } = await getIgnoredPaths(workspaceRoot);
547
547
  const excludeDirArgs = Array.from(ignoredDirs)
@@ -559,6 +559,7 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob) {
559
559
  cwd: workspaceRoot,
560
560
  timeout: 15_000,
561
561
  maxBuffer: 1024 * 1024 * 2,
562
+ signal: abortSignal,
562
563
  });
563
564
  // Limit output to 50 results
564
565
  const lines = stdout.trim().split('\n');
@@ -570,11 +571,12 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob) {
570
571
  return { output: wrappedResult };
571
572
  }
572
573
  catch (err) {
573
- // grep returns exit code 1 when no matches are found.
574
- // Node's execException returns the code as a number.
575
574
  if (err instanceof Error && 'code' in err && err.code === 1) {
576
575
  return { output: `No matches found for "${pattern}".` };
577
576
  }
577
+ if (err instanceof Error && err.name === 'AbortError') {
578
+ return { output: '', error: 'Grep search aborted by user.' };
579
+ }
578
580
  const message = err instanceof Error ? err.message : String(err);
579
581
  // Also handle stringified exit codes just in case
580
582
  if (message.includes('Command failed') &&
@@ -768,7 +770,7 @@ export async function executeTool(workspaceRoot, toolName, args, abortSignal) {
768
770
  case 'run_debug_script':
769
771
  return runDebugScript(workspaceRoot, args.language, args.code, abortSignal);
770
772
  case 'grep_search':
771
- return grepSearch(workspaceRoot, args.pattern, args.fileGlob);
773
+ return grepSearch(workspaceRoot, args.pattern, args.fileGlob, abortSignal);
772
774
  case 'find_dependencies':
773
775
  return traceDependencies(workspaceRoot, args.filePath, args.direction, args.maxDepth);
774
776
  case 'find_recent_changes':
@@ -25,7 +25,7 @@ import * as p from '@clack/prompts';
25
25
  *
26
26
  * 1. **Pause execution** at any time by pressing a key.
27
27
  * 2. **Queue feedback** ("chained messages") without waiting for the entire run to finish.
28
- * 3. **Force abort** the current model or tool run by entering "stop" or "stop!".
28
+ * 3. **Force abort** the current model or tool run by entering "stop".
29
29
  *
30
30
  * ### Raw Mode & Terminal States
31
31
  * In normal CLI execution, Node.js waits for a line feed (Enter) before emitting input.
@@ -32,7 +32,7 @@ const execAsync = promisify(exec);
32
32
  *
33
33
  * 1. **Pause execution** at any time by pressing a key.
34
34
  * 2. **Queue feedback** ("chained messages") without waiting for the entire run to finish.
35
- * 3. **Force abort** the current model or tool run by entering "stop" or "stop!".
35
+ * 3. **Force abort** the current model or tool run by entering "stop".
36
36
  *
37
37
  * ### Raw Mode & Terminal States
38
38
  * In normal CLI execution, Node.js waits for a line feed (Enter) before emitting input.
@@ -111,8 +111,9 @@ export class AsyncInputHandler {
111
111
  }
112
112
  // Hide the background spinner before prompt output to avoid corrupting terminal lines
113
113
  if (this.spinner) {
114
- this.spinner.stop('Paused to receive input');
114
+ this.spinner.stop();
115
115
  }
116
+ p.log.step(pc.cyan('Paused to receive input'));
116
117
  // Capture the user feedback. The character typed to trigger this event is passed
117
118
  // as the initial value of the prompt to avoid losing the first keystroke.
118
119
  const userInput = await p.text({
@@ -120,12 +121,14 @@ export class AsyncInputHandler {
120
121
  placeholder: '(Leave blank and press Enter to cancel)',
121
122
  initialValue: char,
122
123
  });
124
+ let wasAborted = false;
123
125
  if (!p.isCancel(userInput) && userInput.trim()) {
124
126
  const text = userInput.trim();
125
- if (text.toLowerCase() === 'stop' || text.toLowerCase() === 'stop!') {
127
+ if (text.toLowerCase() === 'stop') {
126
128
  if (this.ac) {
127
129
  this.ac.abort();
128
130
  p.log.warn(pc.yellow(`Generation aborted by user.`));
131
+ wasAborted = true;
129
132
  }
130
133
  }
131
134
  else {
@@ -133,8 +136,9 @@ export class AsyncInputHandler {
133
136
  p.log.info(pc.cyan(`📥 Queued message: "${text}"`));
134
137
  }
135
138
  }
136
- if (this.spinner) {
137
- this.spinner.start('Resuming execution...');
139
+ if (this.spinner && !wasAborted) {
140
+ const resumeMsg = this.spinner._lastMessage || 'Resuming execution...';
141
+ this.spinner.start(resumeMsg);
138
142
  }
139
143
  }
140
144
  catch (err) {
@@ -145,6 +149,7 @@ export class AsyncInputHandler {
145
149
  if (process.stdin.isTTY) {
146
150
  process.stdin.setRawMode(true);
147
151
  }
152
+ process.stdin.resume();
148
153
  // Small delay before reattaching listener to avoid capturing duplicate keypress frames
149
154
  setTimeout(() => {
150
155
  if (!this.stopped) {
@@ -168,6 +173,26 @@ export class AsyncInputHandler {
168
173
  this.stopped = false;
169
174
  if (spinner) {
170
175
  this.spinner = spinner;
176
+ // Monkey-patch to track the latest message for un-pausing
177
+ if (!spinner._isPatched) {
178
+ ;
179
+ spinner._isPatched = true;
180
+ spinner._lastMessage = 'Executing...';
181
+ const originalMessage = spinner.message.bind(spinner);
182
+ spinner.message = (msg) => {
183
+ if (msg) {
184
+ spinner._lastMessage = msg;
185
+ }
186
+ originalMessage(msg);
187
+ };
188
+ const originalStart = spinner.start.bind(spinner);
189
+ spinner.start = (msg) => {
190
+ if (msg) {
191
+ spinner._lastMessage = msg;
192
+ }
193
+ originalStart(msg);
194
+ };
195
+ }
171
196
  }
172
197
  if (process.stdin.isTTY) {
173
198
  this.originalRawMode = process.stdin.isRaw;
@@ -447,6 +472,7 @@ async function processResponse(chat, result, workspaceRoot, inputHandler, agentS
447
472
  // Process each tool call requested by the model
448
473
  const toolResponses = [];
449
474
  for (const fc of functionCalls) {
475
+ await inputHandler.waitForPrompt();
450
476
  if (abortSignal.aborted) {
451
477
  p.log.warn(pc.yellow('Execution aborted by user.'));
452
478
  return '[Generation stopped by user]';
@@ -476,6 +502,7 @@ async function processResponse(chat, result, workspaceRoot, inputHandler, agentS
476
502
  }
477
503
  // Execute the underlying filesystem, shell or search tool logic
478
504
  const toolResult = await executeTool(workspaceRoot, toolName, toolArgs, abortSignal);
505
+ await inputHandler.waitForPrompt();
479
506
  if (toolResult.error) {
480
507
  debugLog(`Raw Tool Error for ${toolName}: ${toolResult.error}`);
481
508
  // Do not spam the user with expected chunking warnings
@@ -526,6 +553,7 @@ async function processResponse(chat, result, workspaceRoot, inputHandler, agentS
526
553
  }
527
554
  throw e;
528
555
  }
556
+ await inputHandler.waitForPrompt();
529
557
  const grounding = followUp.response.groundingMetadata?.();
530
558
  if (grounding?.webSearchQueries && grounding.webSearchQueries.length > 0) {
531
559
  p.log.info(`${pc.blue('🌐')} ${pc.dim('Google Search Queries:')} ${grounding.webSearchQueries.map((q) => pc.cyan(`"${q}"`)).join(', ')}`);
@@ -632,9 +660,8 @@ export async function startAgentLoop(workspaceRoot, version) {
632
660
  }
633
661
  else {
634
662
  lines.push(line);
635
- if (isMultiLine) {
636
- p.log.step(pc.cyan(line));
637
- }
663
+ // Unconditionally log the user's message so it is always visible
664
+ p.log.step(pc.cyan(line));
638
665
  break;
639
666
  }
640
667
  }
@@ -708,15 +735,26 @@ export async function startAgentLoop(workspaceRoot, version) {
708
735
  continue;
709
736
  }
710
737
  if (userInput.toLowerCase() === '/models') {
738
+ const currentModel = chat.getModel();
711
739
  const selectedModel = await p.select({
712
- message: 'Select AI Model',
740
+ message: `Select AI Model (Current: ${pc.cyan(currentModel)})`,
741
+ initialValue: currentModel,
713
742
  options: [
714
- { value: 'gemini-3.1-pro-preview', label: 'Gemini 3.1 Pro', hint: 'The newest Pro model for complex logic' },
715
- { value: 'gemini-3.5-flash', label: 'Gemini 3.5 Flash', hint: 'Balanced performance' },
716
- { value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro', hint: 'Best for complex coding & large context' },
717
- // {value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash', hint: 'Balanced performance'},
718
- // {value: 'claude-opus-4-6', label: 'Claude Opus 4.6', hint: 'Anthropic: Highly capable, complex reasoning'},
719
- // {value: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', hint: 'Anthropic: Fast and highly intelligent'},
743
+ {
744
+ value: 'gemini-3.1-pro-preview',
745
+ label: 'Gemini 3.1 Pro',
746
+ hint: 'The newest Pro model for complex logic',
747
+ },
748
+ {
749
+ value: 'gemini-3.5-flash',
750
+ label: 'Gemini 3.5 Flash',
751
+ hint: 'Balanced performance',
752
+ },
753
+ {
754
+ value: 'gemini-3.1-flash-lite',
755
+ label: 'Gemini 3.1 Flash-Lite',
756
+ hint: 'Ultra-fast and cost-effective',
757
+ },
720
758
  ],
721
759
  });
722
760
  if (!p.isCancel(selectedModel)) {
@@ -786,7 +824,7 @@ ${diffOut}
786
824
  </diff>`;
787
825
  // Use flash-lite for extreme speed and low cost for this simple task
788
826
  const commitSystemPrompt = 'You are an expert developer. Output only the git commit message, no markdown formatting, no explanations. Follow Conventional Commits format (feat:, fix:, chore:, refactor:, etc.) for the first line, keeping it under 70 characters and using the imperative mood. Then, add a blank line followed by a more descriptive bulleted list explaining the "what" and "why" of the changes based on the diff.';
789
- const commitAgent = new ProxyChatSession('gemini-2.5-flash-lite', commitSystemPrompt, [], {});
827
+ const commitAgent = new ProxyChatSession('gemini-3.1-flash-lite', commitSystemPrompt, [], {});
790
828
  const commitResult = await commitAgent.sendMessage(prompt);
791
829
  const commitMsg = commitResult.response.text().trim();
792
830
  commitSpinner.message('Committing...');
@@ -905,14 +943,26 @@ ${diffOut}
905
943
  break;
906
944
  finalText = await processResponse(chat, result, workspaceRoot, inputHandler, agentState, ac.signal);
907
945
  debugLog(`processResponse returned finalText (length ${finalText.length}): "${finalText.substring(0, 10)}..."`);
946
+ if (finalText === '[Generation stopped by user]') {
947
+ break;
948
+ }
908
949
  if (finalText.startsWith('[The AI repeatedly returned empty responses')) {
909
950
  break;
910
951
  }
911
952
  const currentChanges = changeLogger.getCurrentChangeSet()?.changes || [];
912
953
  // If the correction cycle is active but no new changes were registered, the model gave up
913
954
  if (correctionAttempts > 0 && currentChanges.length <= previousChangeCount) {
914
- p.log.warn('AI failed to modify any files during the correction attempt. Aborting auto-correction.');
915
- break;
955
+ if (correctionAttempts >= MAX_CORRECTIONS) {
956
+ p.log.warn('Max self-correction attempts reached. Leaving remaining errors for manual review.');
957
+ break;
958
+ }
959
+ debugLog('AI failed to modify any files during the correction attempt. Retrying...');
960
+ const forcePrompt = `AUTOMATED SYSTEM CHECK: You did not modify any files. You MUST use your file modification tools to apply a fix for the previously mentioned errors. Do not just explain the issue.`;
961
+ spinner.start('Thinking (Correction)...');
962
+ result = await chat.sendMessage(forcePrompt);
963
+ spinner.stop('');
964
+ correctionAttempts++;
965
+ continue;
916
966
  }
917
967
  previousChangeCount = currentChanges.length;
918
968
  const changedFiles = currentChanges
@@ -922,7 +972,11 @@ ${diffOut}
922
972
  break;
923
973
  // Stage 3: Static code analysis / verification
924
974
  p.log.step('Verifying modified files...');
925
- const verificationErrors = await verifyChangedFiles(workspaceRoot, changedFiles);
975
+ const verificationErrors = await verifyChangedFiles(workspaceRoot, changedFiles, ac.signal);
976
+ if (verificationErrors === '[Verification Aborted]') {
977
+ p.log.warn(pc.yellow('Verification aborted by user.'));
978
+ break;
979
+ }
926
980
  if (!verificationErrors) {
927
981
  p.log.success('Verification passed.');
928
982
  break;
@@ -53,13 +53,13 @@ export declare function getPlanExecutionConfig(): {
53
53
  }[];
54
54
  };
55
55
  /**
56
- * Compresses a large string of text using gemini-2.5-flash-lite.
56
+ * Compresses a large string of text using gemini-3.1-flash-lite.
57
57
  * Used for shrinking context payloads to prevent OOM/choking.
58
58
  */
59
59
  export declare function compressTextUsingFlashLite(text: string, instruction?: string): Promise<string>;
60
60
  export declare const CONTEXT_AGENT_MODEL: "gemini-3.5-flash";
61
61
  export declare function createContextAgentSession(): any;
62
- export declare const INTENT_ROUTER_MODEL: "gemini-2.5-flash";
62
+ export declare const INTENT_ROUTER_MODEL: "gemini-3.1-flash-lite";
63
63
  export declare function createIntentRouterSession(): any;
64
64
  export declare const WEB_SEARCH_AGENT_MODEL: "gemini-3.5-flash";
65
65
  export declare function createWebSearchAgentSession(): any;
@@ -62,7 +62,7 @@ export class ProxyChatSession {
62
62
  let formattedHistory = '';
63
63
  for (const item of recentItems) {
64
64
  const role = item.role === 'user' ? 'User' : 'Assistant';
65
- const textParts = item.parts.map(p => p.text).filter(Boolean);
65
+ const textParts = item.parts.map((p) => p.text).filter(Boolean);
66
66
  if (textParts.length > 0) {
67
67
  formattedHistory += `[${role}]: ${textParts.join(' ')}\n`;
68
68
  }
@@ -125,7 +125,9 @@ export class ProxyChatSession {
125
125
  // Use the exact parts provided by the API (preserves thought_signature)
126
126
  modelParts = [...result.parts];
127
127
  for (const fc of allFunctionCalls) {
128
- const existsInParts = modelParts.some(p => p.functionCall && p.functionCall.name === fc.name && JSON.stringify(p.functionCall.args) === JSON.stringify(fc.args));
128
+ const existsInParts = modelParts.some((p) => p.functionCall &&
129
+ p.functionCall.name === fc.name &&
130
+ JSON.stringify(p.functionCall.args) === JSON.stringify(fc.args));
129
131
  if (!existsInParts) {
130
132
  modelParts.push({ functionCall: fc });
131
133
  }
@@ -182,7 +184,7 @@ export function getPlanExecutionConfig() {
182
184
  };
183
185
  }
184
186
  /**
185
- * Compresses a large string of text using gemini-2.5-flash-lite.
187
+ * Compresses a large string of text using gemini-3.1-flash-lite.
186
188
  * Used for shrinking context payloads to prevent OOM/choking.
187
189
  */
188
190
  export async function compressTextUsingFlashLite(text, instruction = 'Summarize the following text concisely. Preserve the most critical technical details, function names, and architecture logic. Keep it under 1500 characters.') {
@@ -193,7 +195,7 @@ export async function compressTextUsingFlashLite(text, instruction = 'Summarize
193
195
  if (!idToken)
194
196
  return text;
195
197
  const contents = [{ role: 'user', parts: [{ text }] }];
196
- const result = await proxyClient.generateFunctionCallViaProxy(idToken, 'gemini-2.5-flash-lite', contents, [], // no tools
198
+ const result = await proxyClient.generateFunctionCallViaProxy(idToken, 'gemini-3.1-flash-lite', contents, [], // no tools
197
199
  undefined, instruction, { temperature: 0.2 });
198
200
  const summary = result.thought || result.parts?.find((p) => p.text)?.text || '';
199
201
  return summary ? summary : text;
@@ -345,7 +347,7 @@ export function createContextAgentSession() {
345
347
  });
346
348
  }
347
349
  // ─── Intent Router Service ───────────────────────────────────────────
348
- export const INTENT_ROUTER_MODEL = GEMINI_MODELS.FLASH_LATEST;
350
+ export const INTENT_ROUTER_MODEL = GEMINI_MODELS.FLASH_LITE_3_1;
349
351
  export function createIntentRouterSession() {
350
352
  return new ProxyChatSession(INTENT_ROUTER_MODEL, INTENT_ROUTER_SYSTEM_INSTRUCTION, [], // no tools
351
353
  { temperature: 0, responseMimeType: 'application/json' });
@@ -220,6 +220,9 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
220
220
  let isFinished = false;
221
221
  const functionResponses = [];
222
222
  for (const call of functionCalls) {
223
+ await inputHandler.waitForPrompt();
224
+ if (abortSignal.aborted)
225
+ break;
223
226
  const args = call.args;
224
227
  let logMsg = ` [Context Agent] Executing ${call.name}`;
225
228
  if (call.name === 'finish_investigation') {
@@ -5,6 +5,6 @@ export interface VerificationResult {
5
5
  errors: string[];
6
6
  }
7
7
  export declare function detectVerificationCommand(workspaceRoot: string): Promise<string | null>;
8
- export declare function runVerification(workspaceRoot: string): Promise<VerificationResult | null>;
8
+ export declare function runVerification(workspaceRoot: string, abortSignal?: AbortSignal): Promise<VerificationResult | null>;
9
9
  export declare function formatVerificationForModel(result: VerificationResult): string;
10
- export declare function verifyChangedFiles(workspaceRoot: string, filePaths: string[]): Promise<string | null>;
10
+ export declare function verifyChangedFiles(workspaceRoot: string, filePaths: string[], abortSignal?: AbortSignal): Promise<string | null>;
@@ -35,7 +35,7 @@ export async function detectVerificationCommand(workspaceRoot) {
35
35
  catch { }
36
36
  return null;
37
37
  }
38
- export async function runVerification(workspaceRoot) {
38
+ export async function runVerification(workspaceRoot, abortSignal) {
39
39
  const command = await detectVerificationCommand(workspaceRoot);
40
40
  if (!command)
41
41
  return null;
@@ -46,6 +46,7 @@ export async function runVerification(workspaceRoot) {
46
46
  cwd: workspaceRoot,
47
47
  timeout: 90_000, // 90s (1 min & 30 secs) - builds can take a while (e.g., Next.js)
48
48
  maxBuffer: 1024 * 1024, // 1 MB buffer
49
+ signal: abortSignal,
49
50
  });
50
51
  if (stdout.trim())
51
52
  debugLog(`[Verification Output - ${command}]\n${stdout.trim()}`);
@@ -59,6 +60,10 @@ export async function runVerification(workspaceRoot) {
59
60
  };
60
61
  }
61
62
  catch (err) {
63
+ if (err.name === 'AbortError' || err.message?.includes('abort')) {
64
+ debugLog(`[Verification Aborted - ${command}]`);
65
+ return { success: false, command, output: 'Aborted', errors: [], aborted: true };
66
+ }
62
67
  const rawStdout = (err.stdout || '').substring(0, MAX_VERIFY_OUTPUT);
63
68
  const rawStderr = (err.stderr || '').substring(0, MAX_VERIFY_OUTPUT);
64
69
  const output = rawStdout + '\n' + rawStderr;
@@ -100,13 +105,16 @@ ${result.errors.join('\n')}
100
105
 
101
106
  Please fix these errors using the modify_file tool.`;
102
107
  }
103
- export async function verifyChangedFiles(workspaceRoot, filePaths) {
108
+ export async function verifyChangedFiles(workspaceRoot, filePaths, abortSignal) {
104
109
  if (filePaths.length === 0)
105
110
  return null;
106
111
  const errors = [];
107
112
  // Project-level build check (e.g., npm run build)
108
- const buildResult = await runVerification(workspaceRoot);
113
+ const buildResult = await runVerification(workspaceRoot, abortSignal);
109
114
  if (buildResult && !buildResult.success) {
115
+ if (buildResult.aborted) {
116
+ return '[Verification Aborted]';
117
+ }
110
118
  const buildErrorMsg = `[Build Error: ${buildResult.command}]\n${buildResult.errors.join('\n').substring(0, 5000)}`;
111
119
  errors.push(buildErrorMsg);
112
120
  }
@@ -6,8 +6,7 @@ export declare const GITHUB_CLIENT_ID = "Ov23linFYFfjO3JILG7r";
6
6
  export declare const GEMINI_MODELS: {
7
7
  readonly PRO_3_1: "gemini-3.1-pro-preview";
8
8
  readonly FLASH_3_5: "gemini-3.5-flash";
9
- readonly FLASH_PRO: "gemini-2.5-pro";
10
- readonly FLASH_LATEST: "gemini-2.5-flash";
9
+ readonly FLASH_LITE_3_1: "gemini-3.1-flash-lite";
11
10
  };
12
11
  export declare const CLAUDE_MODELS: {
13
12
  readonly OPUS: "claude-opus-4-6";
@@ -16,4 +15,4 @@ export declare const CLAUDE_MODELS: {
16
15
  /** Default Gemini model for the coding agent. */
17
16
  export declare const DEFAULT_MODEL: "gemini-3.5-flash";
18
17
  /** Maximum tokens the model can output per response. */
19
- export declare const MAX_OUTPUT_TOKENS = 65000;
18
+ export declare const MAX_OUTPUT_TOKENS = 60000;
@@ -6,8 +6,7 @@ export const GITHUB_CLIENT_ID = 'Ov23linFYFfjO3JILG7r';
6
6
  export const GEMINI_MODELS = {
7
7
  PRO_3_1: 'gemini-3.1-pro-preview',
8
8
  FLASH_3_5: 'gemini-3.5-flash',
9
- FLASH_PRO: 'gemini-2.5-pro',
10
- FLASH_LATEST: 'gemini-2.5-flash',
9
+ FLASH_LITE_3_1: 'gemini-3.1-flash-lite',
11
10
  };
12
11
  export const CLAUDE_MODELS = {
13
12
  OPUS: 'claude-opus-4-6',
@@ -16,4 +15,4 @@ export const CLAUDE_MODELS = {
16
15
  /** Default Gemini model for the coding agent. */
17
16
  export const DEFAULT_MODEL = GEMINI_MODELS.FLASH_3_5;
18
17
  /** Maximum tokens the model can output per response. */
19
- export const MAX_OUTPUT_TOKENS = 65_000;
18
+ export const MAX_OUTPUT_TOKENS = 60_000;
@@ -3,10 +3,10 @@
3
3
  "chat": {
4
4
  "aliases": [],
5
5
  "args": {},
6
- "description": "Start an interactive AI coding agent session powered by Vertex AI.",
6
+ "description": "Start an interactive AI coding agent session powered by Vertex AI.\n\nInside the chat session, you can use the following commands in the slash menu:\n /models - Select the active model\n /paste - Enter multi-line paste mode for long snippets\n /clear - Clear conversation history\n /debug - Debug tests or command execution in a sandbox loop\n /auto-approve - Toggle automatic approval of tool/command runs\n /revert - Revert the last file modification made by the agent\n /commit - Commit current workspace changes to Git\n\nChat Controls:\n - Multi-line Input: End a line with \\ to continue on the next line\n - Stop/Abort: Type \"stop\" to immediately interrupt agent generation\n - Exit Session: Type \"exit\" or \"quit\" to end the agent session",
7
7
  "examples": [
8
- "<%= config.bin %>",
9
- "<%= config.bin %> --help"
8
+ "<%= config.bin %> chat",
9
+ "<%= config.bin %> chat --help"
10
10
  ],
11
11
  "flags": {},
12
12
  "hasDynamicHelp": false,
@@ -65,5 +65,5 @@
65
65
  ]
66
66
  }
67
67
  },
68
- "version": "1.1.5"
68
+ "version": "1.2.1"
69
69
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "minovative-mind-cli",
3
3
  "description": "An automated AI agent powered by Vertex AI that helps you write software",
4
- "version": "1.1.5",
4
+ "version": "1.2.1",
5
5
  "author": "Daniel Ward",
6
6
  "bin": {
7
7
  "minovative-mind-cli": "./bin/run.js"
@@ -63,6 +63,7 @@
63
63
  "dirname": "minovative-mind-cli",
64
64
  "default": "chat",
65
65
  "commands": "./dist/commands",
66
+ "helpClass": "./dist/help.js",
66
67
  "plugins": [
67
68
  "@oclif/plugin-help"
68
69
  ],