minovative-mind-cli 2.6.4 → 2.8.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 +3 -3
- package/dist/services/agent/slashCommands.js +12 -5
- package/dist/services/agent/toolLoop.js +52 -2
- package/dist/services/agent-tools.d.ts +26 -0
- package/dist/services/agent-tools.js +255 -0
- package/dist/services/orchestration/messageBus.d.ts +1 -0
- package/dist/services/orchestration/messageBus.js +5 -2
- package/dist/services/orchestration/scopedTools.js +26 -0
- package/dist/services/orchestration/subAgent.js +1 -1
- package/dist/services/verificationService.d.ts +42 -0
- package/dist/services/verificationService.js +235 -0
- package/dist/utils/analysisRunner.d.ts +85 -0
- package/dist/utils/analysisRunner.js +675 -0
- package/dist/utils/atomicWrite.js +2 -1
- package/dist/utils/systemPrompts.d.ts +1 -1
- package/dist/utils/systemPrompts.js +6 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ and hope for the best.
|
|
|
7
7
|
|
|
8
8
|
This CLI does the opposite — it uses a custom built agentic system called,
|
|
9
9
|
**Precision-Context Verification (PCV)** engine to feed lightweight Flash models
|
|
10
|
-
exactly the right context, execute code, verify
|
|
10
|
+
exactly the right context, execute code, verify compilation, execute Property-Based Testing (PBT) suites and diff-scoped mutation audits, and self-correct (if it even needs to), until
|
|
11
11
|
the build/performance metrics are green.
|
|
12
12
|
|
|
13
13
|
> The result: **Genuine Pro reasoning accuracy at Flash-level speed and efficiency.**
|
|
@@ -142,7 +142,7 @@ By prefixing file paths with `@alias/` (e.g., `@backend/src/api.ts` and `@fronte
|
|
|
142
142
|
|
|
143
143
|
Minovative Mind CLI fundamentally supports **ALL programming languages** for chat, code generation, planning, and execution, as it relies on Gemini's vast training data.
|
|
144
144
|
|
|
145
|
-
However, the PCV engine features deep, context-aware analysis across **12 major programming language families**. Our core advanced engines—**Smart Dependency Tracing**, **Performance Auditing** (across 9 language families), and **Ephemeral Analysis Scripts**—provide tailored support depending on the language's syntax and runtime model:
|
|
145
|
+
However, the PCV engine features deep, context-aware analysis across **12 major programming language families**. Our core advanced engines—**Smart Dependency Tracing**, **Property-Based & Mutation Verification**, **Performance Auditing** (across 9 language families), and **Ephemeral Analysis Scripts**—provide tailored support depending on the language's syntax and runtime model:
|
|
146
146
|
|
|
147
147
|
| Language Family | Supported Extensions | Dependency Tracing | Performance Auditing | Ephemeral Analysis |
|
|
148
148
|
| :--------------------------- | :------------------------------------------- | :----------------: | :------------------: | :----------------: |
|
|
@@ -165,7 +165,7 @@ _⚠️\*Support is limited or requires custom local system tooling/environment
|
|
|
165
165
|
|
|
166
166
|
1. **Dependency Tracing:** Fully supported across language families. It uses lightning-fast static regex pattern matching to resolve local import structures and determine the "blast radius" of code changes without requiring local compilations.
|
|
167
167
|
2. **Performance Auditing:** Executed natively across 9 major language families (JavaScript/TypeScript, Python, Go, Rust, PHP, C#, Java, C/C++, and Ruby). It uses zero-dependency, ultra-fast (<50ms) language-aware regex heuristics with comment/string stripping and line-preserving offset tracking to detect severe runtime anti-patterns (such as unbounded loops, synchronous I/O, chained array allocations, unnecessary allocations, and unclosed resources).
|
|
168
|
-
3. **Ephemeral Analysis
|
|
168
|
+
3. **Ephemeral Analysis & Property-Based Probing:** Defaults to Node.js as a safe baseline, but natively leverages host-installed runtimes (Python, Rust, Go compilers) and standard libraries whenever available in the workspace.
|
|
169
169
|
|
|
170
170
|
---
|
|
171
171
|
|
|
@@ -223,20 +223,27 @@ export async function handleSlashCommand(command, context) {
|
|
|
223
223
|
try {
|
|
224
224
|
const { getInvestigationCacheStats } = await import('../orchestration/investigationCache.js');
|
|
225
225
|
const invStats = getInvestigationCacheStats(workspaceRoot);
|
|
226
|
-
const
|
|
227
|
-
|
|
226
|
+
const formatBytesToSize = (bytes) => {
|
|
227
|
+
if (bytes >= 1024 * 1024) {
|
|
228
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
229
|
+
}
|
|
230
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
231
|
+
};
|
|
232
|
+
const memBankSizeStr = formatBytesToSize(invStats.sizeBytes);
|
|
233
|
+
console.log(`${pc.bold('Memory Bank:')} ${pc.cyan(`${invStats.entries} cached investigations (${memBankSizeStr} / 5 MB)`)}`);
|
|
228
234
|
const { readCache } = await import('../../utils/projectStorage.js');
|
|
229
235
|
const contextCache = readCache(workspaceRoot, 'context_cache.json');
|
|
230
236
|
const contextEntries = contextCache ? Object.keys(contextCache).length : 0;
|
|
231
|
-
let
|
|
237
|
+
let contextSizeBytes = 0;
|
|
232
238
|
try {
|
|
233
239
|
const fs = await import('node:fs');
|
|
234
240
|
const path = await import('node:path');
|
|
235
241
|
const stat = await fs.promises.stat(path.join(workspaceRoot, '.minovativemind', 'context_cache.json'));
|
|
236
|
-
|
|
242
|
+
contextSizeBytes = stat.size;
|
|
237
243
|
}
|
|
238
244
|
catch (e) { }
|
|
239
|
-
|
|
245
|
+
const contextSizeStr = formatBytesToSize(contextSizeBytes);
|
|
246
|
+
console.log(`${pc.bold('Context Cache:')} ${pc.cyan(`${contextEntries} file summaries (${contextSizeStr} / 5 MB)`)}`);
|
|
240
247
|
}
|
|
241
248
|
catch (e) {
|
|
242
249
|
// Ignored if cache stats fail
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'path';
|
|
1
2
|
import * as p from '@clack/prompts';
|
|
2
3
|
import pc from 'picocolors';
|
|
3
4
|
import { debugLog } from '../../utils/logger.js';
|
|
@@ -24,6 +25,9 @@ const TOOL_ICONS = {
|
|
|
24
25
|
find_dependencies: '🔗',
|
|
25
26
|
run_analysis_script: '🔬',
|
|
26
27
|
run_debug_script: '🧪',
|
|
28
|
+
run_fuzz_probe: '🎯',
|
|
29
|
+
check_heap_delta: '📊',
|
|
30
|
+
check_behavioral_drift: '🔀',
|
|
27
31
|
};
|
|
28
32
|
/**
|
|
29
33
|
* Human-readable translations for tool actions.
|
|
@@ -41,6 +45,9 @@ const TOOL_LABELS = {
|
|
|
41
45
|
find_dependencies: 'Tracing dependencies',
|
|
42
46
|
run_analysis_script: 'Analyzing workspace',
|
|
43
47
|
run_debug_script: 'Executing debug/validation script',
|
|
48
|
+
run_fuzz_probe: 'Executing fuzz testing probe',
|
|
49
|
+
check_heap_delta: 'Inspecting memory heap delta',
|
|
50
|
+
check_behavioral_drift: 'Comparing behavioral drift',
|
|
44
51
|
};
|
|
45
52
|
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
46
53
|
/**
|
|
@@ -52,8 +59,20 @@ const TOOL_LABELS = {
|
|
|
52
59
|
* @returns A fully colorized and formatted ANSI console string.
|
|
53
60
|
*/
|
|
54
61
|
export function formatToolCall(name, args) {
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
let icon = TOOL_ICONS[name] ?? '🔧';
|
|
63
|
+
let label = TOOL_LABELS[name] ?? name;
|
|
64
|
+
if (name === 'rename_file') {
|
|
65
|
+
const src = String(args.sourcePath ?? '');
|
|
66
|
+
const tgt = String(args.targetPath ?? '');
|
|
67
|
+
if (src && tgt && path.dirname(src) === path.dirname(tgt)) {
|
|
68
|
+
label = 'Renaming file';
|
|
69
|
+
icon = '🏷️';
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
label = 'Moving file';
|
|
73
|
+
icon = '🚚';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
57
76
|
const formatPath = (path) => pc.cyan(path);
|
|
58
77
|
const argsMap = {
|
|
59
78
|
read_file: () => {
|
|
@@ -86,6 +105,37 @@ export function formatToolCall(name, args) {
|
|
|
86
105
|
find_dependencies: () => `: ${formatPath(String(args.filePath))}${args.direction ? ` (${args.direction})` : ''}`,
|
|
87
106
|
run_analysis_script: () => `: ${formatPath(String(args.targetFile ?? 'workspace'))}`,
|
|
88
107
|
run_debug_script: () => `: ${pc.dim(`(${String(args.language ?? 'script')})`)}`,
|
|
108
|
+
run_fuzz_probe: () => {
|
|
109
|
+
const lang = String(args.language ?? 'node');
|
|
110
|
+
const extras = [];
|
|
111
|
+
if (args.iterations !== undefined)
|
|
112
|
+
extras.push(`${args.iterations} iterations`);
|
|
113
|
+
if (args.seed !== undefined)
|
|
114
|
+
extras.push(`seed: ${args.seed}`);
|
|
115
|
+
const meta = extras.length > 0 ? `, ${extras.join(', ')}` : '';
|
|
116
|
+
return `: ${pc.dim(`(${lang}${meta})`)}`;
|
|
117
|
+
},
|
|
118
|
+
check_heap_delta: () => {
|
|
119
|
+
const lang = String(args.language ?? 'node');
|
|
120
|
+
const extras = [];
|
|
121
|
+
if (args.testIterations !== undefined)
|
|
122
|
+
extras.push(`${args.testIterations} iterations`);
|
|
123
|
+
if (args.samplingIntervalMs !== undefined)
|
|
124
|
+
extras.push(`${args.samplingIntervalMs}ms interval`);
|
|
125
|
+
const meta = extras.length > 0 ? `, ${extras.join(', ')}` : '';
|
|
126
|
+
return `: ${pc.dim(`(${lang}${meta})`)}`;
|
|
127
|
+
},
|
|
128
|
+
check_behavioral_drift: () => {
|
|
129
|
+
const lang = String(args.language ?? 'node');
|
|
130
|
+
const extras = [];
|
|
131
|
+
if (args.tolerance !== undefined)
|
|
132
|
+
extras.push(`tol: ${args.tolerance}`);
|
|
133
|
+
if (Array.isArray(args.compareKeys) && args.compareKeys.length > 0) {
|
|
134
|
+
extras.push(`keys: ${args.compareKeys.join(', ')}`);
|
|
135
|
+
}
|
|
136
|
+
const meta = extras.length > 0 ? `, ${extras.join(', ')}` : '';
|
|
137
|
+
return `: ${pc.dim(`(${lang}${meta})`)}`;
|
|
138
|
+
},
|
|
89
139
|
};
|
|
90
140
|
const formatter = argsMap[name];
|
|
91
141
|
const details = formatter ? formatter() : '';
|
|
@@ -183,6 +183,32 @@ export declare function findRecentChanges(workspaceRoot: string, dirPath?: strin
|
|
|
183
183
|
* @returns A promise resolving to a {@link ToolResult} containing execution output.
|
|
184
184
|
*/
|
|
185
185
|
export declare function runDebugScript(workspaceRoot: string, language: string, code: string, abortSignal?: AbortSignal): Promise<ToolResult>;
|
|
186
|
+
/**
|
|
187
|
+
* Executes a fuzz probing analysis script and formats the resulting pass/fail and crash statistics.
|
|
188
|
+
*/
|
|
189
|
+
export declare function executeFuzzProbe(workspaceRoot: string, code: string, language?: string, config?: {
|
|
190
|
+
iterations?: number;
|
|
191
|
+
maxInputLength?: number;
|
|
192
|
+
seed?: number;
|
|
193
|
+
timeoutMs?: number;
|
|
194
|
+
}, abortSignal?: AbortSignal): Promise<ToolResult>;
|
|
195
|
+
/**
|
|
196
|
+
* Executes a heap memory inspection script and formats memory consumption and growth delta metrics.
|
|
197
|
+
*/
|
|
198
|
+
export declare function executeHeapDelta(workspaceRoot: string, code: string, language?: string, config?: {
|
|
199
|
+
warmupIterations?: number;
|
|
200
|
+
testIterations?: number;
|
|
201
|
+
samplingIntervalMs?: number;
|
|
202
|
+
timeoutMs?: number;
|
|
203
|
+
}, abortSignal?: AbortSignal): Promise<ToolResult>;
|
|
204
|
+
/**
|
|
205
|
+
* Executes baseline and candidate scripts, returning output diffs and behavioral drift findings.
|
|
206
|
+
*/
|
|
207
|
+
export declare function executeBehavioralDrift(workspaceRoot: string, baselineCode: string, candidateCode: string, language?: string, config?: {
|
|
208
|
+
tolerance?: number;
|
|
209
|
+
compareKeys?: string[];
|
|
210
|
+
timeoutMs?: number;
|
|
211
|
+
}, abortSignal?: AbortSignal): Promise<ToolResult>;
|
|
186
212
|
/**
|
|
187
213
|
* Central tool dispatcher that resolves multi-workspace paths, executes the requested tool
|
|
188
214
|
* with the provided arguments, records metrics, and returns the standardized {@link ToolResult}.
|
|
@@ -24,6 +24,7 @@ import { extractSymbols } from '../utils/symbolExtractor.js';
|
|
|
24
24
|
import { getMetricCollector } from './metrics.js';
|
|
25
25
|
import { getCurrentAgentId } from '../utils/asyncContext.js';
|
|
26
26
|
import { recordFileRead, hasAgentReadFile } from '../utils/fileReadGuard.js';
|
|
27
|
+
import { runFuzzProbe, checkHeapDelta as runCheckHeapDelta, checkBehavioralDrift as runCheckBehavioralDrift, } from '../utils/analysisRunner.js';
|
|
27
28
|
import ignore from 'ignore';
|
|
28
29
|
const execAsync = promisify(exec);
|
|
29
30
|
// ─── Tool Declarations for Gemini Function Calling ───────────────────
|
|
@@ -341,6 +342,109 @@ export const toolDeclarations = [
|
|
|
341
342
|
required: ['summary'],
|
|
342
343
|
},
|
|
343
344
|
},
|
|
345
|
+
{
|
|
346
|
+
name: 'run_fuzz_probe',
|
|
347
|
+
description: 'Execute a fuzz testing probe script to detect edge-case failures, unhandled exceptions, or unexpected crashes under randomized or boundary inputs.',
|
|
348
|
+
parameters: {
|
|
349
|
+
type: SchemaType.OBJECT,
|
|
350
|
+
properties: {
|
|
351
|
+
code: {
|
|
352
|
+
type: SchemaType.STRING,
|
|
353
|
+
description: 'The fuzz test script or probe code to execute.',
|
|
354
|
+
},
|
|
355
|
+
language: {
|
|
356
|
+
type: SchemaType.STRING,
|
|
357
|
+
description: 'Runtime environment: "node", "ts-node", "python", "bash", "go", or "rust". Defaults to "node".',
|
|
358
|
+
},
|
|
359
|
+
iterations: {
|
|
360
|
+
type: SchemaType.NUMBER,
|
|
361
|
+
description: 'Optional. Number of fuzz test iterations to run.',
|
|
362
|
+
},
|
|
363
|
+
maxInputLength: {
|
|
364
|
+
type: SchemaType.NUMBER,
|
|
365
|
+
description: 'Optional. Maximum length/size for generated fuzz input vectors.',
|
|
366
|
+
},
|
|
367
|
+
seed: {
|
|
368
|
+
type: SchemaType.NUMBER,
|
|
369
|
+
description: 'Optional. Random seed for deterministic fuzz generation.',
|
|
370
|
+
},
|
|
371
|
+
timeoutMs: {
|
|
372
|
+
type: SchemaType.NUMBER,
|
|
373
|
+
description: 'Optional. Execution timeout in milliseconds.',
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
required: ['code'],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: 'check_heap_delta',
|
|
381
|
+
description: 'Execute a heap memory inspection script to measure memory consumption, detect heap growth/leaks, and evaluate memory delta statistics.',
|
|
382
|
+
parameters: {
|
|
383
|
+
type: SchemaType.OBJECT,
|
|
384
|
+
properties: {
|
|
385
|
+
code: {
|
|
386
|
+
type: SchemaType.STRING,
|
|
387
|
+
description: 'The script or code snippet to execute for heap memory analysis.',
|
|
388
|
+
},
|
|
389
|
+
language: {
|
|
390
|
+
type: SchemaType.STRING,
|
|
391
|
+
description: 'Runtime environment: "node", "ts-node", "python", "bash", "go", or "rust". Defaults to "node".',
|
|
392
|
+
},
|
|
393
|
+
warmupIterations: {
|
|
394
|
+
type: SchemaType.NUMBER,
|
|
395
|
+
description: 'Optional. Number of warmup iterations prior to initial heap snapshot.',
|
|
396
|
+
},
|
|
397
|
+
testIterations: {
|
|
398
|
+
type: SchemaType.NUMBER,
|
|
399
|
+
description: 'Optional. Number of test iterations executed between snapshots.',
|
|
400
|
+
},
|
|
401
|
+
samplingIntervalMs: {
|
|
402
|
+
type: SchemaType.NUMBER,
|
|
403
|
+
description: 'Optional. Memory sampling interval in milliseconds.',
|
|
404
|
+
},
|
|
405
|
+
timeoutMs: {
|
|
406
|
+
type: SchemaType.NUMBER,
|
|
407
|
+
description: 'Optional. Execution timeout in milliseconds.',
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
required: ['code'],
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: 'check_behavioral_drift',
|
|
415
|
+
description: 'Execute baseline and candidate scripts in parallel or sequence to detect output differences, return value mismatches, or behavioral drift.',
|
|
416
|
+
parameters: {
|
|
417
|
+
type: SchemaType.OBJECT,
|
|
418
|
+
properties: {
|
|
419
|
+
baselineCode: {
|
|
420
|
+
type: SchemaType.STRING,
|
|
421
|
+
description: 'The baseline implementation script source code.',
|
|
422
|
+
},
|
|
423
|
+
candidateCode: {
|
|
424
|
+
type: SchemaType.STRING,
|
|
425
|
+
description: 'The candidate implementation script source code to compare against baseline.',
|
|
426
|
+
},
|
|
427
|
+
language: {
|
|
428
|
+
type: SchemaType.STRING,
|
|
429
|
+
description: 'Runtime environment: "node", "ts-node", "python", "bash", "go", or "rust". Defaults to "node".',
|
|
430
|
+
},
|
|
431
|
+
tolerance: {
|
|
432
|
+
type: SchemaType.NUMBER,
|
|
433
|
+
description: 'Optional. Numerical tolerance threshold for floating point comparison.',
|
|
434
|
+
},
|
|
435
|
+
compareKeys: {
|
|
436
|
+
type: SchemaType.ARRAY,
|
|
437
|
+
items: { type: SchemaType.STRING },
|
|
438
|
+
description: 'Optional. Specific JSON object keys/fields to compare for drift.',
|
|
439
|
+
},
|
|
440
|
+
timeoutMs: {
|
|
441
|
+
type: SchemaType.NUMBER,
|
|
442
|
+
description: 'Optional. Execution timeout in milliseconds.',
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
required: ['baselineCode', 'candidateCode'],
|
|
446
|
+
},
|
|
447
|
+
},
|
|
344
448
|
];
|
|
345
449
|
let currentApprovalMode = 'ask';
|
|
346
450
|
/**
|
|
@@ -1312,6 +1416,112 @@ export async function runDebugScript(workspaceRoot, language, code, abortSignal)
|
|
|
1312
1416
|
}
|
|
1313
1417
|
}
|
|
1314
1418
|
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Executes a fuzz probing analysis script and formats the resulting pass/fail and crash statistics.
|
|
1421
|
+
*/
|
|
1422
|
+
export async function executeFuzzProbe(workspaceRoot, code, language = 'node', config, abortSignal) {
|
|
1423
|
+
try {
|
|
1424
|
+
const res = await runFuzzProbe(workspaceRoot, code, language, {
|
|
1425
|
+
...config,
|
|
1426
|
+
abortSignal,
|
|
1427
|
+
});
|
|
1428
|
+
let output = `Fuzz Probe Result: ${res.passed ? 'PASSED' : 'FAILED'}\n`;
|
|
1429
|
+
if (res.totalFuzzRuns > 0)
|
|
1430
|
+
output += `Total Fuzz Runs: ${res.totalFuzzRuns}\n`;
|
|
1431
|
+
if (res.seed !== undefined)
|
|
1432
|
+
output += `Seed: ${res.seed}\n`;
|
|
1433
|
+
if (res.failingInputs && res.failingInputs.length > 0) {
|
|
1434
|
+
output += `Failing Inputs / Counterexamples:\n${res.failingInputs.map((inp) => ` - ${inp}`).join('\n')}\n`;
|
|
1435
|
+
}
|
|
1436
|
+
if (res.errorSummary) {
|
|
1437
|
+
output += `Error Summary:\n${res.errorSummary}\n`;
|
|
1438
|
+
}
|
|
1439
|
+
if (res.stdout)
|
|
1440
|
+
output += `\n[STDOUT]\n${res.stdout}\n`;
|
|
1441
|
+
if (res.stderr)
|
|
1442
|
+
output += `\n[STDERR]\n${res.stderr}\n`;
|
|
1443
|
+
return {
|
|
1444
|
+
output: output.trim(),
|
|
1445
|
+
...(res.exitCode !== 0 && !res.passed ? { error: `Fuzz probe failed with exit code ${res.exitCode}` } : {}),
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
catch (err) {
|
|
1449
|
+
return { output: '', error: `Fuzz probe error: ${err.message || String(err)}` };
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Executes a heap memory inspection script and formats memory consumption and growth delta metrics.
|
|
1454
|
+
*/
|
|
1455
|
+
export async function executeHeapDelta(workspaceRoot, code, language = 'node', config, abortSignal) {
|
|
1456
|
+
try {
|
|
1457
|
+
const res = await runCheckHeapDelta(workspaceRoot, code, language, {
|
|
1458
|
+
...config,
|
|
1459
|
+
abortSignal,
|
|
1460
|
+
});
|
|
1461
|
+
let output = `Heap Delta Analysis Result:\n`;
|
|
1462
|
+
output += `Initial Heap: ${res.initialHeapBytes} bytes\n`;
|
|
1463
|
+
output += `Final Heap: ${res.finalHeapBytes} bytes\n`;
|
|
1464
|
+
output += `Delta: ${res.deltaBytes} bytes\n`;
|
|
1465
|
+
output += `Leak Detected: ${res.leakDetected ? 'YES' : 'NO'}\n`;
|
|
1466
|
+
if (res.growthRateBytesPerIter !== undefined) {
|
|
1467
|
+
output += `Growth Rate: ${res.growthRateBytesPerIter} bytes/iter\n`;
|
|
1468
|
+
}
|
|
1469
|
+
if (res.samples && res.samples.length > 0) {
|
|
1470
|
+
output += `Samples (${res.samples.length}):\n`;
|
|
1471
|
+
for (const s of res.samples) {
|
|
1472
|
+
output += ` Iter ${s.iteration}: ${s.heapBytes} bytes\n`;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
if (res.stdout)
|
|
1476
|
+
output += `\n[STDOUT]\n${res.stdout}\n`;
|
|
1477
|
+
if (res.stderr)
|
|
1478
|
+
output += `\n[STDERR]\n${res.stderr}\n`;
|
|
1479
|
+
return {
|
|
1480
|
+
output: output.trim(),
|
|
1481
|
+
...(res.exitCode !== 0 ? { error: `Heap delta failed with exit code ${res.exitCode}` } : {}),
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
catch (err) {
|
|
1485
|
+
return { output: '', error: `Heap delta error: ${err.message || String(err)}` };
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Executes baseline and candidate scripts, returning output diffs and behavioral drift findings.
|
|
1490
|
+
*/
|
|
1491
|
+
export async function executeBehavioralDrift(workspaceRoot, baselineCode, candidateCode, language = 'node', config, abortSignal) {
|
|
1492
|
+
try {
|
|
1493
|
+
const res = await runCheckBehavioralDrift(workspaceRoot, baselineCode, candidateCode, language, {
|
|
1494
|
+
...config,
|
|
1495
|
+
abortSignal,
|
|
1496
|
+
});
|
|
1497
|
+
let output = `Behavioral Drift Analysis Result:\n`;
|
|
1498
|
+
output += `Has Drift: ${res.hasDrift ? 'YES' : 'NO'}\n`;
|
|
1499
|
+
if (res.diffSummary) {
|
|
1500
|
+
output += `Diff Summary:\n${res.diffSummary}\n`;
|
|
1501
|
+
}
|
|
1502
|
+
if (res.driftDetails && res.driftDetails.length > 0) {
|
|
1503
|
+
output += `Drift Details:\n`;
|
|
1504
|
+
for (const detail of res.driftDetails) {
|
|
1505
|
+
output += ` - ${detail.field ? `[${detail.field}] ` : ''}Baseline: ${JSON.stringify(detail.baseline)} vs Candidate: ${JSON.stringify(detail.candidate)}${detail.diff ? ` (${detail.diff})` : ''}\n`;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
if (res.baselineOutput)
|
|
1509
|
+
output += `\n[BASELINE OUTPUT]\n${res.baselineOutput}\n`;
|
|
1510
|
+
if (res.candidateOutput)
|
|
1511
|
+
output += `\n[CANDIDATE OUTPUT]\n${res.candidateOutput}\n`;
|
|
1512
|
+
if (res.stdout)
|
|
1513
|
+
output += `\n[STDOUT]\n${res.stdout}\n`;
|
|
1514
|
+
if (res.stderr)
|
|
1515
|
+
output += `\n[STDERR]\n${res.stderr}\n`;
|
|
1516
|
+
return {
|
|
1517
|
+
output: output.trim(),
|
|
1518
|
+
...(res.exitCode !== 0 ? { error: `Behavioral drift check failed with exit code ${res.exitCode}` } : {}),
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
catch (err) {
|
|
1522
|
+
return { output: '', error: `Behavioral drift error: ${err.message || String(err)}` };
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1315
1525
|
/**
|
|
1316
1526
|
* Resolves `@alias/` prefixed paths in tool arguments to the correct workspace root
|
|
1317
1527
|
* and relative path. Returns the effective workspaceRoot and the cleaned arguments.
|
|
@@ -1424,6 +1634,15 @@ export async function executeTool(workspaceRoot, toolName, args, abortSignal) {
|
|
|
1424
1634
|
else if (normalizedToolName === 'finish_investigation') {
|
|
1425
1635
|
normalizedToolName = 'finish_task';
|
|
1426
1636
|
}
|
|
1637
|
+
else if (normalizedToolName === 'fuzz_probe' || normalizedToolName === 'run_fuzz') {
|
|
1638
|
+
normalizedToolName = 'run_fuzz_probe';
|
|
1639
|
+
}
|
|
1640
|
+
else if (normalizedToolName === 'heap_delta' || normalizedToolName === 'check_heap') {
|
|
1641
|
+
normalizedToolName = 'check_heap_delta';
|
|
1642
|
+
}
|
|
1643
|
+
else if (normalizedToolName === 'behavioral_drift' || normalizedToolName === 'check_drift') {
|
|
1644
|
+
normalizedToolName = 'check_behavioral_drift';
|
|
1645
|
+
}
|
|
1427
1646
|
// ─── Multi-Workspace Path Resolution ─────────────────────────────
|
|
1428
1647
|
// Intercept @alias/ prefixed paths and swap workspaceRoot + relative path
|
|
1429
1648
|
// before dispatching to the underlying tool functions (which remain unchanged).
|
|
@@ -1620,6 +1839,42 @@ export async function executeTool(workspaceRoot, toolName, args, abortSignal) {
|
|
|
1620
1839
|
case 'find_recent_changes':
|
|
1621
1840
|
result = await findRecentChanges(effectiveRoot, resolvedArgs.dirPath, resolvedArgs.minutes, resolvedArgs.maxDepth);
|
|
1622
1841
|
break;
|
|
1842
|
+
case 'run_fuzz_probe': {
|
|
1843
|
+
const code = resolvedArgs.code;
|
|
1844
|
+
const language = resolvedArgs.language || 'node';
|
|
1845
|
+
const config = {
|
|
1846
|
+
iterations: resolvedArgs.iterations,
|
|
1847
|
+
maxInputLength: resolvedArgs.maxInputLength,
|
|
1848
|
+
seed: resolvedArgs.seed,
|
|
1849
|
+
timeoutMs: resolvedArgs.timeoutMs,
|
|
1850
|
+
};
|
|
1851
|
+
result = await executeFuzzProbe(effectiveRoot, code, language, config, abortSignal);
|
|
1852
|
+
break;
|
|
1853
|
+
}
|
|
1854
|
+
case 'check_heap_delta': {
|
|
1855
|
+
const code = resolvedArgs.code;
|
|
1856
|
+
const language = resolvedArgs.language || 'node';
|
|
1857
|
+
const config = {
|
|
1858
|
+
warmupIterations: resolvedArgs.warmupIterations,
|
|
1859
|
+
testIterations: resolvedArgs.testIterations,
|
|
1860
|
+
samplingIntervalMs: resolvedArgs.samplingIntervalMs,
|
|
1861
|
+
timeoutMs: resolvedArgs.timeoutMs,
|
|
1862
|
+
};
|
|
1863
|
+
result = await executeHeapDelta(effectiveRoot, code, language, config, abortSignal);
|
|
1864
|
+
break;
|
|
1865
|
+
}
|
|
1866
|
+
case 'check_behavioral_drift': {
|
|
1867
|
+
const baselineCode = resolvedArgs.baselineCode;
|
|
1868
|
+
const candidateCode = resolvedArgs.candidateCode;
|
|
1869
|
+
const language = resolvedArgs.language || 'node';
|
|
1870
|
+
const config = {
|
|
1871
|
+
tolerance: resolvedArgs.tolerance,
|
|
1872
|
+
compareKeys: resolvedArgs.compareKeys,
|
|
1873
|
+
timeoutMs: resolvedArgs.timeoutMs,
|
|
1874
|
+
};
|
|
1875
|
+
result = await executeBehavioralDrift(effectiveRoot, baselineCode, candidateCode, language, config, abortSignal);
|
|
1876
|
+
break;
|
|
1877
|
+
}
|
|
1623
1878
|
default:
|
|
1624
1879
|
result = { output: '', error: `Unknown tool: "${toolName}"` };
|
|
1625
1880
|
break;
|
|
@@ -35,6 +35,7 @@ export class MessageBus {
|
|
|
35
35
|
signals = [];
|
|
36
36
|
activityCursors = new Map();
|
|
37
37
|
signalCursors = new Map();
|
|
38
|
+
persistQueue = Promise.resolve();
|
|
38
39
|
persistPath;
|
|
39
40
|
workspaceRoot;
|
|
40
41
|
/** Maximum semantic signals any single agent can post */
|
|
@@ -172,8 +173,10 @@ export class MessageBus {
|
|
|
172
173
|
activityCursors: Object.fromEntries(this.activityCursors),
|
|
173
174
|
signalCursors: Object.fromEntries(this.signalCursors),
|
|
174
175
|
};
|
|
175
|
-
// Fire-and-forget — don't block the calling agent's tool loop
|
|
176
|
-
|
|
176
|
+
// Fire-and-forget — don't block the calling agent's tool loop, but serialize writes sequentially
|
|
177
|
+
this.persistQueue = this.persistQueue
|
|
178
|
+
.then(() => atomicWriteFile(this.persistPath, JSON.stringify(snapshot)))
|
|
179
|
+
.catch((err) => {
|
|
177
180
|
debugLog(`MessageBus: Failed to persist to disk: ${err}`);
|
|
178
181
|
});
|
|
179
182
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'path';
|
|
1
2
|
import { executeTool, getToolDeclarations as getBaseToolDeclarations } from '../agent-tools.js';
|
|
2
3
|
import { MessageBus } from './messageBus.js';
|
|
3
4
|
import { debugLog } from '../../utils/logger.js';
|
|
@@ -149,6 +150,31 @@ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus,
|
|
|
149
150
|
const matchCount = (outputText.match(/\.\/[^:]+:\d+:/g) || []).length;
|
|
150
151
|
resultSummary = `${matchCount} matches`;
|
|
151
152
|
}
|
|
153
|
+
else if (name === 'rename_file') {
|
|
154
|
+
const src = String(args.sourcePath ?? '');
|
|
155
|
+
const tgt = String(args.targetPath ?? '');
|
|
156
|
+
targetDesc = `${src} -> ${tgt}`;
|
|
157
|
+
actionDesc = (src && tgt && path.dirname(src) === path.dirname(tgt)) ? 'Renamed' : 'Moved';
|
|
158
|
+
}
|
|
159
|
+
else if (name === 'delete_file') {
|
|
160
|
+
targetDesc = String(args.filePath);
|
|
161
|
+
actionDesc = 'Deleted';
|
|
162
|
+
}
|
|
163
|
+
else if (name === 'run_fuzz_probe') {
|
|
164
|
+
targetDesc = String(args.language ?? 'node');
|
|
165
|
+
actionDesc = 'Ran fuzz probe';
|
|
166
|
+
resultSummary = typeof result === 'object' && result?.error ? result.error : 'Completed fuzz probe';
|
|
167
|
+
}
|
|
168
|
+
else if (name === 'check_heap_delta') {
|
|
169
|
+
targetDesc = String(args.language ?? 'node');
|
|
170
|
+
actionDesc = 'Inspected heap delta';
|
|
171
|
+
resultSummary = typeof result === 'object' && result?.error ? result.error : 'Completed heap analysis';
|
|
172
|
+
}
|
|
173
|
+
else if (name === 'check_behavioral_drift') {
|
|
174
|
+
targetDesc = String(args.language ?? 'node');
|
|
175
|
+
actionDesc = 'Checked behavioral drift';
|
|
176
|
+
resultSummary = typeof result === 'object' && result?.error ? result.error : 'Completed drift comparison';
|
|
177
|
+
}
|
|
152
178
|
return result;
|
|
153
179
|
}
|
|
154
180
|
catch (error) {
|
|
@@ -72,7 +72,7 @@ export class SubAgentRunner {
|
|
|
72
72
|
`3. EXECUTION WORKFLOW:\n` +
|
|
73
73
|
` - Step 1 (Investigate): Use 'grep_search', 'read_file', or 'list_directory' to inspect the codebase as needed.\n` +
|
|
74
74
|
` - Step 2 (Execute): Use 'modify_file' or 'write_file' to implement the required changes.\n` +
|
|
75
|
-
` - Step 3 (Verify): Use 'run_debug_script' or 'run_command' to test your changes if necessary.\n` +
|
|
75
|
+
` - Step 3 (Verify): Use 'run_debug_script', 'run_fuzz_probe', 'check_heap_delta', 'check_behavioral_drift', or 'run_command' to test your changes if necessary.\n` +
|
|
76
76
|
` - Step 4 (Conclude): Once your specific objective is fully met, stop calling tools and return a text summary of your changes.\n` +
|
|
77
77
|
`4. Coordinate: Use 'read_messages' to check for updates from other agents. Use 'post_message' if you discover breaking changes affecting others.\n` +
|
|
78
78
|
`</critical_guidelines>`);
|
|
@@ -5,11 +5,53 @@ export interface VerificationResult {
|
|
|
5
5
|
errors: string[];
|
|
6
6
|
aborted?: boolean;
|
|
7
7
|
}
|
|
8
|
+
export interface Mutant {
|
|
9
|
+
id: string;
|
|
10
|
+
filePath: string;
|
|
11
|
+
line: number;
|
|
12
|
+
originalCode: string;
|
|
13
|
+
mutatedCode: string;
|
|
14
|
+
operator: string;
|
|
15
|
+
}
|
|
16
|
+
export interface MutationTestResult {
|
|
17
|
+
totalMutants: number;
|
|
18
|
+
killedMutants: number;
|
|
19
|
+
survivedMutants: number;
|
|
20
|
+
mutationScore: number;
|
|
21
|
+
mutants: Array<Mutant & {
|
|
22
|
+
killed: boolean;
|
|
23
|
+
output?: string;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
8
26
|
export declare function detectVerificationCommand(workspaceRoot: string): Promise<string | null>;
|
|
9
27
|
export declare function runVerification(workspaceRoot: string, abortSignal?: AbortSignal): Promise<VerificationResult | null>;
|
|
10
28
|
export declare function formatVerificationForModel(result: VerificationResult): string;
|
|
11
29
|
export interface FullVerificationResult {
|
|
12
30
|
errors: string | null;
|
|
13
31
|
warnings: string | null;
|
|
32
|
+
mutationResult?: MutationTestResult | null;
|
|
14
33
|
}
|
|
15
34
|
export declare function verifyChangedFiles(workspaceRoot: string, filePaths: string[], abortSignal?: AbortSignal): Promise<FullVerificationResult | '[Verification Aborted]'>;
|
|
35
|
+
/**
|
|
36
|
+
* Generates mutants scoped specifically to modified lines of a source file.
|
|
37
|
+
*/
|
|
38
|
+
export declare function generateDiffScopedMutants(filePath: string, content: string, modifiedLines?: number[]): Mutant[];
|
|
39
|
+
/**
|
|
40
|
+
* Runs diff-scoped mutation testing on modified files within the workspace.
|
|
41
|
+
* Mutates target lines in-memory/temp copy, executes workspace tests, and ensures mutants are killed.
|
|
42
|
+
*/
|
|
43
|
+
export declare function runDiffScopedMutationTesting(workspaceRoot: string, filePaths: string[], modifiedLineMap?: Record<string, number[]>, abortSignal?: AbortSignal, options?: {
|
|
44
|
+
maxMutantsPerFile?: number;
|
|
45
|
+
timeoutMs?: number;
|
|
46
|
+
}): Promise<MutationTestResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Detects whether property-based testing framework configuration or dependencies exist in workspace.
|
|
49
|
+
*/
|
|
50
|
+
export declare function detectPropertyBasedTests(workspaceRoot: string): Promise<{
|
|
51
|
+
hasPbt: boolean;
|
|
52
|
+
framework?: string;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Executes workspace property-based tests if detected.
|
|
56
|
+
*/
|
|
57
|
+
export declare function runPropertyBasedVerification(workspaceRoot: string, abortSignal?: AbortSignal): Promise<VerificationResult | null>;
|