Merge 749928c35487d55100477496ba05ffc40b09be2d into 598f1826d8b2bc969aace2c6459824737667218c

This commit is contained in:
Rohin 2026-03-20 22:17:17 -05:00 committed by GitHub
commit 0f45e42fd5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 69 additions and 8 deletions

View File

@ -94,6 +94,52 @@ function formatSkillMissingSummary(skill: SkillStatusEntry): string {
return missing.join("; ");
}
function normalizeSkillLookupToken(value: string): string {
return value
.trim()
.toLowerCase()
.replace(/[\s_/]+/g, "-")
.replace(/[^a-z0-9-]+/g, "")
.replace(/-+/g, "-")
.replace(/^-+|-+$/g, "");
}
function resolveSkillByName(
report: SkillStatusReport,
requestedName: string,
): SkillStatusEntry | null {
const raw = requestedName.trim();
if (!raw) {
return null;
}
const direct = report.skills.find((s) => s.name === raw || s.skillKey === raw);
if (direct) {
return direct;
}
const lower = raw.toLowerCase();
const caseInsensitive = report.skills.find(
(s) => s.name.toLowerCase() === lower || s.skillKey.toLowerCase() === lower,
);
if (caseInsensitive) {
return caseInsensitive;
}
const normalized = normalizeSkillLookupToken(raw);
if (!normalized) {
return null;
}
return (
report.skills.find(
(s) =>
normalizeSkillLookupToken(s.name) === normalized ||
normalizeSkillLookupToken(s.skillKey) === normalized,
) ?? null
);
}
export function formatSkillsList(report: SkillStatusReport, opts: SkillsListOptions): string {
const skills = opts.eligible ? report.skills.filter((s) => s.eligible) : report.skills;
@ -168,14 +214,15 @@ export function formatSkillInfo(
skillName: string,
opts: SkillInfoOptions,
): string {
const skill = report.skills.find((s) => s.name === skillName || s.skillKey === skillName);
const requestedName = skillName.trim();
const skill = resolveSkillByName(report, requestedName);
if (!skill) {
if (opts.json) {
return JSON.stringify({ error: "not found", skill: skillName }, null, 2);
return JSON.stringify({ error: "not found", skill: requestedName }, null, 2);
}
return appendClawHubHint(
`Skill "${skillName}" not found. Run \`${formatCliCommand("openclaw skills list")}\` to see available skills.`,
`Skill "${requestedName}" not found. Run \`${formatCliCommand("openclaw skills list")}\` to see available skills.`,
opts.json,
);
}

View File

@ -149,16 +149,30 @@ describe("skills-cli", () => {
expect(output).toContain("API_KEY");
});
it("normalizes text-presentation emoji selectors in info output", () => {
it("resolves skill info case-insensitively", () => {
const report = createMockReport([
createMockSkill({
name: "info-emoji",
emoji: "🎛\uFE0E",
name: "Excel XLSX",
skillKey: "Excel-XLSX",
description: "Spreadsheet helpers",
}),
]);
const output = formatSkillInfo(report, "info-emoji", {});
expect(output).toContain("🎛️");
const output = formatSkillInfo(report, "excel-xlsx", {});
expect(output).toContain("Spreadsheet helpers");
});
it("resolves skill info across separator variants", () => {
const report = createMockReport([
createMockSkill({
name: "Excel XLSX",
skillKey: "excel_xlsx",
description: "Spreadsheet helpers",
}),
]);
const output = formatSkillInfo(report, "excel-xlsx", {});
expect(output).toContain("Spreadsheet helpers");
});
});