minovative-mind-cli 1.2.3 → 1.3.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/LICENSE.md +1 -1
- package/README.md +68 -110
- package/dist/services/agent.js +11 -7
- package/dist/services/proxyClient.d.ts +42 -0
- package/dist/services/proxyClient.js +20 -0
- package/dist/services/verificationService.d.ts +6 -1
- package/dist/services/verificationService.js +156 -9
- package/dist/utils/performanceAuditor.d.ts +42 -0
- package/dist/utils/performanceAuditor.js +615 -0
- package/dist/utils/symbolExtractor.js +193 -164
- package/dist/utils/systemPrompts.d.ts +1 -1
- package/dist/utils/systemPrompts.js +5 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
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
|
|
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,110 @@
|
|
|
1
|
-
# Minovative Mind CLI
|
|
1
|
+
# Minovative Mind CLI
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## The CLI agent that makes Flash models perform like Pro.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Most AI coding tools throw your entire codebase at the biggest model available
|
|
6
|
+
and hope for the best.
|
|
7
|
+
|
|
8
|
+
This CLI does the opposite — it uses a custom built agentic system called,
|
|
9
|
+
**Precision-Context Verification (PCV)** engine to feed lightweight Flash models
|
|
10
|
+
exactly the right context, execute code, verify the output compiles, and self-correct (if it even needs to), until
|
|
11
|
+
the build/performance metrics are green.
|
|
12
|
+
|
|
13
|
+
> The result: **Genuine Pro-level accuracy at Flash-level cost.**
|
|
6
14
|
|
|
7
15
|
[](https://oclif.io)
|
|
8
16
|
[](https://npmjs.org/package/minovative-mind-cli)
|
|
9
17
|
[](https://npmjs.org/package/minovative-mind-cli)
|
|
10
18
|
|
|
11
|
-
|
|
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)
|
|
19
|
+
---
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
## How it works
|
|
28
22
|
|
|
29
|
-
|
|
23
|
+
- Standard AI coding agents: big model + noisy context = expensive and redundant mistakes
|
|
24
|
+
- Minovative Mind CLI: flash models + precise context + verified output = better and cheaper results.
|
|
30
25
|
|
|
31
|
-
|
|
26
|
+
### The PCV engine runs in three stages before returning a single line of code:
|
|
32
27
|
|
|
33
|
-
1. **
|
|
28
|
+
**1. Investigate**
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```
|
|
30
|
+
- **Maps your project:** Traces import dependencies and identifies recently modified files to understand your focus.
|
|
31
|
+
- **Researches context:** Gathers external web documentation and completes background research before touching any code.
|
|
38
32
|
|
|
39
|
-
2. **
|
|
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:
|
|
33
|
+
**2. Execute with precision**
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```
|
|
35
|
+
- **Surgical snapshots:** Feeds the agent a compressed, relevant slice of code instead of dumping the entire codebase.
|
|
36
|
+
- **Higher success rates:** Delivers 40–50% fewer broken generations on large files compared to standard zero-shot prompts.
|
|
46
37
|
|
|
47
|
-
3. **
|
|
48
|
-
Navigate to any project directory on your computer and start a chat session:
|
|
38
|
+
**3. Verify and self-correct**
|
|
49
39
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
minovative-mind-cli chat
|
|
53
|
-
```
|
|
40
|
+
- **Runs native builds & audits:** Automatically executes your actual build commands (`npm run build`, `cargo check`, etc.) and scans for performance flaws on modified files only.
|
|
41
|
+
- **Autonomously debugs:** Reads the exact compiler output and fixes its own errors across up to 3 hands-free correction cycles.
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
Once in a chat, type `/models` to switch between advanced reasoning models like Gemini 3.5 Flash and Gemini 3.1 Pro!
|
|
43
|
+
---
|
|
57
44
|
|
|
58
|
-
|
|
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!)
|
|
45
|
+
## See it in action
|
|
60
46
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```
|
|
47
|
+
- [AI builds a Python Generative Art Engine in one prompt](https://www.youtube.com/watch?v=dy3qNbETqfs)
|
|
48
|
+
- [AI builds a Quiz Game from scratch](https://www.youtube.com/watch?v=KjE5nbKEf3w)
|
|
64
49
|
|
|
65
50
|
---
|
|
66
51
|
|
|
67
|
-
|
|
68
|
-
|
|
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)
|
|
52
|
+
## Quick Start
|
|
74
53
|
|
|
75
|
-
|
|
54
|
+
```bash
|
|
55
|
+
npm install -g minovative-mind-cli
|
|
56
|
+
```
|
|
76
57
|
|
|
77
|
-
|
|
58
|
+
### Update to latest version
|
|
78
59
|
|
|
60
|
+
```bash
|
|
61
|
+
npm update -g minovative-mind-cli
|
|
79
62
|
```
|
|
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
63
|
|
|
108
|
-
|
|
64
|
+
Create a free account at [minovativemind.dev](https://www.minovativemind.dev),
|
|
65
|
+
then:
|
|
109
66
|
|
|
67
|
+
```bash
|
|
68
|
+
minovative-mind-cli login # One-click GitHub sign-in
|
|
69
|
+
cd your-project
|
|
70
|
+
minovative-mind-cli chat # Start coding
|
|
110
71
|
```
|
|
111
|
-
USAGE
|
|
112
|
-
$ minovative-mind-cli help [COMMAND...] [-n]
|
|
113
72
|
|
|
114
|
-
|
|
115
|
-
[COMMAND...] Command to show help for.
|
|
73
|
+
Works in any terminal — SSH, Docker, CI pipelines, Vim, anywhere Node runs.
|
|
116
74
|
|
|
117
|
-
|
|
118
|
-
-n, --nested-commands Include all nested commands in the output.
|
|
119
|
-
|
|
120
|
-
DESCRIPTION
|
|
121
|
-
Display help for minovative-mind-cli.
|
|
122
|
-
```
|
|
75
|
+
---
|
|
123
76
|
|
|
124
|
-
##
|
|
77
|
+
## Models
|
|
125
78
|
|
|
126
|
-
|
|
79
|
+
Hot-swap during a session with `/models`:
|
|
127
80
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
81
|
+
| Model | Best for |
|
|
82
|
+
| ------------------------------ | ----------------------------------- |
|
|
83
|
+
| **Gemini 3.5 Flash** (default) | Everyday coding — fast and accurate |
|
|
84
|
+
| **Gemini 3.1 Pro** | Complex architectural changes |
|
|
85
|
+
| **Gemini 3.1 Flash Lite** | Maximum speed and cost efficiency |
|
|
131
86
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
87
|
+
> Background tasks (routing, compression, commits) always use lightweight
|
|
88
|
+
> models automatically. You only pay for what your selected model costs
|
|
89
|
+
> during chat and code execution. Use `/debug` to see exactly what's running.
|
|
135
90
|
|
|
136
|
-
|
|
91
|
+
---
|
|
137
92
|
|
|
138
|
-
|
|
93
|
+
## Session Commands
|
|
139
94
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
95
|
+
| Command | What it does |
|
|
96
|
+
| --------------- | ----------------------------------------------------- |
|
|
97
|
+
| `/models` | Hot-swap the active model |
|
|
98
|
+
| `/revert` | Instantly undo all changes from the last turn |
|
|
99
|
+
| `/commit` | Generate a conventional commit message from your diff |
|
|
100
|
+
| `/auto-approve` | Skip confirmation prompts for commands |
|
|
101
|
+
| `/paste` | Multi-line input mode |
|
|
102
|
+
| `/debug` | Expose internal agent diagnostics |
|
|
103
|
+
| `stop` | Abort generation immediately |
|
|
143
104
|
|
|
144
|
-
|
|
145
|
-
Sign out of your Minovative Mind account
|
|
146
|
-
```
|
|
147
|
-
<!-- commandsstop -->
|
|
105
|
+
---
|
|
148
106
|
|
|
149
107
|
## Legal
|
|
150
108
|
|
|
151
|
-
|
|
152
|
-
|
|
109
|
+
[Terms of Service](https://www.minovativemind.dev/legal/cli-terms) ·
|
|
110
|
+
[Privacy Policy](https://www.minovativemind.dev/legal/cli-privacy)
|
package/dist/services/agent.js
CHANGED
|
@@ -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,
|
|
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
|
|
969
|
-
if (
|
|
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
|
-
|
|
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 =
|
|
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${
|
|
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${
|
|
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
|
|
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
|
|
30
|
+
return `${pmPrefix} build`;
|
|
15
31
|
if (pkgJson.scripts.typecheck)
|
|
16
|
-
return
|
|
32
|
+
return `${pmPrefix} typecheck`;
|
|
17
33
|
if (pkgJson.scripts.lint)
|
|
18
|
-
return
|
|
34
|
+
return `${pmPrefix} lint`;
|
|
19
35
|
if (pkgJson.scripts.check)
|
|
20
|
-
return
|
|
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:
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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;
|