minovative-mind-cli 2.14.0 → 2.14.2

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 (35) hide show
  1. package/README.md +51 -68
  2. package/dist/services/agent/slashCommands.js +74 -31
  3. package/dist/services/agent/toolLoop.js +3 -1
  4. package/dist/services/agent-tools.d.ts +18 -0
  5. package/dist/services/agent-tools.js +220 -43
  6. package/dist/services/agent.js +6 -3
  7. package/dist/services/ai.d.ts +3 -0
  8. package/dist/services/ai.js +65 -20
  9. package/dist/services/contextAgent.d.ts +10 -4
  10. package/dist/services/contextAgent.js +33 -9
  11. package/dist/services/orchestration/investigationAgent.js +34 -9
  12. package/dist/services/orchestration/investigationCache.js +19 -9
  13. package/dist/services/orchestration/readCache.d.ts +1 -0
  14. package/dist/services/orchestration/readCache.js +5 -2
  15. package/dist/services/orchestration/scopedTools.js +37 -10
  16. package/dist/services/orchestration/subAgent.js +16 -2
  17. package/dist/services/proxyClient.d.ts +6 -0
  18. package/dist/services/proxyClient.js +24 -10
  19. package/dist/services/userProfileService.d.ts +14 -0
  20. package/dist/services/userProfileService.js +105 -3
  21. package/dist/utils/analysisRunner.d.ts +120 -8
  22. package/dist/utils/analysisRunner.js +946 -125
  23. package/dist/utils/contextPrompts.d.ts +39 -0
  24. package/dist/utils/contextPrompts.js +81 -9
  25. package/dist/utils/contextRanker.d.ts +216 -0
  26. package/dist/utils/contextRanker.js +603 -0
  27. package/dist/utils/dependencyTracer/modules/graph.d.ts +4 -1
  28. package/dist/utils/dependencyTracer/modules/graph.js +11 -0
  29. package/dist/utils/dependencyTracer/modules/types.d.ts +10 -0
  30. package/dist/utils/dependencyTracer.d.ts +25 -0
  31. package/dist/utils/dependencyTracer.js +34 -0
  32. package/dist/utils/systemPrompts.d.ts +1 -1
  33. package/dist/utils/systemPrompts.js +13 -2
  34. package/oclif.manifest.json +1 -1
  35. package/package.json +1 -1
@@ -278,11 +278,15 @@ export const toolDeclarations = [
278
278
  },
