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.
Files changed (39) hide show
  1. package/README.md +21 -7
  2. package/dist/commands/eval.d.ts +22 -0
  3. package/dist/commands/eval.js +141 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +1 -0
  6. package/dist/services/agent/toolLoop.d.ts +4 -0
  7. package/dist/services/agent/toolLoop.js +61 -10
  8. package/dist/services/agent-tools.d.ts +5 -5
  9. package/dist/services/agent-tools.js +147 -9
  10. package/dist/services/ai.d.ts +1 -1
  11. package/dist/services/ai.js +40 -7
  12. package/dist/services/contextAgent.d.ts +1 -0
  13. package/dist/services/contextAgent.js +29 -6
  14. package/dist/services/investigationComplexity.d.ts +39 -13
  15. package/dist/services/investigationComplexity.js +325 -46
  16. package/dist/services/metrics.d.ts +10 -0
  17. package/dist/services/metrics.js +24 -0
  18. package/dist/services/orchestration/scopedTools.d.ts +27 -50
  19. package/dist/services/orchestration/scopedTools.js +60 -18
  20. package/dist/services/swebench/gitDiffExtractor.d.ts +57 -0
  21. package/dist/services/swebench/gitDiffExtractor.js +209 -0
  22. package/dist/services/swebench/index.d.ts +4 -0
  23. package/dist/services/swebench/index.js +4 -0
  24. package/dist/services/swebench/instanceLoader.d.ts +21 -0
  25. package/dist/services/swebench/instanceLoader.js +171 -0
  26. package/dist/services/swebench/sweBenchRunnerService.d.ts +38 -0
  27. package/dist/services/swebench/sweBenchRunnerService.js +618 -0
  28. package/dist/services/swebench/types.d.ts +167 -0
  29. package/dist/services/swebench/types.js +7 -0
  30. package/dist/services/verificationService.js +3 -0
  31. package/dist/services/workspaceRegistry.d.ts +81 -8
  32. package/dist/services/workspaceRegistry.js +222 -34
  33. package/dist/utils/analysisRunner.js +2 -1
  34. package/dist/utils/pathSecurity.d.ts +56 -14
  35. package/dist/utils/pathSecurity.js +120 -39
  36. package/dist/utils/systemPrompts.d.ts +6 -6
  37. package/dist/utils/systemPrompts.js +46 -25
  38. package/oclif.manifest.json +137 -1
  39. package/package.json +1 -1
@@ -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 (additionalText) {
335
- newParts.push({ text: truncatePartText(additionalText) });
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
- abortSignal);
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
- abortSignal);
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')) || (await fileExists('requirements.txt')) || (await fileExists('Pipfile'))) {
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 justification from the PM for its decision */
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
- * Evaluates whether the investigation phase should be parallelized.
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
- * Uses `gemini-3.7-flash` (under auto mode) at temperature 0 to classify the prompt's
35
- * investigation complexity. The PM dynamically identifies domains and groups
36
- * them into agent assignments. Agent count = `agentAssignments.length`, which
37
- * may be fewer than `domains.length` when related domains are batched together.
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 / React").
41
- * @param approximateFileCount - Rough file count from the project tree.
42
- * @param chatHistory - Recent conversation history for context.
43
- * @returns The complexity classification with domain decomposition.
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>;