LeMetaSkill v1.1.0:跨 runtime(ZCode/Claude Code/Codex)的 Agent Skill 创建与迭代工坊

- SKILL.md 双工作流(创建/迭代)+ 两道门(验料/通用化)+ 三档位
- 7 篇方法论 references + 3 个纯标准库脚本(scaffold/validate/trigger_eval)
- 自身触发用例 16 条 + 先例研究/创建/迭代证据报告
- 方法论吸收自 qiaomu-meta-skill 与 luban-skill,取舍见 reports/prior-art.md
This commit is contained in:
Leroy
2026-10-05 12:09:22 +08:00
commit d3e9bd45bd
21 changed files with 1827 additions and 0 deletions
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""LeMetaSkill 结构体检:检查一个 skill 目录是否满足基本契约。
只依赖标准库。检查项(FAIL 阻断 / WARN 提醒):
- SKILL.md 存在、frontmatter 可解析 [FAIL]
- name 存在、kebab-case、1-64 字符、与目录名一致 [FAIL]
- description 存在且够长(>=30 字符) [WARN]
- description 含负触发信号("不要用于"类表述) [WARN]
- 正文非空、<=500 行 [WARN]
- 正文中的本地 .md 链接可达(无悬空引用) [FAIL]
- evals/*.json 可解析 [FAIL]
- 无密钥/私有绝对路径 [WARN]
- 顶层目录都是已知用途 [WARN]
用法: python3 validate_skill.py <skill目录>
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
KNOWN_DIRS = {"references", "scripts", "evals", "assets", "reports", "examples", "tests"}
MAX_BODY_LINES = 500
SECRET_PATTERNS = [
r"AKIA[0-9A-Z]{16}",
r"sk-[A-Za-z0-9]{20,}",
r"(?:api[_-]?key|token|secret|password)\s*[:=]\s*['\"]?[A-Za-z0-9_\-]{16,}",
r"ghp_[A-Za-z0-9]{36}",
]
PRIVATE_PATH = "/Users" + "/[A-Za-z0-9_.\\-]+/" # 拼接写法:避免扫描器匹配到自身源码
NEGATIVE_HINT = re.compile(r"不要|不该|不用于|排除|exclude|not for|don'?t use|do not use", re.I)
MD_LINK = re.compile(r"\[[^\]]*\]\(([^)\s]+)\)")
def parse_frontmatter(text: str) -> tuple[dict[str, str], int]:
"""解析简易 YAML frontmatter(key: value 与 key: | 块),返回 (字段, 正文起始行号)。"""
lines = text.splitlines()
if not lines or lines[0].strip() != "---":
return {}, 0
fields: dict[str, str] = {}
key = ""
for i in range(1, min(len(lines), 60)):
line = lines[i]
if line.strip() == "---":
return fields, i + 1
m = re.match(r"^([A-Za-z_][\w-]*):\s*(.*)$", line)
if m:
key = m.group(1)
fields[key] = m.group(2).strip().strip("'\"")
elif line.startswith((" ", "\t")) and key:
fields[key] = (fields[key] + " " + line.strip().strip("'\"")).strip()
return {}, 0 # 没有闭合的 ---
def main() -> int:
ap = argparse.ArgumentParser(description="skill 结构体检")
ap.add_argument("skill_dir", type=Path)
args = ap.parse_args()
root: Path = args.skill_dir.resolve()
if not root.is_dir():
print(f"FAIL 目录不存在: {root}")
return 1
n_pass = n_warn = n_fail = 0
def ok(msg: str) -> None:
nonlocal n_pass
n_pass += 1
print(f"PASS {msg}")
def warn(msg: str) -> None:
nonlocal n_warn
n_warn += 1
print(f"WARN {msg}")
def fail(msg: str) -> None:
nonlocal n_fail
n_fail += 1
print(f"FAIL {msg}")
skill_md = root / "SKILL.md"
if not skill_md.is_file():
fail("SKILL.md 不存在")
print(f"\n--- Summary --- PASS:{n_pass} WARN:{n_warn} FAIL:{n_fail}")
return 1
ok("SKILL.md 存在")
text = skill_md.read_text(encoding="utf-8")
fields, body_start = parse_frontmatter(text)
if not fields:
fail("frontmatter 缺失或未闭合(需要 --- 包裹的头)")
else:
ok("frontmatter 可解析")
name = fields.get("name", "")
if not name:
fail("缺少 name 字段")
else:
if not re.fullmatch(r"[a-z0-9]+(-[a-z0-9]+)*", name):
fail(f"name 不是 kebab-case: {name!r}")
elif not 1 <= len(name) <= 64:
fail(f"name 长度超出 1-64: {len(name)}")
else:
ok(f"name 合法: {name}")
if root.name != name:
fail(f"name 与目录名不一致: {name!r} vs {root.name!r}")
else:
ok("name 与目录名一致")
desc = fields.get("description", "")
if not desc:
fail("缺少 description 字段(触发信号,必填)")
else:
if len(desc) < 30:
warn(f"description 太短({len(desc)} 字符),盖不住触发场景")
else:
ok(f"description 长度充分({len(desc)} 字符)")
if NEGATIVE_HINT.search(desc):
ok("description 含负触发信号")
else:
warn("description 缺负触发(\"不要用于…\"),边界请求会犹豫")
body = "\n".join(text.splitlines()[body_start:]).strip()
if not body:
fail("正文为空")
else:
n_lines = len(body.splitlines())
if n_lines > MAX_BODY_LINES:
warn(f"正文 {n_lines} 行,超过 {MAX_BODY_LINES} 行预算——考虑拆进 references/")
else:
ok(f"正文 {n_lines} 行,在预算内")
# 悬空引用
dangling = []
for target in MD_LINK.findall(body):
if target.startswith(("http://", "https://", "mailto:", "#")):
continue
if not (root / target).exists():
dangling.append(target)
if dangling:
fail(f"正文引用的本地文件不存在: {', '.join(sorted(set(dangling)))}")
elif MD_LINK.search(body):
ok("正文本地链接全部可达")
# evals JSON
evals_dir = root / "evals"
if evals_dir.is_dir():
for j in sorted(evals_dir.glob("*.json")):
try:
json.loads(j.read_text(encoding="utf-8"))
ok(f"{j.relative_to(root)} 可解析")
except json.JSONDecodeError as e:
fail(f"{j.relative_to(root)} JSON 解析失败: {e}")
# 密钥与私有路径(只读文本类文件)
for p in sorted(root.rglob("*")):
if not p.is_file() or p.suffix not in {".md", ".json", ".py", ".sh", ".txt", ".yaml", ".yml"}:
continue
try:
content = p.read_text(encoding="utf-8")
except (UnicodeDecodeError, PermissionError):
continue
rel = p.relative_to(root)
for pat in SECRET_PATTERNS:
if re.search(pat, content):
warn(f"{rel} 疑似包含密钥/令牌(模式 {pat})")
if rel.name != "SKILL.md" and re.search(PRIVATE_PATH, content):
warn(f"{rel} 含私有绝对路径(/Users/…)")
# 顶层结构
unknown = [d.name for d in root.iterdir() if d.is_dir() and d.name not in KNOWN_DIRS]
if unknown:
warn(f"未知顶层目录(确认其用途,或移除): {', '.join(unknown)}")
present = [d.name for d in root.iterdir() if d.is_dir() and d.name in KNOWN_DIRS]
if present:
ok(f"标准目录: {', '.join(sorted(present))}")
print(f"\n--- Summary --- PASS:{n_pass} WARN:{n_warn} FAIL:{n_fail}")
if n_fail:
print("结论:未过体检,先修 FAIL 再交付。")
return 1
if n_warn:
print("结论:可交付,WARN 项酌情处理。")
else:
print("结论:全绿。")
return 0
if __name__ == "__main__":
sys.exit(main())