279
279
  {
280
280
  name: 'run_debug_script',
281
- description: 'Write a disposable script to a sandboxed temporary file in os.tmpdir(), execute it using the specified runtime, and return the exact standard output and standard error without polluting the workspace or triggering IDE file watchers. ' +
282
- 'Use this to: (1) actively debug the codebase by inspecting variables or logging values, ' +
283
- '(2) validate your changes by importing the modified module and asserting expected behavior with edge-case inputs, ' +
284
- '(3) run quick sanity checks (e.g., verify a config file parses correctly, confirm exports are intact after a refactor, or check that a function returns the expected output), ' +
285
- '(4) write lightweight ML scripts to solve complex problems e.g., polynomial regression to estimate Big-O complexity, fuzz testing with Markov chain input generation, ' +
281
+ description: 'Write a disposable script to a sandboxed temporary file in os.tmpdir(), execute it using the specified runtime, and return the exact standard output, structured JSON result, and standard error without polluting the workspace or triggering IDE file watchers. ' +
282
+ 'Sandbox Features: ' +
283
+ '(1) Preloaded Helpers: "emitResult(data)" / "__emitResult(data)" outputs structured JSON payloads parsed directly into structuredResult; "inspectSymbols(target)" / "inspectObject(target)" inspects functions, classes, and properties. ' +
284
+ '(2) Module & Path Resolution: Automatically inherits tsconfig.json path aliases (@/*), NODE_PATH, and workspace virtualenvs (.venv, venv) for Python. ' +
285
+ '(3) Diagnostics Middleware: Provides actionable [SANDBOX DIAGNOSTIC] advisories for missing imports, ESM/CJS interop, and compiler errors. ' +
286
+ 'Use this to: (a) actively debug the codebase by inspecting variables or logging values, ' +
287
+ '(b) validate your changes by importing the modified module and asserting expected behavior with edge-case inputs, ' +
288
+ '(c) run quick sanity checks (e.g., verify a config file parses correctly, confirm exports are intact after a refactor, or check that a function returns the expected output), ' +
289
+ '(d) write lightweight ML scripts to solve complex problems — e.g., polynomial regression to estimate Big-O complexity, fuzz testing with Markov chain input generation, ' +
286
290
  'output regression detection via statistical similarity scoring, or error message classification with Naive Bayes. ' +
287
291
  'Default to "node" for generic tasks as a safe baseline, but act like a native inhabitant of the host environment — if Python, Go, Rust, or host-native libraries are active in the project, leverage the host\'s native runtimes for maximum efficiency. ' +
288
292
  'Do not guess whether your code works — test it directly!',
@@ -1124,6 +1128,7 @@ const HIGH_SIGNAL_ERROR_PATTERNS = [
1124
1128
  /undefined reference/i,
1125
1129
  /ld: symbol\(s\) not found/i,
1126
1130
  /clang: error:/i,
1131
+ /gcc: error:/i,
1127
1132
  /referenced from:/i,
1128
1133
  /note: expanded from macro/i,
1129
1134
  /note: candidate:/i,
@@ -1133,29 +1138,59 @@ const HIGH_SIGNAL_ERROR_PATTERNS = [
1133
1138
  /TypeError:/i,
1134
1139
  /ImportError:/i,
1135
1140
  /ModuleNotFoundError:/i,
1141
+ /AttributeError:/i,
1142
+ /IndexError:/i,
1143
+ /KeyError:/i,
1144
+ /ValueError:/i,
1136
1145
  /FAILED \(failures=/i,
1137
1146
  /\bE\s{3}\b/,
1138
1147
  /\bFAIL\b/,
1139
- // Node.js / TypeScript / Jest / Mocha
1148
+ /\bFAILED\b/,
1149
+ /=== FAILURES ===/i,
1150
+ /=== ERRORS ===/i,
1151
+ // Sandbox Diagnostics & Structured Results
1152
+ /\[SANDBOX DIAGNOSTIC\]/i,
1153
+ /\[STRUCTURED RESULT\]/i,
1154
+ /Module resolution failure/i,
1155
+ /CommonJS require\(\) attempted to load an ES Module/i,
1156
+ // Node.js / TypeScript / Jest / Mocha / Vitest
1157
+ /TS\d+:/i,
1158
+ /TS\d{4,5}:/i,
1140
1159
  /SyntaxError:/i,
1141
1160
  /ReferenceError:/i,
1161
+ /TypeError:/i,
1162
+ /RangeError:/i,
1163
+ /URIError:/i,
1142
1164
  /Error: Cannot find module/i,
1165
+ /Cannot find module/i,
1166
+ /Cannot find name/i,
1167
+ /Type '.*' is not assignable to type/i,
1168
+ /Property '.*' does not exist on type/i,
1143
1169
  /\bFAIL\b/,
1144
1170
  /✕\s/,
1145
- /TS\d{4}:/,
1146
1171
  // Rust / Cargo
1147
1172
  /error\[E\d+\]:/i,
1148
1173
  /fatal runtime error:/i,
1149
1174
  /panicked at/i,
1175
+ /thread '.*' panicked at/i,
1176
+ /-->\s+.*:\d+:\d+/i,
1177
+ /could not compile/i,
1150
1178
  // Go
1151
1179
  /panic:/i,
1152
1180
  /cannot find package/i,
1153
1181
  /undefined:/i,
1154
1182
  /FAIL\t/,
1155
- // Java / JVM
1183
+ // Java / JVM / Kotlin / Scala
1156
1184
  /Exception in thread/i,
1157
1185
  /java\.lang\./i,
1158
1186
  /error: cannot find symbol/i,
1187
+ /NullPointerException/i,
1188
+ /ClassNotFoundException/i,
1189
+ // C# / .NET
1190
+ /CS\d{4}:/i,
1191
+ /Unhandled exception/i,
1192
+ // Swift
1193
+ /error:\s+/i,
1159
1194
  // Generic Fallbacks
1160
1195
  /\b(cannot|unable to|not found|command not found|no such file)\b/i,
1161
1196
  ];
@@ -1170,8 +1205,77 @@ const STACK_TRACE_START_PATTERNS = [
1170
1205
  /Traceback \(most recent call last\):/i,
1171
1206
  /Exception in thread/i,
1172
1207
  /panic:/i,
1208
+ /thread '.*' panicked at/i,
1209
+ /panicked at/i,
1173
1210
  /Error:\s*$/i,
1211
+ /TypeError:\s*$/i,
1212
+ /ReferenceError:\s*$/i,
1213
+ /AssertionError:\s*$/i,
1214
+ /=== FAILURES ===/i,
1215
+ /=== ERRORS ===/i,
1174
1216
  ];
1217
+ /**
1218
+ * Truncates large string output using bounded head and tail windows with a structured metadata receipt.
1219
+ * Preserves early context (e.g. invocation, build targets) and late context (e.g. error summaries, exit codes)
1220
+ * while bounding total character and line count.
1221
+ *
1222
+ * @param text - The raw output text to truncate.
1223
+ * @param options - Configuration options for character/line limits and custom receipt notices.
1224
+ * @returns The windowed text containing head, structured metadata receipt, and tail.
1225
+ */
1226
+ export function truncateWithHeadTailWindow(text, options) {
1227
+ if (!text || typeof text !== 'string')
1228
+ return '';
1229
+ const maxChars = options?.maxChars ?? 30_000;
1230
+ const maxLines = options?.maxLines ?? 500;
1231
+ const headRatio = options?.headRatio ?? 0.35;
1232
+ const receiptLabel = options?.receiptLabel ?? 'Output';
1233
+ const hint = options?.hint ?? 'Pipe command to a file and read it in chunks, or use grep.';
1234
+ const lines = text.split(/\r?\n/);
1235
+ if (text.length <= maxChars && lines.length <= maxLines) {
1236
+ return text;
1237
+ }
1238
+ const totalBytes = text.length;
1239
+ const totalLines = lines.length;
1240
+ // Calculate target characters for head and tail windows
1241
+ const receiptReserve = 500;
1242
+ const availableChars = Math.max(1000, maxChars - receiptReserve);
1243
+ const targetHeadChars = Math.floor(availableChars * headRatio);
1244
+ const targetTailChars = availableChars - targetHeadChars;
1245
+ // Max lines for head and tail
1246
+ const targetHeadLines = Math.max(5, Math.floor(maxLines * headRatio));
1247
+ const targetTailLines = Math.max(10, maxLines - targetHeadLines);
1248
+ // Accumulate head lines
1249
+ const headLines = [];
1250
+ let headCharsCount = 0;
1251
+ for (let i = 0; i < lines.length; i++) {
1252
+ const line = lines[i];
1253
+ if (headLines.length >= targetHeadLines ||
1254
+ (headLines.length > 0 && headCharsCount + line.length + 1 > targetHeadChars)) {
1255
+ break;
1256
+ }
1257
+ headLines.push(line);
1258
+ headCharsCount += line.length + 1;
1259
+ }
1260
+ // Accumulate tail lines backwards (ensuring no overlap with head)
1261
+ const tailLines = [];
1262
+ let tailCharsCount = 0;
1263
+ for (let i = lines.length - 1; i >= headLines.length; i--) {
1264
+ const line = lines[i];
1265
+ if (tailLines.length >= targetTailLines ||
1266
+ (tailLines.length > 0 && tailCharsCount + line.length + 1 > targetTailChars)) {
1267
+ break;
1268
+ }
1269
+ tailLines.unshift(line);
1270
+ tailCharsCount += line.length + 1;
1271
+ }
1272
+ const omittedLines = Math.max(0, totalLines - headLines.length - tailLines.length);
1273
+ const headText = headLines.join('\n');
1274
+ const tailText = tailLines.join('\n');
1275
+ const omittedBytes = Math.max(0, totalBytes - headText.length - tailText.length);
1276
+ const receipt = `\n\n... [${receiptLabel} truncated: ${omittedLines} lines (${omittedBytes} bytes) omitted. Showing ${headLines.length} head lines and ${tailLines.length} tail lines. Total: ${totalLines} lines (${totalBytes} bytes). ${hint}] ...\n\n`;
1277
+ return headText + receipt + tailText;
1278
+ }
1175
1279
  /**
1176
1280
  * Condenses verbose command and test failure outputs into high-signal diagnostic error logs
1177
1281
  * with dynamic tail sizing and stack trace boundary snapping.
@@ -1189,14 +1293,30 @@ export function extractHighSignalError(rawError) {
1189
1293
  }
1190
1294
  // Filter out noise lines (e.g. repetitive compiler warning flags)
1191
1295
  const nonNoiseLines = lines.filter((l) => !COMPILER_WARNING_NOISE_PATTERN.test(l));
1192
- // Find high-signal error lines
1296
+ // Find high-signal error lines and associated compiler frames/context lines
1193
1297
  const highSignalLines = [];
1194
1298
  const highSignalIndices = new Set();
1195
1299
  for (let i = 0; i < nonNoiseLines.length; i++) {
1196
1300
  const line = nonNoiseLines[i];
1197
1301
  if (HIGH_SIGNAL_ERROR_PATTERNS.some((p) => p.test(line))) {
1198
- highSignalLines.push(line);
1199
- highSignalIndices.add(i);
1302
+ if (!highSignalIndices.has(i)) {
1303
+ highSignalLines.push(line);
1304
+ highSignalIndices.add(i);
1305
+ }
1306
+ // Capture up to 5 immediate compiler frame continuation lines (e.g. code squiggles, expected/found, arrows)
1307
+ for (let j = 1; j <= 5 && i + j < nonNoiseLines.length; j++) {
1308
+ const nextLine = nonNoiseLines[i + j];
1309
+ if (/^\s*(\||-->|\^|~|expected|found|note:|candidate:)/i.test(nextLine) ||
1310
+ /^\s*\d+\s*\|/.test(nextLine)) {
1311
+ if (!highSignalIndices.has(i + j)) {
1312
+ highSignalLines.push(nextLine);
1313
+ highSignalIndices.add(i + j);
1314
+ }
1315
+ }
1316
+ else {
1317
+ break;
1318
+ }
1319
+ }
1200
1320
  }
1201
1321
  }
1202
1322
  // Dynamic tail sizing: Math.min(50, Math.max(15, Math.floor(totalLines * 0.15)))
@@ -1233,7 +1353,15 @@ export function extractHighSignalError(rawError) {
1233
1353
  else {
1234
1354
  resultBlocks.push(tailLines.join('\n'));
1235
1355
  }
1236
- const condensedResult = resultBlocks.join('\n\n');
1356
+ let condensedResult = resultBlocks.join('\n\n');
1357
+ // Bounded head/tail window check on condensed result if still oversized
1358
+ if (condensedResult.length > 30_000) {
1359
+ condensedResult = truncateWithHeadTailWindow(condensedResult, {
1360
+ maxChars: 30_000,
1361
+ receiptLabel: 'Diagnostic output',
1362
+ hint: 'Pipe to a file if you need full logs.',
1363
+ });
1364
+ }
1237
1365
  const condensedLines = condensedResult.split(/\r?\n/).length;
1238
1366
  const linesPruned = Math.max(0, lines.length - condensedLines);
1239
1367
  const charsPruned = Math.max(0, rawError.length - condensedResult.length);
@@ -1257,26 +1385,25 @@ export async function runCommand(workspaceRoot, command, abortSignal) {
1257
1385
  maxBuffer: 1024 * 1024 * 2, // 2 MB buffer
1258
1386
  signal: abortSignal,
1259
1387
  });
1260
- let output = [stdout, stderr].filter(Boolean).join('\n');
1261
- // Truncate command output to prevent memory blowout from massive build logs
1262
- const MAX_CMD_OUTPUT = 30_000;
1263
- if (output.length > MAX_CMD_OUTPUT) {
1264
- output =
1265
- output.substring(0, MAX_CMD_OUTPUT) +
1266
- `\n\n... (Output truncated: ${output.length} bytes exceeded 30KB limit. To view the rest, pipe the command to a file and read it in chunks, or use grep.)`;
1267
- }
1268
- return { output: output || '(command produced no output)' };
1388
+ const output = [stdout, stderr].filter(Boolean).join('\n');
1389
+ // Bounded head/tail window truncation with structured metadata receipt
1390
+ const windowedOutput = truncateWithHeadTailWindow(output, {
1391
+ maxChars: 30_000,
1392
+ receiptLabel: 'Output',
1393
+ hint: 'To view the rest, pipe the command to a file and read it in chunks, or use grep.',
1394
+ });
1395
+ return { output: windowedOutput || '(command produced no output)' };
1269
1396
  }
1270
1397
  catch (err) {
1271
1398
  const message = err instanceof Error ? err.message : String(err);
1272
1399
  // Condense error output with high-signal extraction and dynamic tail sizing
1273
1400
  const condensedError = extractHighSignalError(message);
1274
- const MAX_ERR_OUTPUT = 30_000;
1275
- const truncatedMsg = condensedError.length > MAX_ERR_OUTPUT
1276
- ? condensedError.substring(0, MAX_ERR_OUTPUT) +
1277
- `\n\n... (Error output truncated: ${condensedError.length} bytes exceeded 30KB limit. Pipe to a file if you need full logs.)`
1278
- : condensedError;
1279
- return { output: '', error: `Command failed: ${truncatedMsg}` };
1401
+ const windowedError = truncateWithHeadTailWindow(condensedError, {
1402
+ maxChars: 30_000,
1403
+ receiptLabel: 'Error output',
1404
+ hint: 'Pipe to a file if you need full logs.',
1405
+ });
1406
+ return { output: '', error: `Command failed: ${windowedError}` };
1280
1407
  }
1281
1408
  }
1282
1409
  /**
@@ -1305,14 +1432,18 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob, fixedStrings,
1305
1432
  }
1306
1433
  // Use ignore package to evaluate path globs (e.g. src/**/*.ts)
1307
1434
  const globMatcher = fileGlob ? ignore().add(fileGlob) : null;
1435
+ const MAX_SHOWN_MATCHES = 50;
1436
+ const MAX_SCAN_MATCHES = 500;
1308
1437
  const results = [];
1438
+ let totalMatches = 0;
1439
+ const matchedFiles = new Set();
1309
1440
  /**
1310
1441
  * Recursively walks directory entries to search for pattern matches.
1311
1442
  *
1312
1443
  * @param dir - Absolute path to the current directory being searched.
1313
1444
  */
1314
1445
  async function walk(dir) {
1315
- if (results.length >= 50)
1446
+ if (totalMatches >= MAX_SCAN_MATCHES)
1316
1447
  return;
1317
1448
  if (abortSignal?.aborted)
1318
1449
  return;
@@ -1324,7 +1455,7 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob, fixedStrings,
1324
1455
  return;
1325
1456
  }
1326
1457
  for (const entry of entries) {
1327
- if (results.length >= 50)
1458
+ if (totalMatches >= MAX_SCAN_MATCHES)
1328
1459
  return;
1329
1460
  if (abortSignal?.aborted)
1330
1461
  return;
@@ -1351,12 +1482,16 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob, fixedStrings,
1351
1482
  const content = await fs.readFile(filePath, 'utf-8');
1352
1483
  const lines = content.split(/\r?\n/);
1353
1484
  for (let i = 0; i < lines.length; i++) {
1354
- if (results.length >= 50)
1485
+ if (totalMatches >= MAX_SCAN_MATCHES)
1355
1486
  break;
1356
1487
  regexPattern.lastIndex = 0;
1357
1488
  if (regexPattern.test(lines[i])) {
1358
- const relativePath = path.relative(workspaceRoot, filePath).replace(/\\/g, '/');
1359
- results.push(`${relativePath}:${i + 1}:${lines[i]}`);
1489
+ totalMatches++;
1490
+ matchedFiles.add(relPath);
1491
+ if (results.length < MAX_SHOWN_MATCHES) {
1492
+ const relativePath = path.relative(workspaceRoot, filePath).replace(/\\/g, '/');
1493
+ results.push(`${relativePath}:${i + 1}:${lines[i]}`);
1494
+ }
1360
1495
  }
1361
1496
  }
1362
1497
  }
@@ -1373,8 +1508,18 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob, fixedStrings,
1373
1508
  }
1374
1509
  if (results.length === 0)
1375
1510
  return { output: `No matches found for "${pattern}".` };
1376
- const resultText = results.join('\n') + (results.length >= 50 ? `\n\n... (more results truncated)` : '');
1377
- const wrappedResult = `<workspace_file path="grep_search_results">\n<content_data><![CDATA[\n${sanitizeForCDATA(resultText)}\n]]\\u200B></content_data>\n</workspace_file>`;
1511
+ let resultText = results.join('\n');
1512
+ if (totalMatches > results.length) {
1513
+ const moreSuffix = totalMatches >= MAX_SCAN_MATCHES ? '+' : '';
1514
+ resultText += `\n\n[Showing ${results.length}/${totalMatches}${moreSuffix} matches across ${matchedFiles.size} files. Refine query with fileGlob or dirPath to narrow results.]`;
1515
+ }
1516
+ // Window large grep text if character count is huge
1517
+ const windowedResult = truncateWithHeadTailWindow(resultText, {
1518
+ maxChars: 30_000,
1519
+ receiptLabel: 'Grep search results',
1520
+ hint: 'Refine query with fileGlob or dirPath to narrow results.',
1521
+ });
1522
+ const wrappedResult = `<workspace_file path="grep_search_results">\n<content_data><![CDATA[\n${sanitizeForCDATA(windowedResult)}\n]]\\u200B></content_data>\n</workspace_file>`;
1378
1523
  return { output: wrappedResult };
1379
1524
  }
