fix(skills): 禁止自动注入完整技能内容 (#49)

## Summary

- 将 dashscope-image-gen、image-to-image、markdown、tech-diagram、xiaomi-tts
改为仅按需加载,并递增 patch 版本
- 在 Market validator 与 JSON Schema 中禁止 `disable-model-invocation: false`
- 将回归测试接入 `i18n Validate`,同步中英文技能编写规范

## Root cause and impact

这 5 个技能在首次引入时即声明 `disable-model-invocation: false`,导致完整技能正文进入普通请求的
system prompt。修改后市场技能只能声明 `true` 或省略该字段,完整内容仅在显式调用 Skill 工具后加载。

## Validation

- `uv run --quiet scripts/i18n/test_validate_i18n.py`
- `uv run --quiet scripts/i18n/validate-i18n.py`
- `uv run --quiet scripts/i18n/translate.py --check`
- `python3 -m json.tool
scripts/i18n/schema/skill-frontmatter.schema.json`
- `actionlint .github/workflows/i18n-validate.yml`
- `git diff --check`
This commit is contained in:
2026-07-14 11:55:22 +08:00
committed by GitHub
parent 5ab2994664
commit 73c94ab70e
15 changed files with 110 additions and 31 deletions

View File

@@ -18,6 +18,7 @@ Checks:
9. Skill/Agent counts and builtin skill index match the repository contents.
10. Skill, Agent, and entry.json category references exist in categories.json.
11. entry.json pointers have the required marketplace fields and safe source URLs.
12. Market Skills set `disable-model-invocation` to true or omit it; false is prohibited.
Exit codes:
0 = pass
@@ -108,6 +109,21 @@ def heading_count(text: str) -> int:
return len(HEADING_PATTERN.findall(text or ""))
def validate_model_invocation_policy(
frontmatter: dict[str, Any],
path: str,
report: Report,
) -> None:
"""Reject market skills that request full-content system-prompt injection."""
value = frontmatter.get("disable-model-invocation")
if value is not None and value is not True:
report.add(Issue(
path,
"model-invocation-policy",
"disable-model-invocation must be true or omitted; automatic full-content injection is prohibited",
))
def validate_skill(
skill_dir: Path,
report: Report,
@@ -128,6 +144,8 @@ def validate_skill(
return
assert fm is not None and body is not None
validate_model_invocation_policy(fm, f"{rel_dir}/SKILL.md", report)
name = fm.get("name", "")
description = fm.get("description", "")
market = fm.get("market") or {}