ci(workflows): add skill sync and validation workflows
- Add sync-claude-plugin workflow to auto-update marketplace.json when skills change - Add validate-skill workflow to validate SKILL.md files on push/PR - Add sync-marketplace.js script for skill discovery and count updates
This commit is contained in:
67
.github/scripts/sync-marketplace.js
vendored
Normal file
67
.github/scripts/sync-marketplace.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Sync marketplace.json with skills directory.
|
||||||
|
*
|
||||||
|
* Scans the skills/ directory for valid skills (directories containing SKILL.md)
|
||||||
|
* and updates marketplace.json to match.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const SKILLS_DIR = "skills";
|
||||||
|
const MARKETPLACE_FILE = ".claude-plugin/marketplace.json";
|
||||||
|
|
||||||
|
function getSkillsFromDirectory() {
|
||||||
|
if (!fs.existsSync(SKILLS_DIR)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs
|
||||||
|
.readdirSync(SKILLS_DIR, { withFileTypes: true })
|
||||||
|
.filter((entry) => {
|
||||||
|
if (!entry.isDirectory()) return false;
|
||||||
|
const skillFile = path.join(SKILLS_DIR, entry.name, "SKILL.md");
|
||||||
|
return fs.existsSync(skillFile);
|
||||||
|
})
|
||||||
|
.map((entry) => `./${SKILLS_DIR}/${entry.name}`)
|
||||||
|
.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSkillCount(description, count) {
|
||||||
|
return description.replace(/\d+ marketing skills/, `${count} marketing skills`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const currentSkills = getSkillsFromDirectory();
|
||||||
|
|
||||||
|
const marketplace = JSON.parse(fs.readFileSync(MARKETPLACE_FILE, "utf8"));
|
||||||
|
const plugin = marketplace.plugins[0];
|
||||||
|
const existingSkills = plugin.skills || [];
|
||||||
|
|
||||||
|
// Check if update needed
|
||||||
|
if (JSON.stringify(currentSkills) === JSON.stringify(existingSkills)) {
|
||||||
|
console.log("marketplace.json is already in sync");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update skills list
|
||||||
|
plugin.skills = currentSkills;
|
||||||
|
|
||||||
|
// Update description with new count
|
||||||
|
plugin.description = updateSkillCount(plugin.description, currentSkills.length);
|
||||||
|
|
||||||
|
// Write updated marketplace.json
|
||||||
|
fs.writeFileSync(MARKETPLACE_FILE, JSON.stringify(marketplace, null, 2) + "\n");
|
||||||
|
|
||||||
|
// Report changes
|
||||||
|
const added = currentSkills.filter((s) => !existingSkills.includes(s));
|
||||||
|
const removed = existingSkills.filter((s) => !currentSkills.includes(s));
|
||||||
|
|
||||||
|
if (added.length) console.log(`Added: ${added.join(", ")}`);
|
||||||
|
if (removed.length) console.log(`Removed: ${removed.join(", ")}`);
|
||||||
|
|
||||||
|
console.log(`Updated marketplace.json (${currentSkills.length} skills)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
28
.github/workflows/sync-claude-plugin.yml
vendored
Normal file
28
.github/workflows/sync-claude-plugin.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Sync Claude Plugin
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'skills/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync:
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: true
|
||||||
|
|
||||||
|
- name: Sync skills to marketplace.json
|
||||||
|
run: node .github/scripts/sync-marketplace.js
|
||||||
|
|
||||||
|
- name: Commit changes
|
||||||
|
uses: stefanzweifel/git-auto-commit-action@v7
|
||||||
|
with:
|
||||||
|
commit_message: "chore: sync marketplace.json with skills directory"
|
||||||
|
file_pattern: .claude-plugin/marketplace.json
|
||||||
65
.github/workflows/validate-skill.yml
vendored
Normal file
65
.github/workflows/validate-skill.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Validate Agent Skill
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- "**/SKILL.md"
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- "**/SKILL.md"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: validate-skill-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
detect-changes:
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
if: github.event.pull_request.draft != true && github.actor != 'dependabot[bot]'
|
||||||
|
outputs:
|
||||||
|
skills: ${{ steps.changed-skills.outputs.skills }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Get changed skills
|
||||||
|
id: changed-skills
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||||
|
BASE=${{ github.event.pull_request.base.sha }}
|
||||||
|
HEAD=${{ github.event.pull_request.head.sha }}
|
||||||
|
else
|
||||||
|
BASE=${{ github.event.before }}
|
||||||
|
HEAD=${{ github.event.after }}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find changed SKILL.md files and extract skill directories
|
||||||
|
SKILLS=$(git diff --name-only $BASE $HEAD | \
|
||||||
|
grep 'SKILL.md$' | \
|
||||||
|
xargs -I {} dirname {} | \
|
||||||
|
sort -u | \
|
||||||
|
jq -R -s -c 'split("\n") | map(select(length > 0))')
|
||||||
|
|
||||||
|
echo "skills=$SKILLS" >> $GITHUB_OUTPUT
|
||||||
|
echo "Changed skills: $SKILLS"
|
||||||
|
|
||||||
|
validate:
|
||||||
|
needs: detect-changes
|
||||||
|
if: needs.detect-changes.outputs.skills != '[]'
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
skill: ${{ fromJson(needs.detect-changes.outputs.skills) }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Validate ${{ matrix.skill }}
|
||||||
|
uses: Flash-Brew-Digital/validate-skill@v1
|
||||||
|
with:
|
||||||
|
path: ${{ matrix.skill }}
|
||||||
Reference in New Issue
Block a user