1380
1525
  catch (err) {
@@ -1513,6 +1658,12 @@ export async function runDebugScript(workspaceRoot, language, code, abortSignal)
1513
1658
  if (res.exitCode !== 0) {
1514
1659
  finalOutput += `Script failed with exit code ${res.exitCode}.\n`;
1515
1660
  }
1661
+ if (res.structuredResult !== undefined) {
1662
+ const formattedStructured = typeof res.structuredResult === 'string'
1663
+ ? res.structuredResult
1664
+ : JSON.stringify(res.structuredResult, null, 2);
1665
+ finalOutput += `[STRUCTURED RESULT]\n${formattedStructured}\n`;
1666
+ }
1516
1667
  if (res.stdout) {
1517
1668
  finalOutput += `[STDOUT]\n${res.stdout}\n`;
1518
1669
  }
@@ -1523,11 +1674,17 @@ export async function runDebugScript(workspaceRoot, language, code, abortSignal)
1523
1674
  finalOutput = 'Script executed successfully with no output.';
1524
1675
  }
1525
1676
  const condensedOutput = extractHighSignalError(finalOutput.trim());
1677
+ const windowedOutput = truncateWithHeadTailWindow(condensedOutput, {
1678
+ maxChars: 30_000,
1679
+ receiptLabel: 'Debug script output',
1680
+ hint: 'Inspect specific variables or refine script if full logs are needed.',
1681
+ });
1526
1682
  return {
1527
- output: `<test_results>\n${sanitizeForCDATA(condensedOutput)}\n</test_results>`,
1683
+ output: `<test_results>\n${sanitizeForCDATA(windowedOutput)}\n</test_results>`,
1684
+ ...(res.structuredResult !== undefined ? { structuredResult: res.structuredResult } : {}),
1528
1685
  ...(res.exitCode !== 0
1529
1686
  ? {
1530
- error: `Script failed with exit code ${res.exitCode}:\n${extractHighSignalError(res.stderr || res.stdout || '')}`,
1687
+ error: `Script failed with exit code ${res.exitCode}:\n${truncateWithHeadTailWindow(extractHighSignalError(res.stderr || res.stdout || ''), { maxChars: 30_000, receiptLabel: 'Debug script error' })}`,
1531
1688
  }
1532
1689
  : {}),
1533
1690
  };
