From de656ee1d35be9dce3bd6d27a274945263061bd1 Mon Sep 17 00:00:00 2001 From: williamfzc <178894043@qq.com> Date: Tue, 31 Mar 2026 13:20:48 +0800 Subject: [PATCH 1/5] ci: add GitHub Actions workflow to check skill format --- .github/workflows/skill-format-check.yml | 32 +++++++++++ scripts/check_skill_format.js | 72 ++++++++++++++++++++++++ skills/lark-shared/SKILL.md | 3 + skills/lark-whiteboard/SKILL.md | 1 + 4 files changed, 108 insertions(+) create mode 100644 .github/workflows/skill-format-check.yml create mode 100644 scripts/check_skill_format.js diff --git a/.github/workflows/skill-format-check.yml b/.github/workflows/skill-format-check.yml new file mode 100644 index 000000000..97e2a161d --- /dev/null +++ b/.github/workflows/skill-format-check.yml @@ -0,0 +1,32 @@ +name: Skill Format Check + +on: + push: + branches: [main] + paths: + - "skills/**" + - "scripts/check_skill_format.js" + - ".github/workflows/skill-format-check.yml" + pull_request: + branches: [main] + paths: + - "skills/**" + - "scripts/check_skill_format.js" + - ".github/workflows/skill-format-check.yml" + +permissions: + contents: read + +jobs: + check-format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Run Skill Format Check + run: node scripts/check_skill_format.js diff --git a/scripts/check_skill_format.js b/scripts/check_skill_format.js new file mode 100644 index 000000000..f6a61c7bf --- /dev/null +++ b/scripts/check_skill_format.js @@ -0,0 +1,72 @@ +const fs = require('fs'); +const path = require('path'); + +const SKILLS_DIR = path.join(__dirname, '../skills'); + +function checkSkillFormat() { + console.log('Checking skill format...'); + + if (!fs.existsSync(SKILLS_DIR)) { + console.error('Skills directory not found:', SKILLS_DIR); + process.exit(1); + } + + const skills = fs.readdirSync(SKILLS_DIR).filter(file => { + return fs.statSync(path.join(SKILLS_DIR, file)).isDirectory(); + }); + + let hasErrors = false; + + skills.forEach(skill => { + const skillPath = path.join(SKILLS_DIR, skill); + const skillFile = path.join(skillPath, 'SKILL.md'); + + if (!fs.existsSync(skillFile)) { + console.error(`❌ [${skill}] Missing SKILL.md`); + hasErrors = true; + return; + } + + const content = fs.readFileSync(skillFile, 'utf-8'); + + // 检查 YAML Frontmatter + if (!content.startsWith('---\n') && !content.startsWith('---\r\n')) { + console.error(`❌ [${skill}] SKILL.md must start with YAML frontmatter (---)`); + hasErrors = true; + } else { + // 兼容不同的换行符 + let endOfFrontmatter = content.indexOf('\n---', 3); + if (endOfFrontmatter === -1) { + console.error(`❌ [${skill}] SKILL.md has unclosed YAML frontmatter`); + hasErrors = true; + } else { + const frontmatter = content.substring(3, endOfFrontmatter); + if (!frontmatter.includes('name:')) { + console.error(`❌ [${skill}] YAML frontmatter missing 'name'`); + hasErrors = true; + } + if (!frontmatter.includes('version:')) { + console.error(`❌ [${skill}] YAML frontmatter missing 'version'`); + hasErrors = true; + } + if (!frontmatter.includes('description:')) { + console.error(`❌ [${skill}] YAML frontmatter missing 'description'`); + hasErrors = true; + } + if (!frontmatter.includes('metadata:')) { + console.error(`❌ [${skill}] YAML frontmatter missing 'metadata'`); + hasErrors = true; + } + } + } + }); + + if (hasErrors) { + console.error('\n❌ Skill format check failed. Please fix the errors above.'); + process.exit(1); + } else { + console.log('\n✅ Skill format check passed!'); + } +} + +checkSkillFormat(); diff --git a/skills/lark-shared/SKILL.md b/skills/lark-shared/SKILL.md index c21bca742..b9de0b1a4 100644 --- a/skills/lark-shared/SKILL.md +++ b/skills/lark-shared/SKILL.md @@ -2,6 +2,9 @@ name: lark-shared version: 1.0.0 description: "飞书/Lark CLI 共享基础:应用配置初始化、认证登录(auth login)、身份切换(--as user/bot)、权限与 scope 管理、Permission denied 错误处理、安全规则。当用户需要第一次配置(`lark-cli config init`)、使用登录授权(`lark-cli auth login`)、遇到权限不足、切换 user/bot 身份、配置 scope、或首次使用 lark-cli 时触发。" +metadata: + requires: + bins: ["lark-cli"] --- # lark-cli 共享规则 diff --git a/skills/lark-whiteboard/SKILL.md b/skills/lark-whiteboard/SKILL.md index 0bd7777e3..07000387d 100644 --- a/skills/lark-whiteboard/SKILL.md +++ b/skills/lark-whiteboard/SKILL.md @@ -1,5 +1,6 @@ --- name: lark-whiteboard +version: 1.0.0 description: > 当用户要求在飞书云文档中绘制图表,或使用飞书画板绘制架构图、流程图、思维导图、时序图或其他可视化图表时使用此 skill。 compatibility: Requires Node.js 18+ From c781307bd687ba6e08bbb85f3fcd6b46c976e63a Mon Sep 17 00:00:00 2001 From: williamfzc <178894043@qq.com> Date: Tue, 31 Mar 2026 13:52:53 +0800 Subject: [PATCH 2/5] refactor(scripts): move skill format check to isolated directory and add README --- .github/workflows/skill-format-check.yml | 6 +++--- scripts/skill-format-check/README.md | 21 +++++++++++++++++++ .../index.js} | 2 +- 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 scripts/skill-format-check/README.md rename scripts/{check_skill_format.js => skill-format-check/index.js} (97%) diff --git a/.github/workflows/skill-format-check.yml b/.github/workflows/skill-format-check.yml index 97e2a161d..7c8b8fc5f 100644 --- a/.github/workflows/skill-format-check.yml +++ b/.github/workflows/skill-format-check.yml @@ -5,13 +5,13 @@ on: branches: [main] paths: - "skills/**" - - "scripts/check_skill_format.js" + - "scripts/skill-format-check/**" - ".github/workflows/skill-format-check.yml" pull_request: branches: [main] paths: - "skills/**" - - "scripts/check_skill_format.js" + - "scripts/skill-format-check/**" - ".github/workflows/skill-format-check.yml" permissions: @@ -29,4 +29,4 @@ jobs: node-version: '20' - name: Run Skill Format Check - run: node scripts/check_skill_format.js + run: node scripts/skill-format-check/index.js diff --git a/scripts/skill-format-check/README.md b/scripts/skill-format-check/README.md new file mode 100644 index 000000000..23fe9fedd --- /dev/null +++ b/scripts/skill-format-check/README.md @@ -0,0 +1,21 @@ +# Skill Format Check + +This directory contains a script to validate the format of `SKILL.md` files located in the `../../skills` directory. + +## Purpose + +The `index.js` script ensures that all `SKILL.md` files conform to the standard template defined in `skill-template/skill-template.md`. Specifically, it checks that the YAML frontmatter includes the following required fields: +- `name` +- `version` +- `description` +- `metadata` + +## Usage + +This script is executed automatically via GitHub Actions (`.github/workflows/skill-format-check.yml`) on pull requests and pushes that modify the `skills/` directory. + +To run the check manually from the root of the repository, execute: + +```bash +node scripts/skill-format-check/index.js +``` diff --git a/scripts/check_skill_format.js b/scripts/skill-format-check/index.js similarity index 97% rename from scripts/check_skill_format.js rename to scripts/skill-format-check/index.js index f6a61c7bf..540d75d1e 100644 --- a/scripts/check_skill_format.js +++ b/scripts/skill-format-check/index.js @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); -const SKILLS_DIR = path.join(__dirname, '../skills'); +const SKILLS_DIR = path.join(__dirname, '../../skills'); function checkSkillFormat() { console.log('Checking skill format...'); From 117cb8e6bb6de3aff8aaa7d9deb87b3cbacd5820 Mon Sep 17 00:00:00 2001 From: williamfzc <178894043@qq.com> Date: Tue, 31 Mar 2026 14:15:33 +0800 Subject: [PATCH 3/5] test(scripts): add positive and negative tests for skill format check --- scripts/skill-format-check/README.md | 14 +++++ scripts/skill-format-check/index.js | 6 +- scripts/skill-format-check/test.sh | 63 +++++++++++++++++++ .../tests/bad-skill-no-frontmatter/SKILL.md | 3 + .../bad-skill-unclosed-frontmatter/SKILL.md | 9 +++ .../tests/bad-skill/SKILL.md | 8 +++ .../tests/good-skill-complex/SKILL.md | 17 +++++ .../tests/good-skill-minimal/SKILL.md | 10 +++ .../tests/good-skill/SKILL.md | 12 ++++ 9 files changed, 140 insertions(+), 2 deletions(-) create mode 100755 scripts/skill-format-check/test.sh create mode 100644 scripts/skill-format-check/tests/bad-skill-no-frontmatter/SKILL.md create mode 100644 scripts/skill-format-check/tests/bad-skill-unclosed-frontmatter/SKILL.md create mode 100644 scripts/skill-format-check/tests/bad-skill/SKILL.md create mode 100644 scripts/skill-format-check/tests/good-skill-complex/SKILL.md create mode 100644 scripts/skill-format-check/tests/good-skill-minimal/SKILL.md create mode 100644 scripts/skill-format-check/tests/good-skill/SKILL.md diff --git a/scripts/skill-format-check/README.md b/scripts/skill-format-check/README.md index 23fe9fedd..dffb955dd 100644 --- a/scripts/skill-format-check/README.md +++ b/scripts/skill-format-check/README.md @@ -19,3 +19,17 @@ To run the check manually from the root of the repository, execute: ```bash node scripts/skill-format-check/index.js ``` + +You can also specify a custom target directory as the first argument: + +```bash +node scripts/skill-format-check/index.js ./path/to/my/skills +``` + +## Testing + +This tool comes with a quick validation script to ensure it correctly identifies good and bad skill formats. To run the tests, execute: + +```bash +./scripts/skill-format-check/test.sh +``` diff --git a/scripts/skill-format-check/index.js b/scripts/skill-format-check/index.js index 540d75d1e..d59a45f66 100644 --- a/scripts/skill-format-check/index.js +++ b/scripts/skill-format-check/index.js @@ -1,10 +1,12 @@ const fs = require('fs'); const path = require('path'); -const SKILLS_DIR = path.join(__dirname, '../../skills'); +// Allow passing a target directory as the first argument, default to '../../skills' +const targetDirArg = process.argv[2] || '../../skills'; +const SKILLS_DIR = path.resolve(__dirname, targetDirArg); function checkSkillFormat() { - console.log('Checking skill format...'); + console.log(`Checking skill format in ${SKILLS_DIR}...`); if (!fs.existsSync(SKILLS_DIR)) { console.error('Skills directory not found:', SKILLS_DIR); diff --git a/scripts/skill-format-check/test.sh b/scripts/skill-format-check/test.sh new file mode 100755 index 000000000..6749c4551 --- /dev/null +++ b/scripts/skill-format-check/test.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Get the directory of this script +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +INDEX_JS="$DIR/index.js" + +echo "=== Running tests for skill-format-check ===" +echo "Index script: $INDEX_JS" + +# Function to run a positive test +run_positive_test() { + local test_name=$1 + echo -e "\n--- [Positive] $test_name ---" + + mkdir -p "$DIR/tests/temp_test_dir" + cp -r "$DIR/tests/$test_name" "$DIR/tests/temp_test_dir/" + + node "$INDEX_JS" "$DIR/tests/temp_test_dir" + + if [ $? -eq 0 ]; then + echo "✅ Passed! (Correctly validated $test_name)" + rm -rf "$DIR/tests/temp_test_dir" + return 0 + else + echo "❌ Failed! Expected $test_name to pass but it failed." + rm -rf "$DIR/tests/temp_test_dir" + exit 1 + fi +} + +# Function to run a negative test +run_negative_test() { + local test_name=$1 + echo -e "\n--- [Negative] $test_name ---" + + mkdir -p "$DIR/tests/temp_test_dir" + cp -r "$DIR/tests/$test_name" "$DIR/tests/temp_test_dir/" + + # Run the script and suppress error output since we expect it to fail + node "$INDEX_JS" "$DIR/tests/temp_test_dir" > /dev/null 2>&1 + + if [ $? -eq 1 ]; then + echo "✅ Passed! (Correctly rejected $test_name)" + rm -rf "$DIR/tests/temp_test_dir" + return 0 + else + echo "❌ Failed! Expected $test_name to fail but it passed." + rm -rf "$DIR/tests/temp_test_dir" + exit 1 + fi +} + +# Run positive tests +run_positive_test "good-skill" +run_positive_test "good-skill-minimal" +run_positive_test "good-skill-complex" + +# Run negative tests +run_negative_test "bad-skill" +run_negative_test "bad-skill-no-frontmatter" +run_negative_test "bad-skill-unclosed-frontmatter" + +echo -e "\n🎉 All tests passed successfully!" diff --git a/scripts/skill-format-check/tests/bad-skill-no-frontmatter/SKILL.md b/scripts/skill-format-check/tests/bad-skill-no-frontmatter/SKILL.md new file mode 100644 index 000000000..5a7afa3e9 --- /dev/null +++ b/scripts/skill-format-check/tests/bad-skill-no-frontmatter/SKILL.md @@ -0,0 +1,3 @@ +# No Frontmatter Skill + +This skill completely lacks a YAML frontmatter. diff --git a/scripts/skill-format-check/tests/bad-skill-unclosed-frontmatter/SKILL.md b/scripts/skill-format-check/tests/bad-skill-unclosed-frontmatter/SKILL.md new file mode 100644 index 000000000..189d62533 --- /dev/null +++ b/scripts/skill-format-check/tests/bad-skill-unclosed-frontmatter/SKILL.md @@ -0,0 +1,9 @@ +--- +name: bad-skill-unclosed +version: 1.0.0 +description: "This skill has an unclosed frontmatter block." +metadata: {} + +# Unclosed Frontmatter Skill + +This frontmatter does not have a closing `---` block. \ No newline at end of file diff --git a/scripts/skill-format-check/tests/bad-skill/SKILL.md b/scripts/skill-format-check/tests/bad-skill/SKILL.md new file mode 100644 index 000000000..d59074299 --- /dev/null +++ b/scripts/skill-format-check/tests/bad-skill/SKILL.md @@ -0,0 +1,8 @@ +--- +name: bad-skill +description: "This skill is missing version and metadata." +--- + +# Bad Skill + +This skill is missing required fields. diff --git a/scripts/skill-format-check/tests/good-skill-complex/SKILL.md b/scripts/skill-format-check/tests/good-skill-complex/SKILL.md new file mode 100644 index 000000000..0f7b51836 --- /dev/null +++ b/scripts/skill-format-check/tests/good-skill-complex/SKILL.md @@ -0,0 +1,17 @@ +--- +name: good-skill-complex +version: 2.5.1-beta +description: > + A very complex description + that spans multiple lines + and contains weird chars: !@#$%^&*() +metadata: + requires: + bins: ["lark-cli", "node"] + cliHelp: "lark-cli something --help" + customField: "customValue" +--- + +# Complex Skill + +This skill has a complex frontmatter block. diff --git a/scripts/skill-format-check/tests/good-skill-minimal/SKILL.md b/scripts/skill-format-check/tests/good-skill-minimal/SKILL.md new file mode 100644 index 000000000..ca3f481c2 --- /dev/null +++ b/scripts/skill-format-check/tests/good-skill-minimal/SKILL.md @@ -0,0 +1,10 @@ +--- +name: good-skill-minimal +version: 0.1.0 +description: Minimal valid description +metadata: {} +--- + +# Minimal Skill + +This has the bare minimum required fields. diff --git a/scripts/skill-format-check/tests/good-skill/SKILL.md b/scripts/skill-format-check/tests/good-skill/SKILL.md new file mode 100644 index 000000000..8c2e7b40f --- /dev/null +++ b/scripts/skill-format-check/tests/good-skill/SKILL.md @@ -0,0 +1,12 @@ +--- +name: good-skill +version: 1.0.0 +description: "This is a properly formatted skill." +metadata: + requires: + bins: ["lark-cli"] +--- + +# Good Skill + +This skill follows all the formatting rules. From 701160cca30f3de227eb16e605c45b9d4afe96e5 Mon Sep 17 00:00:00 2001 From: williamfzc <178894043@qq.com> Date: Tue, 31 Mar 2026 14:26:00 +0800 Subject: [PATCH 4/5] fix(scripts): revert skill changes and downgrade version/metadata checks to warnings --- scripts/skill-format-check/index.js | 8 ++++---- scripts/skill-format-check/tests/bad-skill/SKILL.md | 6 +++--- skills/lark-shared/SKILL.md | 3 --- skills/lark-whiteboard/SKILL.md | 1 - 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/scripts/skill-format-check/index.js b/scripts/skill-format-check/index.js index d59a45f66..534b8a926 100644 --- a/scripts/skill-format-check/index.js +++ b/scripts/skill-format-check/index.js @@ -48,16 +48,16 @@ function checkSkillFormat() { hasErrors = true; } if (!frontmatter.includes('version:')) { - console.error(`❌ [${skill}] YAML frontmatter missing 'version'`); - hasErrors = true; + console.warn(`⚠️ [${skill}] YAML frontmatter missing 'version' (Warning only)`); + // hasErrors = true; } if (!frontmatter.includes('description:')) { console.error(`❌ [${skill}] YAML frontmatter missing 'description'`); hasErrors = true; } if (!frontmatter.includes('metadata:')) { - console.error(`❌ [${skill}] YAML frontmatter missing 'metadata'`); - hasErrors = true; + console.warn(`⚠️ [${skill}] YAML frontmatter missing 'metadata' (Warning only)`); + // hasErrors = true; // Downgrade to warning to not fail on existing skills } } } diff --git a/scripts/skill-format-check/tests/bad-skill/SKILL.md b/scripts/skill-format-check/tests/bad-skill/SKILL.md index d59074299..465a05da2 100644 --- a/scripts/skill-format-check/tests/bad-skill/SKILL.md +++ b/scripts/skill-format-check/tests/bad-skill/SKILL.md @@ -1,8 +1,8 @@ --- -name: bad-skill -description: "This skill is missing version and metadata." +version: 1.0.0 +metadata: {} --- # Bad Skill -This skill is missing required fields. +This skill is missing required fields like name and description. diff --git a/skills/lark-shared/SKILL.md b/skills/lark-shared/SKILL.md index b9de0b1a4..c21bca742 100644 --- a/skills/lark-shared/SKILL.md +++ b/skills/lark-shared/SKILL.md @@ -2,9 +2,6 @@ name: lark-shared version: 1.0.0 description: "飞书/Lark CLI 共享基础:应用配置初始化、认证登录(auth login)、身份切换(--as user/bot)、权限与 scope 管理、Permission denied 错误处理、安全规则。当用户需要第一次配置(`lark-cli config init`)、使用登录授权(`lark-cli auth login`)、遇到权限不足、切换 user/bot 身份、配置 scope、或首次使用 lark-cli 时触发。" -metadata: - requires: - bins: ["lark-cli"] --- # lark-cli 共享规则 diff --git a/skills/lark-whiteboard/SKILL.md b/skills/lark-whiteboard/SKILL.md index 07000387d..0bd7777e3 100644 --- a/skills/lark-whiteboard/SKILL.md +++ b/skills/lark-whiteboard/SKILL.md @@ -1,6 +1,5 @@ --- name: lark-whiteboard -version: 1.0.0 description: > 当用户要求在飞书云文档中绘制图表,或使用飞书画板绘制架构图、流程图、思维导图、时序图或其他可视化图表时使用此 skill。 compatibility: Requires Node.js 18+ From cc58e06de1433103e14ebe0ed2f634d8bae4770f Mon Sep 17 00:00:00 2001 From: williamfzc <178894043@qq.com> Date: Tue, 31 Mar 2026 14:53:10 +0800 Subject: [PATCH 5/5] fix(scripts): completely remove version check and skip lark-shared --- scripts/skill-format-check/README.md | 5 +++-- scripts/skill-format-check/index.js | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/scripts/skill-format-check/README.md b/scripts/skill-format-check/README.md index dffb955dd..f67f303c5 100644 --- a/scripts/skill-format-check/README.md +++ b/scripts/skill-format-check/README.md @@ -6,9 +6,10 @@ This directory contains a script to validate the format of `SKILL.md` files loca The `index.js` script ensures that all `SKILL.md` files conform to the standard template defined in `skill-template/skill-template.md`. Specifically, it checks that the YAML frontmatter includes the following required fields: - `name` -- `version` - `description` -- `metadata` +- `metadata` (outputs a warning if missing, does not fail the build) + +> **Note:** The `lark-shared` skill is explicitly excluded from these format checks. ## Usage diff --git a/scripts/skill-format-check/index.js b/scripts/skill-format-check/index.js index 534b8a926..1dd5a38d4 100644 --- a/scripts/skill-format-check/index.js +++ b/scripts/skill-format-check/index.js @@ -20,6 +20,12 @@ function checkSkillFormat() { let hasErrors = false; skills.forEach(skill => { + // Skip lark-shared skill completely + if (skill === 'lark-shared') { + console.log(`⏭️ Skipping check for ${skill}`); + return; + } + const skillPath = path.join(SKILLS_DIR, skill); const skillFile = path.join(skillPath, 'SKILL.md'); @@ -47,10 +53,6 @@ function checkSkillFormat() { console.error(`❌ [${skill}] YAML frontmatter missing 'name'`); hasErrors = true; } - if (!frontmatter.includes('version:')) { - console.warn(`⚠️ [${skill}] YAML frontmatter missing 'version' (Warning only)`); - // hasErrors = true; - } if (!frontmatter.includes('description:')) { console.error(`❌ [${skill}] YAML frontmatter missing 'description'`); hasErrors = true;