Repository: QwenLM/qwen-code (2025-08-16T00:40:01Z)

Issue:
Unexpectedly scanned entire `.venv` directory, sending ~100M tokens from 5,000 files
### What happened?

While using Qwen Code, I noticed that it automatically scanned the entire `.venv` directory—even though I didn’t instruct it to do so. This resulted in nearly 100 million tokens being sent from approximately 5,000 files. These files are mostly irrelevant to the task and should not have been included.

```
Successfully read and concatenated content from **5472 file(s)**.

**Processed Files (first 10 shown):**
- `scripts/.gitignore`
- `scripts/.venv/.gitignore`
- `scripts/.venv/CACHEDIR.TAG`
- `scripts/.venv/bin/activate`
- `scripts/.venv/bin/activate.bat`
- `scripts/.venv/bin/activate.csh`
- `scripts/.venv/bin/activate.fish`
- `scripts/.venv/bin/activate.nu`
- ...and 5462 more.
```

I canceled the operation when I noticed something was wrong, but the final screen revealed that nearly 100 million tokens had already been processed.

```
╭────────────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                │
│  Agent powering down. Goodbye!                                                                 │
│                                                                                                │
│  Performance                                                                                   │
│  Wall Time:                  14m 18s                                                           │
│  Agent Active:               3m 2s                                                             │
│    » API Time:               3m 2s (100.0%)                                                    │
│    » Tool Time:              0s (0.0%)                                                         │
│                                                                                                │
│                                                                                                │
│  Model Usage                  Reqs   Input Tokens  Output Tokens                               │
│  ───────────────────────────────────────────────────────────────                               │
│  qwen3-coder-plus               11    102,939,617            113                               │
│                                                                                                │
│  Savings Highlight: 11,008 (0.0%) of input tokens were served from the cache, reducing costs.  │
│                                                                                                │
│  » Tip: For a full token breakdown, run `/stats model`.                                        │
│                                                                                                │
╰────────────────────────────────────────────────────────────────────────────────────────────────╯
```


### What did you expect to happen?

I expected Qwen Code to ignore large directories like `.venv` by default, or at least prompt the user before scanning and sending such a massive number of files. Ideally, it should have safeguards to avoid reading directories with excessive file counts or known irrelevant content (e.g., virtual environments, build artifacts).

### Client information


* **CLI Version:** 0.0.5
* **Git Commit:** c09abb81 (local modifications)
* **Operating System:** linux v24.4.1
* **Sandbox Environment:** no sandbox
* **Model Version:** qwen3-coder-plus
* **Memory Usage:** 257.0 MB


### Login information

_No response_

### Anything else we need to know?

_No response_


Discussion:
I have similar issue, where my project have some generated file. I tried to put these file in .geminiignore file, as described in the doc https://github.com/QwenLM/qwen-code/blob/main/docs/gemini-ignore.md but content from these files was still sent to LLM model.



Patch:
diff --git a/packages/core/src/tools/grep.ts b/packages/core/src/tools/grep.ts
index 212b5971c..9070375bd 100644
--- a/packages/core/src/tools/grep.ts
+++ b/packages/core/src/tools/grep.ts
@@ -536,12 +536,18 @@ export class GrepTool extends BaseTool<GrepToolParams, ToolResult> {
       );
       strategyUsed = 'javascript fallback';
       const globPattern = include ? include : '**/*';
