minovative-mind-cli 1.2.2 → 1.3.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.
package/LICENSE.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Copyright (c) 2026 Minovative Mind. All Rights Reserved.**
4
4
 
5
- This software, including all source code, documentation, and associated files (the "Software"), is proprietary and confidential property of Minovative Mind.
5
+ This software, including all source code, documentation, and associated files (the "Software"), is proprietary and confidential property of Ward Innovations.
6
6
 
7
7
  ## 1. Grant of License
8
8
 
package/README.md CHANGED
@@ -1,152 +1,103 @@
1
- # Minovative Mind CLI (w/ Gemini 3.5 Flash & Gemini 3.1 Pro)
1
+ # Minovative Mind CLI
2
2
 
3
- ## Highly accurate, lightweight AI coding agent for any terminal (40-50% fewer errors). [(Click Demo)](https://youtu.be/KjE5nbKEf3w?si=mcjb5_2QLf4vofRC)
3
+ ## The CLI agent that makes Flash models perform like Pro.
4
4
 
5
- An automated, multi-agent AI coding assistant powered by Vertex AI. By utilizing a **Precision-Context Verification (PCV)** engine with AST-based structural analysis, it reads, writes, and refactors code with extreme accuracy—delivering a **40% to 50% reduction in broken code generations** on large files compared to standard zero-shot LLM prompts.
5
+ ### The result: **Genuine Pro-level accuracy at Flash-level cost.**
6
+
7
+ Most AI coding tools throw your entire codebase at the biggest model available
8
+ and hope for the best. This CLI does the opposite — it uses a custom built
9
+ **Precision-Context Verification (PCV)** engine to feed lightweight Flash models
10
+ exactly the right context, verify the output compiles, and self-correct, if it even needs to, until
11
+ the build is green.
6
12
 
