minovative-mind-cli 2.2.5 → 2.3.1

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/LICENSE.md +2 -0
  2. package/README.md +8 -8
  3. package/dist/commands/logout.js +1 -1
  4. package/dist/services/agent/slashCommands.js +53 -14
  5. package/dist/services/agent/toolLoop.js +4 -1
  6. package/dist/services/agent/types.d.ts +4 -0
  7. package/dist/services/agent-tools.js +55 -27
  8. package/dist/services/agent.d.ts +4 -0
  9. package/dist/services/agent.js +77 -60
  10. package/dist/services/ai.d.ts +1 -2
  11. package/dist/services/ai.js +21 -19
  12. package/dist/services/auth.d.ts +1 -1
  13. package/dist/services/auth.js +7 -28
  14. package/dist/services/chatHistoryService.d.ts +8 -0
  15. package/dist/services/contextAgent.js +18 -1
  16. package/dist/services/investigationComplexity.d.ts +1 -1
  17. package/dist/services/investigationComplexity.js +1 -1
  18. package/dist/services/orchestration/investigationAgent.d.ts +2 -2
  19. package/dist/services/orchestration/investigationAgent.js +25 -6
  20. package/dist/services/orchestration/orchestrator.d.ts +1 -1
  21. package/dist/services/orchestration/orchestrator.js +37 -18
  22. package/dist/services/orchestration/subAgent.d.ts +1 -1
  23. package/dist/services/orchestration/subAgent.js +23 -13
  24. package/dist/services/proxyClient.d.ts +16 -14
  25. package/dist/services/proxyClient.js +199 -103
  26. package/dist/utils/analysisRunner.js +2 -2
  27. package/dist/utils/config.d.ts +3 -3
  28. package/dist/utils/config.js +3 -3
  29. package/dist/utils/credentialStore.d.ts +30 -0
  30. package/dist/utils/credentialStore.js +540 -0
  31. package/dist/utils/projectStorage.js +70 -0
  32. package/dist/utils/systemPrompts.d.ts +3 -3
  33. package/dist/utils/systemPrompts.js +4 -4
  34. package/oclif.manifest.json +1 -1
  35. package/package.json +2 -1
package/LICENSE.md CHANGED
@@ -17,6 +17,8 @@ You are expressly prohibited from doing any of the following:
17
17
  - **No Reverse Engineering**: You may not decompile, reverse engineer, disassemble, or otherwise attempt to derive the source code of the compiled portions of the Software.
18
18
  - **No Repurposing**: You may not use any part of the Software's source code in your own projects, whether commercial or non-commercial.
19
19
  - **No Removal of Notices**: You may not remove, alter, or obscure any copyright, trademark, or other proprietary rights notices embedded in or attached to the Software.
20
+ - **Confidentiality**: You acknowledge that the Software contains proprietary, confidential information. You agree to take all reasonable precautions to protect the Software from unauthorized disclosure or access by third parties.
21
+ - **Prohibition on Benchmarking**: You are strictly prohibited from using the Software, or any output generated by the Software, for the purpose of developing, testing, or benchmarking a competing product or service, or for the purpose of training or tuning other machine learning or artificial intelligence models.
20
22
 
21
23
  ## 3. Ownership
22
24
 
package/README.md CHANGED
@@ -86,12 +86,12 @@ Works in any terminal — SSH, Docker, CI pipelines, Vim, Windows Command Prompt
86
86
 
87
87
  Hot-swap during a session using `/models`:
88
88
 
89
- | Model | Best for |
90
- | ------------------------- | ---------------------------------------------- |
91
- | **Auto** (default) | Automatically selects (3.5 Flash or 2.5 Flash) |
92
- | **Gemini 3.5 Flash** | Everyday coding — fast and accurate |
93
- | **Gemini 3.1 Pro** | Complex architectural changes |
94
- | **Gemini 3.1 Flash Lite** | Maximum speed and cost efficiency |
89
+ | Model | Best for |
90
+ | ------------------------- | --------------------------------------------------- |
91
+ | **Auto** (default) | Automatically selects (3.6 Flash or 3.5 Flash Lite) |
92
+ | **Gemini 3.6 Flash** | Everyday coding — fast and accurate |
93
+ | **Gemini 3.1 Pro** | Complex architectural changes |
94
+ | **Gemini 3.5 Flash Lite** | Best for speed and cost efficiency |
95
95
 
