From c843a1eccd9b6a5f8f42dde3d6c5a36a132939fc Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Mon, 31 Aug 2026 10:45:04 -0400 Subject: [PATCH] Report which repos a skill's `repos/` directory silently excludes Repo targeting is declared by the shape of `repos/`, not by its contents, and the two shapes mean opposite things. `tools/install` skips a skill whose `repos/` exists but holds no file for the target repo; a skill with no `repos/` installs everywhere. So adding one overlay to a skill that had none narrows it from every repo to that one, and the skill keeps installing correctly in the repo the author was looking at. Both directions are legitimate, so both are warnings rather than errors: 41 of 53 skills on `main` have partial coverage and 11 have none, and the tool skills among them are genuinely repo-agnostic. The warning names the excluded repos and the consequence so the choice is visible at review time. --- .github/scripts/lint-skill-entry.mjs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/scripts/lint-skill-entry.mjs b/.github/scripts/lint-skill-entry.mjs index 00c2ecff..f0c38c2a 100644 --- a/.github/scripts/lint-skill-entry.mjs +++ b/.github/scripts/lint-skill-entry.mjs @@ -106,6 +106,30 @@ export function lintSkill(skill) { } } + // Repo targeting is declared by the SHAPE of repos/, not by its contents, and the two + // shapes mean opposite things to the installer. `tools/install` skips a skill whose + // repos/ exists but holds no file for the target repo, while a skill with no repos/ at + // all installs into every repo. So adding one overlay to a skill that had none does not + // widen it — it narrows it from "everywhere" to "that repo alone", silently, and the + // skill keeps installing fine in the repo you were looking at. + if (skill.repos.length > 0) { + const missing = KNOWN_REPOS.filter((r) => !skill.repos.includes(r)); + if (missing.length > 0) { + warnings.push( + `repos/ covers ${skill.repos.join(', ')} but not ${missing.join(', ')}; ` + + `the installer SKIPS this skill entirely for ${missing.length === 1 ? 'that repo' : 'those repos'}. ` + + `Add repos/.md for each (a stub naming the repo is enough), or delete repos/ ` + + `to install everywhere with no repo-specific guidance.`, + ); + } + } else { + warnings.push( + `no repos/ overlay; installs into every repo (${KNOWN_REPOS.join(', ')}) with no ` + + `repo-specific guidance. Add a repos/.md per consumer, or leave as-is if the ` + + `skill is genuinely repo-agnostic.`, + ); + } + for (const key of Object.keys(raw)) { if (!KNOWN_FRONTMATTER.includes(key) && key !== 'alwaysApply') { warnings.push(`unknown frontmatter key "${key}"; operators silently ignore unrecognised keys (typo?)`);