7
13
  [![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io)
8
14
  [![Version](https://img.shields.io/npm/v/minovative-mind-cli.svg)](https://npmjs.org/package/minovative-mind-cli)
9
15
  [![Downloads/week](https://img.shields.io/npm/dw/minovative-mind-cli.svg)](https://npmjs.org/package/minovative-mind-cli)
10
16
 
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
-
14
- ## Supported Models
15
-
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
-
18
- - **Gemini 3.1 Pro**: Best for highly complex architectural reasoning and large context windows.
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
-
22
- ## Table of Contents
23
-
24
- - [Quick Start](#quick-start)
25
- - [Commands](#commands)
26
-
27
- <!-- tocstop -->
28
-
29
- ## Quick Start
30
-
31
- Getting started with Minovative Mind CLI is easy!
32
-
33
- 1. **Install Globally:**
34
-
35
- ```bash
36
- npm install -g minovative-mind-cli
37
- ```
17
+ ---
38
18
 
39
- 2. **Authenticate:**
40
- Before you can use the CLI, you need to create an account at [https://www.minovativemind.dev/](https://www.minovativemind.dev/).
41
- Once you have an account, sign in securely using GitHub by running:
19
+ ## How it works
42
20
 
43
- ```bash
44
- minovative-mind-cli login
45
- ```
21
+ Standard AI coding agents: big model + noisy context = expensive and redundant mistakes
46
22
 
47
- 3. **Start Coding!**
48
- Navigate to any project directory on your computer and start a chat session:
23
+ Minovative Mind CLI: flash models + precise context + verified output = better results.
49
24
 
50
- ```bash
51
- cd your-project-folder
52
- minovative-mind-cli chat
53
- ```
25
+ The PCV engine runs in three stages before returning a single line of code:
54
26
 
55
- 4. **Change Models (Optional):**
56
- Once in a chat, type `/models` to switch between advanced reasoning models like Gemini 3.5 Flash and Gemini 3.1 Pro!
27
+ **1. Investigate**
28
+ A dedicated Context Agent reads your project structure, traces import
29
+ dependencies, detects recently modified files, and searches the web for
30
+ external documentation — all before the coding agent touches anything.
57
31
 
58
- 5. **Update the CLI:**
59
- To ensure you have the latest features and bug fixes, you can update the CLI at any time by running the following command. (The CLI will automatically notify you when a new version is available!)
32
+ **2. Execute with precision**
33
+ Instead of dumping your entire codebase into a prompt, the engine feeds the
34
+ coding agent a compressed, surgical snapshot of only what's relevant.
35
+ 40–50% fewer broken generations on large files vs. standard zero-shot prompts.
60
36
 
61
- ```bash
62
- npm update -g minovative-mind-cli
63
- ```
37
+ **3. Verify and self-correct**
38
+ After every change, it runs your actual build (`npm run build`, `cargo check`,
39
+ `make`, etc.), reads the exact compiler output, and autonomously fixes errors —
40
+ up to 3 correction cycles without you typing a word.
64
41
 
65
42
  ---
66
43
 
67
- # Commands
44
+ ## See it in action
68
45
 
69
- <!-- commands -->
70
- * [`minovative-mind-cli chat`](#minovative-mind-cli-chat)
71
- * [`minovative-mind-cli help [COMMAND]`](#minovative-mind-cli-help-command)
72
- * [`minovative-mind-cli login`](#minovative-mind-cli-login)
73
- * [`minovative-mind-cli logout`](#minovative-mind-cli-logout)
46
+ - [AI builds a Python Generative Art Engine in one prompt](https://www.youtube.com/watch?v=dy3qNbETqfs)
47
+ - [AI builds a Quiz Game from scratch](https://www.youtube.com/watch?v=KjE5nbKEf3w)
74
48
 
75
- ## `minovative-mind-cli chat`
49
+ ---
76
50
 
77
- Start an interactive AI coding agent session powered by Vertex AI.
51
+ ## Quick Start
78
52
 
53
+ ```bash
54
+ npm install -g minovative-mind-cli
79
55
  ```
80
- USAGE
81
- $ minovative-mind-cli chat
82
-
83
- DESCRIPTION
84
- Start an interactive AI coding agent session powered by Vertex AI.
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
-
100
- EXAMPLES
101
- $ minovative-mind-cli chat
102
-
103
- $ minovative-mind-cli chat --help
104
- ```
105
-
106
- ## `minovative-mind-cli help [COMMAND]`
107
56
 
108
- Display help for minovative-mind-cli.
57
+ Create a free account at [minovativemind.dev](https://www.minovativemind.dev),
58
+ then:
109
59
 
60
+ ```bash
61
+ minovative-mind-cli login # One-click GitHub sign-in
62
+ cd your-project
63
+ minovative-mind-cli chat # Start coding
110
64
  ```
111
- USAGE
112
- $ minovative-mind-cli help [COMMAND...] [-n]
113
65
 
114
- ARGUMENTS
115
- [COMMAND...] Command to show help for.
66
+ Works in any terminal — SSH, Docker, CI pipelines, Vim, anywhere Node runs.
116
67
 
117
- FLAGS
118
- -n, --nested-commands Include all nested commands in the output.
68
+ ---
119
69
 
120
- DESCRIPTION
121
- Display help for minovative-mind-cli.
122
- ```
70
+ ## Models
123
71
 
124
- ## `minovative-mind-cli login`
72
+ Hot-swap during a session with `/models`:
125
73
 
126
- Sign in to your Minovative Mind account using GitHub
74
+ | Model | Best for |
75
+ | ------------------------------ | ----------------------------------- |
76
+ | **Gemini 3.5 Flash** (default) | Everyday coding — fast and accurate |
77
+ | **Gemini 3.1 Pro** | Complex architectural changes |
78
+ | **Gemini 3.1 Flash Lite** | Maximum speed and cost efficiency |
127
79
 
128
- ```
129
- USAGE
130
- $ minovative-mind-cli login
80
+ > Intent routing, context compression, and commit generation always run on
81
+ > lightweight models automatically — you only pay Pro rates when you
82
+ > explicitly ask for it.
131
83
 
132
- DESCRIPTION
133
- Sign in to your Minovative Mind account using GitHub
134
- ```
135
-
136
- ## `minovative-mind-cli logout`
84
+ ---
137
85
 
138
- Sign out of your Minovative Mind account
86
+ ## Session Commands
139
87
 
140
- ```
141
- USAGE
142
- $ minovative-mind-cli logout
88
+ | Command | What it does |
89
+ | --------------- | ----------------------------------------------------- |
90
+ | `/models` | Hot-swap the active model |
91
+ | `/revert` | Instantly undo all changes from the last turn |
92
+ | `/commit` | Generate a conventional commit message from your diff |
93
+ | `/auto-approve` | Skip confirmation prompts for commands |
94
+ | `/paste` | Multi-line input mode |
95
+ | `/debug` | Expose internal agent diagnostics |
96
+ | `stop` | Abort generation immediately |
143
97
 
144
- DESCRIPTION
145
- Sign out of your Minovative Mind account
146
- ```
147
- <!-- commandsstop -->
98
+ ---
148
99
 
149
100
  ## Legal
150
101
 
151
- - [Terms of Service](https://www.minovativemind.dev/legal/cli-terms)
152
- - [Privacy Policy](https://www.minovativemind.dev/legal/cli-privacy)
102
+ [Terms of Service](https://www.minovativemind.dev/legal/cli-terms) ·
103
+ [Privacy Policy](https://www.minovativemind.dev/legal/cli-privacy)
@@ -893,7 +893,7 @@ ${diffOut}
893
893
  let contextInjection = buildContextInjection(gatherRes.contextResult);
894
894
  debugLog(`Raw context injection size: ${contextInjection.length} chars`);
895
895
  // Compress context inputs using Gemini Flash Lite to optimize prompt density and prevent token bloat
896
- contextInjection = await compressTextUsingFlashLite(contextInjection, 'Summarize the following project context concisely. Preserve all file paths, project types, and the high-level purpose of the read files. Keep it under 2000 characters if possible.');
896
+ contextInjection = await compressTextUsingFlashLite(contextInjection, `Summarize the following project context concisely. Preserve all file paths, project types, and the high-level purpose of the read files. Keep it under 2000 characters if possible.\nCRITICAL: Focus specifically on details relevant to the user's current request: "${userInput}"`);
897
897
  debugLog(`Compressed context injection size: ${contextInjection.length} chars`);
898
898
  dynamicSystemInstruction += '\n\n' + contextInjection;
899
899
  }
@@ -965,12 +965,16 @@ ${diffOut}
965
965
  break;
966
966
  // Stage 3: Static code analysis / verification
967
967
  p.log.step('Verifying modified files...');
968
- const verificationErrors = await verifyChangedFiles(workspaceRoot, changedFiles, ac.signal);
969
- if (verificationErrors === '[Verification Aborted]') {
968
+ const verificationResult = await verifyChangedFiles(workspaceRoot, changedFiles, ac.signal);
969
+ if (verificationResult === '[Verification Aborted]') {
970
970
  p.log.warn(pc.yellow('Verification aborted by user.'));
971
971
  break;
972
972
  }
973
- if (!verificationErrors) {
973
+ // Print non-blocking performance warnings to the terminal
974
+ if (verificationResult.warnings) {
975
+ p.log.warn('Performance Audit Warnings:\n' + verificationResult.warnings);
976
+ }
977
+ if (!verificationResult.errors) {
974
978
  p.log.success('Verification passed.');
975
979
  break;
976
980
  }
@@ -980,11 +984,11 @@ ${diffOut}
980
984
  break;
981
985
  }
982
986
  p.log.warn(`${pc.yellow('Verification failed. Auto-correcting errors')} (Attempt ${correctionAttempts}/${MAX_CORRECTIONS})...`);
983
- const displayError = verificationErrors.split('\n').slice(0, 5).join('\n');
987
+ const displayError = verificationResult.errors.split('\n').slice(0, 5).join('\n');
984
988
  console.log(pc.dim(` ${displayError.replace(/\n/g, '\n ')}\n ...`));
985
- debugLog(`Verification failed on attempt ${correctionAttempts}/${MAX_CORRECTIONS}. Errors:\n${verificationErrors}`);
989
+ debugLog(`Verification failed on attempt ${correctionAttempts}/${MAX_CORRECTIONS}. Errors:\n${verificationResult.errors}`);
986
990
  // Compile compilation and syntax diagnostic warnings into an auto-correction prompt
987
- const correctionPrompt = `AUTOMATED SYSTEM CHECK: Your previous changes resulted in the following errors:\n\n${verificationErrors}\n\nPlease analyze these errors and use your file modification tools to fix them.`;
991
+ const correctionPrompt = `AUTOMATED SYSTEM CHECK: Your previous changes resulted in the following errors:\n\n${verificationResult.errors}\n\nPlease analyze these errors and use your file modification tools to fix them.`;
988
992
  spinner.start('Thinking (Correction)...');
989
993
  result = await chat.sendMessage(correctionPrompt);
990
994
  spinner.stop('');
@@ -1,13 +1,55 @@
1
1
  import type { Content, Tool, ToolConfig, FunctionCall } from '@google/generative-ai';
2
+ /**
3
+ * ============================================================================
4
+ * PROXY CLIENT SERVICE
5
+ * ============================================================================
6
+ * Facilitates stream-based communication with the secure, Firebase-authenticated
7
+ * serverless Gemini content generation proxy.
8
+ *
9
+ * Core Capabilities:
10
+ * - Server-Sent Events (SSE) parsing for thoughts, text, and function calls.
11
+ * - Secure Authentication handling (Firebase ID Tokens).
12
+ * - Real-time stream-callback piping for instant responses.
13
+ * - Accurate token usage, credit consumption, and grounding metadata parsing.
14
+ * ============================================================================
15
+ */
16
+ /**
17
+ * Metadata containing real-time proxy token and credit usage diagnostics.
18
+ */
2
19
  export interface ProxyUsageMetadata {
20
+ /** The number of tokens detected in the prompt/input. */
3
21
  promptTokens: number;
22
+ /** The number of tokens generated as candidates. */
4
23
  candidatesTokens: number;
24
+ /** The number of cached tokens, if context caching was utilized. */
5
25
  cachedTokens?: number;
26
+ /** The credit cost of this request in micro-credits or target monetary units. */
6
27
  creditsUsed: number;
28
+ /** The remaining balance left in the user's account. */
7
29
  remainingBalance: number;
8
30
  }
31
+ /**
32
+ * Client service interacting directly with the serverless Gemini proxy endpoint.
33
+ * Ensures authorization via Firebase token passing and parses streamed content.
34
+ */
9
35
  export declare class ProxyClient {
10
36
  private readonly PROXY_URL;
37
+ /**
38
+ * Generates text, thoughts, or function calls via the secure Gemini proxy URL.
39
+ * Utilizes Server-Sent Events (SSE) to stream partial token responses back to the client.
40
+ *
41
+ * @param idToken - The Firebase ID token for authorization.
42
+ * @param modelName - The name of the Gemini model to target.
43
+ * @param contents - Structured conversations/contents matching Google's schema.
44
+ * @param tools - Optional tools available for the model to execute.
45
+ * @param toolConfig - Optional routing/forcing behavior configuration for the tools.
46
+ * @param systemInstruction - Optional instructions for guiding the model's persona/behavior.
47
+ * @param generationConfig - Optional parameters controlling model creativity, output type, etc.
48
+ * @param streamCallbacks - Optional callbacks triggered as thoughts/content stream in.
49
+ * @param abortSignal - Optional signal to abort the fetch request midway.
50
+ * @returns A structured promise resolving to gathered text, function calls, and metadata.
51
+ * @throws {Error} If authentication fails (401), credits are insufficient (402), or network/proxy errors occur.
52
+ */
11
53
  generateFunctionCallViaProxy(idToken: string, modelName: string, contents: Content[], tools?: Tool[], toolConfig?: ToolConfig, systemInstruction?: string | Content, generationConfig?: any, streamCallbacks?: {
12
54
  onChunk: (chunk: string) => void;
13
55
  }, abortSignal?: AbortSignal): Promise<{
@@ -1,6 +1,26 @@
1
1
  import { debugLog } from '../utils/logger.js';
2
+ /**
3
+ * Client service interacting directly with the serverless Gemini proxy endpoint.
4
+ * Ensures authorization via Firebase token passing and parses streamed content.
5
+ */
2
6
  export class ProxyClient {
3
7
  PROXY_URL = 'https://generatecontent-6obg3e4zwa-uc.a.run.app';
8
+ /**
9
+ * Generates text, thoughts, or function calls via the secure Gemini proxy URL.
10
+ * Utilizes Server-Sent Events (SSE) to stream partial token responses back to the client.
11
+ *
12
+ * @param idToken - The Firebase ID token for authorization.
13
+ * @param modelName - The name of the Gemini model to target.
14
+ * @param contents - Structured conversations/contents matching Google's schema.
15
+ * @param tools - Optional tools available for the model to execute.
16
+ * @param toolConfig - Optional routing/forcing behavior configuration for the tools.
17
+ * @param systemInstruction - Optional instructions for guiding the model's persona/behavior.
18
+ * @param generationConfig - Optional parameters controlling model creativity, output type, etc.
19
+ * @param streamCallbacks - Optional callbacks triggered as thoughts/content stream in.
20
+ * @param abortSignal - Optional signal to abort the fetch request midway.
21
+ * @returns A structured promise resolving to gathered text, function calls, and metadata.
22
+ * @throws {Error} If authentication fails (401), credits are insufficient (402), or network/proxy errors occur.
23
+ */
4
24
  async generateFunctionCallViaProxy(idToken, modelName, contents, tools, toolConfig, systemInstruction, generationConfig, streamCallbacks, abortSignal) {
5
25
  const response = await fetch(this.PROXY_URL, {
6
26
  method: 'POST',
@@ -3,8 +3,13 @@ export interface VerificationResult {
3
3
  command: string;
4
4
  output: string;
5
5
  errors: string[];
6
+ aborted?: boolean;
6
7
  }
7
8
  export declare function detectVerificationCommand(workspaceRoot: string): Promise<string | null>;
8
9
  export declare function runVerification(workspaceRoot: string, abortSignal?: AbortSignal): Promise<VerificationResult | null>;
9
10
  export declare function formatVerificationForModel(result: VerificationResult): string;
10
- export declare function verifyChangedFiles(workspaceRoot: string, filePaths: string[], abortSignal?: AbortSignal): Promise<string | null>;
11
+ export interface FullVerificationResult {
12
+ errors: string | null;
13
+ warnings: string | null;
14
+ }
15
+ export declare function verifyChangedFiles(workspaceRoot: string, filePaths: string[], abortSignal?: AbortSignal): Promise<FullVerificationResult | '[Verification Aborted]'>;
@@ -5,19 +5,35 @@ import { promisify } from 'node:util';
5
5
  import { debugLog } from '../utils/logger.js';
6
6
  const execAsync = promisify(exec);
7
7
  export async function detectVerificationCommand(workspaceRoot) {
8
+ // Package Manager Detection
9
+ let pmPrefix = 'npm run';
10
+ const lockFiles = [
11
+ { name: 'pnpm-lock.yaml', prefix: 'pnpm run' },
12
+ { name: 'yarn.lock', prefix: 'yarn run' },
13
+ { name: 'bun.lockb', prefix: 'bun run' },
14
+ { name: 'bun.lock', prefix: 'bun run' }
15
+ ];
16
+ for (const lf of lockFiles) {
17
+ try {
18
+ await fs.access(path.join(workspaceRoot, lf.name));
19
+ pmPrefix = lf.prefix;
20
+ break;
21
+ }
22
+ catch { }
23
+ }
8
24
  try {
9
25
  const pkgJsonPath = path.join(workspaceRoot, 'package.json');
10
26
  const pkgJsonStr = await fs.readFile(pkgJsonPath, 'utf-8');
11
27
  const pkgJson = JSON.parse(pkgJsonStr);
12
28
  if (pkgJson.scripts) {
13
29
  if (pkgJson.scripts.build)
14
- return 'npm run build';
30
+ return `${pmPrefix} build`;
15
31
  if (pkgJson.scripts.typecheck)
16
- return 'npm run typecheck';
32
+ return `${pmPrefix} typecheck`;
17
33
  if (pkgJson.scripts.lint)
18
- return 'npm run lint';
34
+ return `${pmPrefix} lint`;
19
35
  if (pkgJson.scripts.check)
20
- return 'npm run check';
36
+ return `${pmPrefix} check`;
21
37
  }
22
38
  }
23
39
  catch {
@@ -33,6 +49,83 @@ export async function detectVerificationCommand(workspaceRoot) {
33
49
  return 'cargo check';
34
50
  }
35
51
  catch { }
52
+ try {
53
+ await fs.access(path.join(workspaceRoot, 'go.mod'));
54
+ return 'go build ./...';
55
+ }
56
+ catch { }
57
+ try {
58
+ await fs.access(path.join(workspaceRoot, 'pom.xml'));
59
+ return 'mvn clean compile test-compile';
60
+ }
61
+ catch { }
62
+ try {
63
+ await fs.access(path.join(workspaceRoot, 'build.gradle'));
64
+ return (await fs.access(path.join(workspaceRoot, 'gradlew')).then(() => true).catch(() => false))
65
+ ? './gradlew classes testClasses'
66
+ : 'gradle classes testClasses';
67
+ }
68
+ catch { }
69
+ try {
70
+ await fs.access(path.join(workspaceRoot, 'build.gradle.kts'));
71
+ return (await fs.access(path.join(workspaceRoot, 'gradlew')).then(() => true).catch(() => false))
72
+ ? './gradlew classes testClasses'
73
+ : 'gradle classes testClasses';
74
+ }
75
+ catch { }
76
+ try {
77
+ await fs.access(path.join(workspaceRoot, 'pubspec.yaml'));
78
+ return 'dart analyze';
79
+ }
80
+ catch { }
81
+ // Python Environment Awareness
82
+ let isPython = false;
83
+ for (const pyFile of ['pytest.ini', 'pyproject.toml', 'poetry.lock', 'uv.lock', 'Pipfile', '.venv', 'venv']) {
84
+ try {
85
+ await fs.access(path.join(workspaceRoot, pyFile));
86
+ isPython = true;
87
+ break;
88
+ }
89
+ catch { }
90
+ }
91
+ if (isPython) {
92
+ try {
93
+ await fs.access(path.join(workspaceRoot, 'poetry.lock'));
94
+ return 'poetry run pytest';
95
+ }
96
+ catch { }
97
+ try {
98
+ await fs.access(path.join(workspaceRoot, 'uv.lock'));
99
+ return 'uv run pytest';
100
+ }
101
+ catch { }
102
+ try {
103
+ await fs.access(path.join(workspaceRoot, 'Pipfile'));
104
+ return 'pipenv run pytest';
105
+ }
106
+ catch { }
107
+ const isWindows = process.platform === 'win32';
108
+ try {
109
+ await fs.access(path.join(workspaceRoot, '.venv'));
110
+ const pytestPath = isWindows ? '.venv\\Scripts\\pytest' : '.venv/bin/pytest';
111
+ return pytestPath;
112
+ }
113
+ catch { }
114
+ try {
115
+ await fs.access(path.join(workspaceRoot, 'venv'));
116
+ const pytestPath = isWindows ? 'venv\\Scripts\\pytest' : 'venv/bin/pytest';
117
+ return pytestPath;
118
+ }
119
+ catch { }
120
+ return 'pytest';
121
+ }
122
+ try {
123
+ await fs.access(path.join(workspaceRoot, 'Gemfile'));
124
+ return (await fs.access(path.join(workspaceRoot, 'spec')).then(() => true).catch(() => false))
125
+ ? 'bundle exec rspec'
126
+ : 'bundle exec rubocop';
127
+ }
128
+ catch { }
36
129
  return null;
37
130
  }
38
131
  export async function runVerification(workspaceRoot, abortSignal) {
@@ -44,7 +137,7 @@ export async function runVerification(workspaceRoot, abortSignal) {
44
137
  try {
45
138
  const { stdout, stderr } = await execAsync(command, {
46
139
  cwd: workspaceRoot,
47
- timeout: 90_000, // 90s (1 min & 30 secs) - builds can take a while (e.g., Next.js)
140
+ timeout: 120_000, // 120s (2 mins) - builds can take a while (e.g., Next.js, Gradle)
48
141
  maxBuffer: 1024 * 1024, // 1 MB buffer
49
142
  signal: abortSignal,
50
143
  });
@@ -72,9 +165,25 @@ export async function runVerification(workspaceRoot, abortSignal) {
72
165
  // Extract error lines with context
73
166
  const errors = [];
74
167
  let capturingError = false;
168
+ const isErrorLine = (line) => {
169
+ const lower = line.toLowerCase();
170
+ if (lower.includes('fail') ||
171
+ lower.includes('failed') ||
172
+ lower.includes('exception') ||
173
+ lower.includes('fatal') ||
174
+ lower.includes('panic') ||
175
+ lower.includes('traceback') ||
176
+ lower.includes('error') ||
177
+ lower.includes('err!') ||
178
+ /:\d+:\d+:/.test(line) ||
179
+ /:\d+:/.test(line)) {
180
+ return true;
181
+ }
182
+ return false;
183
+ };
75
184
  for (const line of lines) {
76
185
  const lower = line.toLowerCase();
77
- if (lower.includes('error') || lower.includes('err!')) {
186
+ if (isErrorLine(line)) {
78
187
  capturingError = true;
79
188
  errors.push(line);
80
189
  }
@@ -87,6 +196,12 @@ export async function runVerification(workspaceRoot, abortSignal) {
87
196
  }
88
197
  }
89
198
  }
199
+ if (errors.length === 0) {
200
+ const fallbackSource = rawStderr.trim() ? rawStderr : rawStdout;
201
+ const fallbackLines = fallbackSource.split('\n');
202
+ const last40 = fallbackLines.slice(-40);
203
+ errors.push(...last40);
204
+ }
90
205
  return {
91
206
  success: false,
92
207
  command,
@@ -105,11 +220,13 @@ ${result.errors.join('\n')}
105
220
 
106
221
  Please fix these errors using the modify_file tool.`;
107
222
  }
223
+ import { auditFilePerformance, formatAuditForModel, formatAuditForTerminal, isAuditableFile } from '../utils/performanceAuditor.js';
108
224
  export async function verifyChangedFiles(workspaceRoot, filePaths, abortSignal) {
109
225
  if (filePaths.length === 0)
110
- return null;
226
+ return { errors: null, warnings: null };
111
227
  const errors = [];
112
- // Project-level build check (e.g., npm run build)
228
+ const perfAudits = [];
229
+ // 1. Project-level build check (e.g., npm run build)
113
230
  const buildResult = await runVerification(workspaceRoot, abortSignal);
114
231
  if (buildResult && !buildResult.success) {
115
232
  if (buildResult.aborted) {
@@ -118,5 +235,35 @@ export async function verifyChangedFiles(workspaceRoot, filePaths, abortSignal)
118
235
  const buildErrorMsg = `[Build Error: ${buildResult.command}]\n${buildResult.errors.join('\n').substring(0, 5000)}`;
119
236
  errors.push(buildErrorMsg);
120
237
  }
121
- return errors.length > 0 ? errors.join('\n\n---\n\n') : null;
238
+ // 2. Performance Audit
239
+ // We run this even if the build failed, so the model gets all feedback at once
240
+ for (const file of filePaths) {
241
+ if (abortSignal?.aborted)
242
+ return '[Verification Aborted]';
243
+ if (!isAuditableFile(file))
244
+ continue;
245
+ try {
246
+ const absolutePath = path.resolve(workspaceRoot, file);
247
+ const content = await fs.readFile(absolutePath, 'utf-8');
248
+ const audit = auditFilePerformance(content, file);
249
+ if (audit.findings.length > 0) {
250
+ perfAudits.push(audit);
251
+ }
252
+ }
253
+ catch (err) {
254
+ debugLog(`Failed to audit performance for ${file}: ${err}`);
255
+ }
256
+ }
257
+ // Extract blocking performance errors (Severity: ERROR)
258
+ const perfModelPrompt = formatAuditForModel(perfAudits);
259
+ if (perfModelPrompt) {
260
+ errors.push(perfModelPrompt);
261
+ }
262
+ // Extract non-blocking performance warnings for the terminal (Severity: WARNING | INFO)
263
+ // We only show terminal warnings for files that actually had warnings or info (ignoring files that only had errors)
264
+ const terminalWarnings = formatAuditForTerminal(perfAudits.filter((a) => a.warnings.length > 0 || a.infos.length > 0));
265
+ return {
266
+ errors: errors.length > 0 ? errors.join('\n\n---\n\n') : null,
267
+ warnings: terminalWarnings || null,
268
+ };
122
269
  }
@@ -0,0 +1,42 @@
1
+ export type PerfSeverity = 'ERROR' | 'WARNING' | 'INFO';
2
+ export interface PerfFinding {
3
+ severity: PerfSeverity;
4
+ code: string;
5
+ line: number;
6
+ message: string;
7
+ suggestion: string;
8
+ }
9
+ export interface PerfAuditResult {
10
+ filePath: string;
11
+ findings: PerfFinding[];
12
+ errors: PerfFinding[];
13
+ warnings: PerfFinding[];
14
+ infos: PerfFinding[];
15
+ }
16
+ /**
17
+ * Returns true if the file extension is supported by the performance auditor.
18
+ */
19
+ export declare function isAuditableFile(filePath: string): boolean;
20
+ /**
21
+ * Scans source code for performance anti-patterns using language-aware
22
+ * regex-based heuristics. Returns structured findings grouped by severity.
23
+ *
24
+ * This function is designed to be called inline during the verification
25
+ * phase — it's synchronous, zero-dependency, and executes in <50ms on
26
+ * files up to 10,000 lines.
27
+ *
28
+ * @param content - The raw source code string to audit.
29
+ * @param filePath - The file path (used for language detection via extension).
30
+ * @returns A structured audit result with findings separated by severity.
31
+ */
32
+ export declare function auditFilePerformance(content: string, filePath: string): PerfAuditResult;
33
+ /**
34
+ * Formats audit findings for beautiful terminal display using picocolors.
35
+ * Used for non-blocking warnings shown to the user after verification.
36
+ */
37
+ export declare function formatAuditForTerminal(results: PerfAuditResult[]): string;
38
+ /**
39
+ * Formats audit findings into a structured prompt for the AI auto-correction loop.
40
+ * Only includes ERROR-severity findings (warnings and info are non-blocking).
41
+ */
42
+ export declare function formatAuditForModel(results: PerfAuditResult[]): string;