



























@@ -180,46 +180,66 @@ describe("skills-cli", () => {
180180expect(output).toContain("API_KEY");
181181});
182182183-it("shows API key storage guidance for the active config path", () => {
183+it("resolves skill info case-insensitively", () => {
184184const report = createMockReport([
185185createMockSkill({
186-name: "env-aware-skill",
187-skillKey: "env-aware-skill",
188-primaryEnv: "API_KEY",
189-eligible: false,
190-requirements: {
191-bins: [],
192-anyBins: [],
193-env: ["API_KEY"],
194-config: [],
195-os: [],
196-},
197-missing: {
198-bins: [],
199-anyBins: [],
200-env: ["API_KEY"],
201-config: [],
202-os: [],
203-},
186+name: "Excel XLSX",
187+skillKey: "Excel-XLSX",
188+description: "Spreadsheet helpers",
204189}),
205190]);
206191207-const output = formatSkillInfo(report, "env-aware-skill", {});
208-expect(output).toContain("OPENCLAW_CONFIG_PATH");
209-expect(output).toContain("default: ~/.openclaw/openclaw.json");
210-expect(output).toContain("skills.entries.env-aware-skill.apiKey");
192+const output = formatSkillInfo(report, "excel-xlsx", {});
193+expect(output).toContain("Spreadsheet helpers");
211194});
212195213-it("normalizes text-presentation emoji selectors in info output", () => {
196+it("resolves skill info across separator variants", () => {
214197const report = createMockReport([
215198createMockSkill({
216-name: "info-emoji",
217-emoji: "🎛\uFE0E",
199+name: "Excel XLSX",
200+skillKey: "excel_xlsx",
201+description: "Spreadsheet helpers",
218202}),
219203]);
220204221-const output = formatSkillInfo(report, "info-emoji", {});
222-expect(output).toContain("🎛️");
205+const output = formatSkillInfo(report, "excel-xlsx", {});
206+expect(output).toContain("Spreadsheet helpers");
207+});
208+209+it("returns not found for ambiguous case-insensitive matches", () => {
210+const report = createMockReport([
211+createMockSkill({ name: "First Skill", skillKey: "Excel-XLSX", description: "first" }),
212+createMockSkill({ name: "Second Skill", skillKey: "excel-xlsx", description: "second" }),
213+]);
214+215+const output = formatSkillInfo(report, "EXCEL-XLSX", {});
216+expect(output).toContain("not found");
217+expect(output).not.toContain("first");
218+expect(output).not.toContain("second");
219+});
220+221+it("returns not found for ambiguous normalized matches", () => {
222+const report = createMockReport([
223+createMockSkill({ name: "Excel/XLSX", skillKey: "excel-slash", description: "first" }),
224+createMockSkill({
225+name: "Excel_XLSX",
226+skillKey: "excel-underscore",
227+description: "second",
228+}),
229+]);
230+231+const output = formatSkillInfo(report, "excel-xlsx", {});
232+expect(output).toContain("not found");
233+expect(output).not.toContain("first");
234+expect(output).not.toContain("second");
235+});
236+237+it("sanitizes user-supplied skill name in not-found text output", () => {
238+const report = createMockReport([]);
239+const output = formatSkillInfo(report, "evil\u001b[31m\u009f", {});
240+241+expect(output).toContain('Skill "evil" not found');
242+expect(output).not.toContain("\u001b");
223243});
224244225245it("shows agent exclusion and visibility details in skill info", () => {
@@ -482,5 +502,15 @@ describe("skills-cli", () => {
482502expect(parsed.description).toBe("hi");
483503expect(parsed.homepage).toBe("https://example.com/docs");
484504});
505+506+it("sanitizes user-supplied skill name in not-found JSON output", () => {
507+const report = createMockReport([]);
508+const output = formatSkillInfo(report, "evil\u001b[31m\u009f", { json: true });
509+const parsed = JSON.parse(output) as { error: string; skill: string };
510+511+expect(parsed.error).toBe("not found");
512+expect(parsed.skill).toBe("evil");
513+expect(output).not.toContain("\u001b");
514+});
485515});
486516});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。