minovative-mind-cli 2.11.4 → 2.12.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/README.md +21 -7
- package/dist/commands/eval.d.ts +22 -0
- package/dist/commands/eval.js +141 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/services/agent/toolLoop.d.ts +4 -0
- package/dist/services/agent/toolLoop.js +61 -10
- package/dist/services/agent-tools.d.ts +5 -5
- package/dist/services/agent-tools.js +147 -9
- package/dist/services/ai.d.ts +1 -1
- package/dist/services/ai.js +40 -7
- package/dist/services/contextAgent.d.ts +1 -0
- package/dist/services/contextAgent.js +29 -6
- package/dist/services/investigationComplexity.d.ts +39 -13
- package/dist/services/investigationComplexity.js +325 -46
- package/dist/services/metrics.d.ts +10 -0
- package/dist/services/metrics.js +24 -0
- package/dist/services/orchestration/scopedTools.d.ts +27 -50
- package/dist/services/orchestration/scopedTools.js +60 -18
- package/dist/services/swebench/gitDiffExtractor.d.ts +57 -0
- package/dist/services/swebench/gitDiffExtractor.js +209 -0
- package/dist/services/swebench/index.d.ts +4 -0
- package/dist/services/swebench/index.js +4 -0
- package/dist/services/swebench/instanceLoader.d.ts +21 -0
- package/dist/services/swebench/instanceLoader.js +171 -0
- package/dist/services/swebench/sweBenchRunnerService.d.ts +38 -0
- package/dist/services/swebench/sweBenchRunnerService.js +618 -0
- package/dist/services/swebench/types.d.ts +167 -0
- package/dist/services/swebench/types.js +7 -0
- package/dist/services/verificationService.js +3 -0
- package/dist/services/workspaceRegistry.d.ts +81 -8
- package/dist/services/workspaceRegistry.js +222 -34
- package/dist/utils/analysisRunner.js +2 -1
- package/dist/utils/pathSecurity.d.ts +56 -14
- package/dist/utils/pathSecurity.js +120 -39
- package/dist/utils/systemPrompts.d.ts +6 -6
- package/dist/utils/systemPrompts.js +46 -25
- package/oclif.manifest.json +137 -1
- package/package.json +1 -1
package/dist/services/ai.js
CHANGED
|
@@ -13,7 +13,17 @@ function getMultiWorkspaceBlock() {
|
|
|
13
13
|
const summary = workspaceRegistry.buildPromptSummary();
|
|
14
14
|
const primaryRoot = process.cwd();
|
|
15
15
|
const primaryName = primaryRoot.split(/[/\\]/).pop() || 'Primary';
|
|
16
|
+
const primarySubPath = workspaceRegistry.getPrimarySubPath();
|
|
16
17
|
let block = `<workspace_context>\nYour current primary workspace (the default "./" root) is:\n- ./ (Workspace: ${primaryName}) → ${primaryRoot}\n`;
|
|
18
|
+
if (primarySubPath) {
|
|
19
|
+
block += `- Active Primary Sub-Path: "${primarySubPath}" (All relative search, inspection, and file operations focus into this sub-directory by default unless another sub-path or external workspace is explicitly specified).\n`;
|
|
20
|
+
}
|
|
21
|
+
block += `\n<workspace_focus_rules>
|
|
22
|
+
- **Primary Focus Default (CRITICAL)**: Always default your investigation, search, file reading, and modifications to the current primary workspace and active primary sub-path. Unless the user explicitly names another sub-path or external workspace, assume all user instructions ("find this", "update that", "refactor X") refer to the primary workspace and active sub-path you are currently in.
|
|
23
|
+
- **Explicit Sub-Paths**: Only target a different sub-directory if the user explicitly specifies it.
|
|
24
|
+
- **External Workspaces (@alias/)**: Only access external workspaces if the user explicitly references them or uses the registered @alias prefix.
|
|
25
|
+
- **Strict Boundary Defense & Missing Workspace Handling**: Never attempt relative path traversal (like "../other-repo") or use "run_command" (such as node -e or bash scripts with fs/cat/cp) to read or write files outside registered workspace boundaries. If the user asks for a project or workspace that is NOT registered or available, you MUST NOT use commands or scripts to work around it; instead, immediately and clearly inform the user that the project or workspace is not registered and instruct them to add it via "/workspaces".
|
|
26
|
+
</workspace_focus_rules>\n`;
|
|
17
27
|
if (summary) {
|
|
18
28
|
block += `\nThe user has also registered external workspaces that you can access using the @alias/ prefix:\n${summary}\n\nTo read, modify, or search files in an external workspace, prefix the file path with the alias (e.g., "@backend/src/routes.ts"). To search across ALL workspaces, use grep_search with workspace="all".\n\nWhen the user asks to "transfer", "sync", or "port" features between projects, read the source files from one workspace and apply the changes to the target.\n`;
|
|
19
29
|
}
|
|
@@ -65,12 +75,16 @@ const MAX_PART_TEXT_LENGTH = 60_000;
|
|
|
65
75
|
const HISTORICAL_TOOL_OUTPUT_THRESHOLD = 1500;
|
|
66
76
|
const COLLAPSED_TOOL_OUTPUT_MARKER = '\n... [Historical tool output collapsed to save context]';
|
|
67
77
|
function collapseHistoricalOutput(val, threshold = HISTORICAL_TOOL_OUTPUT_THRESHOLD) {
|
|
78
|
+
if (typeof val !== 'string')
|
|
79
|
+
return String(val ?? '');
|
|
68
80
|
if (val.length <= threshold || val.includes('[Historical tool output collapsed')) {
|
|
69
81
|
return val;
|
|
70
82
|
}
|
|
71
83
|
return `${val.substring(0, threshold)}${COLLAPSED_TOOL_OUTPUT_MARKER}`;
|
|
72
84
|
}
|
|
73
85
|
function truncatePartText(text) {
|
|
86
|
+
if (typeof text !== 'string')
|
|
87
|
+
return String(text ?? '');
|
|
74
88
|
if (text.length <= MAX_PART_TEXT_LENGTH)
|
|
75
89
|
return text;
|
|
76
90
|
return text.substring(0, MAX_PART_TEXT_LENGTH) + '\n... (output truncated to prevent memory overflow)';
|
|
@@ -289,6 +303,16 @@ export class ProxyChatSession {
|
|
|
289
303
|
}
|
|
290
304
|
}
|
|
291
305
|
async sendMessage(message, additionalText, abortSignal, onChunk) {
|
|
306
|
+
// Graceful argument normalization if AbortSignal is passed as 2nd argument
|
|
307
|
+
let actualAdditionalText;
|
|
308
|
+
let actualAbortSignal = abortSignal;
|
|
309
|
+
if (additionalText && typeof additionalText !== 'string' && additionalText.aborted !== undefined) {
|
|
310
|
+
actualAbortSignal = additionalText;
|
|
311
|
+
actualAdditionalText = undefined;
|
|
312
|
+
}
|
|
313
|
+
else if (typeof additionalText === 'string') {
|
|
314
|
+
actualAdditionalText = additionalText;
|
|
315
|
+
}
|
|
292
316
|
const idToken = await getAuthorizedIdToken();
|
|
293
317
|
if (!idToken) {
|
|
294
318
|
throw new Error('You are not signed in. Please run `minovative-mind-cli login` first.');
|
|
@@ -331,8 +355,8 @@ export class ProxyChatSession {
|
|
|
331
355
|
}
|
|
332
356
|
}
|
|
333
357
|
}
|
|
334
|
-
if (
|
|
335
|
-
newParts.push({ text: truncatePartText(
|
|
358
|
+
if (actualAdditionalText) {
|
|
359
|
+
newParts.push({ text: truncatePartText(actualAdditionalText) });
|
|
336
360
|
}
|
|
337
361
|
const userEntry = {
|
|
338
362
|
role: 'user',
|
|
@@ -348,12 +372,12 @@ export class ProxyChatSession {
|
|
|
348
372
|
const creds = await loadCredentials();
|
|
349
373
|
result = await proxyClient.generateViaBYOK(creds.geminiApiKey, this.modelName, this.history, this.tools, undefined, // toolConfig
|
|
350
374
|
this.systemInstruction, effectiveGenerationConfig, onChunk ? { onChunk } : undefined, // streamCallbacks
|
|
351
|
-
|
|
375
|
+
actualAbortSignal);
|
|
352
376
|
}
|
|
353
377
|
else {
|
|
354
378
|
result = await proxyClient.generateFunctionCallViaProxy(idToken, this.modelName, this.history, this.tools, undefined, // toolConfig
|
|
355
379
|
this.systemInstruction, effectiveGenerationConfig, onChunk ? { onChunk } : undefined, // streamCallbacks
|
|
356
|
-
|
|
380
|
+
actualAbortSignal);
|
|
357
381
|
}
|
|
358
382
|
this.latestUsageMetadata = result.usageMetadata;
|
|
359
383
|
// Track token usage metrics
|
|
@@ -426,7 +450,7 @@ export function createSharedChatSession() {
|
|
|
426
450
|
let model = executionModelOverride || getGlobalActiveModel();
|
|
427
451
|
if (model === 'auto')
|
|
428
452
|
model = GEMINI_MODELS.FLASH; // Will be overridden per-turn in executeSingleTurn
|
|
429
|
-
return new ProxyChatSession(model, GENERAL_CHAT_INSTRUCTION, [], {
|
|
453
|
+
return new ProxyChatSession(model, GENERAL_CHAT_INSTRUCTION.replace('{{MULTI_WORKSPACE_BLOCK}}', getMultiWorkspaceBlock()), [], {
|
|
430
454
|
maxOutputTokens: MAX_OUTPUT_TOKENS,
|
|
431
455
|
temperature: executionTempOverride !== null ? executionTempOverride : 1,
|
|
432
456
|
topP: 0.95,
|
|
@@ -435,7 +459,7 @@ export function createSharedChatSession() {
|
|
|
435
459
|
}
|
|
436
460
|
export function getGeneralChatConfig() {
|
|
437
461
|
return {
|
|
438
|
-
systemInstruction: GENERAL_CHAT_INSTRUCTION,
|
|
462
|
+
systemInstruction: GENERAL_CHAT_INSTRUCTION.replace('{{MULTI_WORKSPACE_BLOCK}}', getMultiWorkspaceBlock()),
|
|
439
463
|
tools: [{ googleSearch: {} }],
|
|
440
464
|
};
|
|
441
465
|
}
|
|
@@ -447,7 +471,7 @@ export function getPlanExecutionConfig() {
|
|
|
447
471
|
}
|
|
448
472
|
export function getPlanModeConfig() {
|
|
449
473
|
return {
|
|
450
|
-
systemInstruction: PLAN_MODE_INSTRUCTION,
|
|
474
|
+
systemInstruction: PLAN_MODE_INSTRUCTION.replace('{{MULTI_WORKSPACE_BLOCK}}', getMultiWorkspaceBlock()),
|
|
451
475
|
tools: [], // No tools allowed in plan mode
|
|
452
476
|
};
|
|
453
477
|
}
|
|
@@ -737,6 +761,15 @@ export function createInvestigationComplexitySession() {
|
|
|
737
761
|
enum: ['SINGLE', 'PARALLEL'],
|
|
738
762
|
description: 'Investigation strategy (SINGLE or PARALLEL)',
|
|
739
763
|
},
|
|
764
|
+
scope: {
|
|
765
|
+
type: SchemaType.STRING,
|
|
766
|
+
enum: ['SUB_PATH', 'FULL_WORKSPACE', 'EXTERNAL_WORKSPACE'],
|
|
767
|
+
description: 'Investigation scope: SUB_PATH (confined to active primary sub-path), FULL_WORKSPACE (spans whole repository or root files), or EXTERNAL_WORKSPACE (targets @alias/)',
|
|
768
|
+
},
|
|
769
|
+
subPathOverride: {
|
|
770
|
+
type: SchemaType.STRING,
|
|
771
|
+
description: 'The target scope identifier if overridden (e.g. "root", target root file, or @alias), or null if strictly confined to active primary sub-path',
|
|
772
|
+
},
|
|
740
773
|
domains: {
|
|
741
774
|
type: SchemaType.ARRAY,
|
|
742
775
|
items: {
|
|
@@ -10,6 +10,7 @@ export interface ContextAgentResult {
|
|
|
10
10
|
fromMemoryBank?: boolean;
|
|
11
11
|
isParallel?: boolean;
|
|
12
12
|
}
|
|
13
|
+
export declare function detectProjectType(workspaceRoot: string): Promise<string>;
|
|
13
14
|
export interface IntentRoute {
|
|
14
15
|
needsContext: boolean;
|
|
15
16
|
targetAgent: 'CHAT' | 'EXECUTE';
|
|
@@ -51,8 +51,15 @@ function boundToolOutput(output) {
|
|
|
51
51
|
truncationMarker: `\n\n... [Tool output truncated: exceeded ${MAX_TOOL_OUTPUT_TOKENS} tokens limit] ...\n`,
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
|
-
async function detectProjectType(workspaceRoot) {
|
|
54
|
+
export async function detectProjectType(workspaceRoot) {
|
|
55
55
|
const types = [];
|
|
56
|
+
// Detect Host Platform & Architecture
|
|
57
|
+
const platformName = process.platform === 'darwin' ? 'Darwin' : process.platform === 'win32' ? 'Windows' : 'Linux';
|
|
58
|
+
const isAppleSilicon = process.platform === 'darwin' && process.arch === 'arm64';
|
|
59
|
+
const hostDesc = isAppleSilicon
|
|
60
|
+
? `Host: ${platformName} ${process.arch} (Apple Silicon)`
|
|
61
|
+
: `Host: ${platformName} ${process.arch}`;
|
|
62
|
+
types.push(hostDesc);
|
|
56
63
|
const fileExists = async (fileName) => {
|
|
57
64
|
try {
|
|
58
65
|
await fs.access(path.join(workspaceRoot, fileName));
|
|
@@ -62,6 +69,13 @@ async function detectProjectType(workspaceRoot) {
|
|
|
62
69
|
return false;
|
|
63
70
|
}
|
|
64
71
|
};
|
|
72
|
+
// C / C++ / Native Build Toolchains
|
|
73
|
+
if (await fileExists('CMakeLists.txt'))
|
|
74
|
+
types.push('CMake');
|
|
75
|
+
if (await fileExists('Makefile'))
|
|
76
|
+
types.push('Makefile');
|
|
77
|
+
if (await fileExists('meson.build'))
|
|
78
|
+
types.push('Meson');
|
|
65
79
|
// Node.js Ecosystem
|
|
66
80
|
if (await fileExists('package.json')) {
|
|
67
81
|
types.push('Node.js');
|
|
@@ -93,17 +107,17 @@ async function detectProjectType(workspaceRoot) {
|
|
|
93
107
|
types.push('NestJS');
|
|
94
108
|
if (deps['vite'])
|
|
95
109
|
types.push('Vite');
|
|
96
|
-
if (deps['tailwindcss'])
|
|
97
|
-
types.push('Tailwind CSS');
|
|
98
|
-
if (deps['firebase'])
|
|
99
|
-
types.push('Firebase');
|
|
100
110
|
}
|
|
101
111
|
catch { }
|
|
102
112
|
if (await fileExists('tsconfig.json'))
|
|
103
113
|
types.push('TypeScript');
|
|
104
114
|
}
|
|
105
115
|
// Python Ecosystem
|
|
106
|
-
if ((await fileExists('pyproject.toml')) ||
|
|
116
|
+
if ((await fileExists('pyproject.toml')) ||
|
|
117
|
+
(await fileExists('requirements.txt')) ||
|
|
118
|
+
(await fileExists('setup.py')) ||
|
|
119
|
+
(await fileExists('setup.cfg')) ||
|
|
120
|
+
(await fileExists('Pipfile'))) {
|
|
107
121
|
types.push('Python');
|
|
108
122
|
try {
|
|
109
123
|
const reqs = (await fileExists('requirements.txt'))
|
|
@@ -119,6 +133,15 @@ async function detectProjectType(workspaceRoot) {
|
|
|
119
133
|
types.push('Flask');
|
|
120
134
|
if (combined.includes('fastapi'))
|
|
121
135
|
types.push('FastAPI');
|
|
136
|
+
if (combined.includes('cython') || (await fileExists('setup.py'))) {
|
|
137
|
+
try {
|
|
138
|
+
const files = await fs.readdir(workspaceRoot);
|
|
139
|
+
if (files.some((f) => f.endsWith('.pyx') || f.endsWith('.pxd') || f.endsWith('.c') || f.endsWith('.cpp'))) {
|
|
140
|
+
types.push('Cython/C-Extensions');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch { }
|
|
144
|
+
}
|
|
122
145
|
}
|
|
123
146
|
catch { }
|
|
124
147
|
}
|
|
@@ -10,6 +10,17 @@
|
|
|
10
10
|
* Only invoked when the `/sub-agents` toggle is ON and the Intent Router
|
|
11
11
|
* has already determined that context gathering is needed (SEARCH).
|
|
12
12
|
*/
|
|
13
|
+
/** Options for customizing investigation complexity evaluation. */
|
|
14
|
+
export interface InvestigationComplexityOptions {
|
|
15
|
+
/** Explicit primary sub-path focus override for the evaluation */
|
|
16
|
+
autoFocusSubPath?: string | null;
|
|
17
|
+
/** Explicit sub-path override if user explicitly targeted another scope */
|
|
18
|
+
subPathOverride?: string | null;
|
|
19
|
+
/** Flag to disable sub-path auto-focusing completely */
|
|
20
|
+
disableAutoFocus?: boolean;
|
|
21
|
+
/** Primary workspace root directory path */
|
|
22
|
+
primaryRoot?: string;
|
|
23
|
+
}
|
|
13
24
|
/** A single agent assignment grouping related investigation domains. */
|
|
14
25
|
export interface AgentAssignment {
|
|
15
26
|
/** Human-readable label for this agent (e.g., "Frontend", "Backend") */
|
|
@@ -21,25 +32,40 @@ export interface AgentAssignment {
|
|
|
21
32
|
export interface InvestigationComplexityResult {
|
|
22
33
|
/** Whether investigation should use a single agent or parallel agents */
|
|
23
34
|
strategy: 'SINGLE' | 'PARALLEL';
|
|
35
|
+
/** The evaluated investigation scope */
|
|
36
|
+
scope?: 'SUB_PATH' | 'FULL_WORKSPACE' | 'EXTERNAL_WORKSPACE';
|
|
24
37
|
/** All identified investigation domains (flat list) */
|
|
25
38
|
domains: string[];
|
|
26
|
-
/** Grouped domain assignments — one per agent to spawn */
|
|
39
|
+
/** Grouped domain assignments — one per agent to spawn (2–3 max) */
|
|
27
40
|
agentAssignments: AgentAssignment[];
|
|
28
|
-
/** Brief
|
|
41
|
+
/** Brief explanation of the routing decision */
|
|
29
42
|
reasoning: string;
|
|
43
|
+
/** The active primary sub-path auto-focus, if any */
|
|
44
|
+
focusedSubPath?: string | null;
|
|
45
|
+
/** The active sub-path or workspace override, if detected or provided */
|
|
46
|
+
subPathOverride?: string | null;
|
|
47
|
+
/** Whether the evaluation was constrained by active sub-path auto-focusing */
|
|
48
|
+
isAutoFocused?: boolean;
|
|
30
49
|
}
|
|
31
50
|
/**
|
|
32
|
-
*
|
|
51
|
+
* Checks whether the user's prompt contains an explicit path or workspace override
|
|
52
|
+
* that supersedes default primary sub-path auto-focusing.
|
|
33
53
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
|
|
54
|
+
* @param userRequest - The prompt text from the user
|
|
55
|
+
* @param activeSubPath - The currently active primary sub-path (if any)
|
|
56
|
+
* @returns The detected override identifier or null if no override exists
|
|
57
|
+
*/
|
|
58
|
+
export declare function detectSubPathOverride(userRequest: string, activeSubPath?: string | null): string | null;
|
|
59
|
+
/**
|
|
60
|
+
* Evaluates whether a user request warrants parallel investigation or a single agent.
|
|
61
|
+
* Respects default primary sub-path auto-focusing while allowing explicit overrides.
|
|
38
62
|
*
|
|
39
|
-
* @param userRequest - The user's prompt
|
|
40
|
-
* @param projectType - Detected project type (e.g., "Node.js / TypeScript
|
|
41
|
-
* @param approximateFileCount -
|
|
42
|
-
* @param chatHistory -
|
|
43
|
-
* @
|
|
63
|
+
* @param userRequest - The user's prompt/request
|
|
64
|
+
* @param projectType - Detected project type string (e.g., "Node.js / TypeScript")
|
|
65
|
+
* @param approximateFileCount - Approximate total file count in the project
|
|
66
|
+
* @param chatHistory - Formatted recent conversation history (optional)
|
|
67
|
+
* @param abortSignal - Optional signal to abort the LLM request
|
|
68
|
+
* @param options - Optional sub-path auto-focus and override configuration
|
|
69
|
+
* @returns Structured complexity result with strategy and domain assignments
|
|
44
70
|
*/
|
|
45
|
-
export declare function evaluateInvestigationComplexity(userRequest: string, projectType: string, approximateFileCount: number, chatHistory?: string, abortSignal?: AbortSignal): Promise<InvestigationComplexityResult>;
|
|
71
|
+
export declare function evaluateInvestigationComplexity(userRequest: string, projectType: string, approximateFileCount: number, chatHistory?: string, abortSignal?: AbortSignal, options?: InvestigationComplexityOptions): Promise<InvestigationComplexityResult>;
|