+
+      // Get the file discovery service to check ignore patterns
+      const fileDiscovery = this.config.getFileService();
+
+      // Basic ignore patterns
       const ignorePatterns = [
         '.git/**',
         'node_modules/**',
         'bower_components/**',
         '.svn/**',
         '.hg/**',
+        ...fileDiscovery.getGeminiIgnorePatterns(),
       ]; // Use glob patterns for ignores here
 
       const filesIterator = globIterate(globPattern, {


Test patch:
diff --git a/packages/core/src/tools/grep.test.ts b/packages/core/src/tools/grep.test.ts
index aadc93ae1..ec0e57864 100644
--- a/packages/core/src/tools/grep.test.ts
+++ b/packages/core/src/tools/grep.test.ts
@@ -11,6 +11,7 @@ import fs from 'fs/promises';
 import os from 'os';
 import { Config } from '../config/config.js';
 import { createMockWorkspaceContext } from '../test-utils/mockWorkspaceContext.js';
+import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
 
 // Mock the child_process module to control grep/git grep behavior
 vi.mock('child_process', () => ({
@@ -32,9 +33,14 @@ describe('GrepTool', () => {
   let grepTool: GrepTool;
   const abortSignal = new AbortController().signal;
 
+  const mockFileService = {
+    getGeminiIgnorePatterns: () => [],
+  } as unknown as FileDiscoveryService;
+
   const mockConfig = {
     getTargetDir: () => tempRootDir,
     getWorkspaceContext: () => createMockWorkspaceContext(tempRootDir),
+    getFileService: () => mockFileService,
   } as unknown as Config;
 
   beforeEach(async () => {
@@ -220,6 +226,43 @@ describe('GrepTool', () => {
         "Model provided invalid parameters. Error: params must have required property 'pattern'",
       );
     });
+
+    it('should exclude files matching geminiIgnorePatterns', async () => {
+      // Create a file that should be ignored
+      await fs.writeFile(
+        path.join(tempRootDir, 'ignored-file.txt'),
+        'this file should be ignored\nit contains the word world',
+      );
+
+      // Update the mock file service to return ignore patterns
+      mockFileService.getGeminiIgnorePatterns = () => ['ignored-file.txt'];
+
+      // Re-create the grep tool with the updated mock
+      const grepToolWithIgnore = new GrepTool(mockConfig);
+
+      // Search for 'world' which exists in both the regular file and the ignored file
+      const params: GrepToolParams = { pattern: 'world' };
+      const result = await grepToolWithIgnore.execute(params, abortSignal);
+
+      // Should only find matches in the non-ignored files (3 matches)
+      expect(result.llmContent).toContain(
+        'Found 3 matches for pattern "world" in the workspace directory',
+      );
+
+      // Should find matches in the regular files
+      expect(result.llmContent).toContain('File: fileA.txt');
+      expect(result.llmContent).toContain('L1: hello world');
+      expect(result.llmContent).toContain('L2: second line with world');
+      expect(result.llmContent).toContain(
+        `File: ${path.join('sub', 'fileC.txt')}`,
+      );
+      expect(result.llmContent).toContain('L1: another world in sub dir');
+
+      // Should NOT find matches in the ignored file
+      expect(result.llmContent).not.toContain('ignored-file.txt');
+
+      expect(result.returnDisplay).toBe('Found 3 matches');
+    });
   });
 
   describe('multi-directory workspace', () => {
@@ -238,10 +281,15 @@ describe('GrepTool', () => {
       );
 
       // Create a mock config with multiple directories
+      const multiDirFileService = {
+        getGeminiIgnorePatterns: () => [],
+      };
+
       const multiDirConfig = {
         getTargetDir: () => tempRootDir,
         getWorkspaceContext: () =>
           createMockWorkspaceContext(tempRootDir, [secondDir]),
+        getFileService: () => multiDirFileService,
       } as unknown as Config;
 
       const multiDirGrepTool = new GrepTool(multiDirConfig);
@@ -287,10 +335,15 @@ describe('GrepTool', () => {
       );
 
       // Create a mock config with multiple directories
+      const multiDirFileService = {
+        getGeminiIgnorePatterns: () => [],
+      };
+
       const multiDirConfig = {
         getTargetDir: () => tempRootDir,
         getWorkspaceContext: () =>
           createMockWorkspaceContext(tempRootDir, [secondDir]),
+        getFileService: () => multiDirFileService,
       } as unknown as Config;
 
       const multiDirGrepTool = new GrepTool(multiDirConfig);