From 5529771ec8639576c2eb79aa04bf4f837d269197 Mon Sep 17 00:00:00 2001 From: jason Date: Tue, 23 Jun 2026 07:18:23 -0500 Subject: [PATCH] descrip length check and fix --- build.py | 11 ++++++++++- echo-memory-1.2.0.plugin | Bin 116151 -> 116381 bytes echo-memory.plugin | Bin 116151 -> 116381 bytes .../.claude-plugin/plugin.json | 2 +- .../echo-memory/scripts/test_echo_client.py | 9 +++++++++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/build.py b/build.py index dc5f781..ff17494 100644 --- a/build.py +++ b/build.py @@ -41,6 +41,8 @@ EXCLUDE_DIRS = {"__pycache__", ".git"} EXCLUDE_NAMES = {".DS_Store"} 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) _KEY_RE = re.compile(r'(DEFAULT_KEY\s*=\s*")([0-9a-fA-F]{16,})(")') @@ -102,7 +104,14 @@ def main(argv: list[str] | None = None) -> int: return 1 manifest_path = SRC / ".claude-plugin" / "plugin.json" - version = json.loads(manifest_path.read_text(encoding="utf-8"))["version"] + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + version = manifest["version"] + desc = manifest.get("description", "") + if len(desc) >= MAX_DESCRIPTION: + print(f"build: plugin description is {len(desc)} chars — must be under " + f"{MAX_DESCRIPTION} (marketplace cap). Trim \"description\" in {MANIFEST}.", + file=sys.stderr) + return 1 files = included_files() arcnames = {p.relative_to(SRC).as_posix() for p in files} if MANIFEST not in arcnames: diff --git a/echo-memory-1.2.0.plugin b/echo-memory-1.2.0.plugin index 669467da2222225d1947928db7791b56af0544be..b8f9b63fb093d8cbec2c9fe8460c0aef64855041 100644 GIT binary patch delta 3979 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 E3o4r=bn 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 E3o4r=bn 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 None: echo.get_text = orig +def test_plugin_description_under_500_chars() -> None: + # Marketplace cap — the description has silently regressed past it before (a 525-char + # relapse + an earlier "fix description length" commit). build.py fails the build on this; + # this guards it in CI so it's caught before packaging. + manifest = Path(__file__).resolve().parents[3] / ".claude-plugin" / "plugin.json" + desc = json.loads(manifest.read_text(encoding="utf-8")).get("description", "") + assert 0 < len(desc) < 500, f"plugin description is {len(desc)} chars (must be under 500)" + + def test_stem_extracts_literal_directory_prefix() -> None: assert check_routing.stem(r"^projects/active/[^/]+\.md$") == "projects/active/" assert check_routing.stem(r"^areas/(business|personal)/[^/]+\.md$") == "areas/"