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 +1 -1
- package/README.md +65 -114
- 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,103 @@
|
|
|
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
|
+
### 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
|
[](https://oclif.io)
|
|
8
14
|
[](https://npmjs.org/package/minovative-mind-cli)
|
|
9
15
|
[](https://npmjs.org/package/minovative-mind-cli)
|
|
10
16
|
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
minovative-mind-cli login
|
|
45
|
-
```
|
|
21
|
+
Standard AI coding agents: big model + noisy context = expensive and redundant mistakes
|
|
46
22
|
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
44
|
+
## See it in action
|
|
68
45
|
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
49
|
+
---
|
|
76
50
|
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
[COMMAND...] Command to show help for.
|
|
66
|
+
Works in any terminal — SSH, Docker, CI pipelines, Vim, anywhere Node runs.
|
|
116
67
|
|
|
117
|
-
|
|
118
|
-
-n, --nested-commands Include all nested commands in the output.
|
|
68
|
+
---
|
|
119
69
|
|
|
120
|
-
|
|
121
|
-
Display help for minovative-mind-cli.
|
|
122
|
-
```
|
|
70
|
+
## Models
|
|
123
71
|
|
|
124
|
-
|
|
72
|
+
Hot-swap during a session with `/models`:
|
|
125
73
|
|
|
126
|
-
|
|
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
|
-
|
|
130
|
-
|
|
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
|
-
|
|
133
|
-
Sign in to your Minovative Mind account using GitHub
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## `minovative-mind-cli logout`
|
|
84
|
+
---
|
|
137
85
|
|
|
138
|
-
|
|
86
|
+
## Session Commands
|
|
139
87
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
145
|
-
Sign out of your Minovative Mind account
|
|
146
|
-
```
|
|
147
|
-
<!-- commandsstop -->
|
|
98
|
+
---
|
|
148
99
|
|
|
149
100
|
## Legal
|
|
150
101
|
|
|
151
|
-
|
|
152
|
-
|
|
102
|
+
[Terms of Service](https://www.minovativemind.dev/legal/cli-terms) ·
|
|
103
|
+
[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;
|