96
96
  > Background tasks (routing, compression, commits) always use lightweight
97
97
  > models automatically. You pay for those lightweight model
@@ -115,7 +115,7 @@ Hot-swap during a session using `/models`:
115
115
  | `/stats` | View current session statistics and configuration |
116
116
  | `/commit` | Generate a conventional commit message from your diff |
117
117
  | `/revert` | Undo changes from the last turn, or toggle the revert logger |
118
- | `/chats` | View, resume, or delete previous chat sessions (with token tracking, Git branch, auto-approve status, and sub-agent state) |
118
+ | `/chats` | View, resume, or delete previous chat sessions (with centralized token telemetry, Git branch, auto-approve status, and sub-agent state) |
119
119
  | `/workspaces` | Manage active workspaces and linked cross-repo aliases |
120
120
  | `stop` | Abort generation immediately |
121
121
 
@@ -155,7 +155,7 @@ _⚠️\*Support is limited or requires custom local system tooling/environment
155
155
 
156
156
  1. **Dependency Tracing:** Fully supported for all 11 languages. 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.
157
157
  2. **Performance Auditing:** Uses regex-based heuristic analysis to scan for issues. Extending this to complex grammars (such as Ruby or Swift) with pure regex can cause high false-positive rates; those require full AST parsing, which runs counter to the auditor's ultra-fast (<50ms) and zero-dependency goals.
158
- 3. **Ephemeral Analysis Scripts:** Runs temporary files in sandbox directories to parse and extract local symbols. This requires having local runtimes/interpreters installed (e.g., Node, Python, Rust, Go compilers) and standard single-file execution support, which is not suitable for complex build setups (Java Maven/Gradle, C++ Makefiles) or non-executable languages (CSS).
158
+ 3. **Ephemeral Analysis Scripts:** Runs temporary files in sandbox directories to parse and extract local symbols. Also used for infrastructure probing (detecting available runtimes, checking ports, identifying project type), post-change validation (importing modified modules and asserting expected behavior), and lightweight ML-powered analysis (e.g., TF-IDF file ranking, anomaly detection, Big-O estimation). 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.
159
159
 
160
160
  ---
161
161
 
@@ -6,7 +6,7 @@ export default class LogoutCommand extends Command {
6
6
  static description = 'Sign out of your Minovative Mind account';
7
7
  async run() {
8
8
  p.intro(`${pc.bgCyan(pc.black(' Minovative Mind '))} - Logout`);
9
- logout();
9
+ await logout();
10
10
  p.outro(pc.green('You have been signed out.'));
11
11
  }
12
12
  }
@@ -11,7 +11,8 @@ import { chatHistoryService } from '../chatHistoryService.js';
11
11
  import { printLogo, brandBg, brandFg } from '../../utils/logo.js';
12
12
  import { readPaste } from '../../utils/paste.js';
13
13
  import { setApprovalMode, getApprovalMode, isSubAgentsEnabled, setSubAgentsEnabled } from '../agent-tools.js';
14
- import { ProxyChatSession, setGlobalActiveModel, getGlobalActiveModel, getGlobalLatestUsageMetadata } from '../ai.js';
14
+ import { ProxyChatSession, setGlobalActiveModel, getGlobalActiveModel, } from '../ai.js';
15
+ import { GEMINI_MODELS } from '../../utils/config.js';
15
16
  const execAsync = promisify(exec);
