fix(skills): require unique case-insensitive info matches

This commit is contained in:
NewdlDewdl
2026-05-04 19:04:31 -05:00
parent 9b1fac148e
commit 01f3e2d468
3 changed files with 19 additions and 3 deletions

View File

@@ -3908,6 +3908,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- CLI/skills: require unique case-insensitive fallback matches in `openclaw skills info` so case-only collisions return not-found instead of showing guidance for the wrong skill. (#38713)
- Agents/Ollama: forward the configured embedded-run timeout into the global undici stream timeout tuning so slow local Ollama runs no longer inherit the default stream cutoff instead of the operator-set run timeout. (#63175) Thanks @mindcraftreader and @vincentkoc.
- Models/Codex: include `apiKey` in the codex provider catalog output so the Pi ModelRegistry validator no longer rejects the entry and silently drops all custom models from every provider in `models.json`. (#66180) Thanks @hoyyeva.
- Tools/image+pdf: normalize configured provider/model refs before media-tool registry lookup so image and PDF tool runs stop rejecting valid Ollama vision models as unknown just because the tool path skipped the usual model-ref normalization step. (#59943) Thanks @yqli2420 and @vincentkoc.

View File

@@ -123,11 +123,14 @@ function resolveSkillByName(
}
const lower = raw.toLowerCase();
const caseInsensitive = report.skills.find(
const caseInsensitiveMatches = report.skills.filter(
(s) => s.name.toLowerCase() === lower || s.skillKey.toLowerCase() === lower,
);
if (caseInsensitive) {
return caseInsensitive;
if (caseInsensitiveMatches.length === 1) {
return caseInsensitiveMatches[0] ?? null;
}
if (caseInsensitiveMatches.length > 1) {
return null;
}
const normalized = normalizeSkillLookupToken(raw);

View File

@@ -206,6 +206,18 @@ describe("skills-cli", () => {
expect(output).toContain("Spreadsheet helpers");
});
it("returns not found for ambiguous case-insensitive matches", () => {
const report = createMockReport([
createMockSkill({ name: "First Skill", skillKey: "Excel-XLSX", description: "first" }),
createMockSkill({ name: "Second Skill", skillKey: "excel-xlsx", description: "second" }),
]);
const output = formatSkillInfo(report, "EXCEL-XLSX", {});
expect(output).toContain("not found");
expect(output).not.toContain("first");
expect(output).not.toContain("second");
});
it("returns not found for ambiguous normalized matches", () => {
const report = createMockReport([
createMockSkill({ name: "Excel/XLSX", skillKey: "excel-slash", description: "first" }),