google-gemini / google-gemini/gemini-cli

[Bug] SkillManager precedence and active state tracking fails due to case-sensitivity mismatch

Open
#29,150 1 comment 0 reactions 0 assignees View on GitHub
area/agent status/need-triage
Dominant language
TypeScript
Stars
107k
Forks
14.6k
Avg merge
2d 3h
Merged PRs (30d)
45

Description

### What happened?

In `@google/gemini-cli-core`, the `SkillManager` handles skill lookup (`getSkill`) and disabling (`setDisabledSkills`) case-insensitively using `.toLowerCase()`. However, `SkillManager.addSkillsWithPrecedence` indexes existing skills into a `Map` using raw, case-sensitive names (`s.name`).

If a higher-precedence skill (e.g., a workspace or user skill) differs in casing from a lower-precedence skill (e.g., a built-in or extension skill, such as `Git-Commit` vs `git-commit`), `skillMap.get(newSkill.name)` returns `undefined`.

This causes two bugs:
1. **Precedence Override Failure:** Both skills are retained in `this.skills`. When `getSkill(name)` is called, it performs `this.skills.find(s => s.name.toLowerCase() === lowercaseName)`. Because `Array.prototype.find` returns the *first* matching element in discovery order (where built-in and extension skills were added first), `getSkill` returns the lower-precedence skill, completely ignoring the user/workspace override.
2. **Active State Tracking Failure:** `SkillManager.activateSkill(name)` and `SkillManager.isSkillActive(name)` insert into and query `this.activeSkillNames` (`Set`) without lowercase normalization. If the model or caller invokes `activate_skill` with different casing than the canonical definition, `isSkillActive` returns `false`.

### What did you expect to happen?

1. When a user or workspace defines a skill with the same name (regardless of letter casing, e.g. `my-skill` overriding `My-Skill`), the higher-precedence skill should override the lower-precedence one, and `getSkill()` should return the higher-precedence skill.
2. `isSkillActive()` should return `true` regardless of casing variations when activating or checking active skill status.

### Client information

- **Platform:** Windows / macOS / Linux
- **Package:** `@google/gemini-cli-core`

### Login information

NAN

### Anything else we need to know?

### Root Cause & Suggested Fix

In `packages/core/src/skills/skillManager.ts`:

1. In `addSkillsWithPrecedence`, normalize map keys to lowercase:
```typescript
const skillMap = new Map(
this.skills.map((s) => [s.name.toLowerCase(), s]),
);

for (const newSkill of newSkills) {
const key = newSkill.name.toLowerCase();
const existingSkill = skillMap.get(key);
// ... conflict warnings ...
skillMap.set(key, newSkill);
}

Contributor guide

Open the contributing guide

Research direction

Start in packages/core/src/skills/skillManager.ts, reading addSkillsWithPrecedence, getSkill, activateSkill, and isSkillActive. Trace how skill names enter the Map and activeSkillNames, then verify that differently cased names select the higher-precedence definition and report the same active state. Done means both behaviors work consistently for casing variations without retaining an ineffective lower-precedence match.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.