@@ -1573,6 +1730,7 @@ export async function executeFuzzProbe(workspaceRoot, code, language = 'node', c
1573
1730
  output += `\n[STDERR]\n${res.stderr}\n`;
1574
1731
  return {
1575
1732
  output: output.trim(),
1733
+ ...(res.structuredResult !== undefined ? { structuredResult: res.structuredResult } : {}),
1576
1734
  ...(res.exitCode !== 0 && !res.passed ? { error: `Fuzz probe failed with exit code ${res.exitCode}` } : {}),
1577
1735
  };
1578
1736
  }
@@ -1616,6 +1774,7 @@ export async function executeHeapDelta(workspaceRoot, code, language = 'node', c
1616
1774
  output += `\n[STDERR]\n${res.stderr}\n`;
1617
1775
  return {
1618
1776
  output: output.trim(),
1777
+ ...(res.structuredResult !== undefined ? { structuredResult: res.structuredResult } : {}),
1619
1778
  ...(res.exitCode !== 0 ? { error: `Heap delta failed with exit code ${res.exitCode}` } : {}),
1620
1779
  };
1621
1780
  }
@@ -1661,6 +1820,7 @@ export async function executeBehavioralDrift(workspaceRoot, baselineCode, candid
1661
1820
  output += `\n[STDERR]\n${res.stderr}\n`;
1662
1821
  return {
1663
1822
  output: output.trim(),
1823
+ ...(res.structuredResult !== undefined ? { structuredResult: res.structuredResult } : {}),
1664
1824
  ...(res.exitCode !== 0 ? { error: `Behavioral drift check failed with exit code ${res.exitCode}` } : {}),
1665
1825
  };
1666
1826
  }
