google-gemini / google-gemini/gemini-cli
[Bug] SkillManager precedence and active state tracking fails due to case-sensitivity mismatch
- 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
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