@alanwang-merit/meritdata-platform-skill-package 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,119 +0,0 @@
1
- # skill-vetter
2
-
3
- > 校验 meritdata-platform-suite 中所有 skill 的结构合规性。
4
-
5
- ---
6
-
7
- ## 用途
8
-
9
- - CI 门禁:每次 PR 校验所有 skill 的结构
10
- - 人工 review 前快速检查
11
- - skill 新建后确认符合规范
12
-
13
- ## 校验项
14
-
15
- | # | 检查项 | 严重度 |
16
- |---|--------|--------|
17
- | 1 | SKILL.md 存在 | 🔴 阻断 |
18
- | 2 | frontmatter 完整(第 1 行 `---`)| 🔴 阻断 |
19
- | 3 | 必填字段(name/description/metadata/version/layer/sub-layer/stack)| 🔴 阻断 |
20
- | 4 | 4 大章节(触发 / 不触发 / 前置约束 / 路由)| 🔴 阻断 |
21
- | 5 | SKILL.md 行数 ≤ 60 | 🟠 警告 |
22
-
23
- ## 用法
24
-
25
- ```bash
26
- # 校验全部 skill
27
- bash tools/skill-vetter/run.sh
28
-
29
- # 校验单个 skill
30
- bash tools/skill-vetter/run.sh meritdata-platform-cache
31
- ```
32
-
33
- ## 退出码
34
-
35
- | 退出码 | 含义 |
36
- |--------|------|
37
- | 0 | 全部通过 |
38
- | 1 | 有失败 |
39
-
40
- ## 输出示例
41
-
42
- ```
43
- ========================================
44
- skill-vetter: skill 合规校验
45
- ========================================
46
- 扫描目录: /path/to/skills
47
-
48
- ✅ [meritdata-platform-cache] 通过
49
- ✅ [meritdata-platform-nacos-config] 通过
50
- ❌ [my-bad-skill] SKILL.md 缺少字段: sub-layer
51
- ✅ [meritdata-platform-session] 通过
52
-
53
- ========================================
54
- 结果: 28 通过 / 1 失败
55
- ========================================
56
- ```
57
-
58
- ## 集成 CI
59
-
60
- ### GitLab CI
61
-
62
- ```yaml
63
- skill-vet:
64
- stage: check
65
- script:
66
- - bash tools/skill-vetter/run.sh
67
- rules:
68
- - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
69
- ```
70
-
71
- ### GitHub Actions
72
-
73
- ```yaml
74
- name: Skill Vetter
75
- on: [pull_request]
76
-
77
- jobs:
78
- vet:
79
- runs-on: ubuntu-latest
80
- steps:
81
- - uses: actions/checkout@v3
82
- - run: bash tools/skill-vetter/run.sh
83
- ```
84
-
85
- ## 校验失败的修复
86
-
87
- ### 缺失 frontmatter
88
-
89
- ```bash
90
- # 检查 SKILL.md 第一行
91
- head -1 skills/<skill>/SKILL.md
92
- # 应为: ---
93
- ```
94
-
95
- ### 缺失字段
96
-
97
- 在 SKILL.md frontmatter 中补全:
98
-
99
- ```yaml
100
- ---
101
- name: meritdata-platform-xxx
102
- description: 描述
103
- metadata:
104
- version: "1.0.0"
105
- risk: low|medium|high
106
- layer: L1 | L2 | L3 | L4
107
- sub-layer: L2-M | L2-C | L2-F | L3-C | L3-F | L4-T
108
- stack: methodology | java | frontend | tool
109
- ---
110
- ```
111
-
112
- ### SKILL.md 行数过长
113
-
114
- 把详细说明移到 `references/` 子文件,SKILL.md 只保留路由条目。
115
-
116
- ## 关联阅读
117
-
118
- - [../skill-authoring-workflow/README.md](../skill-authoring-workflow/README.md) — skill 编写规范
119
- - [../skill-stocktake/README.md](../skill-stocktake/README.md) — skill 清单盘点
@@ -1,294 +0,0 @@
1
- #!/bin/bash
2
- # skill-vetter: 校验 meritdata-platform-suite 中所有 skill 的结构合规性(v2.6.1)
3
- #
4
- # 用法:
5
- # bash tools/skill-vetter/run.sh # 校验全部 skill
6
- # bash tools/skill-vetter/run.sh <skill-name> # 校验单个 skill
7
- # bash tools/skill-vetter/run.sh --json # 校验全部 + 输出 JSON 报告
8
- #
9
- # 退出码:
10
- # 0 - 全部通过(无 errors)
11
- # 1 - 有 errors
12
- # 2 - 仅 warnings
13
- #
14
- # v2.6.1 增强项(运维优化):
15
- # 1. 死链扫描(references 内 [text](path) 链接是否解析)
16
- # 2. 敏感信息扫描(password/appSecret/keytab/appToken/apiKey/accessToken/privateKey)
17
- # 3. TODO/FIXME 残留扫描
18
- # 4. 重复 skill name 扫描
19
- # 5. JSON 报告输出(--json)
20
- # 6. Summary 表格(行数 TOP-10 + 统计汇总)
21
-
22
- SUITE_DIR="${SUITE_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
23
- SKILLS_DIR="$SUITE_DIR/skills"
24
- TARGET=""
25
- JSON_OUTPUT=0
26
- for arg in "$@"; do
27
- if [ "$arg" = "--json" ]; then
28
- JSON_OUTPUT=1
29
- else
30
- TARGET="$arg"
31
- fi
32
- done
33
-
34
- EXIT_CODE=0
35
- PASS_COUNT=0
36
- FAIL_COUNT=0
37
- WARN_COUNT=0
38
- TOTAL_SKILLS=0
39
- JSON_RESULTS=()
40
- TMP_LOG=/tmp/vetterlog.$$
41
-
42
- echo "========================================"
43
- echo " skill-vetter v2.6.1: skill 合规校验"
44
- echo "========================================"
45
- echo "扫描目录: $SKILLS_DIR"
46
- echo ""
47
-
48
- # 死链检测:传入 md 文件路径 + 链接路径,echo DEADLINK if not exists
49
- check_link() {
50
- local src_md="$1"
51
- local link="$2"
52
- # 跳过 http/https/mailto/$ 锚点
53
- case "$link" in
54
- http*|https*|mailto:*|\$*) return 1 ;;
55
- \#*) return 1 ;;
56
- esac
57
- # 去掉 anchor
58
- local clean="${link%%#*}"
59
- [ -z "$clean" ] && return 1
60
- # 处理 ./ 开头
61
- clean="${clean#./}"
62
- local src_dir
63
- src_dir=$(dirname "$src_md")
64
- local resolved=""
65
- # 绝对路径(以 / 开头)相对于 suite 根目录
66
- case "$clean" in
67
- /*) resolved="$SUITE_DIR$clean" ;;
68
- *) resolved="$src_dir/$clean" ;;
69
- esac
70
- # 如果包含 ../ 跳出 src_dir,允许跨 skill 引用(向上 3 层即达 suite 根)
71
- if [[ "$resolved" == *"/../"* ]] || [[ "$resolved" == *"../../.."* ]]; then
72
- # 跳过跨多层的复杂相对路径(已迁出范围)
73
- return 1
74
- fi
75
- [ ! -e "$resolved" ] && echo "DEADLINK:$src_md:$link"
76
- return 0
77
- }
78
-
79
- check_skill() {
80
- local skill_dir="$1"
81
- local skill_name
82
- skill_name=$(basename "$skill_dir")
83
- local skill_md="$skill_dir/SKILL.md"
84
- local errors=0
85
- local warnings=0
86
- local errs_json=""
87
- local warns_json=""
88
-
89
- add_err() { echo " ERR [$skill_name] $1"; errs_json="${errs_json}\"$1\","; errors=$((errors + 1)); }
90
- add_warn() { echo " WARN [$skill_name] $1"; warns_json="${warns_json}\"$1\","; warnings=$((warnings + 1)); }
91
-
92
- # 1. SKILL.md 存在
93
- if [ ! -f "$skill_md" ]; then
94
- echo "FAIL [$skill_name] SKILL.md 缺失"
95
- FAIL_COUNT=$((FAIL_COUNT + 1))
96
- return
97
- fi
98
-
99
- # 2. frontmatter
100
- head -1 "$skill_md" | grep -q '^---$' || add_err "missing frontmatter"
101
-
102
- # 3. 必填字段(允许 0+ 前导空格)
103
- for f in name description metadata version layer; do
104
- grep -qE "^[[:space:]]*$f:" "$skill_md" || add_err "missing field: $f:"
105
- done
106
- local cur_layer
107
- cur_layer=$(grep -E "^[[:space:]]*layer:" "$skill_md" | head -1 | grep -oE 'L[0-9]+' | head -1)
108
- if [ "$cur_layer" != "L1" ]; then
109
- for f in sub-layer stack; do
110
- grep -qE "^[[:space:]]*$f:" "$skill_md" || add_err "missing field: $f:"
111
- done
112
- fi
113
-
114
- # 4. 4 大章节
115
- for s in "## 触发" "## 不触发" "## 前置约束" "## 路由"; do
116
- grep -q "^$s" "$skill_md" || add_err "missing section: $s"
117
- done
118
-
119
- # 5. 行数
120
- local lines
121
- lines=$(wc -l < "$skill_md")
122
- [ "$lines" -gt 60 ] && add_warn "SKILL.md $lines lines >60"
123
-
124
- # 6. 死链扫描(排除 .bak / dev-docs 归档目录)
125
- rm -f "$TMP_LOG"
126
- while IFS= read -r md_file; do
127
- [ -z "$md_file" ] && continue
128
- while IFS= read -r link; do
129
- [ -z "$link" ] && continue
130
- check_link "$md_file" "$link" >> "$TMP_LOG" 2>/dev/null || true
131
- done < <(grep -oE '\[[^]]+\]\([^)]+\)' "$md_file" 2>/dev/null \
132
- | grep -oE '\([^)]+\)$' \
133
- | sed -e 's/^(//' -e 's/)$//')
134
- done < <(find "$skill_dir" -name "*.md" -type f -not -path "*/.bak/*" -not -path "*/.bak" -not -path "*/references/dev-docs/*" 2>/dev/null)
135
- if [ -f "$TMP_LOG" ] && [ -s "$TMP_LOG" ]; then
136
- while IFS= read -r line; do
137
- echo " ERR [$skill_name] $line"
138
- errs_json="${errs_json}\"$line\","
139
- errors=$((errors + 1))
140
- done < "$TMP_LOG"
141
- fi
142
- rm -f "$TMP_LOG"
143
-
144
- # 7. 敏感信息扫描(降级为 WARN:教学文档必然提及敏感关键词,仅作提示)
145
- # 排除: description 字段 / 安全规则文档 / `${...}` 环境变量 / ✅❌ 教学标记 /
146
- # 注释行(# 开头)/ 占位符(null/~/<...>/xxx/your-...)/ docker -e / .bak 归档 / dev-docs 归档
147
- rm -f "$TMP_LOG"
148
- while IFS= read -r md_file; do
149
- [ -z "$md_file" ] && continue
150
- grep -niE "(password|appSecret|appToken|keytab|api[_-]?key|access[_-]?token|private[_-]?key)" "$md_file" 2>/dev/null \
151
- | grep -vE '^[[:space:]]*description:' \
152
- | grep -vE '(禁止|脱敏|不要|避免|切勿|切记|严禁)' \
153
- | grep -vE '\$\{[A-Za-z_][A-Za-z0-9_]*(:[^}]*)?\}' \
154
- | grep -vE '(✅|❌|✓|✗)' \
155
- | grep -vE '#.*(提交到 git|示例|演示|反例|样例|默认值|占位)' \
156
- | grep -vE '^[[:digit:]]+:[[:space:]]*-e[[:space:]]+[A-Z_]*(PASSWORD|SECRET|KEY|TOKEN)' \
157
- | grep -vE '^[[:digit:]]+:#' \
158
- | grep -vE '=(null|~|xxx|XXX|your-|placeholder|<[^>]+>|xxx\.\.\.)\s*$' \
159
- | grep -vE '=[^=]*\.\.\.\s*$' \
160
- >> "$TMP_LOG" 2>/dev/null || true
161
- done < <(find "$skill_dir" -name "*.md" -type f -not -path "*/.bak/*" -not -path "*/.bak" -not -path "*/references/dev-docs/*" 2>/dev/null)
162
- if [ -f "$TMP_LOG" ] && [ -s "$TMP_LOG" ]; then
163
- SENS_COUNT=$(wc -l < "$TMP_LOG")
164
- echo " WARN [$skill_name] sensitive-keyword $SENS_COUNT hits(教学文档,建议人工 review)"
165
- warns_json="${warns_json}\"sensitive:$SENS_COUNT hits\","
166
- warnings=$((warnings + 1))
167
- fi
168
- rm -f "$TMP_LOG"
169
-
170
- # 8. TODO/FIXME 残留扫描(排除 .bak)
171
- rm -f "$TMP_LOG"
172
- while IFS= read -r md_file; do
173
- [ -z "$md_file" ] && continue
174
- grep -nE "(TODO|FIXME|XXX|HACK|TBD)" "$md_file" 2>/dev/null >> "$TMP_LOG" || true
175
- done < <(find "$skill_dir" -name "*.md" -type f -not -path "*/.bak/*" -not -path "*/.bak" 2>/dev/null)
176
- if [ -f "$TMP_LOG" ] && [ -s "$TMP_LOG" ]; then
177
- while IFS= read -r line; do
178
- add_warn "TODO: $line"
179
- done < "$TMP_LOG"
180
- fi
181
- rm -f "$TMP_LOG"
182
-
183
- # 输出汇总
184
- if [ "$errors" -gt 0 ]; then
185
- echo "FAIL [$skill_name] ($errors errors / $warnings warnings)"
186
- FAIL_COUNT=$((FAIL_COUNT + 1))
187
- [ "$EXIT_CODE" = "0" ] && EXIT_CODE=1
188
- elif [ "$warnings" -gt 0 ]; then
189
- echo "WARN [$skill_name] ($warnings warnings)"
190
- PASS_COUNT=$((PASS_COUNT + 1))
191
- WARN_COUNT=$((WARN_COUNT + 1))
192
- [ "$EXIT_CODE" = "0" ] && EXIT_CODE=2
193
- else
194
- echo "OK [$skill_name]"
195
- PASS_COUNT=$((PASS_COUNT + 1))
196
- fi
197
- TOTAL_SKILLS=$((TOTAL_SKILLS + 1))
198
-
199
- # JSON 记录
200
- errs_json=$(echo "$errs_json" | sed 's/,$//')
201
- warns_json=$(echo "$warns_json" | sed 's/,$//')
202
- JSON_RESULTS+=("{\"skill\":\"$skill_name\",\"lines\":$lines,\"errors\":$errors,\"warnings\":$warnings,\"errs\":[$errs_json],\"warns\":[$warns_json]}")
203
- }
204
-
205
- # 主流程
206
- if [ -n "$TARGET" ]; then
207
- if [ ! -d "$SKILLS_DIR/$TARGET" ]; then
208
- echo "FAIL skill 不存在: $TARGET"
209
- exit 1
210
- fi
211
- check_skill "$SKILLS_DIR/$TARGET"
212
- else
213
- for skill_dir in "$SKILLS_DIR"/*/; do
214
- [ -d "$skill_dir" ] || continue
215
- check_skill "$skill_dir"
216
- done
217
- fi
218
-
219
- # 9. 重复 skill name
220
- echo ""
221
- echo "========================================"
222
- echo " 重复 skill name 检查"
223
- echo "========================================"
224
- dup_count=0
225
- dup_names=$(grep -hE '^[[:space:]]*name:' "$SKILLS_DIR"/*/SKILL.md 2>/dev/null \
226
- | sed -E 's/.*name:[[:space:]]*"?([^"]+)"?.*//' \
227
- | sort | uniq -d)
228
- if [ -n "$dup_names" ]; then
229
- echo "$dup_names" | while read dup; do
230
- [ -z "$dup" ] && continue
231
- echo "FAIL 重复 name: $dup"
232
- dup_count=$((dup_count + 1))
233
- done
234
- FAIL_COUNT=$((FAIL_COUNT + 1))
235
- [ "$EXIT_CODE" = "0" ] && EXIT_CODE=1
236
- else
237
- echo "OK 0 个重复 name"
238
- fi
239
-
240
- echo ""
241
- echo "========================================"
242
- echo " 结果: $PASS_COUNT 通过 / $FAIL_COUNT 失败 (总计 $TOTAL_SKILLS)"
243
- echo "========================================"
244
-
245
- # 行数 TOP-10
246
- echo ""
247
- echo "========================================"
248
- echo " SKILL.md 行数 TOP-10(最长)"
249
- echo "========================================"
250
- printf "%-45s %8s\n" "Skill" "Lines"
251
- printf "%-45s %8s\n" "-----------------------------------------------" "--------"
252
- for skill_dir in "$SKILLS_DIR"/*/; do
253
- [ -d "$skill_dir" ] || continue
254
- sn=$(basename "$skill_dir")
255
- if [ -f "$skill_dir/SKILL.md" ]; then
256
- printf "%-45s %8d\n" "$sn" "$(wc -l < "$skill_dir/SKILL.md")"
257
- fi
258
- done | sort -k2 -n -r | head -10
259
-
260
- echo ""
261
- echo "========================================"
262
- echo " 统计汇总"
263
- echo "========================================"
264
- echo "skill 总数: $TOTAL_SKILLS"
265
- echo "通过: $PASS_COUNT"
266
- echo "失败: $FAIL_COUNT"
267
- echo "仅警告: $WARN_COUNT"
268
-
269
- # JSON 报告
270
- if [ "$JSON_OUTPUT" = "1" ]; then
271
- REPORT="$SUITE_DIR/vetter-report.json"
272
- {
273
- echo "{"
274
- echo " \"version\": \"2.6.1\","
275
- echo " \"timestamp\": \"$(date -Iseconds 2>/dev/null || date)\","
276
- echo " \"suite_dir\": \"$SUITE_DIR\","
277
- echo " \"total\": $TOTAL_SKILLS,"
278
- echo " \"passed\": $PASS_COUNT,"
279
- echo " \"failed\": $FAIL_COUNT,"
280
- echo " \"warning_only\": $WARN_COUNT,"
281
- echo " \"results\": ["
282
- csep=""
283
- for r in "${JSON_RESULTS[@]}"; do
284
- echo " $csep$r"
285
- csep=","
286
- done
287
- echo " ]"
288
- echo "}"
289
- } > "$REPORT"
290
- echo ""
291
- echo "JSON 报告: $REPORT"
292
- fi
293
-
294
- exit $EXIT_CODE