16
17
  /**
17
18
  * Handles all slash command operations (/paste, /clear, /models, /debug, /auto-approve, /revert, /commit).
@@ -70,22 +71,22 @@ export async function handleSlashCommand(command, context) {
70
71
  {
71
72
  value: 'gemini-3.1-pro-preview',
72
73
  label: 'Gemini 3.1 Pro',
73
- hint: 'The newest Pro model for complex logic',
74
+ hint: 'For hardcore & complex problems',
74
75
  },
75
76
  {
76
- value: 'gemini-3.5-flash',
77
- label: 'Gemini 3.5 Flash',
78
- hint: 'Balanced performance',
77
+ value: 'gemini-3.6-flash',
78
+ label: 'Gemini 3.6 Flash',
79
+ hint: 'Balanced performance & fast',
79
80
  },
80
81
  {
81
- value: 'gemini-3.1-flash-lite',
82
- label: 'Gemini 3.1 Flash-Lite',
83
- hint: 'Ultra-fast and cost-effective',
82
+ value: 'gemini-3.5-flash-lite',
83
+ label: 'Gemini 3.5 Flash-Lite',
84
+ hint: 'Best for raw speed and cost efficiency',
84
85
  },
85
86
  {
86
87
  value: 'auto',
87
88
  label: 'Auto (Flash-Lite / Flash)',
88
- hint: 'Dynamically routes between Gemini 3.1 Flash-Lite and Gemini 3.5 based on prompt complexity',
89
+ hint: 'Dynamically routes between Gemini 3.5 Flash-Lite and Gemini 3.6 based on prompt complexity',
89
90
  },
90
91
  ],
91
92
  });
@@ -129,7 +130,8 @@ export async function handleSlashCommand(command, context) {
129
130
  return { shouldContinue: true };
130
131
  }
131
132
  if (lowerCommand === '/stats') {
132
- const latestUsage = getGlobalLatestUsageMetadata();
133
+ const latestUsage = context.chatSessionState?.latestUsageMetadata;
134
+ const prevUsage = context.chatSessionState?.previousUsageMetadata;
133
135
  const currentModel = chat.getModel();
134
136
  const globalModel = getGlobalActiveModel();
135
137
  const displayModel = globalModel === 'auto' ? `Auto (Last turn: ${currentModel})` : currentModel;
@@ -165,15 +167,45 @@ export async function handleSlashCommand(command, context) {
165
167
  }
166
168
  const totalInputTokens = (latestUsage.promptTokens || 0) + (latestUsage.cachedTokens || 0);
167
169
  if (totalInputTokens > 0) {
168
- console.log(`${pc.bold('Last Turn Input:')} ${totalInputTokens.toLocaleString()} tokens`);
170
+ let inputDiffStr = '';
171
+ if (prevUsage) {
172
+ const prevTotalInputTokens = (prevUsage.promptTokens || 0) + (prevUsage.cachedTokens || 0);
173
+ const inputDiff = totalInputTokens - prevTotalInputTokens;
174
+ if (inputDiff > 0) {
175
+ inputDiffStr = ` ${pc.yellow(`(+${inputDiff.toLocaleString()})`)}`;
176
+ }
177
+ else if (inputDiff < 0) {
178
+ inputDiffStr = ` ${pc.green(`(${inputDiff.toLocaleString()})`)}`;
179
+ }
180
+ else {
181
+ inputDiffStr = ` ${pc.dim(`(0)`)}`;
182
+ }
183
+ }
184
+ console.log(`${pc.bold('Last Turn Input:')} ${totalInputTokens.toLocaleString()} tokens${inputDiffStr}`);
169
185
  }
170
186
  if (latestUsage.candidatesTokens) {
171
- console.log(`${pc.bold('Last Turn Output:')} ${latestUsage.candidatesTokens.toLocaleString()} tokens`);
187
+ let outputDiffStr = '';
188
+ if (prevUsage && prevUsage.candidatesTokens !== undefined) {
189
+ const outputDiff = latestUsage.candidatesTokens - prevUsage.candidatesTokens;
190
+ if (outputDiff > 0) {
191
+ outputDiffStr = ` ${pc.yellow(`(+${outputDiff.toLocaleString()})`)}`;
192
+ }
193
+ else if (outputDiff < 0) {
194
+ outputDiffStr = ` ${pc.green(`(${outputDiff.toLocaleString()})`)}`;
195
+ }
196
+ else {
197
+ outputDiffStr = ` ${pc.dim(`(0)`)}`;
198
+ }
199
+ }
200
+ console.log(`${pc.bold('Last Turn Output:')} ${latestUsage.candidatesTokens.toLocaleString()} tokens${outputDiffStr}`);
172
201
  }
173
202
  }
174
203
  else {
175
204
  console.log(`${pc.bold('Token Usage:')} No data available yet.`);
176
205
  }
206
+ if (context.chatSessionState?.totalTokens > 0) {
207
+ console.log(`${pc.bold('Session Total:')} ${context.chatSessionState.totalTokens.toLocaleString()} tokens`);
208
+ }
177
209
  console.log(pc.dim('----------------------------------------\n'));
178
210
  return { shouldContinue: true };
179
211
  }
@@ -306,6 +338,8 @@ export async function handleSlashCommand(command, context) {
306
338
  chatSessionState.id = crypto.randomUUID();
307
339
  chatSessionState.title = '';
308
340
  chatSessionState.totalTokens = 0;
341
+ chatSessionState.totalInputTokens = 0;
342
+ chatSessionState.totalOutputTokens = 0;
309
343
  chat.setSessionInfo(chatSessionState.id, workspaceRoot);
310
344
  }
311
345
  process.stdout.write('\x1B[2J\x1B[3J\x1B[H'); // Hard clear screen and scrollback
@@ -367,6 +401,10 @@ export async function handleSlashCommand(command, context) {
367
401
  chatSessionState.id = session.id;
368
402
  chatSessionState.title = session.title;
369
403
  chatSessionState.totalTokens = session.totalTokens || 0;
404
+ chatSessionState.totalInputTokens = session.totalInputTokens || 0;
405
+ chatSessionState.totalOutputTokens = session.totalOutputTokens || 0;
406
+ chatSessionState.latestUsageMetadata = session.latestUsageMetadata;
407
+ chatSessionState.previousUsageMetadata = session.previousUsageMetadata;
370
408
  chat.setSessionInfo(session.id, workspaceRoot);
371
409
  }
372
410
  process.stdout.write('\x1B[2J\x1B[3J\x1B[H'); // Hard clear screen and scrollback
@@ -457,7 +495,8 @@ export async function handleSlashCommand(command, context) {
457
495
  p.log.info(`${pc.dim('Model:')} ${pc.cyan(session.modelName)}`);
458
496
  }
459
497
  if (session.totalTokens !== undefined) {
460
- p.log.info(`${pc.dim('Session Token Usage:')} ${pc.cyan(session.totalTokens.toLocaleString() + ' total tokens')}`);
498
+ const inputStr = session.totalInputTokens ? ` (Input: ${session.totalInputTokens.toLocaleString()}, Output: ${(session.totalOutputTokens || 0).toLocaleString()})` : '';
499
+ p.log.info(`${pc.dim('Session Token Usage:')} ${pc.cyan(session.totalTokens.toLocaleString() + ' total tokens')}${pc.dim(inputStr)}`);
461
500
  }
462
501
  if (session.gitBranch) {
463
502
  p.log.info(`${pc.dim('Last Active Branch:')} ${pc.cyan(session.gitBranch)}`);
@@ -664,7 +703,7 @@ export async function handleSlashCommand(command, context) {
664
703
  ${diffOut}
665
704
  </diff>`;
666
705
  const commitSystemPrompt = 'You are an expert developer. Output only the git commit message, no markdown formatting, no explanations. Follow Conventional Commits format (feat:, fix:, chore:, refactor:, etc.) for the first line, keeping it under 70 characters and using the imperative mood. Then, add a blank line followed by a more descriptive bulleted list explaining the "what" and "why" of the changes based on the diff.';
667
- const commitAgent = new ProxyChatSession('gemini-3.1-flash-lite', commitSystemPrompt, [], {});
706
+ const commitAgent = new ProxyChatSession(GEMINI_MODELS.FLASH_LITE, commitSystemPrompt, [], {});
668
707
  const commitResult = await commitAgent.sendMessage(prompt);
669
708
  const commitMsg = commitResult.response.text().trim();
670
709
  commitSpinner.message('Committing...');
@@ -23,6 +23,7 @@ const TOOL_ICONS = {
23
23
  rename_file: '🚚',
24
24
  find_dependencies: '🔗',
25
25
  run_analysis_script: '🔬',
26
+ run_debug_script: '🧪',
26
27
  };
27
28
  /**
28
29
  * Human-readable translations for tool actions.
@@ -38,7 +39,8 @@ const TOOL_LABELS = {
38
39
  delete_file: 'Deleting file',
39
40
  rename_file: 'Moving file',
40
41
  find_dependencies: 'Tracing dependencies',
41
- run_analysis_script: 'Analyzing code structure',
42
+ run_analysis_script: 'Analyzing workspace',
43
+ run_debug_script: 'Executing debug/validation script',
42
44
  };
43
45
  // ─── Helpers ─────────────────────────────────────────────────────────
44
46
  /**
@@ -74,6 +76,7 @@ export function formatToolCall(name, args) {
74
76
  rename_file: () => `: ${formatPath(String(args.sourcePath))} -> ${formatPath(String(args.targetPath))}`,
75
77
  find_dependencies: () => `: ${formatPath(String(args.filePath))}${args.direction ? ` (${args.direction})` : ''}`,
76
78
  run_analysis_script: () => `: ${formatPath(String(args.targetFile ?? 'workspace'))}`,
79
+ run_debug_script: () => `: ${pc.dim(`(${String(args.language ?? 'script')})`)}`,
77
80
  };
78
81
  const formatter = argsMap[name];
79
82
  const details = formatter ? formatter() : '';
@@ -14,6 +14,10 @@ export interface SlashCommandContext {
14
14
  id: string;
15
15
  title: string;
16
16
  totalTokens: number;
17
+ totalInputTokens: number;
18
+ totalOutputTokens: number;
19
+ latestUsageMetadata?: any;
20
+ previousUsageMetadata?: any;
17
21
  };
18
22
  }
19
23
  export interface SlashCommandResult {
@@ -16,6 +16,7 @@ import { EXCLUDED_EXTENSIONS } from '../utils/excludedExtensions.js';
16
16
  import { extractSymbols } from '../utils/symbolExtractor.js';
17
17
  import { getMetricCollector } from './metrics.js';
18
18
  import { getCurrentAgentId } from '../utils/asyncContext.js';
19
+ import ignore from 'ignore';
19
20
  const execAsync = promisify(exec);
20
21
  // ─── Tool Declarations for Gemini Function Calling ───────────────────
21
22
  export function getToolDeclarations(options) {
@@ -237,7 +238,14 @@ export const toolDeclarations = [
237
238
  },
238
239
  {
239
240
  name: 'run_debug_script',
240
- description: 'Write a debug script (using console.log, print, etc.) to a temporary file, execute it using the specified runtime, and return the exact standard output and standard error. Use this to actively debug the codebase, inspect variables, or solve problems during execution.',
241
+ description: 'Write a disposable script to a temporary file, execute it using the specified runtime, and return the exact standard output and standard error. ' +
242
+ 'Use this to: (1) actively debug the codebase by inspecting variables or logging values, ' +
243
+ '(2) validate your changes by importing the modified module and asserting expected behavior with edge-case inputs, ' +
244
+ '(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), ' +
245
+ '(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, ' +
246
+ 'output regression detection via statistical similarity scoring, or error message classification with Naive Bayes. ' +
247
+ '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. ' +
248
+ 'Do not guess whether your code works — test it directly!',
241
249
  parameters: {
242
250
  type: SchemaType.OBJECT,
243
251
  properties: {
@@ -325,34 +333,50 @@ const DEFAULT_IGNORED_DIRS = new Set([
325
333
  ]);
326
334
  const DEFAULT_IGNORED_FILES = new Set(['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', '.DS_Store']);
327
335
  /**
328
- * Parses .gitignore to supplement the default ignore lists.
336
+ * Parses .gitignore and .minovativemindignore to supplement the default ignore lists.
329
337
  */
