forked from jason/echo
Fix SKILL.md description over marketplace 1024-char cap + build guard
The plugin upload validates each skills/**/SKILL.md `description` frontmatter against a 1024-char cap (distinct from plugin.json's <500 rule). The skill description was 1171 chars and got rejected on upload. - Trim the chorus-memory SKILL.md description to 1000 chars, preserving the save/recall/group triggers and the full "Do NOT" exclusion list. - Add MAX_SKILL_DESCRIPTION=1024 guard to build.py: scans every SKILL.md and fails the build with an over-by-N message so this can't silently regress. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,8 @@ EXCLUDE_SUFFIXES = {".pyc", ".pyo"}
|
||||
FIXED_DATE = (2026, 1, 1, 0, 0, 0) # stable timestamp for reproducible archives
|
||||
MAX_DESCRIPTION = 500 # plugin-marketplace cap: plugin.json "description" must be UNDER this
|
||||
# (it has silently regressed past the limit before — fail the build now)
|
||||
MAX_SKILL_DESCRIPTION = 1024 # marketplace cap: each SKILL.md frontmatter "description" must be
|
||||
# AT MOST this many chars (upload rejects it otherwise — fail here first)
|
||||
|
||||
# field name -> the constant assigned in chorus_config.py
|
||||
_CONSTS = {"group": "DEFAULT_GROUP", "member": "DEFAULT_MEMBER",
|
||||
@@ -90,6 +92,32 @@ def file_bytes(path: Path, arcname: str, bake: dict | None, strip_key: bool) ->
|
||||
return text.encode("utf-8")
|
||||
|
||||
|
||||
def skill_description_violations() -> list[str]:
|
||||
"""Return a message for every skills/**/SKILL.md whose frontmatter `description`
|
||||
exceeds MAX_SKILL_DESCRIPTION. The marketplace validates this at upload time and
|
||||
rejects the whole plugin, so catch it at build time instead."""
|
||||
problems = []
|
||||
for skill_md in sorted((SRC / "skills").rglob("SKILL.md")):
|
||||
text = skill_md.read_text(encoding="utf-8")
|
||||
fm = re.match(r"^---\n(.*?)\n---", text, re.S)
|
||||
if not fm:
|
||||
continue
|
||||
# description is a single-line scalar: capture from `description:` to the next
|
||||
# top-level `key:` line (or end of frontmatter).
|
||||
dm = re.search(r"^description:[ \t]*(.*?)(?=\n[A-Za-z_][\w-]*:[ \t]|\Z)",
|
||||
fm.group(1), re.S | re.M)
|
||||
if not dm:
|
||||
continue
|
||||
desc = dm.group(1).strip()
|
||||
if len(desc) > 2 and desc[0] in "\"'" and desc[-1] == desc[0]:
|
||||
desc = desc[1:-1]
|
||||
if len(desc) > MAX_SKILL_DESCRIPTION:
|
||||
rel = skill_md.relative_to(SRC).as_posix()
|
||||
problems.append(f"{rel}: description is {len(desc)} chars — must be at most "
|
||||
f"{MAX_SKILL_DESCRIPTION} (over by {len(desc) - MAX_SKILL_DESCRIPTION}).")
|
||||
return problems
|
||||
|
||||
|
||||
def token_present(path: Path) -> bool:
|
||||
"""True if any DEFAULT_* constant in chorus_config.py holds a non-empty value."""
|
||||
text = path.read_text(encoding="utf-8")
|
||||
@@ -190,6 +218,11 @@ def main(argv: list[str] | None = None) -> int:
|
||||
f"{MAX_DESCRIPTION} (marketplace cap). Trim \"description\" in {MANIFEST}.",
|
||||
file=sys.stderr)
|
||||
return 1
|
||||
skill_problems = skill_description_violations()
|
||||
for problem in skill_problems:
|
||||
print(f"build: {problem} Trim the SKILL.md \"description\" frontmatter.", file=sys.stderr)
|
||||
if skill_problems:
|
||||
return 1
|
||||
files = included_files()
|
||||
arcnames = {p.relative_to(SRC).as_posix() for p in files}
|
||||
if MANIFEST not in arcnames:
|
||||
|
||||
Reference in New Issue
Block a user