Allow skill slugs without a platform suffix.
All checks were successful
技能自动化发布 / release (push) Successful in 9s

This commit is contained in:
2026-09-21 11:30:38 +08:00
parent a70da6a076
commit 8c8811e777
10 changed files with 94 additions and 53 deletions

View File

@@ -1,6 +1,6 @@
"""技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger)。"""
SKILL_SLUG = "your-skill-slug"
SKILL_VERSION = "1.0.111"
SKILL_VERSION = "1.0.112"
LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug"
PLATFORM_KIT_MIN_VERSION = "1.2.11"

View File

@@ -7,7 +7,7 @@ import re
_KEBAB_SLUG = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
_MAX_SLUG_LENGTH = 48
_MIN_SEGMENTS = 3
_MIN_SEGMENTS = 2
_MAX_SEGMENTS = 5
_MAX_NOUN_SEGMENTS = 3
@@ -60,7 +60,9 @@ _BUILTIN_PLATFORMS = frozenset(
"etax",
}
)
_BUILTIN_SCOPES = frozenset({"all", "batch", "multi", "default"})
_BUILTIN_SCOPES = frozenset({"all", "batch", "multi"})
# 旧技能用 default 占位末段。不再当成合法 scope;校验只警告,避免同步后挡住已发布仓库。
_DEPRECATED_PLATFORM_PLACEHOLDER = "default"
LEGACY_EXEMPT_SLUGS: frozenset[str] = frozenset(
{
@@ -117,11 +119,12 @@ def _match_platform_suffix(
def classify_slug_form(segments: list[str], *, slug: str = "") -> str:
"""
返回: 'legacy_exempt' | 'standard' | 'scope' | 'invalid'
返回: 'legacy_exempt' | 'standard' | 'scope' | 'plain' | 'invalid'
- legacy_exempt: slug in LEGACY_EXEMPT_SLUGS
- standard: 末段匹配 platforms, 首段 in verbs, 3<=len<=5
- scope: 末段 in scopes, 首段 in verbs, 3<=len<=5
- standard: 末段匹配 platforms,首段 in verbs,2<=len<=5,且动词后至少还有一段名词
- scope: 末段 in scopes(all/batch/multi),首段 in verbs,至少 verb-noun-scope
- plain: 动词 + 名词,无平台、无 scope
- invalid: 其他
"""
if slug in LEGACY_EXEMPT_SLUGS:
@@ -138,12 +141,14 @@ def classify_slug_form(segments: list[str], *, slug: str = "") -> str:
return "invalid"
if segments[-1] in scopes:
if len(segments) < 3:
return "invalid"
return "scope"
if _match_platform_suffix(segments, platforms) is not None:
return "standard"
return "invalid"
return "plain"
def validate_slug_semantics(
@@ -173,8 +178,8 @@ def validate_slug_semantics(
if segment_count < _MIN_SEGMENTS:
msg = (
f"slug 段数应为 {_MIN_SEGMENTS}~{_MAX_SEGMENTS}(verb-noun-platform),"
f"当前为 {segment_count} 段:{slug!r};新技能禁止 2 段无平台形态(如 reconcile-finance)"
f"slug 段数应为 {_MIN_SEGMENTS}~{_MAX_SEGMENTS}(动词-名词,平台可选),"
f"当前为 {segment_count} 段:{slug!r}"
)
(errors if strict else warnings).append(msg)
return errors, warnings
@@ -197,9 +202,6 @@ def validate_slug_semantics(
form = classify_slug_form(segments, slug=slug)
if form == "scope":
if segments[-1] not in scopes:
msg = f"slug 末段应为 scope 白名单中的词:{segments[-1]!r}(slug={slug!r})"
(errors if strict else warnings).append(msg)
noun_count = segment_count - 2
elif form == "standard":
platform_match = _match_platform_suffix(segments, platforms)
@@ -210,13 +212,17 @@ def validate_slug_semantics(
else:
_platform_name, platform_part_count = platform_match
noun_count = segment_count - 1 - platform_part_count
elif form == "plain":
noun_count = segment_count - 1
if segments[-1] == _DEPRECATED_PLATFORM_PLACEHOLDER:
warnings.append(
"slug 末段 default 已不再作为平台占位;没有外部平台时直接省略。"
f"已发布的技能不用改名:{slug!r}"
)
else:
if segment_count >= _MIN_SEGMENTS:
if segments[-1] not in scopes:
platform_match = _match_platform_suffix(segments, platforms)
if platform_match is None:
msg = f"slug 末段应匹配平台或 scope 白名单:{slug!r}"
(errors if strict else warnings).append(msg)
if segments and segments[-1] in scopes and segment_count < 3:
msg = f"scope 型 slug 至少为动词-名词-scope:{slug!r}"
(errors if strict else warnings).append(msg)
noun_count = 0
if noun_count > _MAX_NOUN_SEGMENTS: