1
0
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:
2026-07-25 00:03:05 -05:00
parent f2bfd70dd4
commit 6b538bbb87
2 changed files with 34 additions and 1 deletions
+33
View File
@@ -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: