Skip to content

fix(skills): respect .gitignore/.geminiignore in skill resource listing#28149

Open
abhay-codes07 wants to merge 1 commit into
google-gemini:mainfrom
abhay-codes07:fix/skill-folder-structure-respect-ignores-27205
Open

fix(skills): respect .gitignore/.geminiignore in skill resource listing#28149
abhay-codes07 wants to merge 1 commit into
google-gemini:mainfrom
abhay-codes07:fix/skill-folder-structure-respect-ignores-27205

Conversation

@abhay-codes07

Copy link
Copy Markdown

Summary

When a skill is activated, its folder structure is shared with the model as the skill's "available resources". That listing is built by getFolderStructure() in activate-skill.ts, but it was called without a file service:

this.cachedFolderStructure = await getFolderStructure(path.dirname(skillLocation));

Without a fileService, getFolderStructure() only skips a small hardcoded set of folders (node_modules, .git, dist, __pycache__) and does not apply .gitignore/.geminiignore. So an ignored directory inside a skill — most commonly a Python .venv created by tools like uv — gets fully enumerated and sent to the model. This wastes context (thousands of irrelevant library files) and instantly hits the 200-item display limit, hiding the real source files (issue #27205).

The workspace directory structure already does this correctly in environmentContext.ts:

getFolderStructure(dir, { fileService: config.getFileService() })

Fix

Pass the file service when building the skill's folder structure, so it honors the same .gitignore/.geminiignore rules as the rest of the CLI:

this.cachedFolderStructure = await getFolderStructure(
  path.dirname(skillLocation),
  { fileService: this.config.getFileService() },
);

getFolderStructure defaults fileFilteringOptions to DEFAULT_FILE_FILTERING_OPTIONS (respectGitIgnore: true, respectGeminiIgnore: true), so passing the file service alone is sufficient.

Testing

  • Added a unit test asserting activate-skill calls getFolderStructure with the fileService (the wiring that was missing). Combined with the existing getFolderStructure gitignore/geminiignore tests (which prove { fileService } causes ignored paths to be excluded), this verifies the full chain.
  • Verified at runtime against the exact reported scenario: a skill containing scripts/.venv/... with .venv/ in .geminiignore.
    • Before: .venv is expanded, leaking pyvenv.cfg and lib/big.py.
    • After: .venv\... is collapsed and its contents are excluded.

eslint and tsc --noEmit are clean for the package.

Fixes #27205

When a skill is activated, its folder structure (shared with the model
as "available resources") was built with `getFolderStructure()` without a
file service, so .gitignore/.geminiignore were not applied. Ignored
directories inside a skill — most notably a Python `.venv` created by
tools like `uv` — were enumerated and sent to the model, wasting context
and hitting the 200-item display limit.

Pass `config.getFileService()` to `getFolderStructure()`, matching how
the workspace directory structure is already built in
`environmentContext.ts`, so the skill listing honors the same ignore
rules.

Fixes google-gemini#27205
@abhay-codes07 abhay-codes07 requested a review from a team as a code owner June 25, 2026 19:20
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an issue where skill resource listing failed to honor ignore files, leading to the inclusion of unnecessary files in the model's context. By ensuring the file service is properly passed during the folder structure generation, the system now consistently filters files based on project-specific ignore configurations, improving both performance and model relevance.

Highlights

  • Resource Filtering: Updated the skill activation process to correctly pass the file service to the folder structure utility, ensuring that .gitignore and .geminiignore rules are respected.
  • Context Optimization: Prevents irrelevant files (such as Python .venv directories) from being included in the model's context, resolving issues with token limits and display constraints.
  • Test Coverage: Added a unit test to verify that the file service is correctly injected during the skill resource listing process.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added the size/s A small PR label Jun 25, 2026
@github-actions

Copy link
Copy Markdown

📊 PR Size: size/S

  • Lines changed: 32
  • Additions: +32
  • Deletions: -0
  • Files changed: 2

@github-actions

Copy link
Copy Markdown

🛑 Action Required: Evaluation Approval

Steering changes have been detected in this PR. To prevent regressions, a maintainer must approve the evaluation run before this PR can be merged.

Maintainers:

  1. Go to the Workflow Run Summary.
  2. Click the yellow 'Review deployments' button.
  3. Select the 'eval-gate' environment and click 'Approve'.

Once approved, the evaluation results will be posted here automatically.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates ActivateSkillTool to pass the FileDiscoveryService when retrieving a skill's folder structure, ensuring that ignored directories (like .venv) are respected. The review feedback recommends dynamically instantiating a new FileDiscoveryService when the skill is located outside the workspace root to correctly honor local ignore files, and mocking getProjectRoot in the tests to support this. Additionally, the reviewer suggests using the public shouldConfirmExecute method in tests instead of casting the invocation to access protected methods.

Comment on lines 64 to 73
if (this.cachedFolderStructure === undefined) {
this.cachedFolderStructure = await getFolderStructure(
path.dirname(skillLocation),
// Pass the file service so the skill's folder structure honors
// .gitignore/.geminiignore (matching how the workspace structure is
// built). Without it, ignored directories such as a Python `.venv`
// are shared with the model, wasting context. See issue #27205.
{ fileService: this.config.getFileService() },
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using the workspace's FileDiscoveryService for skills located outside the workspace root (such as extension skills) will fail to respect their local .gitignore or .geminiignore files. This is because the workspace FileDiscoveryService is rooted at the project directory and only loads ignore files from there. For external skills, any .venv or other ignored directories will not be filtered out, causing them to be fully shared with the model and wasting context.

To fix this, we should check if the skill directory is inside the workspace. If it is not, we can dynamically instantiate a new FileDiscoveryService rooted at the skill's directory.

    if (this.cachedFolderStructure === undefined) {
      const skillDir = path.dirname(skillLocation);
      const workspaceRoot = this.config.getProjectRoot();
      const { isSubpath } = await import('../utils/paths.js');
      const { FileDiscoveryService } = await import('../services/fileDiscoveryService.js');

      const fileService = isSubpath(workspaceRoot, skillDir)
        ? this.config.getFileService()
        : new FileDiscoveryService(skillDir);

      this.cachedFolderStructure = await getFolderStructure(
        skillDir,
        { fileService },
      );
    }

Comment on lines +58 to 59
getFileService: vi.fn().mockReturnValue(mockFileService),
} as unknown as Config;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Since the updated implementation of ActivateSkillTool calls getProjectRoot() to determine if the skill is inside the workspace, we need to mock getProjectRoot in the test configuration to prevent runtime errors during test execution.

Suggested change
getFileService: vi.fn().mockReturnValue(mockFileService),
} as unknown as Config;
getFileService: vi.fn().mockReturnValue(mockFileService),
getProjectRoot: vi.fn().mockReturnValue('/path/to/test-skill'),
} as unknown as Config;

Comment on lines +88 to +104
it('builds the resource list with the file service so ignored paths (e.g. .venv) are honored', async () => {
const params = { name: 'test-skill' };
const invocation = tool.build(params);
await (
invocation as unknown as {
getConfirmationDetails: (signal: AbortSignal) => Promise<unknown>;
}
).getConfirmationDetails(new AbortController().signal);

// The skill's folder structure must be built with the file service, so
// that .gitignore/.geminiignore are respected (issue #27205). Without it,
// ignored directories are shared with the model.
expect(getFolderStructure).toHaveBeenCalledWith(
'/path/to/test-skill',
expect.objectContaining({ fileService: mockFileService }),
);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Instead of casting the invocation to unknown to call the protected getConfirmationDetails method, we can use the public shouldConfirmExecute method with the 'ask_user' decision. This is cleaner, safer, and avoids bypassing TypeScript's access controls.

  it('builds the resource list with the file service so ignored paths (e.g. .venv) are honored', async () => {
    const params = { name: 'test-skill' };
    const invocation = tool.build(params);
    await invocation.shouldConfirmExecute(
      new AbortController().signal,
      'ask_user',
    );

    // The skill's folder structure must be built with the file service, so
    // that .gitignore/.geminiignore are respected (issue #27205). Without it,
    // ignored directories are shared with the model.
    expect(getFolderStructure).toHaveBeenCalledWith(
      '/path/to/test-skill',
      expect.objectContaining({ fileService: mockFileService }),
    );
  });
References
  1. If using internal or undocumented SDK properties is unavoidable for critical functionality (e.g., security), define a local interface for those properties and add a detailed comment explaining the rationale and why a public API could not be used.

@gemini-cli gemini-cli Bot added priority/p2 Important but can be addressed in a future release. area/agent Issues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality labels Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/agent Issues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality priority/p2 Important but can be addressed in a future release. size/s A small PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GEMINI CLI aggressively scans .venv in custom skills (ignores .gitignore / .geminiignore)

1 participant