@@ -1727,6 +1887,9 @@ function resolveWorkspaceArgs(primaryRoot, toolName, args) {
1727
1887
  async function crossWorkspaceGrep(primaryRoot, pattern, fileGlob, fixedStrings, dirPath, abortSignal) {
1728
1888
  const allRoots = workspaceRegistry.getAllRoots(primaryRoot);
1729
1889
  const allResults = [];
1890
+ let totalMatches = 0;
1891
+ const matchedFiles = new Set();
1892
+ const MAX_SHOWN_MATCHES = 80;
1730
1893
  for (const { alias, root } of allRoots) {
1731
1894
  const result = await grepSearch(root, pattern, fileGlob, fixedStrings, dirPath, abortSignal);
1732
1895
  if (result.error)
@@ -1740,10 +1903,14 @@ async function crossWorkspaceGrep(primaryRoot, pattern, fileGlob, fixedStrings,
1740
1903
  const prefix = alias ? `@${alias}/` : '';
1741
1904
  const lines = rawText.split('\n').filter((l) => l.trim());
1742
1905
  for (const line of lines) {
1743
- if (line.startsWith('...')) {
1744
- allResults.push(line);
1906
+ if (line.startsWith('...') || line.startsWith('[')) {
1907
+ continue;
1745
1908
  }
1746
- else {
1909
+ totalMatches++;
1910
+ const matchFile = line.split(':')[0];
1911
+ if (matchFile)
1912
+ matchedFiles.add(prefix ? `${prefix}${matchFile}` : matchFile);
1913
+ if (allResults.length < MAX_SHOWN_MATCHES) {
1747
1914
  // Lines are in format "./path:lineNum:content" — prepend alias prefix
1748
1915
  allResults.push(prefix ? line.replace(/^\.?\//, `@${alias}/`) : line);
1749
1916
  }
@@ -1752,9 +1919,16 @@ async function crossWorkspaceGrep(primaryRoot, pattern, fileGlob, fixedStrings,
1752
1919
  if (allResults.length === 0) {
1753
1920
  return { output: `No matches found for "${pattern}" across all workspaces.` };
1754
1921
  }
1755
- const limited = allResults.slice(0, 80);
1756
- const resultText = limited.join('\n') + (allResults.length > 80 ? `\n\n... (${allResults.length - 80} more results truncated)` : '');
1757
- const wrappedResult = `<workspace_file path="grep_search_results">\n<content_data><![CDATA[\n${sanitizeForCDATA(resultText)}\n]]\\u200B></content_data>\n</workspace_file>`;
1922
+ let resultText = allResults.join('\n');
1923
+ if (totalMatches > allResults.length) {
1924
+ resultText += `\n\n[Showing ${allResults.length}/${totalMatches} matches across ${matchedFiles.size} files in registered workspaces. Refine query to narrow results.]`;
1925
+ }
1926
+ const windowedResult = truncateWithHeadTailWindow(resultText, {
1927
+ maxChars: 30_000,
1928
+ receiptLabel: 'Cross-workspace grep results',
1929
+ hint: 'Refine query to narrow results.',
1930
+ });
1931
+ const wrappedResult = `<workspace_file path="grep_search_results">\n<content_data><![CDATA[\n${sanitizeForCDATA(windowedResult)}\n]]\\u200B></content_data>\n</workspace_file>`;
1758
1932
  return { output: wrappedResult };
1759
1933
  }
1760
1934
  const currentTasksByAgent = new Map();
@@ -1946,7 +2120,10 @@ export async function executeTool(workspaceRoot, toolName, args, abortSignal) {
1946
2120
  result = await runCommand(effectiveRoot, resolvedArgs.command, abortSignal);
1947
2121
  break;
1948
2122
  case 'run_debug_script':
1949
- result = await runDebugScript(effectiveRoot, resolvedArgs.language, resolvedArgs.code, abortSignal);
2123
+ case 'debug_script':
2124
+ case 'run_analysis_script':
2125
+ case 'analysis_script':
2126
+ result = await runDebugScript(effectiveRoot, resolvedArgs.language || 'auto', resolvedArgs.code, abortSignal);
1950
2127
  break;
1951
2128
  case 'grep_search': {
1952
2129
  const wsParam = resolvedArgs.workspace;
@@ -32,7 +32,7 @@ import { changeLogger } from './changeLogger.js';
32
32
  import { chatHistoryService } from './chatHistoryService.js';
33
33
  import { gatherContext, routeIntent, evaluateExecutionComplexity } from './contextAgent.js';
34
34
  import { verifyChangedFiles } from './verificationService.js';
35
- import { buildContextInjection } from '../utils/contextPrompts.js';
35
+ import { buildTieredContextInjection, CONTEXT_BUDGET_CONFIG } from '../utils/contextPrompts.js';
36
36
  import { registerContextFiles } from '../utils/fileReadGuard.js';
37
37
  import { historyText } from '../utils/historyPrompt.js';
38
38
  import { loadUserProfile, formatUserProfileForContext, extractAndSaveUserInsights } from './userProfileService.js';
@@ -391,8 +391,11 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
391
391
  if (gatherRes.contextResult) {
392
392
  // Compress each relevant file individually using helper to avoid nested loop warning
393
393
  gatherRes.contextResult.relevantFiles = await compressContextFiles(workspaceRoot, gatherRes.contextResult, ac.signal);
394
- // Assemble the final context injection string
395
- const contextInjection = buildContextInjection(gatherRes.contextResult);
394
+ // Assemble the final context injection string using 3-tier IR/graph context ranking
395
+ const contextInjection = buildTieredContextInjection(gatherRes.contextResult, {
396
+ prompt: userInput,
397
+ maxChars: CONTEXT_BUDGET_CONFIG.DEFAULT_CONTEXT_MAX_CHARS,
398
+ });
396
399
  debugLog(`Final compressed context injection size: ${contextInjection.length} chars`);
397
400
  // Pre-register all injected files with the read-guard so the execution
398
401
  // agent is allowed to modify them without calling read_file first.
@@ -18,6 +18,9 @@ export declare function setModelThinkingLevel(model: string, level: ThinkingLeve
18
18
  * Resets user-configured thinking levels back to default model presets.
19
19
  */
20
20
  export declare function resetModelThinkingLevels(): void;
21
+ export declare const HISTORICAL_TOOL_OUTPUT_THRESHOLD = 1500;
22
+ export declare const COLLAPSED_TOOL_OUTPUT_MARKER = "\n... [Historical tool output collapsed to save context]";
23
+ export declare function collapseHistoricalOutput(val: string, threshold?: number): string;
21
24
  export declare class ProxyChatSession {
22
25
  private history;
23
26
  private fullHistory;
@@ -100,9 +100,9 @@ const MAX_HISTORY_ENTRIES = 500;
100
100
  * payload stays within sane memory bounds.
101
101
  */
102
102
  const MAX_PART_TEXT_LENGTH = 60_000;
103
- const HISTORICAL_TOOL_OUTPUT_THRESHOLD = 1500;
104
- const COLLAPSED_TOOL_OUTPUT_MARKER = '\n... [Historical tool output collapsed to save context]';
105
- function collapseHistoricalOutput(val, threshold = HISTORICAL_TOOL_OUTPUT_THRESHOLD) {
103
+ export const HISTORICAL_TOOL_OUTPUT_THRESHOLD = 1500;
104
+ export const COLLAPSED_TOOL_OUTPUT_MARKER = '\n... [Historical tool output collapsed to save context]';
105
+ export function collapseHistoricalOutput(val, threshold = HISTORICAL_TOOL_OUTPUT_THRESHOLD) {
106
106
  if (typeof val !== 'string')
107
107
  return String(val ?? '');
108
108
  if (val.length <= threshold || val.includes('[Historical tool output collapsed')) {
@@ -354,20 +354,10 @@ export class ProxyChatSession {
354
354
  const funcResp = part.functionResponse;
355
355
  if (funcResp.response && typeof funcResp.response === 'object') {
356
356
  const respObj = funcResp.response;
357
- if (typeof respObj.output === 'string') {
358
- respObj.output = collapseHistoricalOutput(respObj.output, threshold);
359
- }
360
- if (typeof respObj.error === 'string') {
361
- respObj.error = collapseHistoricalOutput(respObj.error, threshold);
362
- }
363
- if (typeof respObj.result === 'string') {
364
- respObj.result = collapseHistoricalOutput(respObj.result, threshold);
365
- }
366
- if (typeof respObj.stdout === 'string') {
367
- respObj.stdout = collapseHistoricalOutput(respObj.stdout, threshold);
368
- }
369
- if (typeof respObj.stderr === 'string') {
370
- respObj.stderr = collapseHistoricalOutput(respObj.stderr, threshold);
357
+ for (const key of Object.keys(respObj)) {
358
+ if (typeof respObj[key] === 'string') {
359
+ respObj[key] = collapseHistoricalOutput(respObj[key], threshold);
360
+ }
371
361
  }
372
362
  }
373
363
  }
@@ -701,7 +691,17 @@ export function getContextToolDeclarations() {
701
691
  },
702
692
  {
703
693
  name: 'run_analysis_script',
704
- description: 'Write and execute a disposable analysis script to structurally map code in the workspace. Use this to get exact line ranges for functions, classes, and variables by leveraging the language\'s native AST parser (e.g., TypeScript compiler API, Python ast module, go/parser). You can also use this to probe the development environment — detecting available runtimes (e.g., node --version, python3 --version), checking if ports are in use, identifying project type (monorepo, package manager), or diagnosing system-level issues (disk space, memory) that may affect execution. For complex investigation tasks, you can write lightweight ML scripts (e.g., TF-IDF cosine similarity to rank file relevance, Z-score outlier detection for anomalous log lines, K-Means clustering, or Naive Bayes classification). Default to "node" for generic math/analysis as a safe baseline, but act like a native inhabitant of the host environment — if Python, Go, Rust, or specialized libraries are available in the project context, leverage the host\'s native runtimes and standard libraries for maximum efficiency. The script is executed from a temporary directory and automatically cleaned up after execution. Output should be structured JSON to stdout. Use the results to make precise read_file calls with exact startLine/endLine instead of guessing. CRITICAL: Do not use this tool on binary, document, or non-code files (e.g. PDF, image, audio, docx).',
694
+ description: 'Write and execute a disposable analysis script to structurally map code in the workspace. ' +
695
+ 'Sandbox Features: ' +
696
+ '(1) Preloaded Helpers: "emitResult(data)" / "__emitResult(data)" sends structured JSON payloads directly to structuredResult without manual stdout parsing; "inspectSymbols(target)" / "inspectObject(target)" inspects functions, classes, and properties. ' +
697
+ '(2) Module & Path Resolution: Automatically inherits tsconfig.json path aliases (@/*), NODE_PATH, and workspace virtualenvs (.venv, venv) for Python. ' +
698
+ '(3) Diagnostics Middleware: Provides actionable [SANDBOX DIAGNOSTIC] advisories for missing imports, ESM/CJS interop, and compiler errors. ' +
699
+ 'Use this to get exact line ranges for functions, classes, and variables by leveraging the language\'s native AST parser (e.g., TypeScript compiler API, Python ast module, go/parser). ' +
700
+ 'You can also use this to probe the development environment — detecting available runtimes (e.g., node --version, python3 --version), checking if ports are in use, identifying project type (monorepo, package manager), or diagnosing system-level issues (disk space, memory) that may affect execution. ' +
701
+ 'For complex investigation tasks, you can write lightweight ML scripts (e.g., TF-IDF cosine similarity to rank file relevance, Z-score outlier detection for anomalous log lines, K-Means clustering, or Naive Bayes classification). ' +
702
+ 'Default to "node" for generic math/analysis as a safe baseline, but act like a native inhabitant of the host environment — if Python, Go, Rust, or specialized libraries are available in the project context, leverage the host\'s native runtimes and standard libraries for maximum efficiency. ' +
703
+ 'The script is executed from a temporary directory and automatically cleaned up after execution. ' +
704
+ 'Use the results to make precise read_file calls with exact startLine/endLine instead of guessing. CRITICAL: Do not use this tool on binary, document, or non-code files (e.g. PDF, image, audio, docx).',
705
705
  parameters: {
706
706
  type: SchemaType.OBJECT,
707
707
  properties: {
@@ -711,7 +711,7 @@ export function getContextToolDeclarations() {
711
711
  },
712
712
  code: {
713
713
  type: SchemaType.STRING,
714
- description: 'The analysis script code. Should output structured JSON to stdout with structural information (name, type, startLine, endLine for each code element).',
714
+ description: 'The analysis script code. Should output structured JSON to stdout or use emitResult(data) with structural information (name, type, startLine, endLine for each code element).',
715
715
  },
716
716
  targetFile: {
717
717
  type: SchemaType.STRING,
@@ -757,13 +757,14 @@ export function createContextAgentSession() {
757
757
  let model = contextModelOverride || getGlobalActiveModel();
758
758
  if (model === 'auto' || model.includes('claude'))
759
759
  model = GEMINI_MODELS.FLASH;
760
+ const thinkingLevel = getModelThinkingLevel(model);
760
761
  return new ProxyChatSession(model, CONTEXT_SYSTEM_INSTRUCTION.replace('{{MULTI_WORKSPACE_BLOCK}}', getMultiWorkspaceBlock()), contextTools, {
761
762
  maxOutputTokens: MAX_OUTPUT_TOKENS,
762
763
  temperature: contextTempOverride !== null ? contextTempOverride : 1,
763
764
  topP: 0.95,
764
765
  topK: 40,
765
766
  thinkingConfig: {
766
- thinkingLevel: 'MEDIUM',
767
+ thinkingLevel,
767
768
  },
768
769
  }, {
769
770
  functionCallingConfig: {
@@ -1108,6 +1109,50 @@ export function createUserProfileExtractorSession() {
1108
1109
  },
1109
1110
  description: 'Observed cognitive decision-making, collaboration, and problem-solving traits',
1110
1111
  },
1112
+ conversationalPersona: {
1113
+ type: SchemaType.OBJECT,
1114
+ properties: {
1115
+ relationshipModel: {
1116
+ type: SchemaType.STRING,
1117
+ description: 'e.g., "collaborative-peer", "command-operator", "rubber-duck", "socratic-explorer"',
1118
+ },
1119
+ banterAffinity: {
1120
+ type: SchemaType.STRING,
1121
+ description: 'e.g., "witty-banter", "dry-professional", "warm-encouraging"',
1122
+ },
1123
+ formalityLevel: {
1124
+ type: SchemaType.STRING,
1125
+ description: 'e.g., "casual-slang", "telegraphic-concise", "polite-cordial"',
1126
+ },
1127
+ apologyTolerance: {
1128
+ type: SchemaType.STRING,
1129
+ description: 'e.g., "zero-apologies", "tolerant-empathetic"',
1130
+ },
1131
+ stressCadence: {
1132
+ type: SchemaType.STRING,
1133
+ description: 'e.g., "urgent-surgical", "calm-exploratory"',
1134
+ },
1135
+ promptingHabit: {
1136
+ type: SchemaType.STRING,
1137
+ description: 'e.g., "code-dump-deducer", "bulleted-architect", "stream-of-consciousness", "rapid-breadcrumbs"',
1138
+ },
1139
+ conversationalQuirks: {
1140
+ type: SchemaType.ARRAY,
1141
+ items: {
1142
+ type: SchemaType.STRING,
1143
+ },
1144
+ description: 'Observed recurring catchphrases, quirks, or unique conversational habits',
1145
+ },
1146
+ removeQuirks: {
1147
+ type: SchemaType.ARRAY,
1148
+ items: {
1149
+ type: SchemaType.STRING,
1150
+ },
1151
+ description: 'Outdated or superseded conversational quirks to prune',
1152
+ },
1153
+ },
1154
+ description: 'Observed interpersonal, psychological, and conversational pairing dynamics',
1155
+ },
1111
1156
  strengths: {
1112
1157
  type: SchemaType.ARRAY,
1113
1158
  items: {
@@ -1,14 +1,20 @@
1
+ import type { TierPartitionResult } from '../utils/contextRanker.js';
2
+ export interface ContextFileEntry {
3
+ text: string;
4
+ inlineData?: any;
5
+ scoped?: boolean;
6
+ tier?: 1 | 2 | 3;
7
+ outline?: string;
8
+ }
1
9
  export interface ContextAgentResult {
2
10
  projectTree: string;
3
11
  projectType: string;
4
- relevantFiles: Map<string, {
5
- text: string;
6
- inlineData?: any;
7
- }>;
12
+ relevantFiles: Map<string, ContextFileEntry>;
8
13
  summary: string;
9
14
  webSearchSummary?: string;
10
15
  fromMemoryBank?: boolean;
11
16
  isParallel?: boolean;
17
+ tierPartition?: TierPartitionResult;
12
18
  }
13
19
  export declare function detectProjectType(workspaceRoot: string): Promise<string>;
14
20
  export interface IntentRoute {