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
@@ -1,14 +1,34 @@
1
1
  import path from 'path';
2
2
  import { executeTool, getToolDeclarations as getBaseToolDeclarations } from '../agent-tools.js';
3
3
  import { MessageBus } from './messageBus.js';
4
+ import { resolveAndValidateMultiWorkspacePath } from '../../utils/pathSecurity.js';
4
5
  import { debugLog } from '../../utils/logger.js';
5
6
  /**
6
- * Extended tool declarations for sub-agents, merging the standard tools
7
- * with orchestration-specific ones (e.g., post_message, read_messages).
7
+ * Resolves a file path to its canonical absolute path for lock registry keying,
8
+ * accounting for multi-workspace alias resolution and default primary sub-path auto-focusing.
9
+ *
10
+ * @param workspaceRoot - The base primary workspace root directory
11
+ * @param filePath - The relative or aliased file path
12
+ * @param options - Optional sub-path override configuration
13
+ * @returns Absolute canonical file path
14
+ */
15
+ export function resolveCanonicalLockPath(workspaceRoot, filePath, options) {
16
+ try {
17
+ const resolved = resolveAndValidateMultiWorkspacePath(workspaceRoot, filePath, options);
18
+ return resolved.absolutePath;
19
+ }
20
+ catch {
21
+ return path.isAbsolute(filePath) ? filePath : path.resolve(workspaceRoot, filePath);
22
+ }
23
+ }
24
+ /**
25
+ * Returns Gemini tool declarations available to sub-agents during orchestration.
26
+ * In addition to standard agent tools (read/write/search/command/etc.), includes
27
+ * inter-agent communication tools (post_message, read_messages).
8
28
  */
9
29
  export function getScopedToolDeclarations() {
10
- return [
11
- ...getBaseToolDeclarations({ isExecutionAgent: true }),
30
+ const baseTools = getBaseToolDeclarations({ isExecutionAgent: true });
31
+ const orchestrationTools = [
12
32
  {
13
33
  name: 'post_message',
14
34
  description: 'Post a semantic message to the orchestration bus to coordinate with other agents.',
@@ -23,15 +43,15 @@ export function getScopedToolDeclarations() {
23
43
  type: 'STRING',
24
44
  description: 'The semantic intent or message content',
25
45
  },
26
- toAgent: {
27
- type: 'STRING',
28
- description: 'Optional target agent ID (required for "request")',
29
- },
30
46
  affectedFiles: {
31
47
  type: 'ARRAY',
32
48
  items: { type: 'STRING' },
33
49
  description: 'Files involved (required for "discovery")',
34
50
  },
51
+ toAgent: {
52
+ type: 'STRING',
53
+ description: 'Optional target agent ID (required for "request")',
54
+ },
35
55
  },
36
56
  required: ['type', 'content'],
37
57
  },
@@ -45,16 +65,23 @@ export function getScopedToolDeclarations() {
45
65
  },
46
66
  },
47
67
  ];
68
+ return [...baseTools, ...orchestrationTools];
48
69
  }
49
70
  /**
50
- * Wraps the global executeTool to provide sub-agent context.
71
+ * Executes a tool within the sub-agent execution boundary.
72
+ * Handles concurrency locking for file-mutating operations, sends progress
73
+ * notifications to the orchestrator, and logs activity to the message bus.
51
74
  *
52
- * 1. Captures tool execution into the MessageBus (Layer 1: zero-cost logs).
53
- * 2. Implements FileLocks for write/modify operations to prevent race conditions.
54
- * 3. Handles `post_message` and `read_messages` directly.
55
- * 4. Calls a heartbeat callback to notify the orchestrator this agent is alive.
75
+ * @param name - Tool function name
76
+ * @param args - Tool arguments object
77
+ * @param workspaceRoot - Primary workspace root directory
78
+ * @param agentId - Unique ID of the executing agent
79
+ * @param bus - Shared message bus instance
80
+ * @param locks - Shared file lock registry instance
81
+ * @param onProgress - Callback to notify parent of sub-agent progress
82
+ * @param options - Optional sub-path auto-focus and override configuration
56
83
  */