330
338
  async function getIgnoredPaths(workspaceRoot) {
331
339
  const ignoredDirs = new Set(DEFAULT_IGNORED_DIRS);
332
340
  const ignoredFiles = new Set(DEFAULT_IGNORED_FILES);
333
- try {
334
- const gitignoreContent = await fs.readFile(path.join(workspaceRoot, '.gitignore'), 'utf-8');
335
- const lines = gitignoreContent
336
- .split('\n')
337
- .map((l) => l.trim())
338
- .filter((l) => l && !l.startsWith('#'));
339
- for (const line of lines) {
340
- const cleanLine = line.replace(/\\/g, '/').replace(/^\//, '').replace(/\/$/, '');
341
- if (!cleanLine.includes('*')) {
342
- ignoredDirs.add(cleanLine);
343
- ignoredFiles.add(cleanLine);
341
+ const ig = ignore();
342
+ ig.add(Array.from(DEFAULT_IGNORED_DIRS).map(d => d + '/'));
343
+ ig.add(Array.from(DEFAULT_IGNORED_FILES));
344
+ const filesToRead = ['.gitignore', '.minovativemindignore'];
345
+ for (const ignoreFile of filesToRead) {
346
+ try {
347
+ const content = await fs.readFile(path.join(workspaceRoot, ignoreFile), 'utf-8');
348
+ ig.add(content);
349
+ const lines = content
350
+ .split('\n')
351
+ .map((l) => l.trim())
352
+ .filter((l) => l && !l.startsWith('#'));
353
+ for (const line of lines) {
354
+ const cleanLine = line.replace(/\\/g, '/').replace(/^\//, '').replace(/\/$/, '');
355
+ if (!cleanLine.includes('*')) {
356
+ ignoredDirs.add(cleanLine);
357
+ ignoredFiles.add(cleanLine);
358
+ }
344
359
  }
345
360
  }
361
+ catch {
362
+ // Ignore missing files
363
+ }
346
364
  }
347
- catch {
348
- // Ignore missing .gitignore
349
- }
350
- return { ignoredDirs, ignoredFiles };
365
+ return { ignoredDirs, ignoredFiles, ig };
351
366
  }
352
367
  // ─── Tool Implementations ────────────────────────────────────────────
353
368
  export async function readFile(workspaceRoot, filePath, startLine, endLine, targetElements) {
354
369
  try {
355
370
  const absPath = resolveAndValidatePath(workspaceRoot, filePath);
371
+ // Check if file is explicitly ignored
372
+ const relPath = path.relative(workspaceRoot, absPath).replace(/\\/g, '/');
373
+ const { ig } = await getIgnoredPaths(workspaceRoot);
374
+ if (ig.ignores(relPath)) {
375
+ return {
376
+ output: '',
377
+ error: `File '${relPath}' is ignored by .gitignore or .minovativemindignore. The agent cannot read it. Please explore other files.`,
378
+ };
379
+ }
356
380
  // Handle PDF files
357
381
  if (filePath.toLowerCase().endsWith('.pdf')) {
358
382
  const stats = await fs.stat(absPath);
@@ -698,7 +722,7 @@ export async function listDirectory(workspaceRoot, dirPath, maxDepth = 3) {
698
722
  try {
699
723
  const absPath = resolveAndValidatePath(workspaceRoot, dirPath);
700
724
  const lines = [];
701
- const { ignoredDirs, ignoredFiles } = await getIgnoredPaths(workspaceRoot);
725
+ const { ig } = await getIgnoredPaths(workspaceRoot);
702
726
  async function walk(currentPath, prefix, depth) {
703
727
  if (depth > maxDepth)
704
728
  return;
@@ -721,7 +745,7 @@ export async function listDirectory(workspaceRoot, dirPath, maxDepth = 3) {
721
745
  const entryPath = path.join(currentPath, entry.name);
722
746
  const relPath = path.relative(workspaceRoot, entryPath).replace(/\\/g, '/');
723
747
  if (entry.isDirectory()) {
724
- if (ignoredDirs.has(entry.name) || ignoredDirs.has(relPath) || ignoredDirs.has(`${relPath}/`)) {
748
+ if (ig.ignores(relPath + '/')) {
725
749
  lines.push(`${prefix}${connector}${entry.name}/ (ignored)`);
726
750
  continue;
727
751
  }
@@ -729,7 +753,7 @@ export async function listDirectory(workspaceRoot, dirPath, maxDepth = 3) {
729
753
  await walk(entryPath, `${prefix}${childPrefix}`, depth + 1);
730
754
  }
731
755
  else {
732
- if (ignoredFiles.has(entry.name) || ignoredFiles.has(relPath))
756
+ if (ig.ignores(relPath))
733
757
  continue;
734
758
  if (EXCLUDED_EXTENSIONS.some((ext) => entry.name.endsWith(ext.replace('*', ''))))
735
759
  continue;
@@ -779,7 +803,7 @@ export async function runCommand(workspaceRoot, command, abortSignal) {
779
803
  }
780
804
  export async function grepSearch(workspaceRoot, pattern, fileGlob, abortSignal) {
781
805
  try {
782
- const { ignoredDirs } = await getIgnoredPaths(workspaceRoot);
806
+ const { ignoredDirs, ig } = await getIgnoredPaths(workspaceRoot);
783
807
  if (os.platform() !== 'win32') {
784
808
  const excludeDirArgs = Array.from(ignoredDirs)
785
809
  .map((dir) => `--exclude-dir="${dir}"`)
@@ -828,12 +852,16 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob, abortSignal)
828
852
  return;
829
853
  if (entry.name.startsWith('.'))
830
854
  continue;
855
+ const entryPath = path.join(dir, entry.name);
856
+ const relPath = path.relative(workspaceRoot, entryPath).replace(/\\/g, '/');
831
857
  if (entry.isDirectory()) {
832
- if (ignoredDirs.has(entry.name))
858
+ if (ig.ignores(relPath + '/'))
833
859
  continue;
834
- await walk(path.join(dir, entry.name));
860
+ await walk(entryPath);
835
861
  }
836
862
  else if (entry.isFile()) {
863
+ if (ig.ignores(relPath))
864
+ continue;
837
865
  if (EXCLUDED_EXTENSIONS.some((ext) => entry.name.endsWith(ext.replace('*', ''))))
838
866
  continue;
839
867
  if (globPattern && !globPattern.test(entry.name))
@@ -898,7 +926,7 @@ export async function traceDependencies(workspaceRoot, filePath, direction, maxD
898
926
  export async function findRecentChanges(workspaceRoot, dirPath = '.', minutes = 60, maxDepth = 5) {
899
927
  try {
900
928
  const absPath = resolveAndValidatePath(workspaceRoot, dirPath);
901
- const { ignoredDirs, ignoredFiles } = await getIgnoredPaths(workspaceRoot);
929
+ const { ig } = await getIgnoredPaths(workspaceRoot);
902
930
  const thresholdMs = Date.now() - minutes * 60 * 1000;
903
931
  const recentFiles = [];
904
932
  async function walk(currentPath, depth) {
@@ -917,12 +945,12 @@ export async function findRecentChanges(workspaceRoot, dirPath = '.', minutes =
917
945
  const entryPath = path.join(currentPath, entry.name);
918
946
  const relPath = path.relative(workspaceRoot, entryPath).replace(/\\/g, '/');
919
947
  if (entry.isDirectory()) {
920
- if (ignoredDirs.has(entry.name) || ignoredDirs.has(relPath) || ignoredDirs.has(`${relPath}/`))
948
+ if (ig.ignores(relPath + '/'))
921
949
  continue;
922
950
  await walk(entryPath, depth + 1);
923
951
  }
924
952
  else {
925
- if (ignoredFiles.has(entry.name) || ignoredFiles.has(relPath))
953
+ if (ig.ignores(relPath))
926
954
  continue;
927
955
  if (EXCLUDED_EXTENSIONS.some((ext) => entry.name.endsWith(ext.replace('*', ''))))
928
956
  continue;
@@ -1009,7 +1037,7 @@ export async function runDebugScript(workspaceRoot, language, code, abortSignal)
1009
1037
  return { output: '', error: `Unsupported language runtime: ${language}` };
1010
1038
  }
1011
1039
  try {
1012
- const { stdout, stderr } = await execAsync(cmd, { cwd: workspaceRoot, timeout: 15_000, signal: abortSignal });
1040
+ const { stdout, stderr } = await execAsync(cmd, { cwd: workspaceRoot, timeout: 60_000, signal: abortSignal });
1013
1041
  const out = stdout.trim();
1014
1042
  const errOut = stderr.trim();
1015
1043
  let finalOutput = '';
@@ -53,6 +53,10 @@ export declare function executeSingleTurn(workspaceRoot: string, userInput: stri
53
53
  id: string;
54
54
  title: string;
55
55
  totalTokens: number;
56
+ totalInputTokens: number;
57
+ totalOutputTokens: number;
58
+ latestUsageMetadata?: any;
59
+ previousUsageMetadata?: any;
56
60
  }, isPlanMode: boolean, cachedContextResult?: any): Promise<{
57
61
  planModeReturn?: string;
58
62
  contextResult?: any;