Skip to content

v2.6.1

Choose a tag to compare

@github-actions github-actions released this 15 Apr 12:24

2.6.1 - 2026-04-15

Bug Fixed

search fails to locate SKILL.md when the marketplace API's skillId has different punctuation from the on-disk directory name or the YAML name field (#126)

search fails to locate SKILL.md when the marketplace API's skillId has different punctuation from the on-disk directory name or the YAML name field, producing warnings like:

SKILL.md not found for 'pdf-ocr-extraction' in claude-office-skills/skills
SKILL.md not found for 'pdf-merge-&-split' in claude-office-skills/skills

Problem

When running search against the marketplace, some skills that do exist in the cloned repository are reported as missing. Concrete reproduction against skills.sh with query pdf ocr / pdf merge:

API skillId API name Actual repo directory YAML name field
pdf-ocr-extraction pdf ocr extraction pdf-ocr/ PDF OCR Extraction
pdf-merge-&-split pdf merge & split pdf-merge-split/ PDF Merge & Split

Both skills exist and have valid SKILL.md files at claude-office-skills/skills (https://github.com/claude-office-skills/skills), but search fails to find them and skips the read/install flow for them.

Solution

It's solved with the following five-tier solution:

Tier 1 — Direct path under skillId (unchanged)

  • Check repoDir / skillId / "SKILL.md". If exists -> return it.

Tier 2 — Direct path under skills/skillId (unchanged)

  • Check repoDir / "skills" / skillId / "SKILL.md". If exists -> return it.

Tier 3 — Directory name exact match (widened)

  • Collect all SKILL.md files via collectSkillMds.
  • Match against either skillId or displayName (string ===, case-sensitive as today).
  • skillId takes precedence over displayName if both match different files (deterministic: .find { md => (md / os.up).last === skillId }.orElse { … displayName }).

Tier 4 — YAML name field exact match (case-insensitive, trimmed) (widened)

  • Normalize via s => s.toLowerCase.trim.
  • For each SKILL.md: read its YAML name and normalize.
  • Match against the normalized skillId or normalized displayName.
  • skillId candidate is tried first, displayName second (same .find { … }.orElse { … } ordering).

Tier 5 — Alphanumeric-only fuzzy match (new, last resort)

  • Normalization: def alnumNormalize(s: String): String = s.toLowerCase.filter(_.isLetterOrDigit).
  • Build normalized skillIdAlnum and displayNameAlnum.
  • Candidate is a SKILL.md if the alphanumeric normalization of either its parent directory name or its YAML name equals either skillIdAlnum or displayNameAlnum.
  • If exactly one SKILL.md matches -> return it.
  • If zero SKILL.md files match -> return None.
  • If two or more distinct SKILL.md files match -> return None (ambiguity policy; avoid guessing).
  • Guard against empty normalized strings: if both skillIdAlnum and displayNameAlnum are empty, skip tier 5 entirely (return None without scanning).

What's Changed

  • Update: README.md by @kevin-lee in #125
  • Fix #126: Fix marketplace search failing to find SKILL.md when the API skillId differs in punctuation from the repo directory or YAML name by @kevin-lee in #127
  • Refactor to follow 2020 Hindsight Scala practices by @kevin-lee in #128
  • ai-skills v2.6.1 by @kevin-lee in #129

Full Changelog: v2.6.0...v2.6.1