57
- export async function executeScopedTool(name, args, workspaceRoot, agentId, bus, locks, onProgress) {
84
+ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus, locks, onProgress, options) {
58
85
  // Update heartbeat so the orchestrator knows we are making progress
59
86
  onProgress();
60
87
  const timestamp = Date.now();
@@ -97,10 +124,25 @@ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus,
97
124
  let lockedFile = null;
98
125
  let diffContext = null;
99
126
  if (name === 'write_file' || name === 'modify_file' || name === 'delete_file') {
100
- lockedFile = args.filePath;
101
- if (lockedFile) {
102
- debugLog(`Agent "${agentId}" requesting lock for "${lockedFile}" (tool: ${name})`);
103
- onProgress(`waiting for lock on ${lockedFile.split('/').pop()}...`);
127
+ const rawPath = args.filePath;
128
+ if (rawPath) {
129
+ lockedFile = resolveCanonicalLockPath(workspaceRoot, rawPath, options);
130
+ debugLog(`Agent "${agentId}" requesting lock for "${lockedFile}" (raw: "${rawPath}", tool: ${name})`);
131
+ onProgress(`waiting for lock on ${path.basename(lockedFile)}...`);
132
+ const lockRes = await locks.acquire(lockedFile, agentId);
133
+ onProgress(`acquired lock, executing...`);
134
+ diffContext = lockRes.previousDiff;
135
+ if (lockRes.forceReleased) {
136
+ debugLog(`Agent "${agentId}" got forced lock on "${lockedFile}" (previous owner stalled)`);
137
+ }
138
+ }
139
+ }
140
+ else if (name === 'rename_file') {
141
+ const rawPath = args.sourcePath;
142
+ if (rawPath) {
143
+ lockedFile = resolveCanonicalLockPath(workspaceRoot, rawPath, options);
144
+ debugLog(`Agent "${agentId}" requesting lock for "${lockedFile}" (raw: "${rawPath}", tool: ${name})`);
145
+ onProgress(`waiting for lock on ${path.basename(lockedFile)}...`);
104
146
  const lockRes = await locks.acquire(lockedFile, agentId);
105
147
  onProgress(`acquired lock, executing...`);
106
148
  diffContext = lockRes.previousDiff;
@@ -0,0 +1,57 @@
1
+ export interface DiffExtractionOptions {
2
+ baseCommit?: string;
3
+ includeUntracked?: boolean;
4
+ maxBuffer?: number;
5
+ }
6
+ /**
7
+ * Checks whether the target directory contains a valid git repository.
8
+ */
9
+ export declare function isGitRepository(workspaceDir: string): Promise<boolean>;
10
+ /**
11
+ * Gets the current HEAD commit hash of the git repository.
12
+ */
13
+ export declare function getHeadCommit(workspaceDir: string): Promise<string>;
14
+ /**
15
+ * Resets the workspace git repository to a clean state matching baseCommit or HEAD.
16
+ */
17
+ export declare function resetWorkspaceRepo(workspaceDir: string, baseCommit?: string): Promise<void>;
18
+ /**
19
+ * Extracts a unified git patch representing all modifications made by the model.
20
+ * Produces standard SWE-bench format (patch starting with `diff --git` or empty string).
21
+ */
22
+ export declare function extractGitPatch(workspaceDir: string, options?: string | DiffExtractionOptions): Promise<string>;
23
+ /**
24
+ * Normalizes git unified diff patch text:
25
+ * - Trims superfluous leading/trailing whitespace
26
+ * - Ensures empty diff returns exact empty string ""
27
+ * - Preserves trailing newline if content exists
28
+ */
29
+ export declare function normalizePatch(patch: string): string;
30
+ /**
31
+ * Validates whether a patch conforms to standard unified diff structure.
32
+ */
33
+ export declare function validatePatchFormat(patch: string): {
34
+ valid: boolean;
35
+ issues?: string[];
36
+ };
37
+ /**
38
+ * Checks out a specific base commit in the repository.
39
+ */
40
+ export declare function checkoutBaseCommit(workspaceDir: string, baseCommit: string, options?: {
41
+ fetchIfMissing?: boolean;
42
+ }): Promise<{
43
+ success: boolean;
44
+ error?: string;
45
+ }>;
46
+ /**
47
+ * Clones a repository if it doesn't already exist or ensures it is checked out.
48
+ */
49
+ export declare function cloneOrEnsureRepo(repo: string, targetDir: string, options?: {
50
+ cloneUrl?: string;
51
+ baseCommit?: string;
52
+ shallow?: boolean;
53
+ }): Promise<{
54
+ success: boolean;
55
+ repoDir: string;
56
+ error?: string;
57
+ }>;
@@ -0,0 +1,209 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import * as fs from 'node:fs';
4
+ import * as path from 'node:path';
5
+ const execFileAsync = promisify(execFile);
6
+ /**
7
+ * Safely executes a git command in the target workspace directory.
8
+ */
9
+ async function runGit(args, cwd, maxBuffer = 20 * 1024 * 1024) {
10
+ try {
11
+ const { stdout, stderr } = await execFileAsync('git', args, {
12
+ cwd,
13
+ maxBuffer,
14
+ encoding: 'utf-8',
15
+ env: {
16
+ ...process.env,
17
+ GIT_PAGER: 'cat',
18
+ GIT_TERMINAL_PROMPT: '0',
19
+ },
20
+ });
21
+ return { stdout, stderr };
22
+ }
23
+ catch (err) {
24
+ const execErr = err;
25
+ throw new Error(`Git command failed: git ${args.join(' ')} (in ${cwd})\n${execErr.stderr || execErr.message}`);
26
+ }
27
+ }
28
+ /**
29
+ * Checks whether the target directory contains a valid git repository.
30
+ */
31
+ export async function isGitRepository(workspaceDir) {
32
+ try {
33
+ const gitDir = path.join(workspaceDir, '.git');
34
+ if (!fs.existsSync(gitDir)) {
35
+ return false;
36
+ }
37
+ const { stdout } = await runGit(['rev-parse', '--is-inside-work-tree'], workspaceDir);
38
+ return stdout.trim() === 'true';
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ /**
45
+ * Gets the current HEAD commit hash of the git repository.
46
+ */
47
+ export async function getHeadCommit(workspaceDir) {
48
+ const { stdout } = await runGit(['rev-parse', 'HEAD'], workspaceDir);
49
+ return stdout.trim();
50
+ }
51
+ /**
52
+ * Resets the workspace git repository to a clean state matching baseCommit or HEAD.
53
+ */
54
+ export async function resetWorkspaceRepo(workspaceDir, baseCommit) {
55
+ const isRepo = await isGitRepository(workspaceDir);
56
+ if (!isRepo) {
57
+ throw new Error(`Directory is not a git repository: ${workspaceDir}`);
58
+ }
59
+ if (baseCommit) {
60
+ await runGit(['checkout', baseCommit, '--force'], workspaceDir);
61
+ await runGit(['reset', '--hard', baseCommit], workspaceDir);
62
+ }
63
+ else {
64
+ await runGit(['reset', '--hard', 'HEAD'], workspaceDir);
65
+ }
66
+ // Clean untracked files and directories
67
+ await runGit(['clean', '-fdx'], workspaceDir);
68
+ }
69
+ /**
70
+ * Extracts a unified git patch representing all modifications made by the model.
71
+ * Produces standard SWE-bench format (patch starting with `diff --git` or empty string).
72
+ */
73
+ export async function extractGitPatch(workspaceDir, options = {}) {
74
+ const opts = typeof options === 'string' ? { baseCommit: options } : options;
75
+ const isRepo = await isGitRepository(workspaceDir);
76
+ if (!isRepo) {
77
+ throw new Error(`Cannot extract patch: Directory is not a git repository (${workspaceDir})`);
78
+ }
79
+ const maxBuffer = opts.maxBuffer ?? 20 * 1024 * 1024;
80
+ // Include untracked files in the git index as intent-to-add so git diff includes newly created files
81
+ if (opts.includeUntracked !== false) {
82
+ try {
83
+ await runGit(['add', '-N', '.'], workspaceDir, maxBuffer);
84
+ }
85
+ catch {
86
+ // Intent-to-add might fail on locked files or non-standard repos; continue anyway
87
+ }
88
+ }
89
+ let patch = '';
90
+ if (opts.baseCommit) {
91
+ try {
92
+ const { stdout } = await runGit(['diff', '--binary', opts.baseCommit, '--'], workspaceDir, maxBuffer);
93
+ patch = stdout;
94
+ }
95
+ catch {
96
+ // Fallback without --binary
97
+ const { stdout } = await runGit(['diff', opts.baseCommit, '--'], workspaceDir, maxBuffer);
98
+ patch = stdout;
99
+ }
100
+ }
101
+ else {
102
+ // If no base commit is specified, diff against HEAD
103
+ const { stdout } = await runGit(['diff', '--binary', 'HEAD', '--'], workspaceDir, maxBuffer);
104
+ patch = stdout;
105
+ }
106
+ return normalizePatch(patch);
107
+ }
108
+ /**
109
+ * Normalizes git unified diff patch text:
110
+ * - Trims superfluous leading/trailing whitespace
111
+ * - Ensures empty diff returns exact empty string ""
112
+ * - Preserves trailing newline if content exists
113
+ */
114
+ export function normalizePatch(patch) {
115
+ if (!patch || patch.trim().length === 0) {
116
+ return '';
117
+ }
118
+ const trimmed = patch.trimEnd();
119
+ if (trimmed.length === 0) {
120
+ return '';
121
+ }
122
+ return trimmed + '\n';
123
+ }
124
+ /**
125
+ * Validates whether a patch conforms to standard unified diff structure.
126
+ */
127
+ export function validatePatchFormat(patch) {
128
+ if (!patch || patch.trim().length === 0) {
129
+ return { valid: true };
130
+ }
131
+ const issues = [];
132
+ const hasDiffHeader = /^diff --git a\/.+ b\/.+$/m.test(patch);
133
+ const hasFileHeader = /^--- (a\/.+|\/dev\/null)$/m.test(patch) && /^\+\+\+ (b\/.+|\/dev\/null)$/m.test(patch);
134
+ const hasHunkHeader = /^@@ -\d+(,\d+)? \+\d+(,\d+)? @@/m.test(patch);
135
+ if (!hasDiffHeader) {
136
+ issues.push('Missing "diff --git a/... b/..." header');
137
+ }
138
+ if (!hasFileHeader) {
139
+ issues.push('Missing "--- a/..." and "+++ b/..." unified diff headers');
140
+ }
141
+ if (!hasHunkHeader) {
142
+ issues.push('Missing "@@ ... @@" hunk header');
143
+ }
144
+ return {
145
+ valid: issues.length === 0,
146
+ issues: issues.length > 0 ? issues : undefined,
147
+ };
148
+ }
149
+ /**
150
+ * Checks out a specific base commit in the repository.
151
+ */
152
+ export async function checkoutBaseCommit(workspaceDir, baseCommit, options) {
153
+ const isRepo = await isGitRepository(workspaceDir);
154
+ if (!isRepo) {
155
+ return { success: false, error: `Directory ${workspaceDir} is not a valid git repository.` };
156
+ }
157
+ try {
158
+ await runGit(['checkout', '-f', baseCommit], workspaceDir);
159
+ return { success: true };
160
+ }
161
+ catch (err) {
162
+ if (options?.fetchIfMissing) {
163
+ try {
164
+ await runGit(['fetch', 'origin', baseCommit], workspaceDir);
165
+ await runGit(['checkout', '-f', baseCommit], workspaceDir);
166
+ return { success: true };
167
+ }
168
+ catch (fetchErr) {
169
+ return { success: false, error: `Failed to checkout base commit ${baseCommit}: ${fetchErr?.message || err?.message}` };
170
+ }
171
+ }
172
+ return { success: false, error: `Failed to checkout base commit ${baseCommit}: ${err?.message || String(err)}` };
173
+ }
174
+ }
175
+ /**
176
+ * Clones a repository if it doesn't already exist or ensures it is checked out.
177
+ */
178
+ export async function cloneOrEnsureRepo(repo, targetDir, options) {
179
+ const isRepo = await isGitRepository(targetDir);
180
+ if (isRepo) {
181
+ if (options?.baseCommit) {
182
+ const checkoutRes = await checkoutBaseCommit(targetDir, options.baseCommit, { fetchIfMissing: true });
183
+ if (!checkoutRes.success) {
184
+ return { success: false, repoDir: targetDir, error: checkoutRes.error };
185
+ }
186
+ }
187
+ return { success: true, repoDir: targetDir };
188
+ }
189
+ try {
190
+ await fs.promises.mkdir(path.dirname(targetDir), { recursive: true });
191
+ const url = options?.cloneUrl || `https://github.com/${repo}.git`;
192
+ const cloneArgs = ['clone'];
193
+ if (options?.shallow) {
194
+ cloneArgs.push('--depth', '1');
195
+ }
196
+ cloneArgs.push(url, targetDir);
197
+ await runGit(cloneArgs, path.dirname(targetDir));
198
+ if (options?.baseCommit) {
199
+ const checkoutRes = await checkoutBaseCommit(targetDir, options.baseCommit, { fetchIfMissing: true });
200
+ if (!checkoutRes.success) {
201
+ return { success: false, repoDir: targetDir, error: checkoutRes.error };
202
+ }
203
+ }
204
+ return { success: true, repoDir: targetDir };
205
+ }
206
+ catch (err) {
207
+ return { success: false, repoDir: targetDir, error: `Failed to clone repository ${repo}: ${err?.message || String(err)}` };
208
+ }
209
+ }
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export * from './instanceLoader.js';
3
+ export * from './gitDiffExtractor.js';
4
+ export * from './sweBenchRunnerService.js';
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export * from './instanceLoader.js';
3
+ export * from './gitDiffExtractor.js';
4
+ export * from './sweBenchRunnerService.js';
@@ -0,0 +1,21 @@
1
+ import { SWEBenchInstance, SWEBenchPrediction, InstanceFilterOptions } from './types.js';
2
+ /**
3
+ * Loads SWE-bench instances from a JSON or JSONL file.
4
+ */
5
+ export declare function loadInstancesFromFile(filePath: string, filterOptions?: InstanceFilterOptions): Promise<SWEBenchInstance[]>;
6
+ /**
7
+ * Applies filtering, pagination, and deterministic shuffling to an array of instances.
8
+ */
9
+ export declare function filterInstances(instances: SWEBenchInstance[], options?: InstanceFilterOptions): SWEBenchInstance[];
10
+ /**
11
+ * Parses a SWE-bench predictions JSONL file into an array of predictions.
12
+ */
13
+ export declare function parsePredictions(predictionsPath: string): Promise<SWEBenchPrediction[]>;
14
+ /**
15
+ * Saves a list of SWE-bench predictions to a JSONL file.
16
+ */
17
+ export declare function savePredictions(predictions: SWEBenchPrediction[], outputPath: string): Promise<void>;
18
+ /**
19
+ * Appends a single prediction object to a JSONL file immediately.
20
+ */
21
+ export declare function appendPrediction(prediction: SWEBenchPrediction, outputPath: string): Promise<void>;
@@ -0,0 +1,171 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as readline from 'node:readline';
4
+ /**
5
+ * Loads SWE-bench instances from a JSON or JSONL file.
6
+ */
7
+ export async function loadInstancesFromFile(filePath, filterOptions = {}) {
8
+ const resolvedPath = path.resolve(filePath);
9
+ if (!fs.existsSync(resolvedPath)) {
10
+ throw new Error(`Failed to load instances: file not found at ${resolvedPath}`);
11
+ }
12
+ const ext = path.extname(resolvedPath).toLowerCase();
13
+ let instances = [];
14
+ try {
15
+ if (ext === '.jsonl') {
16
+ instances = await loadJsonlInstances(resolvedPath);
17
+ }
18
+ else {
19
+ instances = await loadJsonInstances(resolvedPath);
20
+ }
21
+ }
22
+ catch (err) {
23
+ throw new Error(`Failed to load instances from ${resolvedPath}: ${err?.message || String(err)}`);
24
+ }
25
+ return filterInstances(instances, filterOptions);
26
+ }
27
+ /**
28
+ * Loads instances from a JSON file.
29
+ */
30
+ async function loadJsonInstances(filePath) {
31
+ const content = await fs.promises.readFile(filePath, 'utf-8');
32
+ const parsed = JSON.parse(content);
33
+ if (Array.isArray(parsed)) {
34
+ return parsed;
35
+ }
36
+ if (parsed && typeof parsed === 'object') {
37
+ if (Array.isArray(parsed.instances)) {
38
+ return parsed.instances;
39
+ }
40
+ if (Array.isArray(parsed.data)) {
41
+ return parsed.data;
42
+ }
43
+ }
44
+ throw new Error(`Unrecognized JSON format in ${filePath}. Expected an array or { instances: [...] }`);
45
+ }
46
+ /**
47
+ * Loads instances from a JSONL file line-by-line.
48
+ */
49
+ async function loadJsonlInstances(filePath) {
50
+ const instances = [];
51
+ const fileStream = fs.createReadStream(filePath, { encoding: 'utf-8' });
52
+ const rl = readline.createInterface({
53
+ input: fileStream,
54
+ crlfDelay: Infinity,
55
+ });
56
+ let lineNum = 0;
57
+ for await (const line of rl) {
58
+ lineNum++;
59
+ const trimmed = line.trim();
60
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('//')) {
61
+ continue;
62
+ }
63
+ try {
64
+ const parsed = JSON.parse(trimmed);
65
+ if (parsed && typeof parsed === 'object' && parsed.instance_id) {
66
+ instances.push(parsed);
67
+ }
68
+ }
69
+ catch (err) {
70
+ const msg = err instanceof Error ? err.message : String(err);
71
+ throw new Error(`Failed to parse line ${lineNum} in JSONL ${filePath}: ${msg}`);
72
+ }
73
+ }
74
+ return instances;
75
+ }
76
+ /**
77
+ * Applies filtering, pagination, and deterministic shuffling to an array of instances.
78
+ */
79
+ export function filterInstances(instances, options = {}) {
80
+ let result = [...instances];
81
+ if (options.repo) {
82
+ const targetRepo = options.repo.toLowerCase();
83
+ result = result.filter((inst) => inst.repo && inst.repo.toLowerCase().includes(targetRepo));
84
+ }
85
+ if (options.instanceIds && options.instanceIds.length > 0) {
86
+ const idSet = new Set(options.instanceIds);
87
+ result = result.filter((inst) => idSet.has(inst.instance_id));
88
+ }
89
+ if (options.shuffle) {
90
+ result = pseudoShuffle(result, options.seed ?? 42);
91
+ }
92
+ if (typeof options.offset === 'number' && options.offset > 0) {
93
+ result = result.slice(options.offset);
94
+ }
95
+ if (typeof options.limit === 'number' && options.limit > 0) {
96
+ result = result.slice(0, options.limit);
97
+ }
98
+ return result;
99
+ }
100
+ /**
101
+ * Deterministic pseudo-random shuffle (Fisher-Yates) using a simple LCG algorithm.
102
+ */
103
+ function pseudoShuffle(items, seed) {
104
+ const array = [...items];
105
+ let currentSeed = seed;
106
+ const random = () => {
107
+ currentSeed = (currentSeed * 9301 + 49297) % 233280;
108
+ return currentSeed / 233280;
109
+ };
110
+ for (let i = array.length - 1; i > 0; i--) {
111
+ const j = Math.floor(random() * (i + 1));
112
+ const temp = array[i];
113
+ array[i] = array[j];
114
+ array[j] = temp;
115
+ }
116
+ return array;
117
+ }
118
+ /**
119
+ * Parses a SWE-bench predictions JSONL file into an array of predictions.
120
+ */
121
+ export async function parsePredictions(predictionsPath) {
122
+ const resolvedPath = path.resolve(predictionsPath);
123
+ if (!fs.existsSync(resolvedPath)) {
124
+ return [];
125
+ }
126
+ const predictions = [];
127
+ const fileStream = fs.createReadStream(resolvedPath, { encoding: 'utf-8' });
128
+ const rl = readline.createInterface({
129
+ input: fileStream,
130
+ crlfDelay: Infinity,
131
+ });
132
+ for await (const line of rl) {
133
+ const trimmed = line.trim();
134
+ if (!trimmed)
135
+ continue;
136
+ try {
137
+ const parsed = JSON.parse(trimmed);
138
+ if (parsed && parsed.instance_id) {
139
+ predictions.push(parsed);
140
+ }
141
+ }
142
+ catch {
143
+ // Skip malformed individual prediction lines
144
+ }
145
+ }
146
+ return predictions;
147
+ }
148
+ /**
149
+ * Saves a list of SWE-bench predictions to a JSONL file.
150
+ */
151
+ export async function savePredictions(predictions, outputPath) {
152
+ const resolvedPath = path.resolve(outputPath);
153
+ const dir = path.dirname(resolvedPath);
154
+ if (!fs.existsSync(dir)) {
155
+ await fs.promises.mkdir(dir, { recursive: true });
156
+ }
157
+ const lines = predictions.map((pred) => JSON.stringify(pred)).join('\n') + (predictions.length > 0 ? '\n' : '');
158
+ await fs.promises.writeFile(resolvedPath, lines, 'utf-8');
159
+ }
160
+ /**
161
+ * Appends a single prediction object to a JSONL file immediately.
162
+ */
163
+ export async function appendPrediction(prediction, outputPath) {
164
+ const resolvedPath = path.resolve(outputPath);
165
+ const dir = path.dirname(resolvedPath);
166
+ if (!fs.existsSync(dir)) {
167
+ await fs.promises.mkdir(dir, { recursive: true });
168
+ }
169
+ const line = JSON.stringify(prediction) + '\n';
170
+ await fs.promises.appendFile(resolvedPath, line, 'utf-8');
171
+ }
@@ -0,0 +1,38 @@
1
+ import { SWEBenchInstance, SWEBenchInstanceResult, SWEBenchEvaluationOptions, SWEBenchEvaluationSummary, SWEBenchEvaluationMetrics, RunInstanceOptions } from './types.js';
2
+ export declare const DEFAULT_MODEL_NAME = "minovative-mind-agent";
3
+ export declare const DEFAULT_TIMEOUT_MS: number;
4
+ /**
5
+ * Formats a SWE-bench instance into a comprehensive problem prompt for the agent.
6
+ */
7
+ export declare function buildInstancePrompt(instance: SWEBenchInstance): string;
8
+ /**
9
+ * Resolves the workspace directory for an instance based on configuration.
10
+ */
11
+ export declare function resolveInstanceWorkspaceDir(instance: SWEBenchInstance, options?: {
12
+ workspacesBaseDir?: string;
13
+ workspaceDirMap?: Record<string, string>;
14
+ defaultDir?: string;
15
+ autoCloneRepos?: boolean;
16
+ }): string;
17
+ /**
18
+ * Executes the mmcli agent loop on a single SWE-bench instance and extracts the resulting patch.
19
+ */
20
+ export declare function runSWEBenchInstance(instance: SWEBenchInstance, options?: RunInstanceOptions): Promise<SWEBenchInstanceResult>;
21
+ /**
22
+ * Runs evaluation across a suite of SWE-bench instances.
23
+ */
24
+ export declare function runSWEBenchEvaluation(options: SWEBenchEvaluationOptions): Promise<SWEBenchEvaluationSummary>;
25
+ /**
26
+ * Computes aggregate metrics across SWE-bench evaluation results including token usage,
27
+ * repository pass rates, diff sizes, and timing statistics.
28
+ */
29
+ export declare function calculateEvaluationMetrics(results: SWEBenchInstanceResult[], totalDurationMs: number, modelName?: string): SWEBenchEvaluationMetrics;
30
+ /**
31
+ * Saves a structured, public-ready evaluation report JSON file containing summary statistics,
32
+ * token economics, repository breakdown, and individual instance outcomes.
33
+ */
34
+ export declare function saveEvaluationReport(summary: SWEBenchEvaluationSummary, reportPath: string): Promise<void>;
35
+ /**
36
+ * Formats a clean, high-impact ASCII terminal summary dashboard for SWE-bench evaluation runs.
37
+ */
38
+ export declare function formatEvaluationReportSummary(summary: SWEBenchEvaluationSummary): string;