From 6be1d8e4fc11cd9916d4d9cc26b3bda0a019731d Mon Sep 17 00:00:00 2001 From: Mohan Raj Rajamanickam Date: Wed, 11 Mar 2026 14:31:16 -0700 Subject: [PATCH] refactor: streamline skills validation process in workflow and script --- .github/workflows/validate-skills.yml | 17 +--------- scripts/validate-skills.ts | 49 +++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/.github/workflows/validate-skills.yml b/.github/workflows/validate-skills.yml index 55e051b..51d83f2 100644 --- a/.github/workflows/validate-skills.yml +++ b/.github/workflows/validate-skills.yml @@ -18,19 +18,4 @@ jobs: - run: npm ci - - name: Detect changed skill directories - id: changed-skills - run: | - # Collect top-level skill dirs touched by this PR (relative to skills/). - # Passing explicit dirs means pre-existing nested skill categories - # (to be flattened in a separate PR) don't block unrelated skill PRs. - DIRS=$(git diff --name-only origin/${{ github.base_ref }}...HEAD \ - | grep '^skills/' \ - | cut -d'/' -f2 \ - | sort -u \ - | tr '\n' ' ') - echo "dirs=$DIRS" >> $GITHUB_OUTPUT - echo "Validating skill dirs: $DIRS" - - - name: Validate changed skills - run: npx tsx scripts/validate-skills.ts ${{ steps.changed-skills.outputs.dirs }} + - run: npm run validate:skills -- --changed --base=origin/${{ github.base_ref }} diff --git a/scripts/validate-skills.ts b/scripts/validate-skills.ts index e51d30c..5c40d51 100644 --- a/scripts/validate-skills.ts +++ b/scripts/validate-skills.ts @@ -3,11 +3,13 @@ // Exits with code 1 if any violations are found. // // Usage: -// tsx scripts/validate-skills.ts # validate all skills -// tsx scripts/validate-skills.ts apex-class # validate specific skill dirs +// npm run validate:skills # validate all skills +// npm run validate:skills -- --changed --base=origin/main # validate only skills changed vs base +import { execSync } from "child_process" import fs from "fs" import path from "path" +import { parseArgs } from "util" const SKILLS_DIR = path.join(__dirname, "..", "skills") @@ -136,6 +138,23 @@ const CONTENT_CHECKS: ContentCheck[] = [ }, ] +// --------------------------------------------------------------------------- +// Changed-skills detection +// --------------------------------------------------------------------------- + +function getChangedSkillDirs(base: string): string[] { + const output = execSync(`git diff --name-only ${base}...HEAD`, { encoding: "utf8" }) + return [ + ...new Set( + output + .split("\n") + .filter((f) => f.startsWith("skills/")) + .map((f) => f.split("/")[1]) + .filter(Boolean) + ), + ] +} + // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- @@ -193,10 +212,28 @@ function validateSkill(dirName: string, dirPath: string): string[] { } function main(): void { - // If skill dir names are passed as arguments, validate only those. - // Otherwise validate all entries in skills/. - const targets = process.argv.slice(2) - const entries = targets.length > 0 ? targets : fs.readdirSync(SKILLS_DIR) + const { values } = parseArgs({ + args: process.argv.slice(2), + options: { + /** Validate only skill dirs touched in this branch vs the given base ref. */ + changed: { type: "boolean", default: false }, + /** Base ref for --changed (e.g. origin/main). Defaults to origin/HEAD. */ + base: { type: "string", default: "origin/HEAD" }, + }, + }) + + let entries: string[] + + if (values.changed) { + entries = getChangedSkillDirs(values.base!) + if (entries.length === 0) { + console.log("No skill directories changed — nothing to validate.") + return + } + console.log(`Validating ${entries.length} changed skill(s): ${entries.join(", ")}`) + } else { + entries = fs.readdirSync(SKILLS_DIR) + } const allErrors: string[] = [] let passed = 0